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