Passed
Push — master ( 630eee...e4be3b )
by Mark
05:32 queued 10s
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 CC-BY-SA',
40
            'logo' => 'osm_logo.png',
41
            'url'  => 'https://tile.openstreetmap.org/{Z}/{X}/{Y}.png'
42
        ),
43
        // OCM sources
44
        'cycle'         => array(
45
            'txt'  => 'Thunderforest tiles',
46
            'logo' => 'tf_logo.png',
47
            'url'  => 'https://tile.thunderforest.com/cycle/{Z}/{X}/{Y}.png?apikey='
48
        ),
49
        'transport'     => array(
50
            'txt'  => 'Thunderforest tiles',
51
            'logo' => 'tf_logo.png',
52
            'url'  => 'https://tile.thunderforest.com/transport/{Z}/{X}/{Y}.png?apikey='
53
        ),
54
        'landscape'     => array(
55
            'txt'  => 'Thunderforest tiles',
56
            'logo' => 'tf_logo.png',
57
            'url'  => 'https://tile.thunderforest.com/landscape/{Z}/{X}/{Y}.png?apikey='
58
        ),
59
        'outdoors'      => array(
60
            'txt'  => 'Thunderforest tiles',
61
            'logo' => 'tf_logo.png',
62
            'url'  => 'https://tile.thunderforest.com/outdoors/{Z}/{X}/{Y}.png?apikey='
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 = true;
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 $mediaDir
177
     *            Directory to store/cache maps
178
     * @param string $tileCacheBaseDir
179
     *            Directory to cache map tiles
180
     * @param bool   $autoZoomExtent
181
     *            Wheter or not to override zoom/lat/lon and zoom to the extent of gpx/kml and markers
182
     * @param string apikey
0 ignored issues
show
Bug introduced by
The type apikey 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...
183
     *            Some service require a key to access
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 = '';
0 ignored issues
show
Unused Code introduced by
The assignment to $doc is dead and can be removed.
Loading history...
245
        $doc = $this->mapCacheIDToFilename();
246
        // make url relative to media dir
247
        return str_replace($this->mediaBaseDir, '', $doc);
248
    }
249
250
    /**
251
     * Calculate the lat/lon/zoom values to make sure that all of the markers and gpx/kml are on the map.
252
     * can throw an error like
253
     * "Fatal error: Uncaught Exception: Cannot create a collection with non-geometries in
254
     * D:\www\wild-water.nl\www\dokuwiki\lib\plugins\geophp\geoPHP\lib\geometry\Collection.class.php:29"
255
     *
256
     * @param float $paddingFactor
257
     *            buffer constant to enlarge (>1.0) the zoom level
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
        $g = false;
268
        if(file_exists($this->kmlFileName)) {
269
            $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...
270
            if($g !== false) {
271
                $geoms [] = $g;
272
            }
273
        }
274
        if(file_exists($this->gpxFileName)) {
275
            $g = geoPHP::load(file_get_contents($this->gpxFileName), 'gpx');
276
            if($g !== false) {
277
                $geoms [] = $g;
278
            }
279
        }
280
        if(file_exists($this->geojsonFileName)) {
281
            $g = geoPHP::load(file_get_contents($this->geojsonFileName), 'geojson');
282
            if($g !== false) {
283
                $geoms [] = $g;
284
            }
285
        }
286
287
        if(count($geoms) <= 1) {
288
            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

288
            /** @scrutinizer ignore-call */ 
289
            dbglog($geoms, "StaticMap::autoZoom: Skip setting autozoom options");
Loading history...
289
            return;
290
        }
291
292
        $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...
293
        $centroid = $geom->centroid();
294
        $bbox     = $geom->getBBox();
295
296
        // determine vertical resolution, this depends on the distance from the equator
297
        // $vy00 = log(tan(M_PI*(0.25 + $centroid->getY()/360)));
298
        $vy0 = log(tan(M_PI * (0.25 + $bbox ['miny'] / 360)));
299
        $vy1 = log(tan(M_PI * (0.25 + $bbox ['maxy'] / 360)));
300
        dbglog("StaticMap::autoZoom: vertical resolution: $vy0, $vy1");
301
        $zoomFactorPowered  = ($this->height / 2) / (40.7436654315252 * ($vy1 - $vy0));
302
        $resolutionVertical = 360 / ($zoomFactorPowered * $this->tileSize);
303
        // determine horizontal resolution
304
        $resolutionHorizontal = ($bbox ['maxx'] - $bbox ['minx']) / $this->width;
305
        $resolution           = max($resolutionHorizontal, $resolutionVertical) * $paddingFactor;
306
        $zoom                 = log(360 / ($resolution * $this->tileSize), 2);
307
308
        if(is_finite($zoom) && $zoom < 15 && $zoom > 2) {
309
            $this->zoom = floor($zoom);
310
        }
311
        $this->lon = $centroid->getX();
312
        $this->lat = $centroid->getY();
313
        dbglog("StaticMap::autoZoom: Set autozoom options to: z: $this->zoom, lon: $this->lon, lat: $this->lat");
314
    }
315
316
    public function checkMapCache(): bool {
317
        // side effect: set the mapCacheID
318
        $this->mapCacheID = md5($this->serializeParams());
319
        $filename         = $this->mapCacheIDToFilename();
320
        return file_exists($filename);
321
    }
322
323
    public function serializeParams() {
324
        return join(
325
            "&", array(
326
                   $this->zoom,
327
                   $this->lat,
328
                   $this->lon,
329
                   $this->width,
330
                   $this->height,
331
                   serialize($this->markers),
332
                   $this->maptype,
333
                   $this->kmlFileName,
334
                   $this->gpxFileName,
335
                   $this->geojsonFileName
336
               )
337
        );
338
    }
339
340
    public function mapCacheIDToFilename(): string {
341
        if(!$this->mapCacheFile) {
342
            $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

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

429
                    $tileImage = imagecreatefromstring(/** @scrutinizer ignore-type */ $tileData);
Loading history...
430
                } else {
431
                    $tileImage = imagecreate($this->tileSize, $this->tileSize);
432
                    $color     = imagecolorallocate($tileImage, 255, 255, 255);
433
                    @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

433
                    /** @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...
434
                }
435
                $destX = ($x - $startX) * $this->tileSize + $this->offsetX;
436
                $destY = ($y - $startY) * $this->tileSize + $this->offsetY;
437
                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

437
                /** @scrutinizer ignore-call */ 
438
                dbglog($this->tileSize, "imagecopy tile into image: $destX, $destY");
Loading history...
438
                imagecopy(
439
                    $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

439
                    $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

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

555
                    if(preg_match($markerPrototype ['regex'], $markerType, /** @scrutinizer ignore-type */ $matches)) {
Loading history...
556
                        $markerFilename = $matches [0] . $markerPrototype ['extension'];
557
                        if($markerPrototype ['offsetImage']) {
558
                            list ($markerImageOffsetX, $markerImageOffsetY) = explode(
559
                                ",",
560
                                $markerPrototype ['offsetImage']
561
                            );
562
                        }
563
                        $markerShadow = $markerPrototype ['shadow'];
564
                        if($markerShadow) {
565
                            list ($markerShadowOffsetX, $markerShadowOffsetY) = explode(
566
                                ",",
567
                                $markerPrototype ['offsetShadow']
568
                            );
569
                        }
570
                    }
571
                }
572
            }
573
            // create img resource
574
            if(file_exists($markerBaseDir . '/' . $markerFilename)) {
575
                $markerImg = imagecreatefrompng($markerBaseDir . '/' . $markerFilename);
576
            } else {
577
                $markerImg = imagecreatefrompng($markerBaseDir . '/marker.png');
578
            }
579
            // check for shadow + create shadow recource
580
            if($markerShadow && file_exists($markerBaseDir . '/' . $markerShadow)) {
581
                $markerShadowImg = imagecreatefrompng($markerBaseDir . '/' . $markerShadow);
582
            }
583
            // calc position
584
            $destX = floor(
585
                ($this->width / 2) -
586
                $this->tileSize * ($this->centerX - $this->lonToTile($markerLon, $this->zoom))
587
            );
588
            $destY = floor(
589
                ($this->height / 2) -
590
                $this->tileSize * ($this->centerY - $this->latToTile($markerLat, $this->zoom))
591
            );
592
            // copy shadow on basemap
593
            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...
594
                imagecopy(
595
                    $this->image,
596
                    $markerShadowImg,
597
                    $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

597
                    /** @scrutinizer ignore-type */ $destX + (int) $markerShadowOffsetX,
Loading history...
598
                    $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

598
                    /** @scrutinizer ignore-type */ $destY + (int) $markerShadowOffsetY,
Loading history...
599
                    0,
600
                    0,
601
                    imagesx($markerShadowImg),
602
                    imagesy($markerShadowImg)
603
                );
604
            }
605
            // copy marker on basemap above shadow
606
            imagecopy(
607
                $this->image,
608
                $markerImg,
609
                $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...
610
                $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...
611
                0,
612
                0,
613
                imagesx($markerImg),
614
                imagesy($markerImg)
615
            );
616
            // add label
617
            imagestring(
618
                $this->image,
619
                3,
620
                $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

620
                /** @scrutinizer ignore-type */ $destX - imagesx($markerImg) + 1,
Loading history...
621
                $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

621
                /** @scrutinizer ignore-type */ $destY + (int) $markerImageOffsetY + 1,
Loading history...
622
                ++$count,
623
                $bgcolor
624
            );
625
            imagestring(
626
                $this->image,
627
                3,
628
                $destX - imagesx($markerImg),
629
                $destY + (int) $markerImageOffsetY,
630
                $count,
631
                $color
632
            );
633
        }
634
    }
635
636
    /**
637
     * Draw kml trace on the map.
638
     */
639
    public function drawKML(): void {
640
        // TODO get colour from kml node (not currently supported in geoPHP)
641
        $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

641
        $col     = imagecolorallocatealpha($this->image, 255, 0, 0, /** @scrutinizer ignore-type */ .4 * 127);
Loading history...
642
        $kmlgeom = geoPHP::load(file_get_contents($this->kmlFileName), 'kml');
643
        $this->drawGeometry($kmlgeom, $col);
644
    }
645
646
    /**
647
     * Draw geometry or geometry collection on the map.
648
     *
649
     * @param Geometry $geom
650
     * @param int      $colour
651
     *            drawing colour
652
     */
653
    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...
654
        if(empty($geom)) {
655
            return;
656
        }
657
658
        switch($geom->geometryType()) {
659
            case 'GeometryCollection' :
660
                // recursively draw part of the collection
661
                for($i = 1; $i < $geom->numGeometries() + 1; $i++) {
662
                    $_geom = $geom->geometryN($i);
663
                    $this->drawGeometry($_geom, $colour);
664
                }
665
                break;
666
            case 'MultiPolygon' :
667
            case 'MultiLineString' :
668
            case 'MultiPoint' :
669
                // TODO implement / do nothing
670
                break;
671
            case 'Polygon' :
672
                $this->drawPolygon($geom, $colour);
673
                break;
674
            case 'LineString' :
675
                $this->drawLineString($geom, $colour);
676
                break;
677
            case 'Point' :
678
                $this->drawPoint($geom, $colour);
679
                break;
680
            default :
681
                // draw nothing
682
                break;
683
        }
684
    }
685
686
    /**
687
     * Draw a polygon on the map.
688
     *
689
     * @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...
690
     * @param int     $colour
691
     *            drawing colour
692
     */
693
    private function drawPolygon($polygon, $colour) {
694
        // TODO implementation of drawing holes,
695
        // maybe draw the polygon to an in-memory image and use imagecopy, draw polygon in col., draw holes in bgcol?
696
697
        // print_r('Polygon:<br />');
698
        // print_r($polygon);
699
        $extPoints = array();
700
        // extring is a linestring actually..
701
        $extRing = $polygon->exteriorRing();
702
703
        for($i = 1; $i < $extRing->numGeometries(); $i++) {
704
            $p1           = $extRing->geometryN($i);
705
            $x            = floor(
706
                ($this->width / 2) - $this->tileSize * ($this->centerX - $this->lonToTile($p1->x(), $this->zoom))
707
            );
708
            $y            = floor(
709
                ($this->height / 2) - $this->tileSize * ($this->centerY - $this->latToTile($p1->y(), $this->zoom))
710
            );
711
            $extPoints [] = $x;
712
            $extPoints [] = $y;
713
        }
714
        // print_r('points:('.($i-1).')<br />');
715
        // print_r($extPoints);
716
        // imagepolygon ($this->image, $extPoints, $i-1, $colour );
717
        imagefilledpolygon($this->image, $extPoints, $i - 1, $colour);
718
    }
719
720
    /**
721
     * Draw a line on the map.
722
     *
723
     * @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...
724
     * @param int        $colour
725
     *            drawing colour
726
     */
727
    private function drawLineString($line, $colour) {
728
        imagesetthickness($this->image, 2);
729
        for($p = 1; $p < $line->numGeometries(); $p++) {
730
            // get first pair of points
731
            $p1 = $line->geometryN($p);
732
            $p2 = $line->geometryN($p + 1);
733
            // translate to paper space
734
            $x1 = floor(
735
                ($this->width / 2) - $this->tileSize * ($this->centerX - $this->lonToTile($p1->x(), $this->zoom))
736
            );
737
            $y1 = floor(
738
                ($this->height / 2) - $this->tileSize * ($this->centerY - $this->latToTile($p1->y(), $this->zoom))
739
            );
740
            $x2 = floor(
741
                ($this->width / 2) - $this->tileSize * ($this->centerX - $this->lonToTile($p2->x(), $this->zoom))
742
            );
743
            $y2 = floor(
744
                ($this->height / 2) - $this->tileSize * ($this->centerY - $this->latToTile($p2->y(), $this->zoom))
745
            );
746
            // draw to image
747
            imageline($this->image, $x1, $y1, $x2, $y2, $colour);
0 ignored issues
show
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

747
            imageline($this->image, $x1, $y1, $x2, /** @scrutinizer ignore-type */ $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

747
            imageline($this->image, /** @scrutinizer ignore-type */ $x1, $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

747
            imageline($this->image, $x1, $y1, /** @scrutinizer ignore-type */ $x2, $y2, $colour);
Loading history...
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

747
            imageline($this->image, $x1, /** @scrutinizer ignore-type */ $y1, $x2, $y2, $colour);
Loading history...
748
        }
749
        imagesetthickness($this->image, 1);
750
    }
751
752
    /**
753
     * Draw a point on the map.
754
     *
755
     * @param Point $point
756
     * @param int   $colour
757
     *            drawing colour
758
     */
759
    private function drawPoint($point, $colour) {
760
        imagesetthickness($this->image, 2);
761
        // translate to paper space
762
        $cx = floor(
763
            ($this->width / 2) - $this->tileSize * ($this->centerX - $this->lonToTile($point->x(), $this->zoom))
764
        );
765
        $cy = floor(
766
            ($this->height / 2) - $this->tileSize * ($this->centerY - $this->latToTile($point->y(), $this->zoom))
767
        );
768
        $r  = 5;
769
        // draw to image
770
        // imageellipse($this->image, $cx, $cy,$r, $r, $colour);
771
        imagefilledellipse($this->image, $cx, $cy, $r, $r, $colour);
0 ignored issues
show
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

771
        imagefilledellipse($this->image, $cx, /** @scrutinizer ignore-type */ $cy, $r, $r, $colour);
Loading history...
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

771
        imagefilledellipse($this->image, /** @scrutinizer ignore-type */ $cx, $cy, $r, $r, $colour);
Loading history...
772
        // don't use imageellipse because the imagesetthickness function has
773
        // no effect. So the better workaround is to use imagearc.
774
        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

774
        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

774
        imagearc($this->image, $cx, /** @scrutinizer ignore-type */ $cy, $r, $r, 0, 359, $colour);
Loading history...
775
        imagesetthickness($this->image, 1);
776
    }
777
778
    /**
779
     * Draw gpx trace on the map.
780
     */
781
    public function drawGPX() {
782
        $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

782
        $col     = imagecolorallocatealpha($this->image, 0, 0, 255, /** @scrutinizer ignore-type */ .4 * 127);
Loading history...
783
        $gpxgeom = geoPHP::load(file_get_contents($this->gpxFileName), 'gpx');
784
        $this->drawGeometry($gpxgeom, $col);
785
    }
786
787
    /**
788
     * Draw geojson on the map.
789
     */
790
    public function drawGeojson() {
791
        $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

791
        $col     = imagecolorallocatealpha($this->image, 255, 0, 255, /** @scrutinizer ignore-type */ .4 * 127);
Loading history...
792
        $gpxgeom = geoPHP::load(file_get_contents($this->geojsonFileName), 'json');
793
        $this->drawGeometry($gpxgeom, $col);
794
    }
795
796
    /**
797
     * add copyright and origin notice and icons to the map.
798
     */
799
    public function drawCopyright() {
800
        $logoBaseDir = dirname(__FILE__) . '/' . 'logo/';
801
        $logoImg     = imagecreatefrompng($logoBaseDir . $this->tileInfo ['openstreetmap'] ['logo']);
802
        $textcolor   = imagecolorallocate($this->image, 0, 0, 0);
803
        $bgcolor     = imagecolorallocate($this->image, 200, 200, 200);
804
805
        imagecopy(
806
            $this->image,
807
            $logoImg,
808
            0,
809
            imagesy($this->image) - imagesy($logoImg),
810
            0,
811
            0,
812
            imagesx($logoImg),
813
            imagesy($logoImg)
814
        );
815
        imagestring(
816
            $this->image,
817
            1,
818
            imagesx($logoImg) + 2,
819
            imagesy($this->image) - imagesy($logoImg) + 1,
820
            $this->tileInfo ['openstreetmap'] ['txt'],
821
            $bgcolor
822
        );
823
        imagestring(
824
            $this->image,
825
            1,
826
            imagesx($logoImg) + 1,
827
            imagesy($this->image) - imagesy($logoImg),
828
            $this->tileInfo ['openstreetmap'] ['txt'],
829
            $textcolor
830
        );
831
832
        // additional tile source info, ie. who created/hosted the tiles
833
        if($this->maptype != 'openstreetmap') {
834
            $iconImg = imagecreatefrompng($logoBaseDir . $this->tileInfo [$this->maptype] ['logo']);
835
            imagecopy(
836
                $this->image,
837
                $iconImg, imagesx($logoImg) + 1,
838
                imagesy($this->image) - imagesy($iconImg),
839
                0,
840
                0,
841
                imagesx($iconImg), imagesy($iconImg)
842
            );
843
            imagestring(
844
                $this->image,
845
                1, imagesx($logoImg) + imagesx($iconImg) + 4,
846
                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

846
                /** @scrutinizer ignore-type */ imagesy($this->image) - ceil(imagesy($logoImg) / 2) + 1,
Loading history...
847
                $this->tileInfo [$this->maptype] ['txt'],
848
                $bgcolor
849
            );
850
            imagestring(
851
                $this->image,
852
                1, imagesx($logoImg) + imagesx($iconImg) + 3,
853
                imagesy($this->image) - ceil(imagesy($logoImg) / 2),
854
                $this->tileInfo [$this->maptype] ['txt'],
855
                $textcolor
856
            );
857
        }
858
    }
859
}
860