1628e43ccSMark Prins<?php 2*a760825cSgithub-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; 303ebe658bSMark Prins 31628e43ccSMark Prins/** 32ab8cbd2bSMark Prins * 33628e43ccSMark Prins * @author Mark C. Prins <mprins@users.sf.net> 34628e43ccSMark Prins * @author Gerhard Koch <gerhard.koch AT ymail.com> 35628e43ccSMark Prins * 36628e43ccSMark Prins */ 37*a760825cSgithub-actions[bot]class StaticMap 38*a760825cSgithub-actions[bot]{ 39628e43ccSMark Prins // the final output 4057f8d5bbSMark Prins private $tileSize = 256; 41*a760825cSgithub-actions[bot] private $tileInfo = [ 42628e43ccSMark Prins // OSM sources 43*a760825cSgithub-actions[bot] 'openstreetmap' => ['txt' => '(c) OpenStreetMap data/ODbl', 'logo' => 'osm_logo.png', 'url' => 'https://tile.openstreetmap.org/{Z}/{X}/{Y}.png'], 44976e50f5SMark Prins // OpenTopoMap sources 45*a760825cSgithub-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'], 46628e43ccSMark Prins // OCM sources 47*a760825cSgithub-actions[bot] 'cycle' => ['txt' => '(c) Thunderforest maps', 'logo' => 'tf_logo.png', 'url' => 'https://tile.thunderforest.com/cycle/{Z}/{X}/{Y}.png'], 48*a760825cSgithub-actions[bot] 'transport' => ['txt' => '(c) Thunderforest maps', 'logo' => 'tf_logo.png', 'url' => 'https://tile.thunderforest.com/transport/{Z}/{X}/{Y}.png'], 49*a760825cSgithub-actions[bot] 'landscape' => ['txt' => '(c) Thunderforest maps', 'logo' => 'tf_logo.png', 'url' => 'https://tile.thunderforest.com/landscape/{Z}/{X}/{Y}.png'], 50*a760825cSgithub-actions[bot] 'outdoors' => ['txt' => '(c) Thunderforest maps', 'logo' => 'tf_logo.png', 'url' => 'https://tile.thunderforest.com/outdoors/{Z}/{X}/{Y}.png'], 51*a760825cSgithub-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'], 52*a760825cSgithub-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'], 53*a760825cSgithub-actions[bot] ]; 5457f8d5bbSMark Prins private $tileDefaultSrc = 'openstreetmap'; 55628e43ccSMark Prins 56628e43ccSMark Prins // set up markers 57*a760825cSgithub-actions[bot] private $markerPrototypes = [ 58628e43ccSMark Prins // found at http://www.mapito.net/map-marker-icons.html 59628e43ccSMark Prins // these are 17x19 px with a pointer at the bottom left 60*a760825cSgithub-actions[bot] 'lightblue' => ['regex' => '/^lightblue(\d+)$/', 'extension' => '.png', 'shadow' => false, 'offsetImage' => '0,-19', 'offsetShadow' => false], 61628e43ccSMark Prins // openlayers std markers are 21x25px with shadow 62*a760825cSgithub-actions[bot] 'ol-marker' => ['regex' => '/^marker(|-blue|-gold|-green|-red)+$/', 'extension' => '.png', 'shadow' => 'marker_shadow.png', 'offsetImage' => '-10,-25', 'offsetShadow' => '-1,-13'], 63628e43ccSMark Prins // these are 16x16 px 64*a760825cSgithub-actions[bot] 'ww_icon' => ['regex' => '/ww_\S+$/', 'extension' => '.png', 'shadow' => false, 'offsetImage' => '-8,-8', 'offsetShadow' => false], 65628e43ccSMark Prins // assume these are 16x16 px 66*a760825cSgithub-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'], 67*a760825cSgithub-actions[bot] ]; 6857f8d5bbSMark Prins private $centerX; 6957f8d5bbSMark Prins private $centerY; 7057f8d5bbSMark Prins private $offsetX; 7157f8d5bbSMark Prins private $offsetY; 7257f8d5bbSMark Prins private $image; 7357f8d5bbSMark Prins private $zoom; 7457f8d5bbSMark Prins private $lat; 7557f8d5bbSMark Prins private $lon; 7657f8d5bbSMark Prins private $width; 7757f8d5bbSMark Prins private $height; 7857f8d5bbSMark Prins private $markers; 7957f8d5bbSMark Prins private $maptype; 8057f8d5bbSMark Prins private $kmlFileName; 8157f8d5bbSMark Prins private $gpxFileName; 8257f8d5bbSMark Prins private $geojsonFileName; 8357f8d5bbSMark Prins private $autoZoomExtent; 8457f8d5bbSMark Prins private $apikey; 8557f8d5bbSMark Prins private $tileCacheBaseDir; 8657f8d5bbSMark Prins private $mapCacheBaseDir; 8757f8d5bbSMark Prins private $mediaBaseDir; 88257dffd7SMark Prins private $useTileCache; 8957f8d5bbSMark Prins private $mapCacheID = ''; 9057f8d5bbSMark Prins private $mapCacheFile = ''; 9157f8d5bbSMark Prins private $mapCacheExtension = 'png'; 92628e43ccSMark Prins 93628e43ccSMark Prins /** 94f4b9bdacSMark Prins * Constructor. 95ab8cbd2bSMark Prins * 96ab8cbd2bSMark Prins * @param float $lat 97ab8cbd2bSMark Prins * Latitude (x) of center of map 98ab8cbd2bSMark Prins * @param float $lon 99ab8cbd2bSMark Prins * Longitude (y) of center of map 100ab8cbd2bSMark Prins * @param int $zoom 101ab8cbd2bSMark Prins * Zoomlevel 102ab8cbd2bSMark Prins * @param int $width 103ab8cbd2bSMark Prins * Width in pixels 104ab8cbd2bSMark Prins * @param int $height 105ab8cbd2bSMark Prins * Height in pixels 106ab8cbd2bSMark Prins * @param string $maptype 107ab8cbd2bSMark Prins * Name of the map 10857f8d5bbSMark Prins * @param array $markers 109ab8cbd2bSMark Prins * array of markers 110ab8cbd2bSMark Prins * @param string $gpx 111ab8cbd2bSMark Prins * GPX filename 112ab8cbd2bSMark Prins * @param string $kml 113ab8cbd2bSMark Prins * KML filename 114257dffd7SMark Prins * @param string $geojson 115ab8cbd2bSMark Prins * @param string $mediaDir 116ab8cbd2bSMark Prins * Directory to store/cache maps 117ab8cbd2bSMark Prins * @param string $tileCacheBaseDir 118ab8cbd2bSMark Prins * Directory to cache map tiles 11957f8d5bbSMark Prins * @param bool $autoZoomExtent 120ab8cbd2bSMark Prins * Wheter or not to override zoom/lat/lon and zoom to the extent of gpx/kml and markers 121257dffd7SMark Prins * @param string $apikey 122628e43ccSMark Prins */ 12357f8d5bbSMark Prins public function __construct( 12457f8d5bbSMark Prins float $lat, 12557f8d5bbSMark Prins float $lon, 12657f8d5bbSMark Prins int $zoom, 12757f8d5bbSMark Prins int $width, 12857f8d5bbSMark Prins int $height, 12957f8d5bbSMark Prins string $maptype, 13057f8d5bbSMark Prins array $markers, 13157f8d5bbSMark Prins string $gpx, 13257f8d5bbSMark Prins string $kml, 13357f8d5bbSMark Prins string $geojson, 13457f8d5bbSMark Prins string $mediaDir, 13557f8d5bbSMark Prins string $tileCacheBaseDir, 13657f8d5bbSMark Prins bool $autoZoomExtent = true, 137257dffd7SMark Prins string $apikey = '' 13857f8d5bbSMark Prins ) { 139628e43ccSMark Prins $this->zoom = $zoom; 140628e43ccSMark Prins $this->lat = $lat; 141628e43ccSMark Prins $this->lon = $lon; 142628e43ccSMark Prins $this->width = $width; 143628e43ccSMark Prins $this->height = $height; 144628e43ccSMark Prins // validate + set maptype 145628e43ccSMark Prins $this->maptype = $this->tileDefaultSrc; 146628e43ccSMark Prins if (array_key_exists($maptype, $this->tileInfo)) { 147628e43ccSMark Prins $this->maptype = $maptype; 148628e43ccSMark Prins } 1492d11d700SMark Prins $this->markers = $markers; 1502d11d700SMark Prins $this->kmlFileName = $kml; 1512d11d700SMark Prins $this->gpxFileName = $gpx; 1526914b920SMark Prins $this->geojsonFileName = $geojson; 1532d11d700SMark Prins $this->mediaBaseDir = $mediaDir; 154628e43ccSMark Prins $this->tileCacheBaseDir = $tileCacheBaseDir . '/olmaptiles'; 155628e43ccSMark Prins $this->useTileCache = $this->tileCacheBaseDir !== ''; 156628e43ccSMark Prins $this->mapCacheBaseDir = $mediaDir . '/olmapmaps'; 1572d11d700SMark Prins $this->autoZoomExtent = $autoZoomExtent; 1585c603532SMark Prins $this->apikey = $apikey; 159628e43ccSMark Prins } 160628e43ccSMark Prins 161628e43ccSMark Prins /** 16257f8d5bbSMark Prins * get the map, this may return a reference to a cached copy. 163628e43ccSMark Prins * 16457f8d5bbSMark Prins * @return string url relative to media dir 165628e43ccSMark Prins */ 166*a760825cSgithub-actions[bot] public function getMap(): string 167*a760825cSgithub-actions[bot] { 16857f8d5bbSMark Prins try { 16957f8d5bbSMark Prins if ($this->autoZoomExtent) { 17057f8d5bbSMark Prins $this->autoZoom(); 171628e43ccSMark Prins } 17257f8d5bbSMark Prins } catch (Exception $e) { 17357f8d5bbSMark Prins dbglog($e); 17457f8d5bbSMark Prins } 17557f8d5bbSMark Prins 17657f8d5bbSMark Prins // use map cache, so check cache for map 17757f8d5bbSMark Prins if (!$this->checkMapCache()) { 17857f8d5bbSMark Prins // map is not in cache, needs to be build 17957f8d5bbSMark Prins $this->makeMap(); 18057f8d5bbSMark Prins $this->mkdirRecursive(dirname($this->mapCacheIDToFilename()), 0777); 18157f8d5bbSMark Prins imagepng($this->image, $this->mapCacheIDToFilename(), 9); 18257f8d5bbSMark Prins } 183ba56c962SMark Prins $doc = $this->mapCacheIDToFilename(); 18457f8d5bbSMark Prins // make url relative to media dir 185ba56c962SMark Prins return str_replace($this->mediaBaseDir, '', $doc); 18657f8d5bbSMark Prins } 18757f8d5bbSMark Prins 188628e43ccSMark Prins /** 18957f8d5bbSMark Prins * Calculate the lat/lon/zoom values to make sure that all of the markers and gpx/kml are on the map. 190628e43ccSMark Prins * 19157f8d5bbSMark Prins * @param float $paddingFactor 19257f8d5bbSMark Prins * buffer constant to enlarge (>1.0) the zoom level 1933ebe658bSMark Prins * @throws Exception if non-geometries are found in the collection 194628e43ccSMark Prins */ 195*a760825cSgithub-actions[bot] private function autoZoom(float $paddingFactor = 1.0): void 196*a760825cSgithub-actions[bot] { 197*a760825cSgithub-actions[bot] $geoms = []; 19857f8d5bbSMark Prins $geoms [] = new Point($this->lon, $this->lat); 199*a760825cSgithub-actions[bot] if ($this->markers !== []) { 20057f8d5bbSMark Prins foreach ($this->markers as $marker) { 20157f8d5bbSMark Prins $geoms [] = new Point($marker ['lon'], $marker ['lat']); 20257f8d5bbSMark Prins } 20357f8d5bbSMark Prins } 20457f8d5bbSMark Prins if (file_exists($this->kmlFileName)) { 20557f8d5bbSMark Prins $g = geoPHP::load(file_get_contents($this->kmlFileName), 'kml'); 20657f8d5bbSMark Prins if ($g !== false) { 20757f8d5bbSMark Prins $geoms [] = $g; 20857f8d5bbSMark Prins } 20957f8d5bbSMark Prins } 21057f8d5bbSMark Prins if (file_exists($this->gpxFileName)) { 21157f8d5bbSMark Prins $g = geoPHP::load(file_get_contents($this->gpxFileName), 'gpx'); 21257f8d5bbSMark Prins if ($g !== false) { 21357f8d5bbSMark Prins $geoms [] = $g; 21457f8d5bbSMark Prins } 21557f8d5bbSMark Prins } 21657f8d5bbSMark Prins if (file_exists($this->geojsonFileName)) { 21757f8d5bbSMark Prins $g = geoPHP::load(file_get_contents($this->geojsonFileName), 'geojson'); 21857f8d5bbSMark Prins if ($g !== false) { 21957f8d5bbSMark Prins $geoms [] = $g; 22057f8d5bbSMark Prins } 22157f8d5bbSMark Prins } 22257f8d5bbSMark Prins 22357f8d5bbSMark Prins if (count($geoms) <= 1) { 22457f8d5bbSMark Prins dbglog($geoms, "StaticMap::autoZoom: Skip setting autozoom options"); 22557f8d5bbSMark Prins return; 22657f8d5bbSMark Prins } 22757f8d5bbSMark Prins 22857f8d5bbSMark Prins $geom = new GeometryCollection($geoms); 22957f8d5bbSMark Prins $centroid = $geom->centroid(); 23057f8d5bbSMark Prins $bbox = $geom->getBBox(); 23157f8d5bbSMark Prins 23257f8d5bbSMark Prins // determine vertical resolution, this depends on the distance from the equator 23357f8d5bbSMark Prins // $vy00 = log(tan(M_PI*(0.25 + $centroid->getY()/360))); 23457f8d5bbSMark Prins $vy0 = log(tan(M_PI * (0.25 + $bbox ['miny'] / 360))); 23557f8d5bbSMark Prins $vy1 = log(tan(M_PI * (0.25 + $bbox ['maxy'] / 360))); 23657f8d5bbSMark Prins dbglog("StaticMap::autoZoom: vertical resolution: $vy0, $vy1"); 237e98967e1SMark Prins if ($vy1 - $vy0 === 0.0) { 238e98967e1SMark Prins $resolutionVertical = 0; 239e98967e1SMark Prins dbglog("StaticMap::autoZoom: using $resolutionVertical"); 240e98967e1SMark Prins } else { 24157f8d5bbSMark Prins $zoomFactorPowered = ($this->height / 2) / (40.7436654315252 * ($vy1 - $vy0)); 24257f8d5bbSMark Prins $resolutionVertical = 360 / ($zoomFactorPowered * $this->tileSize); 243e98967e1SMark Prins } 24457f8d5bbSMark Prins // determine horizontal resolution 24557f8d5bbSMark Prins $resolutionHorizontal = ($bbox ['maxx'] - $bbox ['minx']) / $this->width; 246e98967e1SMark Prins dbglog("StaticMap::autoZoom: using $resolutionHorizontal"); 24757f8d5bbSMark Prins $resolution = max($resolutionHorizontal, $resolutionVertical) * $paddingFactor; 2483e7791adSMark Prins $zoom = $this->zoom; 2493e7791adSMark Prins if ($resolution > 0) { 25057f8d5bbSMark Prins $zoom = log(360 / ($resolution * $this->tileSize), 2); 2513e7791adSMark Prins } 25257f8d5bbSMark Prins 25357f8d5bbSMark Prins if (is_finite($zoom) && $zoom < 15 && $zoom > 2) { 25457f8d5bbSMark Prins $this->zoom = floor($zoom); 25557f8d5bbSMark Prins } 25657f8d5bbSMark Prins $this->lon = $centroid->getX(); 25757f8d5bbSMark Prins $this->lat = $centroid->getY(); 25857f8d5bbSMark Prins dbglog("StaticMap::autoZoom: Set autozoom options to: z: $this->zoom, lon: $this->lon, lat: $this->lat"); 25957f8d5bbSMark Prins } 26057f8d5bbSMark Prins 261*a760825cSgithub-actions[bot] public function checkMapCache(): bool 262*a760825cSgithub-actions[bot] { 26357f8d5bbSMark Prins // side effect: set the mapCacheID 26457f8d5bbSMark Prins $this->mapCacheID = md5($this->serializeParams()); 26557f8d5bbSMark Prins $filename = $this->mapCacheIDToFilename(); 26657f8d5bbSMark Prins return file_exists($filename); 26757f8d5bbSMark Prins } 26857f8d5bbSMark Prins 269*a760825cSgithub-actions[bot] public function serializeParams(): string 270*a760825cSgithub-actions[bot] { 2713e7791adSMark Prins return implode( 272*a760825cSgithub-actions[bot] "&", 273*a760825cSgithub-actions[bot] [$this->zoom, $this->lat, $this->lon, $this->width, $this->height, serialize($this->markers), $this->maptype, $this->kmlFileName, $this->gpxFileName, $this->geojsonFileName] 27457f8d5bbSMark Prins ); 27557f8d5bbSMark Prins } 27657f8d5bbSMark Prins 277*a760825cSgithub-actions[bot] public function mapCacheIDToFilename(): string 278*a760825cSgithub-actions[bot] { 27957f8d5bbSMark Prins if (!$this->mapCacheFile) { 28057f8d5bbSMark Prins $this->mapCacheFile = $this->mapCacheBaseDir . "/" . $this->maptype . "/" . $this->zoom . "/cache_" 28157f8d5bbSMark Prins . substr($this->mapCacheID, 0, 2) . "/" . substr($this->mapCacheID, 2, 2) 28257f8d5bbSMark Prins . "/" . substr($this->mapCacheID, 4); 28357f8d5bbSMark Prins } 28457f8d5bbSMark Prins return $this->mapCacheFile . "." . $this->mapCacheExtension; 28557f8d5bbSMark Prins } 28657f8d5bbSMark Prins 28757f8d5bbSMark Prins /** 28857f8d5bbSMark Prins * make the map. 28957f8d5bbSMark Prins */ 290*a760825cSgithub-actions[bot] public function makeMap(): void 291*a760825cSgithub-actions[bot] { 29257f8d5bbSMark Prins $this->initCoords(); 29357f8d5bbSMark Prins $this->createBaseMap(); 294*a760825cSgithub-actions[bot] if ($this->markers !== []) { 29557f8d5bbSMark Prins $this->placeMarkers(); 296e98967e1SMark Prins } 297e98967e1SMark Prins if (file_exists($this->kmlFileName)) { 298e98967e1SMark Prins try { 29957f8d5bbSMark Prins $this->drawKML(); 300e98967e1SMark Prins } catch (exception $e) { 301e98967e1SMark Prins dbglog('failed to load KML file', $e); 302e98967e1SMark Prins } 303e98967e1SMark Prins } 304e98967e1SMark Prins if (file_exists($this->gpxFileName)) { 305e98967e1SMark Prins try { 30657f8d5bbSMark Prins $this->drawGPX(); 307e98967e1SMark Prins } catch (exception $e) { 308e98967e1SMark Prins dbglog('failed to load GPX file', $e); 309e98967e1SMark Prins } 310e98967e1SMark Prins } 311e98967e1SMark Prins if (file_exists($this->geojsonFileName)) { 312e98967e1SMark Prins try { 31357f8d5bbSMark Prins $this->drawGeojson(); 314e98967e1SMark Prins } catch (exception $e) { 315e98967e1SMark Prins dbglog('failed to load GeoJSON file', $e); 316e98967e1SMark Prins } 317e98967e1SMark Prins } 31857f8d5bbSMark Prins 31957f8d5bbSMark Prins $this->drawCopyright(); 320628e43ccSMark Prins } 321f4b9bdacSMark Prins 322628e43ccSMark Prins /** 323628e43ccSMark Prins */ 324*a760825cSgithub-actions[bot] public function initCoords(): void 325*a760825cSgithub-actions[bot] { 326628e43ccSMark Prins $this->centerX = $this->lonToTile($this->lon, $this->zoom); 327628e43ccSMark Prins $this->centerY = $this->latToTile($this->lat, $this->zoom); 328628e43ccSMark Prins $this->offsetX = floor((floor($this->centerX) - $this->centerX) * $this->tileSize); 329628e43ccSMark Prins $this->offsetY = floor((floor($this->centerY) - $this->centerY) * $this->tileSize); 330628e43ccSMark Prins } 331628e43ccSMark Prins 332628e43ccSMark Prins /** 33357f8d5bbSMark Prins * 33457f8d5bbSMark Prins * @param float $long 33557f8d5bbSMark Prins * @param int $zoom 33657f8d5bbSMark Prins * @return float|int 33757f8d5bbSMark Prins */ 338*a760825cSgithub-actions[bot] public function lonToTile(float $long, int $zoom) 339*a760825cSgithub-actions[bot] { 340*a760825cSgithub-actions[bot] return (($long + 180) / 360) * 2 ** $zoom; 34157f8d5bbSMark Prins } 34257f8d5bbSMark Prins 34357f8d5bbSMark Prins /** 34457f8d5bbSMark Prins * 34557f8d5bbSMark Prins * @param float $lat 34657f8d5bbSMark Prins * @param int $zoom 34757f8d5bbSMark Prins * @return float|int 34857f8d5bbSMark Prins */ 349*a760825cSgithub-actions[bot] public function latToTile(float $lat, int $zoom) 350*a760825cSgithub-actions[bot] { 351*a760825cSgithub-actions[bot] return (1 - log(tan($lat * M_PI / 180) + 1 / cos($lat * M_PI / 180)) / M_PI) / 2 * 2 ** $zoom; 35257f8d5bbSMark Prins } 35357f8d5bbSMark Prins 35457f8d5bbSMark Prins /** 355628e43ccSMark Prins * make basemap image. 356628e43ccSMark Prins */ 357*a760825cSgithub-actions[bot] public function createBaseMap(): void 358*a760825cSgithub-actions[bot] { 359628e43ccSMark Prins $this->image = imagecreatetruecolor($this->width, $this->height); 360628e43ccSMark Prins $startX = floor($this->centerX - ($this->width / $this->tileSize) / 2); 361628e43ccSMark Prins $startY = floor($this->centerY - ($this->height / $this->tileSize) / 2); 362628e43ccSMark Prins $endX = ceil($this->centerX + ($this->width / $this->tileSize) / 2); 363628e43ccSMark Prins $endY = ceil($this->centerY + ($this->height / $this->tileSize) / 2); 364628e43ccSMark Prins $this->offsetX = -floor(($this->centerX - floor($this->centerX)) * $this->tileSize); 365628e43ccSMark Prins $this->offsetY = -floor(($this->centerY - floor($this->centerY)) * $this->tileSize); 366628e43ccSMark Prins $this->offsetX += floor($this->width / 2); 367628e43ccSMark Prins $this->offsetY += floor($this->height / 2); 368628e43ccSMark Prins $this->offsetX += floor($startX - floor($this->centerX)) * $this->tileSize; 369628e43ccSMark Prins $this->offsetY += floor($startY - floor($this->centerY)) * $this->tileSize; 370628e43ccSMark Prins 371628e43ccSMark Prins for ($x = $startX; $x <= $endX; $x++) { 372628e43ccSMark Prins for ($y = $startY; $y <= $endY; $y++) { 37357f8d5bbSMark Prins $url = str_replace( 374*a760825cSgithub-actions[bot] ['{Z}', '{X}', '{Y}'], 375*a760825cSgithub-actions[bot] [$this->zoom, $x, $y], 376*a760825cSgithub-actions[bot] $this->tileInfo [$this->maptype] ['url'] 37757f8d5bbSMark Prins ); 378c8eb1362SMark Prins 379628e43ccSMark Prins $tileData = $this->fetchTile($url); 380628e43ccSMark Prins if ($tileData) { 381628e43ccSMark Prins $tileImage = imagecreatefromstring($tileData); 382628e43ccSMark Prins } else { 383628e43ccSMark Prins $tileImage = imagecreate($this->tileSize, $this->tileSize); 384628e43ccSMark Prins $color = imagecolorallocate($tileImage, 255, 255, 255); 385628e43ccSMark Prins @imagestring($tileImage, 1, 127, 127, 'err', $color); 386628e43ccSMark Prins } 387628e43ccSMark Prins $destX = ($x - $startX) * $this->tileSize + $this->offsetX; 388628e43ccSMark Prins $destY = ($y - $startY) * $this->tileSize + $this->offsetY; 389c8eb1362SMark Prins dbglog($this->tileSize, "imagecopy tile into image: $destX, $destY"); 39057f8d5bbSMark Prins imagecopy( 391*a760825cSgithub-actions[bot] $this->image, 392*a760825cSgithub-actions[bot] $tileImage, 393*a760825cSgithub-actions[bot] $destX, 394*a760825cSgithub-actions[bot] $destY, 395*a760825cSgithub-actions[bot] 0, 396*a760825cSgithub-actions[bot] 0, 397*a760825cSgithub-actions[bot] $this->tileSize, 39857f8d5bbSMark Prins $this->tileSize 39957f8d5bbSMark Prins ); 400628e43ccSMark Prins } 401628e43ccSMark Prins } 402628e43ccSMark Prins } 403628e43ccSMark Prins 404628e43ccSMark Prins /** 4052d11d700SMark Prins * Fetch a tile and (if configured) store it in the cache. 4062d11d700SMark Prins * @param string $url 407257dffd7SMark Prins * @return bool|string 408ebb06a66SMark Prins * @todo refactor this to use dokuwiki\HTTP\HTTPClient or dokuwiki\HTTP\DokuHTTPClient 409ebb06a66SMark Prins * for better proxy handling... 4102d11d700SMark Prins */ 411*a760825cSgithub-actions[bot] public function fetchTile(string $url) 412*a760825cSgithub-actions[bot] { 413ab8cbd2bSMark Prins if ($this->useTileCache && ($cached = $this->checkTileCache($url))) 414ab8cbd2bSMark Prins return $cached; 415e4f115f4SMark Prins 416e4f115f4SMark Prins $_UA = 'Mozilla/4.0 (compatible; DokuWikiSpatial HTTP Client; ' . PHP_OS . ')'; 417e4f115f4SMark Prins if (function_exists("curl_init")) { 418e4f115f4SMark Prins // use cUrl 419628e43ccSMark Prins $ch = curl_init(); 420628e43ccSMark Prins curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 421e4f115f4SMark Prins curl_setopt($ch, CURLOPT_USERAGENT, $_UA); 422628e43ccSMark Prins curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); 423b63742deSMark Prins curl_setopt($ch, CURLOPT_URL, $url . $this->apikey); 424ebb06a66SMark Prins dbglog("StaticMap::fetchTile: getting: $url using curl_exec"); 425628e43ccSMark Prins $tile = curl_exec($ch); 426628e43ccSMark Prins curl_close($ch); 427e4f115f4SMark Prins } else { 428e4f115f4SMark Prins // use file_get_contents 429e4f115f4SMark Prins global $conf; 430*a760825cSgithub-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]]; 431*a760825cSgithub-actions[bot] if ( 432*a760825cSgithub-actions[bot] isset($conf['proxy']['host'], $conf['proxy']['port']) 433ebb06a66SMark Prins && $conf['proxy']['host'] !== '' 434*a760825cSgithub-actions[bot] && $conf['proxy']['port'] !== '' 435*a760825cSgithub-actions[bot] ) { 436ebb06a66SMark Prins $opts['http'] += ['proxy' => "tcp://" . $conf['proxy']['host'] . ":" . $conf['proxy']['port']]; 437ebb06a66SMark Prins } 438ebb06a66SMark Prins 439e4f115f4SMark Prins $context = stream_context_create($opts); 44068188742SMark Prins // dbglog("StaticMap::fetchTile: getting: $url . $this->apikey using file_get_contents and options $opts"); 441b63742deSMark Prins $tile = file_get_contents($url . $this->apikey, false, $context); 442e4f115f4SMark Prins } 443628e43ccSMark Prins if ($tile && $this->useTileCache) { 444628e43ccSMark Prins $this->writeTileToCache($url, $tile); 445628e43ccSMark Prins } 446628e43ccSMark Prins return $tile; 447628e43ccSMark Prins } 448628e43ccSMark Prins 449628e43ccSMark Prins /** 45057f8d5bbSMark Prins * 45157f8d5bbSMark Prins * @param string $url 452257dffd7SMark Prins * @return string|false 453628e43ccSMark Prins */ 454*a760825cSgithub-actions[bot] public function checkTileCache(string $url) 455*a760825cSgithub-actions[bot] { 45657f8d5bbSMark Prins $filename = $this->tileUrlToFilename($url); 45757f8d5bbSMark Prins if (file_exists($filename)) { 45857f8d5bbSMark Prins return file_get_contents($filename); 45957f8d5bbSMark Prins } 460257dffd7SMark Prins return false; 461628e43ccSMark Prins } 462628e43ccSMark Prins 4636914b920SMark Prins /** 46457f8d5bbSMark Prins * 46557f8d5bbSMark Prins * @param string $url 4666914b920SMark Prins */ 467*a760825cSgithub-actions[bot] public function tileUrlToFilename(string $url): string 468*a760825cSgithub-actions[bot] { 469257dffd7SMark Prins return $this->tileCacheBaseDir . "/" . substr($url, strpos($url, '/') + 1); 47057f8d5bbSMark Prins } 47157f8d5bbSMark Prins 47257f8d5bbSMark Prins /** 47357f8d5bbSMark Prins * Write a tile into the cache. 47457f8d5bbSMark Prins * 47557f8d5bbSMark Prins * @param string $url 47657f8d5bbSMark Prins * @param mixed $data 47757f8d5bbSMark Prins */ 478*a760825cSgithub-actions[bot] public function writeTileToCache($url, $data): void 479*a760825cSgithub-actions[bot] { 48057f8d5bbSMark Prins $filename = $this->tileUrlToFilename($url); 48157f8d5bbSMark Prins $this->mkdirRecursive(dirname($filename), 0777); 48257f8d5bbSMark Prins file_put_contents($filename, $data); 48357f8d5bbSMark Prins } 48457f8d5bbSMark Prins 48557f8d5bbSMark Prins /** 48657f8d5bbSMark Prins * Recursively create the directory. 48757f8d5bbSMark Prins * 48857f8d5bbSMark Prins * @param string $pathname 48957f8d5bbSMark Prins * The directory path. 49057f8d5bbSMark Prins * @param int $mode 49157f8d5bbSMark Prins * File access mode. For more information on modes, read the details on the chmod manpage. 49257f8d5bbSMark Prins */ 493*a760825cSgithub-actions[bot] public function mkdirRecursive(string $pathname, int $mode): bool 494*a760825cSgithub-actions[bot] { 495*a760825cSgithub-actions[bot] if (!is_dir(dirname($pathname))) { 496*a760825cSgithub-actions[bot] $this->mkdirRecursive(dirname($pathname), $mode); 497*a760825cSgithub-actions[bot] } 49857f8d5bbSMark Prins return is_dir($pathname) || mkdir($pathname, $mode) || is_dir($pathname); 49957f8d5bbSMark Prins } 50057f8d5bbSMark Prins 50157f8d5bbSMark Prins /** 50257f8d5bbSMark Prins * Place markers on the map and number them in the same order as they are listed in the html. 50357f8d5bbSMark Prins */ 504*a760825cSgithub-actions[bot] public function placeMarkers(): void 505*a760825cSgithub-actions[bot] { 50657f8d5bbSMark Prins $count = 0; 50757f8d5bbSMark Prins $color = imagecolorallocate($this->image, 0, 0, 0); 50857f8d5bbSMark Prins $bgcolor = imagecolorallocate($this->image, 200, 200, 200); 50957f8d5bbSMark Prins $markerBaseDir = __DIR__ . '/icons'; 510e98967e1SMark Prins $markerImageOffsetX = 0; 511e98967e1SMark Prins $markerImageOffsetY = 0; 512e98967e1SMark Prins $markerShadowOffsetX = 0; 513e98967e1SMark Prins $markerShadowOffsetY = 0; 514e98967e1SMark Prins $markerShadowImg = null; 51557f8d5bbSMark Prins // loop thru marker array 51657f8d5bbSMark Prins foreach ($this->markers as $marker) { 51757f8d5bbSMark Prins // set some local variables 51857f8d5bbSMark Prins $markerLat = $marker ['lat']; 51957f8d5bbSMark Prins $markerLon = $marker ['lon']; 52057f8d5bbSMark Prins $markerType = $marker ['type']; 52157f8d5bbSMark Prins // clear variables from previous loops 52257f8d5bbSMark Prins $markerFilename = ''; 52357f8d5bbSMark Prins $markerShadow = ''; 52457f8d5bbSMark Prins $matches = false; 52557f8d5bbSMark Prins // check for marker type, get settings from markerPrototypes 52657f8d5bbSMark Prins if ($markerType) { 52757f8d5bbSMark Prins foreach ($this->markerPrototypes as $markerPrototype) { 52857f8d5bbSMark Prins if (preg_match($markerPrototype ['regex'], $markerType, $matches)) { 52957f8d5bbSMark Prins $markerFilename = $matches [0] . $markerPrototype ['extension']; 53057f8d5bbSMark Prins if ($markerPrototype ['offsetImage']) { 531*a760825cSgithub-actions[bot] [$markerImageOffsetX, $markerImageOffsetY] = explode( 53257f8d5bbSMark Prins ",", 53357f8d5bbSMark Prins $markerPrototype ['offsetImage'] 53457f8d5bbSMark Prins ); 53557f8d5bbSMark Prins } 53657f8d5bbSMark Prins $markerShadow = $markerPrototype ['shadow']; 53757f8d5bbSMark Prins if ($markerShadow) { 538*a760825cSgithub-actions[bot] [$markerShadowOffsetX, $markerShadowOffsetY] = explode( 53957f8d5bbSMark Prins ",", 54057f8d5bbSMark Prins $markerPrototype ['offsetShadow'] 54157f8d5bbSMark Prins ); 54257f8d5bbSMark Prins } 54357f8d5bbSMark Prins } 54457f8d5bbSMark Prins } 54557f8d5bbSMark Prins } 54657f8d5bbSMark Prins // create img resource 54757f8d5bbSMark Prins if (file_exists($markerBaseDir . '/' . $markerFilename)) { 54857f8d5bbSMark Prins $markerImg = imagecreatefrompng($markerBaseDir . '/' . $markerFilename); 54957f8d5bbSMark Prins } else { 55057f8d5bbSMark Prins $markerImg = imagecreatefrompng($markerBaseDir . '/marker.png'); 55157f8d5bbSMark Prins } 55257f8d5bbSMark Prins // check for shadow + create shadow recource 55357f8d5bbSMark Prins if ($markerShadow && file_exists($markerBaseDir . '/' . $markerShadow)) { 55457f8d5bbSMark Prins $markerShadowImg = imagecreatefrompng($markerBaseDir . '/' . $markerShadow); 55557f8d5bbSMark Prins } 55657f8d5bbSMark Prins // calc position 55757f8d5bbSMark Prins $destX = floor( 55857f8d5bbSMark Prins ($this->width / 2) - 55957f8d5bbSMark Prins $this->tileSize * ($this->centerX - $this->lonToTile($markerLon, $this->zoom)) 56057f8d5bbSMark Prins ); 56157f8d5bbSMark Prins $destY = floor( 56257f8d5bbSMark Prins ($this->height / 2) - 56357f8d5bbSMark Prins $this->tileSize * ($this->centerY - $this->latToTile($markerLat, $this->zoom)) 56457f8d5bbSMark Prins ); 56557f8d5bbSMark Prins // copy shadow on basemap 56657f8d5bbSMark Prins if ($markerShadow && $markerShadowImg) { 56757f8d5bbSMark Prins imagecopy( 56857f8d5bbSMark Prins $this->image, 56957f8d5bbSMark Prins $markerShadowImg, 57057f8d5bbSMark Prins $destX + (int) $markerShadowOffsetX, 57157f8d5bbSMark Prins $destY + (int) $markerShadowOffsetY, 57257f8d5bbSMark Prins 0, 57357f8d5bbSMark Prins 0, 57457f8d5bbSMark Prins imagesx($markerShadowImg), 57557f8d5bbSMark Prins imagesy($markerShadowImg) 57657f8d5bbSMark Prins ); 57757f8d5bbSMark Prins } 57857f8d5bbSMark Prins // copy marker on basemap above shadow 57957f8d5bbSMark Prins imagecopy( 58057f8d5bbSMark Prins $this->image, 58157f8d5bbSMark Prins $markerImg, 58257f8d5bbSMark Prins $destX + (int) $markerImageOffsetX, 58357f8d5bbSMark Prins $destY + (int) $markerImageOffsetY, 58457f8d5bbSMark Prins 0, 58557f8d5bbSMark Prins 0, 58657f8d5bbSMark Prins imagesx($markerImg), 58757f8d5bbSMark Prins imagesy($markerImg) 58857f8d5bbSMark Prins ); 58957f8d5bbSMark Prins // add label 59057f8d5bbSMark Prins imagestring( 59157f8d5bbSMark Prins $this->image, 59257f8d5bbSMark Prins 3, 59357f8d5bbSMark Prins $destX - imagesx($markerImg) + 1, 59457f8d5bbSMark Prins $destY + (int) $markerImageOffsetY + 1, 59557f8d5bbSMark Prins ++$count, 59657f8d5bbSMark Prins $bgcolor 59757f8d5bbSMark Prins ); 59857f8d5bbSMark Prins imagestring( 59957f8d5bbSMark Prins $this->image, 60057f8d5bbSMark Prins 3, 60157f8d5bbSMark Prins $destX - imagesx($markerImg), 60257f8d5bbSMark Prins $destY + (int) $markerImageOffsetY, 60357f8d5bbSMark Prins $count, 60457f8d5bbSMark Prins $color 60557f8d5bbSMark Prins ); 60657f8d5bbSMark Prins } 6076914b920SMark Prins } 60857e65445SMark Prins 609628e43ccSMark Prins /** 610628e43ccSMark Prins * Draw kml trace on the map. 6113e7791adSMark Prins * @throws exception when loading the KML fails 612628e43ccSMark Prins */ 613*a760825cSgithub-actions[bot] public function drawKML(): void 614*a760825cSgithub-actions[bot] { 6152d11d700SMark Prins // TODO get colour from kml node (not currently supported in geoPHP) 616c977deacSMark Prins $col = imagecolorallocatealpha($this->image, 255, 0, 0, .4 * 127); 617c977deacSMark Prins $kmlgeom = geoPHP::load(file_get_contents($this->kmlFileName), 'kml'); 618c977deacSMark Prins $this->drawGeometry($kmlgeom, $col); 619c977deacSMark Prins } 62057e65445SMark Prins 621c977deacSMark Prins /** 622c977deacSMark Prins * Draw geometry or geometry collection on the map. 623ab8cbd2bSMark Prins * 624c977deacSMark Prins * @param Geometry $geom 625ab8cbd2bSMark Prins * @param int $colour 626ab8cbd2bSMark Prins * drawing colour 627c977deacSMark Prins */ 628*a760825cSgithub-actions[bot] private function drawGeometry(Geometry $geom, int $colour): void 629*a760825cSgithub-actions[bot] { 63057f8d5bbSMark Prins if (empty($geom)) { 63157f8d5bbSMark Prins return; 63257f8d5bbSMark Prins } 633e5840fc5SMark Prins 6346c6bb022SMark Prins switch ($geom->geometryType()) { 635c977deacSMark Prins case 'GeometryCollection': 636c977deacSMark Prins // recursively draw part of the collection 637c977deacSMark Prins for ($i = 1; $i < $geom->numGeometries() + 1; $i++) { 638c977deacSMark Prins $_geom = $geom->geometryN($i); 639c977deacSMark Prins $this->drawGeometry($_geom, $colour); 640c977deacSMark Prins } 6416c6bb022SMark Prins break; 642c977deacSMark Prins case 'MultiPolygon': 643c977deacSMark Prins case 'MultiLineString': 644c977deacSMark Prins case 'MultiPoint': 645c977deacSMark Prins // TODO implement / do nothing 6466c6bb022SMark Prins break; 6476c6bb022SMark Prins case 'Polygon': 648c977deacSMark Prins $this->drawPolygon($geom, $colour); 649c977deacSMark Prins break; 650c977deacSMark Prins case 'LineString': 651c977deacSMark Prins $this->drawLineString($geom, $colour); 652c977deacSMark Prins break; 653c977deacSMark Prins case 'Point': 654c977deacSMark Prins $this->drawPoint($geom, $colour); 6556c6bb022SMark Prins break; 6566c6bb022SMark Prins default: 6572d11d700SMark Prins // draw nothing 6586c6bb022SMark Prins break; 6596c6bb022SMark Prins } 6606c6bb022SMark Prins } 661c977deacSMark Prins 662e61425c7SMark Prins /** 663e61425c7SMark Prins * Draw a polygon on the map. 664ab8cbd2bSMark Prins * 665e61425c7SMark Prins * @param Polygon $polygon 666ab8cbd2bSMark Prins * @param int $colour 667ab8cbd2bSMark Prins * drawing colour 668e61425c7SMark Prins */ 669*a760825cSgithub-actions[bot] private function drawPolygon($polygon, int $colour) 670*a760825cSgithub-actions[bot] { 671c977deacSMark Prins // TODO implementation of drawing holes, 672c977deacSMark Prins // maybe draw the polygon to an in-memory image and use imagecopy, draw polygon in col., draw holes in bgcol? 673c977deacSMark Prins 674c977deacSMark Prins // print_r('Polygon:<br />'); 675c977deacSMark Prins // print_r($polygon); 676*a760825cSgithub-actions[bot] $extPoints = []; 677c977deacSMark Prins // extring is a linestring actually.. 678c977deacSMark Prins $extRing = $polygon->exteriorRing(); 679c977deacSMark Prins 680c977deacSMark Prins for ($i = 1; $i < $extRing->numGeometries(); $i++) { 681c977deacSMark Prins $p1 = $extRing->geometryN($i); 68257f8d5bbSMark Prins $x = floor( 68357f8d5bbSMark Prins ($this->width / 2) - $this->tileSize * ($this->centerX - $this->lonToTile($p1->x(), $this->zoom)) 68457f8d5bbSMark Prins ); 68557f8d5bbSMark Prins $y = floor( 68657f8d5bbSMark Prins ($this->height / 2) - $this->tileSize * ($this->centerY - $this->latToTile($p1->y(), $this->zoom)) 68757f8d5bbSMark Prins ); 688c977deacSMark Prins $extPoints [] = $x; 689c977deacSMark Prins $extPoints [] = $y; 690e61425c7SMark Prins } 691c977deacSMark Prins // print_r('points:('.($i-1).')<br />'); 692c977deacSMark Prins // print_r($extPoints); 693c977deacSMark Prins // imagepolygon ($this->image, $extPoints, $i-1, $colour ); 694c977deacSMark Prins imagefilledpolygon($this->image, $extPoints, $i - 1, $colour); 695c977deacSMark Prins } 696c977deacSMark Prins 697628e43ccSMark Prins /** 69857f8d5bbSMark Prins * Draw a line on the map. 69957f8d5bbSMark Prins * 70057f8d5bbSMark Prins * @param LineString $line 70157f8d5bbSMark Prins * @param int $colour 70257f8d5bbSMark Prins * drawing colour 70357f8d5bbSMark Prins */ 704*a760825cSgithub-actions[bot] private function drawLineString($line, $colour) 705*a760825cSgithub-actions[bot] { 70657f8d5bbSMark Prins imagesetthickness($this->image, 2); 70757f8d5bbSMark Prins for ($p = 1; $p < $line->numGeometries(); $p++) { 70857f8d5bbSMark Prins // get first pair of points 70957f8d5bbSMark Prins $p1 = $line->geometryN($p); 71057f8d5bbSMark Prins $p2 = $line->geometryN($p + 1); 71157f8d5bbSMark Prins // translate to paper space 71257f8d5bbSMark Prins $x1 = floor( 71357f8d5bbSMark Prins ($this->width / 2) - $this->tileSize * ($this->centerX - $this->lonToTile($p1->x(), $this->zoom)) 71457f8d5bbSMark Prins ); 71557f8d5bbSMark Prins $y1 = floor( 71657f8d5bbSMark Prins ($this->height / 2) - $this->tileSize * ($this->centerY - $this->latToTile($p1->y(), $this->zoom)) 71757f8d5bbSMark Prins ); 71857f8d5bbSMark Prins $x2 = floor( 71957f8d5bbSMark Prins ($this->width / 2) - $this->tileSize * ($this->centerX - $this->lonToTile($p2->x(), $this->zoom)) 72057f8d5bbSMark Prins ); 72157f8d5bbSMark Prins $y2 = floor( 72257f8d5bbSMark Prins ($this->height / 2) - $this->tileSize * ($this->centerY - $this->latToTile($p2->y(), $this->zoom)) 72357f8d5bbSMark Prins ); 72457f8d5bbSMark Prins // draw to image 72557f8d5bbSMark Prins imageline($this->image, $x1, $y1, $x2, $y2, $colour); 72657f8d5bbSMark Prins } 72757f8d5bbSMark Prins imagesetthickness($this->image, 1); 72857f8d5bbSMark Prins } 72957f8d5bbSMark Prins 73057f8d5bbSMark Prins /** 73157f8d5bbSMark Prins * Draw a point on the map. 73257f8d5bbSMark Prins * 73357f8d5bbSMark Prins * @param Point $point 73457f8d5bbSMark Prins * @param int $colour 73557f8d5bbSMark Prins * drawing colour 73657f8d5bbSMark Prins */ 737*a760825cSgithub-actions[bot] private function drawPoint($point, $colour) 738*a760825cSgithub-actions[bot] { 73957f8d5bbSMark Prins imagesetthickness($this->image, 2); 74057f8d5bbSMark Prins // translate to paper space 74157f8d5bbSMark Prins $cx = floor( 74257f8d5bbSMark Prins ($this->width / 2) - $this->tileSize * ($this->centerX - $this->lonToTile($point->x(), $this->zoom)) 74357f8d5bbSMark Prins ); 74457f8d5bbSMark Prins $cy = floor( 74557f8d5bbSMark Prins ($this->height / 2) - $this->tileSize * ($this->centerY - $this->latToTile($point->y(), $this->zoom)) 74657f8d5bbSMark Prins ); 74757f8d5bbSMark Prins $r = 5; 74857f8d5bbSMark Prins // draw to image 74957f8d5bbSMark Prins // imageellipse($this->image, $cx, $cy,$r, $r, $colour); 75057f8d5bbSMark Prins imagefilledellipse($this->image, $cx, $cy, $r, $r, $colour); 75157f8d5bbSMark Prins // don't use imageellipse because the imagesetthickness function has 75257f8d5bbSMark Prins // no effect. So the better workaround is to use imagearc. 75357f8d5bbSMark Prins imagearc($this->image, $cx, $cy, $r, $r, 0, 359, $colour); 75457f8d5bbSMark Prins imagesetthickness($this->image, 1); 75557f8d5bbSMark Prins } 75657f8d5bbSMark Prins 75757f8d5bbSMark Prins /** 75857f8d5bbSMark Prins * Draw gpx trace on the map. 7593e7791adSMark Prins * @throws exception when loading the GPX fails 76057f8d5bbSMark Prins */ 761*a760825cSgithub-actions[bot] public function drawGPX() 762*a760825cSgithub-actions[bot] { 76357f8d5bbSMark Prins $col = imagecolorallocatealpha($this->image, 0, 0, 255, .4 * 127); 76457f8d5bbSMark Prins $gpxgeom = geoPHP::load(file_get_contents($this->gpxFileName), 'gpx'); 76557f8d5bbSMark Prins $this->drawGeometry($gpxgeom, $col); 76657f8d5bbSMark Prins } 76757f8d5bbSMark Prins 76857f8d5bbSMark Prins /** 76957f8d5bbSMark Prins * Draw geojson on the map. 7703e7791adSMark Prins * @throws exception when loading the JSON fails 77157f8d5bbSMark Prins */ 772*a760825cSgithub-actions[bot] public function drawGeojson() 773*a760825cSgithub-actions[bot] { 77457f8d5bbSMark Prins $col = imagecolorallocatealpha($this->image, 255, 0, 255, .4 * 127); 77557f8d5bbSMark Prins $gpxgeom = geoPHP::load(file_get_contents($this->geojsonFileName), 'json'); 77657f8d5bbSMark Prins $this->drawGeometry($gpxgeom, $col); 77757f8d5bbSMark Prins } 77857f8d5bbSMark Prins 77957f8d5bbSMark Prins /** 780628e43ccSMark Prins * add copyright and origin notice and icons to the map. 781628e43ccSMark Prins */ 782*a760825cSgithub-actions[bot] public function drawCopyright() 783*a760825cSgithub-actions[bot] { 784*a760825cSgithub-actions[bot] $logoBaseDir = __DIR__ . '/' . 'logo/'; 785628e43ccSMark Prins $logoImg = imagecreatefrompng($logoBaseDir . $this->tileInfo ['openstreetmap'] ['logo']); 786628e43ccSMark Prins $textcolor = imagecolorallocate($this->image, 0, 0, 0); 787628e43ccSMark Prins $bgcolor = imagecolorallocate($this->image, 200, 200, 200); 788628e43ccSMark Prins 78957f8d5bbSMark Prins imagecopy( 79057f8d5bbSMark Prins $this->image, 79157f8d5bbSMark Prins $logoImg, 79257f8d5bbSMark Prins 0, 79357f8d5bbSMark Prins imagesy($this->image) - imagesy($logoImg), 79457f8d5bbSMark Prins 0, 79557f8d5bbSMark Prins 0, 79657f8d5bbSMark Prins imagesx($logoImg), 79757f8d5bbSMark Prins imagesy($logoImg) 79857f8d5bbSMark Prins ); 79957f8d5bbSMark Prins imagestring( 80057f8d5bbSMark Prins $this->image, 80157f8d5bbSMark Prins 1, 80257f8d5bbSMark Prins imagesx($logoImg) + 2, 80357f8d5bbSMark Prins imagesy($this->image) - imagesy($logoImg) + 1, 80457f8d5bbSMark Prins $this->tileInfo ['openstreetmap'] ['txt'], 80557f8d5bbSMark Prins $bgcolor 80657f8d5bbSMark Prins ); 80757f8d5bbSMark Prins imagestring( 80857f8d5bbSMark Prins $this->image, 80957f8d5bbSMark Prins 1, 81057f8d5bbSMark Prins imagesx($logoImg) + 1, 81157f8d5bbSMark Prins imagesy($this->image) - imagesy($logoImg), 81257f8d5bbSMark Prins $this->tileInfo ['openstreetmap'] ['txt'], 81357f8d5bbSMark Prins $textcolor 81457f8d5bbSMark Prins ); 815628e43ccSMark Prins 816628e43ccSMark Prins // additional tile source info, ie. who created/hosted the tiles 817257dffd7SMark Prins $xIconOffset = 0; 818257dffd7SMark Prins if ($this->maptype === 'openstreetmap') { 819257dffd7SMark Prins $mapAuthor = "(c) OpenStreetMap maps/CC BY-SA"; 820257dffd7SMark Prins } else { 821257dffd7SMark Prins $mapAuthor = $this->tileInfo [$this->maptype] ['txt']; 822628e43ccSMark Prins $iconImg = imagecreatefrompng($logoBaseDir . $this->tileInfo [$this->maptype] ['logo']); 823257dffd7SMark Prins $xIconOffset = imagesx($iconImg); 82457f8d5bbSMark Prins imagecopy( 82557f8d5bbSMark Prins $this->image, 826*a760825cSgithub-actions[bot] $iconImg, 827*a760825cSgithub-actions[bot] imagesx($logoImg) + 1, 82857f8d5bbSMark Prins imagesy($this->image) - imagesy($iconImg), 82957f8d5bbSMark Prins 0, 83057f8d5bbSMark Prins 0, 831*a760825cSgithub-actions[bot] imagesx($iconImg), 832*a760825cSgithub-actions[bot] imagesy($iconImg) 83357f8d5bbSMark Prins ); 834257dffd7SMark Prins } 83557f8d5bbSMark Prins imagestring( 83657f8d5bbSMark Prins $this->image, 837*a760825cSgithub-actions[bot] 1, 838*a760825cSgithub-actions[bot] imagesx($logoImg) + $xIconOffset + 4, 83957f8d5bbSMark Prins imagesy($this->image) - ceil(imagesy($logoImg) / 2) + 1, 840257dffd7SMark Prins $mapAuthor, 84157f8d5bbSMark Prins $bgcolor 84257f8d5bbSMark Prins ); 84357f8d5bbSMark Prins imagestring( 84457f8d5bbSMark Prins $this->image, 845*a760825cSgithub-actions[bot] 1, 846*a760825cSgithub-actions[bot] imagesx($logoImg) + $xIconOffset + 3, 84757f8d5bbSMark Prins imagesy($this->image) - ceil(imagesy($logoImg) / 2), 848257dffd7SMark Prins $mapAuthor, 84957f8d5bbSMark Prins $textcolor 85057f8d5bbSMark Prins ); 851628e43ccSMark Prins } 852628e43ccSMark Prins} 853