Passed
Pull Request — master (#8)
by Mark
02:40
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
use dokuwiki\Logger;
0 ignored issues
show
Bug introduced by
The type dokuwiki\Logger 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...
23
24
// phpcs:disable PSR1.Files.SideEffects
25
// TODO resolve side effect
26
include_once(realpath(__DIR__) . '/../geophp/geoPHP/geoPHP.inc');
27
28
/**
29
 *
30
 * @author Mark C. Prins <[email protected]>
31
 * @author Gerhard Koch <gerhard.koch AT ymail.com>
32
 *
33
 */
34
class StaticMap
35
{
36
37
    // the final output
38
    private $tileSize = 256;
39
    private $tileInfo = array(
40
        // OSM sources
41
        'openstreetmap' => array(
42
            'txt'  => '(c) OpenStreetMap data/ODbl',
43
            'logo' => 'osm_logo.png',
44
            'url'  => 'https://tile.openstreetmap.org/{Z}/{X}/{Y}.png'
45
        ),
46
        // OCM sources
47
        'cycle'         => array(
48
            'txt'  => '(c) Thunderforest maps',
49
            'logo' => 'tf_logo.png',
50
            'url'  => 'https://tile.thunderforest.com/cycle/{Z}/{X}/{Y}.png'
51
        ),
52
        'transport'     => array(
53
            'txt'  => '(c) Thunderforest maps',
54
            'logo' => 'tf_logo.png',
55
            'url'  => 'https://tile.thunderforest.com/transport/{Z}/{X}/{Y}.png'
56
        ),
57
        'landscape'     => array(
58
            'txt'  => '(c) Thunderforest maps',
59
            'logo' => 'tf_logo.png',
60
            'url'  => 'https://tile.thunderforest.com/landscape/{Z}/{X}/{Y}.png'
61
        ),
62
        'outdoors'      => array(
63
            'txt'  => '(c) Thunderforest maps',
64
            'logo' => 'tf_logo.png',
65
            'url'  => 'https://tile.thunderforest.com/outdoors/{Z}/{X}/{Y}.png'
66
        ),
67
        'toner-lite'    => array(
68
            'txt'  => 'Stamen tiles',
69
            'logo' => 'stamen.png',
70
            'url'  => 'https://stamen-tiles.a.ssl.fastly.net/toner/{Z}/{X}/{Y}.png'
71
        ),
72
        'terrain'       => array(
73
            'txt'  => 'Stamen tiles',
74
            'logo' => 'stamen.png',
75
            'url'  => 'https://stamen-tiles.a.ssl.fastly.net/terrain/{Z}/{X}/{Y}.jpg'
76
        )
77
        //,
78
        // 'piste'=>array(
79
        // 'txt'=>'OpenPisteMap tiles',
80
        // 'logo'=>'piste_logo.png',
81
        // 'url'=>''),
82
        // 'sea'=>array(
83
        // 'txt'=>'OpenSeaMap tiles',
84
        // 'logo'=>'sea_logo.png',
85
        // 'url'=>''),
86
        // H&B sources
87
        //          'hikeandbike' => array (
88
        //                  'txt' => 'Hike & Bike Map',
89
        //                  'logo' => 'hnb_logo.png',
90
        //                  //'url' => 'http://toolserver.org/tiles/hikebike/{Z}/{X}/{Y}.png'
91
        //                  //moved to: https://www.toolserver.org/tiles/hikebike/12/2105/1388.png
92
        //                  'url' => 'http://c.tiles.wmflabs.org/hikebike/{Z}/{X}/{Y}.png'
93
        //          )
94
    );
95
    private $tileDefaultSrc = 'openstreetmap';
96
97
    // set up markers
98
    private $markerPrototypes = array(
99
        // found at http://www.mapito.net/map-marker-icons.html
100
        // these are 17x19 px with a pointer at the bottom left
101
        'lightblue' => array(
102
            'regex'        => '/^lightblue([0-9]+)$/',
103
            'extension'    => '.png',
104
            'shadow'       => false,
105
            'offsetImage'  => '0,-19',
106
            'offsetShadow' => false
107
        ),
108
        // openlayers std markers are 21x25px with shadow
109
        'ol-marker' => array(
110
            'regex'        => '/^marker(|-blue|-gold|-green|-red)+$/',
111
            'extension'    => '.png',
112
            'shadow'       => 'marker_shadow.png',
113
            'offsetImage'  => '-10,-25',
114
            'offsetShadow' => '-1,-13'
115
        ),
116
        // these are 16x16 px
117
        'ww_icon'   => array(
118
            'regex'        => '/ww_\S+$/',
119
            'extension'    => '.png',
120
            'shadow'       => false,
121
            'offsetImage'  => '-8,-8',
122
            'offsetShadow' => false
123
        ),
124
        // assume these are 16x16 px
125
        'rest'      => array(
126
            'regex'        => '/^(?!lightblue([0-9]+)$)(?!(ww_\S+$))(?!marker(|-blue|-gold|-green|-red)+$)(.*)/',
127
            'extension'    => '.png',
128
            'shadow'       => 'marker_shadow.png',
129
            'offsetImage'  => '-8,-8',
130
            'offsetShadow' => '-1,-1'
131
        )
132
    );
133
    private $centerX;
134
    private $centerY;
135
    private $offsetX;
136
    private $offsetY;
137
    private $image;
138
    private $zoom;
139
    private $lat;
140
    private $lon;
141
    private $width;
142
    private $height;
143
    private $markers;
144
    private $maptype;
145
    private $kmlFileName;
146
    private $gpxFileName;
147
    private $geojsonFileName;
148
    private $autoZoomExtent;
149
    private $apikey;
150
    private $tileCacheBaseDir;
151
    private $mapCacheBaseDir;
152
    private $mediaBaseDir;
153
    private $useTileCache;
154
    private $mapCacheID = '';
155
    private $mapCacheFile = '';
156
    private $mapCacheExtension = 'png';
157
158
    /**
159
     * Constructor.
160
     *
161
     * @param float  $lat
162
     *            Latitude (x) of center of map
163
     * @param float  $lon
164
     *            Longitude (y) of center of map
165
     * @param int    $zoom
166
     *            Zoomlevel
167
     * @param int    $width
168
     *            Width in pixels
169
     * @param int    $height
170
     *            Height in pixels
171
     * @param string $maptype
172
     *            Name of the map
173
     * @param array  $markers
174
     *            array of markers
175
     * @param string $gpx
176
     *            GPX filename
177
     * @param string $kml
178
     *            KML filename
179
     * @param string $geojson
180
     * @param string $mediaDir
181
     *            Directory to store/cache maps
182
     * @param string $tileCacheBaseDir
183
     *            Directory to cache map tiles
184
     * @param bool   $autoZoomExtent
185
     *            Wheter or not to override zoom/lat/lon and zoom to the extent of gpx/kml and markers
186
     * @param string $apikey
187
     */
188
    public function __construct(
189
        float $lat,
190
        float $lon,
191
        int $zoom,
192
        int $width,
193
        int $height,
194
        string $maptype,
195
        array $markers,
196
        string $gpx,
197
        string $kml,
198
        string $geojson,
199
        string $mediaDir,
200
        string $tileCacheBaseDir,
201
        bool $autoZoomExtent = true,
202
        string $apikey = ''
203
    ) {
204
        $this->zoom   = $zoom;
205
        $this->lat    = $lat;
206
        $this->lon    = $lon;
207
        $this->width  = $width;
208
        $this->height = $height;
209
        // validate + set maptype
210
        $this->maptype = $this->tileDefaultSrc;
211
        if (array_key_exists($maptype, $this->tileInfo)) {
212
            $this->maptype = $maptype;
213
        }
214
        $this->markers          = $markers;
215
        $this->kmlFileName      = $kml;
216
        $this->gpxFileName      = $gpx;
217
        $this->geojsonFileName  = $geojson;
218
        $this->mediaBaseDir     = $mediaDir;
219
        $this->tileCacheBaseDir = $tileCacheBaseDir . '/olmaptiles';
220
        $this->useTileCache     = $this->tileCacheBaseDir !== '';
221
        $this->mapCacheBaseDir  = $mediaDir . '/olmapmaps';
222
        $this->autoZoomExtent   = $autoZoomExtent;
223
        $this->apikey           = $apikey;
224
    }
225
226
    /**
227
     * get the map, this may return a reference to a cached copy.
228
     *
229
     * @return string url relative to media dir
230
     */
231
    public function getMap(): string
232
    {
233
        try {
234
            if ($this->autoZoomExtent) {
235
                $this->autoZoom();
236
            }
237
        } catch (Exception $e) {
238
            Logger::debug($e);
239
        }
240
241
        // use map cache, so check cache for map
242
        if (!$this->checkMapCache()) {
243
            // map is not in cache, needs to be build
244
            $this->makeMap();
245
            $this->mkdirRecursive(dirname($this->mapCacheIDToFilename()), 0777);
246
            imagepng($this->image, $this->mapCacheIDToFilename(), 9);
247
        }
248
        $doc = $this->mapCacheIDToFilename();
249
        // make url relative to media dir
250
        return str_replace($this->mediaBaseDir, '', $doc);
251
    }
252
253
    /**
254
     * Calculate the lat/lon/zoom values to make sure that all of the markers and gpx/kml are on the map.
255
     * can throw an error like
256
     * "Fatal error: Uncaught Exception: Cannot create a collection with non-geometries in
257
     * D:\www\wild-water.nl\www\dokuwiki\lib\plugins\geophp\geoPHP\lib\geometry\Collection.class.php:29"
258
     *
259
     * @param float $paddingFactor
260
     *            buffer constant to enlarge (>1.0) the zoom level
261
     * @throws Exception
262
     */
263
    private function autoZoom(float $paddingFactor = 1.0): void
264
    {
265
        $geoms    = array();
266
        $geoms [] = new Point ($this->lon, $this->lat);
267
        if (!empty ($this->markers)) {
268
            foreach ($this->markers as $marker) {
269
                $geoms [] = new Point ($marker ['lon'], $marker ['lat']);
270
            }
271
        }
272
        if (file_exists($this->kmlFileName)) {
273
            $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...
274
            if ($g !== false) {
275
                $geoms [] = $g;
276
            }
277
        }
278
        if (file_exists($this->gpxFileName)) {
279
            $g = geoPHP::load(file_get_contents($this->gpxFileName), 'gpx');
280
            if ($g !== false) {
281
                $geoms [] = $g;
282
            }
283
        }
284
        if (file_exists($this->geojsonFileName)) {
285
            $g = geoPHP::load(file_get_contents($this->geojsonFileName), 'geojson');
286
            if ($g !== false) {
287
                $geoms [] = $g;
288
            }
289
        }
290
291
        if (count($geoms) <= 1) {
292
            Logger::debug("StaticMap::autoZoom: Skip setting autozoom options", $geoms);
293
            return;
294
        }
295
296
        $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...
297
        $centroid = $geom->centroid();
298
        $bbox     = $geom->getBBox();
299
300
        // determine vertical resolution, this depends on the distance from the equator
301
        // $vy00 = log(tan(M_PI*(0.25 + $centroid->getY()/360)));
302
        $vy0 = log(tan(M_PI * (0.25 + $bbox ['miny'] / 360)));
303
        $vy1 = log(tan(M_PI * (0.25 + $bbox ['maxy'] / 360)));
304
        Logger::debug("StaticMap::autoZoom: vertical resolution: $vy0, $vy1");
305
        if ($vy1 - $vy0 === 0.0){
306
            $resolutionVertical = 0;
307
            Logger::debug("StaticMap::autoZoom: using $resolutionVertical");
308
        } else {
309
            $zoomFactorPowered  = ($this->height / 2) / (40.7436654315252 * ($vy1 - $vy0));
310
            $resolutionVertical = 360 / ($zoomFactorPowered * $this->tileSize);
311
        }
312
        // determine horizontal resolution
313
        $resolutionHorizontal = ($bbox ['maxx'] - $bbox ['minx']) / $this->width;
314
        Logger::debug("StaticMap::autoZoom: using $resolutionHorizontal");
315
        $resolution           = max($resolutionHorizontal, $resolutionVertical) * $paddingFactor;
316
        $zoom                 = $this->zoom;
317
        if ($resolution > 0){
318
            $zoom             = log(360 / ($resolution * $this->tileSize), 2);
319
        }
320
321
        if (is_finite($zoom) && $zoom < 15 && $zoom > 2) {
322
            $this->zoom = floor($zoom);
323
        }
324
        $this->lon = $centroid->getX();
325
        $this->lat = $centroid->getY();
326
        Logger::debug("StaticMap::autoZoom: Set autozoom options to: z: $this->zoom, lon: $this->lon, lat: $this->lat");
327
    }
328
329
    public function checkMapCache(): bool
330
    {
331
        // side effect: set the mapCacheID
332
        $this->mapCacheID = md5($this->serializeParams());
333
        $filename         = $this->mapCacheIDToFilename();
334
        return file_exists($filename);
335
    }
336
337
    public function serializeParams(): string
338
    {
339
        return join(
340
            "&", array(
341
                   $this->zoom,
342
                   $this->lat,
343
                   $this->lon,
344
                   $this->width,
345
                   $this->height,
346
                   serialize($this->markers),
347
                   $this->maptype,
348
                   $this->kmlFileName,
349
                   $this->gpxFileName,
350
                   $this->geojsonFileName
351
               )
352
        );
353
    }
354
355
    public function mapCacheIDToFilename(): string
356
    {
357
        if (!$this->mapCacheFile) {
358
            $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

358
            $this->mapCacheFile = $this->mapCacheBaseDir . "/" . /** @scrutinizer ignore-type */ $this->maptype . "/" . $this->zoom . "/cache_"
Loading history...
359
                . substr($this->mapCacheID, 0, 2) . "/" . substr($this->mapCacheID, 2, 2)
360
                . "/" . substr($this->mapCacheID, 4);
361
        }
362
        return $this->mapCacheFile . "." . $this->mapCacheExtension;
363
    }
364
365
    /**
366
     * make the map.
367
     */
368
    public function makeMap(): void
369
    {
370
        $this->initCoords();
371
        $this->createBaseMap();
372
        if (!empty ($this->markers)) {
373
            $this->placeMarkers();
374
        }
375
        if (file_exists($this->kmlFileName)) {
376
            try {
377
                $this->drawKML();
378
            } catch (exception $e) {
379
                Logger::error('failed to load KML file', $e);
380
            }
381
        }
382
        if (file_exists($this->gpxFileName)) {
383
            try {
384
                $this->drawGPX();
385
            } catch (exception $e) {
386
                Logger::error('failed to load GPX file', $e);
387
            }
388
        }
389
        if (file_exists($this->geojsonFileName)) {
390
            try {
391
                $this->drawGeojson();
392
            } catch (exception $e) {
393
                Logger::error('failed to load GeoJSON file', $e);
394
            }
395
        }
396
397
        $this->drawCopyright();
398
    }
399
400
    /**
401
     */
402
    public function initCoords(): void
403
    {
404
        $this->centerX = $this->lonToTile($this->lon, $this->zoom);
405
        $this->centerY = $this->latToTile($this->lat, $this->zoom);
406
        $this->offsetX = floor((floor($this->centerX) - $this->centerX) * $this->tileSize);
407
        $this->offsetY = floor((floor($this->centerY) - $this->centerY) * $this->tileSize);
408
    }
409
410
    /**
411
     *
412
     * @param float $long
413
     * @param int   $zoom
414
     * @return float|int
415
     */
416
    public function lonToTile(float $long, int $zoom)
417
    {
418
        return (($long + 180) / 360) * pow(2, $zoom);
419
    }
420
421
    /**
422
     *
423
     * @param float $lat
424
     * @param int   $zoom
425
     * @return float|int
426
     */
427
    public function latToTile(float $lat, int $zoom)
428
    {
429
        return (1 - log(tan($lat * pi() / 180) + 1 / cos($lat * M_PI / 180)) / M_PI) / 2 * pow(2, $zoom);
430
    }
431
432
    /**
433
     * make basemap image.
434
     */
435
    public function createBaseMap(): void
436
    {
437
        $this->image   = imagecreatetruecolor($this->width, $this->height);
438
        $startX        = floor($this->centerX - ($this->width / $this->tileSize) / 2);
439
        $startY        = floor($this->centerY - ($this->height / $this->tileSize) / 2);
440
        $endX          = ceil($this->centerX + ($this->width / $this->tileSize) / 2);
441
        $endY          = ceil($this->centerY + ($this->height / $this->tileSize) / 2);
442
        $this->offsetX = -floor(($this->centerX - floor($this->centerX)) * $this->tileSize);
443
        $this->offsetY = -floor(($this->centerY - floor($this->centerY)) * $this->tileSize);
444
        $this->offsetX += floor($this->width / 2);
445
        $this->offsetY += floor($this->height / 2);
446
        $this->offsetX += floor($startX - floor($this->centerX)) * $this->tileSize;
447
        $this->offsetY += floor($startY - floor($this->centerY)) * $this->tileSize;
448
449
        for ($x = $startX; $x <= $endX; $x++) {
450
            for ($y = $startY; $y <= $endY; $y++) {
451
                $url = str_replace(
452
                    array(
453
                        '{Z}',
454
                        '{X}',
455
                        '{Y}'
456
                    ),
457
                    array(
458
                        $this->zoom,
459
                        $x,
460
                        $y
461
                    ),
462
                    $this->tileInfo [$this->maptype] ['url']
463
                );
464
465
                $tileData = $this->fetchTile($url);
466
                if ($tileData) {
467
                    $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

467
                    $tileImage = imagecreatefromstring(/** @scrutinizer ignore-type */ $tileData);
Loading history...
468
                } else {
469
                    $tileImage = imagecreate($this->tileSize, $this->tileSize);
470
                    $color     = imagecolorallocate($tileImage, 255, 255, 255);
471
                    @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

471
                    /** @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...
472
                }
473
                $destX = ($x - $startX) * $this->tileSize + $this->offsetX;
474
                $destY = ($y - $startY) * $this->tileSize + $this->offsetY;
475
                Logger::debug("imagecopy tile into image: $destX, $destY", $this->tileSize);
476
                imagecopy(
477
                    $this->image,
478
                    $tileImage,
479
                    $destX,
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

479
                    /** @scrutinizer ignore-type */ $destX,
Loading history...
480
                    $destY,
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

480
                    /** @scrutinizer ignore-type */ $destY,
Loading history...
481
                    0,
482
                    0,
483
                    $this->tileSize,
484
                    $this->tileSize
485
                );
486
            }
487
        }
488
    }
489
490
    /**
491
     * Fetch a tile and (if configured) store it in the cache.
492
     * @param string $url
493
     * @return bool|string
494
     * @todo refactor this to use dokuwiki\HTTP\HTTPClient or dokuwiki\HTTP\DokuHTTPClient
495
     *          for better proxy handling...
496
     */
497
    public function fetchTile(string $url)
498
    {
499
        if ($this->useTileCache && ($cached = $this->checkTileCache($url))) {
500
            return $cached;
501
        }
502
503
        $_UA = 'Mozilla/4.0 (compatible; DokuWikiSpatial HTTP Client; ' . PHP_OS . ')';
504
        if (function_exists("curl_init")) {
505
            // use cUrl
506
            $ch = curl_init();
507
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
508
            curl_setopt($ch, CURLOPT_USERAGENT, $_UA);
509
            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
510
            curl_setopt($ch, CURLOPT_URL, $url . $this->apikey);
511
            Logger::debug("StaticMap::fetchTile: getting: $url using curl_exec");
512
            $tile = curl_exec($ch);
513
            curl_close($ch);
514
        } else {
515
            // use file_get_contents
516
            global $conf;
517
            $opts = array(
518
                'http' => array(
519
                    'method'          => "GET",
520
                    'header'          => "Accept-language: en\r\n" . "User-Agent: $_UA\r\n" . "accept: image/png\r\n",
521
                    'request_fulluri' => true
522
                )
523
            );
524
            if (isset($conf['proxy']['host'], $conf['proxy']['port'])
525
                && $conf['proxy']['host'] !== ''
526
                && $conf['proxy']['port'] !== '') {
527
                $opts['http'] += ['proxy' => "tcp://" . $conf['proxy']['host'] . ":" . $conf['proxy']['port']];
528
            }
529
530
            $context = stream_context_create($opts);
531
            Logger::debug(
532
                "StaticMap::fetchTile: getting: $url . $this->apikey using file_get_contents and options $opts"
533
            );
534
            $tile = file_get_contents($url . $this->apikey, false, $context);
535
        }
536
        if ($tile && $this->useTileCache) {
537
            $this->writeTileToCache($url, $tile);
538
        }
539
        return $tile;
540
    }
541
542
    /**
543
     *
544
     * @param string $url
545
     * @return string|false
546
     */
547
    public function checkTileCache(string $url)
548
    {
549
        $filename = $this->tileUrlToFilename($url);
550
        if (file_exists($filename)) {
551
            return file_get_contents($filename);
552
        }
553
        return false;
554
    }
555
556
    /**
557
     *
558
     * @param string $url
559
     * @return string
560
     */
561
    public function tileUrlToFilename(string $url): string
562
    {
563
        return $this->tileCacheBaseDir . "/" . substr($url, strpos($url, '/') + 1);
564
    }
565
566
    /**
567
     * Write a tile into the cache.
568
     *
569
     * @param string $url
570
     * @param mixed  $data
571
     */
572
    public function writeTileToCache(string $url, $data): void
573
    {
574
        $filename = $this->tileUrlToFilename($url);
575
        $this->mkdirRecursive(dirname($filename), 0777);
576
        file_put_contents($filename, $data);
577
    }
578
579
    /**
580
     * Recursively create the directory.
581
     *
582
     * @param string $pathname
583
     *            The directory path.
584
     * @param int    $mode
585
     *            File access mode. For more information on modes, read the details on the chmod manpage.
586
     */
587
    public function mkdirRecursive(string $pathname, int $mode): bool
588
    {
589
        is_dir(dirname($pathname)) || $this->mkdirRecursive(dirname($pathname), $mode);
590
        return is_dir($pathname) || mkdir($pathname, $mode) || is_dir($pathname);
591
    }
592
593
    /**
594
     * Place markers on the map and number them in the same order as they are listed in the html.
595
     */
596
    public function placeMarkers(): void
597
    {
598
        $count               = 0;
599
        $color               = imagecolorallocate($this->image, 0, 0, 0);
600
        $bgcolor             = imagecolorallocate($this->image, 200, 200, 200);
601
        $markerBaseDir       = __DIR__ . '/icons';
602
        $markerImageOffsetX  = 0;
603
        $markerImageOffsetY  = 0;
604
        $markerShadowOffsetX = 0;
605
        $markerShadowOffsetY = 0;
606
        $markerShadowImg     = null;
607
        // loop thru marker array
608
        foreach ($this->markers as $marker) {
609
            // set some local variables
610
            $markerLat  = $marker ['lat'];
611
            $markerLon  = $marker ['lon'];
612
            $markerType = $marker ['type'];
613
            // clear variables from previous loops
614
            $markerFilename = '';
615
            $markerShadow   = '';
616
            $matches        = false;
617
            // check for marker type, get settings from markerPrototypes
618
            if ($markerType) {
619
                foreach ($this->markerPrototypes as $markerPrototype) {
620
                    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

620
                    if (preg_match($markerPrototype ['regex'], $markerType, /** @scrutinizer ignore-type */ $matches)) {
Loading history...
621
                        $markerFilename = $matches [0] . $markerPrototype ['extension'];
622
                        if ($markerPrototype ['offsetImage']) {
623
                            list ($markerImageOffsetX, $markerImageOffsetY) = explode(
624
                                ",",
625
                                $markerPrototype ['offsetImage']
626
                            );
627
                        }
628
                        $markerShadow = $markerPrototype ['shadow'];
629
                        if ($markerShadow) {
630
                            list ($markerShadowOffsetX, $markerShadowOffsetY) = explode(
631
                                ",",
632
                                $markerPrototype ['offsetShadow']
633
                            );
634
                        }
635
                    }
636
                }
637
            }
638
            // create img resource
639
            if (file_exists($markerBaseDir . '/' . $markerFilename)) {
640
                $markerImg = imagecreatefrompng($markerBaseDir . '/' . $markerFilename);
641
            } else {
642
                $markerImg = imagecreatefrompng($markerBaseDir . '/marker.png');
643
            }
644
            // check for shadow + create shadow recource
645
            if ($markerShadow && file_exists($markerBaseDir . '/' . $markerShadow)) {
646
                $markerShadowImg = imagecreatefrompng($markerBaseDir . '/' . $markerShadow);
647
            }
648
            // calc position
649
            $destX = floor(
650
                ($this->width / 2) -
651
                $this->tileSize * ($this->centerX - $this->lonToTile($markerLon, $this->zoom))
652
            );
653
            $destY = floor(
654
                ($this->height / 2) -
655
                $this->tileSize * ($this->centerY - $this->latToTile($markerLat, $this->zoom))
656
            );
657
            // copy shadow on basemap
658
            if ($markerShadow && $markerShadowImg) {
659
                imagecopy(
660
                    $this->image,
661
                    $markerShadowImg,
662
                    $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

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

663
                    /** @scrutinizer ignore-type */ $destY + (int)$markerShadowOffsetY,
Loading history...
664
                    0,
665
                    0,
666
                    imagesx($markerShadowImg),
667
                    imagesy($markerShadowImg)
668
                );
669
            }
670
            // copy marker on basemap above shadow
671
            imagecopy(
672
                $this->image,
673
                $markerImg,
674
                $destX + (int)$markerImageOffsetX,
675
                $destY + (int)$markerImageOffsetY,
676
                0,
677
                0,
678
                imagesx($markerImg),
679
                imagesy($markerImg)
680
            );
681
            // add label
682
            imagestring(
683
                $this->image,
684
                3,
685
                $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

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

686
                /** @scrutinizer ignore-type */ $destY + (int)$markerImageOffsetY + 1,
Loading history...
687
                ++$count,
688
                $bgcolor
689
            );
690
            imagestring(
691
                $this->image,
692
                3,
693
                $destX - imagesx($markerImg),
694
                $destY + (int)$markerImageOffsetY,
695
                $count,
696
                $color
697
            );
698
        }
699
    }
700
701
    /**
702
     * Draw kml trace on the map.
703
     * @throws exception if loading the specified KML fails
704
     */
705
    public function drawKML(): void
706
    {
707
        // TODO get colour from kml node (not currently supported in geoPHP)
708
        $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

708
        $col     = imagecolorallocatealpha($this->image, 255, 0, 0, /** @scrutinizer ignore-type */ .4 * 127);
Loading history...
709
        $kmlgeom = geoPHP::load(file_get_contents($this->kmlFileName), 'kml');
710
        $this->drawGeometry($kmlgeom, $col);
711
    }
712
713
    /**
714
     * Draw geometry or geometry collection on the map.
715
     *
716
     * @param Geometry|GeometryCollection|MultiPolygon|MultiLineString|MultiPoint|Polygon|LineString|Point $geom
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...
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...
Bug introduced by
The type MultiLineString 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...
Bug introduced by
The type MultiPolygon 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...
Bug introduced by
The type MultiPoint 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...
717
     * @param int                                                                                          $colour
718
     *            drawing colour
719
     */
720
    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...
721
    {
722
        if (empty($geom)) {
723
            return;
724
        }
725
726
        switch ($geom->geometryType()) {
727
            case 'GeometryCollection' :
728
                // recursively draw part of the collection
729
                for ($i = 1; $i < $geom->numGeometries() + 1; $i++) {
730
                    $_geom = $geom->geometryN($i);
731
                    $this->drawGeometry($_geom, $colour);
732
                }
733
                break;
734
            case 'MultiPolygon' :
735
            case 'MultiLineString' :
736
            case 'MultiPoint' :
737
                // TODO implement / do nothing
738
                break;
739
            case 'Polygon' :
740
                $this->drawPolygon($geom, $colour);
741
                break;
742
            case 'LineString' :
743
                $this->drawLineString($geom, $colour);
744
                break;
745
            case 'Point' :
746
                $this->drawPoint($geom, $colour);
747
                break;
748
            default :
749
                // draw nothing
750
                break;
751
        }
752
    }
753
754
    /**
755
     * Draw a polygon on the map.
756
     *
757
     * @param Polygon $polygon
758
     * @param int     $colour
759
     *            drawing colour
760
     */
761
    private function drawPolygon(Polygon $polygon, int $colour)
762
    {
763
        // TODO implementation of drawing holes,
764
        // maybe draw the polygon to an in-memory image and use imagecopy, draw polygon in col., draw holes in bgcol?
765
766
        // print_r('Polygon:<br />');
767
        // print_r($polygon);
768
        $extPoints = array();
769
        // extring is a linestring actually..
770
        $extRing = $polygon->exteriorRing();
771
772
        for ($i = 1; $i < $extRing->numGeometries(); $i++) {
773
            $p1           = $extRing->geometryN($i);
774
            $x            = floor(
775
                ($this->width / 2) - $this->tileSize * ($this->centerX - $this->lonToTile($p1->x(), $this->zoom))
776
            );
777
            $y            = floor(
778
                ($this->height / 2) - $this->tileSize * ($this->centerY - $this->latToTile($p1->y(), $this->zoom))
779
            );
780
            $extPoints [] = $x;
781
            $extPoints [] = $y;
782
        }
783
        // print_r('points:('.($i-1).')<br />');
784
        // print_r($extPoints);
785
        // imagepolygon ($this->image, $extPoints, $i-1, $colour );
786
        imagefilledpolygon($this->image, $extPoints, $i - 1, $colour);
787
    }
788
789
    /**
790
     * Draw a line on the map.
791
     *
792
     * @param LineString $line
793
     * @param int        $colour
794
     *            drawing colour
795
     */
796
    private function drawLineString(LineString $line, int $colour)
797
    {
798
        imagesetthickness($this->image, 2);
799
        for ($p = 1; $p < $line->numGeometries(); $p++) {
800
            // get first pair of points
801
            $p1 = $line->geometryN($p);
802
            $p2 = $line->geometryN($p + 1);
803
            // translate to paper space
804
            $x1 = floor(
805
                ($this->width / 2) - $this->tileSize * ($this->centerX - $this->lonToTile($p1->x(), $this->zoom))
806
            );
807
            $y1 = floor(
808
                ($this->height / 2) - $this->tileSize * ($this->centerY - $this->latToTile($p1->y(), $this->zoom))
809
            );
810
            $x2 = floor(
811
                ($this->width / 2) - $this->tileSize * ($this->centerX - $this->lonToTile($p2->x(), $this->zoom))
812
            );
813
            $y2 = floor(
814
                ($this->height / 2) - $this->tileSize * ($this->centerY - $this->latToTile($p2->y(), $this->zoom))
815
            );
816
            // draw to image
817
            imageline($this->image, $x1, $y1, $x2, $y2, $colour);
0 ignored issues
show
Bug introduced by
$y1 of type double is incompatible with the type integer expected by parameter $y1 of imageline(). ( Ignorable by Annotation )

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

817
            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

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

817
            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

817
            imageline($this->image, /** @scrutinizer ignore-type */ $x1, $y1, $x2, $y2, $colour);
Loading history...
818
        }
819
        imagesetthickness($this->image, 1);
820
    }
821
822
    /**
823
     * Draw a point on the map.
824
     *
825
     * @param Point $point
826
     * @param int   $colour
827
     *            drawing colour
828
     */
829
    private function drawPoint(Point $point, int $colour)
830
    {
831
        imagesetthickness($this->image, 2);
832
        // translate to paper space
833
        $cx = floor(
834
            ($this->width / 2) - $this->tileSize * ($this->centerX - $this->lonToTile($point->x(), $this->zoom))
835
        );
836
        $cy = floor(
837
            ($this->height / 2) - $this->tileSize * ($this->centerY - $this->latToTile($point->y(), $this->zoom))
838
        );
839
        $r  = 5;
840
        // draw to image
841
        // imageellipse($this->image, $cx, $cy,$r, $r, $colour);
842
        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

842
        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

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

845
        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

845
        imagearc($this->image, $cx, /** @scrutinizer ignore-type */ $cy, $r, $r, 0, 359, $colour);
Loading history...
846
        imagesetthickness($this->image, 1);
847
    }
848
849
    /**
850
     * Draw gpx trace on the map.
851
     * @throws exception if loading the specified GPX fails
852
     */
853
    public function drawGPX()
854
    {
855
        $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

855
        $col     = imagecolorallocatealpha($this->image, 0, 0, 255, /** @scrutinizer ignore-type */ .4 * 127);
Loading history...
856
        $gpxgeom = geoPHP::load(file_get_contents($this->gpxFileName), 'gpx');
857
        $this->drawGeometry($gpxgeom, $col);
858
    }
859
860
    /**
861
     * Draw geojson on the map.
862
     * @throws exception if loading the specified GeoJSON fails
863
     */
864
    public function drawGeojson()
865
    {
866
        $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

866
        $col     = imagecolorallocatealpha($this->image, 255, 0, 255, /** @scrutinizer ignore-type */ .4 * 127);
Loading history...
867
        $gpxgeom = geoPHP::load(file_get_contents($this->geojsonFileName), 'json');
868
        $this->drawGeometry($gpxgeom, $col);
869
    }
870
871
    /**
872
     * add copyright and origin notice and icons to the map.
873
     */
874
    public function drawCopyright()
875
    {
876
        $logoBaseDir = dirname(__FILE__) . '/' . 'logo/';
877
        $logoImg     = imagecreatefrompng($logoBaseDir . $this->tileInfo ['openstreetmap'] ['logo']);
878
        $textcolor   = imagecolorallocate($this->image, 0, 0, 0);
879
        $bgcolor     = imagecolorallocate($this->image, 200, 200, 200);
880
881
        imagecopy(
882
            $this->image,
883
            $logoImg,
884
            0,
885
            imagesy($this->image) - imagesy($logoImg),
886
            0,
887
            0,
888
            imagesx($logoImg),
889
            imagesy($logoImg)
890
        );
891
        imagestring(
892
            $this->image,
893
            1,
894
            imagesx($logoImg) + 2,
895
            imagesy($this->image) - imagesy($logoImg) + 1,
896
            $this->tileInfo ['openstreetmap'] ['txt'],
897
            $bgcolor
898
        );
899
        imagestring(
900
            $this->image,
901
            1,
902
            imagesx($logoImg) + 1,
903
            imagesy($this->image) - imagesy($logoImg),
904
            $this->tileInfo ['openstreetmap'] ['txt'],
905
            $textcolor
906
        );
907
908
        // additional tile source info, ie. who created/hosted the tiles
909
        $xIconOffset = 0;
910
        if ($this->maptype === 'openstreetmap') {
911
            $mapAuthor = "(c) OpenStreetMap maps/CC BY-SA";
912
        } else {
913
            $mapAuthor   = $this->tileInfo [$this->maptype] ['txt'];
914
            $iconImg     = imagecreatefrompng($logoBaseDir . $this->tileInfo [$this->maptype] ['logo']);
915
            $xIconOffset = imagesx($iconImg);
916
            imagecopy(
917
                $this->image,
918
                $iconImg,
919
                imagesx($logoImg) + 1,
920
                imagesy($this->image) - imagesy($iconImg),
921
                0,
922
                0,
923
                imagesx($iconImg),
924
                imagesy($iconImg)
925
            );
926
        }
927
        imagestring(
928
            $this->image,
929
            1,
930
            imagesx($logoImg) + $xIconOffset + 4,
931
            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

931
            /** @scrutinizer ignore-type */ imagesy($this->image) - ceil(imagesy($logoImg) / 2) + 1,
Loading history...
932
            $mapAuthor,
933
            $bgcolor
934
        );
935
        imagestring(
936
            $this->image,
937
            1,
938
            imagesx($logoImg) + $xIconOffset + 3,
939
            imagesy($this->image) - ceil(imagesy($logoImg) / 2),
940
            $mapAuthor,
941
            $textcolor
942
        );
943
    }
944
}
945