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