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

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

filter:
    dependency_paths: ["lib/*"]

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

Loading history...
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
    {
267
        $geoms    = array();
268
        $geoms [] = new Point ($this->lon, $this->lat);
269
        if (!empty ($this->markers)) {
270
            foreach ($this->markers as $marker) {
271
                $geoms [] = new Point ($marker ['lon'], $marker ['lat']);
272
            }
273
        }
274
        if (file_exists($this->kmlFileName)) {
275
            $g = geoPHP::load(file_get_contents($this->kmlFileName), 'kml');
276
            if ($g !== false) {
277
                $geoms [] = $g;
278
            }
279
        }
280
        if (file_exists($this->gpxFileName)) {
281
            $g = geoPHP::load(file_get_contents($this->gpxFileName), 'gpx');
282
            if ($g !== false) {
283
                $geoms [] = $g;
284
            }
285
        }
286
        if (file_exists($this->geojsonFileName)) {
287
            $g = geoPHP::load(file_get_contents($this->geojsonFileName), 'geojson');
288
            if ($g !== false) {
289
                $geoms [] = $g;
290
            }
291
        }
292
293
        if (count($geoms) <= 1) {
294
            Logger::debug("StaticMap::autoZoom: Skip setting autozoom options", $geoms);
295
            return;
296
        }
297
298
        $geom     = new GeometryCollection ($geoms);
299
        $centroid = $geom->centroid();
300
        $bbox     = $geom->getBBox();
301
302
        // determine vertical resolution, this depends on the distance from the equator
303
        // $vy00 = log(tan(M_PI*(0.25 + $centroid->getY()/360)));
304
        $vy0 = log(tan(M_PI * (0.25 + $bbox ['miny'] / 360)));
305
        $vy1 = log(tan(M_PI * (0.25 + $bbox ['maxy'] / 360)));
306
        Logger::debug("StaticMap::autoZoom: vertical resolution: $vy0, $vy1");
307
        if ($vy1 - $vy0 === 0.0){
308
            $resolutionVertical = 0;
309
            Logger::debug("StaticMap::autoZoom: using $resolutionVertical");
310
        } else {
311
            $zoomFactorPowered  = ($this->height / 2) / (40.7436654315252 * ($vy1 - $vy0));
312
            $resolutionVertical = 360 / ($zoomFactorPowered * $this->tileSize);
313
        }
314
        // determine horizontal resolution
315
        $resolutionHorizontal = ($bbox ['maxx'] - $bbox ['minx']) / $this->width;
316
        Logger::debug("StaticMap::autoZoom: using $resolutionHorizontal");
317
        $resolution           = max($resolutionHorizontal, $resolutionVertical) * $paddingFactor;
318
        $zoom                 = $this->zoom;
319
        if ($resolution > 0){
320
            $zoom             = log(360 / ($resolution * $this->tileSize), 2);
321
        }
322
323
        if (is_finite($zoom) && $zoom < 15 && $zoom > 2) {
324
            $this->zoom = floor($zoom);
325
        }
326
        $this->lon = $centroid->getX();
327
        $this->lat = $centroid->getY();
328
        Logger::debug("StaticMap::autoZoom: Set autozoom options to: z: $this->zoom, lon: $this->lon, lat: $this->lat");
329
    }
330
331
    public function checkMapCache(): bool
332
    {
333
        // side effect: set the mapCacheID
334
        $this->mapCacheID = md5($this->serializeParams());
335
        $filename         = $this->mapCacheIDToFilename();
336
        return file_exists($filename);
337
    }
338
339
    public function serializeParams(): string
340
    {
341
        return join(
342
            "&", array(
343
                   $this->zoom,
344
                   $this->lat,
345
                   $this->lon,
346
                   $this->width,
347
                   $this->height,
348
                   serialize($this->markers),
349
                   $this->maptype,
350
                   $this->kmlFileName,
351
                   $this->gpxFileName,
352
                   $this->geojsonFileName
353
               )
354
        );
355
    }
356
357
    public function mapCacheIDToFilename(): string
358
    {
359
        if (!$this->mapCacheFile) {
360
            $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

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

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

473
                    /** @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...
474
                }
475
                $destX = ($x - $startX) * $this->tileSize + $this->offsetX;
476
                $destY = ($y - $startY) * $this->tileSize + $this->offsetY;
477
                Logger::debug("imagecopy tile into image: $destX, $destY", $this->tileSize);
478
                imagecopy(
479
                    $this->image,
480
                    $tileImage,
481
                    $destX,
0 ignored issues
show
Bug introduced by
$destX of type double is incompatible with the type integer expected by parameter $dst_x of imagecopy(). ( Ignorable by Annotation )

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

481
                    /** @scrutinizer ignore-type */ $destX,
Loading history...
482
                    $destY,
0 ignored issues
show
Bug introduced by
$destY of type double is incompatible with the type integer expected by parameter $dst_y of imagecopy(). ( Ignorable by Annotation )

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

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

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

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

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

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

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

710
        $col     = imagecolorallocatealpha($this->image, 255, 0, 0, /** @scrutinizer ignore-type */ .4 * 127);
Loading history...
711
        $kmlgeom = geoPHP::load(file_get_contents($this->kmlFileName), 'kml');
712
        $this->drawGeometry($kmlgeom, $col);
713
    }
714
715
    /**
716
     * Draw geometry or geometry collection on the map.
717
     *
718
     * @param Geometry|GeometryCollection|MultiPolygon|MultiLineString|MultiPoint|Polygon|LineString|Point $geom
0 ignored issues
show
Bug introduced by
The type MultiLineString was not found. Maybe you did not declare it correctly or list all dependencies?

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

filter:
    dependency_paths: ["lib/*"]

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

Loading history...
Bug introduced by
The type MultiPoint was not found. Maybe you did not declare it correctly or list all dependencies?

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

filter:
    dependency_paths: ["lib/*"]

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

Loading history...
Bug introduced by
The type MultiPolygon was not found. Maybe you did not declare it correctly or list all dependencies?

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

filter:
    dependency_paths: ["lib/*"]

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

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

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

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

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

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

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

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

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

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

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

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

847
        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

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

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

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

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