'http://tile.openstreetmap.org/{z}/{x}/{y}.png', 'osmarenderer' => 'http://otile1.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.png', 'cycle' => 'http://a.tile.opencyclemap.org/cycle/{z}/{x}/{y}.png', 'wikimedia-intl' => 'https://maps.wikimedia.org/osm-intl/{z}/{x}/{y}.png', ]; protected $tileDefaultSrc = 'mapnik'; protected $osmLogo = '../images/osm_logo.png'; protected $useTileCache = true; protected $useMapCache = false; protected $mapCacheBaseDir = '../cache/maps'; protected $mapCacheID = ''; protected $mapCacheFile = ''; protected $mapCacheExtension = 'png'; protected $zoom, $lat, $lon, $width, $height, $image, $maptype; protected $centerX, $centerY, $offsetX, $offsetY; protected $markers = []; protected $polylines = []; public function __construct() { $this->zoom = 0; $this->lat = 0; $this->lon = 0; $this->width = 500; $this->height = 350; $this->maptype = $this->tileDefaultSrc; $this->tileResolver = new CachedTileResolver(); $this->tileResolver->setTileLayerUrl($this->tileSrcUrl[$this->maptype]); } public function addMarker(Marker $marker): Printer { $this->markers[] = $marker; return $this; } public function addPolyline(Polyline $polyline): Printer { $this->polylines[] = $polyline; return $this; } public function setCenter(float $latitude, float $longitude): Printer { $this->lat = $latitude; $this->lon = $longitude; return $this; } public function setSize(int $width, int $height): Printer { $this->width = $width; $this->height = $height; if ($this->width > $this->maxWidth) { $this->width = $this->maxWidth; } if ($this->height > $this->maxHeight) { $this->height = $this->maxHeight; } return $this; } public function setZoom(int $zoom): Printer { $this->zoom = $zoom; if ($this->zoom > 18) { $this->zoom = 18; } return $this; } public function setMapType(string $mapType): Printer { $this->maptype = $mapType; $this->tileResolver->setTileLayerUrl($this->tileSrcUrl[$this->maptype]); return $this; } public function initCoords() { $this->centerX = Util::lonToTile($this->lon, $this->zoom); $this->centerY = Util::latToTile($this->lat, $this->zoom); $this->offsetX = floor((floor($this->centerX) - $this->centerX) * $this->tileSize); $this->offsetY = floor((floor($this->centerY) - $this->centerY) * $this->tileSize); } public function createBaseMap() { $this->canvas = new Canvas( $this->width, $this->height, $this->zoom, $this->centerX, $this->centerY ); $startX = floor($this->centerX - ($this->width / $this->tileSize) / 2); $startY = floor($this->centerY - ($this->height / $this->tileSize) / 2); $endX = ceil($this->centerX + ($this->width / $this->tileSize) / 2); $endY = ceil($this->centerY + ($this->height / $this->tileSize) / 2); $this->offsetX = -floor(($this->centerX - floor($this->centerX)) * $this->tileSize); $this->offsetY = -floor(($this->centerY - floor($this->centerY)) * $this->tileSize); $this->offsetX += floor($this->width / 2); $this->offsetY += floor($this->height / 2); $this->offsetX += floor($startX - floor($this->centerX)) * $this->tileSize; $this->offsetY += floor($startY - floor($this->centerY)) * $this->tileSize; for ($x = $startX; $x <= $endX; $x++) { for ($y = $startY; $y <= $endY; $y++) { $tileData = $this->tileResolver->fetch($this->zoom, $x, $y); if ($tileData) { $tileImage = imagecreatefromstring($tileData); } else { $tileImage = imagecreate($this->tileSize, $this->tileSize); $color = imagecolorallocate($tileImage, 255, 255, 255); @imagestring($tileImage, 1, 127, 127, 'err', $color); } $destX = ($x - $startX) * $this->tileSize + $this->offsetX; $destY = ($y - $startY) * $this->tileSize + $this->offsetY; imagecopy($this->canvas->getImage(), $tileImage, $destX, $destY, 0, 0, $this->tileSize, $this->tileSize); } } } public function placeMarkers() { $printer = new MarkerPrinter(); foreach ($this->markers as $marker) { $printer ->setMarker($marker) ->paint($this->canvas) ; } } public function placePolylines() { $printer = new PolylinePrinter(); /** @var Polyline $polyline */ foreach ($this->polylines as $polyline) { $printer ->setPolyline($polyline) ->paint($this->canvas) ; } } public function checkMapCache() { $this->mapCacheID = md5($this->serializeParams()); $filename = $this->mapCacheIDToFilename(); if (file_exists($filename)) return true; } public function serializeParams() { return join("&", array($this->zoom, $this->lat, $this->lon, $this->width, $this->height, serialize($this->markers), $this->maptype)); } public function mapCacheIDToFilename() { if (!$this->mapCacheFile) { $this->mapCacheFile = $this->mapCacheBaseDir . "/" . $this->maptype . "/" . $this->zoom . "/cache_" . substr($this->mapCacheID, 0, 2) . "/" . substr($this->mapCacheID, 2, 2) . "/" . substr($this->mapCacheID, 4); } return $this->mapCacheFile . "." . $this->mapCacheExtension; } public function copyrightNotice() { $logoImg = imagecreatefrompng($this->osmLogo); imagecopy($this->canvas->getImage(), $logoImg, imagesx($this->canvas->getImage()) - imagesx($logoImg), imagesy($this->canvas->getImage()) - imagesy($logoImg), 0, 0, imagesx($logoImg), imagesy($logoImg)); } public function sendHeader() { header('Content-Type: image/png'); $expires = 60 * 60 * 24 * 14; header("Pragma: public"); header("Cache-Control: maxage=" . $expires); header('Expires: ' . gmdate('D, d M Y H:i:s', time() + $expires) . ' GMT'); } public function makeMap() { $this->initCoords(); $this->createBaseMap(); if (count($this->markers)) $this->placeMarkers(); if (count($this->polylines)) $this->placePolylines(); if ($this->osmLogo) $this->copyrightNotice(); } public function showMap() { if ($this->useMapCache) { // use map cache, so check cache for map if (!$this->checkMapCache()) { // map is not in cache, needs to be build $this->makeMap(); $this->mkdir_recursive(dirname($this->mapCacheIDToFilename()), 0777); imagepng($this->canvas->getImage(), $this->mapCacheIDToFilename(), 9); $this->sendHeader(); if (file_exists($this->mapCacheIDToFilename())) { return file_get_contents($this->mapCacheIDToFilename()); } else { return imagepng($this->canvas->getImage()); } } else { // map is in cache $this->sendHeader(); return file_get_contents($this->mapCacheIDToFilename()); } } else { // no cache, make map, send headers and deliver png $this->makeMap(); $this->sendHeader(); return imagepng($this->canvas->getImage()); } } public function mkdir_recursive($pathname, $mode) { is_dir(dirname($pathname)) || $this->mkdir_recursive(dirname($pathname), $mode); return is_dir($pathname) || @mkdir($pathname, $mode); } }