1<?php 2/* 3 * Copyright (c) 2012-2013 Mark C. Prins <mprins@users.sf.net> 4 * 5 * In part based on staticMapLite 0.03 available at http://staticmaplite.svn.sourceforge.net/viewvc/staticmaplite/ 6 * 7 * Copyright (c) 2009 Gerhard Koch <gerhard.koch AT ymail.com> 8 * 9 * Licensed under the Apache License, Version 2.0 (the "License"); 10 * you may not use this file except in compliance with the License. 11 * You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, software 16 * distributed under the License is distributed on an "AS IS" BASIS, 17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 * See the License for the specific language governing permissions and 19 * limitations under the License. 20 */ 21include_once(realpath(dirname(__FILE__)).'/../geophp/geoPHP/geoPHP.inc'); 22/** 23 * @author Mark C. Prins <mprins@users.sf.net> 24 * @author Gerhard Koch <gerhard.koch AT ymail.com> 25 * 26 */ 27class StaticMap { 28 // these should probably not be changed 29 protected $tileSize = 256; 30 31 // the final output 32 var $doc = ''; 33 34 protected $tileInfo = array( 35 // OSM sources 36 'openstreetmap'=>array( 37 'txt'=>'(c) OpenStreetMap CC-BY-SA', 38 'logo'=>'osm_logo.png', 39 'url'=>'http://tile.openstreetmap.org/{Z}/{X}/{Y}.png'), 40 // cloudmade 41 'cloudmade' =>array( 42 'txt'=>'CloudMade tiles', 43 'logo'=>'cloudmade_logo.png', 44 'url'=> 'http://tile.cloudmade.com/BC9A493B41014CAABB98F0471D759707/2/256/{Z}/{X}/{Y}.png'), 45 'fresh' =>array( 46 'txt'=>'CloudMade tiles', 47 'logo'=>'cloudmade_logo.png', 48 'url'=> 'http://tile.cloudmade.com/BC9A493B41014CAABB98F0471D759707/997/256/{Z}/{X}/{Y}.png'), 49 // OCM sources 50 'cycle'=>array( 51 'txt'=>'OpenCycleMap tiles', 52 'logo'=>'cycle_logo.png', 53 'url'=>'http://tile.opencyclemap.org/cycle/{Z}/{X}/{Y}.png'), 54 'transport'=>array( 55 'txt'=>'OpenCycleMap tiles', 56 'logo'=>'cycle_logo.png', 57 'url'=>'http://tile2.opencyclemap.org/transport/{Z}/{X}/{Y}.png'), 58 'landscape'=>array( 59 'txt'=>'OpenCycleMap tiles', 60 'logo'=>'cycle_logo.png', 61 'url'=>'http://tile3.opencyclemap.org/landscape/{Z}/{X}/{Y}.png'), 62 // H&B sources 63 'hikeandbike'=>array( 64 'txt'=>'Hike & Bike Map', 65 'logo'=>'hnb_logo.png', 66 'url'=>'http://toolserver.org/tiles/hikebike/{Z}/{X}/{Y}.png'), 67 //'piste'=>array( 68 // 'txt'=>'OpenPisteMap tiles', 69 // 'logo'=>'piste_logo.png', 70 // 'url'=>''), 71 //'sea'=>array( 72 // 'txt'=>'OpenSeaMap tiles', 73 // 'logo'=>'sea_logo.png', 74 // 'url'=>''), 75 // MapQuest 76 'mapquest'=>array( 77 'txt'=>'MapQuest tiles', 78 'logo'=>'mq_logo.png', 79 'url'=>'http://otile3.mqcdn.com/tiles/1.0.0/map/{Z}/{X}/{Y}.png') 80 ); 81 protected $tileDefaultSrc = 'openstreetmap'; 82 83 // set up markers 84 protected $markerPrototypes = array( 85 // found at http://www.mapito.net/map-marker-icons.html 86 // these are 17x19 px with a pointer at the bottom left 87 'lighblue' => array('regex'=>'/^lightblue([0-9]+)$/', 88 'extension'=>'.png', 89 'shadow'=>false, 90 'offsetImage'=>'0,-19', 91 'offsetShadow'=>false 92 ), 93 // openlayers std markers are 21x25px with shadow 94 'ol-marker'=> array('regex'=>'/^marker(|-blue|-gold|-green|-red)+$/', 95 'extension'=>'.png', 96 'shadow'=>'marker_shadow.png', 97 'offsetImage'=>'-10,-25', 98 'offsetShadow'=>'-1,-13' 99 ), 100 // these are 16x16 px 101 'ww_icon'=> array('regex'=>'/ww_\S+$/', 102 'extension'=>'.png', 103 'shadow'=>false, 104 'offsetImage'=>'-8,-8', 105 'offsetShadow'=>false 106 ), 107 // assume these are 16x16 px 108 'rest' => array('regex'=>'/^(?!lightblue([0-9]+)$)(?!(ww_\S+$))(?!marker(|-blue|-gold|-green|-red)+$)(.*)/', 109 'extension'=>'.png', 110 'shadow'=>'marker_shadow.png', 111 'offsetImage'=>'-8,-8', 112 'offsetShadow'=>'-1,-1' 113 ) 114 ); 115 protected $centerX, $centerY, $offsetX, $offsetY, $image; 116 protected $zoom, $lat, $lon, $width, $height, $markers, $maptype, $kmlFileName, $gpxFileName, $geojsonFileName, $autoZoomExtent; 117 protected $tileCacheBaseDir, $mapCacheBaseDir, $mediaBaseDir; 118 protected $useTileCache = true; 119 protected $mapCacheID = ''; 120 protected $mapCacheFile = ''; 121 protected $mapCacheExtension = 'png'; 122 123 /** 124 * Constructor. 125 * @param float $lat Latitude (x) of center of map 126 * @param float $lon Longitude (y) of center of map 127 * @param int $zoom Zoomlevel 128 * @param int $width Width in pixels 129 * @param int $height Height in pixels 130 * @param string $maptype Name of the map 131 * @param mixed $markers array of markers 132 * @param string $gpx GPX filename 133 * @param string $kml KML filename 134 * @param string $mediaDir Directory to store/cache maps 135 * @param string $tileCacheBaseDir Directory to cache map tiles 136 * @param boolean $autoZoomExtent Wheter or not to override zoom/lat/lon and zoom to the extent of gpx/kml and markers 137 */ 138 public function __construct($lat, $lon, $zoom, $width, $height, $maptype, $markers, $gpx, $kml, $geojson, $mediaDir, $tileCacheBaseDir, $autoZoomExtent=TRUE){ 139 $this->zoom = $zoom; 140 $this->lat = $lat; 141 $this->lon = $lon; 142 $this->width = $width; 143 $this->height = $height; 144 // validate + set maptype 145 $this->maptype = $this->tileDefaultSrc; 146 if(array_key_exists($maptype,$this->tileInfo)) { 147 $this->maptype = $maptype; 148 } 149 $this->markers = $markers; 150 $this->kmlFileName = $kml; 151 $this->gpxFileName = $gpx; 152 $this->geojsonFileName = $geojson; 153 $this->mediaBaseDir = $mediaDir; 154 $this->tileCacheBaseDir= $tileCacheBaseDir.'/olmaptiles'; 155 $this->useTileCache = $this->tileCacheBaseDir !==''; 156 $this->mapCacheBaseDir = $mediaDir.'/olmapmaps'; 157 $this->autoZoomExtent = $autoZoomExtent; 158 } 159 160 /** 161 * 162 * @param number $long 163 * @param number $zoom 164 * @return number 165 */ 166 public function lonToTile($long, $zoom){ 167 return (($long + 180) / 360) * pow(2, $zoom); 168 } 169 /** 170 * 171 * @param number $lat 172 * @param number $zoom 173 * @return number 174 */ 175 public function latToTile($lat, $zoom){ 176 return (1 - log(tan($lat * pi()/180) + 1 / cos($lat* M_PI/180)) / M_PI) /2 * pow(2, $zoom); 177 } 178 179 /** 180 * 181 */ 182 public function initCoords(){ 183 $this->centerX = $this->lonToTile($this->lon, $this->zoom); 184 $this->centerY = $this->latToTile($this->lat, $this->zoom); 185 $this->offsetX = floor((floor($this->centerX)-$this->centerX)*$this->tileSize); 186 $this->offsetY = floor((floor($this->centerY)-$this->centerY)*$this->tileSize); 187 } 188 189 /** 190 * make basemap image. 191 */ 192 public function createBaseMap(){ 193 $this->image = imagecreatetruecolor($this->width, $this->height); 194 $startX = floor($this->centerX-($this->width/$this->tileSize)/2); 195 $startY = floor($this->centerY-($this->height/$this->tileSize)/2); 196 $endX = ceil($this->centerX+($this->width/$this->tileSize)/2); 197 $endY = ceil($this->centerY+($this->height/$this->tileSize)/2); 198 $this->offsetX = -floor(($this->centerX-floor($this->centerX))*$this->tileSize); 199 $this->offsetY = -floor(($this->centerY-floor($this->centerY))*$this->tileSize); 200 $this->offsetX += floor($this->width/2); 201 $this->offsetY += floor($this->height/2); 202 $this->offsetX += floor($startX-floor($this->centerX))*$this->tileSize; 203 $this->offsetY += floor($startY-floor($this->centerY))*$this->tileSize; 204 205 for($x=$startX; $x<=$endX; $x++){ 206 for($y=$startY; $y<=$endY; $y++){ 207 $url = str_replace(array('{Z}','{X}','{Y}'),array($this->zoom, $x, $y), $this->tileInfo[$this->maptype]['url']); 208 $tileData = $this->fetchTile($url); 209 if($tileData){ 210 $tileImage = imagecreatefromstring($tileData); 211 } else { 212 $tileImage = imagecreate($this->tileSize,$this->tileSize); 213 $color = imagecolorallocate($tileImage, 255, 255, 255); 214 @imagestring($tileImage,1,127,127,'err',$color); 215 } 216 $destX = ($x-$startX)*$this->tileSize+$this->offsetX; 217 $destY = ($y-$startY)*$this->tileSize+$this->offsetY; 218 imagecopy($this->image, $tileImage, $destX, $destY, 0, 0, $this->tileSize, $this->tileSize); 219 } 220 } 221 } 222 223 /** 224 * Place markers on the map and number them in the same order as they are listed in the html. 225 */ 226 public function placeMarkers(){ 227 $count=0; 228 $color=imagecolorallocate ($this->image,0,0,0 ); 229 $bgcolor=imagecolorallocate ($this->image,200,200,200 ); 230 $markerBaseDir = dirname(__FILE__).'/icons'; 231 // loop thru marker array 232 foreach($this->markers as $marker) { 233 // set some local variables 234 $markerLat = $marker['lat']; 235 $markerLon = $marker['lon']; 236 $markerType = $marker['type']; 237 // clear variables from previous loops 238 $markerFilename = ''; 239 $markerShadow = ''; 240 $matches = false; 241 // check for marker type, get settings from markerPrototypes 242 if($markerType){ 243 foreach($this->markerPrototypes as $markerPrototype){ 244 if(preg_match($markerPrototype['regex'],$markerType,$matches)){ 245 $markerFilename = $matches[0].$markerPrototype['extension']; 246 if($markerPrototype['offsetImage']){ 247 list($markerImageOffsetX, $markerImageOffsetY) = split(",",$markerPrototype['offsetImage']); 248 } 249 $markerShadow = $markerPrototype['shadow']; 250 if($markerShadow){ 251 list($markerShadowOffsetX, $markerShadowOffsetY) = split(",",$markerPrototype['offsetShadow']); 252 } 253 } 254 } 255 } 256 // create img resource 257 if(file_exists($markerBaseDir.'/'.$markerFilename)){ 258 $markerImg = imagecreatefrompng($markerBaseDir.'/'.$markerFilename); 259 } else { 260 $markerImg = imagecreatefrompng($markerBaseDir.'/marker.png'); 261 } 262 // check for shadow + create shadow recource 263 if($markerShadow && file_exists($markerBaseDir.'/'.$markerShadow)){ 264 $markerShadowImg = imagecreatefrompng($markerBaseDir.'/'.$markerShadow); 265 } 266 // calc position 267 $destX = floor(($this->width/2)-$this->tileSize*($this->centerX-$this->lonToTile($markerLon, $this->zoom))); 268 $destY = floor(($this->height/2)-$this->tileSize*($this->centerY-$this->latToTile($markerLat, $this->zoom))); 269 // copy shadow on basemap 270 if($markerShadow && $markerShadowImg){ 271 imagecopy($this->image, $markerShadowImg, $destX+intval($markerShadowOffsetX), $destY+intval($markerShadowOffsetY), 272 0, 0, imagesx($markerShadowImg), imagesy($markerShadowImg)); 273 } 274 // copy marker on basemap above shadow 275 imagecopy($this->image, $markerImg, $destX+intval($markerImageOffsetX), $destY+intval($markerImageOffsetY), 276 0, 0, imagesx($markerImg), imagesy($markerImg)); 277 // add label 278 imagestring ($this->image , 3 , $destX-imagesx($markerImg)+1 , $destY+intval($markerImageOffsetY)+1 , ++$count , $bgcolor ); 279 imagestring ($this->image , 3 , $destX-imagesx($markerImg) , $destY+intval($markerImageOffsetY) , $count , $color ); 280 }; 281 } 282 283 /** 284 * 285 * @param string $url 286 * @return string 287 */ 288 public function tileUrlToFilename($url){ 289 return $this->tileCacheBaseDir."/".str_replace(array('http://'),'',$url); 290 } 291 292 /** 293 * 294 * @param string $url 295 */ 296 public function checkTileCache($url){ 297 $filename = $this->tileUrlToFilename($url); 298 if(file_exists($filename)){ 299 return file_get_contents($filename); 300 } 301 } 302 303 public function checkMapCache(){ 304 $this->mapCacheID = md5($this->serializeParams()); 305 $filename = $this->mapCacheIDToFilename(); 306 if(file_exists($filename)) return true; 307 } 308 309 public function serializeParams(){ 310 return join("&",array($this->zoom,$this->lat,$this->lon,$this->width,$this->height, serialize($this->markers),$this->maptype, $this->kmlFileName, $this->gpxFileName, $this->geojsonFileName)); 311 } 312 313 public function mapCacheIDToFilename(){ 314 if(!$this->mapCacheFile){ 315 $this->mapCacheFile = $this->mapCacheBaseDir."/".$this->maptype."/".$this->zoom."/cache_".substr($this->mapCacheID,0,2)."/".substr($this->mapCacheID,2,2)."/".substr($this->mapCacheID,4); 316 } 317 return $this->mapCacheFile.".".$this->mapCacheExtension; 318 } 319 320 /** 321 * Recursively create the directory. 322 * @param string $pathname The directory path. 323 * @param int $mode File access mode. For more information on modes, read the details on the chmod manpage. 324 */ 325 public function mkdir_recursive($pathname, $mode){ 326 is_dir(dirname($pathname)) || $this->mkdir_recursive(dirname($pathname), $mode); 327 return is_dir($pathname) || @mkdir($pathname, $mode); 328 } 329 330 /** 331 * Write a tile into the cache. 332 * @param string $url 333 * @param mixed $data 334 */ 335 public function writeTileToCache($url, $data){ 336 $filename = $this->tileUrlToFilename($url); 337 $this->mkdir_recursive(dirname($filename),0777); 338 file_put_contents($filename, $data); 339 } 340 341 /** 342 * Fetch a tile and (if configured) store it in the cache. 343 * @param string $url 344 */ 345 public function fetchTile($url){ 346 if($this->useTileCache && ($cached = $this->checkTileCache($url))) return $cached; 347 348 $_UA = 'Mozilla/4.0 (compatible; DokuWikiSpatial HTTP Client; '.PHP_OS.')'; 349 if(function_exists("curl_init")){ 350 // use cUrl 351 $ch = curl_init(); 352 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 353 curl_setopt($ch, CURLOPT_USERAGENT, $_UA); 354 curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); 355 curl_setopt($ch, CURLOPT_URL, $url); 356 $tile = curl_exec($ch); 357 curl_close($ch); 358 } else { 359 // use file_get_contents 360 global $conf; 361 $opts = array( 362 'http'=>array( 363 'method'=>"GET", 364 'header'=>"Accept-language: en\r\n" . 365 "User-Agent: $_UA\r\n". 366 "accept: image/png\r\n", 367 'proxy' => "tcp://".$conf['proxy']['host'] . ":" . $conf['proxy']['port'], 368 'request_fulluri' => true, 369 ) 370 ); 371 $context = stream_context_create($opts); 372 $tile = file_get_contents($url, false, $context); 373 } 374 if($tile && $this->useTileCache){ 375 $this->writeTileToCache($url,$tile); 376 } 377 return $tile; 378 } 379 380 /** 381 * Draw gpx trace on the map. 382 */ 383 public function drawGPX(){ 384 $col = imagecolorallocatealpha($this->image, 0, 0, 255, .4*127); 385 $gpxgeom = geoPHP::load(file_get_contents($this->gpxFileName),'gpx'); 386 $this->drawGeometry($gpxgeom, $col); 387 } 388 389 /** 390 * Draw geojson on the map. 391 */ 392 public function drawGeojson(){ 393 $col = imagecolorallocatealpha($this->image, 255, 0, 255, .4*127); 394 $gpxgeom = geoPHP::load(file_get_contents($this->geojsonFileName),'json'); 395 $this->drawGeometry($gpxgeom, $col); 396 } 397 398 /** 399 * Draw kml trace on the map. 400 */ 401 public function drawKML(){ 402 // TODO get colour from kml node (not currently supported in geoPHP) 403 $col = imagecolorallocatealpha($this->image, 255, 0, 0, .4*127); 404 $kmlgeom = geoPHP::load(file_get_contents($this->kmlFileName),'kml'); 405 $this->drawGeometry($kmlgeom, $col); 406 } 407 408 /** 409 * Draw geometry or geometry collection on the map. 410 * @param Geometry $geom 411 * @param int $colour drawing colour 412 */ 413 private function drawGeometry($geom, $colour){ 414 switch ($geom->geometryType()) { 415 case 'GeometryCollection': 416 // recursively draw part of the collection 417 for ($i = 1; $i < $geom->numGeometries()+1; $i++) { 418 $_geom = $geom->geometryN($i); 419 $this->drawGeometry($_geom, $colour); 420 } 421 break; 422 case 'MultiPolygon': 423 // TODO implement / do nothing 424 break; 425 case 'MultiLineString': 426 // TODO implement / do nothing 427 break; 428 case 'MultiPoint': 429 // TODO implement / do nothing 430 break; 431 case 'Polygon': 432 $this->drawPolygon($geom, $colour); 433 break; 434 case 'LineString': 435 $this->drawLineString($geom, $colour); 436 break; 437 case 'Point': 438 $this->drawPoint($geom, $colour); 439 break; 440 default: 441 //draw nothing 442 break; 443 } 444 } 445 446 /** 447 * Draw a line on the map. 448 * @param LineString $line 449 * @param int $colour drawing colour 450 */ 451 private function drawLineString($line, $colour){ 452 imagesetthickness($this->image,2); 453 for ($p = 1; $p < $line->numGeometries(); $p++) { 454 // get first pair of points 455 $p1 = $line->geometryN($p); 456 $p2 = $line->geometryN($p+1); 457 // translate to paper space 458 $x1 = floor(($this->width/2)-$this->tileSize*($this->centerX-$this->lonToTile($p1->x(), $this->zoom))); 459 $y1 = floor(($this->height/2)-$this->tileSize*($this->centerY-$this->latToTile($p1->y(), $this->zoom))); 460 $x2 = floor(($this->width/2)-$this->tileSize*($this->centerX-$this->lonToTile($p2->x(), $this->zoom))); 461 $y2 = floor(($this->height/2)-$this->tileSize*($this->centerY-$this->latToTile($p2->y(), $this->zoom))); 462 // draw to image 463 imageline ($this->image, $x1, $y1, $x2, $y2, $colour); 464 } 465 imagesetthickness($this->image,1); 466 } 467 468 /** 469 * Draw a point on the map. 470 * @param Point $point 471 * @param int $colour drawing colour 472 */ 473 private function drawPoint($point, $colour){ 474 imagesetthickness($this->image,2); 475 // translate to paper space 476 $cx = floor(($this->width/2)-$this->tileSize*($this->centerX-$this->lonToTile($point->x(), $this->zoom))); 477 $cy = floor(($this->height/2)-$this->tileSize*($this->centerY-$this->latToTile($point->y(), $this->zoom))); 478 $r = 5; 479 // draw to image 480 // imageellipse($this->image, $cx, $cy,$r, $r, $colour); 481 imagefilledellipse ($this->image, $cx, $cy, $r, $r, $colour); 482 // don't use imageellipse because the imagesetthickness function has 483 // no effect. So the better workaround is to use imagearc. 484 imagearc($this->image, $cx, $cy, $r, $r, 0, 359, $colour); 485 imagesetthickness($this->image,1); 486 } 487 488 /** 489 * Draw a polygon on the map. 490 * @param Polygon $polygon 491 * @param int $colour drawing colour 492 */ 493 private function drawPolygon($polygon, $colour){ 494 // TODO implementation of drawing holes, 495 // maybe draw the polygon to an in-memory image and use imagecopy, draw polygon in col., draw holes in bgcol? 496 497 //print_r('Polygon:<br />'); 498 //print_r($polygon); 499 500 $extPoints = array(); 501 // extring is a linestring actually.. 502 $extRing = $polygon->exteriorRing(); 503 504 for ($i = 1; $i < $extRing->numGeometries(); $i++) { 505 $p1 = $extRing->geometryN($i); 506 $x = floor(($this->width/2)-$this->tileSize*($this->centerX-$this->lonToTile($p1->x(), $this->zoom))); 507 $y = floor(($this->height/2)-$this->tileSize*($this->centerY-$this->latToTile($p1->y(), $this->zoom))); 508 $extPoints[]=$x; 509 $extPoints[]=$y; 510 } 511 //print_r('points:('.($i-1).')<br />'); 512 //print_r($extPoints); 513 //imagepolygon ($this->image, $extPoints, $i-1, $colour ); 514 imagefilledpolygon($this->image, $extPoints, $i-1, $colour ); 515 } 516 517 /** 518 * add copyright and origin notice and icons to the map. 519 */ 520 public function drawCopyright(){ 521 $logoBaseDir = dirname(__FILE__).'/'.'logo/'; 522 $logoImg = imagecreatefrompng($logoBaseDir.$this->tileInfo['openstreetmap']['logo']); 523 $textcolor = imagecolorallocate($this->image, 0, 0, 0); 524 $bgcolor = imagecolorallocate($this->image, 200, 200, 200); 525 526 imagecopy($this->image, $logoImg, 0, imagesy($this->image)-imagesy($logoImg), 0, 0, imagesx($logoImg), imagesy($logoImg)); 527 imagestring($this->image , 1, imagesx($logoImg)+2 ,imagesy($this->image)-imagesy($logoImg)+1 ,$this->tileInfo['openstreetmap']['txt'], $bgcolor ); 528 imagestring($this->image , 1, imagesx($logoImg)+1 ,imagesy($this->image)-imagesy($logoImg) ,$this->tileInfo['openstreetmap']['txt'] ,$textcolor ); 529 530 // additional tile source info, ie. who created/hosted the tiles 531 if ($this->maptype!='openstreetmap') { 532 $iconImg = imagecreatefrompng($logoBaseDir.$this->tileInfo[$this->maptype]['logo']); 533 imagecopy($this->image, $iconImg, imagesx($logoImg)+1, imagesy($this->image)-imagesy($iconImg), 0, 0, imagesx($iconImg), imagesy($iconImg)); 534 imagestring($this->image, 1, imagesx($logoImg)+imagesx($iconImg)+4, imagesy($this->image)-ceil(imagesy($logoImg)/2)+1, $this->tileInfo[$this->maptype]['txt'], $bgcolor ); 535 imagestring($this->image, 1, imagesx($logoImg)+imagesx($iconImg)+3, imagesy($this->image)-ceil(imagesy($logoImg)/2), $this->tileInfo[$this->maptype]['txt'], $textcolor ); 536 } 537 } 538 539 /** 540 * make the map. 541 */ 542 public function makeMap(){ 543 $this->initCoords(); 544 $this->createBaseMap(); 545 if(!empty($this->markers))$this->placeMarkers(); 546 if(file_exists($this->kmlFileName)) $this->drawKML(); 547 if(file_exists($this->gpxFileName)) $this->drawGPX(); 548 if(file_exists($this->geojsonFileName)) $this->drawGeojson(); 549 550 $this->drawCopyright(); 551 } 552 553 /** 554 * Calculate the lat/lon/zoom values to make sure that all of the markers and gpx/kml are on the map. 555 * @param float $paddingFactor buffer constant to enlarge (>1.0) the zoom level 556 */ 557 private function autoZoom($paddingFactor=1.0){ 558 $geoms = array(); 559 $geoms[] = new Point($this->lon, $this->lat); 560 if(!empty($this->markers)){ 561 foreach($this->markers as $marker){ 562 $geoms[] = new Point($marker['lon'],$marker['lat']); 563 } 564 } 565 if(file_exists($this->kmlFileName)){ 566 $geoms[] = geoPHP::load(file_get_contents($this->kmlFileName),'kml'); 567 } 568 if(file_exists($this->gpxFileName)) { 569 $geoms[] = geoPHP::load(file_get_contents($this->gpxFileName),'gpx'); 570 } 571 if(file_exists($this->geojsonFileName)) { 572 $geoms[] = geoPHP::load(file_get_contents($this->geojsonFileName),'geojson'); 573 } 574 575 if (count($geoms)<=1) return; 576 577 $geom = new GeometryCollection($geoms); 578 $centroid=$geom->centroid(); 579 $bbox=$geom->getBBox(); 580 581 // determine vertical resolution, this depends on the distance from the equator 582 // $vy00 = log(tan(M_PI*(0.25 + $centroid->getY()/360))); 583 $vy0 = log(tan(M_PI*(0.25 + $bbox['miny']/360))); 584 $vy1 = log(tan(M_PI*(0.25 + $bbox['maxy']/360))); 585 $zoomFactorPowered = ($this->height/2) / (40.7436654315252 * ($vy1 - $vy0)); 586 $resolutionVertical = 360 / ($zoomFactorPowered * $this->tileSize); 587 // determine horizontal resolution 588 $resolutionHorizontal = ($bbox['maxx']-$bbox['minx']) / $this->width; 589 $resolution = max($resolutionHorizontal, $resolutionVertical) * $paddingFactor; 590 $zoom = log(360 / ($resolution * $this->tileSize), 2); 591 592 $this->zoom = floor($zoom); 593 $this->lon=$centroid->getX(); 594 $this->lat=$centroid->getY(); 595 } 596 597 /** 598 * get the map, this may return a reference to a cached copy. 599 * @return string url relative to media dir 600 */ 601 public function getMap(){ 602 if($this->autoZoomExtent) $this->autoZoom() ; 603 604 // use map cache, so check cache for map 605 if(!$this->checkMapCache()){ 606 // map is not in cache, needs to be build 607 $this->makeMap(); 608 $this->mkdir_recursive(dirname($this->mapCacheIDToFilename()),0777); 609 imagepng($this->image,$this->mapCacheIDToFilename(),9); 610 } 611 $this->doc =$this->mapCacheIDToFilename(); 612 // make url relative to media dir 613 return str_replace($this->mediaBaseDir, '', $this->doc); 614 } 615} 616