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