Completed
Push — master ( b8fb7e...15aea0 )
by Mark
18s queued 14s
created

StaticMap::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 36
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

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

How to fix   Many Parameters   

Many Parameters

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

There are several approaches to avoid long parameter lists:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

787
            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

787
            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

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

811
        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

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

814
        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

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

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

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

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