Passed
Pull Request — master (#10)
by Mark
08:11
created

StaticMap::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 36
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

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

How to fix   Many Parameters   

Many Parameters

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

There are several approaches to avoid long parameter lists:

1
<?php
2
/*
3
 * Copyright (c) 2012-2018 Mark C. Prins <[email protected]>
4
 *
5
 * In part based on staticMapLite 0.03 available at http://staticmaplite.svn.sourceforge.net/viewvc/staticmaplite/
6
 *
7
 * Copyright (c) 2009 Gerhard Koch <gerhard.koch AT ymail.com>
8
 *
9
 * Licensed under the Apache License, Version 2.0 (the "License");
10
 * you may not use this file except in compliance with the License.
11
 * You may obtain a copy of the License at
12
 *
13
 *     http://www.apache.org/licenses/LICENSE-2.0
14
 *
15
 * Unless required by applicable law or agreed to in writing, software
16
 * distributed under the License is distributed on an "AS IS" BASIS,
17
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
 * See the License for the specific language governing permissions and
19
 * limitations under the License.
20
 */
21
22
// phpcs:disable PSR1.Files.SideEffects
23
// TODO resolve side effect
24
include_once(realpath(__DIR__) . '/../geophp/geoPHP/geoPHP.inc');
25
26
/**
27
 *
28
 * @author Mark C. Prins <[email protected]>
29
 * @author Gerhard Koch <gerhard.koch AT ymail.com>
30
 *
31
 */
32
class StaticMap {
33
34
    // the final output
35
    private $tileSize = 256;
36
    private $tileInfo = array(
37
        // OSM sources
38
        'openstreetmap' => array(
39
            'txt'  => '(c) OpenStreetMap data/ODbl',
40
            'logo' => 'osm_logo.png',
41
            'url'  => 'https://tile.openstreetmap.org/{Z}/{X}/{Y}.png'
42
        ),
43
        // OCM sources
44
        'cycle'         => array(
45
            'txt'  => '(c) Thunderforest maps',
46
            'logo' => 'tf_logo.png',
47
            'url'  => 'https://tile.thunderforest.com/cycle/{Z}/{X}/{Y}.png'
48
        ),
49
        'transport'     => array(
50
            'txt'  => '(c) Thunderforest maps',
51
            'logo' => 'tf_logo.png',
52
            'url'  => 'https://tile.thunderforest.com/transport/{Z}/{X}/{Y}.png'
53
        ),
54
        'landscape'     => array(
55
            'txt'  => '(c) Thunderforest maps',
56
            'logo' => 'tf_logo.png',
57
            'url'  => 'https://tile.thunderforest.com/landscape/{Z}/{X}/{Y}.png'
58
        ),
59
        'outdoors'      => array(
60
            'txt'  => '(c) Thunderforest maps',
61
            'logo' => 'tf_logo.png',
62
            'url'  => 'https://tile.thunderforest.com/outdoors/{Z}/{X}/{Y}.png'
63
        ),
64
        'toner-lite'    => array(
65
            'txt'  => 'Stamen tiles',
66
            'logo' => 'stamen.png',
67
            'url'  => 'http://tile.stamen.com/toner-lite/{Z}/{X}/{Y}.png'
68
        ),
69
        'terrain'       => array(
70
            'txt'  => 'Stamen tiles',
71
            'logo' => 'stamen.png',
72
            'url'  => 'http://tile.stamen.com/terrain/{Z}/{X}/{Y}.png'
73
        )
74
        //,
75
        // 'piste'=>array(
76
        // 'txt'=>'OpenPisteMap tiles',
77
        // 'logo'=>'piste_logo.png',
78
        // 'url'=>''),
79
        // 'sea'=>array(
80
        // 'txt'=>'OpenSeaMap tiles',
81
        // 'logo'=>'sea_logo.png',
82
        // 'url'=>''),
83
        // H&B sources
84
        //          'hikeandbike' => array (
85
        //                  'txt' => 'Hike & Bike Map',
86
        //                  'logo' => 'hnb_logo.png',
87
        //                  //'url' => 'http://toolserver.org/tiles/hikebike/{Z}/{X}/{Y}.png'
88
        //                  //moved to: https://www.toolserver.org/tiles/hikebike/12/2105/1388.png
89
        //                  'url' => 'http://c.tiles.wmflabs.org/hikebike/{Z}/{X}/{Y}.png'
90
        //          )
91
    );
92
    private $tileDefaultSrc = 'openstreetmap';
93
94
    // set up markers
95
    private $markerPrototypes = array(
96
        // found at http://www.mapito.net/map-marker-icons.html
97
        // these are 17x19 px with a pointer at the bottom left
98
        'lightblue' => array(
99
            'regex'        => '/^lightblue([0-9]+)$/',
100
            'extension'    => '.png',
101
            'shadow'       => false,
102
            'offsetImage'  => '0,-19',
103
            'offsetShadow' => false
104
        ),
105
        // openlayers std markers are 21x25px with shadow
106
        'ol-marker' => array(
107
            'regex'        => '/^marker(|-blue|-gold|-green|-red)+$/',
108
            'extension'    => '.png',
109
            'shadow'       => 'marker_shadow.png',
110
            'offsetImage'  => '-10,-25',
111
            'offsetShadow' => '-1,-13'
112
        ),
113
        // these are 16x16 px
114
        'ww_icon'   => array(
115
            'regex'        => '/ww_\S+$/',
116
            'extension'    => '.png',
117
            'shadow'       => false,
118
            'offsetImage'  => '-8,-8',
119
            'offsetShadow' => false
120
        ),
121
        // assume these are 16x16 px
122
        'rest'      => array(
123
            'regex'        => '/^(?!lightblue([0-9]+)$)(?!(ww_\S+$))(?!marker(|-blue|-gold|-green|-red)+$)(.*)/',
124
            'extension'    => '.png',
125
            'shadow'       => 'marker_shadow.png',
126
            'offsetImage'  => '-8,-8',
127
            'offsetShadow' => '-1,-1'
128
        )
129
    );
130
    private $centerX;
131
    private $centerY;
132
    private $offsetX;
133
    private $offsetY;
134
    private $image;
135
    private $zoom;
136
    private $lat;
137
    private $lon;
138
    private $width;
139
    private $height;
140
    private $markers;
141
    private $maptype;
142
    private $kmlFileName;
143
    private $gpxFileName;
144
    private $geojsonFileName;
145
    private $autoZoomExtent;
146
    private $apikey;
147
    private $tileCacheBaseDir;
148
    private $mapCacheBaseDir;
149
    private $mediaBaseDir;
150
    private $useTileCache;
151
    private $mapCacheID = '';
152
    private $mapCacheFile = '';
153
    private $mapCacheExtension = 'png';
154
155
    /**
156
     * Constructor.
157
     *
158
     * @param float  $lat
159
     *            Latitude (x) of center of map
160
     * @param float  $lon
161
     *            Longitude (y) of center of map
162
     * @param int    $zoom
163
     *            Zoomlevel
164
     * @param int    $width
165
     *            Width in pixels
166
     * @param int    $height
167
     *            Height in pixels
168
     * @param string $maptype
169
     *            Name of the map
170
     * @param array  $markers
171
     *            array of markers
172
     * @param string $gpx
173
     *            GPX filename
174
     * @param string $kml
175
     *            KML filename
176
     * @param string $geojson
177
     * @param string $mediaDir
178
     *            Directory to store/cache maps
179
     * @param string $tileCacheBaseDir
180
     *            Directory to cache map tiles
181
     * @param bool   $autoZoomExtent
182
     *            Wheter or not to override zoom/lat/lon and zoom to the extent of gpx/kml and markers
183
     * @param string $apikey
184
     */
185
    public function __construct(
186
        float $lat,
187
        float $lon,
188
        int $zoom,
189
        int $width,
190
        int $height,
191
        string $maptype,
192
        array $markers,
193
        string $gpx,
194
        string $kml,
195
        string $geojson,
196
        string $mediaDir,
197
        string $tileCacheBaseDir,
198
        bool $autoZoomExtent = true,
199
        string $apikey = ''
200
    ) {
201
        $this->zoom   = $zoom;
202
        $this->lat    = $lat;
203
        $this->lon    = $lon;
204
        $this->width  = $width;
205
        $this->height = $height;
206
        // validate + set maptype
207
        $this->maptype = $this->tileDefaultSrc;
208
        if(array_key_exists($maptype, $this->tileInfo)) {
209
            $this->maptype = $maptype;
210
        }
211
        $this->markers          = $markers;
212
        $this->kmlFileName      = $kml;
213
        $this->gpxFileName      = $gpx;
214
        $this->geojsonFileName  = $geojson;
215
        $this->mediaBaseDir     = $mediaDir;
216
        $this->tileCacheBaseDir = $tileCacheBaseDir . '/olmaptiles';
217
        $this->useTileCache     = $this->tileCacheBaseDir !== '';
218
        $this->mapCacheBaseDir  = $mediaDir . '/olmapmaps';
219
        $this->autoZoomExtent   = $autoZoomExtent;
220
        $this->apikey           = $apikey;
221
    }
222
223
    /**
224
     * get the map, this may return a reference to a cached copy.
225
     *
226
     * @return string url relative to media dir
227
     */
228
    public function getMap(): string {
229
        try {
230
            if($this->autoZoomExtent) {
231
                $this->autoZoom();
232
            }
233
        } catch(Exception $e) {
234
            dbglog($e);
0 ignored issues
show
Bug introduced by
The function dbglog was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

234
            /** @scrutinizer ignore-call */ 
235
            dbglog($e);
Loading history...
235
        }
236
237
        // use map cache, so check cache for map
238
        if(!$this->checkMapCache()) {
239
            // map is not in cache, needs to be build
240
            $this->makeMap();
241
            $this->mkdirRecursive(dirname($this->mapCacheIDToFilename()), 0777);
242
            imagepng($this->image, $this->mapCacheIDToFilename(), 9);
243
        }
244
        $doc = $this->mapCacheIDToFilename();
245
        // make url relative to media dir
246
        return str_replace($this->mediaBaseDir, '', $doc);
247
    }
248
249
    /**
250
     * Calculate the lat/lon/zoom values to make sure that all of the markers and gpx/kml are on the map.
251
     * can throw an error like
252
     * "Fatal error: Uncaught Exception: Cannot create a collection with non-geometries in
253
     * D:\www\wild-water.nl\www\dokuwiki\lib\plugins\geophp\geoPHP\lib\geometry\Collection.class.php:29"
254
     *
255
     * @param float $paddingFactor
256
     *            buffer constant to enlarge (>1.0) the zoom level
257
     * @throws Exception
258
     */
259
    private function autoZoom(float $paddingFactor = 1.0): void {
260
        $geoms    = array();
261
        $geoms [] = new Point ($this->lon, $this->lat);
262
        if(!empty ($this->markers)) {
263
            foreach($this->markers as $marker) {
264
                $geoms [] = new Point ($marker ['lon'], $marker ['lat']);
265
            }
266
        }
267
        if(file_exists($this->kmlFileName)) {
268
            $g = geoPHP::load(file_get_contents($this->kmlFileName), 'kml');
0 ignored issues
show
Bug introduced by
The type geoPHP was not found. Maybe you did not declare it correctly or list all dependencies?

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

filter:
    dependency_paths: ["lib/*"]

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

Loading history...
269
            if($g !== false) {
270
                $geoms [] = $g;
271
            }
272
        }
273
        if(file_exists($this->gpxFileName)) {
274
            $g = geoPHP::load(file_get_contents($this->gpxFileName), 'gpx');
275
            if($g !== false) {
276
                $geoms [] = $g;
277
            }
278
        }
279
        if(file_exists($this->geojsonFileName)) {
280
            $g = geoPHP::load(file_get_contents($this->geojsonFileName), 'geojson');
281
            if($g !== false) {
282
                $geoms [] = $g;
283
            }
284
        }
285
286
        if(count($geoms) <= 1) {
287
            dbglog($geoms, "StaticMap::autoZoom: Skip setting autozoom options");
0 ignored issues
show
Bug introduced by
The function dbglog was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

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

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

filter:
    dependency_paths: ["lib/*"]

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

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

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

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

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

453
                    /** @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...
454
                }
455
                $destX = ($x - $startX) * $this->tileSize + $this->offsetX;
456
                $destY = ($y - $startY) * $this->tileSize + $this->offsetY;
457
                dbglog($this->tileSize, "imagecopy tile into image: $destX, $destY");
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

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

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

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

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

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

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

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

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

653
                /** @scrutinizer ignore-type */ $destY + (int) $markerImageOffsetY + 1,
Loading history...
654
                ++$count,
655
                $bgcolor
656
            );
657
            imagestring(
658
                $this->image,
659
                3,
660
                $destX - imagesx($markerImg),
661
                $destY + (int) $markerImageOffsetY,
662
                $count,
663
                $color
664
            );
665
        }
666
    }
667
668
    /**
669
     * Draw kml trace on the map.
670
     */
671
    public function drawKML(): void {
672
        // TODO get colour from kml node (not currently supported in geoPHP)
673
        $col     = imagecolorallocatealpha($this->image, 255, 0, 0, .4 * 127);
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

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

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

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

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

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

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

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

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

803
        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

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

806
        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

806
        imagearc($this->image, /** @scrutinizer ignore-type */ $cx, $cy, $r, $r, 0, 359, $colour);
Loading history...
807
        imagesetthickness($this->image, 1);
808
    }
809
810
    /**
811
     * Draw gpx trace on the map.
812
     */
813
    public function drawGPX() {
814
        $col     = imagecolorallocatealpha($this->image, 0, 0, 255, .4 * 127);
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

814
        $col     = imagecolorallocatealpha($this->image, 0, 0, 255, /** @scrutinizer ignore-type */ .4 * 127);
Loading history...
815
        $gpxgeom = geoPHP::load(file_get_contents($this->gpxFileName), 'gpx');
816
        $this->drawGeometry($gpxgeom, $col);
817
    }
818
819
    /**
820
     * Draw geojson on the map.
821
     */
822
    public function drawGeojson() {
823
        $col     = imagecolorallocatealpha($this->image, 255, 0, 255, .4 * 127);
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

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

884
            /** @scrutinizer ignore-type */ imagesy($this->image) - ceil(imagesy($logoImg) / 2) + 1,
Loading history...
885
            $mapAuthor,
886
            $bgcolor
887
        );
888
        imagestring(
889
            $this->image,
890
            1, imagesx($logoImg) + $xIconOffset + 3,
891
            imagesy($this->image) - ceil(imagesy($logoImg) / 2),
892
            $mapAuthor,
893
            $textcolor
894
        );
895
896
    }
897
}
898