]> dev.renevier.net Git - syp.git/blob - openlayers/examples/kamap.txt
err_lonlat_invalid exceptions not thrown correctly
[syp.git] / openlayers / examples / kamap.txt
1 <?php
2 /*
3
4 This is a PHP file to be used as a backend for a ka-Map layer. It requires
5 PHP with Mapscript and libgd modules installed. The top of the file
6 is a configuration section: please edit the variables in this configuration
7 section to meet your needs, then rename this file to tile.php or something
8 similar and put it in a web accessible directory. More information
9 on the OpenLayers ka-Map layer is available from:
10
11   http://trac.openlayers.org/wiki/OpenLayers.Layer.KaMap
12
13 */
14 /**********************************************************************
15  *
16  * $Id: tile.php,v 1.33 2006/02/07 03:19:55 pspencer Exp $
17  *
18  * purpose: a simple phpmapscript-based tile renderer that implements
19  *          rudimentary caching for reasonable efficiency.  Note the
20  *          cache never shrinks in this version so your disk could
21  *          easily fill up!
22  *
23  * author: Paul Spencer (pspencer@dmsolutions.ca)
24  *
25  * modifications by Daniel Morissette (dmorissette@dmsolutions.ca)
26  *
27  * Modified by Christopher Schmidt for OpenLayers redistribution.
28  * 
29  **********************************************************************
30  *
31  * Copyright (c) 2005, DM Solutions Group Inc.
32  *
33  * Permission is hereby granted, free of charge, to any person obtaining a
34  * copy of this software and associated documentation files (the "Software"),
35  * to deal in the Software without restriction, including without limitation
36  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
37  * and/or sell copies of the Software, and to permit persons to whom the
38  * Software is furnished to do so, subject to the following conditions:
39  * 
40  * The above copyright notice and this permission notice shall be included
41  * in all copies or substantial portions of the Software.
42  * 
43  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
44  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
45  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
46  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
47  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
48  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
49  * DEALINGS IN THE SOFTWARE.
50  *
51  **********************************************************************/
52  
53
54 /****************************************************************************** 
55  * basic system configuration
56  *
57  * kaMap! uses PHP/MapScript and the PHP GD extension to
58  * render tiles, and uses PHP/MapScript to generate initialization parameters
59  * a legend, and a keymap from the selected map file.
60  *
61  * Make sure to set the correct module names for your PHP extensions.
62  *
63  * WINDOWS USERS: you will likely need to use php_gd2.dll instead of php_gd.dll
64  */
65 $szPHPMapScriptModule = 'php_mapscript.'.PHP_SHLIB_SUFFIX;
66 $szPHPGDModule = 'php_gd.'.PHP_SHLIB_SUFFIX;
67
68 /****************************************************************************** 
69  * tile generation parameters
70  *
71  * kaMap! generates tiles to load in the client application by first rendering
72  * larger areas from the map file and then slicing them up into smaller tiles.
73  * This approach reduces the overhead of loading PHP/MapScript and PHP GD and 
74  * drawing the map file.  These larger areas are referred to as metaTiles in
75  * the code.  You can set the size of both the small tiles and the metaTiles
76  * here.  A reasonable size for the small tiles seems to be 200 pixels square.
77  * Smaller tiles seem to cause problems in client browsers by causing too many
78  * images to be created and thus slowing performance of live dragging.  Larger
79  * tiles take longer to download to the client and are inefficient.
80  *
81  * The number of smaller tiles that form a metaTile can also be configured.
82  * This parameter allows tuning of the tile generator to ensure optimal
83  * performance and for label placement.  MapServer will produce labels only
84  * within a rendered area.  If the area is too small then features may be
85  * labelled multiple times.  If the area is too large, it may exceed MapServer,s
86  * maximum map size (by default 2000x2000) or be too resource-intensive on the
87  * server, ultimately reducing performance.
88  */
89 $tileWidth = 256;
90 $tileHeight = 256;
91 $metaWidth = 5;
92 $metaHeight = 5;
93 /* $metaBuffer = Buffer size in pixels to add around metatiles to avoid 
94  * rendering issues along the edge of the map image
95  */
96 $metaBuffer = 10;  
97     
98 /****************************************************************************** 
99  * in-image debugging information - tile location, outlines etc.
100  * to use this, you need to remove images from your cache first.  This also
101  * affects the meta tiles - if debug is on, they are not deleted.
102  */
103 $bDebug = false;
104  
105 /****************************************************************************** 
106  * aszMapFiles - an array of map files available to the application.  How this
107  * is used is determined by the application.  Each map file is entered into
108  * this array as a key->value pair.
109  *
110  * The key is the name to be used by the tile caching system to store cached
111  * tiles within the base cache directory.  This key should be a single word
112  * that uniquely identifies the map.
113  *
114  * The value associated with each key is an array of three values.  The first
115  * value is a human-readable name to be presented to the user (should the
116  * application choose to do so) and the second value is the path to the map
117  * file.  It is assumed that the map file is fully configured for use with
118  * MapServer/MapScript as no error checking or setting of values is done.  The 
119  * third value is an array of scale values for zooming.
120  */
121  
122 $aszMapFiles = array(
123   "world"   => array( "World", "/path/to/your/mapfile",
124                array( 10000  ), # in openlayers, the scale array doesn't matter.
125                "PNG24")
126
127 /* Add more elements to this array to offer multiple mapfiles */
128
129 );
130
131 /****************************************************************************** 
132  * figure out which map file to use and set up the necessary variables for
133  * the rest of the code to use.  This does need to be done on every page load
134  * unfortunately.
135  *
136  * szMap should be set to the default map file to use but can change if
137  * this script is called with map=<mapname>. 
138  */
139 $szMap = 'world';
140
141 /****************************************************************************** 
142  * kaMap! caching
143  * 
144  * this is the directory within which kaMap! will create its tile cache.  The
145  * directory does NOT have to be web-accessible, but it must be writable by the
146  * web-server-user and allow creation of both directories AND files.
147  *
148  * the tile caching system will create a separate subdirectory within the base
149  * cache directory for each map file.  Within the cache directory for each map
150  * file, directories will be created for each group of layers.  Within the group
151  * directories, directories will be created at each of the configured scales
152  * for the application (see mapfile configuration above.)
153  */
154 $szBaseCacheDir =  "/var/cache/kamap/";
155
156 /*****  END OF CONFIGURABLE STUFF - unless you know what you are doing   *****/
157 /*****                                                                   *****/
158 /*****                                                                   *****/
159 /*****                                                                   *****/
160 /*****  END OF CONFIGURABLE STUFF - unless you know what you are doing   *****/
161
162 if (isset($_REQUEST['map']) && isset($aszMapFiles[$_REQUEST['map']]))
163 {
164     $szMap = $_REQUEST['map'];
165 }
166
167 $szMapCacheDir = $szBaseCacheDir.$szMap."/";
168 $szMapName = $aszMapFiles[$szMap][0];
169 $szMapFile = $aszMapFiles[$szMap][1];
170 $anScales = $aszMapFiles[$szMap][2];
171 setOutputFormat($aszMapFiles[$szMap][3]);
172 /****************************************************************************** 
173  * output format of the map and resulting tiles
174  *
175  * The output format used with MapServer can greatly affect appearance and
176  * performance.  It is recommended to use an 8 bit format such as PNG
177  *
178  * NOTE: the tile caching code in tile.php is not configurable here.  It
179  * currently assumes that it is outputting 8bit PNG files.  If you change to
180  * PNG24 here then you will need to update tile.php to use the gd function
181  * imagecreatetruecolor.  If you change the output format to jpeg then
182  * you would need to change imagepng() to imagejpeg().  A nice enhancement
183  * would be to make that fully configurable from here.
184  */
185 function setOutputFormat($szFormat)
186 {
187     switch($szFormat) {
188         case "PNG24":
189             $GLOBALS['szMapImageFormat'] = 'PNG24'; //mapscript format name
190             $GLOBALS['szMapImageCreateFunction'] = "imagecreatefrompng"; // appropriate GD function
191             $GLOBALS['szImageExtension'] = '.png'; //file extension
192             $GLOBALS['szImageCreateFunction'] = "imagecreatetruecolor"; //or imagecreatetruecolor if PNG24 ...
193             $GLOBALS['szImageOutputFunction'] = "imagepng"; //or imagegif, imagejpeg ...
194             $GLOBALS['szImageHeader'] = 'image/png'; //the content-type of the image        
195             break;
196         case "GIF":
197             $GLOBALS['szMapImageFormat'] = 'GIF'; //mapscript format name
198             $GLOBALS['szMapImageCreateFunction'] = "imagecreatefromgif"; // appropriate GD function
199             $GLOBALS['szImageExtension'] = '.gif'; //file extension
200             $GLOBALS['szImageCreateFunction'] = "imagecreate"; //or imagecreatetruecolor if PNG24 ...
201             $GLOBALS['szImageOutputFunction'] = "imagegif"; //or imagegif, imagejpeg ...
202             $GLOBALS['szImageHeader'] = 'image/gif'; //the content-type of the image        
203             break;
204         case "JPEG":
205             $GLOBALS['szMapImageFormat'] = 'JPEG'; //mapscript format name
206             $GLOBALS['szMapImageCreateFunction'] = "imagecreatefromjpeg"; // appropriate GD function
207             $GLOBALS['szImageExtension'] = '.jpg'; //file extension
208             $GLOBALS['szImageCreateFunction'] = "imagecreatetruecolor"; //or imagecreatetruecolor if PNG24 ...
209             $GLOBALS['szImageOutputFunction'] = "imagejpeg"; //or imagegif, imagejpeg ...
210             $GLOBALS['szImageHeader'] = 'image/jpeg'; //the content-type of the image        
211             break;
212         case "PNG":
213             $GLOBALS['szMapImageFormat'] = 'PNG'; //mapscript format name
214             $GLOBALS['szMapImageCreateFunction'] = "imagecreatefrompng"; // appropriate GD function
215             $GLOBALS['szImageExtension'] = '.png'; //file extension
216             $GLOBALS['szImageCreateFunction'] = "imagecreate"; //or imagecreatetruecolor if PNG24 ...
217             $GLOBALS['szImageOutputFunction'] = "imagepng"; //or imagegif, imagejpeg ...
218             $GLOBALS['szImageHeader'] = 'image/png'; //the content-type of the image        
219             break;
220         case "DITHERED":
221         case "PNG8":
222             $GLOBALS['szMapImageFormat'] = 'dithered';
223             $GLOBALS['szMapImageCreateFunction'] = "imagecreatefrompng";
224             $GLOBALS['szImageExtension'] = '.png';
225             $GLOBALS['szImageCreateFunction'] = "imagecreate";
226             $GLOBALS['szImageOutputFunction'] = "imagepng";
227             $GLOBALS['szImageHeader'] = 'image/png';
228             break;
229     }
230 }
231
232 /**
233  * create all directories in a directory tree - found on the php web site
234  * under the mkdir function ...
235  */
236 function makeDirs($strPath, $mode = 0775)
237 {
238    return is_dir($strPath) or ( makeDirs(dirname($strPath), $mode) and mkdir($strPath, $mode) );
239 }
240
241 /**
242  * This function replaces all special characters in the given string.
243  *
244  * @param szString string - The string to convert.
245  *
246  * @return string converted
247  */
248 function normalizeString($szString)
249 {
250     // Normalize string by replacing all special characters
251     // e.g.    "http://my.host.com/cgi-bin/mywms?"
252     // becomes "http___my_host_com_cgi_bin_mywms_"
253     return preg_replace("/(\W)/", "_", $szString);
254 }
255
256 /* bug 1253 - root permissions required to delete cached files */
257 $orig_umask = umask(0);
258
259 /* create the main cache directory if necessary */
260 if (!@is_dir($szMapCacheDir))
261     makeDirs($szMapCacheDir);
262
263 /* get the various request parameters 
264  * also need to make sure inputs are clean, especially those used to
265  * build paths and filenames
266  */
267  /*
268  * the tile renderer accepts several parameters and returns a tile image from
269  * the cache, creating the tile only if necessary.
270  *
271  * all requests include the pixel location of the request at a certain scale
272  * and this script figures out the geographic location of the tile from the
273  * scale assuming that 0,0 in pixels is 0,0 in geographic units
274  * 
275  * Request parameters are:
276  *
277  * map: the name of the map to use.  This is handled by config.php.
278  * 
279  * t: top pixel position
280  * l: left pixel position
281  * s: scale
282  * g: (optional) comma-delimited list of group names to draw
283  * layers: (optional) comma-delimited list of layers to draw
284  * force: optional.  If set, force redraw of the meta tile.  This was added to
285  *        help with invalid images sometimes being generated.
286  * tileid: (optional) can be used instead of t+l to specify the tile coord.,
287  *         useful in regenerating the cache
288  */
289
290 $top = isset( $_REQUEST['t'] ) ? intval($_REQUEST['t']) : 0;
291 $left = isset( $_REQUEST['l'] ) ? intval($_REQUEST['l']) : 0;
292 $scale = isset( $_REQUEST['s'] ) ? $_REQUEST['s'] : $anScales[0];
293 $bForce = isset($_REQUEST['force'])? true : false;
294 $groups = isset( $_REQUEST['g'] ) ? $_REQUEST['g'] : "";
295 $layers = isset( $_REQUEST['layers'] ) ? $_REQUEST['layers'] : "";
296
297 // dynamic imageformat ----------------------------------------------
298 //use the function in config.php to set the output format
299 if (isset($_REQUEST['i']))
300    setOutputFormat( $_REQUEST['i'] );
301 //----------------------------------------------------------------
302
303 /* tileid=t#####l#### can be used instead of t+l parameters. Useful in
304  * regenerating the cache for instance.
305  */
306 if (isset( $_REQUEST['tileid']) &&
307     preg_match("/t(-?\d+)l(-?\d+)/", $_REQUEST['tileid'], $aMatch) )
308 {
309     $top = intval($aMatch[1]);
310     $left = intval($aMatch[2]);
311 }
312
313 /* Calculate the metatile's top-left corner coordinates.
314  * Include the $metaBuffer around the metatile to account for various
315  * rendering issues happening around the edge of a map
316  */
317 $metaLeft = floor( ($left)/($tileWidth*$metaWidth) ) * $tileWidth * $metaWidth;
318 $metaTop = floor( ($top)/($tileHeight*$metaHeight) ) * $tileHeight *$metaHeight;
319 $szMetaTileId = "t".$metaTop."l".$metaLeft;
320 $metaLeft -= $metaBuffer;
321 $metaTop -= $metaBuffer;
322
323 /* caching is done by scale value, then groups and layers and finally metatile
324  * and tile id. Create a new directory if necessary
325  */
326 $szGroupDir = $groups != "" ? normalizeString($groups) : "def"; 
327 $szLayerDir = $layers != "" ? normalizeString($layers) : "def"; 
328
329 $szCacheDir = $szMapCacheDir."/".$scale."/".$szGroupDir."/".$szLayerDir."/".$szMetaTileId;
330 if (!@is_dir($szCacheDir))
331     makeDirs($szCacheDir);
332
333 /* resolve cache hit - clear the os stat cache if necessary */
334 $szTileId = "t".$top."l".$left;
335 $szCacheFile = $szCacheDir."/".$szTileId.$szImageExtension;
336 clearstatcache();
337
338 $szMetaDir = $szCacheDir."/meta";
339 if (!@is_Dir($szMetaDir))
340     makeDirs($szMetaDir);
341
342 /* simple locking in case there are several requests for the same meta
343    tile at the same time - only draw it once to help with performance */
344 $szLockFile = $szMetaDir."/lock_".$metaTop."_".$metaLeft;
345 $fpLockFile = fopen($szLockFile, "a+");
346 clearstatcache();
347 if (!file_exists($szCacheFile) || $bForce)
348 {
349     flock($fpLockFile, LOCK_EX);
350     fwrite($fpLockFile, ".");
351     
352     //check once more to see if the cache file was created while waiting for
353     //the lock
354     clearstatcache();
355     if (!file_exists($szCacheFile) || $bForce)
356     {
357         if (!extension_loaded('MapScript'))
358         {
359             dl( $szPHPMapScriptModule );
360         }
361         if (!extension_loaded('gd'))
362         {
363             dl( $szPHPGDModule);
364         }
365         
366         if (!@is_Dir($szMetaDir))
367             makeDirs($szMetaDir);    
368         
369         $oMap = ms_newMapObj($szMapFile);
370                
371         /* Metatile width/height include 2x the metaBuffer value */
372         $oMap->set('width', $tileWidth * $metaWidth + 2*$metaBuffer);
373         $oMap->set('height', $tileHeight * $metaHeight + 2*$metaBuffer);
374         
375         /* Tell MapServer to not render labels inside the metaBuffer area
376          * (new in 4.6) 
377          * TODO: Until MapServer bugs 1353/1355 are resolved, we need to
378          * pass a negative value for "labelcache_map_edge_buffer"
379          */
380         $oMap->setMetadata("labelcache_map_edge_buffer", -$metaBuffer);
381
382         $inchesPerUnit = array(1, 12, 63360.0, 39.3701, 39370.1, 4374754);
383         $geoWidth = $scale/($oMap->resolution*$inchesPerUnit[$oMap->units]);
384         $geoHeight = $scale/($oMap->resolution*$inchesPerUnit[$oMap->units]);
385         
386         /* draw the metatile */
387         $minx = $metaLeft * $geoWidth;
388         $maxx = $minx + $geoWidth * $oMap->width;
389         $maxy = -1 * $metaTop * $geoHeight;
390         $miny = $maxy - $geoHeight * $oMap->height;
391         
392         $nLayers = $oMap->numlayers;
393         $oMap->setExtent($minx,$miny,$maxx,$maxy);
394         $oMap->selectOutputFormat( $szMapImageFormat );               
395         $aszLayers = array();
396         if ($groups || $layers)
397         {
398             /* Draw only specified layers instead of default from mapfile*/
399             if ($layers)
400             {
401                 $aszLayers = explode(",", $layers);
402             }
403
404             if ($groups)
405             {
406                 $aszGroups = explode(",", $groups);
407             }
408
409             for($i=0;$i<$nLayers;$i++)
410             {
411                 $oLayer = $oMap->getLayer($i);
412                 if (($aszGroups && in_array($oLayer->group,$aszGroups)) ||
413                     ($aszLayers && in_array($oLayer->name,$aszLayers)) ||
414                     ($aszGroups && $oLayer->group == '' && 
415                      in_array( "__base__", $aszGroups)))
416                 {
417                     $oLayer->set("status", MS_ON );
418                 }
419                 else
420                 {
421                     $oLayer->set("status", MS_OFF );
422                 }
423             }
424             //need transparency if groups or layers are used
425             $oMap->outputformat->set("transparent", MS_ON );
426         }
427         else
428         {
429             $oMap->outputformat->set("transparent", MS_OFF );
430         }
431
432         
433         $szMetaImg = $szMetaDir."/t".$metaTop."l".$metaLeft.$szImageExtension;
434         $oImg = $oMap->draw();
435         $oImg->saveImage($szMetaImg);
436         $oImg->free();
437         eval("\$oGDImg = ".$szMapImageCreateFunction."('".$szMetaImg."');");
438         if ($bDebug)
439         {
440             $blue = imagecolorallocate($oGDImg, 0, 0, 255);
441             imagerectangle($oGDImg, 0, 0, $tileWidth * $metaWidth - 1, $tileHeight * $metaHeight - 1, $blue );
442         }
443         for($i=0;$i<$metaWidth;$i++)
444         {
445             for ($j=0;$j<$metaHeight;$j++)
446             {
447                 eval("\$oTile = ".$szImageCreateFunction."( ".$tileWidth.",".$tileHeight." );");
448                 // Allocate BG color for the tile (in case the metatile has transparent BG)
449                 $nTransparent = imagecolorallocate($oTile, $oMap->imagecolor->red, $oMap->imagecolor->green, $oMap->imagecolor->blue);
450                 //if ($oMap->outputformat->transparent == MS_ON)
451                 //{
452                     imagecolortransparent( $oTile,$nTransparent);
453                 //}
454                 $tileTop = $j*$tileHeight + $metaBuffer;
455                 $tileLeft = $i*$tileWidth + $metaBuffer;
456                 imagecopy( $oTile, $oGDImg, 0, 0, $tileLeft, $tileTop, $tileWidth, $tileHeight );
457                 /* debugging stuff */
458                 if ($bDebug)
459                 {
460                     $black = imagecolorallocate($oTile, 1, 1, 1);
461                     $green = imagecolorallocate($oTile, 0, 128, 0 );
462                     $red = imagecolorallocate($oTile, 255, 0, 0);
463                     imagerectangle( $oTile, 1, 1, $tileWidth-2, $tileHeight-2, $green ); 
464                     imageline( $oTile, 0, $tileHeight/2, $tileWidth-1, $tileHeight/2, $red);
465                     imageline( $oTile, $tileWidth/2, 0, $tileWidth/2, $tileHeight-1, $red);
466                     imagestring ( $oTile, 3, 10, 10, ($metaLeft+$tileLeft)." x ".($metaTop+$tileTop), $black );
467                     imagestring ( $oTile, 3, 10, 30, ($minx+$i*$geoWidth)." x ".($maxy - $j*$geoHeight), $black );
468                 }
469                 $szTileImg = $szCacheDir."/t".($metaTop+$tileTop)."l".($metaLeft+$tileLeft).$szImageExtension;
470                 eval("$szImageOutputFunction( \$oTile, '".$szTileImg."' );");
471                 imagedestroy($oTile);
472                 $oTile = null;
473             }
474         }
475         if ($oGDImg != null)
476         {   
477             imagedestroy($oGDImg);
478             $oGDImg = null;
479         }
480         if (!$bDebug)
481         {
482             unlink( $szMetaImg );
483         }
484     }
485     //release the exclusive lock
486     flock($fpLockFile, LOCK_UN );
487 }
488
489 //acquire shared lock for reading to prevent a problem that could occur
490 //if a tile exists but is only partially generated.
491 flock($fpLockFile, LOCK_SH);
492
493 $h = fopen($szCacheFile, "r");
494 header("Content-Type: ".$szImageHeader);
495 header("Content-Length: " . filesize($szCacheFile));
496 header("Expires: " . date( "D, d M Y H:i:s GMT", time() + 31536000 ));
497 header("Cache-Control: max-age=31536000, must-revalidate" );
498 fpassthru($h);
499 fclose($h);
500
501 //release lock
502 fclose($fpLockFile);
503
504 /* bug 1253 - root permissions required to delete cached files */
505 umask($orig_umask);
506
507 exit;
508 ?>