| 1 | #!/usr/bin/php5-cgi |
| 2 | <?php |
| 3 | |
| 4 | /** |
| 5 | * staticMapLite 0.03 |
| 6 | * modded for RRZE settings -unrz191 2012-06-10 |
| 7 | * |
| 8 | * Copyright 2009 Gerhard Koch |
| 9 | * |
| 10 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 11 | * you may not use this file except in compliance with the License. |
| 12 | * You may obtain a copy of the License at |
| 13 | * |
| 14 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 15 | * |
| 16 | * Unless required by applicable law or agreed to in writing, software |
| 17 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 18 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 19 | * See the License for the specific language governing permissions and |
| 20 | * limitations under the License. |
| 21 | * |
| 22 | * @author Gerhard Koch <gerhard.koch AT ymail.com> |
| 23 | * |
| 24 | * USAGE: |
| 25 | * |
| 26 | * staticmap.php?center=40.714728,-73.998672&zoom=14&size=512x512&maptype=mapnik&markers=40.702147,-74.015794,blues|40.711614,-74.012318,greeng|40.718217,-73.998284,redc |
| 27 | * |
| 28 | */ |
| 29 | |
| 30 | error_reporting(0); |
| 31 | ini_set('display_errors','off'); |
| 32 | |
| 33 | Class staticMapLite { |
| 34 | |
| 35 | protected $maxWidth = 2048; |
| 36 | protected $maxHeight = 2048; |
| 37 | |
| 38 | protected $tileSize = 256; |
| 39 | protected $tileSrcUrl = array( 'osmorg' => 'http://osm.rrze.fau.de/tiles/{Z}/{X}/{Y}.png', |
| 40 | 'osmde' => 'http://osm.rrze.fau.de/osmde/{Z}/{X}/{Y}.png' |
| 41 | ); |
| 42 | |
| 43 | protected $tileDefaultSrc = 'osmde'; |
| 44 | protected $markerBaseDir = '/var/www/staticmaplite/images/markers'; |
| 45 | protected $osmLogo = '/var/www/staticmaplite/images/osm_logo.png'; |
| 46 | |
| 47 | protected $markerPrototypes = array(// found at http://www.mapito.net/map-marker-icons.html |
| 48 | 'lighblue' => array('regex'=>'/^lightblue([0-9]+)$/', |
| 49 | 'extension'=>'.png', |
| 50 | 'shadow'=>false, |
| 51 | 'offsetImage'=>'0,-19', |
| 52 | 'offsetShadow'=>false |
| 53 | ), |
| 54 | // openlayers std markers |
| 55 | 'ol-marker'=> array('regex'=>'/^ol-marker(|-blue|-gold|-green)+$/', |
| 56 | 'extension'=>'.png', |
| 57 | 'shadow'=>'../marker_shadow.png', |
| 58 | 'offsetImage'=>'-10,-25', |
| 59 | 'offsetShadow'=>'-1,-13' |
| 60 | ), |
| 61 | // taken from http://www.visual-case.it/cgi-bin/vc/GMapsIcons.pl |
| 62 | 'ylw'=> array('regex'=>'/^(pink|purple|red|ltblu|ylw)-pushpin$/', |
| 63 | 'extension'=>'.png', |
| 64 | 'shadow'=>'../marker_shadow.png', |
| 65 | 'offsetImage'=>'-10,-32', |
| 66 | 'offsetShadow'=>'-1,-13' |
| 67 | ) |
| 68 | |
| 69 | ); |
| 70 | |
| 71 | |
| 72 | |
| 73 | # No point in caching - our source is localhost |
| 74 | protected $useTileCache = false; |
| 75 | protected $tileCacheBaseDir = 'cache/tiles'; |
| 76 | |
| 77 | protected $useMapCache = false; |
| 78 | protected $mapCacheBaseDir = 'cache/maps'; |
| 79 | protected $mapCacheID = ''; |
| 80 | protected $mapCacheFile = ''; |
| 81 | protected $mapCacheExtension = 'png'; |
| 82 | |
| 83 | protected $zoom, $lat, $lon, $width, $height, $markers, $image, $maptype; |
| 84 | protected $centerX, $centerY, $offsetX, $offsetY; |
| 85 | |
| 86 | public function __construct(){ |
| 87 | $this->zoom = 0; |
| 88 | $this->lat = 0; |
| 89 | $this->lon = 0; |
| 90 | $this->width = 500; |
| 91 | $this->height = 350; |
| 92 | $this->markers = array(); |
| 93 | $this->maptype = $this->tileDefaultSrc; |
| 94 | } |
| 95 | |
| 96 | public function parseParams(){ |
| 97 | global $_GET; |
| 98 | |
| 99 | // get zoom from GET paramter |
| 100 | $this->zoom = $_GET['zoom']?intval($_GET['zoom']):0; |
| 101 | if($this->zoom>18)$this->zoom = 18; |
| 102 | |
| 103 | // get lat and lon from GET paramter |
| 104 | list($this->lat,$this->lon) = split(',',$_GET['center']); |
| 105 | $this->lat = floatval($this->lat); |
| 106 | $this->lon = floatval($this->lon); |
| 107 | |
| 108 | // get zoom from GET paramter |
| 109 | if($_GET['size']){ |
| 110 | list($this->width, $this->height) = split('x',$_GET['size']); |
| 111 | $this->width = intval($this->width); |
| 112 | if($this->width > $this->maxWidth) $this->width = $this->maxWidth; |
| 113 | $this->height = intval($this->height); |
| 114 | if($this->height > $this->maxHeight) $this->height = $this->maxHeight; |
| 115 | } |
| 116 | if($_GET['markers']){ |
| 117 | $markers = split('%7C|\|',$_GET['markers']); |
| 118 | foreach($markers as $marker){ |
| 119 | list($markerLat, $markerLon, $markerType) = split(',',$marker); |
| 120 | $markerLat = floatval($markerLat); |
| 121 | $markerLon = floatval($markerLon); |
| 122 | $markerType = basename($markerType); |
| 123 | $this->markers[] = array('lat'=>$markerLat, 'lon'=>$markerLon, 'type'=>$markerType); |
| 124 | } |
| 125 | |
| 126 | } |
| 127 | if($_GET['maptype']){ |
| 128 | if(array_key_exists($_GET['maptype'],$this->tileSrcUrl)) $this->maptype = $_GET['maptype']; |
| 129 | } |
| 130 | } |
| 131 | |
| 132 | public function lonToTile($long, $zoom){ |
| 133 | return (($long + 180) / 360) * pow(2, $zoom); |
| 134 | } |
| 135 | |
| 136 | public function latToTile($lat, $zoom){ |
| 137 | return (1 - log(tan($lat * pi()/180) + 1 / cos($lat* pi()/180)) / pi()) /2 * pow(2, $zoom); |
| 138 | } |
| 139 | |
| 140 | public function initCoords(){ |
| 141 | $this->centerX = $this->lonToTile($this->lon, $this->zoom); |
| 142 | $this->centerY = $this->latToTile($this->lat, $this->zoom); |
| 143 | $this->offsetX = floor((floor($this->centerX)-$this->centerX)*$this->tileSize); |
| 144 | $this->offsetY = floor((floor($this->centerY)-$this->centerY)*$this->tileSize); |
| 145 | } |
| 146 | |
| 147 | public function createBaseMap(){ |
| 148 | $this->image = imagecreatetruecolor($this->width, $this->height); |
| 149 | $startX = floor($this->centerX-($this->width/$this->tileSize)/2); |
| 150 | $startY = floor($this->centerY-($this->height/$this->tileSize)/2); |
| 151 | $endX = ceil($this->centerX+($this->width/$this->tileSize)/2); |
| 152 | $endY = ceil($this->centerY+($this->height/$this->tileSize)/2); |
| 153 | $this->offsetX = -floor(($this->centerX-floor($this->centerX))*$this->tileSize); |
| 154 | $this->offsetY = -floor(($this->centerY-floor($this->centerY))*$this->tileSize); |
| 155 | $this->offsetX += floor($this->width/2); |
| 156 | $this->offsetY += floor($this->height/2); |
| 157 | $this->offsetX += floor($startX-floor($this->centerX))*$this->tileSize; |
| 158 | $this->offsetY += floor($startY-floor($this->centerY))*$this->tileSize; |
| 159 | |
| 160 | for($x=$startX; $x<=$endX; $x++){ |
| 161 | for($y=$startY; $y<=$endY; $y++){ |
| 162 | $url = str_replace(array('{Z}','{X}','{Y}'),array($this->zoom, $x, $y), $this->tileSrcUrl[$this->maptype]); |
| 163 | $tileData = $this->fetchTile($url); |
| 164 | if($tileData){ |
| 165 | $tileImage = imagecreatefromstring($tileData); |
| 166 | } else { |
| 167 | $tileImage = imagecreate($this->tileSize,$this->tileSize); |
| 168 | $color = imagecolorallocate($tileImage, 255, 255, 255); |
| 169 | @imagestring($tileImage,1,127,127,'err',$color); |
| 170 | } |
| 171 | $destX = ($x-$startX)*$this->tileSize+$this->offsetX; |
| 172 | $destY = ($y-$startY)*$this->tileSize+$this->offsetY; |
| 173 | imagecopy($this->image, $tileImage, $destX, $destY, 0, 0, $this->tileSize, $this->tileSize); |
| 174 | } |
| 175 | } |
| 176 | } |
| 177 | |
| 178 | |
| 179 | public function placeMarkers(){ |
| 180 | // loop thru marker array |
| 181 | foreach($this->markers as $marker){ |
| 182 | // set some local variables |
| 183 | $markerLat = $marker['lat']; |
| 184 | $markerLon = $marker['lon']; |
| 185 | $markerType = $marker['type']; |
| 186 | // clear variables from previous loops |
| 187 | $markerFilename = ''; |
| 188 | $markerShadow = ''; |
| 189 | $matches = false; |
| 190 | // check for marker type, get settings from markerPrototypes |
| 191 | if($markerType){ |
| 192 | foreach($this->markerPrototypes as $markerPrototype){ |
| 193 | if(preg_match($markerPrototype['regex'],$markerType,$matches)){ |
| 194 | $markerFilename = $matches[0].$markerPrototype['extension']; |
| 195 | if($markerPrototype['offsetImage']){ |
| 196 | list($markerImageOffsetX, $markerImageOffsetY) = split(",",$markerPrototype['offsetImage']); |
| 197 | } |
| 198 | $markerShadow = $markerPrototype['shadow']; |
| 199 | if($markerShadow){ |
| 200 | list($markerShadowOffsetX, $markerShadowOffsetY) = split(",",$markerPrototype['offsetShadow']); |
| 201 | } |
| 202 | } |
| 203 | } |
| 204 | } |
| 205 | |
| 206 | // check required files or set default |
| 207 | if($markerFilename == '' || !file_exists($this->markerBaseDir.'/'.$markerFilename)){ |
| 208 | $markerIndex++; |
| 209 | $markerFilename = 'lightblue'.$markerIndex.'.png'; |
| 210 | $markerImageOffsetX = 0; |
| 211 | $markerImageOffsetY = -19; } |
| 212 | |
| 213 | // create img resource |
| 214 | if(file_exists($this->markerBaseDir.'/'.$markerFilename)){ |
| 215 | $markerImg = imagecreatefrompng($this->markerBaseDir.'/'.$markerFilename); |
| 216 | } else { |
| 217 | $markerImg = imagecreatefrompng($this->markerBaseDir.'/lightblue1.png'); |
| 218 | } |
| 219 | |
| 220 | // check for shadow + create shadow recource |
| 221 | if($markerShadow && file_exists($this->markerBaseDir.'/'.$markerShadow)){ |
| 222 | $markerShadowImg = imagecreatefrompng($this->markerBaseDir.'/'.$markerShadow); |
| 223 | } |
| 224 | |
| 225 | // calc position |
| 226 | $destX = floor(($this->width/2)-$this->tileSize*($this->centerX-$this->lonToTile($markerLon, $this->zoom))); |
| 227 | $destY = floor(($this->height/2)-$this->tileSize*($this->centerY-$this->latToTile($markerLat, $this->zoom))); |
| 228 | |
| 229 | // copy shadow on basemap |
| 230 | if($markerShadow && $markerShadowImg){ |
| 231 | imagecopy($this->image, $markerShadowImg, $destX+intval($markerShadowOffsetX), $destY+intval($markerShadowOffsetY), |
| 232 | 0, 0, imagesx($markerShadowImg), imagesy($markerShadowImg)); |
| 233 | } |
| 234 | |
| 235 | // copy marker on basemap above shadow |
| 236 | imagecopy($this->image, $markerImg, $destX+intval($markerImageOffsetX), $destY+intval($markerImageOffsetY), |
| 237 | 0, 0, imagesx($markerImg), imagesy($markerImg)); |
| 238 | |
| 239 | }; |
| 240 | } |
| 241 | |
| 242 | |
| 243 | |
| 244 | public function tileUrlToFilename($url){ |
| 245 | return $this->tileCacheBaseDir."/".str_replace(array('http://'),'',$url); |
| 246 | } |
| 247 | |
| 248 | public function checkTileCache($url){ |
| 249 | $filename = $this->tileUrlToFilename($url); |
| 250 | if(file_exists($filename)){ |
| 251 | return file_get_contents($filename); |
| 252 | } |
| 253 | } |
| 254 | |
| 255 | public function checkMapCache(){ |
| 256 | $this->mapCacheID = md5($this->serializeParams()); |
| 257 | $filename = $this->mapCacheIDToFilename(); |
| 258 | if(file_exists($filename)) return true; |
| 259 | } |
| 260 | |
| 261 | public function serializeParams(){ |
| 262 | return join("&",array($this->zoom,$this->lat,$this->lon,$this->width,$this->height, serialize($this->markers),$this->maptype)); |
| 263 | } |
| 264 | |
| 265 | public function mapCacheIDToFilename(){ |
| 266 | if(!$this->mapCacheFile){ |
| 267 | $this->mapCacheFile = $this->mapCacheBaseDir."/".$this->maptype."/".$this->zoom."/cache_".substr($this->mapCacheID,0,2)."/".substr($this->mapCacheID,2,2)."/".substr($this->mapCacheID,4); |
| 268 | } |
| 269 | return $this->mapCacheFile.".".$this->mapCacheExtension; |
| 270 | } |
| 271 | |
| 272 | |
| 273 | |
| 274 | public function mkdir_recursive($pathname, $mode){ |
| 275 | is_dir(dirname($pathname)) || $this->mkdir_recursive(dirname($pathname), $mode); |
| 276 | return is_dir($pathname) || @mkdir($pathname, $mode); |
| 277 | } |
| 278 | public function writeTileToCache($url, $data){ |
| 279 | $filename = $this->tileUrlToFilename($url); |
| 280 | $this->mkdir_recursive(dirname($filename),0777); |
| 281 | file_put_contents($filename, $data); |
| 282 | } |
| 283 | |
| 284 | public function fetchTile($url){ |
| 285 | if($this->useTileCache && ($cached = $this->checkTileCache($url))) return $cached; |
| 286 | $ch = curl_init(); |
| 287 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); |
| 288 | curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/4.0"); |
| 289 | curl_setopt($ch, CURLOPT_URL, $url); |
| 290 | $tile = curl_exec($ch); |
| 291 | curl_close($ch); |
| 292 | if($tile && $this->useTileCache){ |
| 293 | $this->writeTileToCache($url,$tile); |
| 294 | } |
| 295 | return $tile; |
| 296 | |
| 297 | } |
| 298 | |
| 299 | public function copyrightNotice(){ |
| 300 | $logoImg = imagecreatefrompng($this->osmLogo); |
| 301 | imagecopy($this->image, $logoImg, imagesx($this->image)-imagesx($logoImg), imagesy($this->image)-imagesy($logoImg), 0, 0, imagesx($logoImg), imagesy($logoImg)); |
| 302 | |
| 303 | } |
| 304 | |
| 305 | public function sendHeader(){ |
| 306 | header('Content-Type: image/png'); |
| 307 | $expires = 60*60*24*14; |
| 308 | header("Pragma: public"); |
| 309 | header("Cache-Control: maxage=".$expires); |
| 310 | header('Expires: ' . gmdate('D, d M Y H:i:s', time()+$expires) . ' GMT'); |
| 311 | } |
| 312 | |
| 313 | public function makeMap(){ |
| 314 | $this->initCoords(); |
| 315 | $this->createBaseMap(); |
| 316 | if(count($this->markers))$this->placeMarkers(); |
| 317 | if($this->osmLogo) $this->copyrightNotice(); |
| 318 | } |
| 319 | |
| 320 | public function showMap(){ |
| 321 | $this->parseParams(); |
| 322 | if($this->useMapCache){ |
| 323 | // use map cache, so check cache for map |
| 324 | if(!$this->checkMapCache()){ |
| 325 | // map is not in cache, needs to be build |
| 326 | $this->makeMap(); |
| 327 | $this->mkdir_recursive(dirname($this->mapCacheIDToFilename()),0777); |
| 328 | imagepng($this->image,$this->mapCacheIDToFilename(),9); |
| 329 | $this->sendHeader(); |
| 330 | if(file_exists($this->mapCacheIDToFilename())){ |
| 331 | return file_get_contents($this->mapCacheIDToFilename()); |
| 332 | } else { |
| 333 | return imagepng($this->image); |
| 334 | } |
| 335 | } else { |
| 336 | // map is in cache |
| 337 | $this->sendHeader(); |
| 338 | return file_get_contents($this->mapCacheIDToFilename()); |
| 339 | } |
| 340 | |
| 341 | } else { |
| 342 | // no cache, make map, send headers and deliver png |
| 343 | $this->makeMap(); |
| 344 | $this->sendHeader(); |
| 345 | return imagepng($this->image); |
| 346 | |
| 347 | } |
| 348 | } |
| 349 | |
| 350 | } |
| 351 | |
| 352 | $map = new staticMapLite(); |
| 353 | print $map->showMap(); |
| 354 | |
| 355 | ?> |