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