9530e62141
Define 'mapnik' and 'osmrender' explcitly as aliases of 'standard' rather than just setting to the same tile URL. That approach would mean the aliases will have their own cache directory (which is unnecessary). Now they will all use the 'standard' cache directory.
418 lines
15 KiB
PHP
418 lines
15 KiB
PHP
<?php
|
|
|
|
/**
|
|
* staticMapLite 0.3.1
|
|
*
|
|
* Copyright 2009 Gerhard Koch
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
* @author Gerhard Koch <gerhard.koch AT ymail.com>
|
|
*
|
|
* USAGE:
|
|
*
|
|
* staticmap.php?center=40.714728,-73.998672&zoom=14&size=512x512&maptype=standrd&markers=40.702147,-74.015794,blues|40.711614,-74.012318,greeng|40.718217,-73.998284,redc
|
|
*
|
|
*/
|
|
|
|
error_reporting(0);
|
|
ini_set('display_errors', 'off');
|
|
|
|
Class staticMapLite
|
|
{
|
|
|
|
protected $maxWidth = 1024;
|
|
protected $maxHeight = 1024;
|
|
|
|
protected $tileSize = 256;
|
|
protected $tileSrcUrl = array(
|
|
'standard' => 'http://tile.openstreetmap.org/{Z}/{X}/{Y}.png',
|
|
'cycle' => 'http://a.tile.opencyclemap.org/cycle/{Z}/{X}/{Y}.png',
|
|
);
|
|
|
|
// Deprecated map_type values still supported as aliases
|
|
protected $mapTypeAliases = array(
|
|
'mapnik' => 'standard',
|
|
'osmarender' => 'standard'
|
|
);
|
|
|
|
protected $tileDefaultSrc = 'standard';
|
|
protected $markerBaseDir = 'images/markers';
|
|
protected $osmLogo = 'images/osm_logo.png';
|
|
|
|
protected $markerPrototypes = array(
|
|
// found at http://www.mapito.net/map-marker-icons.html
|
|
'lighblue' => array('regex' => '/^lightblue([0-9]+)$/',
|
|
'extension' => '.png',
|
|
'shadow' => false,
|
|
'offsetImage' => '0,-19',
|
|
'offsetShadow' => false
|
|
),
|
|
// openlayers std markers
|
|
'ol-marker' => array('regex' => '/^ol-marker(|-blue|-gold|-green)+$/',
|
|
'extension' => '.png',
|
|
'shadow' => '../marker_shadow.png',
|
|
'offsetImage' => '-10,-25',
|
|
'offsetShadow' => '-1,-13'
|
|
),
|
|
// taken from http://www.visual-case.it/cgi-bin/vc/GMapsIcons.pl
|
|
'ylw' => array('regex' => '/^(pink|purple|red|ltblu|ylw)-pushpin$/',
|
|
'extension' => '.png',
|
|
'shadow' => '../marker_shadow.png',
|
|
'offsetImage' => '-10,-32',
|
|
'offsetShadow' => '-1,-13'
|
|
),
|
|
// http://svn.openstreetmap.org/sites/other/StaticMap/symbols/0.png
|
|
'ojw' => array('regex' => '/^bullseye$/',
|
|
'extension' => '.png',
|
|
'shadow' => false,
|
|
'offsetImage' => '-20,-20',
|
|
'offsetShadow' => false
|
|
)
|
|
);
|
|
|
|
|
|
protected $useTileCache = true;
|
|
protected $tileCacheBaseDir = '../cache/tiles';
|
|
|
|
protected $useMapCache = true;
|
|
protected $mapCacheBaseDir = '../cache/maps';
|
|
protected $mapCacheID = '';
|
|
protected $mapCacheFile = '';
|
|
protected $mapCacheExtension = 'png';
|
|
|
|
protected $zoom, $lat, $lon, $width, $height, $markers, $image, $maptype;
|
|
protected $centerX, $centerY, $offsetX, $offsetY;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->zoom = 0;
|
|
$this->lat = 0;
|
|
$this->lon = 0;
|
|
$this->width = 500;
|
|
$this->height = 350;
|
|
$this->markers = array();
|
|
$this->maptype = $this->tileDefaultSrc;
|
|
}
|
|
|
|
public function parseParams()
|
|
{
|
|
global $_GET;
|
|
|
|
if (!empty($_GET['show'])) {
|
|
$this->parseOjwParams();
|
|
}
|
|
else {
|
|
$this->parseLiteParams();
|
|
}
|
|
}
|
|
|
|
public function parseLiteParams()
|
|
{
|
|
// get zoom from GET paramter
|
|
$this->zoom = $_GET['zoom'] ? intval($_GET['zoom']) : 0;
|
|
if ($this->zoom > 18) $this->zoom = 18;
|
|
|
|
// get lat and lon from GET paramter
|
|
list($this->lat, $this->lon) = explode(',', $_GET['center']);
|
|
$this->lat = floatval($this->lat);
|
|
$this->lon = floatval($this->lon);
|
|
|
|
// get size from GET paramter
|
|
if ($_GET['size']) {
|
|
list($this->width, $this->height) = explode('x', $_GET['size']);
|
|
$this->width = intval($this->width);
|
|
if ($this->width > $this->maxWidth) $this->width = $this->maxWidth;
|
|
$this->height = intval($this->height);
|
|
if ($this->height > $this->maxHeight) $this->height = $this->maxHeight;
|
|
}
|
|
if (!empty($_GET['markers'])) {
|
|
$markers = explode('|', $_GET['markers']);
|
|
foreach ($markers as $marker) {
|
|
list($markerLat, $markerLon, $markerType) = explode(',', $marker);
|
|
$markerLat = floatval($markerLat);
|
|
$markerLon = floatval($markerLon);
|
|
$markerType = basename($markerType);
|
|
$this->markers[] = array('lat' => $markerLat, 'lon' => $markerLon, 'type' => $markerType);
|
|
}
|
|
|
|
}
|
|
if ($_GET['maptype']) {
|
|
$mapTypeParam = $_GET['maptype'];
|
|
if (array_key_exists($mapTypeParam, $this->mapTypeAliases)) $mapTypeParam = $this->mapTypeAliases[$mapTypeParam];
|
|
if (array_key_exists($mapTypeParam, $this->tileSrcUrl)) $this->maptype = $mapTypeParam;
|
|
}
|
|
}
|
|
|
|
public function parseOjwParams()
|
|
{
|
|
$this->lat = floatval($_GET['lat']);
|
|
$this->lon = floatval($_GET['lon']);
|
|
$this->zoom = intval($_GET['z']);
|
|
|
|
$this->width = intval($_GET['w']);
|
|
if ($this->width > $this->maxWidth) $this->width = $this->maxWidth;
|
|
$this->height = intval($_GET['h']);
|
|
if ($this->height > $this->maxHeight) $this->height = $this->maxHeight;
|
|
|
|
|
|
if (!empty($_GET['mlat0'])) {
|
|
$markerLat = floatval($_GET['mlat0']);
|
|
if (!empty($_GET['mlon0'])) {
|
|
$markerLon = floatval($_GET['mlon0']);
|
|
$this->markers[] = array('lat' => $markerLat, 'lon' => $markerLon, 'type' => "bullseye");
|
|
}
|
|
}
|
|
}
|
|
|
|
public function lonToTile($long, $zoom)
|
|
{
|
|
return (($long + 180) / 360) * pow(2, $zoom);
|
|
}
|
|
|
|
public function latToTile($lat, $zoom)
|
|
{
|
|
return (1 - log(tan($lat * pi() / 180) + 1 / cos($lat * pi() / 180)) / pi()) / 2 * pow(2, $zoom);
|
|
}
|
|
|
|
public function initCoords()
|
|
{
|
|
$this->centerX = $this->lonToTile($this->lon, $this->zoom);
|
|
$this->centerY = $this->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->image = imagecreatetruecolor($this->width, $this->height);
|
|
$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++) {
|
|
$url = str_replace(array('{Z}', '{X}', '{Y}'), array($this->zoom, $x, $y), $this->tileSrcUrl[$this->maptype]);
|
|
$tileData = $this->fetchTile($url);
|
|
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->image, $tileImage, $destX, $destY, 0, 0, $this->tileSize, $this->tileSize);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
public function placeMarkers()
|
|
{
|
|
// loop thru marker array
|
|
foreach ($this->markers as $marker) {
|
|
// set some local variables
|
|
$markerLat = $marker['lat'];
|
|
$markerLon = $marker['lon'];
|
|
$markerType = $marker['type'];
|
|
// clear variables from previous loops
|
|
$markerFilename = '';
|
|
$markerShadow = '';
|
|
$matches = false;
|
|
// check for marker type, get settings from markerPrototypes
|
|
if ($markerType) {
|
|
foreach ($this->markerPrototypes as $markerPrototype) {
|
|
if (preg_match($markerPrototype['regex'], $markerType, $matches)) {
|
|
$markerFilename = $matches[0] . $markerPrototype['extension'];
|
|
if ($markerPrototype['offsetImage']) {
|
|
list($markerImageOffsetX, $markerImageOffsetY) = explode(",", $markerPrototype['offsetImage']);
|
|
}
|
|
$markerShadow = $markerPrototype['shadow'];
|
|
if ($markerShadow) {
|
|
list($markerShadowOffsetX, $markerShadowOffsetY) = explode(",", $markerPrototype['offsetShadow']);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// check required files or set default
|
|
if ($markerFilename == '' || !file_exists($this->markerBaseDir . '/' . $markerFilename)) {
|
|
$markerIndex++;
|
|
$markerFilename = 'lightblue' . $markerIndex . '.png';
|
|
$markerImageOffsetX = 0;
|
|
$markerImageOffsetY = -19;
|
|
}
|
|
|
|
// create img resource
|
|
if (file_exists($this->markerBaseDir . '/' . $markerFilename)) {
|
|
$markerImg = imagecreatefrompng($this->markerBaseDir . '/' . $markerFilename);
|
|
} else {
|
|
$markerImg = imagecreatefrompng($this->markerBaseDir . '/lightblue1.png');
|
|
}
|
|
|
|
// check for shadow + create shadow recource
|
|
if ($markerShadow && file_exists($this->markerBaseDir . '/' . $markerShadow)) {
|
|
$markerShadowImg = imagecreatefrompng($this->markerBaseDir . '/' . $markerShadow);
|
|
}
|
|
|
|
// calc position
|
|
$destX = floor(($this->width / 2) - $this->tileSize * ($this->centerX - $this->lonToTile($markerLon, $this->zoom)));
|
|
$destY = floor(($this->height / 2) - $this->tileSize * ($this->centerY - $this->latToTile($markerLat, $this->zoom)));
|
|
|
|
// copy shadow on basemap
|
|
if ($markerShadow && $markerShadowImg) {
|
|
imagecopy($this->image, $markerShadowImg, $destX + intval($markerShadowOffsetX), $destY + intval($markerShadowOffsetY),
|
|
0, 0, imagesx($markerShadowImg), imagesy($markerShadowImg));
|
|
}
|
|
|
|
// copy marker on basemap above shadow
|
|
imagecopy($this->image, $markerImg, $destX + intval($markerImageOffsetX), $destY + intval($markerImageOffsetY),
|
|
0, 0, imagesx($markerImg), imagesy($markerImg));
|
|
|
|
};
|
|
}
|
|
|
|
|
|
public function tileUrlToFilename($url)
|
|
{
|
|
return $this->tileCacheBaseDir . "/" . str_replace(array('http://'), '', $url);
|
|
}
|
|
|
|
public function checkTileCache($url)
|
|
{
|
|
$filename = $this->tileUrlToFilename($url);
|
|
if (file_exists($filename)) {
|
|
return file_get_contents($filename);
|
|
}
|
|
}
|
|
|
|
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 mkdir_recursive($pathname, $mode)
|
|
{
|
|
is_dir(dirname($pathname)) || $this->mkdir_recursive(dirname($pathname), $mode);
|
|
return is_dir($pathname) || @mkdir($pathname, $mode);
|
|
}
|
|
|
|
public function writeTileToCache($url, $data)
|
|
{
|
|
$filename = $this->tileUrlToFilename($url);
|
|
$this->mkdir_recursive(dirname($filename), 0777);
|
|
file_put_contents($filename, $data);
|
|
}
|
|
|
|
public function fetchTile($url)
|
|
{
|
|
if ($this->useTileCache && ($cached = $this->checkTileCache($url))) return $cached;
|
|
$ch = curl_init();
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
|
curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/4.0");
|
|
curl_setopt($ch, CURLOPT_URL, $url);
|
|
$tile = curl_exec($ch);
|
|
curl_close($ch);
|
|
if ($tile && $this->useTileCache) {
|
|
$this->writeTileToCache($url, $tile);
|
|
}
|
|
return $tile;
|
|
|
|
}
|
|
|
|
public function copyrightNotice()
|
|
{
|
|
$logoImg = imagecreatefrompng($this->osmLogo);
|
|
imagecopy($this->image, $logoImg, imagesx($this->image) - imagesx($logoImg), imagesy($this->image) - 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 ($this->osmLogo) $this->copyrightNotice();
|
|
}
|
|
|
|
public function showMap()
|
|
{
|
|
$this->parseParams();
|
|
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->image, $this->mapCacheIDToFilename(), 9);
|
|
$this->sendHeader();
|
|
if (file_exists($this->mapCacheIDToFilename())) {
|
|
return file_get_contents($this->mapCacheIDToFilename());
|
|
} else {
|
|
return imagepng($this->image);
|
|
}
|
|
} 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->image);
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
$map = new staticMapLite();
|
|
print $map->showMap();
|