Passed
Pull Request — master (#20)
by Mark
03:04
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'  => 'https://stamen-tiles.a.ssl.fastly.net/toner/{Z}/{X}/{Y}.png'
68
        ),
69
        'terrain'       => array(
70
            'txt'  => 'Stamen tiles',
71
            'logo' => 'stamen.png',
72
            'url'  => 'https://stamen-tiles.a.ssl.fastly.net/terrain/{Z}/{X}/{Y}.jpg'
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
        if ($vy1 - $vy0 === 0.0){
301
            $resolutionVertical = 0;
302
            dbglog("StaticMap::autoZoom: using $resolutionVertical");
303
        } else {
304
            $zoomFactorPowered  = ($this->height / 2) / (40.7436654315252 * ($vy1 - $vy0));
305
            $resolutionVertical = 360 / ($zoomFactorPowered * $this->tileSize);
306
        }
307
        // determine horizontal resolution
308
        $resolutionHorizontal = ($bbox ['maxx'] - $bbox ['minx']) / $this->width;
309
        dbglog("StaticMap::autoZoom: using $resolutionHorizontal");
310
        $resolution           = max($resolutionHorizontal, $resolutionVertical) * $paddingFactor;
311
        $zoom                 = $this->zoom;
312
        if ($resolution > 0){
313
            $zoom             = log(360 / ($resolution * $this->tileSize), 2);
314
        }
315
316
        if(is_finite($zoom) && $zoom < 15 && $zoom > 2) {
317
            $this->zoom = floor($zoom);
318
        }
319
        $this->lon = $centroid->getX();
320
        $this->lat = $centroid->getY();
321
        dbglog("StaticMap::autoZoom: Set autozoom options to: z: $this->zoom, lon: $this->lon, lat: $this->lat");
322
    }
323
324
    public function checkMapCache(): bool {
325
        // side effect: set the mapCacheID
326
        $this->mapCacheID = md5($this->serializeParams());
327
        $filename         = $this->mapCacheIDToFilename();
328
        return file_exists($filename);
329
    }
330
331
    public function serializeParams(): string {
332
        return implode(
333
            "&", array(
334
                   $this->zoom,
335
                   $this->lat,
336
                   $this->lon,
337
                   $this->width,
338
                   $this->height,
339
                   serialize($this->markers),
340
                   $this->maptype,
341
                   $this->kmlFileName,
342
                   $this->gpxFileName,
343
                   $this->geojsonFileName
344
               )
345
        );
346
    }
347
348
    public function mapCacheIDToFilename(): string {
349
        if(!$this->mapCacheFile) {
350
            $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

350
            $this->mapCacheFile = $this->mapCacheBaseDir . "/" . /** @scrutinizer ignore-type */ $this->maptype . "/" . $this->zoom . "/cache_"
Loading history...
351
                . substr($this->mapCacheID, 0, 2) . "/" . substr($this->mapCacheID, 2, 2)
352
                . "/" . substr($this->mapCacheID, 4);
353
        }
354
        return $this->mapCacheFile . "." . $this->mapCacheExtension;
355
    }
356
357
    /**
358
     * make the map.
359
     */
360
    public function makeMap(): void {
361
        $this->initCoords();
362
        $this->createBaseMap();
363
        if(!empty ($this->markers)) {
364
            $this->placeMarkers();
365
        }
366
        if (file_exists($this->kmlFileName)) {
367
            try {
368
                $this->drawKML();
369
            } catch (exception $e) {
370
                dbglog('failed to load KML file', $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

370
                /** @scrutinizer ignore-call */ 
371
                dbglog('failed to load KML file', $e);
Loading history...
371
            }
372
        }
373
        if (file_exists($this->gpxFileName)) {
374
            try {
375
                $this->drawGPX();
376
            } catch (exception $e) {
377
                dbglog('failed to load GPX file', $e);
378
            }
379
        }
380
        if (file_exists($this->geojsonFileName)) {
381
            try {
382
                $this->drawGeojson();
383
            } catch (exception $e) {
384
                dbglog('failed to load GeoJSON file', $e);
385
            }
386
        }
387
388
        $this->drawCopyright();
389
    }
390
391
    /**
392
     */
393
    public function initCoords(): void {
394
        $this->centerX = $this->lonToTile($this->lon, $this->zoom);
395
        $this->centerY = $this->latToTile($this->lat, $this->zoom);
396
        $this->offsetX = floor((floor($this->centerX) - $this->centerX) * $this->tileSize);
397
        $this->offsetY = floor((floor($this->centerY) - $this->centerY) * $this->tileSize);
398
    }
399
400
    /**
401
     *
402
     * @param float $long
403
     * @param int   $zoom
404
     * @return float|int
405
     */
406
    public function lonToTile(float $long, int $zoom) {
407
        return (($long + 180) / 360) * pow(2, $zoom);
408
    }
409
410
    /**
411
     *
412
     * @param float $lat
413
     * @param int   $zoom
414
     * @return float|int
415
     */
416
    public function latToTile(float $lat, int $zoom) {
417
        return (1 - log(tan($lat * pi() / 180) + 1 / cos($lat * M_PI / 180)) / M_PI) / 2 * pow(2, $zoom);
418
    }
419
420
    /**
421
     * make basemap image.
422
     */
423
    public function createBaseMap(): void {
424
        $this->image   = imagecreatetruecolor($this->width, $this->height);
425
        $startX        = floor($this->centerX - ($this->width / $this->tileSize) / 2);
426
        $startY        = floor($this->centerY - ($this->height / $this->tileSize) / 2);
427
        $endX          = ceil($this->centerX + ($this->width / $this->tileSize) / 2);
428
        $endY          = ceil($this->centerY + ($this->height / $this->tileSize) / 2);
429
        $this->offsetX = -floor(($this->centerX - floor($this->centerX)) * $this->tileSize);
430
        $this->offsetY = -floor(($this->centerY - floor($this->centerY)) * $this->tileSize);
431
        $this->offsetX += floor($this->width / 2);
432
        $this->offsetY += floor($this->height / 2);
433
        $this->offsetX += floor($startX - floor($this->centerX)) * $this->tileSize;
434
        $this->offsetY += floor($startY - floor($this->centerY)) * $this->tileSize;
435
436
        for($x = $startX; $x <= $endX; $x++) {
437
            for($y = $startY; $y <= $endY; $y++) {
438
                $url = str_replace(
439
                    array(
440
                        '{Z}',
441
                        '{X}',
442
                        '{Y}'
443
                    ), array(
444
                        $this->zoom,
445
                        $x,
446
                        $y
447
                    ), $this->tileInfo [$this->maptype] ['url']
448
                );
449
450
                $tileData = $this->fetchTile($url);
451
                if($tileData) {
452
                    $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

452
                    $tileImage = imagecreatefromstring(/** @scrutinizer ignore-type */ $tileData);
Loading history...
453
                } else {
454
                    $tileImage = imagecreate($this->tileSize, $this->tileSize);
455
                    $color     = imagecolorallocate($tileImage, 255, 255, 255);
456
                    @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

456
                    /** @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...
457
                }
458
                $destX = ($x - $startX) * $this->tileSize + $this->offsetX;
459
                $destY = ($y - $startY) * $this->tileSize + $this->offsetY;
460
                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

460
                /** @scrutinizer ignore-call */ 
461
                dbglog($this->tileSize, "imagecopy tile into image: $destX, $destY");
Loading history...
461
                imagecopy(
462
                    $this->image, $tileImage, $destX, $destY, 0, 0, $this->tileSize,
0 ignored issues
show
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

462
                    $this->image, $tileImage, $destX, /** @scrutinizer ignore-type */ $destY, 0, 0, $this->tileSize,
Loading history...
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

462
                    $this->image, $tileImage, /** @scrutinizer ignore-type */ $destX, $destY, 0, 0, $this->tileSize,
Loading history...
463
                    $this->tileSize
464
                );
465
            }
466
        }
467
    }
468
469
    /**
470
     * Fetch a tile and (if configured) store it in the cache.
471
     * @param string $url
472
     * @return bool|string
473
     * @todo refactor this to use dokuwiki\HTTP\HTTPClient or dokuwiki\HTTP\DokuHTTPClient
474
     *          for better proxy handling...
475
     */
476
    public function fetchTile(string $url) {
477
        if($this->useTileCache && ($cached = $this->checkTileCache($url)))
478
            return $cached;
479
480
        $_UA = 'Mozilla/4.0 (compatible; DokuWikiSpatial HTTP Client; ' . PHP_OS . ')';
481
        if(function_exists("curl_init")) {
482
            // use cUrl
483
            $ch = curl_init();
484
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
485
            curl_setopt($ch, CURLOPT_USERAGENT, $_UA);
486
            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
487
            curl_setopt($ch, CURLOPT_URL, $url . $this->apikey);
488
            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

488
            /** @scrutinizer ignore-call */ 
489
            dbglog("StaticMap::fetchTile: getting: $url using curl_exec");
Loading history...
489
            $tile = curl_exec($ch);
490
            curl_close($ch);
491
        } else {
492
            // use file_get_contents
493
            global $conf;
494
            $opts = array(
495
                'http' => array(
496
                    'method'          => "GET",
497
                    'header'          => "Accept-language: en\r\n" . "User-Agent: $_UA\r\n" . "accept: image/png\r\n",
498
                    'request_fulluri' => true
499
                )
500
            );
501
            if(isset($conf['proxy']['host'], $conf['proxy']['port'])
502
                && $conf['proxy']['host'] !== ''
503
                && $conf['proxy']['port'] !== '') {
504
                $opts['http'] += ['proxy' => "tcp://" . $conf['proxy']['host'] . ":" . $conf['proxy']['port']];
505
            }
506
507
            $context = stream_context_create($opts);
508
            dbglog("StaticMap::fetchTile: getting: $url . $this->apikey using file_get_contents and options $opts");
509
            $tile = file_get_contents($url . $this->apikey, false, $context);
510
        }
511
        if($tile && $this->useTileCache) {
512
            $this->writeTileToCache($url, $tile);
513
        }
514
        return $tile;
515
    }
516
517
    /**
518
     *
519
     * @param string $url
520
     * @return string|false
521
     */
522
    public function checkTileCache(string $url) {
523
        $filename = $this->tileUrlToFilename($url);
524
        if(file_exists($filename)) {
525
            return file_get_contents($filename);
526
        }
527
        return false;
528
    }
529
530
    /**
531
     *
532
     * @param string $url
533
     * @return string
534
     */
535
    public function tileUrlToFilename(string $url): string {
536
        return $this->tileCacheBaseDir . "/" . substr($url, strpos($url, '/') + 1);
537
    }
538
539
    /**
540
     * Write a tile into the cache.
541
     *
542
     * @param string $url
543
     * @param mixed  $data
544
     */
545
    public function writeTileToCache($url, $data): void {
546
        $filename = $this->tileUrlToFilename($url);
547
        $this->mkdirRecursive(dirname($filename), 0777);
548
        file_put_contents($filename, $data);
549
    }
550
551
    /**
552
     * Recursively create the directory.
553
     *
554
     * @param string $pathname
555
     *            The directory path.
556
     * @param int    $mode
557
     *            File access mode. For more information on modes, read the details on the chmod manpage.
558
     */
559
    public function mkdirRecursive(string $pathname, int $mode): bool {
560
        is_dir(dirname($pathname)) || $this->mkdirRecursive(dirname($pathname), $mode);
561
        return is_dir($pathname) || mkdir($pathname, $mode) || is_dir($pathname);
562
    }
563
564
    /**
565
     * Place markers on the map and number them in the same order as they are listed in the html.
566
     */
567
    public function placeMarkers(): void {
568
        $count         = 0;
569
        $color         = imagecolorallocate($this->image, 0, 0, 0);
570
        $bgcolor       = imagecolorallocate($this->image, 200, 200, 200);
571
        $markerBaseDir = __DIR__ . '/icons';
572
        $markerImageOffsetX  = 0;
573
        $markerImageOffsetY  = 0;
574
        $markerShadowOffsetX = 0;
575
        $markerShadowOffsetY = 0;
576
        $markerShadowImg     = null;
577
        // loop thru marker array
578
        foreach($this->markers as $marker) {
579
            // set some local variables
580
            $markerLat  = $marker ['lat'];
581
            $markerLon  = $marker ['lon'];
582
            $markerType = $marker ['type'];
583
            // clear variables from previous loops
584
            $markerFilename = '';
585
            $markerShadow   = '';
586
            $matches        = false;
587
            // check for marker type, get settings from markerPrototypes
588
            if($markerType) {
589
                foreach($this->markerPrototypes as $markerPrototype) {
590
                    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

590
                    if(preg_match($markerPrototype ['regex'], $markerType, /** @scrutinizer ignore-type */ $matches)) {
Loading history...
591
                        $markerFilename = $matches [0] . $markerPrototype ['extension'];
592
                        if($markerPrototype ['offsetImage']) {
593
                            list ($markerImageOffsetX, $markerImageOffsetY) = explode(
594
                                ",",
595
                                $markerPrototype ['offsetImage']
596
                            );
597
                        }
598
                        $markerShadow = $markerPrototype ['shadow'];
599
                        if($markerShadow) {
600
                            list ($markerShadowOffsetX, $markerShadowOffsetY) = explode(
601
                                ",",
602
                                $markerPrototype ['offsetShadow']
603
                            );
604
                        }
605
                    }
606
                }
607
            }
608
            // create img resource
609
            if(file_exists($markerBaseDir . '/' . $markerFilename)) {
610
                $markerImg = imagecreatefrompng($markerBaseDir . '/' . $markerFilename);
611
            } else {
612
                $markerImg = imagecreatefrompng($markerBaseDir . '/marker.png');
613
            }
614
            // check for shadow + create shadow recource
615
            if($markerShadow && file_exists($markerBaseDir . '/' . $markerShadow)) {
616
                $markerShadowImg = imagecreatefrompng($markerBaseDir . '/' . $markerShadow);
617
            }
618
            // calc position
619
            $destX = floor(
620
                ($this->width / 2) -
621
                $this->tileSize * ($this->centerX - $this->lonToTile($markerLon, $this->zoom))
622
            );
623
            $destY = floor(
624
                ($this->height / 2) -
625
                $this->tileSize * ($this->centerY - $this->latToTile($markerLat, $this->zoom))
626
            );
627
            // copy shadow on basemap
628
            if($markerShadow && $markerShadowImg) {
629
                imagecopy(
630
                    $this->image,
631
                    $markerShadowImg,
632
                    $destX + (int) $markerShadowOffsetX,
0 ignored issues
show
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

632
                    /** @scrutinizer ignore-type */ $destX + (int) $markerShadowOffsetX,
Loading history...
633
                    $destY + (int) $markerShadowOffsetY,
0 ignored issues
show
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

633
                    /** @scrutinizer ignore-type */ $destY + (int) $markerShadowOffsetY,
Loading history...
634
                    0,
635
                    0,
636
                    imagesx($markerShadowImg),
637
                    imagesy($markerShadowImg)
638
                );
639
            }
640
            // copy marker on basemap above shadow
641
            imagecopy(
642
                $this->image,
643
                $markerImg,
644
                $destX + (int) $markerImageOffsetX,
645
                $destY + (int) $markerImageOffsetY,
646
                0,
647
                0,
648
                imagesx($markerImg),
649
                imagesy($markerImg)
650
            );
651
            // add label
652
            imagestring(
653
                $this->image,
654
                3,
655
                $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

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

656
                /** @scrutinizer ignore-type */ $destY + (int) $markerImageOffsetY + 1,
Loading history...
657
                ++$count,
658
                $bgcolor
659
            );
660
            imagestring(
661
                $this->image,
662
                3,
663
                $destX - imagesx($markerImg),
664
                $destY + (int) $markerImageOffsetY,
665
                $count,
666
                $color
667
            );
668
        }
669
    }
670
671
    /**
672
     * Draw kml trace on the map.
673
     * @throws exception when loading the KML fails
674
     */
675
    public function drawKML(): void {
676
        // TODO get colour from kml node (not currently supported in geoPHP)
677
        $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

677
        $col     = imagecolorallocatealpha($this->image, 255, 0, 0, /** @scrutinizer ignore-type */ .4 * 127);
Loading history...
678
        $kmlgeom = geoPHP::load(file_get_contents($this->kmlFileName), 'kml');
679
        $this->drawGeometry($kmlgeom, $col);
680
    }
681
682
    /**
683
     * Draw geometry or geometry collection on the map.
684
     *
685
     * @param Geometry $geom
686
     * @param int      $colour
687
     *            drawing colour
688
     */
689
    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...
690
        if(empty($geom)) {
691
            return;
692
        }
693
694
        switch($geom->geometryType()) {
695
            case 'GeometryCollection' :
696
                // recursively draw part of the collection
697
                for($i = 1; $i < $geom->numGeometries() + 1; $i++) {
698
                    $_geom = $geom->geometryN($i);
699
                    $this->drawGeometry($_geom, $colour);
700
                }
701
                break;
702
            case 'MultiPolygon' :
703
            case 'MultiLineString' :
704
            case 'MultiPoint' :
705
                // TODO implement / do nothing
706
                break;
707
            case 'Polygon' :
708
                $this->drawPolygon($geom, $colour);
709
                break;
710
            case 'LineString' :
711
                $this->drawLineString($geom, $colour);
712
                break;
713
            case 'Point' :
714
                $this->drawPoint($geom, $colour);
715
                break;
716
            default :
717
                // draw nothing
718
                break;
719
        }
720
    }
721
722
    /**
723
     * Draw a polygon on the map.
724
     *
725
     * @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...
726
     * @param int     $colour
727
     *            drawing colour
728
     */
729
    private function drawPolygon($polygon, int $colour) {
730
        // TODO implementation of drawing holes,
731
        // maybe draw the polygon to an in-memory image and use imagecopy, draw polygon in col., draw holes in bgcol?
732
733
        // print_r('Polygon:<br />');
734
        // print_r($polygon);
735
        $extPoints = array();
736
        // extring is a linestring actually..
737
        $extRing = $polygon->exteriorRing();
738
739
        for($i = 1; $i < $extRing->numGeometries(); $i++) {
740
            $p1           = $extRing->geometryN($i);
741
            $x            = floor(
742
                ($this->width / 2) - $this->tileSize * ($this->centerX - $this->lonToTile($p1->x(), $this->zoom))
743
            );
744
            $y            = floor(
745
                ($this->height / 2) - $this->tileSize * ($this->centerY - $this->latToTile($p1->y(), $this->zoom))
746
            );
747
            $extPoints [] = $x;
748
            $extPoints [] = $y;
749
        }
750
        // print_r('points:('.($i-1).')<br />');
751
        // print_r($extPoints);
752
        // imagepolygon ($this->image, $extPoints, $i-1, $colour );
753
        imagefilledpolygon($this->image, $extPoints, $i - 1, $colour);
754
    }
755
756
    /**
757
     * Draw a line on the map.
758
     *
759
     * @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...
760
     * @param int        $colour
761
     *            drawing colour
762
     */
763
    private function drawLineString($line, $colour) {
764
        imagesetthickness($this->image, 2);
765
        for($p = 1; $p < $line->numGeometries(); $p++) {
766
            // get first pair of points
767
            $p1 = $line->geometryN($p);
768
            $p2 = $line->geometryN($p + 1);
769
            // translate to paper space
770
            $x1 = floor(
771
                ($this->width / 2) - $this->tileSize * ($this->centerX - $this->lonToTile($p1->x(), $this->zoom))
772
            );
773
            $y1 = floor(
774
                ($this->height / 2) - $this->tileSize * ($this->centerY - $this->latToTile($p1->y(), $this->zoom))
775
            );
776
            $x2 = floor(
777
                ($this->width / 2) - $this->tileSize * ($this->centerX - $this->lonToTile($p2->x(), $this->zoom))
778
            );
779
            $y2 = floor(
780
                ($this->height / 2) - $this->tileSize * ($this->centerY - $this->latToTile($p2->y(), $this->zoom))
781
            );
782
            // draw to image
783
            imageline($this->image, $x1, $y1, $x2, $y2, $colour);
0 ignored issues
show
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

783
            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

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

783
            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

783
            imageline($this->image, $x1, $y1, /** @scrutinizer ignore-type */ $x2, $y2, $colour);
Loading history...
784
        }
785
        imagesetthickness($this->image, 1);
786
    }
787
788
    /**
789
     * Draw a point on the map.
790
     *
791
     * @param Point $point
792
     * @param int   $colour
793
     *            drawing colour
794
     */
795
    private function drawPoint($point, $colour) {
796
        imagesetthickness($this->image, 2);
797
        // translate to paper space
798
        $cx = floor(
799
            ($this->width / 2) - $this->tileSize * ($this->centerX - $this->lonToTile($point->x(), $this->zoom))
800
        );
801
        $cy = floor(
802
            ($this->height / 2) - $this->tileSize * ($this->centerY - $this->latToTile($point->y(), $this->zoom))
803
        );
804
        $r  = 5;
805
        // draw to image
806
        // imageellipse($this->image, $cx, $cy,$r, $r, $colour);
807
        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

807
        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

807
        imagefilledellipse($this->image, $cx, /** @scrutinizer ignore-type */ $cy, $r, $r, $colour);
Loading history...
808
        // don't use imageellipse because the imagesetthickness function has
809
        // no effect. So the better workaround is to use imagearc.
810
        imagearc($this->image, $cx, $cy, $r, $r, 0, 359, $colour);
0 ignored issues
show
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

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

810
        imagearc($this->image, /** @scrutinizer ignore-type */ $cx, $cy, $r, $r, 0, 359, $colour);
Loading history...
811
        imagesetthickness($this->image, 1);
812
    }
813
814
    /**
815
     * Draw gpx trace on the map.
816
     * @throws exception when loading the GPX fails
817
     */
818
    public function drawGPX() {
819
        $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

819
        $col     = imagecolorallocatealpha($this->image, 0, 0, 255, /** @scrutinizer ignore-type */ .4 * 127);
Loading history...
820
        $gpxgeom = geoPHP::load(file_get_contents($this->gpxFileName), 'gpx');
821
        $this->drawGeometry($gpxgeom, $col);
822
    }
823
824
    /**
825
     * Draw geojson on the map.
826
     * @throws exception when loading the JSON fails
827
     */
828
    public function drawGeojson() {
829
        $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

829
        $col     = imagecolorallocatealpha($this->image, 255, 0, 255, /** @scrutinizer ignore-type */ .4 * 127);
Loading history...
830
        $gpxgeom = geoPHP::load(file_get_contents($this->geojsonFileName), 'json');
831
        $this->drawGeometry($gpxgeom, $col);
832
    }
833
834
    /**
835
     * add copyright and origin notice and icons to the map.
836
     */
837
    public function drawCopyright() {
838
        $logoBaseDir = dirname(__FILE__) . '/' . 'logo/';
839
        $logoImg     = imagecreatefrompng($logoBaseDir . $this->tileInfo ['openstreetmap'] ['logo']);
840
        $textcolor   = imagecolorallocate($this->image, 0, 0, 0);
841
        $bgcolor     = imagecolorallocate($this->image, 200, 200, 200);
842
843
        imagecopy(
844
            $this->image,
845
            $logoImg,
846
            0,
847
            imagesy($this->image) - imagesy($logoImg),
848
            0,
849
            0,
850
            imagesx($logoImg),
851
            imagesy($logoImg)
852
        );
853
        imagestring(
854
            $this->image,
855
            1,
856
            imagesx($logoImg) + 2,
857
            imagesy($this->image) - imagesy($logoImg) + 1,
858
            $this->tileInfo ['openstreetmap'] ['txt'],
859
            $bgcolor
860
        );
861
        imagestring(
862
            $this->image,
863
            1,
864
            imagesx($logoImg) + 1,
865
            imagesy($this->image) - imagesy($logoImg),
866
            $this->tileInfo ['openstreetmap'] ['txt'],
867
            $textcolor
868
        );
869
870
        // additional tile source info, ie. who created/hosted the tiles
871
        $xIconOffset = 0;
872
        if($this->maptype === 'openstreetmap') {
873
            $mapAuthor = "(c) OpenStreetMap maps/CC BY-SA";
874
        } else {
875
            $mapAuthor   = $this->tileInfo [$this->maptype] ['txt'];
876
            $iconImg     = imagecreatefrompng($logoBaseDir . $this->tileInfo [$this->maptype] ['logo']);
877
            $xIconOffset = imagesx($iconImg);
878
            imagecopy(
879
                $this->image,
880
                $iconImg, imagesx($logoImg) + 1,
881
                imagesy($this->image) - imagesy($iconImg),
882
                0,
883
                0,
884
                imagesx($iconImg), imagesy($iconImg)
885
            );
886
        }
887
        imagestring(
888
            $this->image,
889
            1, imagesx($logoImg) + $xIconOffset + 4,
890
            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

890
            /** @scrutinizer ignore-type */ imagesy($this->image) - ceil(imagesy($logoImg) / 2) + 1,
Loading history...
891
            $mapAuthor,
892
            $bgcolor
893
        );
894
        imagestring(
895
            $this->image,
896
            1, imagesx($logoImg) + $xIconOffset + 3,
897
            imagesy($this->image) - ceil(imagesy($logoImg) / 2),
898
            $mapAuthor,
899
            $textcolor
900
        );
901
902
    }
903
}
904