Passed
Pull Request — master (#28)
by Mark
02:48
created
StaticMap.php 2 patches
Indentation   +866 added lines, -866 removed lines patch added patch discarded remove patch
@@ -38,870 +38,870 @@
 block discarded – undo
38 38
  */
39 39
 class StaticMap {
40 40
 
41
-    // the final output
42
-    private $tileSize = 256;
43
-    private $tileInfo = array(
44
-        // OSM sources
45
-        'openstreetmap' => array(
46
-            'txt'  => '(c) OpenStreetMap data/ODbl',
47
-            'logo' => 'osm_logo.png',
48
-            'url'  => 'https://tile.openstreetmap.org/{Z}/{X}/{Y}.png'
49
-        ),
50
-        // OCM sources
51
-        'cycle'         => array(
52
-            'txt'  => '(c) Thunderforest maps',
53
-            'logo' => 'tf_logo.png',
54
-            'url'  => 'https://tile.thunderforest.com/cycle/{Z}/{X}/{Y}.png'
55
-        ),
56
-        'transport'     => array(
57
-            'txt'  => '(c) Thunderforest maps',
58
-            'logo' => 'tf_logo.png',
59
-            'url'  => 'https://tile.thunderforest.com/transport/{Z}/{X}/{Y}.png'
60
-        ),
61
-        'landscape'     => array(
62
-            'txt'  => '(c) Thunderforest maps',
63
-            'logo' => 'tf_logo.png',
64
-            'url'  => 'https://tile.thunderforest.com/landscape/{Z}/{X}/{Y}.png'
65
-        ),
66
-        'outdoors'      => array(
67
-            'txt'  => '(c) Thunderforest maps',
68
-            'logo' => 'tf_logo.png',
69
-            'url'  => 'https://tile.thunderforest.com/outdoors/{Z}/{X}/{Y}.png'
70
-        ),
71
-        'toner-lite'    => array(
72
-            'txt'  => 'Stamen tiles',
73
-            'logo' => 'stamen.png',
74
-            'url'  => 'https://stamen-tiles.a.ssl.fastly.net/toner/{Z}/{X}/{Y}.png'
75
-        ),
76
-        'terrain'       => array(
77
-            'txt'  => 'Stamen tiles',
78
-            'logo' => 'stamen.png',
79
-            'url'  => 'https://stamen-tiles.a.ssl.fastly.net/terrain/{Z}/{X}/{Y}.jpg'
80
-        )
81
-        //,
82
-        // 'piste'=>array(
83
-        // 'txt'=>'OpenPisteMap tiles',
84
-        // 'logo'=>'piste_logo.png',
85
-        // 'url'=>''),
86
-        // 'sea'=>array(
87
-        // 'txt'=>'OpenSeaMap tiles',
88
-        // 'logo'=>'sea_logo.png',
89
-        // 'url'=>''),
90
-        // H&B sources
91
-        //          'hikeandbike' => array (
92
-        //                  'txt' => 'Hike & Bike Map',
93
-        //                  'logo' => 'hnb_logo.png',
94
-        //                  //'url' => 'http://toolserver.org/tiles/hikebike/{Z}/{X}/{Y}.png'
95
-        //                  //moved to: https://www.toolserver.org/tiles/hikebike/12/2105/1388.png
96
-        //                  'url' => 'http://c.tiles.wmflabs.org/hikebike/{Z}/{X}/{Y}.png'
97
-        //          )
98
-    );
99
-    private $tileDefaultSrc = 'openstreetmap';
100
-
101
-    // set up markers
102
-    private $markerPrototypes = array(
103
-        // found at http://www.mapito.net/map-marker-icons.html
104
-        // these are 17x19 px with a pointer at the bottom left
105
-        'lightblue' => array(
106
-            'regex'        => '/^lightblue([0-9]+)$/',
107
-            'extension'    => '.png',
108
-            'shadow'       => false,
109
-            'offsetImage'  => '0,-19',
110
-            'offsetShadow' => false
111
-        ),
112
-        // openlayers std markers are 21x25px with shadow
113
-        'ol-marker' => array(
114
-            'regex'        => '/^marker(|-blue|-gold|-green|-red)+$/',
115
-            'extension'    => '.png',
116
-            'shadow'       => 'marker_shadow.png',
117
-            'offsetImage'  => '-10,-25',
118
-            'offsetShadow' => '-1,-13'
119
-        ),
120
-        // these are 16x16 px
121
-        'ww_icon'   => array(
122
-            'regex'        => '/ww_\S+$/',
123
-            'extension'    => '.png',
124
-            'shadow'       => false,
125
-            'offsetImage'  => '-8,-8',
126
-            'offsetShadow' => false
127
-        ),
128
-        // assume these are 16x16 px
129
-        'rest'      => array(
130
-            'regex'        => '/^(?!lightblue([0-9]+)$)(?!(ww_\S+$))(?!marker(|-blue|-gold|-green|-red)+$)(.*)/',
131
-            'extension'    => '.png',
132
-            'shadow'       => 'marker_shadow.png',
133
-            'offsetImage'  => '-8,-8',
134
-            'offsetShadow' => '-1,-1'
135
-        )
136
-    );
137
-    private $centerX;
138
-    private $centerY;
139
-    private $offsetX;
140
-    private $offsetY;
141
-    private $image;
142
-    private $zoom;
143
-    private $lat;
144
-    private $lon;
145
-    private $width;
146
-    private $height;
147
-    private $markers;
148
-    private $maptype;
149
-    private $kmlFileName;
150
-    private $gpxFileName;
151
-    private $geojsonFileName;
152
-    private $autoZoomExtent;
153
-    private $apikey;
154
-    private $tileCacheBaseDir;
155
-    private $mapCacheBaseDir;
156
-    private $mediaBaseDir;
157
-    private $useTileCache;
158
-    private $mapCacheID = '';
159
-    private $mapCacheFile = '';
160
-    private $mapCacheExtension = 'png';
161
-
162
-    /**
163
-     * Constructor.
164
-     *
165
-     * @param float  $lat
166
-     *            Latitude (x) of center of map
167
-     * @param float  $lon
168
-     *            Longitude (y) of center of map
169
-     * @param int    $zoom
170
-     *            Zoomlevel
171
-     * @param int    $width
172
-     *            Width in pixels
173
-     * @param int    $height
174
-     *            Height in pixels
175
-     * @param string $maptype
176
-     *            Name of the map
177
-     * @param array  $markers
178
-     *            array of markers
179
-     * @param string $gpx
180
-     *            GPX filename
181
-     * @param string $kml
182
-     *            KML filename
183
-     * @param string $geojson
184
-     * @param string $mediaDir
185
-     *            Directory to store/cache maps
186
-     * @param string $tileCacheBaseDir
187
-     *            Directory to cache map tiles
188
-     * @param bool   $autoZoomExtent
189
-     *            Wheter or not to override zoom/lat/lon and zoom to the extent of gpx/kml and markers
190
-     * @param string $apikey
191
-     */
192
-    public function __construct(
193
-        float $lat,
194
-        float $lon,
195
-        int $zoom,
196
-        int $width,
197
-        int $height,
198
-        string $maptype,
199
-        array $markers,
200
-        string $gpx,
201
-        string $kml,
202
-        string $geojson,
203
-        string $mediaDir,
204
-        string $tileCacheBaseDir,
205
-        bool $autoZoomExtent = true,
206
-        string $apikey = ''
207
-    ) {
208
-        $this->zoom   = $zoom;
209
-        $this->lat    = $lat;
210
-        $this->lon    = $lon;
211
-        $this->width  = $width;
212
-        $this->height = $height;
213
-        // validate + set maptype
214
-        $this->maptype = $this->tileDefaultSrc;
215
-        if(array_key_exists($maptype, $this->tileInfo)) {
216
-            $this->maptype = $maptype;
217
-        }
218
-        $this->markers          = $markers;
219
-        $this->kmlFileName      = $kml;
220
-        $this->gpxFileName      = $gpx;
221
-        $this->geojsonFileName  = $geojson;
222
-        $this->mediaBaseDir     = $mediaDir;
223
-        $this->tileCacheBaseDir = $tileCacheBaseDir . '/olmaptiles';
224
-        $this->useTileCache     = $this->tileCacheBaseDir !== '';
225
-        $this->mapCacheBaseDir  = $mediaDir . '/olmapmaps';
226
-        $this->autoZoomExtent   = $autoZoomExtent;
227
-        $this->apikey           = $apikey;
228
-    }
229
-
230
-    /**
231
-     * get the map, this may return a reference to a cached copy.
232
-     *
233
-     * @return string url relative to media dir
234
-     */
235
-    public function getMap(): string {
236
-        try {
237
-            if($this->autoZoomExtent) {
238
-                $this->autoZoom();
239
-            }
240
-        } catch(Exception $e) {
241
-            dbglog($e);
242
-        }
243
-
244
-        // use map cache, so check cache for map
245
-        if(!$this->checkMapCache()) {
246
-            // map is not in cache, needs to be build
247
-            $this->makeMap();
248
-            $this->mkdirRecursive(dirname($this->mapCacheIDToFilename()), 0777);
249
-            imagepng($this->image, $this->mapCacheIDToFilename(), 9);
250
-        }
251
-        $doc = $this->mapCacheIDToFilename();
252
-        // make url relative to media dir
253
-        return str_replace($this->mediaBaseDir, '', $doc);
254
-    }
255
-
256
-    /**
257
-     * Calculate the lat/lon/zoom values to make sure that all of the markers and gpx/kml are on the map.
258
-     *
259
-     * @param float $paddingFactor
260
-     *            buffer constant to enlarge (>1.0) the zoom level
261
-     * @throws Exception if non-geometries are found in the collection
262
-     */
263
-    private function autoZoom(float $paddingFactor = 1.0): void {
264
-        $geoms    = array();
265
-        $geoms [] = new Point ($this->lon, $this->lat);
266
-        if(!empty ($this->markers)) {
267
-            foreach($this->markers as $marker) {
268
-                $geoms [] = new Point ($marker ['lon'], $marker ['lat']);
269
-            }
270
-        }
271
-        if(file_exists($this->kmlFileName)) {
272
-            $g = geoPHP::load(file_get_contents($this->kmlFileName), 'kml');
273
-            if($g !== false) {
274
-                $geoms [] = $g;
275
-            }
276
-        }
277
-        if(file_exists($this->gpxFileName)) {
278
-            $g = geoPHP::load(file_get_contents($this->gpxFileName), 'gpx');
279
-            if($g !== false) {
280
-                $geoms [] = $g;
281
-            }
282
-        }
283
-        if(file_exists($this->geojsonFileName)) {
284
-            $g = geoPHP::load(file_get_contents($this->geojsonFileName), 'geojson');
285
-            if($g !== false) {
286
-                $geoms [] = $g;
287
-            }
288
-        }
289
-
290
-        if(count($geoms) <= 1) {
291
-            dbglog($geoms, "StaticMap::autoZoom: Skip setting autozoom options");
292
-            return;
293
-        }
294
-
295
-        $geom     = new GeometryCollection ($geoms);
296
-        $centroid = $geom->centroid();
297
-        $bbox     = $geom->getBBox();
298
-
299
-        // determine vertical resolution, this depends on the distance from the equator
300
-        // $vy00 = log(tan(M_PI*(0.25 + $centroid->getY()/360)));
301
-        $vy0 = log(tan(M_PI * (0.25 + $bbox ['miny'] / 360)));
302
-        $vy1 = log(tan(M_PI * (0.25 + $bbox ['maxy'] / 360)));
303
-        dbglog("StaticMap::autoZoom: vertical resolution: $vy0, $vy1");
304
-        if ($vy1 - $vy0 === 0.0){
305
-            $resolutionVertical = 0;
306
-            dbglog("StaticMap::autoZoom: using $resolutionVertical");
307
-        } else {
308
-            $zoomFactorPowered  = ($this->height / 2) / (40.7436654315252 * ($vy1 - $vy0));
309
-            $resolutionVertical = 360 / ($zoomFactorPowered * $this->tileSize);
310
-        }
311
-        // determine horizontal resolution
312
-        $resolutionHorizontal = ($bbox ['maxx'] - $bbox ['minx']) / $this->width;
313
-        dbglog("StaticMap::autoZoom: using $resolutionHorizontal");
314
-        $resolution           = max($resolutionHorizontal, $resolutionVertical) * $paddingFactor;
315
-        $zoom                 = $this->zoom;
316
-        if ($resolution > 0){
317
-            $zoom             = log(360 / ($resolution * $this->tileSize), 2);
318
-        }
319
-
320
-        if(is_finite($zoom) && $zoom < 15 && $zoom > 2) {
321
-            $this->zoom = floor($zoom);
322
-        }
323
-        $this->lon = $centroid->getX();
324
-        $this->lat = $centroid->getY();
325
-        dbglog("StaticMap::autoZoom: Set autozoom options to: z: $this->zoom, lon: $this->lon, lat: $this->lat");
326
-    }
327
-
328
-    public function checkMapCache(): bool {
329
-        // side effect: set the mapCacheID
330
-        $this->mapCacheID = md5($this->serializeParams());
331
-        $filename         = $this->mapCacheIDToFilename();
332
-        return file_exists($filename);
333
-    }
334
-
335
-    public function serializeParams(): string {
336
-        return implode(
337
-            "&", array(
338
-                   $this->zoom,
339
-                   $this->lat,
340
-                   $this->lon,
341
-                   $this->width,
342
-                   $this->height,
343
-                   serialize($this->markers),
344
-                   $this->maptype,
345
-                   $this->kmlFileName,
346
-                   $this->gpxFileName,
347
-                   $this->geojsonFileName
348
-               )
349
-        );
350
-    }
351
-
352
-    public function mapCacheIDToFilename(): string {
353
-        if(!$this->mapCacheFile) {
354
-            $this->mapCacheFile = $this->mapCacheBaseDir . "/" . $this->maptype . "/" . $this->zoom . "/cache_"
355
-                . substr($this->mapCacheID, 0, 2) . "/" . substr($this->mapCacheID, 2, 2)
356
-                . "/" . substr($this->mapCacheID, 4);
357
-        }
358
-        return $this->mapCacheFile . "." . $this->mapCacheExtension;
359
-    }
360
-
361
-    /**
362
-     * make the map.
363
-     */
364
-    public function makeMap(): void {
365
-        $this->initCoords();
366
-        $this->createBaseMap();
367
-        if(!empty ($this->markers)) {
368
-            $this->placeMarkers();
369
-        }
370
-        if (file_exists($this->kmlFileName)) {
371
-            try {
372
-                $this->drawKML();
373
-            } catch (exception $e) {
374
-                dbglog('failed to load KML file', $e);
375
-            }
376
-        }
377
-        if (file_exists($this->gpxFileName)) {
378
-            try {
379
-                $this->drawGPX();
380
-            } catch (exception $e) {
381
-                dbglog('failed to load GPX file', $e);
382
-            }
383
-        }
384
-        if (file_exists($this->geojsonFileName)) {
385
-            try {
386
-                $this->drawGeojson();
387
-            } catch (exception $e) {
388
-                dbglog('failed to load GeoJSON file', $e);
389
-            }
390
-        }
391
-
392
-        $this->drawCopyright();
393
-    }
394
-
395
-    /**
396
-     */
397
-    public function initCoords(): void {
398
-        $this->centerX = $this->lonToTile($this->lon, $this->zoom);
399
-        $this->centerY = $this->latToTile($this->lat, $this->zoom);
400
-        $this->offsetX = floor((floor($this->centerX) - $this->centerX) * $this->tileSize);
401
-        $this->offsetY = floor((floor($this->centerY) - $this->centerY) * $this->tileSize);
402
-    }
403
-
404
-    /**
405
-     *
406
-     * @param float $long
407
-     * @param int   $zoom
408
-     * @return float|int
409
-     */
410
-    public function lonToTile(float $long, int $zoom) {
411
-        return (($long + 180) / 360) * pow(2, $zoom);
412
-    }
413
-
414
-    /**
415
-     *
416
-     * @param float $lat
417
-     * @param int   $zoom
418
-     * @return float|int
419
-     */
420
-    public function latToTile(float $lat, int $zoom) {
421
-        return (1 - log(tan($lat * pi() / 180) + 1 / cos($lat * M_PI / 180)) / M_PI) / 2 * pow(2, $zoom);
422
-    }
423
-
424
-    /**
425
-     * make basemap image.
426
-     */
427
-    public function createBaseMap(): void {
428
-        $this->image   = imagecreatetruecolor($this->width, $this->height);
429
-        $startX        = floor($this->centerX - ($this->width / $this->tileSize) / 2);
430
-        $startY        = floor($this->centerY - ($this->height / $this->tileSize) / 2);
431
-        $endX          = ceil($this->centerX + ($this->width / $this->tileSize) / 2);
432
-        $endY          = ceil($this->centerY + ($this->height / $this->tileSize) / 2);
433
-        $this->offsetX = -floor(($this->centerX - floor($this->centerX)) * $this->tileSize);
434
-        $this->offsetY = -floor(($this->centerY - floor($this->centerY)) * $this->tileSize);
435
-        $this->offsetX += floor($this->width / 2);
436
-        $this->offsetY += floor($this->height / 2);
437
-        $this->offsetX += floor($startX - floor($this->centerX)) * $this->tileSize;
438
-        $this->offsetY += floor($startY - floor($this->centerY)) * $this->tileSize;
439
-
440
-        for($x = $startX; $x <= $endX; $x++) {
441
-            for($y = $startY; $y <= $endY; $y++) {
442
-                $url = str_replace(
443
-                    array(
444
-                        '{Z}',
445
-                        '{X}',
446
-                        '{Y}'
447
-                    ), array(
448
-                        $this->zoom,
449
-                        $x,
450
-                        $y
451
-                    ), $this->tileInfo [$this->maptype] ['url']
452
-                );
453
-
454
-                $tileData = $this->fetchTile($url);
455
-                if($tileData) {
456
-                    $tileImage = imagecreatefromstring($tileData);
457
-                } else {
458
-                    $tileImage = imagecreate($this->tileSize, $this->tileSize);
459
-                    $color     = imagecolorallocate($tileImage, 255, 255, 255);
460
-                    @imagestring($tileImage, 1, 127, 127, 'err', $color);
461
-                }
462
-                $destX = ($x - $startX) * $this->tileSize + $this->offsetX;
463
-                $destY = ($y - $startY) * $this->tileSize + $this->offsetY;
464
-                dbglog($this->tileSize, "imagecopy tile into image: $destX, $destY");
465
-                imagecopy(
466
-                    $this->image, $tileImage, $destX, $destY, 0, 0, $this->tileSize,
467
-                    $this->tileSize
468
-                );
469
-            }
470
-        }
471
-    }
472
-
473
-    /**
474
-     * Fetch a tile and (if configured) store it in the cache.
475
-     * @param string $url
476
-     * @return bool|string
477
-     * @todo refactor this to use dokuwiki\HTTP\HTTPClient or dokuwiki\HTTP\DokuHTTPClient
478
-     *          for better proxy handling...
479
-     */
480
-    public function fetchTile(string $url) {
481
-        if($this->useTileCache && ($cached = $this->checkTileCache($url)))
482
-            return $cached;
483
-
484
-        $_UA = 'Mozilla/4.0 (compatible; DokuWikiSpatial HTTP Client; ' . PHP_OS . ')';
485
-        if(function_exists("curl_init")) {
486
-            // use cUrl
487
-            $ch = curl_init();
488
-            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
489
-            curl_setopt($ch, CURLOPT_USERAGENT, $_UA);
490
-            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
491
-            curl_setopt($ch, CURLOPT_URL, $url . $this->apikey);
492
-            dbglog("StaticMap::fetchTile: getting: $url using curl_exec");
493
-            $tile = curl_exec($ch);
494
-            curl_close($ch);
495
-        } else {
496
-            // use file_get_contents
497
-            global $conf;
498
-            $opts = array(
499
-                'http' => array(
500
-                    'method'          => "GET",
501
-                    'header'          => "Accept-language: en\r\n" . "User-Agent: $_UA\r\n" . "accept: image/png\r\n",
502
-                    'request_fulluri' => true
503
-                )
504
-            );
505
-            if(isset($conf['proxy']['host'], $conf['proxy']['port'])
506
-                && $conf['proxy']['host'] !== ''
507
-                && $conf['proxy']['port'] !== '') {
508
-                $opts['http'] += ['proxy' => "tcp://" . $conf['proxy']['host'] . ":" . $conf['proxy']['port']];
509
-            }
510
-
511
-            $context = stream_context_create($opts);
512
-            dbglog("StaticMap::fetchTile: getting: $url . $this->apikey using file_get_contents and options $opts");
513
-            $tile = file_get_contents($url . $this->apikey, false, $context);
514
-        }
515
-        if($tile && $this->useTileCache) {
516
-            $this->writeTileToCache($url, $tile);
517
-        }
518
-        return $tile;
519
-    }
520
-
521
-    /**
522
-     *
523
-     * @param string $url
524
-     * @return string|false
525
-     */
526
-    public function checkTileCache(string $url) {
527
-        $filename = $this->tileUrlToFilename($url);
528
-        if(file_exists($filename)) {
529
-            return file_get_contents($filename);
530
-        }
531
-        return false;
532
-    }
533
-
534
-    /**
535
-     *
536
-     * @param string $url
537
-     * @return string
538
-     */
539
-    public function tileUrlToFilename(string $url): string {
540
-        return $this->tileCacheBaseDir . "/" . substr($url, strpos($url, '/') + 1);
541
-    }
542
-
543
-    /**
544
-     * Write a tile into the cache.
545
-     *
546
-     * @param string $url
547
-     * @param mixed  $data
548
-     */
549
-    public function writeTileToCache($url, $data): void {
550
-        $filename = $this->tileUrlToFilename($url);
551
-        $this->mkdirRecursive(dirname($filename), 0777);
552
-        file_put_contents($filename, $data);
553
-    }
554
-
555
-    /**
556
-     * Recursively create the directory.
557
-     *
558
-     * @param string $pathname
559
-     *            The directory path.
560
-     * @param int    $mode
561
-     *            File access mode. For more information on modes, read the details on the chmod manpage.
562
-     */
563
-    public function mkdirRecursive(string $pathname, int $mode): bool {
564
-        is_dir(dirname($pathname)) || $this->mkdirRecursive(dirname($pathname), $mode);
565
-        return is_dir($pathname) || mkdir($pathname, $mode) || is_dir($pathname);
566
-    }
567
-
568
-    /**
569
-     * Place markers on the map and number them in the same order as they are listed in the html.
570
-     */
571
-    public function placeMarkers(): void {
572
-        $count         = 0;
573
-        $color         = imagecolorallocate($this->image, 0, 0, 0);
574
-        $bgcolor       = imagecolorallocate($this->image, 200, 200, 200);
575
-        $markerBaseDir = __DIR__ . '/icons';
576
-        $markerImageOffsetX  = 0;
577
-        $markerImageOffsetY  = 0;
578
-        $markerShadowOffsetX = 0;
579
-        $markerShadowOffsetY = 0;
580
-        $markerShadowImg     = null;
581
-        // loop thru marker array
582
-        foreach($this->markers as $marker) {
583
-            // set some local variables
584
-            $markerLat  = $marker ['lat'];
585
-            $markerLon  = $marker ['lon'];
586
-            $markerType = $marker ['type'];
587
-            // clear variables from previous loops
588
-            $markerFilename = '';
589
-            $markerShadow   = '';
590
-            $matches        = false;
591
-            // check for marker type, get settings from markerPrototypes
592
-            if($markerType) {
593
-                foreach($this->markerPrototypes as $markerPrototype) {
594
-                    if(preg_match($markerPrototype ['regex'], $markerType, $matches)) {
595
-                        $markerFilename = $matches [0] . $markerPrototype ['extension'];
596
-                        if($markerPrototype ['offsetImage']) {
597
-                            list ($markerImageOffsetX, $markerImageOffsetY) = explode(
598
-                                ",",
599
-                                $markerPrototype ['offsetImage']
600
-                            );
601
-                        }
602
-                        $markerShadow = $markerPrototype ['shadow'];
603
-                        if($markerShadow) {
604
-                            list ($markerShadowOffsetX, $markerShadowOffsetY) = explode(
605
-                                ",",
606
-                                $markerPrototype ['offsetShadow']
607
-                            );
608
-                        }
609
-                    }
610
-                }
611
-            }
612
-            // create img resource
613
-            if(file_exists($markerBaseDir . '/' . $markerFilename)) {
614
-                $markerImg = imagecreatefrompng($markerBaseDir . '/' . $markerFilename);
615
-            } else {
616
-                $markerImg = imagecreatefrompng($markerBaseDir . '/marker.png');
617
-            }
618
-            // check for shadow + create shadow recource
619
-            if($markerShadow && file_exists($markerBaseDir . '/' . $markerShadow)) {
620
-                $markerShadowImg = imagecreatefrompng($markerBaseDir . '/' . $markerShadow);
621
-            }
622
-            // calc position
623
-            $destX = floor(
624
-                ($this->width / 2) -
625
-                $this->tileSize * ($this->centerX - $this->lonToTile($markerLon, $this->zoom))
626
-            );
627
-            $destY = floor(
628
-                ($this->height / 2) -
629
-                $this->tileSize * ($this->centerY - $this->latToTile($markerLat, $this->zoom))
630
-            );
631
-            // copy shadow on basemap
632
-            if($markerShadow && $markerShadowImg) {
633
-                imagecopy(
634
-                    $this->image,
635
-                    $markerShadowImg,
636
-                    $destX + (int) $markerShadowOffsetX,
637
-                    $destY + (int) $markerShadowOffsetY,
638
-                    0,
639
-                    0,
640
-                    imagesx($markerShadowImg),
641
-                    imagesy($markerShadowImg)
642
-                );
643
-            }
644
-            // copy marker on basemap above shadow
645
-            imagecopy(
646
-                $this->image,
647
-                $markerImg,
648
-                $destX + (int) $markerImageOffsetX,
649
-                $destY + (int) $markerImageOffsetY,
650
-                0,
651
-                0,
652
-                imagesx($markerImg),
653
-                imagesy($markerImg)
654
-            );
655
-            // add label
656
-            imagestring(
657
-                $this->image,
658
-                3,
659
-                $destX - imagesx($markerImg) + 1,
660
-                $destY + (int) $markerImageOffsetY + 1,
661
-                ++$count,
662
-                $bgcolor
663
-            );
664
-            imagestring(
665
-                $this->image,
666
-                3,
667
-                $destX - imagesx($markerImg),
668
-                $destY + (int) $markerImageOffsetY,
669
-                $count,
670
-                $color
671
-            );
672
-        }
673
-    }
674
-
675
-    /**
676
-     * Draw kml trace on the map.
677
-     * @throws exception when loading the KML fails
678
-     */
679
-    public function drawKML(): void {
680
-        // TODO get colour from kml node (not currently supported in geoPHP)
681
-        $col     = imagecolorallocatealpha($this->image, 255, 0, 0, .4 * 127);
682
-        $kmlgeom = geoPHP::load(file_get_contents($this->kmlFileName), 'kml');
683
-        $this->drawGeometry($kmlgeom, $col);
684
-    }
685
-
686
-    /**
687
-     * Draw geometry or geometry collection on the map.
688
-     *
689
-     * @param Geometry $geom
690
-     * @param int      $colour
691
-     *            drawing colour
692
-     */
693
-    private function drawGeometry(Geometry $geom, int $colour): void {
694
-        if(empty($geom)) {
695
-            return;
696
-        }
697
-
698
-        switch($geom->geometryType()) {
699
-            case 'GeometryCollection' :
700
-                // recursively draw part of the collection
701
-                for($i = 1; $i < $geom->numGeometries() + 1; $i++) {
702
-                    $_geom = $geom->geometryN($i);
703
-                    $this->drawGeometry($_geom, $colour);
704
-                }
705
-                break;
706
-            case 'MultiPolygon' :
707
-            case 'MultiLineString' :
708
-            case 'MultiPoint' :
709
-                // TODO implement / do nothing
710
-                break;
711
-            case 'Polygon' :
712
-                $this->drawPolygon($geom, $colour);
713
-                break;
714
-            case 'LineString' :
715
-                $this->drawLineString($geom, $colour);
716
-                break;
717
-            case 'Point' :
718
-                $this->drawPoint($geom, $colour);
719
-                break;
720
-            default :
721
-                // draw nothing
722
-                break;
723
-        }
724
-    }
725
-
726
-    /**
727
-     * Draw a polygon on the map.
728
-     *
729
-     * @param Polygon $polygon
730
-     * @param int     $colour
731
-     *            drawing colour
732
-     */
733
-    private function drawPolygon($polygon, int $colour) {
734
-        // TODO implementation of drawing holes,
735
-        // maybe draw the polygon to an in-memory image and use imagecopy, draw polygon in col., draw holes in bgcol?
736
-
737
-        // print_r('Polygon:<br />');
738
-        // print_r($polygon);
739
-        $extPoints = array();
740
-        // extring is a linestring actually..
741
-        $extRing = $polygon->exteriorRing();
742
-
743
-        for($i = 1; $i < $extRing->numGeometries(); $i++) {
744
-            $p1           = $extRing->geometryN($i);
745
-            $x            = floor(
746
-                ($this->width / 2) - $this->tileSize * ($this->centerX - $this->lonToTile($p1->x(), $this->zoom))
747
-            );
748
-            $y            = floor(
749
-                ($this->height / 2) - $this->tileSize * ($this->centerY - $this->latToTile($p1->y(), $this->zoom))
750
-            );
751
-            $extPoints [] = $x;
752
-            $extPoints [] = $y;
753
-        }
754
-        // print_r('points:('.($i-1).')<br />');
755
-        // print_r($extPoints);
756
-        // imagepolygon ($this->image, $extPoints, $i-1, $colour );
757
-        imagefilledpolygon($this->image, $extPoints, $i - 1, $colour);
758
-    }
759
-
760
-    /**
761
-     * Draw a line on the map.
762
-     *
763
-     * @param LineString $line
764
-     * @param int        $colour
765
-     *            drawing colour
766
-     */
767
-    private function drawLineString($line, $colour) {
768
-        imagesetthickness($this->image, 2);
769
-        for($p = 1; $p < $line->numGeometries(); $p++) {
770
-            // get first pair of points
771
-            $p1 = $line->geometryN($p);
772
-            $p2 = $line->geometryN($p + 1);
773
-            // translate to paper space
774
-            $x1 = floor(
775
-                ($this->width / 2) - $this->tileSize * ($this->centerX - $this->lonToTile($p1->x(), $this->zoom))
776
-            );
777
-            $y1 = floor(
778
-                ($this->height / 2) - $this->tileSize * ($this->centerY - $this->latToTile($p1->y(), $this->zoom))
779
-            );
780
-            $x2 = floor(
781
-                ($this->width / 2) - $this->tileSize * ($this->centerX - $this->lonToTile($p2->x(), $this->zoom))
782
-            );
783
-            $y2 = floor(
784
-                ($this->height / 2) - $this->tileSize * ($this->centerY - $this->latToTile($p2->y(), $this->zoom))
785
-            );
786
-            // draw to image
787
-            imageline($this->image, $x1, $y1, $x2, $y2, $colour);
788
-        }
789
-        imagesetthickness($this->image, 1);
790
-    }
791
-
792
-    /**
793
-     * Draw a point on the map.
794
-     *
795
-     * @param Point $point
796
-     * @param int   $colour
797
-     *            drawing colour
798
-     */
799
-    private function drawPoint($point, $colour) {
800
-        imagesetthickness($this->image, 2);
801
-        // translate to paper space
802
-        $cx = floor(
803
-            ($this->width / 2) - $this->tileSize * ($this->centerX - $this->lonToTile($point->x(), $this->zoom))
804
-        );
805
-        $cy = floor(
806
-            ($this->height / 2) - $this->tileSize * ($this->centerY - $this->latToTile($point->y(), $this->zoom))
807
-        );
808
-        $r  = 5;
809
-        // draw to image
810
-        // imageellipse($this->image, $cx, $cy,$r, $r, $colour);
811
-        imagefilledellipse($this->image, $cx, $cy, $r, $r, $colour);
812
-        // don't use imageellipse because the imagesetthickness function has
813
-        // no effect. So the better workaround is to use imagearc.
814
-        imagearc($this->image, $cx, $cy, $r, $r, 0, 359, $colour);
815
-        imagesetthickness($this->image, 1);
816
-    }
817
-
818
-    /**
819
-     * Draw gpx trace on the map.
820
-     * @throws exception when loading the GPX fails
821
-     */
822
-    public function drawGPX() {
823
-        $col     = imagecolorallocatealpha($this->image, 0, 0, 255, .4 * 127);
824
-        $gpxgeom = geoPHP::load(file_get_contents($this->gpxFileName), 'gpx');
825
-        $this->drawGeometry($gpxgeom, $col);
826
-    }
827
-
828
-    /**
829
-     * Draw geojson on the map.
830
-     * @throws exception when loading the JSON fails
831
-     */
832
-    public function drawGeojson() {
833
-        $col     = imagecolorallocatealpha($this->image, 255, 0, 255, .4 * 127);
834
-        $gpxgeom = geoPHP::load(file_get_contents($this->geojsonFileName), 'json');
835
-        $this->drawGeometry($gpxgeom, $col);
836
-    }
837
-
838
-    /**
839
-     * add copyright and origin notice and icons to the map.
840
-     */
841
-    public function drawCopyright() {
842
-        $logoBaseDir = dirname(__FILE__) . '/' . 'logo/';
843
-        $logoImg     = imagecreatefrompng($logoBaseDir . $this->tileInfo ['openstreetmap'] ['logo']);
844
-        $textcolor   = imagecolorallocate($this->image, 0, 0, 0);
845
-        $bgcolor     = imagecolorallocate($this->image, 200, 200, 200);
846
-
847
-        imagecopy(
848
-            $this->image,
849
-            $logoImg,
850
-            0,
851
-            imagesy($this->image) - imagesy($logoImg),
852
-            0,
853
-            0,
854
-            imagesx($logoImg),
855
-            imagesy($logoImg)
856
-        );
857
-        imagestring(
858
-            $this->image,
859
-            1,
860
-            imagesx($logoImg) + 2,
861
-            imagesy($this->image) - imagesy($logoImg) + 1,
862
-            $this->tileInfo ['openstreetmap'] ['txt'],
863
-            $bgcolor
864
-        );
865
-        imagestring(
866
-            $this->image,
867
-            1,
868
-            imagesx($logoImg) + 1,
869
-            imagesy($this->image) - imagesy($logoImg),
870
-            $this->tileInfo ['openstreetmap'] ['txt'],
871
-            $textcolor
872
-        );
873
-
874
-        // additional tile source info, ie. who created/hosted the tiles
875
-        $xIconOffset = 0;
876
-        if($this->maptype === 'openstreetmap') {
877
-            $mapAuthor = "(c) OpenStreetMap maps/CC BY-SA";
878
-        } else {
879
-            $mapAuthor   = $this->tileInfo [$this->maptype] ['txt'];
880
-            $iconImg     = imagecreatefrompng($logoBaseDir . $this->tileInfo [$this->maptype] ['logo']);
881
-            $xIconOffset = imagesx($iconImg);
882
-            imagecopy(
883
-                $this->image,
884
-                $iconImg, imagesx($logoImg) + 1,
885
-                imagesy($this->image) - imagesy($iconImg),
886
-                0,
887
-                0,
888
-                imagesx($iconImg), imagesy($iconImg)
889
-            );
890
-        }
891
-        imagestring(
892
-            $this->image,
893
-            1, imagesx($logoImg) + $xIconOffset + 4,
894
-            imagesy($this->image) - ceil(imagesy($logoImg) / 2) + 1,
895
-            $mapAuthor,
896
-            $bgcolor
897
-        );
898
-        imagestring(
899
-            $this->image,
900
-            1, imagesx($logoImg) + $xIconOffset + 3,
901
-            imagesy($this->image) - ceil(imagesy($logoImg) / 2),
902
-            $mapAuthor,
903
-            $textcolor
904
-        );
905
-
906
-    }
41
+	// the final output
42
+	private $tileSize = 256;
43
+	private $tileInfo = array(
44
+		// OSM sources
45
+		'openstreetmap' => array(
46
+			'txt'  => '(c) OpenStreetMap data/ODbl',
47
+			'logo' => 'osm_logo.png',
48
+			'url'  => 'https://tile.openstreetmap.org/{Z}/{X}/{Y}.png'
49
+		),
50
+		// OCM sources
51
+		'cycle'         => array(
52
+			'txt'  => '(c) Thunderforest maps',
53
+			'logo' => 'tf_logo.png',
54
+			'url'  => 'https://tile.thunderforest.com/cycle/{Z}/{X}/{Y}.png'
55
+		),
56
+		'transport'     => array(
57
+			'txt'  => '(c) Thunderforest maps',
58
+			'logo' => 'tf_logo.png',
59
+			'url'  => 'https://tile.thunderforest.com/transport/{Z}/{X}/{Y}.png'
60
+		),
61
+		'landscape'     => array(
62
+			'txt'  => '(c) Thunderforest maps',
63
+			'logo' => 'tf_logo.png',
64
+			'url'  => 'https://tile.thunderforest.com/landscape/{Z}/{X}/{Y}.png'
65
+		),
66
+		'outdoors'      => array(
67
+			'txt'  => '(c) Thunderforest maps',
68
+			'logo' => 'tf_logo.png',
69
+			'url'  => 'https://tile.thunderforest.com/outdoors/{Z}/{X}/{Y}.png'
70
+		),
71
+		'toner-lite'    => array(
72
+			'txt'  => 'Stamen tiles',
73
+			'logo' => 'stamen.png',
74
+			'url'  => 'https://stamen-tiles.a.ssl.fastly.net/toner/{Z}/{X}/{Y}.png'
75
+		),
76
+		'terrain'       => array(
77
+			'txt'  => 'Stamen tiles',
78
+			'logo' => 'stamen.png',
79
+			'url'  => 'https://stamen-tiles.a.ssl.fastly.net/terrain/{Z}/{X}/{Y}.jpg'
80
+		)
81
+		//,
82
+		// 'piste'=>array(
83
+		// 'txt'=>'OpenPisteMap tiles',
84
+		// 'logo'=>'piste_logo.png',
85
+		// 'url'=>''),
86
+		// 'sea'=>array(
87
+		// 'txt'=>'OpenSeaMap tiles',
88
+		// 'logo'=>'sea_logo.png',
89
+		// 'url'=>''),
90
+		// H&B sources
91
+		//          'hikeandbike' => array (
92
+		//                  'txt' => 'Hike & Bike Map',
93
+		//                  'logo' => 'hnb_logo.png',
94
+		//                  //'url' => 'http://toolserver.org/tiles/hikebike/{Z}/{X}/{Y}.png'
95
+		//                  //moved to: https://www.toolserver.org/tiles/hikebike/12/2105/1388.png
96
+		//                  'url' => 'http://c.tiles.wmflabs.org/hikebike/{Z}/{X}/{Y}.png'
97
+		//          )
98
+	);
99
+	private $tileDefaultSrc = 'openstreetmap';
100
+
101
+	// set up markers
102
+	private $markerPrototypes = array(
103
+		// found at http://www.mapito.net/map-marker-icons.html
104
+		// these are 17x19 px with a pointer at the bottom left
105
+		'lightblue' => array(
106
+			'regex'        => '/^lightblue([0-9]+)$/',
107
+			'extension'    => '.png',
108
+			'shadow'       => false,
109
+			'offsetImage'  => '0,-19',
110
+			'offsetShadow' => false
111
+		),
112
+		// openlayers std markers are 21x25px with shadow
113
+		'ol-marker' => array(
114
+			'regex'        => '/^marker(|-blue|-gold|-green|-red)+$/',
115
+			'extension'    => '.png',
116
+			'shadow'       => 'marker_shadow.png',
117
+			'offsetImage'  => '-10,-25',
118
+			'offsetShadow' => '-1,-13'
119
+		),
120
+		// these are 16x16 px
121
+		'ww_icon'   => array(
122
+			'regex'        => '/ww_\S+$/',
123
+			'extension'    => '.png',
124
+			'shadow'       => false,
125
+			'offsetImage'  => '-8,-8',
126
+			'offsetShadow' => false
127
+		),
128
+		// assume these are 16x16 px
129
+		'rest'      => array(
130
+			'regex'        => '/^(?!lightblue([0-9]+)$)(?!(ww_\S+$))(?!marker(|-blue|-gold|-green|-red)+$)(.*)/',
131
+			'extension'    => '.png',
132
+			'shadow'       => 'marker_shadow.png',
133
+			'offsetImage'  => '-8,-8',
134
+			'offsetShadow' => '-1,-1'
135
+		)
136
+	);
137
+	private $centerX;
138
+	private $centerY;
139
+	private $offsetX;
140
+	private $offsetY;
141
+	private $image;
142
+	private $zoom;
143
+	private $lat;
144
+	private $lon;
145
+	private $width;
146
+	private $height;
147
+	private $markers;
148
+	private $maptype;
149
+	private $kmlFileName;
150
+	private $gpxFileName;
151
+	private $geojsonFileName;
152
+	private $autoZoomExtent;
153
+	private $apikey;
154
+	private $tileCacheBaseDir;
155
+	private $mapCacheBaseDir;
156
+	private $mediaBaseDir;
157
+	private $useTileCache;
158
+	private $mapCacheID = '';
159
+	private $mapCacheFile = '';
160
+	private $mapCacheExtension = 'png';
161
+
162
+	/**
163
+	 * Constructor.
164
+	 *
165
+	 * @param float  $lat
166
+	 *            Latitude (x) of center of map
167
+	 * @param float  $lon
168
+	 *            Longitude (y) of center of map
169
+	 * @param int    $zoom
170
+	 *            Zoomlevel
171
+	 * @param int    $width
172
+	 *            Width in pixels
173
+	 * @param int    $height
174
+	 *            Height in pixels
175
+	 * @param string $maptype
176
+	 *            Name of the map
177
+	 * @param array  $markers
178
+	 *            array of markers
179
+	 * @param string $gpx
180
+	 *            GPX filename
181
+	 * @param string $kml
182
+	 *            KML filename
183
+	 * @param string $geojson
184
+	 * @param string $mediaDir
185
+	 *            Directory to store/cache maps
186
+	 * @param string $tileCacheBaseDir
187
+	 *            Directory to cache map tiles
188
+	 * @param bool   $autoZoomExtent
189
+	 *            Wheter or not to override zoom/lat/lon and zoom to the extent of gpx/kml and markers
190
+	 * @param string $apikey
191
+	 */
192
+	public function __construct(
193
+		float $lat,
194
+		float $lon,
195
+		int $zoom,
196
+		int $width,
197
+		int $height,
198
+		string $maptype,
199
+		array $markers,
200
+		string $gpx,
201
+		string $kml,
202
+		string $geojson,
203
+		string $mediaDir,
204
+		string $tileCacheBaseDir,
205
+		bool $autoZoomExtent = true,
206
+		string $apikey = ''
207
+	) {
208
+		$this->zoom   = $zoom;
209
+		$this->lat    = $lat;
210
+		$this->lon    = $lon;
211
+		$this->width  = $width;
212
+		$this->height = $height;
213
+		// validate + set maptype
214
+		$this->maptype = $this->tileDefaultSrc;
215
+		if(array_key_exists($maptype, $this->tileInfo)) {
216
+			$this->maptype = $maptype;
217
+		}
218
+		$this->markers          = $markers;
219
+		$this->kmlFileName      = $kml;
220
+		$this->gpxFileName      = $gpx;
221
+		$this->geojsonFileName  = $geojson;
222
+		$this->mediaBaseDir     = $mediaDir;
223
+		$this->tileCacheBaseDir = $tileCacheBaseDir . '/olmaptiles';
224
+		$this->useTileCache     = $this->tileCacheBaseDir !== '';
225
+		$this->mapCacheBaseDir  = $mediaDir . '/olmapmaps';
226
+		$this->autoZoomExtent   = $autoZoomExtent;
227
+		$this->apikey           = $apikey;
228
+	}
229
+
230
+	/**
231
+	 * get the map, this may return a reference to a cached copy.
232
+	 *
233
+	 * @return string url relative to media dir
234
+	 */
235
+	public function getMap(): string {
236
+		try {
237
+			if($this->autoZoomExtent) {
238
+				$this->autoZoom();
239
+			}
240
+		} catch(Exception $e) {
241
+			dbglog($e);
242
+		}
243
+
244
+		// use map cache, so check cache for map
245
+		if(!$this->checkMapCache()) {
246
+			// map is not in cache, needs to be build
247
+			$this->makeMap();
248
+			$this->mkdirRecursive(dirname($this->mapCacheIDToFilename()), 0777);
249
+			imagepng($this->image, $this->mapCacheIDToFilename(), 9);
250
+		}
251
+		$doc = $this->mapCacheIDToFilename();
252
+		// make url relative to media dir
253
+		return str_replace($this->mediaBaseDir, '', $doc);
254
+	}
255
+
256
+	/**
257
+	 * Calculate the lat/lon/zoom values to make sure that all of the markers and gpx/kml are on the map.
258
+	 *
259
+	 * @param float $paddingFactor
260
+	 *            buffer constant to enlarge (>1.0) the zoom level
261
+	 * @throws Exception if non-geometries are found in the collection
262
+	 */
263
+	private function autoZoom(float $paddingFactor = 1.0): void {
264
+		$geoms    = array();
265
+		$geoms [] = new Point ($this->lon, $this->lat);
266
+		if(!empty ($this->markers)) {
267
+			foreach($this->markers as $marker) {
268
+				$geoms [] = new Point ($marker ['lon'], $marker ['lat']);
269
+			}
270
+		}
271
+		if(file_exists($this->kmlFileName)) {
272
+			$g = geoPHP::load(file_get_contents($this->kmlFileName), 'kml');
273
+			if($g !== false) {
274
+				$geoms [] = $g;
275
+			}
276
+		}
277
+		if(file_exists($this->gpxFileName)) {
278
+			$g = geoPHP::load(file_get_contents($this->gpxFileName), 'gpx');
279
+			if($g !== false) {
280
+				$geoms [] = $g;
281
+			}
282
+		}
283
+		if(file_exists($this->geojsonFileName)) {
284
+			$g = geoPHP::load(file_get_contents($this->geojsonFileName), 'geojson');
285
+			if($g !== false) {
286
+				$geoms [] = $g;
287
+			}
288
+		}
289
+
290
+		if(count($geoms) <= 1) {
291
+			dbglog($geoms, "StaticMap::autoZoom: Skip setting autozoom options");
292
+			return;
293
+		}
294
+
295
+		$geom     = new GeometryCollection ($geoms);
296
+		$centroid = $geom->centroid();
297
+		$bbox     = $geom->getBBox();
298
+
299
+		// determine vertical resolution, this depends on the distance from the equator
300
+		// $vy00 = log(tan(M_PI*(0.25 + $centroid->getY()/360)));
301
+		$vy0 = log(tan(M_PI * (0.25 + $bbox ['miny'] / 360)));
302
+		$vy1 = log(tan(M_PI * (0.25 + $bbox ['maxy'] / 360)));
303
+		dbglog("StaticMap::autoZoom: vertical resolution: $vy0, $vy1");
304
+		if ($vy1 - $vy0 === 0.0){
305
+			$resolutionVertical = 0;
306
+			dbglog("StaticMap::autoZoom: using $resolutionVertical");
307
+		} else {
308
+			$zoomFactorPowered  = ($this->height / 2) / (40.7436654315252 * ($vy1 - $vy0));
309
+			$resolutionVertical = 360 / ($zoomFactorPowered * $this->tileSize);
310
+		}
311
+		// determine horizontal resolution
312
+		$resolutionHorizontal = ($bbox ['maxx'] - $bbox ['minx']) / $this->width;
313
+		dbglog("StaticMap::autoZoom: using $resolutionHorizontal");
314
+		$resolution           = max($resolutionHorizontal, $resolutionVertical) * $paddingFactor;
315
+		$zoom                 = $this->zoom;
316
+		if ($resolution > 0){
317
+			$zoom             = log(360 / ($resolution * $this->tileSize), 2);
318
+		}
319
+
320
+		if(is_finite($zoom) && $zoom < 15 && $zoom > 2) {
321
+			$this->zoom = floor($zoom);
322
+		}
323
+		$this->lon = $centroid->getX();
324
+		$this->lat = $centroid->getY();
325
+		dbglog("StaticMap::autoZoom: Set autozoom options to: z: $this->zoom, lon: $this->lon, lat: $this->lat");
326
+	}
327
+
328
+	public function checkMapCache(): bool {
329
+		// side effect: set the mapCacheID
330
+		$this->mapCacheID = md5($this->serializeParams());
331
+		$filename         = $this->mapCacheIDToFilename();
332
+		return file_exists($filename);
333
+	}
334
+
335
+	public function serializeParams(): string {
336
+		return implode(
337
+			"&", array(
338
+				   $this->zoom,
339
+				   $this->lat,
340
+				   $this->lon,
341
+				   $this->width,
342
+				   $this->height,
343
+				   serialize($this->markers),
344
+				   $this->maptype,
345
+				   $this->kmlFileName,
346
+				   $this->gpxFileName,
347
+				   $this->geojsonFileName
348
+			   )
349
+		);
350
+	}
351
+
352
+	public function mapCacheIDToFilename(): string {
353
+		if(!$this->mapCacheFile) {
354
+			$this->mapCacheFile = $this->mapCacheBaseDir . "/" . $this->maptype . "/" . $this->zoom . "/cache_"
355
+				. substr($this->mapCacheID, 0, 2) . "/" . substr($this->mapCacheID, 2, 2)
356
+				. "/" . substr($this->mapCacheID, 4);
357
+		}
358
+		return $this->mapCacheFile . "." . $this->mapCacheExtension;
359
+	}
360
+
361
+	/**
362
+	 * make the map.
363
+	 */
364
+	public function makeMap(): void {
365
+		$this->initCoords();
366
+		$this->createBaseMap();
367
+		if(!empty ($this->markers)) {
368
+			$this->placeMarkers();
369
+		}
370
+		if (file_exists($this->kmlFileName)) {
371
+			try {
372
+				$this->drawKML();
373
+			} catch (exception $e) {
374
+				dbglog('failed to load KML file', $e);
375
+			}
376
+		}
377
+		if (file_exists($this->gpxFileName)) {
378
+			try {
379
+				$this->drawGPX();
380
+			} catch (exception $e) {
381
+				dbglog('failed to load GPX file', $e);
382
+			}
383
+		}
384
+		if (file_exists($this->geojsonFileName)) {
385
+			try {
386
+				$this->drawGeojson();
387
+			} catch (exception $e) {
388
+				dbglog('failed to load GeoJSON file', $e);
389
+			}
390
+		}
391
+
392
+		$this->drawCopyright();
393
+	}
394
+
395
+	/**
396
+	 */
397
+	public function initCoords(): void {
398
+		$this->centerX = $this->lonToTile($this->lon, $this->zoom);
399
+		$this->centerY = $this->latToTile($this->lat, $this->zoom);
400
+		$this->offsetX = floor((floor($this->centerX) - $this->centerX) * $this->tileSize);
401
+		$this->offsetY = floor((floor($this->centerY) - $this->centerY) * $this->tileSize);
402
+	}
403
+
404
+	/**
405
+	 *
406
+	 * @param float $long
407
+	 * @param int   $zoom
408
+	 * @return float|int
409
+	 */
410
+	public function lonToTile(float $long, int $zoom) {
411
+		return (($long + 180) / 360) * pow(2, $zoom);
412
+	}
413
+
414
+	/**
415
+	 *
416
+	 * @param float $lat
417
+	 * @param int   $zoom
418
+	 * @return float|int
419
+	 */
420
+	public function latToTile(float $lat, int $zoom) {
421
+		return (1 - log(tan($lat * pi() / 180) + 1 / cos($lat * M_PI / 180)) / M_PI) / 2 * pow(2, $zoom);
422
+	}
423
+
424
+	/**
425
+	 * make basemap image.
426
+	 */
427
+	public function createBaseMap(): void {
428
+		$this->image   = imagecreatetruecolor($this->width, $this->height);
429
+		$startX        = floor($this->centerX - ($this->width / $this->tileSize) / 2);
430
+		$startY        = floor($this->centerY - ($this->height / $this->tileSize) / 2);
431
+		$endX          = ceil($this->centerX + ($this->width / $this->tileSize) / 2);
432
+		$endY          = ceil($this->centerY + ($this->height / $this->tileSize) / 2);
433
+		$this->offsetX = -floor(($this->centerX - floor($this->centerX)) * $this->tileSize);
434
+		$this->offsetY = -floor(($this->centerY - floor($this->centerY)) * $this->tileSize);
435
+		$this->offsetX += floor($this->width / 2);
436
+		$this->offsetY += floor($this->height / 2);
437
+		$this->offsetX += floor($startX - floor($this->centerX)) * $this->tileSize;
438
+		$this->offsetY += floor($startY - floor($this->centerY)) * $this->tileSize;
439
+
440
+		for($x = $startX; $x <= $endX; $x++) {
441
+			for($y = $startY; $y <= $endY; $y++) {
442
+				$url = str_replace(
443
+					array(
444
+						'{Z}',
445
+						'{X}',
446
+						'{Y}'
447
+					), array(
448
+						$this->zoom,
449
+						$x,
450
+						$y
451
+					), $this->tileInfo [$this->maptype] ['url']
452
+				);
453
+
454
+				$tileData = $this->fetchTile($url);
455
+				if($tileData) {
456
+					$tileImage = imagecreatefromstring($tileData);
457
+				} else {
458
+					$tileImage = imagecreate($this->tileSize, $this->tileSize);
459
+					$color     = imagecolorallocate($tileImage, 255, 255, 255);
460
+					@imagestring($tileImage, 1, 127, 127, 'err', $color);
461
+				}
462
+				$destX = ($x - $startX) * $this->tileSize + $this->offsetX;
463
+				$destY = ($y - $startY) * $this->tileSize + $this->offsetY;
464
+				dbglog($this->tileSize, "imagecopy tile into image: $destX, $destY");
465
+				imagecopy(
466
+					$this->image, $tileImage, $destX, $destY, 0, 0, $this->tileSize,
467
+					$this->tileSize
468
+				);
469
+			}
470
+		}
471
+	}
472
+
473
+	/**
474
+	 * Fetch a tile and (if configured) store it in the cache.
475
+	 * @param string $url
476
+	 * @return bool|string
477
+	 * @todo refactor this to use dokuwiki\HTTP\HTTPClient or dokuwiki\HTTP\DokuHTTPClient
478
+	 *          for better proxy handling...
479
+	 */
480
+	public function fetchTile(string $url) {
481
+		if($this->useTileCache && ($cached = $this->checkTileCache($url)))
482
+			return $cached;
483
+
484
+		$_UA = 'Mozilla/4.0 (compatible; DokuWikiSpatial HTTP Client; ' . PHP_OS . ')';
485
+		if(function_exists("curl_init")) {
486
+			// use cUrl
487
+			$ch = curl_init();
488
+			curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
489
+			curl_setopt($ch, CURLOPT_USERAGENT, $_UA);
490
+			curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
491
+			curl_setopt($ch, CURLOPT_URL, $url . $this->apikey);
492
+			dbglog("StaticMap::fetchTile: getting: $url using curl_exec");
493
+			$tile = curl_exec($ch);
494
+			curl_close($ch);
495
+		} else {
496
+			// use file_get_contents
497
+			global $conf;
498
+			$opts = array(
499
+				'http' => array(
500
+					'method'          => "GET",
501
+					'header'          => "Accept-language: en\r\n" . "User-Agent: $_UA\r\n" . "accept: image/png\r\n",
502
+					'request_fulluri' => true
503
+				)
504
+			);
505
+			if(isset($conf['proxy']['host'], $conf['proxy']['port'])
506
+				&& $conf['proxy']['host'] !== ''
507
+				&& $conf['proxy']['port'] !== '') {
508
+				$opts['http'] += ['proxy' => "tcp://" . $conf['proxy']['host'] . ":" . $conf['proxy']['port']];
509
+			}
510
+
511
+			$context = stream_context_create($opts);
512
+			dbglog("StaticMap::fetchTile: getting: $url . $this->apikey using file_get_contents and options $opts");
513
+			$tile = file_get_contents($url . $this->apikey, false, $context);
514
+		}
515
+		if($tile && $this->useTileCache) {
516
+			$this->writeTileToCache($url, $tile);
517
+		}
518
+		return $tile;
519
+	}
520
+
521
+	/**
522
+	 *
523
+	 * @param string $url
524
+	 * @return string|false
525
+	 */
526
+	public function checkTileCache(string $url) {
527
+		$filename = $this->tileUrlToFilename($url);
528
+		if(file_exists($filename)) {
529
+			return file_get_contents($filename);
530
+		}
531
+		return false;
532
+	}
533
+
534
+	/**
535
+	 *
536
+	 * @param string $url
537
+	 * @return string
538
+	 */
539
+	public function tileUrlToFilename(string $url): string {
540
+		return $this->tileCacheBaseDir . "/" . substr($url, strpos($url, '/') + 1);
541
+	}
542
+
543
+	/**
544
+	 * Write a tile into the cache.
545
+	 *
546
+	 * @param string $url
547
+	 * @param mixed  $data
548
+	 */
549
+	public function writeTileToCache($url, $data): void {
550
+		$filename = $this->tileUrlToFilename($url);
551
+		$this->mkdirRecursive(dirname($filename), 0777);
552
+		file_put_contents($filename, $data);
553
+	}
554
+
555
+	/**
556
+	 * Recursively create the directory.
557
+	 *
558
+	 * @param string $pathname
559
+	 *            The directory path.
560
+	 * @param int    $mode
561
+	 *            File access mode. For more information on modes, read the details on the chmod manpage.
562
+	 */
563
+	public function mkdirRecursive(string $pathname, int $mode): bool {
564
+		is_dir(dirname($pathname)) || $this->mkdirRecursive(dirname($pathname), $mode);
565
+		return is_dir($pathname) || mkdir($pathname, $mode) || is_dir($pathname);
566
+	}
567
+
568
+	/**
569
+	 * Place markers on the map and number them in the same order as they are listed in the html.
570
+	 */
571
+	public function placeMarkers(): void {
572
+		$count         = 0;
573
+		$color         = imagecolorallocate($this->image, 0, 0, 0);
574
+		$bgcolor       = imagecolorallocate($this->image, 200, 200, 200);
575
+		$markerBaseDir = __DIR__ . '/icons';
576
+		$markerImageOffsetX  = 0;
577
+		$markerImageOffsetY  = 0;
578
+		$markerShadowOffsetX = 0;
579
+		$markerShadowOffsetY = 0;
580
+		$markerShadowImg     = null;
581
+		// loop thru marker array
582
+		foreach($this->markers as $marker) {
583
+			// set some local variables
584
+			$markerLat  = $marker ['lat'];
585
+			$markerLon  = $marker ['lon'];
586
+			$markerType = $marker ['type'];
587
+			// clear variables from previous loops
588
+			$markerFilename = '';
589
+			$markerShadow   = '';
590
+			$matches        = false;
591
+			// check for marker type, get settings from markerPrototypes
592
+			if($markerType) {
593
+				foreach($this->markerPrototypes as $markerPrototype) {
594
+					if(preg_match($markerPrototype ['regex'], $markerType, $matches)) {
595
+						$markerFilename = $matches [0] . $markerPrototype ['extension'];
596
+						if($markerPrototype ['offsetImage']) {
597
+							list ($markerImageOffsetX, $markerImageOffsetY) = explode(
598
+								",",
599
+								$markerPrototype ['offsetImage']
600
+							);
601
+						}
602
+						$markerShadow = $markerPrototype ['shadow'];
603
+						if($markerShadow) {
604
+							list ($markerShadowOffsetX, $markerShadowOffsetY) = explode(
605
+								",",
606
+								$markerPrototype ['offsetShadow']
607
+							);
608
+						}
609
+					}
610
+				}
611
+			}
612
+			// create img resource
613
+			if(file_exists($markerBaseDir . '/' . $markerFilename)) {
614
+				$markerImg = imagecreatefrompng($markerBaseDir . '/' . $markerFilename);
615
+			} else {
616
+				$markerImg = imagecreatefrompng($markerBaseDir . '/marker.png');
617
+			}
618
+			// check for shadow + create shadow recource
619
+			if($markerShadow && file_exists($markerBaseDir . '/' . $markerShadow)) {
620
+				$markerShadowImg = imagecreatefrompng($markerBaseDir . '/' . $markerShadow);
621
+			}
622
+			// calc position
623
+			$destX = floor(
624
+				($this->width / 2) -
625
+				$this->tileSize * ($this->centerX - $this->lonToTile($markerLon, $this->zoom))
626
+			);
627
+			$destY = floor(
628
+				($this->height / 2) -
629
+				$this->tileSize * ($this->centerY - $this->latToTile($markerLat, $this->zoom))
630
+			);
631
+			// copy shadow on basemap
632
+			if($markerShadow && $markerShadowImg) {
633
+				imagecopy(
634
+					$this->image,
635
+					$markerShadowImg,
636
+					$destX + (int) $markerShadowOffsetX,
637
+					$destY + (int) $markerShadowOffsetY,
638
+					0,
639
+					0,
640
+					imagesx($markerShadowImg),
641
+					imagesy($markerShadowImg)
642
+				);
643
+			}
644
+			// copy marker on basemap above shadow
645
+			imagecopy(
646
+				$this->image,
647
+				$markerImg,
648
+				$destX + (int) $markerImageOffsetX,
649
+				$destY + (int) $markerImageOffsetY,
650
+				0,
651
+				0,
652
+				imagesx($markerImg),
653
+				imagesy($markerImg)
654
+			);
655
+			// add label
656
+			imagestring(
657
+				$this->image,
658
+				3,
659
+				$destX - imagesx($markerImg) + 1,
660
+				$destY + (int) $markerImageOffsetY + 1,
661
+				++$count,
662
+				$bgcolor
663
+			);
664
+			imagestring(
665
+				$this->image,
666
+				3,
667
+				$destX - imagesx($markerImg),
668
+				$destY + (int) $markerImageOffsetY,
669
+				$count,
670
+				$color
671
+			);
672
+		}
673
+	}
674
+
675
+	/**
676
+	 * Draw kml trace on the map.
677
+	 * @throws exception when loading the KML fails
678
+	 */
679
+	public function drawKML(): void {
680
+		// TODO get colour from kml node (not currently supported in geoPHP)
681
+		$col     = imagecolorallocatealpha($this->image, 255, 0, 0, .4 * 127);
682
+		$kmlgeom = geoPHP::load(file_get_contents($this->kmlFileName), 'kml');
683
+		$this->drawGeometry($kmlgeom, $col);
684
+	}
685
+
686
+	/**
687
+	 * Draw geometry or geometry collection on the map.
688
+	 *
689
+	 * @param Geometry $geom
690
+	 * @param int      $colour
691
+	 *            drawing colour
692
+	 */
693
+	private function drawGeometry(Geometry $geom, int $colour): void {
694
+		if(empty($geom)) {
695
+			return;
696
+		}
697
+
698
+		switch($geom->geometryType()) {
699
+			case 'GeometryCollection' :
700
+				// recursively draw part of the collection
701
+				for($i = 1; $i < $geom->numGeometries() + 1; $i++) {
702
+					$_geom = $geom->geometryN($i);
703
+					$this->drawGeometry($_geom, $colour);
704
+				}
705
+				break;
706
+			case 'MultiPolygon' :
707
+			case 'MultiLineString' :
708
+			case 'MultiPoint' :
709
+				// TODO implement / do nothing
710
+				break;
711
+			case 'Polygon' :
712
+				$this->drawPolygon($geom, $colour);
713
+				break;
714
+			case 'LineString' :
715
+				$this->drawLineString($geom, $colour);
716
+				break;
717
+			case 'Point' :
718
+				$this->drawPoint($geom, $colour);
719
+				break;
720
+			default :
721
+				// draw nothing
722
+				break;
723
+		}
724
+	}
725
+
726
+	/**
727
+	 * Draw a polygon on the map.
728
+	 *
729
+	 * @param Polygon $polygon
730
+	 * @param int     $colour
731
+	 *            drawing colour
732
+	 */
733
+	private function drawPolygon($polygon, int $colour) {
734
+		// TODO implementation of drawing holes,
735
+		// maybe draw the polygon to an in-memory image and use imagecopy, draw polygon in col., draw holes in bgcol?
736
+
737
+		// print_r('Polygon:<br />');
738
+		// print_r($polygon);
739
+		$extPoints = array();
740
+		// extring is a linestring actually..
741
+		$extRing = $polygon->exteriorRing();
742
+
743
+		for($i = 1; $i < $extRing->numGeometries(); $i++) {
744
+			$p1           = $extRing->geometryN($i);
745
+			$x            = floor(
746
+				($this->width / 2) - $this->tileSize * ($this->centerX - $this->lonToTile($p1->x(), $this->zoom))
747
+			);
748
+			$y            = floor(
749
+				($this->height / 2) - $this->tileSize * ($this->centerY - $this->latToTile($p1->y(), $this->zoom))
750
+			);
751
+			$extPoints [] = $x;
752
+			$extPoints [] = $y;
753
+		}
754
+		// print_r('points:('.($i-1).')<br />');
755
+		// print_r($extPoints);
756
+		// imagepolygon ($this->image, $extPoints, $i-1, $colour );
757
+		imagefilledpolygon($this->image, $extPoints, $i - 1, $colour);
758
+	}
759
+
760
+	/**
761
+	 * Draw a line on the map.
762
+	 *
763
+	 * @param LineString $line
764
+	 * @param int        $colour
765
+	 *            drawing colour
766
+	 */
767
+	private function drawLineString($line, $colour) {
768
+		imagesetthickness($this->image, 2);
769
+		for($p = 1; $p < $line->numGeometries(); $p++) {
770
+			// get first pair of points
771
+			$p1 = $line->geometryN($p);
772
+			$p2 = $line->geometryN($p + 1);
773
+			// translate to paper space
774
+			$x1 = floor(
775
+				($this->width / 2) - $this->tileSize * ($this->centerX - $this->lonToTile($p1->x(), $this->zoom))
776
+			);
777
+			$y1 = floor(
778
+				($this->height / 2) - $this->tileSize * ($this->centerY - $this->latToTile($p1->y(), $this->zoom))
779
+			);
780
+			$x2 = floor(
781
+				($this->width / 2) - $this->tileSize * ($this->centerX - $this->lonToTile($p2->x(), $this->zoom))
782
+			);
783
+			$y2 = floor(
784
+				($this->height / 2) - $this->tileSize * ($this->centerY - $this->latToTile($p2->y(), $this->zoom))
785
+			);
786
+			// draw to image
787
+			imageline($this->image, $x1, $y1, $x2, $y2, $colour);
788
+		}
789
+		imagesetthickness($this->image, 1);
790
+	}
791
+
792
+	/**
793
+	 * Draw a point on the map.
794
+	 *
795
+	 * @param Point $point
796
+	 * @param int   $colour
797
+	 *            drawing colour
798
+	 */
799
+	private function drawPoint($point, $colour) {
800
+		imagesetthickness($this->image, 2);
801
+		// translate to paper space
802
+		$cx = floor(
803
+			($this->width / 2) - $this->tileSize * ($this->centerX - $this->lonToTile($point->x(), $this->zoom))
804
+		);
805
+		$cy = floor(
806
+			($this->height / 2) - $this->tileSize * ($this->centerY - $this->latToTile($point->y(), $this->zoom))
807
+		);
808
+		$r  = 5;
809
+		// draw to image
810
+		// imageellipse($this->image, $cx, $cy,$r, $r, $colour);
811
+		imagefilledellipse($this->image, $cx, $cy, $r, $r, $colour);
812
+		// don't use imageellipse because the imagesetthickness function has
813
+		// no effect. So the better workaround is to use imagearc.
814
+		imagearc($this->image, $cx, $cy, $r, $r, 0, 359, $colour);
815
+		imagesetthickness($this->image, 1);
816
+	}
817
+
818
+	/**
819
+	 * Draw gpx trace on the map.
820
+	 * @throws exception when loading the GPX fails
821
+	 */
822
+	public function drawGPX() {
823
+		$col     = imagecolorallocatealpha($this->image, 0, 0, 255, .4 * 127);
824
+		$gpxgeom = geoPHP::load(file_get_contents($this->gpxFileName), 'gpx');
825
+		$this->drawGeometry($gpxgeom, $col);
826
+	}
827
+
828
+	/**
829
+	 * Draw geojson on the map.
830
+	 * @throws exception when loading the JSON fails
831
+	 */
832
+	public function drawGeojson() {
833
+		$col     = imagecolorallocatealpha($this->image, 255, 0, 255, .4 * 127);
834
+		$gpxgeom = geoPHP::load(file_get_contents($this->geojsonFileName), 'json');
835
+		$this->drawGeometry($gpxgeom, $col);
836
+	}
837
+
838
+	/**
839
+	 * add copyright and origin notice and icons to the map.
840
+	 */
841
+	public function drawCopyright() {
842
+		$logoBaseDir = dirname(__FILE__) . '/' . 'logo/';
843
+		$logoImg     = imagecreatefrompng($logoBaseDir . $this->tileInfo ['openstreetmap'] ['logo']);
844
+		$textcolor   = imagecolorallocate($this->image, 0, 0, 0);
845
+		$bgcolor     = imagecolorallocate($this->image, 200, 200, 200);
846
+
847
+		imagecopy(
848
+			$this->image,
849
+			$logoImg,
850
+			0,
851
+			imagesy($this->image) - imagesy($logoImg),
852
+			0,
853
+			0,
854
+			imagesx($logoImg),
855
+			imagesy($logoImg)
856
+		);
857
+		imagestring(
858
+			$this->image,
859
+			1,
860
+			imagesx($logoImg) + 2,
861
+			imagesy($this->image) - imagesy($logoImg) + 1,
862
+			$this->tileInfo ['openstreetmap'] ['txt'],
863
+			$bgcolor
864
+		);
865
+		imagestring(
866
+			$this->image,
867
+			1,
868
+			imagesx($logoImg) + 1,
869
+			imagesy($this->image) - imagesy($logoImg),
870
+			$this->tileInfo ['openstreetmap'] ['txt'],
871
+			$textcolor
872
+		);
873
+
874
+		// additional tile source info, ie. who created/hosted the tiles
875
+		$xIconOffset = 0;
876
+		if($this->maptype === 'openstreetmap') {
877
+			$mapAuthor = "(c) OpenStreetMap maps/CC BY-SA";
878
+		} else {
879
+			$mapAuthor   = $this->tileInfo [$this->maptype] ['txt'];
880
+			$iconImg     = imagecreatefrompng($logoBaseDir . $this->tileInfo [$this->maptype] ['logo']);
881
+			$xIconOffset = imagesx($iconImg);
882
+			imagecopy(
883
+				$this->image,
884
+				$iconImg, imagesx($logoImg) + 1,
885
+				imagesy($this->image) - imagesy($iconImg),
886
+				0,
887
+				0,
888
+				imagesx($iconImg), imagesy($iconImg)
889
+			);
890
+		}
891
+		imagestring(
892
+			$this->image,
893
+			1, imagesx($logoImg) + $xIconOffset + 4,
894
+			imagesy($this->image) - ceil(imagesy($logoImg) / 2) + 1,
895
+			$mapAuthor,
896
+			$bgcolor
897
+		);
898
+		imagestring(
899
+			$this->image,
900
+			1, imagesx($logoImg) + $xIconOffset + 3,
901
+			imagesy($this->image) - ceil(imagesy($logoImg) / 2),
902
+			$mapAuthor,
903
+			$textcolor
904
+		);
905
+
906
+	}
907 907
 }
Please login to merge, or discard this patch.
Spacing   +65 added lines, -65 removed lines patch added patch discarded remove patch
@@ -28,7 +28,7 @@  discard block
 block discarded – undo
28 28
 
29 29
 // phpcs:disable PSR1.Files.SideEffects
30 30
 // TODO resolve side effect
31
-require_once __DIR__ . '/../geophp/vendor/autoload.php';
31
+require_once __DIR__.'/../geophp/vendor/autoload.php';
32 32
 
33 33
 /**
34 34
  *
@@ -212,7 +212,7 @@  discard block
 block discarded – undo
212 212
         $this->height = $height;
213 213
         // validate + set maptype
214 214
         $this->maptype = $this->tileDefaultSrc;
215
-        if(array_key_exists($maptype, $this->tileInfo)) {
215
+        if (array_key_exists($maptype, $this->tileInfo)) {
216 216
             $this->maptype = $maptype;
217 217
         }
218 218
         $this->markers          = $markers;
@@ -220,9 +220,9 @@  discard block
 block discarded – undo
220 220
         $this->gpxFileName      = $gpx;
221 221
         $this->geojsonFileName  = $geojson;
222 222
         $this->mediaBaseDir     = $mediaDir;
223
-        $this->tileCacheBaseDir = $tileCacheBaseDir . '/olmaptiles';
223
+        $this->tileCacheBaseDir = $tileCacheBaseDir.'/olmaptiles';
224 224
         $this->useTileCache     = $this->tileCacheBaseDir !== '';
225
-        $this->mapCacheBaseDir  = $mediaDir . '/olmapmaps';
225
+        $this->mapCacheBaseDir  = $mediaDir.'/olmapmaps';
226 226
         $this->autoZoomExtent   = $autoZoomExtent;
227 227
         $this->apikey           = $apikey;
228 228
     }
@@ -234,15 +234,15 @@  discard block
 block discarded – undo
234 234
      */
235 235
     public function getMap(): string {
236 236
         try {
237
-            if($this->autoZoomExtent) {
237
+            if ($this->autoZoomExtent) {
238 238
                 $this->autoZoom();
239 239
             }
240
-        } catch(Exception $e) {
240
+        } catch (Exception $e) {
241 241
             dbglog($e);
242 242
         }
243 243
 
244 244
         // use map cache, so check cache for map
245
-        if(!$this->checkMapCache()) {
245
+        if (!$this->checkMapCache()) {
246 246
             // map is not in cache, needs to be build
247 247
             $this->makeMap();
248 248
             $this->mkdirRecursive(dirname($this->mapCacheIDToFilename()), 0777);
@@ -262,37 +262,37 @@  discard block
 block discarded – undo
262 262
      */
263 263
     private function autoZoom(float $paddingFactor = 1.0): void {
264 264
         $geoms    = array();
265
-        $geoms [] = new Point ($this->lon, $this->lat);
266
-        if(!empty ($this->markers)) {
267
-            foreach($this->markers as $marker) {
268
-                $geoms [] = new Point ($marker ['lon'], $marker ['lat']);
265
+        $geoms [] = new Point($this->lon, $this->lat);
266
+        if (!empty ($this->markers)) {
267
+            foreach ($this->markers as $marker) {
268
+                $geoms [] = new Point($marker ['lon'], $marker ['lat']);
269 269
             }
270 270
         }
271
-        if(file_exists($this->kmlFileName)) {
271
+        if (file_exists($this->kmlFileName)) {
272 272
             $g = geoPHP::load(file_get_contents($this->kmlFileName), 'kml');
273
-            if($g !== false) {
273
+            if ($g !== false) {
274 274
                 $geoms [] = $g;
275 275
             }
276 276
         }
277
-        if(file_exists($this->gpxFileName)) {
277
+        if (file_exists($this->gpxFileName)) {
278 278
             $g = geoPHP::load(file_get_contents($this->gpxFileName), 'gpx');
279
-            if($g !== false) {
279
+            if ($g !== false) {
280 280
                 $geoms [] = $g;
281 281
             }
282 282
         }
283
-        if(file_exists($this->geojsonFileName)) {
283
+        if (file_exists($this->geojsonFileName)) {
284 284
             $g = geoPHP::load(file_get_contents($this->geojsonFileName), 'geojson');
285
-            if($g !== false) {
285
+            if ($g !== false) {
286 286
                 $geoms [] = $g;
287 287
             }
288 288
         }
289 289
 
290
-        if(count($geoms) <= 1) {
290
+        if (count($geoms) <= 1) {
291 291
             dbglog($geoms, "StaticMap::autoZoom: Skip setting autozoom options");
292 292
             return;
293 293
         }
294 294
 
295
-        $geom     = new GeometryCollection ($geoms);
295
+        $geom     = new GeometryCollection($geoms);
296 296
         $centroid = $geom->centroid();
297 297
         $bbox     = $geom->getBBox();
298 298
 
@@ -301,7 +301,7 @@  discard block
 block discarded – undo
301 301
         $vy0 = log(tan(M_PI * (0.25 + $bbox ['miny'] / 360)));
302 302
         $vy1 = log(tan(M_PI * (0.25 + $bbox ['maxy'] / 360)));
303 303
         dbglog("StaticMap::autoZoom: vertical resolution: $vy0, $vy1");
304
-        if ($vy1 - $vy0 === 0.0){
304
+        if ($vy1 - $vy0 === 0.0) {
305 305
             $resolutionVertical = 0;
306 306
             dbglog("StaticMap::autoZoom: using $resolutionVertical");
307 307
         } else {
@@ -313,11 +313,11 @@  discard block
 block discarded – undo
313 313
         dbglog("StaticMap::autoZoom: using $resolutionHorizontal");
314 314
         $resolution           = max($resolutionHorizontal, $resolutionVertical) * $paddingFactor;
315 315
         $zoom                 = $this->zoom;
316
-        if ($resolution > 0){
316
+        if ($resolution > 0) {
317 317
             $zoom             = log(360 / ($resolution * $this->tileSize), 2);
318 318
         }
319 319
 
320
-        if(is_finite($zoom) && $zoom < 15 && $zoom > 2) {
320
+        if (is_finite($zoom) && $zoom < 15 && $zoom > 2) {
321 321
             $this->zoom = floor($zoom);
322 322
         }
323 323
         $this->lon = $centroid->getX();
@@ -350,12 +350,12 @@  discard block
 block discarded – undo
350 350
     }
351 351
 
352 352
     public function mapCacheIDToFilename(): string {
353
-        if(!$this->mapCacheFile) {
354
-            $this->mapCacheFile = $this->mapCacheBaseDir . "/" . $this->maptype . "/" . $this->zoom . "/cache_"
355
-                . substr($this->mapCacheID, 0, 2) . "/" . substr($this->mapCacheID, 2, 2)
356
-                . "/" . substr($this->mapCacheID, 4);
353
+        if (!$this->mapCacheFile) {
354
+            $this->mapCacheFile = $this->mapCacheBaseDir."/".$this->maptype."/".$this->zoom."/cache_"
355
+                . substr($this->mapCacheID, 0, 2)."/".substr($this->mapCacheID, 2, 2)
356
+                . "/".substr($this->mapCacheID, 4);
357 357
         }
358
-        return $this->mapCacheFile . "." . $this->mapCacheExtension;
358
+        return $this->mapCacheFile.".".$this->mapCacheExtension;
359 359
     }
360 360
 
361 361
     /**
@@ -364,7 +364,7 @@  discard block
 block discarded – undo
364 364
     public function makeMap(): void {
365 365
         $this->initCoords();
366 366
         $this->createBaseMap();
367
-        if(!empty ($this->markers)) {
367
+        if (!empty ($this->markers)) {
368 368
             $this->placeMarkers();
369 369
         }
370 370
         if (file_exists($this->kmlFileName)) {
@@ -437,8 +437,8 @@  discard block
 block discarded – undo
437 437
         $this->offsetX += floor($startX - floor($this->centerX)) * $this->tileSize;
438 438
         $this->offsetY += floor($startY - floor($this->centerY)) * $this->tileSize;
439 439
 
440
-        for($x = $startX; $x <= $endX; $x++) {
441
-            for($y = $startY; $y <= $endY; $y++) {
440
+        for ($x = $startX; $x <= $endX; $x++) {
441
+            for ($y = $startY; $y <= $endY; $y++) {
442 442
                 $url = str_replace(
443 443
                     array(
444 444
                         '{Z}',
@@ -452,7 +452,7 @@  discard block
 block discarded – undo
452 452
                 );
453 453
 
454 454
                 $tileData = $this->fetchTile($url);
455
-                if($tileData) {
455
+                if ($tileData) {
456 456
                     $tileImage = imagecreatefromstring($tileData);
457 457
                 } else {
458 458
                     $tileImage = imagecreate($this->tileSize, $this->tileSize);
@@ -478,17 +478,17 @@  discard block
 block discarded – undo
478 478
      *          for better proxy handling...
479 479
      */
480 480
     public function fetchTile(string $url) {
481
-        if($this->useTileCache && ($cached = $this->checkTileCache($url)))
481
+        if ($this->useTileCache && ($cached = $this->checkTileCache($url)))
482 482
             return $cached;
483 483
 
484
-        $_UA = 'Mozilla/4.0 (compatible; DokuWikiSpatial HTTP Client; ' . PHP_OS . ')';
485
-        if(function_exists("curl_init")) {
484
+        $_UA = 'Mozilla/4.0 (compatible; DokuWikiSpatial HTTP Client; '.PHP_OS.')';
485
+        if (function_exists("curl_init")) {
486 486
             // use cUrl
487 487
             $ch = curl_init();
488 488
             curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
489 489
             curl_setopt($ch, CURLOPT_USERAGENT, $_UA);
490 490
             curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
491
-            curl_setopt($ch, CURLOPT_URL, $url . $this->apikey);
491
+            curl_setopt($ch, CURLOPT_URL, $url.$this->apikey);
492 492
             dbglog("StaticMap::fetchTile: getting: $url using curl_exec");
493 493
             $tile = curl_exec($ch);
494 494
             curl_close($ch);
@@ -498,21 +498,21 @@  discard block
 block discarded – undo
498 498
             $opts = array(
499 499
                 'http' => array(
500 500
                     'method'          => "GET",
501
-                    'header'          => "Accept-language: en\r\n" . "User-Agent: $_UA\r\n" . "accept: image/png\r\n",
501
+                    'header'          => "Accept-language: en\r\n"."User-Agent: $_UA\r\n"."accept: image/png\r\n",
502 502
                     'request_fulluri' => true
503 503
                 )
504 504
             );
505
-            if(isset($conf['proxy']['host'], $conf['proxy']['port'])
505
+            if (isset($conf['proxy']['host'], $conf['proxy']['port'])
506 506
                 && $conf['proxy']['host'] !== ''
507 507
                 && $conf['proxy']['port'] !== '') {
508
-                $opts['http'] += ['proxy' => "tcp://" . $conf['proxy']['host'] . ":" . $conf['proxy']['port']];
508
+                $opts['http'] += ['proxy' => "tcp://".$conf['proxy']['host'].":".$conf['proxy']['port']];
509 509
             }
510 510
 
511 511
             $context = stream_context_create($opts);
512 512
             dbglog("StaticMap::fetchTile: getting: $url . $this->apikey using file_get_contents and options $opts");
513
-            $tile = file_get_contents($url . $this->apikey, false, $context);
513
+            $tile = file_get_contents($url.$this->apikey, false, $context);
514 514
         }
515
-        if($tile && $this->useTileCache) {
515
+        if ($tile && $this->useTileCache) {
516 516
             $this->writeTileToCache($url, $tile);
517 517
         }
518 518
         return $tile;
@@ -525,7 +525,7 @@  discard block
 block discarded – undo
525 525
      */
526 526
     public function checkTileCache(string $url) {
527 527
         $filename = $this->tileUrlToFilename($url);
528
-        if(file_exists($filename)) {
528
+        if (file_exists($filename)) {
529 529
             return file_get_contents($filename);
530 530
         }
531 531
         return false;
@@ -537,7 +537,7 @@  discard block
 block discarded – undo
537 537
      * @return string
538 538
      */
539 539
     public function tileUrlToFilename(string $url): string {
540
-        return $this->tileCacheBaseDir . "/" . substr($url, strpos($url, '/') + 1);
540
+        return $this->tileCacheBaseDir."/".substr($url, strpos($url, '/') + 1);
541 541
     }
542 542
 
543 543
     /**
@@ -572,14 +572,14 @@  discard block
 block discarded – undo
572 572
         $count         = 0;
573 573
         $color         = imagecolorallocate($this->image, 0, 0, 0);
574 574
         $bgcolor       = imagecolorallocate($this->image, 200, 200, 200);
575
-        $markerBaseDir = __DIR__ . '/icons';
575
+        $markerBaseDir = __DIR__.'/icons';
576 576
         $markerImageOffsetX  = 0;
577 577
         $markerImageOffsetY  = 0;
578 578
         $markerShadowOffsetX = 0;
579 579
         $markerShadowOffsetY = 0;
580 580
         $markerShadowImg     = null;
581 581
         // loop thru marker array
582
-        foreach($this->markers as $marker) {
582
+        foreach ($this->markers as $marker) {
583 583
             // set some local variables
584 584
             $markerLat  = $marker ['lat'];
585 585
             $markerLon  = $marker ['lon'];
@@ -589,18 +589,18 @@  discard block
 block discarded – undo
589 589
             $markerShadow   = '';
590 590
             $matches        = false;
591 591
             // check for marker type, get settings from markerPrototypes
592
-            if($markerType) {
593
-                foreach($this->markerPrototypes as $markerPrototype) {
594
-                    if(preg_match($markerPrototype ['regex'], $markerType, $matches)) {
595
-                        $markerFilename = $matches [0] . $markerPrototype ['extension'];
596
-                        if($markerPrototype ['offsetImage']) {
592
+            if ($markerType) {
593
+                foreach ($this->markerPrototypes as $markerPrototype) {
594
+                    if (preg_match($markerPrototype ['regex'], $markerType, $matches)) {
595
+                        $markerFilename = $matches [0].$markerPrototype ['extension'];
596
+                        if ($markerPrototype ['offsetImage']) {
597 597
                             list ($markerImageOffsetX, $markerImageOffsetY) = explode(
598 598
                                 ",",
599 599
                                 $markerPrototype ['offsetImage']
600 600
                             );
601 601
                         }
602 602
                         $markerShadow = $markerPrototype ['shadow'];
603
-                        if($markerShadow) {
603
+                        if ($markerShadow) {
604 604
                             list ($markerShadowOffsetX, $markerShadowOffsetY) = explode(
605 605
                                 ",",
606 606
                                 $markerPrototype ['offsetShadow']
@@ -610,14 +610,14 @@  discard block
 block discarded – undo
610 610
                 }
611 611
             }
612 612
             // create img resource
613
-            if(file_exists($markerBaseDir . '/' . $markerFilename)) {
614
-                $markerImg = imagecreatefrompng($markerBaseDir . '/' . $markerFilename);
613
+            if (file_exists($markerBaseDir.'/'.$markerFilename)) {
614
+                $markerImg = imagecreatefrompng($markerBaseDir.'/'.$markerFilename);
615 615
             } else {
616
-                $markerImg = imagecreatefrompng($markerBaseDir . '/marker.png');
616
+                $markerImg = imagecreatefrompng($markerBaseDir.'/marker.png');
617 617
             }
618 618
             // check for shadow + create shadow recource
619
-            if($markerShadow && file_exists($markerBaseDir . '/' . $markerShadow)) {
620
-                $markerShadowImg = imagecreatefrompng($markerBaseDir . '/' . $markerShadow);
619
+            if ($markerShadow && file_exists($markerBaseDir.'/'.$markerShadow)) {
620
+                $markerShadowImg = imagecreatefrompng($markerBaseDir.'/'.$markerShadow);
621 621
             }
622 622
             // calc position
623 623
             $destX = floor(
@@ -629,7 +629,7 @@  discard block
 block discarded – undo
629 629
                 $this->tileSize * ($this->centerY - $this->latToTile($markerLat, $this->zoom))
630 630
             );
631 631
             // copy shadow on basemap
632
-            if($markerShadow && $markerShadowImg) {
632
+            if ($markerShadow && $markerShadowImg) {
633 633
                 imagecopy(
634 634
                     $this->image,
635 635
                     $markerShadowImg,
@@ -691,14 +691,14 @@  discard block
 block discarded – undo
691 691
      *            drawing colour
692 692
      */
693 693
     private function drawGeometry(Geometry $geom, int $colour): void {
694
-        if(empty($geom)) {
694
+        if (empty($geom)) {
695 695
             return;
696 696
         }
697 697
 
698
-        switch($geom->geometryType()) {
698
+        switch ($geom->geometryType()) {
699 699
             case 'GeometryCollection' :
700 700
                 // recursively draw part of the collection
701
-                for($i = 1; $i < $geom->numGeometries() + 1; $i++) {
701
+                for ($i = 1; $i < $geom->numGeometries() + 1; $i++) {
702 702
                     $_geom = $geom->geometryN($i);
703 703
                     $this->drawGeometry($_geom, $colour);
704 704
                 }
@@ -740,7 +740,7 @@  discard block
 block discarded – undo
740 740
         // extring is a linestring actually..
741 741
         $extRing = $polygon->exteriorRing();
742 742
 
743
-        for($i = 1; $i < $extRing->numGeometries(); $i++) {
743
+        for ($i = 1; $i < $extRing->numGeometries(); $i++) {
744 744
             $p1           = $extRing->geometryN($i);
745 745
             $x            = floor(
746 746
                 ($this->width / 2) - $this->tileSize * ($this->centerX - $this->lonToTile($p1->x(), $this->zoom))
@@ -766,7 +766,7 @@  discard block
 block discarded – undo
766 766
      */
767 767
     private function drawLineString($line, $colour) {
768 768
         imagesetthickness($this->image, 2);
769
-        for($p = 1; $p < $line->numGeometries(); $p++) {
769
+        for ($p = 1; $p < $line->numGeometries(); $p++) {
770 770
             // get first pair of points
771 771
             $p1 = $line->geometryN($p);
772 772
             $p2 = $line->geometryN($p + 1);
@@ -839,8 +839,8 @@  discard block
 block discarded – undo
839 839
      * add copyright and origin notice and icons to the map.
840 840
      */
841 841
     public function drawCopyright() {
842
-        $logoBaseDir = dirname(__FILE__) . '/' . 'logo/';
843
-        $logoImg     = imagecreatefrompng($logoBaseDir . $this->tileInfo ['openstreetmap'] ['logo']);
842
+        $logoBaseDir = dirname(__FILE__).'/'.'logo/';
843
+        $logoImg     = imagecreatefrompng($logoBaseDir.$this->tileInfo ['openstreetmap'] ['logo']);
844 844
         $textcolor   = imagecolorallocate($this->image, 0, 0, 0);
845 845
         $bgcolor     = imagecolorallocate($this->image, 200, 200, 200);
846 846
 
@@ -873,11 +873,11 @@  discard block
 block discarded – undo
873 873
 
874 874
         // additional tile source info, ie. who created/hosted the tiles
875 875
         $xIconOffset = 0;
876
-        if($this->maptype === 'openstreetmap') {
876
+        if ($this->maptype === 'openstreetmap') {
877 877
             $mapAuthor = "(c) OpenStreetMap maps/CC BY-SA";
878 878
         } else {
879 879
             $mapAuthor   = $this->tileInfo [$this->maptype] ['txt'];
880
-            $iconImg     = imagecreatefrompng($logoBaseDir . $this->tileInfo [$this->maptype] ['logo']);
880
+            $iconImg     = imagecreatefrompng($logoBaseDir.$this->tileInfo [$this->maptype] ['logo']);
881 881
             $xIconOffset = imagesx($iconImg);
882 882
             imagecopy(
883 883
                 $this->image,
Please login to merge, or discard this patch.