Completed
Push — master ( 3f2aa7...277f2b )
by Mark
25s queued 11s
created

StaticMap::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 36
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 18
c 2
b 0
f 0
dl 0
loc 36
rs 9.6666
cc 2
nc 2
nop 14

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
/*
3
 * Copyright (c) 2012-2018 Mark C. Prins <[email protected]>
4
 *
5
 * In part based on staticMapLite 0.03 available at http://staticmaplite.svn.sourceforge.net/viewvc/staticmaplite/
6
 *
7
 * Copyright (c) 2009 Gerhard Koch <gerhard.koch AT ymail.com>
8
 *
9
 * Licensed under the Apache License, Version 2.0 (the "License");
10
 * you may not use this file except in compliance with the License.
11
 * You may obtain a copy of the License at
12
 *
13
 *     http://www.apache.org/licenses/LICENSE-2.0
14
 *
15
 * Unless required by applicable law or agreed to in writing, software
16
 * distributed under the License is distributed on an "AS IS" BASIS,
17
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
 * See the License for the specific language governing permissions and
19
 * limitations under the License.
20
 */
21
22
// phpcs:disable PSR1.Files.SideEffects
23
// TODO resolve side effect
24
include_once(realpath(__DIR__) . '/../geophp/geoPHP/geoPHP.inc');
25
26
/**
27
 *
28
 * @author Mark C. Prins <[email protected]>
29
 * @author Gerhard Koch <gerhard.koch AT ymail.com>
30
 *
31
 */
32
class StaticMap {
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);
0 ignored issues
show
Bug introduced by
The function dbglog was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

234
            /** @scrutinizer ignore-call */ 
235
            dbglog($e);
Loading history...
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');
0 ignored issues
show
Bug introduced by
The type geoPHP was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
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");
0 ignored issues
show
Bug introduced by
The function dbglog was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

287
            /** @scrutinizer ignore-call */ 
288
            dbglog($geoms, "StaticMap::autoZoom: Skip setting autozoom options");
Loading history...
288
            return;
289
        }
290
291
        $geom     = new GeometryCollection ($geoms);
0 ignored issues
show
Bug introduced by
The type GeometryCollection was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
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
        $zoomFactorPowered  = ($this->height / 2) / (40.7436654315252 * ($vy1 - $vy0));
301
        $resolutionVertical = 360 / ($zoomFactorPowered * $this->tileSize);
302
        // determine horizontal resolution
303
        $resolutionHorizontal = ($bbox ['maxx'] - $bbox ['minx']) / $this->width;
304
        $resolution           = max($resolutionHorizontal, $resolutionVertical) * $paddingFactor;
305
        $zoom                 = log(360 / ($resolution * $this->tileSize), 2);
306
307
        if(is_finite($zoom) && $zoom < 15 && $zoom > 2) {
308
            $this->zoom = floor($zoom);
309
        }
310
        $this->lon = $centroid->getX();
311
        $this->lat = $centroid->getY();
312
        dbglog("StaticMap::autoZoom: Set autozoom options to: z: $this->zoom, lon: $this->lon, lat: $this->lat");
313
    }
314
315
    public function checkMapCache(): bool {
316
        // side effect: set the mapCacheID
317
        $this->mapCacheID = md5($this->serializeParams());
318
        $filename         = $this->mapCacheIDToFilename();
319
        return file_exists($filename);
320
    }
321
322
    public function serializeParams(): string {
323
        return join(
324
            "&", array(
325
                   $this->zoom,
326
                   $this->lat,
327
                   $this->lon,
328
                   $this->width,
329
                   $this->height,
330
                   serialize($this->markers),
331
                   $this->maptype,
332
                   $this->kmlFileName,
333
                   $this->gpxFileName,
334
                   $this->geojsonFileName
335
               )
336
        );
337
    }
338
339
    public function mapCacheIDToFilename(): string {
340
        if(!$this->mapCacheFile) {
341
            $this->mapCacheFile = $this->mapCacheBaseDir . "/" . $this->maptype . "/" . $this->zoom . "/cache_"
0 ignored issues
show
Bug introduced by
Are you sure $this->maptype of type mixed|string can be used in concatenation? ( Ignorable by Annotation )

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

341
            $this->mapCacheFile = $this->mapCacheBaseDir . "/" . /** @scrutinizer ignore-type */ $this->maptype . "/" . $this->zoom . "/cache_"
Loading history...
342
                . substr($this->mapCacheID, 0, 2) . "/" . substr($this->mapCacheID, 2, 2)
343
                . "/" . substr($this->mapCacheID, 4);
344
        }
345
        return $this->mapCacheFile . "." . $this->mapCacheExtension;
346
    }
347
348
    /**
349
     * make the map.
350
     */
351
    public function makeMap(): void {
352
        $this->initCoords();
353
        $this->createBaseMap();
354
        if(!empty ($this->markers))
355
            $this->placeMarkers();
356
        if(file_exists($this->kmlFileName))
357
            $this->drawKML();
358
        if(file_exists($this->gpxFileName))
359
            $this->drawGPX();
360
        if(file_exists($this->geojsonFileName))
361
            $this->drawGeojson();
362
363
        $this->drawCopyright();
364
    }
365
366
    /**
367
     */
368
    public function initCoords(): void {
369
        $this->centerX = $this->lonToTile($this->lon, $this->zoom);
370
        $this->centerY = $this->latToTile($this->lat, $this->zoom);
371
        $this->offsetX = floor((floor($this->centerX) - $this->centerX) * $this->tileSize);
372
        $this->offsetY = floor((floor($this->centerY) - $this->centerY) * $this->tileSize);
373
    }
374
375
    /**
376
     *
377
     * @param float $long
378
     * @param int   $zoom
379
     * @return float|int
380
     */
381
    public function lonToTile(float $long, int $zoom) {
382
        return (($long + 180) / 360) * pow(2, $zoom);
383
    }
384
385
    /**
386
     *
387
     * @param float $lat
388
     * @param int   $zoom
389
     * @return float|int
390
     */
391
    public function latToTile(float $lat, int $zoom) {
392
        return (1 - log(tan($lat * pi() / 180) + 1 / cos($lat * M_PI / 180)) / M_PI) / 2 * pow(2, $zoom);
393
    }
394
395
    /**
396
     * make basemap image.
397
     */
398
    public function createBaseMap(): void {
399
        $this->image   = imagecreatetruecolor($this->width, $this->height);
400
        $startX        = floor($this->centerX - ($this->width / $this->tileSize) / 2);
401
        $startY        = floor($this->centerY - ($this->height / $this->tileSize) / 2);
402
        $endX          = ceil($this->centerX + ($this->width / $this->tileSize) / 2);
403
        $endY          = ceil($this->centerY + ($this->height / $this->tileSize) / 2);
404
        $this->offsetX = -floor(($this->centerX - floor($this->centerX)) * $this->tileSize);
405
        $this->offsetY = -floor(($this->centerY - floor($this->centerY)) * $this->tileSize);
406
        $this->offsetX += floor($this->width / 2);
407
        $this->offsetY += floor($this->height / 2);
408
        $this->offsetX += floor($startX - floor($this->centerX)) * $this->tileSize;
409
        $this->offsetY += floor($startY - floor($this->centerY)) * $this->tileSize;
410
411
        for($x = $startX; $x <= $endX; $x++) {
412
            for($y = $startY; $y <= $endY; $y++) {
413
                $url = str_replace(
414
                    array(
415
                        '{Z}',
416
                        '{X}',
417
                        '{Y}'
418
                    ), array(
419
                        $this->zoom,
420
                        $x,
421
                        $y
422
                    ), $this->tileInfo [$this->maptype] ['url']
423
                );
424
425
                $tileData = $this->fetchTile($url);
426
                if($tileData) {
427
                    $tileImage = imagecreatefromstring($tileData);
0 ignored issues
show
Bug introduced by
It seems like $tileData can also be of type true; however, parameter $image of imagecreatefromstring() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

427
                    $tileImage = imagecreatefromstring(/** @scrutinizer ignore-type */ $tileData);
Loading history...
428
                } else {
429
                    $tileImage = imagecreate($this->tileSize, $this->tileSize);
430
                    $color     = imagecolorallocate($tileImage, 255, 255, 255);
431
                    @imagestring($tileImage, 1, 127, 127, 'err', $color);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for imagestring(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

431
                    /** @scrutinizer ignore-unhandled */ @imagestring($tileImage, 1, 127, 127, 'err', $color);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
432
                }
433
                $destX = ($x - $startX) * $this->tileSize + $this->offsetX;
434
                $destY = ($y - $startY) * $this->tileSize + $this->offsetY;
435
                dbglog($this->tileSize, "imagecopy tile into image: $destX, $destY");
0 ignored issues
show
Bug introduced by
The function dbglog was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

435
                /** @scrutinizer ignore-call */ 
436
                dbglog($this->tileSize, "imagecopy tile into image: $destX, $destY");
Loading history...
436
                imagecopy(
437
                    $this->image, $tileImage, $destX, $destY, 0, 0, $this->tileSize,
0 ignored issues
show
Bug introduced by
$destX of type double is incompatible with the type integer expected by parameter $dst_x of imagecopy(). ( Ignorable by Annotation )

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

437
                    $this->image, $tileImage, /** @scrutinizer ignore-type */ $destX, $destY, 0, 0, $this->tileSize,
Loading history...
Bug introduced by
$destY of type double is incompatible with the type integer expected by parameter $dst_y of imagecopy(). ( Ignorable by Annotation )

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

437
                    $this->image, $tileImage, $destX, /** @scrutinizer ignore-type */ $destY, 0, 0, $this->tileSize,
Loading history...
438
                    $this->tileSize
439
                );
440
            }
441
        }
442
    }
443
444
    /**
445
     * Fetch a tile and (if configured) store it in the cache.
446
     * @param string $url
447
     * @return bool|string
448
     * @todo refactor this to use dokuwiki\HTTP\HTTPClient or dokuwiki\HTTP\DokuHTTPClient
449
     *          for better proxy handling...
450
     */
451
    public function fetchTile(string $url) {
452
        if($this->useTileCache && ($cached = $this->checkTileCache($url)))
453
            return $cached;
454
455
        $_UA = 'Mozilla/4.0 (compatible; DokuWikiSpatial HTTP Client; ' . PHP_OS . ')';
456
        if(function_exists("curl_init")) {
457
            // use cUrl
458
            $ch = curl_init();
459
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
460
            curl_setopt($ch, CURLOPT_USERAGENT, $_UA);
461
            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
462
            curl_setopt($ch, CURLOPT_URL, $url . $this->apikey);
463
            dbglog("StaticMap::fetchTile: getting: $url using curl_exec");
0 ignored issues
show
Bug introduced by
The function dbglog was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

463
            /** @scrutinizer ignore-call */ 
464
            dbglog("StaticMap::fetchTile: getting: $url using curl_exec");
Loading history...
464
            $tile = curl_exec($ch);
465
            curl_close($ch);
466
        } else {
467
            // use file_get_contents
468
            global $conf;
469
            $opts = array(
470
                'http' => array(
471
                    'method'          => "GET",
472
                    'header'          => "Accept-language: en\r\n" . "User-Agent: $_UA\r\n" . "accept: image/png\r\n",
473
                    'request_fulluri' => true
474
                )
475
            );
476
            if(isset($conf['proxy']['host'], $conf['proxy']['port'])
477
                && $conf['proxy']['host'] !== ''
478
                && $conf['proxy']['port'] !== '') {
479
                $opts['http'] += ['proxy' => "tcp://" . $conf['proxy']['host'] . ":" . $conf['proxy']['port']];
480
            }
481
482
            $context = stream_context_create($opts);
483
            dbglog("StaticMap::fetchTile: getting: $url . $this->apikey using file_get_contents and options $opts");
484
            $tile = file_get_contents($url . $this->apikey, false, $context);
485
        }
486
        if($tile && $this->useTileCache) {
487
            $this->writeTileToCache($url, $tile);
488
        }
489
        return $tile;
490
    }
491
492
    /**
493
     *
494
     * @param string $url
495
     * @return string|false
496
     */
497
    public function checkTileCache(string $url) {
498
        $filename = $this->tileUrlToFilename($url);
499
        if(file_exists($filename)) {
500
            return file_get_contents($filename);
501
        }
502
        return false;
503
    }
504
505
    /**
506
     *
507
     * @param string $url
508
     * @return string
509
     */
510
    public function tileUrlToFilename(string $url): string {
511
        return $this->tileCacheBaseDir . "/" . substr($url, strpos($url, '/') + 1);
512
    }
513
514
    /**
515
     * Write a tile into the cache.
516
     *
517
     * @param string $url
518
     * @param mixed  $data
519
     */
520
    public function writeTileToCache($url, $data): void {
521
        $filename = $this->tileUrlToFilename($url);
522
        $this->mkdirRecursive(dirname($filename), 0777);
523
        file_put_contents($filename, $data);
524
    }
525
526
    /**
527
     * Recursively create the directory.
528
     *
529
     * @param string $pathname
530
     *            The directory path.
531
     * @param int    $mode
532
     *            File access mode. For more information on modes, read the details on the chmod manpage.
533
     */
534
    public function mkdirRecursive(string $pathname, int $mode): bool {
535
        is_dir(dirname($pathname)) || $this->mkdirRecursive(dirname($pathname), $mode);
536
        return is_dir($pathname) || mkdir($pathname, $mode) || is_dir($pathname);
537
    }
538
539
    /**
540
     * Place markers on the map and number them in the same order as they are listed in the html.
541
     */
542
    public function placeMarkers(): void {
543
        $count         = 0;
544
        $color         = imagecolorallocate($this->image, 0, 0, 0);
545
        $bgcolor       = imagecolorallocate($this->image, 200, 200, 200);
546
        $markerBaseDir = __DIR__ . '/icons';
547
        // loop thru marker array
548
        foreach($this->markers as $marker) {
549
            // set some local variables
550
            $markerLat  = $marker ['lat'];
551
            $markerLon  = $marker ['lon'];
552
            $markerType = $marker ['type'];
553
            // clear variables from previous loops
554
            $markerFilename = '';
555
            $markerShadow   = '';
556
            $matches        = false;
557
            // check for marker type, get settings from markerPrototypes
558
            if($markerType) {
559
                foreach($this->markerPrototypes as $markerPrototype) {
560
                    if(preg_match($markerPrototype ['regex'], $markerType, $matches)) {
0 ignored issues
show
Bug introduced by
$matches of type false is incompatible with the type string[] expected by parameter $matches of preg_match(). ( Ignorable by Annotation )

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

560
                    if(preg_match($markerPrototype ['regex'], $markerType, /** @scrutinizer ignore-type */ $matches)) {
Loading history...
561
                        $markerFilename = $matches [0] . $markerPrototype ['extension'];
562
                        if($markerPrototype ['offsetImage']) {
563
                            list ($markerImageOffsetX, $markerImageOffsetY) = explode(
564
                                ",",
565
                                $markerPrototype ['offsetImage']
566
                            );
567
                        }
568
                        $markerShadow = $markerPrototype ['shadow'];
569
                        if($markerShadow) {
570
                            list ($markerShadowOffsetX, $markerShadowOffsetY) = explode(
571
                                ",",
572
                                $markerPrototype ['offsetShadow']
573
                            );
574
                        }
575
                    }
576
                }
577
            }
578
            // create img resource
579
            if(file_exists($markerBaseDir . '/' . $markerFilename)) {
580
                $markerImg = imagecreatefrompng($markerBaseDir . '/' . $markerFilename);
581
            } else {
582
                $markerImg = imagecreatefrompng($markerBaseDir . '/marker.png');
583
            }
584
            // check for shadow + create shadow recource
585
            if($markerShadow && file_exists($markerBaseDir . '/' . $markerShadow)) {
586
                $markerShadowImg = imagecreatefrompng($markerBaseDir . '/' . $markerShadow);
587
            }
588
            // calc position
589
            $destX = floor(
590
                ($this->width / 2) -
591
                $this->tileSize * ($this->centerX - $this->lonToTile($markerLon, $this->zoom))
592
            );
593
            $destY = floor(
594
                ($this->height / 2) -
595
                $this->tileSize * ($this->centerY - $this->latToTile($markerLat, $this->zoom))
596
            );
597
            // copy shadow on basemap
598
            if($markerShadow && $markerShadowImg) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $markerShadowImg does not seem to be defined for all execution paths leading up to this point.
Loading history...
599
                imagecopy(
600
                    $this->image,
601
                    $markerShadowImg,
602
                    $destX + (int) $markerShadowOffsetX,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $markerShadowOffsetX does not seem to be defined for all execution paths leading up to this point.
Loading history...
Bug introduced by
$destX + (int)$markerShadowOffsetX of type double is incompatible with the type integer expected by parameter $dst_x of imagecopy(). ( Ignorable by Annotation )

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

602
                    /** @scrutinizer ignore-type */ $destX + (int) $markerShadowOffsetX,
Loading history...
603
                    $destY + (int) $markerShadowOffsetY,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $markerShadowOffsetY does not seem to be defined for all execution paths leading up to this point.
Loading history...
Bug introduced by
$destY + (int)$markerShadowOffsetY of type double is incompatible with the type integer expected by parameter $dst_y of imagecopy(). ( Ignorable by Annotation )

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

603
                    /** @scrutinizer ignore-type */ $destY + (int) $markerShadowOffsetY,
Loading history...
604
                    0,
605
                    0,
606
                    imagesx($markerShadowImg),
607
                    imagesy($markerShadowImg)
608
                );
609
            }
610
            // copy marker on basemap above shadow
611
            imagecopy(
612
                $this->image,
613
                $markerImg,
614
                $destX + (int) $markerImageOffsetX,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $markerImageOffsetX does not seem to be defined for all execution paths leading up to this point.
Loading history...
615
                $destY + (int) $markerImageOffsetY,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $markerImageOffsetY does not seem to be defined for all execution paths leading up to this point.
Loading history...
616
                0,
617
                0,
618
                imagesx($markerImg),
619
                imagesy($markerImg)
620
            );
621
            // add label
622
            imagestring(
623
                $this->image,
624
                3,
625
                $destX - imagesx($markerImg) + 1,
0 ignored issues
show
Bug introduced by
$destX - imagesx($markerImg) + 1 of type double is incompatible with the type integer expected by parameter $x of imagestring(). ( Ignorable by Annotation )

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

625
                /** @scrutinizer ignore-type */ $destX - imagesx($markerImg) + 1,
Loading history...
626
                $destY + (int) $markerImageOffsetY + 1,
0 ignored issues
show
Bug introduced by
$destY + (int)$markerImageOffsetY + 1 of type double is incompatible with the type integer expected by parameter $y of imagestring(). ( Ignorable by Annotation )

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

626
                /** @scrutinizer ignore-type */ $destY + (int) $markerImageOffsetY + 1,
Loading history...
627
                ++$count,
628
                $bgcolor
629
            );
630
            imagestring(
631
                $this->image,
632
                3,
633
                $destX - imagesx($markerImg),
634
                $destY + (int) $markerImageOffsetY,
635
                $count,
636
                $color
637
            );
638
        }
639
    }
640
641
    /**
642
     * Draw kml trace on the map.
643
     */
644
    public function drawKML(): void {
645
        // TODO get colour from kml node (not currently supported in geoPHP)
646
        $col     = imagecolorallocatealpha($this->image, 255, 0, 0, .4 * 127);
0 ignored issues
show
Bug introduced by
0.4 * 127 of type double is incompatible with the type integer expected by parameter $alpha of imagecolorallocatealpha(). ( Ignorable by Annotation )

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

646
        $col     = imagecolorallocatealpha($this->image, 255, 0, 0, /** @scrutinizer ignore-type */ .4 * 127);
Loading history...
647
        $kmlgeom = geoPHP::load(file_get_contents($this->kmlFileName), 'kml');
648
        $this->drawGeometry($kmlgeom, $col);
649
    }
650
651
    /**
652
     * Draw geometry or geometry collection on the map.
653
     *
654
     * @param Geometry $geom
655
     * @param int      $colour
656
     *            drawing colour
657
     */
658
    private function drawGeometry(Geometry $geom, int $colour): void {
0 ignored issues
show
Bug introduced by
The type Geometry was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
659
        if(empty($geom)) {
660
            return;
661
        }
662
663
        switch($geom->geometryType()) {
664
            case 'GeometryCollection' :
665
                // recursively draw part of the collection
666
                for($i = 1; $i < $geom->numGeometries() + 1; $i++) {
667
                    $_geom = $geom->geometryN($i);
668
                    $this->drawGeometry($_geom, $colour);
669
                }
670
                break;
671
            case 'MultiPolygon' :
672
            case 'MultiLineString' :
673
            case 'MultiPoint' :
674
                // TODO implement / do nothing
675
                break;
676
            case 'Polygon' :
677
                $this->drawPolygon($geom, $colour);
678
                break;
679
            case 'LineString' :
680
                $this->drawLineString($geom, $colour);
681
                break;
682
            case 'Point' :
683
                $this->drawPoint($geom, $colour);
684
                break;
685
            default :
686
                // draw nothing
687
                break;
688
        }
689
    }
690
691
    /**
692
     * Draw a polygon on the map.
693
     *
694
     * @param Polygon $polygon
0 ignored issues
show
Bug introduced by
The type Polygon was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
695
     * @param int     $colour
696
     *            drawing colour
697
     */
698
    private function drawPolygon($polygon, int $colour) {
699
        // TODO implementation of drawing holes,
700
        // maybe draw the polygon to an in-memory image and use imagecopy, draw polygon in col., draw holes in bgcol?
701
702
        // print_r('Polygon:<br />');
703
        // print_r($polygon);
704
        $extPoints = array();
705
        // extring is a linestring actually..
706
        $extRing = $polygon->exteriorRing();
707
708
        for($i = 1; $i < $extRing->numGeometries(); $i++) {
709
            $p1           = $extRing->geometryN($i);
710
            $x            = floor(
711
                ($this->width / 2) - $this->tileSize * ($this->centerX - $this->lonToTile($p1->x(), $this->zoom))
712
            );
713
            $y            = floor(
714
                ($this->height / 2) - $this->tileSize * ($this->centerY - $this->latToTile($p1->y(), $this->zoom))
715
            );
716
            $extPoints [] = $x;
717
            $extPoints [] = $y;
718
        }
719
        // print_r('points:('.($i-1).')<br />');
720
        // print_r($extPoints);
721
        // imagepolygon ($this->image, $extPoints, $i-1, $colour );
722
        imagefilledpolygon($this->image, $extPoints, $i - 1, $colour);
723
    }
724
725
    /**
726
     * Draw a line on the map.
727
     *
728
     * @param LineString $line
0 ignored issues
show
Bug introduced by
The type LineString was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
729
     * @param int        $colour
730
     *            drawing colour
731
     */
732
    private function drawLineString($line, $colour) {
733
        imagesetthickness($this->image, 2);
734
        for($p = 1; $p < $line->numGeometries(); $p++) {
735
            // get first pair of points
736
            $p1 = $line->geometryN($p);
737
            $p2 = $line->geometryN($p + 1);
738
            // translate to paper space
739
            $x1 = floor(
740
                ($this->width / 2) - $this->tileSize * ($this->centerX - $this->lonToTile($p1->x(), $this->zoom))
741
            );
742
            $y1 = floor(
743
                ($this->height / 2) - $this->tileSize * ($this->centerY - $this->latToTile($p1->y(), $this->zoom))
744
            );
745
            $x2 = floor(
746
                ($this->width / 2) - $this->tileSize * ($this->centerX - $this->lonToTile($p2->x(), $this->zoom))
747
            );
748
            $y2 = floor(
749
                ($this->height / 2) - $this->tileSize * ($this->centerY - $this->latToTile($p2->y(), $this->zoom))
750
            );
751
            // draw to image
752
            imageline($this->image, $x1, $y1, $x2, $y2, $colour);
0 ignored issues
show
Bug introduced by
$y1 of type double is incompatible with the type integer expected by parameter $y1 of imageline(). ( Ignorable by Annotation )

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

752
            imageline($this->image, $x1, /** @scrutinizer ignore-type */ $y1, $x2, $y2, $colour);
Loading history...
Bug introduced by
$x2 of type double is incompatible with the type integer expected by parameter $x2 of imageline(). ( Ignorable by Annotation )

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

752
            imageline($this->image, $x1, $y1, /** @scrutinizer ignore-type */ $x2, $y2, $colour);
Loading history...
Bug introduced by
$x1 of type double is incompatible with the type integer expected by parameter $x1 of imageline(). ( Ignorable by Annotation )

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

752
            imageline($this->image, /** @scrutinizer ignore-type */ $x1, $y1, $x2, $y2, $colour);
Loading history...
Bug introduced by
$y2 of type double is incompatible with the type integer expected by parameter $y2 of imageline(). ( Ignorable by Annotation )

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

752
            imageline($this->image, $x1, $y1, $x2, /** @scrutinizer ignore-type */ $y2, $colour);
Loading history...
753
        }
754
        imagesetthickness($this->image, 1);
755
    }
756
757
    /**
758
     * Draw a point on the map.
759
     *
760
     * @param Point $point
761
     * @param int   $colour
762
     *            drawing colour
763
     */
764
    private function drawPoint($point, $colour) {
765
        imagesetthickness($this->image, 2);
766
        // translate to paper space
767
        $cx = floor(
768
            ($this->width / 2) - $this->tileSize * ($this->centerX - $this->lonToTile($point->x(), $this->zoom))
769
        );
770
        $cy = floor(
771
            ($this->height / 2) - $this->tileSize * ($this->centerY - $this->latToTile($point->y(), $this->zoom))
772
        );
773
        $r  = 5;
774
        // draw to image
775
        // imageellipse($this->image, $cx, $cy,$r, $r, $colour);
776
        imagefilledellipse($this->image, $cx, $cy, $r, $r, $colour);
0 ignored issues
show
Bug introduced by
$cx of type double is incompatible with the type integer expected by parameter $cx of imagefilledellipse(). ( Ignorable by Annotation )

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

776
        imagefilledellipse($this->image, /** @scrutinizer ignore-type */ $cx, $cy, $r, $r, $colour);
Loading history...
Bug introduced by
$cy of type double is incompatible with the type integer expected by parameter $cy of imagefilledellipse(). ( Ignorable by Annotation )

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

776
        imagefilledellipse($this->image, $cx, /** @scrutinizer ignore-type */ $cy, $r, $r, $colour);
Loading history...
777
        // don't use imageellipse because the imagesetthickness function has
778
        // no effect. So the better workaround is to use imagearc.
779
        imagearc($this->image, $cx, $cy, $r, $r, 0, 359, $colour);
0 ignored issues
show
Bug introduced by
$cx of type double is incompatible with the type integer expected by parameter $cx of imagearc(). ( Ignorable by Annotation )

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

779
        imagearc($this->image, /** @scrutinizer ignore-type */ $cx, $cy, $r, $r, 0, 359, $colour);
Loading history...
Bug introduced by
$cy of type double is incompatible with the type integer expected by parameter $cy of imagearc(). ( Ignorable by Annotation )

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

779
        imagearc($this->image, $cx, /** @scrutinizer ignore-type */ $cy, $r, $r, 0, 359, $colour);
Loading history...
780
        imagesetthickness($this->image, 1);
781
    }
782
783
    /**
784
     * Draw gpx trace on the map.
785
     */
786
    public function drawGPX() {
787
        $col     = imagecolorallocatealpha($this->image, 0, 0, 255, .4 * 127);
0 ignored issues
show
Bug introduced by
0.4 * 127 of type double is incompatible with the type integer expected by parameter $alpha of imagecolorallocatealpha(). ( Ignorable by Annotation )

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

787
        $col     = imagecolorallocatealpha($this->image, 0, 0, 255, /** @scrutinizer ignore-type */ .4 * 127);
Loading history...
788
        $gpxgeom = geoPHP::load(file_get_contents($this->gpxFileName), 'gpx');
789
        $this->drawGeometry($gpxgeom, $col);
790
    }
791
792
    /**
793
     * Draw geojson on the map.
794
     */
795
    public function drawGeojson() {
796
        $col     = imagecolorallocatealpha($this->image, 255, 0, 255, .4 * 127);
0 ignored issues
show
Bug introduced by
0.4 * 127 of type double is incompatible with the type integer expected by parameter $alpha of imagecolorallocatealpha(). ( Ignorable by Annotation )

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

796
        $col     = imagecolorallocatealpha($this->image, 255, 0, 255, /** @scrutinizer ignore-type */ .4 * 127);
Loading history...
797
        $gpxgeom = geoPHP::load(file_get_contents($this->geojsonFileName), 'json');
798
        $this->drawGeometry($gpxgeom, $col);
799
    }
800
801
    /**
802
     * add copyright and origin notice and icons to the map.
803
     */
804
    public function drawCopyright() {
805
        $logoBaseDir = dirname(__FILE__) . '/' . 'logo/';
806
        $logoImg     = imagecreatefrompng($logoBaseDir . $this->tileInfo ['openstreetmap'] ['logo']);
807
        $textcolor   = imagecolorallocate($this->image, 0, 0, 0);
808
        $bgcolor     = imagecolorallocate($this->image, 200, 200, 200);
809
810
        imagecopy(
811
            $this->image,
812
            $logoImg,
813
            0,
814
            imagesy($this->image) - imagesy($logoImg),
815
            0,
816
            0,
817
            imagesx($logoImg),
818
            imagesy($logoImg)
819
        );
820
        imagestring(
821
            $this->image,
822
            1,
823
            imagesx($logoImg) + 2,
824
            imagesy($this->image) - imagesy($logoImg) + 1,
825
            $this->tileInfo ['openstreetmap'] ['txt'],
826
            $bgcolor
827
        );
828
        imagestring(
829
            $this->image,
830
            1,
831
            imagesx($logoImg) + 1,
832
            imagesy($this->image) - imagesy($logoImg),
833
            $this->tileInfo ['openstreetmap'] ['txt'],
834
            $textcolor
835
        );
836
837
        // additional tile source info, ie. who created/hosted the tiles
838
        $xIconOffset = 0;
839
        if($this->maptype === 'openstreetmap') {
840
            $mapAuthor = "(c) OpenStreetMap maps/CC BY-SA";
841
        } else {
842
            $mapAuthor   = $this->tileInfo [$this->maptype] ['txt'];
843
            $iconImg     = imagecreatefrompng($logoBaseDir . $this->tileInfo [$this->maptype] ['logo']);
844
            $xIconOffset = imagesx($iconImg);
845
            imagecopy(
846
                $this->image,
847
                $iconImg, imagesx($logoImg) + 1,
848
                imagesy($this->image) - imagesy($iconImg),
849
                0,
850
                0,
851
                imagesx($iconImg), imagesy($iconImg)
852
            );
853
        }
854
        imagestring(
855
            $this->image,
856
            1, imagesx($logoImg) + $xIconOffset + 4,
857
            imagesy($this->image) - ceil(imagesy($logoImg) / 2) + 1,
0 ignored issues
show
Bug introduced by
imagesy($this->image) - ...gesy($logoImg) / 2) + 1 of type double is incompatible with the type integer expected by parameter $y of imagestring(). ( Ignorable by Annotation )

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

857
            /** @scrutinizer ignore-type */ imagesy($this->image) - ceil(imagesy($logoImg) / 2) + 1,
Loading history...
858
            $mapAuthor,
859
            $bgcolor
860
        );
861
        imagestring(
862
            $this->image,
863
            1, imagesx($logoImg) + $xIconOffset + 3,
864
            imagesy($this->image) - ceil(imagesy($logoImg) / 2),
865
            $mapAuthor,
866
            $textcolor
867
        );
868
869
    }
870
}
871