Passed
Pull Request — master (#40)
by Mark
13:45
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-2022 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 geoPHP\Geometry\Geometry;
0 ignored issues
show
Bug introduced by
The type geoPHP\Geometry\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...
23
use geoPHP\Geometry\GeometryCollection;
0 ignored issues
show
Bug introduced by
The type geoPHP\Geometry\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...
24
use geoPHP\Geometry\LineString;
0 ignored issues
show
Bug introduced by
The type geoPHP\Geometry\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...
25
use geoPHP\Geometry\Point;
0 ignored issues
show
Bug introduced by
The type geoPHP\Geometry\Point 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...
26
use geoPHP\Geometry\Polygon;
0 ignored issues
show
Bug introduced by
The type geoPHP\Geometry\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...
27
use geoPHP\geoPHP;
0 ignored issues
show
Bug introduced by
The type geoPHP\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...
28
29
/**
30
 *
31
 * @author Mark C. Prins <[email protected]>
32
 * @author Gerhard Koch <gerhard.koch AT ymail.com>
33
 *
34
 */
35
class StaticMap {
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
        // OpenTopoMap sources
47
        'opentopomap' => array(
48
            'txt'  => '(c) OpenStreetMap data/ODbl, SRTM | style: (c) OpenTopoMap',
49
            'logo' => 'osm_logo.png',
50
            'url'  => 'https:/tile.opentopomap.org/{Z}/{X}/{Y}.png'
51
        ),
52
        // OCM sources
53
        'cycle'         => array(
54
            'txt'  => '(c) Thunderforest maps',
55
            'logo' => 'tf_logo.png',
56
            'url'  => 'https://tile.thunderforest.com/cycle/{Z}/{X}/{Y}.png'
57
        ),
58
        'transport'     => array(
59
            'txt'  => '(c) Thunderforest maps',
60
            'logo' => 'tf_logo.png',
61
            'url'  => 'https://tile.thunderforest.com/transport/{Z}/{X}/{Y}.png'
62
        ),
63
        'landscape'     => array(
64
            'txt'  => '(c) Thunderforest maps',
65
            'logo' => 'tf_logo.png',
66
            'url'  => 'https://tile.thunderforest.com/landscape/{Z}/{X}/{Y}.png'
67
        ),
68
        'outdoors'      => array(
69
            'txt'  => '(c) Thunderforest maps',
70
            'logo' => 'tf_logo.png',
71
            'url'  => 'https://tile.thunderforest.com/outdoors/{Z}/{X}/{Y}.png'
72
        ),
73
        'toner-lite'    => array(
74
            'txt'  => 'Stamen tiles',
75
            'logo' => 'stamen.png',
76
            'url'  => 'https://stamen-tiles.a.ssl.fastly.net/toner/{Z}/{X}/{Y}.png'
77
        ),
78
        'terrain'       => array(
79
            'txt'  => 'Stamen tiles',
80
            'logo' => 'stamen.png',
81
            'url'  => 'https://stamen-tiles.a.ssl.fastly.net/terrain/{Z}/{X}/{Y}.jpg'
82
        )
83
        //,
84
        // 'piste'=>array(
85
        // 'txt'=>'OpenPisteMap tiles',
86
        // 'logo'=>'piste_logo.png',
87
        // 'url'=>''),
88
        // 'sea'=>array(
89
        // 'txt'=>'OpenSeaMap tiles',
90
        // 'logo'=>'sea_logo.png',
91
        // 'url'=>''),
92
        // H&B sources
93
        //          'hikeandbike' => array (
94
        //                  'txt' => 'Hike & Bike Map',
95
        //                  'logo' => 'hnb_logo.png',
96
        //                  //'url' => 'http://toolserver.org/tiles/hikebike/{Z}/{X}/{Y}.png'
97
        //                  //moved to: https://www.toolserver.org/tiles/hikebike/12/2105/1388.png
98
        //                  'url' => 'http://c.tiles.wmflabs.org/hikebike/{Z}/{X}/{Y}.png'
99
        //          )
100
    );
101
    private $tileDefaultSrc = 'openstreetmap';
102
103
    // set up markers
104
    private $markerPrototypes = array(
105
        // found at http://www.mapito.net/map-marker-icons.html
106
        // these are 17x19 px with a pointer at the bottom left
107
        'lightblue' => array(
108
            'regex'        => '/^lightblue([0-9]+)$/',
109
            'extension'    => '.png',
110
            'shadow'       => false,
111
            'offsetImage'  => '0,-19',
112
            'offsetShadow' => false
113
        ),
114
        // openlayers std markers are 21x25px with shadow
115
        'ol-marker' => array(
116
            'regex'        => '/^marker(|-blue|-gold|-green|-red)+$/',
117
            'extension'    => '.png',
118
            'shadow'       => 'marker_shadow.png',
119
            'offsetImage'  => '-10,-25',
120
            'offsetShadow' => '-1,-13'
121
        ),
122
        // these are 16x16 px
123
        'ww_icon'   => array(
124
            'regex'        => '/ww_\S+$/',
125
            'extension'    => '.png',
126
            'shadow'       => false,
127
            'offsetImage'  => '-8,-8',
128
            'offsetShadow' => false
129
        ),
130
        // assume these are 16x16 px
131
        'rest'      => array(
132
            'regex'        => '/^(?!lightblue([0-9]+)$)(?!(ww_\S+$))(?!marker(|-blue|-gold|-green|-red)+$)(.*)/',
133
            'extension'    => '.png',
134
            'shadow'       => 'marker_shadow.png',
135
            'offsetImage'  => '-8,-8',
136
            'offsetShadow' => '-1,-1'
137
        )
138
    );
139
    private $centerX;
140
    private $centerY;
141
    private $offsetX;
142
    private $offsetY;
143
    private $image;
144
    private $zoom;
145
    private $lat;
146
    private $lon;
147
    private $width;
148
    private $height;
149
    private $markers;
150
    private $maptype;
151
    private $kmlFileName;
152
    private $gpxFileName;
153
    private $geojsonFileName;
154
    private $autoZoomExtent;
155
    private $apikey;
156
    private $tileCacheBaseDir;
157
    private $mapCacheBaseDir;
158
    private $mediaBaseDir;
159
    private $useTileCache;
160
    private $mapCacheID = '';
161
    private $mapCacheFile = '';
162
    private $mapCacheExtension = 'png';
163
164
    /**
165
     * Constructor.
166
     *
167
     * @param float  $lat
168
     *            Latitude (x) of center of map
169
     * @param float  $lon
170
     *            Longitude (y) of center of map
171
     * @param int    $zoom
172
     *            Zoomlevel
173
     * @param int    $width
174
     *            Width in pixels
175
     * @param int    $height
176
     *            Height in pixels
177
     * @param string $maptype
178
     *            Name of the map
179
     * @param array  $markers
180
     *            array of markers
181
     * @param string $gpx
182
     *            GPX filename
183
     * @param string $kml
184
     *            KML filename
185
     * @param string $geojson
186
     * @param string $mediaDir
187
     *            Directory to store/cache maps
188
     * @param string $tileCacheBaseDir
189
     *            Directory to cache map tiles
190
     * @param bool   $autoZoomExtent
191
     *            Wheter or not to override zoom/lat/lon and zoom to the extent of gpx/kml and markers
192
     * @param string $apikey
193
     */
194
    public function __construct(
195
        float $lat,
196
        float $lon,
197
        int $zoom,
198
        int $width,
199
        int $height,
200
        string $maptype,
201
        array $markers,
202
        string $gpx,
203
        string $kml,
204
        string $geojson,
205
        string $mediaDir,
206
        string $tileCacheBaseDir,
207
        bool $autoZoomExtent = true,
208
        string $apikey = ''
209
    ) {
210
        $this->zoom   = $zoom;
211
        $this->lat    = $lat;
212
        $this->lon    = $lon;
213
        $this->width  = $width;
214
        $this->height = $height;
215
        // validate + set maptype
216
        $this->maptype = $this->tileDefaultSrc;
217
        if(array_key_exists($maptype, $this->tileInfo)) {
218
            $this->maptype = $maptype;
219
        }
220
        $this->markers          = $markers;
221
        $this->kmlFileName      = $kml;
222
        $this->gpxFileName      = $gpx;
223
        $this->geojsonFileName  = $geojson;
224
        $this->mediaBaseDir     = $mediaDir;
225
        $this->tileCacheBaseDir = $tileCacheBaseDir . '/olmaptiles';
226
        $this->useTileCache     = $this->tileCacheBaseDir !== '';
227
        $this->mapCacheBaseDir  = $mediaDir . '/olmapmaps';
228
        $this->autoZoomExtent   = $autoZoomExtent;
229
        $this->apikey           = $apikey;
230
    }
231
232
    /**
233
     * get the map, this may return a reference to a cached copy.
234
     *
235
     * @return string url relative to media dir
236
     */
237
    public function getMap(): string {
238
        try {
239
            if($this->autoZoomExtent) {
240
                $this->autoZoom();
241
            }
242
        } catch(Exception $e) {
243
            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

243
            /** @scrutinizer ignore-call */ 
244
            dbglog($e);
Loading history...
244
        }
245
246
        // use map cache, so check cache for map
247
        if(!$this->checkMapCache()) {
248
            // map is not in cache, needs to be build
249
            $this->makeMap();
250
            $this->mkdirRecursive(dirname($this->mapCacheIDToFilename()), 0777);
251
            imagepng($this->image, $this->mapCacheIDToFilename(), 9);
252
        }
253
        $doc = $this->mapCacheIDToFilename();
254
        // make url relative to media dir
255
        return str_replace($this->mediaBaseDir, '', $doc);
256
    }
257
258
    /**
259
     * Calculate the lat/lon/zoom values to make sure that all of the markers and gpx/kml are on the map.
260
     *
261
     * @param float $paddingFactor
262
     *            buffer constant to enlarge (>1.0) the zoom level
263
     * @throws Exception if non-geometries are found in the collection
264
     */
265
    private function autoZoom(float $paddingFactor = 1.0): void {
266
        $geoms    = array();
267
        $geoms [] = new Point ($this->lon, $this->lat);
268
        if(!empty ($this->markers)) {
269
            foreach($this->markers as $marker) {
270
                $geoms [] = new Point ($marker ['lon'], $marker ['lat']);
271
            }
272
        }
273
        if(file_exists($this->kmlFileName)) {
274
            $g = geoPHP::load(file_get_contents($this->kmlFileName), 'kml');
275
            if($g !== false) {
276
                $geoms [] = $g;
277
            }
278
        }
279
        if(file_exists($this->gpxFileName)) {
280
            $g = geoPHP::load(file_get_contents($this->gpxFileName), 'gpx');
281
            if($g !== false) {
282
                $geoms [] = $g;
283
            }
284
        }
285
        if(file_exists($this->geojsonFileName)) {
286
            $g = geoPHP::load(file_get_contents($this->geojsonFileName), 'geojson');
287
            if($g !== false) {
288
                $geoms [] = $g;
289
            }
290
        }
291
292
        if(count($geoms) <= 1) {
293
            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

293
            /** @scrutinizer ignore-call */ 
294
            dbglog($geoms, "StaticMap::autoZoom: Skip setting autozoom options");
Loading history...
294
            return;
295
        }
296
297
        $geom     = new GeometryCollection ($geoms);
298
        $centroid = $geom->centroid();
299
        $bbox     = $geom->getBBox();
300
301
        // determine vertical resolution, this depends on the distance from the equator
302
        // $vy00 = log(tan(M_PI*(0.25 + $centroid->getY()/360)));
303
        $vy0 = log(tan(M_PI * (0.25 + $bbox ['miny'] / 360)));
304
        $vy1 = log(tan(M_PI * (0.25 + $bbox ['maxy'] / 360)));
305
        dbglog("StaticMap::autoZoom: vertical resolution: $vy0, $vy1");
306
        if ($vy1 - $vy0 === 0.0){
307
            $resolutionVertical = 0;
308
            dbglog("StaticMap::autoZoom: using $resolutionVertical");
309
        } else {
310
            $zoomFactorPowered  = ($this->height / 2) / (40.7436654315252 * ($vy1 - $vy0));
311
            $resolutionVertical = 360 / ($zoomFactorPowered * $this->tileSize);
312
        }
313
        // determine horizontal resolution
314
        $resolutionHorizontal = ($bbox ['maxx'] - $bbox ['minx']) / $this->width;
315
        dbglog("StaticMap::autoZoom: using $resolutionHorizontal");
316
        $resolution           = max($resolutionHorizontal, $resolutionVertical) * $paddingFactor;
317
        $zoom                 = $this->zoom;
318
        if ($resolution > 0){
319
            $zoom             = log(360 / ($resolution * $this->tileSize), 2);
320
        }
321
322
        if(is_finite($zoom) && $zoom < 15 && $zoom > 2) {
323
            $this->zoom = floor($zoom);
324
        }
325
        $this->lon = $centroid->getX();
326
        $this->lat = $centroid->getY();
327
        dbglog("StaticMap::autoZoom: Set autozoom options to: z: $this->zoom, lon: $this->lon, lat: $this->lat");
328
    }
329
330
    public function checkMapCache(): bool {
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
        return implode(
339
            "&", array(
340
                   $this->zoom,
341
                   $this->lat,
342
                   $this->lon,
343
                   $this->width,
344
                   $this->height,
345
                   serialize($this->markers),
346
                   $this->maptype,
347
                   $this->kmlFileName,
348
                   $this->gpxFileName,
349
                   $this->geojsonFileName
350
               )
351
        );
352
    }
353
354
    public function mapCacheIDToFilename(): string {
355
        if(!$this->mapCacheFile) {
356
            $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

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

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

458
                    $tileImage = imagecreatefromstring(/** @scrutinizer ignore-type */ $tileData);
Loading history...
459
                } else {
460
                    $tileImage = imagecreate($this->tileSize, $this->tileSize);
461
                    $color     = imagecolorallocate($tileImage, 255, 255, 255);
462
                    @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

462
                    /** @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...
463
                }
464
                $destX = ($x - $startX) * $this->tileSize + $this->offsetX;
465
                $destY = ($y - $startY) * $this->tileSize + $this->offsetY;
466
                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

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

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

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

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

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

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

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

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

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

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

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

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

789
            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

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

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

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

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

813
        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

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

816
        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

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

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

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

896
            /** @scrutinizer ignore-type */ imagesy($this->image) - ceil(imagesy($logoImg) / 2) + 1,
Loading history...
897
            $mapAuthor,
898
            $bgcolor
899
        );
900
        imagestring(
901
            $this->image,
902
            1, imagesx($logoImg) + $xIconOffset + 3,
903
            imagesy($this->image) - ceil(imagesy($logoImg) / 2),
904
            $mapAuthor,
905
            $textcolor
906
        );
907
908
    }
909
}
910