1628e43ccSMark Prins<?php 2a760825cSgithub-actions[bot] 3628e43ccSMark Prins/* 4751c8ef2SMark Prins * Copyright (c) 2012-2023 Mark C. Prins <mprins@users.sf.net> 5628e43ccSMark Prins * 6f4b9bdacSMark Prins * In part based on staticMapLite 0.03 available at http://staticmaplite.svn.sourceforge.net/viewvc/staticmaplite/ 7628e43ccSMark Prins * 8628e43ccSMark Prins * Copyright (c) 2009 Gerhard Koch <gerhard.koch AT ymail.com> 9628e43ccSMark Prins * 10628e43ccSMark Prins * Licensed under the Apache License, Version 2.0 (the "License"); 11628e43ccSMark Prins * you may not use this file except in compliance with the License. 12628e43ccSMark Prins * You may obtain a copy of the License at 13628e43ccSMark Prins * 14628e43ccSMark Prins * http://www.apache.org/licenses/LICENSE-2.0 15628e43ccSMark Prins * 16628e43ccSMark Prins * Unless required by applicable law or agreed to in writing, software 17628e43ccSMark Prins * distributed under the License is distributed on an "AS IS" BASIS, 18628e43ccSMark Prins * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19628e43ccSMark Prins * See the License for the specific language governing permissions and 20628e43ccSMark Prins * limitations under the License. 21628e43ccSMark Prins */ 22d2f1674eSMark Prinsnamespace dokuwiki\plugin\openlayersmap; 23ba56c962SMark Prins 243ebe658bSMark Prinsuse geoPHP\Geometry\Geometry; 253ebe658bSMark Prinsuse geoPHP\Geometry\GeometryCollection; 263ebe658bSMark Prinsuse geoPHP\Geometry\LineString; 273ebe658bSMark Prinsuse geoPHP\Geometry\Point; 283ebe658bSMark Prinsuse geoPHP\Geometry\Polygon; 293ebe658bSMark Prinsuse geoPHP\geoPHP; 30*f204b8caSMark Prinsuse dokuwiki\Logger; 313ebe658bSMark Prins 32628e43ccSMark Prins/** 33ab8cbd2bSMark Prins * 34628e43ccSMark Prins * @author Mark C. Prins <mprins@users.sf.net> 35628e43ccSMark Prins * @author Gerhard Koch <gerhard.koch AT ymail.com> 36628e43ccSMark Prins * 37628e43ccSMark Prins */ 38a760825cSgithub-actions[bot]class StaticMap 39a760825cSgithub-actions[bot]{ 40628e43ccSMark Prins // the final output 4157f8d5bbSMark Prins private $tileSize = 256; 42a760825cSgithub-actions[bot] private $tileInfo = [ 43628e43ccSMark Prins // OSM sources 44a760825cSgithub-actions[bot] 'openstreetmap' => ['txt' => '(c) OpenStreetMap data/ODbl', 'logo' => 'osm_logo.png', 'url' => 'https://tile.openstreetmap.org/{Z}/{X}/{Y}.png'], 45976e50f5SMark Prins // OpenTopoMap sources 46a760825cSgithub-actions[bot] 'opentopomap' => ['txt' => '(c) OpenStreetMap data/ODbl, SRTM | style: (c) OpenTopoMap', 'logo' => 'osm_logo.png', 'url' => 'https:/tile.opentopomap.org/{Z}/{X}/{Y}.png'], 47628e43ccSMark Prins // OCM sources 48a760825cSgithub-actions[bot] 'cycle' => ['txt' => '(c) Thunderforest maps', 'logo' => 'tf_logo.png', 'url' => 'https://tile.thunderforest.com/cycle/{Z}/{X}/{Y}.png'], 49a760825cSgithub-actions[bot] 'transport' => ['txt' => '(c) Thunderforest maps', 'logo' => 'tf_logo.png', 'url' => 'https://tile.thunderforest.com/transport/{Z}/{X}/{Y}.png'], 50a760825cSgithub-actions[bot] 'landscape' => ['txt' => '(c) Thunderforest maps', 'logo' => 'tf_logo.png', 'url' => 'https://tile.thunderforest.com/landscape/{Z}/{X}/{Y}.png'], 51a760825cSgithub-actions[bot] 'outdoors' => ['txt' => '(c) Thunderforest maps', 'logo' => 'tf_logo.png', 'url' => 'https://tile.thunderforest.com/outdoors/{Z}/{X}/{Y}.png'], 52a760825cSgithub-actions[bot] 'toner' => ['txt' => '(c) Stadia Maps;Stamen Design;OpenStreetMap contributors', 'logo' => 'stamen.png', 'url' => 'https://tiles-eu.stadiamaps.com/tiles/stamen_toner/{Z}/{X}/{Y}.png'], 53a760825cSgithub-actions[bot] 'terrain' => ['txt' => '(c) Stadia Maps;Stamen Design;OpenStreetMap contributors', 'logo' => 'stamen.png', 'url' => 'https://tiles-eu.stadiamaps.com/tiles/stamen_terrain/{Z}/{X}/{Y}.png'], 54a760825cSgithub-actions[bot] ]; 5557f8d5bbSMark Prins private $tileDefaultSrc = 'openstreetmap'; 56628e43ccSMark Prins 57628e43ccSMark Prins // set up markers 58a760825cSgithub-actions[bot] private $markerPrototypes = [ 59628e43ccSMark Prins // found at http://www.mapito.net/map-marker-icons.html 60628e43ccSMark Prins // these are 17x19 px with a pointer at the bottom left 61a760825cSgithub-actions[bot] 'lightblue' => ['regex' => '/^lightblue(\d+)$/', 'extension' => '.png', 'shadow' => false, 'offsetImage' => '0,-19', 'offsetShadow' => false], 62628e43ccSMark Prins // openlayers std markers are 21x25px with shadow 63a760825cSgithub-actions[bot] 'ol-marker' => ['regex' => '/^marker(|-blue|-gold|-green|-red)+$/', 'extension' => '.png', 'shadow' => 'marker_shadow.png', 'offsetImage' => '-10,-25', 'offsetShadow' => '-1,-13'], 64628e43ccSMark Prins // these are 16x16 px 65a760825cSgithub-actions[bot] 'ww_icon' => ['regex' => '/ww_\S+$/', 'extension' => '.png', 'shadow' => false, 'offsetImage' => '-8,-8', 'offsetShadow' => false], 66628e43ccSMark Prins // assume these are 16x16 px 67a760825cSgithub-actions[bot] 'rest' => ['regex' => '/^(?!lightblue(\d+)$)(?!(ww_\S+$))(?!marker(|-blue|-gold|-green|-red)+$)(.*)/', 'extension' => '.png', 'shadow' => 'marker_shadow.png', 'offsetImage' => '-8,-8', 'offsetShadow' => '-1,-1'], 68a760825cSgithub-actions[bot] ]; 6957f8d5bbSMark Prins private $centerX; 7057f8d5bbSMark Prins private $centerY; 7157f8d5bbSMark Prins private $offsetX; 7257f8d5bbSMark Prins private $offsetY; 7357f8d5bbSMark Prins private $image; 7457f8d5bbSMark Prins private $zoom; 7557f8d5bbSMark Prins private $lat; 7657f8d5bbSMark Prins private $lon; 7757f8d5bbSMark Prins private $width; 7857f8d5bbSMark Prins private $height; 7957f8d5bbSMark Prins private $markers; 8057f8d5bbSMark Prins private $maptype; 8157f8d5bbSMark Prins private $kmlFileName; 8257f8d5bbSMark Prins private $gpxFileName; 8357f8d5bbSMark Prins private $geojsonFileName; 8457f8d5bbSMark Prins private $autoZoomExtent; 8557f8d5bbSMark Prins private $apikey; 8657f8d5bbSMark Prins private $tileCacheBaseDir; 8757f8d5bbSMark Prins private $mapCacheBaseDir; 8857f8d5bbSMark Prins private $mediaBaseDir; 89257dffd7SMark Prins private $useTileCache; 9057f8d5bbSMark Prins private $mapCacheID = ''; 9157f8d5bbSMark Prins private $mapCacheFile = ''; 9257f8d5bbSMark Prins private $mapCacheExtension = 'png'; 93628e43ccSMark Prins 94628e43ccSMark Prins /** 95f4b9bdacSMark Prins * Constructor. 96ab8cbd2bSMark Prins * 97ab8cbd2bSMark Prins * @param float $lat 98ab8cbd2bSMark Prins * Latitude (x) of center of map 99ab8cbd2bSMark Prins * @param float $lon 100ab8cbd2bSMark Prins * Longitude (y) of center of map 101ab8cbd2bSMark Prins * @param int $zoom 102ab8cbd2bSMark Prins * Zoomlevel 103ab8cbd2bSMark Prins * @param int $width 104ab8cbd2bSMark Prins * Width in pixels 105ab8cbd2bSMark Prins * @param int $height 106ab8cbd2bSMark Prins * Height in pixels 107ab8cbd2bSMark Prins * @param string $maptype 108ab8cbd2bSMark Prins * Name of the map 10957f8d5bbSMark Prins * @param array $markers 110ab8cbd2bSMark Prins * array of markers 111ab8cbd2bSMark Prins * @param string $gpx 112ab8cbd2bSMark Prins * GPX filename 113ab8cbd2bSMark Prins * @param string $kml 114ab8cbd2bSMark Prins * KML filename 115257dffd7SMark Prins * @param string $geojson 116ab8cbd2bSMark Prins * @param string $mediaDir 117ab8cbd2bSMark Prins * Directory to store/cache maps 118ab8cbd2bSMark Prins * @param string $tileCacheBaseDir 119ab8cbd2bSMark Prins * Directory to cache map tiles 12057f8d5bbSMark Prins * @param bool $autoZoomExtent 121ab8cbd2bSMark Prins * Wheter or not to override zoom/lat/lon and zoom to the extent of gpx/kml and markers 122257dffd7SMark Prins * @param string $apikey 123628e43ccSMark Prins */ 12457f8d5bbSMark Prins public function __construct( 12557f8d5bbSMark Prins float $lat, 12657f8d5bbSMark Prins float $lon, 12757f8d5bbSMark Prins int $zoom, 12857f8d5bbSMark Prins int $width, 12957f8d5bbSMark Prins int $height, 13057f8d5bbSMark Prins string $maptype, 13157f8d5bbSMark Prins array $markers, 13257f8d5bbSMark Prins string $gpx, 13357f8d5bbSMark Prins string $kml, 13457f8d5bbSMark Prins string $geojson, 13557f8d5bbSMark Prins string $mediaDir, 13657f8d5bbSMark Prins string $tileCacheBaseDir, 13757f8d5bbSMark Prins bool $autoZoomExtent = true, 138257dffd7SMark Prins string $apikey = '' 13957f8d5bbSMark Prins ) { 140628e43ccSMark Prins $this->zoom = $zoom; 141628e43ccSMark Prins $this->lat = $lat; 142628e43ccSMark Prins $this->lon = $lon; 143628e43ccSMark Prins $this->width = $width; 144628e43ccSMark Prins $this->height = $height; 145628e43ccSMark Prins // validate + set maptype 146628e43ccSMark Prins $this->maptype = $this->tileDefaultSrc; 147628e43ccSMark Prins if (array_key_exists($maptype, $this->tileInfo)) { 148628e43ccSMark Prins $this->maptype = $maptype; 149628e43ccSMark Prins } 1502d11d700SMark Prins $this->markers = $markers; 1512d11d700SMark Prins $this->kmlFileName = $kml; 1522d11d700SMark Prins $this->gpxFileName = $gpx; 1536914b920SMark Prins $this->geojsonFileName = $geojson; 1542d11d700SMark Prins $this->mediaBaseDir = $mediaDir; 155628e43ccSMark Prins $this->tileCacheBaseDir = $tileCacheBaseDir . '/olmaptiles'; 156628e43ccSMark Prins $this->useTileCache = $this->tileCacheBaseDir !== ''; 157628e43ccSMark Prins $this->mapCacheBaseDir = $mediaDir . '/olmapmaps'; 1582d11d700SMark Prins $this->autoZoomExtent = $autoZoomExtent; 1595c603532SMark Prins $this->apikey = $apikey; 160628e43ccSMark Prins } 161628e43ccSMark Prins 162628e43ccSMark Prins /** 16357f8d5bbSMark Prins * get the map, this may return a reference to a cached copy. 164628e43ccSMark Prins * 16557f8d5bbSMark Prins * @return string url relative to media dir 166628e43ccSMark Prins */ 167a760825cSgithub-actions[bot] public function getMap(): string 168a760825cSgithub-actions[bot] { 16957f8d5bbSMark Prins try { 17057f8d5bbSMark Prins if ($this->autoZoomExtent) { 17157f8d5bbSMark Prins $this->autoZoom(); 172628e43ccSMark Prins } 17357f8d5bbSMark Prins } catch (Exception $e) { 174*f204b8caSMark Prins Logger::debug($e); 17557f8d5bbSMark Prins } 17657f8d5bbSMark Prins 17757f8d5bbSMark Prins // use map cache, so check cache for map 17857f8d5bbSMark Prins if (!$this->checkMapCache()) { 17957f8d5bbSMark Prins // map is not in cache, needs to be build 18057f8d5bbSMark Prins $this->makeMap(); 18157f8d5bbSMark Prins $this->mkdirRecursive(dirname($this->mapCacheIDToFilename()), 0777); 18257f8d5bbSMark Prins imagepng($this->image, $this->mapCacheIDToFilename(), 9); 18357f8d5bbSMark Prins } 184ba56c962SMark Prins $doc = $this->mapCacheIDToFilename(); 18557f8d5bbSMark Prins // make url relative to media dir 186ba56c962SMark Prins return str_replace($this->mediaBaseDir, '', $doc); 18757f8d5bbSMark Prins } 18857f8d5bbSMark Prins 189628e43ccSMark Prins /** 19057f8d5bbSMark Prins * Calculate the lat/lon/zoom values to make sure that all of the markers and gpx/kml are on the map. 191628e43ccSMark Prins * 19257f8d5bbSMark Prins * @param float $paddingFactor 19357f8d5bbSMark Prins * buffer constant to enlarge (>1.0) the zoom level 1943ebe658bSMark Prins * @throws Exception if non-geometries are found in the collection 195628e43ccSMark Prins */ 196a760825cSgithub-actions[bot] private function autoZoom(float $paddingFactor = 1.0): void 197a760825cSgithub-actions[bot] { 198a760825cSgithub-actions[bot] $geoms = []; 19957f8d5bbSMark Prins $geoms [] = new Point($this->lon, $this->lat); 200a760825cSgithub-actions[bot] if ($this->markers !== []) { 20157f8d5bbSMark Prins foreach ($this->markers as $marker) { 20257f8d5bbSMark Prins $geoms [] = new Point($marker ['lon'], $marker ['lat']); 20357f8d5bbSMark Prins } 20457f8d5bbSMark Prins } 20557f8d5bbSMark Prins if (file_exists($this->kmlFileName)) { 20657f8d5bbSMark Prins $g = geoPHP::load(file_get_contents($this->kmlFileName), 'kml'); 20757f8d5bbSMark Prins if ($g !== false) { 20857f8d5bbSMark Prins $geoms [] = $g; 20957f8d5bbSMark Prins } 21057f8d5bbSMark Prins } 21157f8d5bbSMark Prins if (file_exists($this->gpxFileName)) { 21257f8d5bbSMark Prins $g = geoPHP::load(file_get_contents($this->gpxFileName), 'gpx'); 21357f8d5bbSMark Prins if ($g !== false) { 21457f8d5bbSMark Prins $geoms [] = $g; 21557f8d5bbSMark Prins } 21657f8d5bbSMark Prins } 21757f8d5bbSMark Prins if (file_exists($this->geojsonFileName)) { 21857f8d5bbSMark Prins $g = geoPHP::load(file_get_contents($this->geojsonFileName), 'geojson'); 21957f8d5bbSMark Prins if ($g !== false) { 22057f8d5bbSMark Prins $geoms [] = $g; 22157f8d5bbSMark Prins } 22257f8d5bbSMark Prins } 22357f8d5bbSMark Prins 22457f8d5bbSMark Prins if (count($geoms) <= 1) { 225*f204b8caSMark Prins Logger::debug("StaticMap::autoZoom: Skip setting autozoom options", $geoms); 22657f8d5bbSMark Prins return; 22757f8d5bbSMark Prins } 22857f8d5bbSMark Prins 22957f8d5bbSMark Prins $geom = new GeometryCollection($geoms); 23057f8d5bbSMark Prins $centroid = $geom->centroid(); 23157f8d5bbSMark Prins $bbox = $geom->getBBox(); 23257f8d5bbSMark Prins 23357f8d5bbSMark Prins // determine vertical resolution, this depends on the distance from the equator 23457f8d5bbSMark Prins // $vy00 = log(tan(M_PI*(0.25 + $centroid->getY()/360))); 23557f8d5bbSMark Prins $vy0 = log(tan(M_PI * (0.25 + $bbox ['miny'] / 360))); 23657f8d5bbSMark Prins $vy1 = log(tan(M_PI * (0.25 + $bbox ['maxy'] / 360))); 237*f204b8caSMark Prins Logger::debug("StaticMap::autoZoom: vertical resolution: $vy0, $vy1"); 238e98967e1SMark Prins if ($vy1 - $vy0 === 0.0) { 239e98967e1SMark Prins $resolutionVertical = 0; 240*f204b8caSMark Prins Logger::debug("StaticMap::autoZoom: using $resolutionVertical"); 241e98967e1SMark Prins } else { 24257f8d5bbSMark Prins $zoomFactorPowered = ($this->height / 2) / (40.7436654315252 * ($vy1 - $vy0)); 24357f8d5bbSMark Prins $resolutionVertical = 360 / ($zoomFactorPowered * $this->tileSize); 244e98967e1SMark Prins } 24557f8d5bbSMark Prins // determine horizontal resolution 24657f8d5bbSMark Prins $resolutionHorizontal = ($bbox ['maxx'] - $bbox ['minx']) / $this->width; 247*f204b8caSMark Prins Logger::debug("StaticMap::autoZoom: using $resolutionHorizontal"); 24857f8d5bbSMark Prins $resolution = max($resolutionHorizontal, $resolutionVertical) * $paddingFactor; 2493e7791adSMark Prins $zoom = $this->zoom; 2503e7791adSMark Prins if ($resolution > 0) { 25157f8d5bbSMark Prins $zoom = log(360 / ($resolution * $this->tileSize), 2); 2523e7791adSMark Prins } 25357f8d5bbSMark Prins 25457f8d5bbSMark Prins if (is_finite($zoom) && $zoom < 15 && $zoom > 2) { 25557f8d5bbSMark Prins $this->zoom = floor($zoom); 25657f8d5bbSMark Prins } 25757f8d5bbSMark Prins $this->lon = $centroid->getX(); 25857f8d5bbSMark Prins $this->lat = $centroid->getY(); 259*f204b8caSMark Prins Logger::debug("StaticMap::autoZoom: Set autozoom options to: z: $this->zoom, lon: $this->lon, lat: $this->lat"); 26057f8d5bbSMark Prins } 26157f8d5bbSMark Prins 262a760825cSgithub-actions[bot] public function checkMapCache(): bool 263a760825cSgithub-actions[bot] { 26457f8d5bbSMark Prins // side effect: set the mapCacheID 26557f8d5bbSMark Prins $this->mapCacheID = md5($this->serializeParams()); 26657f8d5bbSMark Prins $filename = $this->mapCacheIDToFilename(); 26757f8d5bbSMark Prins return file_exists($filename); 26857f8d5bbSMark Prins } 26957f8d5bbSMark Prins 270a760825cSgithub-actions[bot] public function serializeParams(): string 271a760825cSgithub-actions[bot] { 2723e7791adSMark Prins return implode( 273a760825cSgithub-actions[bot] "&", 274a760825cSgithub-actions[bot] [$this->zoom, $this->lat, $this->lon, $this->width, $this->height, serialize($this->markers), $this->maptype, $this->kmlFileName, $this->gpxFileName, $this->geojsonFileName] 27557f8d5bbSMark Prins ); 27657f8d5bbSMark Prins } 27757f8d5bbSMark Prins 278a760825cSgithub-actions[bot] public function mapCacheIDToFilename(): string 279a760825cSgithub-actions[bot] { 28057f8d5bbSMark Prins if (!$this->mapCacheFile) { 28157f8d5bbSMark Prins $this->mapCacheFile = $this->mapCacheBaseDir . "/" . $this->maptype . "/" . $this->zoom . "/cache_" 28257f8d5bbSMark Prins . substr($this->mapCacheID, 0, 2) . "/" . substr($this->mapCacheID, 2, 2) 28357f8d5bbSMark Prins . "/" . substr($this->mapCacheID, 4); 28457f8d5bbSMark Prins } 28557f8d5bbSMark Prins return $this->mapCacheFile . "." . $this->mapCacheExtension; 28657f8d5bbSMark Prins } 28757f8d5bbSMark Prins 28857f8d5bbSMark Prins /** 28957f8d5bbSMark Prins * make the map. 29057f8d5bbSMark Prins */ 291a760825cSgithub-actions[bot] public function makeMap(): void 292a760825cSgithub-actions[bot] { 29357f8d5bbSMark Prins $this->initCoords(); 29457f8d5bbSMark Prins $this->createBaseMap(); 295a760825cSgithub-actions[bot] if ($this->markers !== []) { 29657f8d5bbSMark Prins $this->placeMarkers(); 297e98967e1SMark Prins } 298e98967e1SMark Prins if (file_exists($this->kmlFileName)) { 299e98967e1SMark Prins try { 30057f8d5bbSMark Prins $this->drawKML(); 301e98967e1SMark Prins } catch (exception $e) { 302*f204b8caSMark Prins Logger::error('failed to load KML file', $e); 303e98967e1SMark Prins } 304e98967e1SMark Prins } 305e98967e1SMark Prins if (file_exists($this->gpxFileName)) { 306e98967e1SMark Prins try { 30757f8d5bbSMark Prins $this->drawGPX(); 308e98967e1SMark Prins } catch (exception $e) { 309*f204b8caSMark Prins Logger::error('failed to load GPX file', $e); 310e98967e1SMark Prins } 311e98967e1SMark Prins } 312e98967e1SMark Prins if (file_exists($this->geojsonFileName)) { 313e98967e1SMark Prins try { 31457f8d5bbSMark Prins $this->drawGeojson(); 315e98967e1SMark Prins } catch (exception $e) { 316*f204b8caSMark Prins Logger::error('failed to load GeoJSON file', $e); 317e98967e1SMark Prins } 318e98967e1SMark Prins } 31957f8d5bbSMark Prins 32057f8d5bbSMark Prins $this->drawCopyright(); 321628e43ccSMark Prins } 322f4b9bdacSMark Prins 323628e43ccSMark Prins /** 324628e43ccSMark Prins */ 325a760825cSgithub-actions[bot] public function initCoords(): void 326a760825cSgithub-actions[bot] { 327628e43ccSMark Prins $this->centerX = $this->lonToTile($this->lon, $this->zoom); 328628e43ccSMark Prins $this->centerY = $this->latToTile($this->lat, $this->zoom); 329628e43ccSMark Prins $this->offsetX = floor((floor($this->centerX) - $this->centerX) * $this->tileSize); 330628e43ccSMark Prins $this->offsetY = floor((floor($this->centerY) - $this->centerY) * $this->tileSize); 331628e43ccSMark Prins } 332628e43ccSMark Prins 333628e43ccSMark Prins /** 33457f8d5bbSMark Prins * 33557f8d5bbSMark Prins * @param float $long 33657f8d5bbSMark Prins * @param int $zoom 33757f8d5bbSMark Prins * @return float|int 33857f8d5bbSMark Prins */ 339a760825cSgithub-actions[bot] public function lonToTile(float $long, int $zoom) 340a760825cSgithub-actions[bot] { 341a760825cSgithub-actions[bot] return (($long + 180) / 360) * 2 ** $zoom; 34257f8d5bbSMark Prins } 34357f8d5bbSMark Prins 34457f8d5bbSMark Prins /** 34557f8d5bbSMark Prins * 34657f8d5bbSMark Prins * @param float $lat 34757f8d5bbSMark Prins * @param int $zoom 34857f8d5bbSMark Prins * @return float|int 34957f8d5bbSMark Prins */ 350a760825cSgithub-actions[bot] public function latToTile(float $lat, int $zoom) 351a760825cSgithub-actions[bot] { 352a760825cSgithub-actions[bot] return (1 - log(tan($lat * M_PI / 180) + 1 / cos($lat * M_PI / 180)) / M_PI) / 2 * 2 ** $zoom; 35357f8d5bbSMark Prins } 35457f8d5bbSMark Prins 35557f8d5bbSMark Prins /** 356628e43ccSMark Prins * make basemap image. 357628e43ccSMark Prins */ 358a760825cSgithub-actions[bot] public function createBaseMap(): void 359a760825cSgithub-actions[bot] { 360628e43ccSMark Prins $this->image = imagecreatetruecolor($this->width, $this->height); 361628e43ccSMark Prins $startX = floor($this->centerX - ($this->width / $this->tileSize) / 2); 362628e43ccSMark Prins $startY = floor($this->centerY - ($this->height / $this->tileSize) / 2); 363628e43ccSMark Prins $endX = ceil($this->centerX + ($this->width / $this->tileSize) / 2); 364628e43ccSMark Prins $endY = ceil($this->centerY + ($this->height / $this->tileSize) / 2); 365628e43ccSMark Prins $this->offsetX = -floor(($this->centerX - floor($this->centerX)) * $this->tileSize); 366628e43ccSMark Prins $this->offsetY = -floor(($this->centerY - floor($this->centerY)) * $this->tileSize); 367628e43ccSMark Prins $this->offsetX += floor($this->width / 2); 368628e43ccSMark Prins $this->offsetY += floor($this->height / 2); 369628e43ccSMark Prins $this->offsetX += floor($startX - floor($this->centerX)) * $this->tileSize; 370628e43ccSMark Prins $this->offsetY += floor($startY - floor($this->centerY)) * $this->tileSize; 371628e43ccSMark Prins 372628e43ccSMark Prins for ($x = $startX; $x <= $endX; $x++) { 373628e43ccSMark Prins for ($y = $startY; $y <= $endY; $y++) { 37457f8d5bbSMark Prins $url = str_replace( 375a760825cSgithub-actions[bot] ['{Z}', '{X}', '{Y}'], 376a760825cSgithub-actions[bot] [$this->zoom, $x, $y], 377a760825cSgithub-actions[bot] $this->tileInfo [$this->maptype] ['url'] 37857f8d5bbSMark Prins ); 379c8eb1362SMark Prins 380628e43ccSMark Prins $tileData = $this->fetchTile($url); 381628e43ccSMark Prins if ($tileData) { 382628e43ccSMark Prins $tileImage = imagecreatefromstring($tileData); 383628e43ccSMark Prins } else { 384628e43ccSMark Prins $tileImage = imagecreate($this->tileSize, $this->tileSize); 385628e43ccSMark Prins $color = imagecolorallocate($tileImage, 255, 255, 255); 386628e43ccSMark Prins @imagestring($tileImage, 1, 127, 127, 'err', $color); 387628e43ccSMark Prins } 388628e43ccSMark Prins $destX = ($x - $startX) * $this->tileSize + $this->offsetX; 389628e43ccSMark Prins $destY = ($y - $startY) * $this->tileSize + $this->offsetY; 390*f204b8caSMark Prins Logger::debug("imagecopy tile into image: $destX, $destY", $this->tileSize); 39157f8d5bbSMark Prins imagecopy( 392a760825cSgithub-actions[bot] $this->image, 393a760825cSgithub-actions[bot] $tileImage, 394a760825cSgithub-actions[bot] $destX, 395a760825cSgithub-actions[bot] $destY, 396a760825cSgithub-actions[bot] 0, 397a760825cSgithub-actions[bot] 0, 398a760825cSgithub-actions[bot] $this->tileSize, 39957f8d5bbSMark Prins $this->tileSize 40057f8d5bbSMark Prins ); 401628e43ccSMark Prins } 402628e43ccSMark Prins } 403628e43ccSMark Prins } 404628e43ccSMark Prins 405628e43ccSMark Prins /** 4062d11d700SMark Prins * Fetch a tile and (if configured) store it in the cache. 4072d11d700SMark Prins * @param string $url 408257dffd7SMark Prins * @return bool|string 409ebb06a66SMark Prins * @todo refactor this to use dokuwiki\HTTP\HTTPClient or dokuwiki\HTTP\DokuHTTPClient 410ebb06a66SMark Prins * for better proxy handling... 4112d11d700SMark Prins */ 412a760825cSgithub-actions[bot] public function fetchTile(string $url) 413a760825cSgithub-actions[bot] { 414ab8cbd2bSMark Prins if ($this->useTileCache && ($cached = $this->checkTileCache($url))) 415ab8cbd2bSMark Prins return $cached; 416e4f115f4SMark Prins 417e4f115f4SMark Prins $_UA = 'Mozilla/4.0 (compatible; DokuWikiSpatial HTTP Client; ' . PHP_OS . ')'; 418e4f115f4SMark Prins if (function_exists("curl_init")) { 419e4f115f4SMark Prins // use cUrl 420628e43ccSMark Prins $ch = curl_init(); 421628e43ccSMark Prins curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 422e4f115f4SMark Prins curl_setopt($ch, CURLOPT_USERAGENT, $_UA); 423628e43ccSMark Prins curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); 424b63742deSMark Prins curl_setopt($ch, CURLOPT_URL, $url . $this->apikey); 425*f204b8caSMark Prins Logger::debug("StaticMap::fetchTile: getting: $url using curl_exec"); 426628e43ccSMark Prins $tile = curl_exec($ch); 427628e43ccSMark Prins curl_close($ch); 428e4f115f4SMark Prins } else { 429e4f115f4SMark Prins // use file_get_contents 430e4f115f4SMark Prins global $conf; 431a760825cSgithub-actions[bot] $opts = ['http' => ['method' => "GET", 'header' => "Accept-language: en\r\n" . "User-Agent: $_UA\r\n" . "accept: image/png\r\n", 'request_fulluri' => true]]; 432a760825cSgithub-actions[bot] if ( 433a760825cSgithub-actions[bot] isset($conf['proxy']['host'], $conf['proxy']['port']) 434ebb06a66SMark Prins && $conf['proxy']['host'] !== '' 435a760825cSgithub-actions[bot] && $conf['proxy']['port'] !== '' 436a760825cSgithub-actions[bot] ) { 437ebb06a66SMark Prins $opts['http'] += ['proxy' => "tcp://" . $conf['proxy']['host'] . ":" . $conf['proxy']['port']]; 438ebb06a66SMark Prins } 439ebb06a66SMark Prins 440e4f115f4SMark Prins $context = stream_context_create($opts); 441*f204b8caSMark Prins Logger::debug( 442*f204b8caSMark Prins "StaticMap::fetchTile: getting: $url . $this->apikey using file_get_contents and options $opts" 443*f204b8caSMark Prins ); 444b63742deSMark Prins $tile = file_get_contents($url . $this->apikey, false, $context); 445e4f115f4SMark Prins } 446628e43ccSMark Prins if ($tile && $this->useTileCache) { 447628e43ccSMark Prins $this->writeTileToCache($url, $tile); 448628e43ccSMark Prins } 449628e43ccSMark Prins return $tile; 450628e43ccSMark Prins } 451628e43ccSMark Prins 452628e43ccSMark Prins /** 45357f8d5bbSMark Prins * 45457f8d5bbSMark Prins * @param string $url 455257dffd7SMark Prins * @return string|false 456628e43ccSMark Prins */ 457a760825cSgithub-actions[bot] public function checkTileCache(string $url) 458a760825cSgithub-actions[bot] { 45957f8d5bbSMark Prins $filename = $this->tileUrlToFilename($url); 46057f8d5bbSMark Prins if (file_exists($filename)) { 46157f8d5bbSMark Prins return file_get_contents($filename); 46257f8d5bbSMark Prins } 463257dffd7SMark Prins return false; 464628e43ccSMark Prins } 465628e43ccSMark Prins 4666914b920SMark Prins /** 46757f8d5bbSMark Prins * 46857f8d5bbSMark Prins * @param string $url 4696914b920SMark Prins */ 470a760825cSgithub-actions[bot] public function tileUrlToFilename(string $url): string 471a760825cSgithub-actions[bot] { 472257dffd7SMark Prins return $this->tileCacheBaseDir . "/" . substr($url, strpos($url, '/') + 1); 47357f8d5bbSMark Prins } 47457f8d5bbSMark Prins 47557f8d5bbSMark Prins /** 47657f8d5bbSMark Prins * Write a tile into the cache. 47757f8d5bbSMark Prins * 47857f8d5bbSMark Prins * @param string $url 47957f8d5bbSMark Prins * @param mixed $data 48057f8d5bbSMark Prins */ 481a760825cSgithub-actions[bot] public function writeTileToCache($url, $data): void 482a760825cSgithub-actions[bot] { 48357f8d5bbSMark Prins $filename = $this->tileUrlToFilename($url); 48457f8d5bbSMark Prins $this->mkdirRecursive(dirname($filename), 0777); 48557f8d5bbSMark Prins file_put_contents($filename, $data); 48657f8d5bbSMark Prins } 48757f8d5bbSMark Prins 48857f8d5bbSMark Prins /** 48957f8d5bbSMark Prins * Recursively create the directory. 49057f8d5bbSMark Prins * 49157f8d5bbSMark Prins * @param string $pathname 49257f8d5bbSMark Prins * The directory path. 49357f8d5bbSMark Prins * @param int $mode 49457f8d5bbSMark Prins * File access mode. For more information on modes, read the details on the chmod manpage. 49557f8d5bbSMark Prins */ 496a760825cSgithub-actions[bot] public function mkdirRecursive(string $pathname, int $mode): bool 497a760825cSgithub-actions[bot] { 498a760825cSgithub-actions[bot] if (!is_dir(dirname($pathname))) { 499a760825cSgithub-actions[bot] $this->mkdirRecursive(dirname($pathname), $mode); 500a760825cSgithub-actions[bot] } 50157f8d5bbSMark Prins return is_dir($pathname) || mkdir($pathname, $mode) || is_dir($pathname); 50257f8d5bbSMark Prins } 50357f8d5bbSMark Prins 50457f8d5bbSMark Prins /** 50557f8d5bbSMark Prins * Place markers on the map and number them in the same order as they are listed in the html. 50657f8d5bbSMark Prins */ 507a760825cSgithub-actions[bot] public function placeMarkers(): void 508a760825cSgithub-actions[bot] { 50957f8d5bbSMark Prins $count = 0; 51057f8d5bbSMark Prins $color = imagecolorallocate($this->image, 0, 0, 0); 51157f8d5bbSMark Prins $bgcolor = imagecolorallocate($this->image, 200, 200, 200); 51257f8d5bbSMark Prins $markerBaseDir = __DIR__ . '/icons'; 513e98967e1SMark Prins $markerImageOffsetX = 0; 514e98967e1SMark Prins $markerImageOffsetY = 0; 515e98967e1SMark Prins $markerShadowOffsetX = 0; 516e98967e1SMark Prins $markerShadowOffsetY = 0; 517e98967e1SMark Prins $markerShadowImg = null; 51857f8d5bbSMark Prins // loop thru marker array 51957f8d5bbSMark Prins foreach ($this->markers as $marker) { 52057f8d5bbSMark Prins // set some local variables 52157f8d5bbSMark Prins $markerLat = $marker ['lat']; 52257f8d5bbSMark Prins $markerLon = $marker ['lon']; 52357f8d5bbSMark Prins $markerType = $marker ['type']; 52457f8d5bbSMark Prins // clear variables from previous loops 52557f8d5bbSMark Prins $markerFilename = ''; 52657f8d5bbSMark Prins $markerShadow = ''; 52757f8d5bbSMark Prins $matches = false; 52857f8d5bbSMark Prins // check for marker type, get settings from markerPrototypes 52957f8d5bbSMark Prins if ($markerType) { 53057f8d5bbSMark Prins foreach ($this->markerPrototypes as $markerPrototype) { 53157f8d5bbSMark Prins if (preg_match($markerPrototype ['regex'], $markerType, $matches)) { 53257f8d5bbSMark Prins $markerFilename = $matches [0] . $markerPrototype ['extension']; 53357f8d5bbSMark Prins if ($markerPrototype ['offsetImage']) { 534a760825cSgithub-actions[bot] [$markerImageOffsetX, $markerImageOffsetY] = explode( 53557f8d5bbSMark Prins ",", 53657f8d5bbSMark Prins $markerPrototype ['offsetImage'] 53757f8d5bbSMark Prins ); 53857f8d5bbSMark Prins } 53957f8d5bbSMark Prins $markerShadow = $markerPrototype ['shadow']; 54057f8d5bbSMark Prins if ($markerShadow) { 541a760825cSgithub-actions[bot] [$markerShadowOffsetX, $markerShadowOffsetY] = explode( 54257f8d5bbSMark Prins ",", 54357f8d5bbSMark Prins $markerPrototype ['offsetShadow'] 54457f8d5bbSMark Prins ); 54557f8d5bbSMark Prins } 54657f8d5bbSMark Prins } 54757f8d5bbSMark Prins } 54857f8d5bbSMark Prins } 54957f8d5bbSMark Prins // create img resource 55057f8d5bbSMark Prins if (file_exists($markerBaseDir . '/' . $markerFilename)) { 55157f8d5bbSMark Prins $markerImg = imagecreatefrompng($markerBaseDir . '/' . $markerFilename); 55257f8d5bbSMark Prins } else { 55357f8d5bbSMark Prins $markerImg = imagecreatefrompng($markerBaseDir . '/marker.png'); 55457f8d5bbSMark Prins } 55557f8d5bbSMark Prins // check for shadow + create shadow recource 55657f8d5bbSMark Prins if ($markerShadow && file_exists($markerBaseDir . '/' . $markerShadow)) { 55757f8d5bbSMark Prins $markerShadowImg = imagecreatefrompng($markerBaseDir . '/' . $markerShadow); 55857f8d5bbSMark Prins } 55957f8d5bbSMark Prins // calc position 56057f8d5bbSMark Prins $destX = floor( 56157f8d5bbSMark Prins ($this->width / 2) - 56257f8d5bbSMark Prins $this->tileSize * ($this->centerX - $this->lonToTile($markerLon, $this->zoom)) 56357f8d5bbSMark Prins ); 56457f8d5bbSMark Prins $destY = floor( 56557f8d5bbSMark Prins ($this->height / 2) - 56657f8d5bbSMark Prins $this->tileSize * ($this->centerY - $this->latToTile($markerLat, $this->zoom)) 56757f8d5bbSMark Prins ); 56857f8d5bbSMark Prins // copy shadow on basemap 56957f8d5bbSMark Prins if ($markerShadow && $markerShadowImg) { 57057f8d5bbSMark Prins imagecopy( 57157f8d5bbSMark Prins $this->image, 57257f8d5bbSMark Prins $markerShadowImg, 57357f8d5bbSMark Prins $destX + (int) $markerShadowOffsetX, 57457f8d5bbSMark Prins $destY + (int) $markerShadowOffsetY, 57557f8d5bbSMark Prins 0, 57657f8d5bbSMark Prins 0, 57757f8d5bbSMark Prins imagesx($markerShadowImg), 57857f8d5bbSMark Prins imagesy($markerShadowImg) 57957f8d5bbSMark Prins ); 58057f8d5bbSMark Prins } 58157f8d5bbSMark Prins // copy marker on basemap above shadow 58257f8d5bbSMark Prins imagecopy( 58357f8d5bbSMark Prins $this->image, 58457f8d5bbSMark Prins $markerImg, 58557f8d5bbSMark Prins $destX + (int) $markerImageOffsetX, 58657f8d5bbSMark Prins $destY + (int) $markerImageOffsetY, 58757f8d5bbSMark Prins 0, 58857f8d5bbSMark Prins 0, 58957f8d5bbSMark Prins imagesx($markerImg), 59057f8d5bbSMark Prins imagesy($markerImg) 59157f8d5bbSMark Prins ); 59257f8d5bbSMark Prins // add label 59357f8d5bbSMark Prins imagestring( 59457f8d5bbSMark Prins $this->image, 59557f8d5bbSMark Prins 3, 59657f8d5bbSMark Prins $destX - imagesx($markerImg) + 1, 59757f8d5bbSMark Prins $destY + (int) $markerImageOffsetY + 1, 59857f8d5bbSMark Prins ++$count, 59957f8d5bbSMark Prins $bgcolor 60057f8d5bbSMark Prins ); 60157f8d5bbSMark Prins imagestring( 60257f8d5bbSMark Prins $this->image, 60357f8d5bbSMark Prins 3, 60457f8d5bbSMark Prins $destX - imagesx($markerImg), 60557f8d5bbSMark Prins $destY + (int) $markerImageOffsetY, 60657f8d5bbSMark Prins $count, 60757f8d5bbSMark Prins $color 60857f8d5bbSMark Prins ); 60957f8d5bbSMark Prins } 6106914b920SMark Prins } 61157e65445SMark Prins 612628e43ccSMark Prins /** 613628e43ccSMark Prins * Draw kml trace on the map. 6143e7791adSMark Prins * @throws exception when loading the KML fails 615628e43ccSMark Prins */ 616a760825cSgithub-actions[bot] public function drawKML(): void 617a760825cSgithub-actions[bot] { 6182d11d700SMark Prins // TODO get colour from kml node (not currently supported in geoPHP) 619c977deacSMark Prins $col = imagecolorallocatealpha($this->image, 255, 0, 0, .4 * 127); 620c977deacSMark Prins $kmlgeom = geoPHP::load(file_get_contents($this->kmlFileName), 'kml'); 621c977deacSMark Prins $this->drawGeometry($kmlgeom, $col); 622c977deacSMark Prins } 62357e65445SMark Prins 624c977deacSMark Prins /** 625c977deacSMark Prins * Draw geometry or geometry collection on the map. 626ab8cbd2bSMark Prins * 627c977deacSMark Prins * @param Geometry $geom 628ab8cbd2bSMark Prins * @param int $colour 629ab8cbd2bSMark Prins * drawing colour 630c977deacSMark Prins */ 631a760825cSgithub-actions[bot] private function drawGeometry(Geometry $geom, int $colour): void 632a760825cSgithub-actions[bot] { 63357f8d5bbSMark Prins if (empty($geom)) { 63457f8d5bbSMark Prins return; 63557f8d5bbSMark Prins } 636e5840fc5SMark Prins 6376c6bb022SMark Prins switch ($geom->geometryType()) { 638c977deacSMark Prins case 'GeometryCollection': 639c977deacSMark Prins // recursively draw part of the collection 640c977deacSMark Prins for ($i = 1; $i < $geom->numGeometries() + 1; $i++) { 641c977deacSMark Prins $_geom = $geom->geometryN($i); 642c977deacSMark Prins $this->drawGeometry($_geom, $colour); 643c977deacSMark Prins } 6446c6bb022SMark Prins break; 6456c6bb022SMark Prins case 'Polygon': 646c977deacSMark Prins $this->drawPolygon($geom, $colour); 647c977deacSMark Prins break; 648c977deacSMark Prins case 'LineString': 649c977deacSMark Prins $this->drawLineString($geom, $colour); 650c977deacSMark Prins break; 651c977deacSMark Prins case 'Point': 652c977deacSMark Prins $this->drawPoint($geom, $colour); 6536c6bb022SMark Prins break; 654ad8fb8a2SMark Prins // TODO implement / do nothing 655ad8fb8a2SMark Prins case 'MultiPolygon': 656ad8fb8a2SMark Prins case 'MultiLineString': 657ad8fb8a2SMark Prins case 'MultiPoint': 6586c6bb022SMark Prins default: 6592d11d700SMark Prins // draw nothing 6606c6bb022SMark Prins break; 6616c6bb022SMark Prins } 6626c6bb022SMark Prins } 663c977deacSMark Prins 664e61425c7SMark Prins /** 665e61425c7SMark Prins * Draw a polygon on the map. 666ab8cbd2bSMark Prins * 667e61425c7SMark Prins * @param Polygon $polygon 668ab8cbd2bSMark Prins * @param int $colour 669ab8cbd2bSMark Prins * drawing colour 670e61425c7SMark Prins */ 671a760825cSgithub-actions[bot] private function drawPolygon($polygon, int $colour) 672a760825cSgithub-actions[bot] { 673c977deacSMark Prins // TODO implementation of drawing holes, 674c977deacSMark Prins // maybe draw the polygon to an in-memory image and use imagecopy, draw polygon in col., draw holes in bgcol? 675c977deacSMark Prins 676c977deacSMark Prins // print_r('Polygon:<br />'); 677c977deacSMark Prins // print_r($polygon); 678a760825cSgithub-actions[bot] $extPoints = []; 679c977deacSMark Prins // extring is a linestring actually.. 680c977deacSMark Prins $extRing = $polygon->exteriorRing(); 681c977deacSMark Prins 682c977deacSMark Prins for ($i = 1; $i < $extRing->numGeometries(); $i++) { 683c977deacSMark Prins $p1 = $extRing->geometryN($i); 68457f8d5bbSMark Prins $x = floor( 68557f8d5bbSMark Prins ($this->width / 2) - $this->tileSize * ($this->centerX - $this->lonToTile($p1->x(), $this->zoom)) 68657f8d5bbSMark Prins ); 68757f8d5bbSMark Prins $y = floor( 68857f8d5bbSMark Prins ($this->height / 2) - $this->tileSize * ($this->centerY - $this->latToTile($p1->y(), $this->zoom)) 68957f8d5bbSMark Prins ); 690c977deacSMark Prins $extPoints [] = $x; 691c977deacSMark Prins $extPoints [] = $y; 692e61425c7SMark Prins } 693c977deacSMark Prins // print_r('points:('.($i-1).')<br />'); 694c977deacSMark Prins // print_r($extPoints); 695c977deacSMark Prins // imagepolygon ($this->image, $extPoints, $i-1, $colour ); 696c977deacSMark Prins imagefilledpolygon($this->image, $extPoints, $i - 1, $colour); 697c977deacSMark Prins } 698c977deacSMark Prins 699628e43ccSMark Prins /** 70057f8d5bbSMark Prins * Draw a line on the map. 70157f8d5bbSMark Prins * 70257f8d5bbSMark Prins * @param LineString $line 70357f8d5bbSMark Prins * @param int $colour 70457f8d5bbSMark Prins * drawing colour 70557f8d5bbSMark Prins */ 706a760825cSgithub-actions[bot] private function drawLineString($line, $colour) 707a760825cSgithub-actions[bot] { 70857f8d5bbSMark Prins imagesetthickness($this->image, 2); 70957f8d5bbSMark Prins for ($p = 1; $p < $line->numGeometries(); $p++) { 71057f8d5bbSMark Prins // get first pair of points 71157f8d5bbSMark Prins $p1 = $line->geometryN($p); 71257f8d5bbSMark Prins $p2 = $line->geometryN($p + 1); 71357f8d5bbSMark Prins // translate to paper space 71457f8d5bbSMark Prins $x1 = floor( 71557f8d5bbSMark Prins ($this->width / 2) - $this->tileSize * ($this->centerX - $this->lonToTile($p1->x(), $this->zoom)) 71657f8d5bbSMark Prins ); 71757f8d5bbSMark Prins $y1 = floor( 71857f8d5bbSMark Prins ($this->height / 2) - $this->tileSize * ($this->centerY - $this->latToTile($p1->y(), $this->zoom)) 71957f8d5bbSMark Prins ); 72057f8d5bbSMark Prins $x2 = floor( 72157f8d5bbSMark Prins ($this->width / 2) - $this->tileSize * ($this->centerX - $this->lonToTile($p2->x(), $this->zoom)) 72257f8d5bbSMark Prins ); 72357f8d5bbSMark Prins $y2 = floor( 72457f8d5bbSMark Prins ($this->height / 2) - $this->tileSize * ($this->centerY - $this->latToTile($p2->y(), $this->zoom)) 72557f8d5bbSMark Prins ); 72657f8d5bbSMark Prins // draw to image 72757f8d5bbSMark Prins imageline($this->image, $x1, $y1, $x2, $y2, $colour); 72857f8d5bbSMark Prins } 72957f8d5bbSMark Prins imagesetthickness($this->image, 1); 73057f8d5bbSMark Prins } 73157f8d5bbSMark Prins 73257f8d5bbSMark Prins /** 73357f8d5bbSMark Prins * Draw a point on the map. 73457f8d5bbSMark Prins * 73557f8d5bbSMark Prins * @param Point $point 73657f8d5bbSMark Prins * @param int $colour 73757f8d5bbSMark Prins * drawing colour 73857f8d5bbSMark Prins */ 739a760825cSgithub-actions[bot] private function drawPoint($point, $colour) 740a760825cSgithub-actions[bot] { 74157f8d5bbSMark Prins imagesetthickness($this->image, 2); 74257f8d5bbSMark Prins // translate to paper space 74357f8d5bbSMark Prins $cx = floor( 74457f8d5bbSMark Prins ($this->width / 2) - $this->tileSize * ($this->centerX - $this->lonToTile($point->x(), $this->zoom)) 74557f8d5bbSMark Prins ); 74657f8d5bbSMark Prins $cy = floor( 74757f8d5bbSMark Prins ($this->height / 2) - $this->tileSize * ($this->centerY - $this->latToTile($point->y(), $this->zoom)) 74857f8d5bbSMark Prins ); 74957f8d5bbSMark Prins $r = 5; 75057f8d5bbSMark Prins // draw to image 75157f8d5bbSMark Prins // imageellipse($this->image, $cx, $cy,$r, $r, $colour); 75257f8d5bbSMark Prins imagefilledellipse($this->image, $cx, $cy, $r, $r, $colour); 75357f8d5bbSMark Prins // don't use imageellipse because the imagesetthickness function has 75457f8d5bbSMark Prins // no effect. So the better workaround is to use imagearc. 75557f8d5bbSMark Prins imagearc($this->image, $cx, $cy, $r, $r, 0, 359, $colour); 75657f8d5bbSMark Prins imagesetthickness($this->image, 1); 75757f8d5bbSMark Prins } 75857f8d5bbSMark Prins 75957f8d5bbSMark Prins /** 76057f8d5bbSMark Prins * Draw gpx trace on the map. 7613e7791adSMark Prins * @throws exception when loading the GPX fails 76257f8d5bbSMark Prins */ 763a760825cSgithub-actions[bot] public function drawGPX() 764a760825cSgithub-actions[bot] { 76557f8d5bbSMark Prins $col = imagecolorallocatealpha($this->image, 0, 0, 255, .4 * 127); 76657f8d5bbSMark Prins $gpxgeom = geoPHP::load(file_get_contents($this->gpxFileName), 'gpx'); 76757f8d5bbSMark Prins $this->drawGeometry($gpxgeom, $col); 76857f8d5bbSMark Prins } 76957f8d5bbSMark Prins 77057f8d5bbSMark Prins /** 77157f8d5bbSMark Prins * Draw geojson on the map. 7723e7791adSMark Prins * @throws exception when loading the JSON fails 77357f8d5bbSMark Prins */ 774a760825cSgithub-actions[bot] public function drawGeojson() 775a760825cSgithub-actions[bot] { 77657f8d5bbSMark Prins $col = imagecolorallocatealpha($this->image, 255, 0, 255, .4 * 127); 77757f8d5bbSMark Prins $gpxgeom = geoPHP::load(file_get_contents($this->geojsonFileName), 'json'); 77857f8d5bbSMark Prins $this->drawGeometry($gpxgeom, $col); 77957f8d5bbSMark Prins } 78057f8d5bbSMark Prins 78157f8d5bbSMark Prins /** 782628e43ccSMark Prins * add copyright and origin notice and icons to the map. 783628e43ccSMark Prins */ 784a760825cSgithub-actions[bot] public function drawCopyright() 785a760825cSgithub-actions[bot] { 786a760825cSgithub-actions[bot] $logoBaseDir = __DIR__ . '/' . 'logo/'; 787628e43ccSMark Prins $logoImg = imagecreatefrompng($logoBaseDir . $this->tileInfo ['openstreetmap'] ['logo']); 788628e43ccSMark Prins $textcolor = imagecolorallocate($this->image, 0, 0, 0); 789628e43ccSMark Prins $bgcolor = imagecolorallocate($this->image, 200, 200, 200); 790628e43ccSMark Prins 79157f8d5bbSMark Prins imagecopy( 79257f8d5bbSMark Prins $this->image, 79357f8d5bbSMark Prins $logoImg, 79457f8d5bbSMark Prins 0, 79557f8d5bbSMark Prins imagesy($this->image) - imagesy($logoImg), 79657f8d5bbSMark Prins 0, 79757f8d5bbSMark Prins 0, 79857f8d5bbSMark Prins imagesx($logoImg), 79957f8d5bbSMark Prins imagesy($logoImg) 80057f8d5bbSMark Prins ); 80157f8d5bbSMark Prins imagestring( 80257f8d5bbSMark Prins $this->image, 80357f8d5bbSMark Prins 1, 80457f8d5bbSMark Prins imagesx($logoImg) + 2, 80557f8d5bbSMark Prins imagesy($this->image) - imagesy($logoImg) + 1, 80657f8d5bbSMark Prins $this->tileInfo ['openstreetmap'] ['txt'], 80757f8d5bbSMark Prins $bgcolor 80857f8d5bbSMark Prins ); 80957f8d5bbSMark Prins imagestring( 81057f8d5bbSMark Prins $this->image, 81157f8d5bbSMark Prins 1, 81257f8d5bbSMark Prins imagesx($logoImg) + 1, 81357f8d5bbSMark Prins imagesy($this->image) - imagesy($logoImg), 81457f8d5bbSMark Prins $this->tileInfo ['openstreetmap'] ['txt'], 81557f8d5bbSMark Prins $textcolor 81657f8d5bbSMark Prins ); 817628e43ccSMark Prins 818628e43ccSMark Prins // additional tile source info, ie. who created/hosted the tiles 819257dffd7SMark Prins $xIconOffset = 0; 820257dffd7SMark Prins if ($this->maptype === 'openstreetmap') { 821257dffd7SMark Prins $mapAuthor = "(c) OpenStreetMap maps/CC BY-SA"; 822257dffd7SMark Prins } else { 823257dffd7SMark Prins $mapAuthor = $this->tileInfo [$this->maptype] ['txt']; 824628e43ccSMark Prins $iconImg = imagecreatefrompng($logoBaseDir . $this->tileInfo [$this->maptype] ['logo']); 825257dffd7SMark Prins $xIconOffset = imagesx($iconImg); 82657f8d5bbSMark Prins imagecopy( 82757f8d5bbSMark Prins $this->image, 828a760825cSgithub-actions[bot] $iconImg, 829a760825cSgithub-actions[bot] imagesx($logoImg) + 1, 83057f8d5bbSMark Prins imagesy($this->image) - imagesy($iconImg), 83157f8d5bbSMark Prins 0, 83257f8d5bbSMark Prins 0, 833a760825cSgithub-actions[bot] imagesx($iconImg), 834a760825cSgithub-actions[bot] imagesy($iconImg) 83557f8d5bbSMark Prins ); 836257dffd7SMark Prins } 83757f8d5bbSMark Prins imagestring( 83857f8d5bbSMark Prins $this->image, 839a760825cSgithub-actions[bot] 1, 840a760825cSgithub-actions[bot] imagesx($logoImg) + $xIconOffset + 4, 84157f8d5bbSMark Prins imagesy($this->image) - ceil(imagesy($logoImg) / 2) + 1, 842257dffd7SMark Prins $mapAuthor, 84357f8d5bbSMark Prins $bgcolor 84457f8d5bbSMark Prins ); 84557f8d5bbSMark Prins imagestring( 84657f8d5bbSMark Prins $this->image, 847a760825cSgithub-actions[bot] 1, 848a760825cSgithub-actions[bot] imagesx($logoImg) + $xIconOffset + 3, 84957f8d5bbSMark Prins imagesy($this->image) - ceil(imagesy($logoImg) / 2), 850257dffd7SMark Prins $mapAuthor, 85157f8d5bbSMark Prins $textcolor 85257f8d5bbSMark Prins ); 853628e43ccSMark Prins } 854628e43ccSMark Prins} 855