Passed
Push — master ( 630eee...e4be3b )
by Mark
05:32 queued 10s
created

StaticMap.php (4 issues)

1
<?php
2
/*
3
 * Copyright (c) 2012-2018 Mark C. Prins <[email protected]>
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
 */
21
include_once (realpath ( dirname ( __FILE__ ) ) . '/../geophp/geoPHP/geoPHP.inc');
22
/**
23
 *
24
 * @author Mark C. Prins <[email protected]>
25
 * @author Gerhard Koch <gerhard.koch AT ymail.com>
26
 *
27
 */
28
class StaticMap {
29
	// this should probably not be changed
30
	protected $tileSize = 256;
31
32
	// the final output
33
	var $doc = '';
34
35
	protected $tileInfo = array (
36
			// OSM sources
37
			'openstreetmap' => array (
38
					'txt' => '(c) OpenStreetMap CC-BY-SA',
39
					'logo' => 'osm_logo.png',
40
					'url' => 'http://tile.openstreetmap.org/{Z}/{X}/{Y}.png'
41
			),
42
			// OCM sources
43
			'cycle' => array (
44
					'txt' => 'OpenCycleMap tiles',
45
					'logo' => 'cycle_logo.png',
46
					'url' => 'https://tile.thunderforest.com/cycle/{Z}/{X}/{Y}.png?apikey='
47
			),
48
			'transport' => array (
49
					'txt' => 'OpenCycleMap tiles',
50
					'logo' => 'cycle_logo.png',
51
					'url' => 'https://tile.thunderforest.com/transport/{Z}/{X}/{Y}.png?apikey='
52
			),
53
			'landscape' => array (
54
					'txt' => 'OpenCycleMap tiles',
55
					'logo' => 'cycle_logo.png',
56
					'url' => 'https://tile.thunderforest.com/landscape/{Z}/{X}/{Y}.png?apikey='
57
			),
58
			'outdoors' => array (
59
					'txt' => 'OpenCycleMap tiles',
60
					'logo' => 'cycle_logo.png',
61
					'url' => 'https://tile.thunderforest.com/outdoors/{Z}/{X}/{Y}.png?apikey='
62
			),
63
			'toner-lite' => array (
64
					'txt' => 'Stamen tiles',
65
					'logo' => 'stamen.png',
66
					'url' => 'http://tile.stamen.com/toner-lite/{Z}/{X}/{Y}.png'
67
			),
68
			'terrain' => array (
69
					'txt' => 'Stamen tiles',
70
					'logo' => 'stamen.png',
71
					'url' => 'http://tile.stamen.com/terrain/{Z}/{X}/{Y}.png'
72
			)
73
			//,
74
			// 'piste'=>array(
75
			// 'txt'=>'OpenPisteMap tiles',
76
			// 'logo'=>'piste_logo.png',
77
			// 'url'=>''),
78
			// 'sea'=>array(
79
			// 'txt'=>'OpenSeaMap tiles',
80
			// 'logo'=>'sea_logo.png',
81
			// 'url'=>''),
82
			// H&B sources
83
//			'hikeandbike' => array (
84
//					'txt' => 'Hike & Bike Map',
85
//					'logo' => 'hnb_logo.png',
86
//					//'url' => 'http://toolserver.org/tiles/hikebike/{Z}/{X}/{Y}.png'
87
//					//moved to: https://www.toolserver.org/tiles/hikebike/12/2105/1388.png
88
//					'url' => 'http://c.tiles.wmflabs.org/hikebike/{Z}/{X}/{Y}.png'
89
//			)
90
	);
91
	protected $tileDefaultSrc = 'openstreetmap';
92
93
	// set up markers
94
	protected $markerPrototypes = array (
95
			// found at http://www.mapito.net/map-marker-icons.html
96
			// these are 17x19 px with a pointer at the bottom left
97
			'lightblue' => array (
98
					'regex' => '/^lightblue([0-9]+)$/',
99
					'extension' => '.png',
100
					'shadow' => false,
101
					'offsetImage' => '0,-19',
102
					'offsetShadow' => false
103
			),
104
			// openlayers std markers are 21x25px with shadow
105
			'ol-marker' => array (
106
					'regex' => '/^marker(|-blue|-gold|-green|-red)+$/',
107
					'extension' => '.png',
108
					'shadow' => 'marker_shadow.png',
109
					'offsetImage' => '-10,-25',
110
					'offsetShadow' => '-1,-13'
111
			),
112
			// these are 16x16 px
113
			'ww_icon' => array (
114
					'regex' => '/ww_\S+$/',
115
					'extension' => '.png',
116
					'shadow' => false,
117
					'offsetImage' => '-8,-8',
118
					'offsetShadow' => false
119
			),
120
			// assume these are 16x16 px
121
			'rest' => array (
122
					'regex' => '/^(?!lightblue([0-9]+)$)(?!(ww_\S+$))(?!marker(|-blue|-gold|-green|-red)+$)(.*)/',
123
					'extension' => '.png',
124
					'shadow' => 'marker_shadow.png',
125
					'offsetImage' => '-8,-8',
126
					'offsetShadow' => '-1,-1'
127
			)
128
	);
129
	protected $centerX, $centerY, $offsetX, $offsetY, $image;
130
	protected $zoom, $lat, $lon, $width, $height, $markers, $maptype, $kmlFileName, $gpxFileName, $geojsonFileName, $autoZoomExtent, $apikey;
131
	protected $tileCacheBaseDir, $mapCacheBaseDir, $mediaBaseDir;
132
	protected $useTileCache = true;
133
	protected $mapCacheID = '';
134
	protected $mapCacheFile = '';
135
	protected $mapCacheExtension = 'png';
136
137
	/**
138
	 * Constructor.
139
	 *
140
	 * @param float $lat
141
	 *        	Latitude (x) of center of map
142
	 * @param float $lon
143
	 *        	Longitude (y) of center of map
144
	 * @param int $zoom
145
	 *        	Zoomlevel
146
	 * @param int $width
147
	 *        	Width in pixels
148
	 * @param int $height
149
	 *        	Height in pixels
150
	 * @param string $maptype
151
	 *        	Name of the map
152
	 * @param mixed $markers
153
	 *        	array of markers
154
	 * @param string $gpx
155
	 *        	GPX filename
156
	 * @param string $kml
157
	 *        	KML filename
158
	 * @param string $mediaDir
159
	 *        	Directory to store/cache maps
160
	 * @param string $tileCacheBaseDir
161
	 *        	Directory to cache map tiles
162
	 * @param boolean $autoZoomExtent
163
	 *        	Wheter or not to override zoom/lat/lon and zoom to the extent of gpx/kml and markers
164
	 * @param apikey
165
	 *          Some service require a key to access
166
	 */
167
	public function __construct($lat, $lon, $zoom, $width, $height, $maptype, $markers, $gpx, $kml, $geojson, $mediaDir, $tileCacheBaseDir, $autoZoomExtent = TRUE, $apikey = '') {
168
		$this->zoom = $zoom;
169
		$this->lat = $lat;
170
		$this->lon = $lon;
171
		$this->width = $width;
172
		$this->height = $height;
173
		// validate + set maptype
174
		$this->maptype = $this->tileDefaultSrc;
175
		if (array_key_exists ( $maptype, $this->tileInfo )) {
176
			$this->maptype = $maptype;
177
		}
178
		$this->markers = $markers;
179
		$this->kmlFileName = $kml;
180
		$this->gpxFileName = $gpx;
181
		$this->geojsonFileName = $geojson;
182
		$this->mediaBaseDir = $mediaDir;
183
		$this->tileCacheBaseDir = $tileCacheBaseDir . '/olmaptiles';
184
		$this->useTileCache = $this->tileCacheBaseDir !== '';
185
		$this->mapCacheBaseDir = $mediaDir . '/olmapmaps';
186
		$this->autoZoomExtent = $autoZoomExtent;
187
		$this->apikey = $apikey;
188
	}
189
190
	/**
191
	 *
192
	 * @param number $long
193
	 * @param number $zoom
194
	 * @return number
195
	 */
196
	public function lonToTile($long, $zoom) {
197
		return (($long + 180) / 360) * pow ( 2, $zoom );
198
	}
199
	/**
200
	 *
201
	 * @param number $lat
202
	 * @param number $zoom
203
	 * @return number
204
	 */
205
	public function latToTile($lat, $zoom) {
206
		return (1 - log ( tan ( $lat * pi () / 180 ) + 1 / cos ( $lat * M_PI / 180 ) ) / M_PI) / 2 * pow ( 2, $zoom );
207
	}
208
209
	/**
210
	 */
211
	public function initCoords() {
212
		$this->centerX = $this->lonToTile ( $this->lon, $this->zoom );
213
		$this->centerY = $this->latToTile ( $this->lat, $this->zoom );
214
		$this->offsetX = floor ( (floor ( $this->centerX ) - $this->centerX) * $this->tileSize );
215
		$this->offsetY = floor ( (floor ( $this->centerY ) - $this->centerY) * $this->tileSize );
216
	}
217
218
	/**
219
	 * make basemap image.
220
	 */
221
	public function createBaseMap() {
222
		$this->image = imagecreatetruecolor ( $this->width, $this->height );
223
		$startX = floor ( $this->centerX - ($this->width / $this->tileSize) / 2 );
224
		$startY = floor ( $this->centerY - ($this->height / $this->tileSize) / 2 );
225
		$endX = ceil ( $this->centerX + ($this->width / $this->tileSize) / 2 );
226
		$endY = ceil ( $this->centerY + ($this->height / $this->tileSize) / 2 );
227
		$this->offsetX = - floor ( ($this->centerX - floor ( $this->centerX )) * $this->tileSize );
228
		$this->offsetY = - floor ( ($this->centerY - floor ( $this->centerY )) * $this->tileSize );
229
		$this->offsetX += floor ( $this->width / 2 );
230
		$this->offsetY += floor ( $this->height / 2 );
231
		$this->offsetX += floor ( $startX - floor ( $this->centerX ) ) * $this->tileSize;
232
		$this->offsetY += floor ( $startY - floor ( $this->centerY ) ) * $this->tileSize;
233
234
		for($x = $startX; $x <= $endX; $x ++) {
235
			for($y = $startY; $y <= $endY; $y ++) {
236
				$url = str_replace ( array (
237
						'{Z}',
238
						'{X}',
239
						'{Y}'
240
				), array (
241
						$this->zoom,
242
						$x,
243
						$y
244
				), $this->tileInfo [$this->maptype] ['url'] );
245
				$url .= $this->apikey;
246
				
247
				$tileData = $this->fetchTile ( $url );
248
				if ($tileData) {
249
					$tileImage = imagecreatefromstring ( $tileData );
250
				} else {
251
					$tileImage = imagecreate ( $this->tileSize, $this->tileSize );
252
					$color = imagecolorallocate ( $tileImage, 255, 255, 255 );
253
					@imagestring ( $tileImage, 1, 127, 127, 'err', $color );
254
				}
255
				$destX = ($x - $startX) * $this->tileSize + $this->offsetX;
256
				$destY = ($y - $startY) * $this->tileSize + $this->offsetY;
257
				dbglog($this->tileSize,"imagecopy tile into image: $destX, $destY");
258
				imagecopy ( $this->image, $tileImage, $destX, $destY, 0, 0, $this->tileSize, $this->tileSize );
259
			}
260
		}
261
	}
262
263
	/**
264
	 * Place markers on the map and number them in the same order as they are listed in the html.
265
	 */
266
	public function placeMarkers() {
267
		$count = 0;
268
		$color = imagecolorallocate ( $this->image, 0, 0, 0 );
269
		$bgcolor = imagecolorallocate ( $this->image, 200, 200, 200 );
270
		$markerBaseDir = dirname ( __FILE__ ) . '/icons';
271
		// loop thru marker array
272
		foreach ( $this->markers as $marker ) {
273
			// set some local variables
274
			$markerLat = $marker ['lat'];
275
			$markerLon = $marker ['lon'];
276
			$markerType = $marker ['type'];
277
			// clear variables from previous loops
278
			$markerFilename = '';
279
			$markerShadow = '';
280
			$matches = false;
281
			// check for marker type, get settings from markerPrototypes
282
			if ($markerType) {
283
				foreach ( $this->markerPrototypes as $markerPrototype ) {
284
					if (preg_match ( $markerPrototype ['regex'], $markerType, $matches )) {
285
						$markerFilename = $matches [0] . $markerPrototype ['extension'];
286
						if ($markerPrototype ['offsetImage']) {
287
							list ( $markerImageOffsetX, $markerImageOffsetY ) = explode ( ",", $markerPrototype ['offsetImage'] );
288
						}
289
						$markerShadow = $markerPrototype ['shadow'];
290
						if ($markerShadow) {
291
							list ( $markerShadowOffsetX, $markerShadowOffsetY ) = explode ( ",", $markerPrototype ['offsetShadow'] );
292
						}
293
					}
294
				}
295
			}
296
			// create img resource
297
			if (file_exists ( $markerBaseDir . '/' . $markerFilename )) {
298
				$markerImg = imagecreatefrompng ( $markerBaseDir . '/' . $markerFilename );
299
			} else {
300
				$markerImg = imagecreatefrompng ( $markerBaseDir . '/marker.png' );
301
			}
302
			// check for shadow + create shadow recource
303
			if ($markerShadow && file_exists ( $markerBaseDir . '/' . $markerShadow )) {
304
				$markerShadowImg = imagecreatefrompng ( $markerBaseDir . '/' . $markerShadow );
305
			}
306
			// calc position
307
			$destX = floor ( ($this->width / 2) - $this->tileSize * ($this->centerX - $this->lonToTile ( $markerLon, $this->zoom )) );
308
			$destY = floor ( ($this->height / 2) - $this->tileSize * ($this->centerY - $this->latToTile ( $markerLat, $this->zoom )) );
309
			// copy shadow on basemap
310
			if ($markerShadow && $markerShadowImg) {
311
				imagecopy ( $this->image, $markerShadowImg, $destX + intval ( $markerShadowOffsetX ), $destY + intval ( $markerShadowOffsetY ), 0, 0, imagesx ( $markerShadowImg ), imagesy ( $markerShadowImg ) );
0 ignored issues
show
$destX + intval($markerShadowOffsetX) of type double is incompatible with the type integer expected by parameter $dst_x of imagecopy(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

311
				imagecopy ( $this->image, $markerShadowImg, /** @scrutinizer ignore-type */ $destX + intval ( $markerShadowOffsetX ), $destY + intval ( $markerShadowOffsetY ), 0, 0, imagesx ( $markerShadowImg ), imagesy ( $markerShadowImg ) );
Loading history...
$destY + intval($markerShadowOffsetY) of type double is incompatible with the type integer expected by parameter $dst_y of imagecopy(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

311
				imagecopy ( $this->image, $markerShadowImg, $destX + intval ( $markerShadowOffsetX ), /** @scrutinizer ignore-type */ $destY + intval ( $markerShadowOffsetY ), 0, 0, imagesx ( $markerShadowImg ), imagesy ( $markerShadowImg ) );
Loading history...
312
			}
313
			// copy marker on basemap above shadow
314
			imagecopy ( $this->image, $markerImg, $destX + intval ( $markerImageOffsetX ), $destY + intval ( $markerImageOffsetY ), 0, 0, imagesx ( $markerImg ), imagesy ( $markerImg ) );
315
			// add label
316
			imagestring ( $this->image, 3, $destX - imagesx ( $markerImg ) + 1, $destY + intval ( $markerImageOffsetY ) + 1, ++ $count, $bgcolor );
0 ignored issues
show
$destY + intval($markerImageOffsetY) + 1 of type double is incompatible with the type integer expected by parameter $y of imagestring(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

316
			imagestring ( $this->image, 3, $destX - imagesx ( $markerImg ) + 1, /** @scrutinizer ignore-type */ $destY + intval ( $markerImageOffsetY ) + 1, ++ $count, $bgcolor );
Loading history...
317
			imagestring ( $this->image, 3, $destX - imagesx ( $markerImg ), $destY + intval ( $markerImageOffsetY ), $count, $color );
318
		}
319
		;
320
	}
321
322
	/**
323
	 *
324
	 * @param string $url
325
	 * @return string
326
	 */
327
	public function tileUrlToFilename($url) {
328
		return $this->tileCacheBaseDir . "/" . str_replace ( array (
329
				'http://'
330
		), '', $url );
331
	}
332
333
	/**
334
	 *
335
	 * @param string $url
336
	 */
337
	public function checkTileCache($url) {
338
		$filename = $this->tileUrlToFilename ( $url );
339
		if (file_exists ( $filename )) {
340
			return file_get_contents ( $filename );
341
		}
342
	}
343
	public function checkMapCache() {
344
		$this->mapCacheID = md5 ( $this->serializeParams () );
345
		$filename = $this->mapCacheIDToFilename ();
346
		if (file_exists ( $filename ))
347
			return true;
348
	}
349
	public function serializeParams() {
350
		return join ( "&", array (
351
				$this->zoom,
352
				$this->lat,
353
				$this->lon,
354
				$this->width,
355
				$this->height,
356
				serialize ( $this->markers ),
357
				$this->maptype,
358
				$this->kmlFileName,
359
				$this->gpxFileName,
360
				$this->geojsonFileName
361
		) );
362
	}
363
	public function mapCacheIDToFilename() {
364
		if (! $this->mapCacheFile) {
365
			$this->mapCacheFile = $this->mapCacheBaseDir . "/" . $this->maptype . "/" . $this->zoom . "/cache_" . substr ( $this->mapCacheID, 0, 2 ) . "/" . substr ( $this->mapCacheID, 2, 2 ) . "/" . substr ( $this->mapCacheID, 4 );
366
		}
367
		return $this->mapCacheFile . "." . $this->mapCacheExtension;
368
	}
369
370
	/**
371
	 * Recursively create the directory.
372
	 *
373
	 * @param string $pathname
374
	 *        	The directory path.
375
	 * @param int $mode
376
	 *        	File access mode. For more information on modes, read the details on the chmod manpage.
377
	 */
378
	public function mkdir_recursive($pathname, $mode) {
379
		is_dir ( dirname ( $pathname ) ) || $this->mkdir_recursive ( dirname ( $pathname ), $mode );
380
		return is_dir ( $pathname ) || @mkdir ( $pathname, $mode );
381
	}
382
383
	/**
384
	 * Write a tile into the cache.
385
	 *
386
	 * @param string $url
387
	 * @param mixed $data
388
	 */
389
	public function writeTileToCache($url, $data) {
390
		$filename = $this->tileUrlToFilename ( $url );
391
		$this->mkdir_recursive ( dirname ( $filename ), 0777 );
392
		file_put_contents ( $filename, $data );
393
	}
394
395
	/**
396
	 * Fetch a tile and (if configured) store it in the cache.
397
	 *
398
	 * @param string $url
399
	 */
400
	public function fetchTile($url) {
401
		if ($this->useTileCache && ($cached = $this->checkTileCache ( $url )))
402
			return $cached;
403
404
		$_UA = 'Mozilla/4.0 (compatible; DokuWikiSpatial HTTP Client; ' . PHP_OS . ')';
405
		if (function_exists ( "curl_init" )) {
406
			// use cUrl
407
			$ch = curl_init ();
408
			curl_setopt ( $ch, CURLOPT_RETURNTRANSFER, 1 );
409
			curl_setopt ( $ch, CURLOPT_USERAGENT, $_UA );
410
			curl_setopt ( $ch, CURLOPT_CONNECTTIMEOUT, 10 );
411
			curl_setopt ( $ch, CURLOPT_URL, $url );
412
			$tile = curl_exec ( $ch );
413
			curl_close ( $ch );
414
		} else {
415
			// use file_get_contents
416
			global $conf;
417
			$opts = array (
418
					'http' => array (
419
							'method' => "GET",
420
							'header' => "Accept-language: en\r\n" . "User-Agent: $_UA\r\n" . "accept: image/png\r\n",
421
							'proxy' => "tcp://" . $conf ['proxy'] ['host'] . ":" . $conf ['proxy'] ['port'],
422
							'request_fulluri' => true
423
					)
424
			);
425
			$context = stream_context_create ( $opts );
426
			$tile = file_get_contents ( $url, false, $context );
427
		}
428
		if ($tile && $this->useTileCache) {
429
			$this->writeTileToCache ( $url, $tile );
430
		}
431
		return $tile;
432
	}
433
434
	/**
435
	 * Draw gpx trace on the map.
436
	 */
437
	public function drawGPX() {
438
		$col = imagecolorallocatealpha ( $this->image, 0, 0, 255, .4 * 127 );
439
		$gpxgeom = geoPHP::load ( file_get_contents ( $this->gpxFileName ), 'gpx' );
440
		$this->drawGeometry ( $gpxgeom, $col );
441
	}
442
443
	/**
444
	 * Draw geojson on the map.
445
	 */
446
	public function drawGeojson() {
447
		$col = imagecolorallocatealpha ( $this->image, 255, 0, 255, .4 * 127 );
448
		$gpxgeom = geoPHP::load ( file_get_contents ( $this->geojsonFileName ), 'json' );
449
		$this->drawGeometry ( $gpxgeom, $col );
450
	}
451
452
	/**
453
	 * Draw kml trace on the map.
454
	 */
455
	public function drawKML() {
456
		// TODO get colour from kml node (not currently supported in geoPHP)
457
		$col = imagecolorallocatealpha ( $this->image, 255, 0, 0, .4 * 127 );
458
		$kmlgeom = geoPHP::load ( file_get_contents ( $this->kmlFileName ), 'kml' );
459
		$this->drawGeometry ( $kmlgeom, $col );
460
	}
461
462
	/**
463
	 * Draw geometry or geometry collection on the map.
464
	 *
465
	 * @param Geometry $geom
466
	 * @param int $colour
467
	 *        	drawing colour
468
	 */
469
	private function drawGeometry($geom, $colour) {
470
		if (empty($geom)) return;
471
		
472
		switch ($geom->geometryType ()) {
473
			case 'GeometryCollection' :
474
				// recursively draw part of the collection
475
				for($i = 1; $i < $geom->numGeometries () + 1; $i ++) {
476
					$_geom = $geom->geometryN ( $i );
477
					$this->drawGeometry ( $_geom, $colour );
478
				}
479
				break;
480
			case 'MultiPolygon' :
481
				// TODO implement / do nothing
482
				break;
483
			case 'MultiLineString' :
484
				// TODO implement / do nothing
485
				break;
486
			case 'MultiPoint' :
487
				// TODO implement / do nothing
488
				break;
489
			case 'Polygon' :
490
				$this->drawPolygon ( $geom, $colour );
491
				break;
492
			case 'LineString' :
493
				$this->drawLineString ( $geom, $colour );
494
				break;
495
			case 'Point' :
496
				$this->drawPoint ( $geom, $colour );
497
				break;
498
			default :
499
				// draw nothing
500
				break;
501
		}
502
	}
503
504
	/**
505
	 * Draw a line on the map.
506
	 *
507
	 * @param LineString $line
508
	 * @param int $colour
509
	 *        	drawing colour
510
	 */
511
	private function drawLineString($line, $colour) {
512
		imagesetthickness ( $this->image, 2 );
513
		for($p = 1; $p < $line->numGeometries (); $p ++) {
514
			// get first pair of points
515
			$p1 = $line->geometryN ( $p );
516
			$p2 = $line->geometryN ( $p + 1 );
517
			// translate to paper space
518
			$x1 = floor ( ($this->width / 2) - $this->tileSize * ($this->centerX - $this->lonToTile ( $p1->x (), $this->zoom )) );
519
			$y1 = floor ( ($this->height / 2) - $this->tileSize * ($this->centerY - $this->latToTile ( $p1->y (), $this->zoom )) );
520
			$x2 = floor ( ($this->width / 2) - $this->tileSize * ($this->centerX - $this->lonToTile ( $p2->x (), $this->zoom )) );
521
			$y2 = floor ( ($this->height / 2) - $this->tileSize * ($this->centerY - $this->latToTile ( $p2->y (), $this->zoom )) );
522
			// draw to image
523
			imageline ( $this->image, $x1, $y1, $x2, $y2, $colour );
524
		}
525
		imagesetthickness ( $this->image, 1 );
526
	}
527
528
	/**
529
	 * Draw a point on the map.
530
	 *
531
	 * @param Point $point
532
	 * @param int $colour
533
	 *        	drawing colour
534
	 */
535
	private function drawPoint($point, $colour) {
536
		imagesetthickness ( $this->image, 2 );
537
		// translate to paper space
538
		$cx = floor ( ($this->width / 2) - $this->tileSize * ($this->centerX - $this->lonToTile ( $point->x (), $this->zoom )) );
539
		$cy = floor ( ($this->height / 2) - $this->tileSize * ($this->centerY - $this->latToTile ( $point->y (), $this->zoom )) );
540
		$r = 5;
541
		// draw to image
542
		// imageellipse($this->image, $cx, $cy,$r, $r, $colour);
543
		imagefilledellipse ( $this->image, $cx, $cy, $r, $r, $colour );
544
		// don't use imageellipse because the imagesetthickness function has
545
		// no effect. So the better workaround is to use imagearc.
546
		imagearc ( $this->image, $cx, $cy, $r, $r, 0, 359, $colour );
547
		imagesetthickness ( $this->image, 1 );
548
	}
549
550
	/**
551
	 * Draw a polygon on the map.
552
	 *
553
	 * @param Polygon $polygon
554
	 * @param int $colour
555
	 *        	drawing colour
556
	 */
557
	private function drawPolygon($polygon, $colour) {
558
		// TODO implementation of drawing holes,
559
		// maybe draw the polygon to an in-memory image and use imagecopy, draw polygon in col., draw holes in bgcol?
560
561
		// print_r('Polygon:<br />');
562
		// print_r($polygon);
563
		$extPoints = array ();
564
		// extring is a linestring actually..
565
		$extRing = $polygon->exteriorRing ();
566
567
		for($i = 1; $i < $extRing->numGeometries (); $i ++) {
568
			$p1 = $extRing->geometryN ( $i );
569
			$x = floor ( ($this->width / 2) - $this->tileSize * ($this->centerX - $this->lonToTile ( $p1->x (), $this->zoom )) );
570
			$y = floor ( ($this->height / 2) - $this->tileSize * ($this->centerY - $this->latToTile ( $p1->y (), $this->zoom )) );
571
			$extPoints [] = $x;
572
			$extPoints [] = $y;
573
		}
574
		// print_r('points:('.($i-1).')<br />');
575
		// print_r($extPoints);
576
		// imagepolygon ($this->image, $extPoints, $i-1, $colour );
577
		imagefilledpolygon ( $this->image, $extPoints, $i - 1, $colour );
578
	}
579
580
	/**
581
	 * add copyright and origin notice and icons to the map.
582
	 */
583
	public function drawCopyright() {
584
		$logoBaseDir = dirname ( __FILE__ ) . '/' . 'logo/';
585
		$logoImg = imagecreatefrompng ( $logoBaseDir . $this->tileInfo ['openstreetmap'] ['logo'] );
586
		$textcolor = imagecolorallocate ( $this->image, 0, 0, 0 );
587
		$bgcolor = imagecolorallocate ( $this->image, 200, 200, 200 );
588
589
		imagecopy ( $this->image, $logoImg, 0, imagesy ( $this->image ) - imagesy ( $logoImg ), 0, 0, imagesx ( $logoImg ), imagesy ( $logoImg ) );
590
		imagestring ( $this->image, 1, imagesx ( $logoImg ) + 2, imagesy ( $this->image ) - imagesy ( $logoImg ) + 1, $this->tileInfo ['openstreetmap'] ['txt'], $bgcolor );
591
		imagestring ( $this->image, 1, imagesx ( $logoImg ) + 1, imagesy ( $this->image ) - imagesy ( $logoImg ), $this->tileInfo ['openstreetmap'] ['txt'], $textcolor );
592
593
		// additional tile source info, ie. who created/hosted the tiles
594
		if ($this->maptype != 'openstreetmap') {
595
			$iconImg = imagecreatefrompng ( $logoBaseDir . $this->tileInfo [$this->maptype] ['logo'] );
596
			imagecopy ( $this->image, $iconImg, imagesx ( $logoImg ) + 1, imagesy ( $this->image ) - imagesy ( $iconImg ), 0, 0, imagesx ( $iconImg ), imagesy ( $iconImg ) );
597
			imagestring ( $this->image, 1, imagesx ( $logoImg ) + imagesx ( $iconImg ) + 4, imagesy ( $this->image ) - ceil ( imagesy ( $logoImg ) / 2 ) + 1, $this->tileInfo [$this->maptype] ['txt'], $bgcolor );
598
			imagestring ( $this->image, 1, imagesx ( $logoImg ) + imagesx ( $iconImg ) + 3, imagesy ( $this->image ) - ceil ( imagesy ( $logoImg ) / 2 ), $this->tileInfo [$this->maptype] ['txt'], $textcolor );
599
		}
600
	}
601
602
	/**
603
	 * make the map.
604
	 */
605
	public function makeMap() {
606
		$this->initCoords ();
607
		$this->createBaseMap ();
608
		if (! empty ( $this->markers ))
609
			$this->placeMarkers ();
610
		if (file_exists ( $this->kmlFileName ))
611
			$this->drawKML ();
612
		if (file_exists ( $this->gpxFileName ))
613
			$this->drawGPX ();
614
		if (file_exists ( $this->geojsonFileName ))
615
			$this->drawGeojson ();
616
617
		$this->drawCopyright ();
618
	}
619
620
	/**
621
	 * Calculate the lat/lon/zoom values to make sure that all of the markers and gpx/kml are on the map.
622
	 * can throw an error like
623
	 * "Fatal error: Uncaught Exception: Cannot create a collection with non-geometries in
624
	 * D:\www\wild-water.nl\www\dokuwiki\lib\plugins\geophp\geoPHP\lib\geometry\Collection.class.php:29"
625
	 *
626
	 * @param float $paddingFactor
627
	 *        	buffer constant to enlarge (>1.0) the zoom level
628
	 */
629
	private function autoZoom($paddingFactor = 1.0) {
630
		$geoms = array ();
631
		$geoms [] = new Point ( $this->lon, $this->lat );
632
		if (! empty ( $this->markers )) {
633
			foreach ( $this->markers as $marker ) {
634
				$geoms [] = new Point ( $marker ['lon'], $marker ['lat'] );
635
			}
636
		}
637
		$g = FALSE;
638
		if (file_exists ( $this->kmlFileName )) {
639
			$g = geoPHP::load ( file_get_contents ( $this->kmlFileName ), 'kml' );
640
			if($g !== FALSE) {
641
				$geoms [] = $g;
642
			}
643
		}
644
		if (file_exists ( $this->gpxFileName )) {
645
			$g = geoPHP::load ( file_get_contents ( $this->gpxFileName ), 'gpx' );
646
			if($g !== FALSE) {
647
				$geoms [] = $g;
648
			}
649
		}
650
		if (file_exists ( $this->geojsonFileName )) {
651
			$g = geoPHP::load ( file_get_contents ( $this->geojsonFileName ), 'geojson' );
652
			if($g !== FALSE) {
653
				$geoms [] = $g;
654
			}
655
		}
656
657
		if (count ( $geoms ) <= 1) {
658
			dbglog($geoms,"StaticMap::autoZoom: Skip setting autozoom options");
659
			return;
660
		}
661
662
		$geom = new GeometryCollection ( $geoms );
663
		$centroid = $geom->centroid ();
664
		$bbox = $geom->getBBox ();
665
666
		// determine vertical resolution, this depends on the distance from the equator
667
		// $vy00 = log(tan(M_PI*(0.25 + $centroid->getY()/360)));
668
		$vy0 = log ( tan ( M_PI * (0.25 + $bbox ['miny'] / 360) ) );
669
		$vy1 = log ( tan ( M_PI * (0.25 + $bbox ['maxy'] / 360) ) );
670
		dbglog("StaticMap::autoZoom: vertical resolution: $vy0, $vy1");
671
		$zoomFactorPowered = ($this->height / 2) / (40.7436654315252 * ($vy1 - $vy0));
672
		$resolutionVertical = 360 / ($zoomFactorPowered * $this->tileSize);
673
		// determine horizontal resolution
674
		$resolutionHorizontal = ($bbox ['maxx'] - $bbox ['minx']) / $this->width;
675
		$resolution = max ( $resolutionHorizontal, $resolutionVertical ) * $paddingFactor;
676
		$zoom = log ( 360 / ($resolution * $this->tileSize), 2 );
677
678
		if (is_finite($zoom) && $zoom < 15 && $zoom > 2) {
679
			$this->zoom = floor ( $zoom );
680
		}
681
		$this->lon = $centroid->getX ();
682
		$this->lat = $centroid->getY ();
683
		dbglog("StaticMap::autoZoom: Set autozoom options to: z: $this->zoom, lon: $this->lon, lat: $this->lat");
684
	}
685
686
	/**
687
	 * get the map, this may return a reference to a cached copy.
688
	 *
689
	 * @return string url relative to media dir
690
	 */
691
	public function getMap() {
692
		try {
693
			if ($this->autoZoomExtent) {
694
				$this->autoZoom ();
695
			}
696
		} catch (Exception $e) {
697
			dbglog($e);
698
		}
699
700
			// use map cache, so check cache for map
701
		if (! $this->checkMapCache ()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->checkMapCache() of type null|true is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
702
			// map is not in cache, needs to be build
703
			$this->makeMap ();
704
			$this->mkdir_recursive ( dirname ( $this->mapCacheIDToFilename () ), 0777 );
705
			imagepng ( $this->image, $this->mapCacheIDToFilename (), 9 );
706
		}
707
		$this->doc = $this->mapCacheIDToFilename ();
708
		// make url relative to media dir
709
		return str_replace ( $this->mediaBaseDir, '', $this->doc );
710
	}
711
}
712