1
0
mirror of https://github.com/klokantech/tileserver-php.git synced 2025-08-22 06:03:08 +02:00

22 Commits
design ... v0.1

Author SHA1 Message Date
Dalibor Janák
324129f8f8 Update README.md
UTFgrid
2014-03-26 10:00:01 +01:00
Dalibor Janák
59c2d03552 Added UTFgrid functionality on url {layer}.mbtiles/{z}/{x}/{y}.grid.json returns jsonp with grid and also callback is supported 2014-03-26 09:36:44 +01:00
Dalibor Janák
bd8c6b286f Added basic support for UTFgrid for url {server}/{layer}.mbtiles/{z}/{x}/{y}.grid 2014-03-25 10:10:06 +01:00
Petr Pridal
8fa1bfcbab Updated links to MapTiler in README 2014-01-31 08:18:01 +01:00
Petr Pridal
41dd3ec847 Fixing typo in metadata "sheme" -> "scheme". 2013-11-12 16:24:32 +01:00
Petr Pridal
b7b41e9725 Added screenshot to readme 2013-08-28 14:19:06 +02:00
Petr Pridal
ff7c48f0b0 Added the interactive user interface made 2013-08-26 15:29:59 +02:00
Petr Pridal
780e1c6796 Fixed DOCTYPE header 2013-04-26 08:57:32 +02:00
Petr Pridal
fcf88d62c0 Added CORS headers for cross side AJAX requests and WebGL textures 2013-04-26 08:40:24 +02:00
Petr Pridal
ad902c1a46 Fixed warning for missing files on older php. 2013-04-25 23:52:19 +02:00
Petr Pridal
d241d32716 Update README.md 2013-02-08 10:40:20 +01:00
Petr Pridal
4eee2225db TMS TileMap Metadata with type="InvertedTMS" too 2013-01-11 16:20:25 +01:00
Petr Pridal
121308fbf8 Update README.md 2012-09-11 09:41:11 +03:00
Petr Pridal
1aeb3bdb8c Fix of the Gaia client - it displays now all layers and correctly load the WGS84 ones too. 2012-08-12 23:55:20 +02:00
Petr Pridal
82414cb082 Removed duplicate file - a typo during commit. 2012-08-12 20:34:40 +02:00
Petr Pridal
0d4d50ab9c Initial .mbtiles implementation. 2012-08-12 20:28:23 +02:00
Petr Pridal
efcb733670 The initial commit for 0.1pre. 2012-08-12 16:47:34 +02:00
Petr Pridal
184a363778 Fixed missing tag in the /tms root metadata. 2012-08-12 16:46:14 +02:00
Petr Pridal
edce8b391b Performace chapter related to multiple domains. 2012-08-12 16:36:19 +02:00
Petr Pridal
754de66927 Default baseUrl is http://localhost/ 2012-08-12 16:23:48 +02:00
Petr Pridal
ab4980c06b The initial commit for 0.1pre. 2012-08-12 15:47:27 +02:00
Petr Pridal
555154b2d8 Update README.md 2012-08-11 00:27:36 +03:00
6 changed files with 1451 additions and 3 deletions

99
.htaccess Normal file
View File

@@ -0,0 +1,99 @@
# tileserver.php integration with Apache via .htaccess
# Restrictions for data crawlers
Options -Indexes
Options +FollowSymLinks
Options -MultiViews
# Optional CORS header for cross-domain origin access to all data
#<ifModule mod_headers.c>
# Header set Access-Control-Allow-Origin *
#</ifModule>
# Mapping of the WMTS standardized URLs to real files and XML capabilities to tileserver.php
RewriteEngine On
# WMTS RESTful
# ------------
# The file can be accessed directly:
# Example: http://www.tileserver.com/grandcanyon/10/192/401.png
# map /wmts/layer/[ANYTHING]z/x/y.ext -> /layer/z/x/y.ext (WMTS ignoring tilematrixset,style,..)
# BTW This is used only by Gaia which ignores the <ResourceURL template="...">
RewriteRule ^wmts/([\w\d\._-]+)/.*?(\d+)/(\d+)/(\d+)\.(\w+)$ $1/$2/$4/$3.$5 [L]
# Example: http://www.tileserver.com/wmts/grandcanyon/style/tilematrixset/10/192/401.png
# map /layer/[ANYTHING]z/x/y.ext -> /layer/z/x/y.ext (WMTS ignoring tilematrix,style,..)
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([\w\d\._-]+)/.+?(\d+)/(\d+)/(\d+)\.(\w+)$ $1/$2/$3/$4.$5 [L]
# Example: http://www.tileserver.com/grandcanyon/style/tilematrixset/10/192/401.png
# rewrite .jpeg -> .jpg
RewriteRule ^(.+).jpeg$ $1.jpg [L]
# MBTiles support at /layer/z/x/y.ext - loads the tile from mbtiles with php
# TODO: serve also 404 errors for tiles
RewriteRule ^([^\/]+\.mbtiles)\/.*?(\d+)\/(\d+)\/(\d+)\.(\w+)$ tileserver-mbtiles.php?tileset=$1&z=$2&x=$3&y=$4&ext=$5 [L]
RewriteRule ^([^\/]+\.mbtiles)\/.*?(\d+)\/(\d+)\/(\d+)\.grid.json?$ tileserver-mbtiles.php?tileset=$1&z=$2&x=$3&y=$4&ext=grid [QSA,L]
# TODO: use mod_sqlite if available to map the tiles to URL directly by apache
# WMTS KVP
# --------
# map the /?key=value&... -> /layer/z/x/y.ext KVP getTile queries directly to files
# format passed as mime-extension, cleaning formats (jpeg->jpg)
RewriteCond %{QUERY_STRING} ^(.*)format=image/jpeg(.*)$ [NC]
RewriteRule ^(.*)$ $1?%1format=jpg%2 [N]
RewriteCond %{QUERY_STRING} ^(.*)format=jpeg(.*)$ [NC]
RewriteRule ^(.*)$ $1?%1format=jpg%2 [N]
RewriteCond %{QUERY_STRING} ^(.*)format=image/png(.*)$ [NC]
RewriteRule ^(.*)$ $1?%1format=png%2 [N]
# variable order of keys: TODO: sort the same way as mime-extension to fixed order
RewriteCond %{QUERY_STRING} ^.*request=gettile.*layer=([\w\d\._-]+).*tilematrix=(\d+).*tilerow=(\d+).*tilecol=(\d+).*format=(\w+).*$ [NC]
RewriteRule ^ %1/%2/%3/%4.%5 [L]
RewriteCond %{QUERY_STRING} ^.*request=gettile.*layer=([\w\d\._-]+).*format=(\w+).*tilematrix=(\d+).*tilerow=(\d+).*tilecol=(\d+).*$ [NC]
RewriteRule ^ %1/%3/%5/%4.%2 [L]
RewriteCond %{QUERY_STRING} ^.*request=gettile.*layer=([\w\d\._-]+).*tilematrix=(\d+).*tilecol=(\d+).*tilerow=(\d+).*format=(\w+).*$ [NC]
RewriteRule ^ %1/%2/%4/%3.%5 [L]
# Example: http://www.tileserver.com/wmts?request=getTile&layer=grandcanyon&tileMatrix=10&tilerow=192&tilecol=401&format=png
# Example: http://www.tileserver.com/wmts?service=WMTS&request=GetTile&version=1.0.0&layer=ne2geo&style=&format=image/jpeg&TileMatrixSet=WGS84&TileMatrix=1&TileRow=2&TileCol=2
# WMTS ServiceMetadata (GetCapabilities)
# --------------------------------------
RewriteRule ^.*WMTSCapabilities.xml$ tileserver-wmts.php [QSA,L]
RewriteRule ^wmts$ tileserver-wmts.php [QSA,L]
RewriteCond %{QUERY_STRING} ^.*request=getcapabilities.*$ [NC]
RewriteRule ^ tileserver-wmts.php [L]
# Example: http://www.tileserver.com/dev/?service=WMTS&version=1.0.0&request=GetCapabilities
# TMS XML (ArcBruTile)
# --------------------
RewriteRule ^tms$ tileserver-tms.php [QSA,L]
RewriteRule ^(.+)/tms$ tileserver-tms.php?layer=$1 [QSA,L]
# Example: http://www.tileserver.com/dev/?service=WMTS&version=1.0.0&request=GetCapabilities
# request for non-existent tiles -> layer/none.png or none.png
#RewriteCond %{REQUEST_FILENAME} !-f
#RewriteRule ^([\w\d\._-]+)/(\d+)/(\d+)/(\d+)\.png$ $1/none.png [L]
#RewriteCond %{REQUEST_FILENAME} !-f
#RewriteRule ^([\w\d\._-]+)/none\.png$ none.png [L]
# request for non-existent tiles -> layer/none.jpg or none.jpg
#RewriteCond %{REQUEST_FILENAME} !-f
#RewriteRule ^([\w\d\._-]+)/(\d+)/(\d+)/(\d+)\.jpg$ $1/none.jpg [L]
#RewriteCond %{REQUEST_FILENAME} !-f
#RewriteRule ^([\w\d\._-]+)/none\.jpg$ none.jpg [L]
# TileJSON JSONP wrapper for MapBOX.js API
RewriteRule ^maps.jsonp?$ tileserver.php?service=json [QSA,L]
RewriteCond %{REQUEST_URI} !\.grid\.json [NC]
RewriteRule ^(.+).jsonp?$ tileserver.php?service=json&layer=$1 [QSA,L]
# If-Modified-Since (if php is not installed as cgi then comment lines below)
#RewriteRule .* - [E=HTTP_IF_MODIFIED_SINCE:%{HTTP:If-Modified-Since}]
#RewriteRule .* - [E=HTTP_IF_NONE_MATCH:%{HTTP:If-None-Match}]
# handle all request on the root '/' by tileserver.php
RewriteRule ^$ tileserver.php?service=html [QSA,L]

260
README.md
View File

@@ -1,4 +1,258 @@
tileserver.php
==============
=================================================
TileServer PHP - OGC Web Map Tiling Server (WMTS)
=================================================
Easy to install OGC WMTS compliant map tile server for pre-rendered maps made with MapTiler / TileMill / MBTiles
This server distributes maps to desktop, web, and mobile applications from
a standard Apache+PHP web hosting.
It is a free and open-source project implementing OGC WMTS standard for
pre-rendered map tiles made with [MapTiler](http://www.maptiler.com/), GDAL2Tiles,
or available as MBTiles files.
It is the easiest and cheapest way how to serve zoomable maps in a
standardized way - practically from any ordinary web hosting.
It is easy to install - just copy the project files to a PHP-enabled
directory along with your map data containing metadata.json file.
It comes with an online interface showing the list of the maps and step-by-step guides for desktop GIS software:
![tileserver-screenshot](https://f.cloud.github.com/assets/59284/1041807/a040160c-0fdb-11e3-8941-ab367b2a648d.png)
This project is developed in PHP, not because it is the best language for
development of web applications, but because it maximally simplify the
deployment on large number of web hosting providers including various free
web hostings.
Tiles are served directly by Apache with mod_rewrite rules as static files
and therefore are very fast and with correct HTTP caching headers.
Only XML metadata are delivered via PHP.
MBTiles are served via PHP, unless they are unpacked with mbutil.
Requirements:
-------------
- Apache webserver (with mod_rewrite / .htaccess supported)
- PHP 5.2+
(or anther webserver implementing mod_rewrite rules and PHP)
Installation:
-------------
Download the project files as a zip archive or source code from GitHub and
unpack it into a web-hosting of your choice.
If you access the web address relevant to the installation directory,
the TileServer.php Server should display you a welcome message and further
instructions.
Then you can upload to the web hosting your mapping data - a directory with
tiles rendered with [MapTiler](http://www.maptiler.com/).
Tiles produced by open-source GDAL2Tiles or MapTiler and tiles in .mbtiles
files can be easily converted to required structure (XYZ with top-left origin
and metadata.json file).
The OpenSource utility mbutil (https://github.com/mapbox/mbutil) produces
exactly the required format.
Direct reading of .mbtiles files is supported, but with decreased performance
compared to the static files in a directory. Therefore the data management,
especially upload over FTP or similar protocols, is easier.
Supported protocols:
--------------------
- OpenGIS WMTS 1.0.0
The Open Geospatial Consortium (OGC) Web Map Tile Service (WMTS)
Both KVP and RESTful version 1.0.0:
http://www.opengeospatial.org/standards/wmts/
Target is maximal compliance to the standard.
Exposed at http://[...]/wmts
- OSGeo TMS 1.0.0
The OSGeo Tile Maps Service, but with inverted y-coordinates:
http://wiki.osgeo.org/wiki/Tile_Map_Service_Specification
This means request compatible with OpenStreetMap tile servers.
Target is "InvertedTMS" implementation used by the ArcBruTile client
which is available from http://arcbrutile.codeplex.com/ and uses
flipped y-axis.
Exposed at http://[...]/tms
- TileJSON.js
Metadata about the individual maps in a ready to use form for web
clients following the standard http://mapbox.com/developers/tilejson/
and with support for JSONP access.
Exposed at http://[...]/layer.jsonp
- Direct access with XYZ tile requests (to existing tiles in a directory
or to .mbtiles)
Compatible with Google Maps API / Bing SDK / OpenStreetMap clients.
Exposed at http://[...]/layer/z/x/y.ext
- MapBox UTFgrid request (for existing tiles in .mbtiles with UTFgrid support). Callback is supported
Example https://www.mapbox.com/demo/visiblemap/
Specification https://github.com/mapbox/utfgrid-spec
Exposed at http://[...]/layer/z/x/y.grid.json
To use the OGC WMTS services point your client (desktop or web) to the URL
of 'directory' where you installed tileserver.php project with suffix "wmts".
For example: http://www.example.com/directory/wmts
You can also install the project into a root directory of a virtual server:
Then the address is: http://www.example.com/wmts
Similarly for another end points.
The supported WMTS requests includes:
GetCapabilities RESTful/KVP:
http://[...]/1.0.0/WMTSCapabilities.xml
http://[...]?service=wmts&request=getcapabilities&version=1.0.0
GetTile RESTful/KVP:
http://[...]/layer/[ANYTHING-OPTIONAL][z]/[x]/[y].[ext]
http://[...]?service=wmts&request=getTile&layer=[layer]&tilematrix=[z]&tilerow=[y]&tilecol=[y]&format=[ext]
Another example requests are mentioned in the .htaccess.
Performance from the web clients
--------------------------------
It is highly recommended to map several domain names to the service, such as:
http://a.example.com/, http://b.example.com/, http://c.example.com/.
This can be done with DNS CNAME records pointing to your hosting.
The reason for this is that traditionally browsers will not send more then two
simultaneous http request to the same domain - with multiple domains for the
same server you can better saturate the network and receive the maps faster.
Performance
-----------
In case the data are available in a form of directory with XYZ tiles, then
Apache webserver is serving these files directly as WMTS RESTful or KVP.
This means performance is excellent, maps are delivered very fast and large
number of concurrent visitors can be handled even with quite a low-end
hardware or cheap/free web hosting providers.
Mod_rewrite rules are utilized to ensure the HTTP requests defined in the OCG
WMTS standard are served, and Apache preserve standard caching headers & eTag.
The performance should be significantly better then performance of any other
tile caching project (such as TileCache.org or GeoWebCache).
Performance graph for "apache static" comparing other tile caching projects
is available online at:
http://code.google.com/p/mod-geocache/wiki/PreliminaryBenchmark
Limits of actual implementation
-------------------------------
With intention, in this moment the project supports only:
- Mercator tiles (a la OpenStreetMap) and Geodetic tiles (WGS84 unprojected)
with known and described tiling scheme.
- All tiles must be 256x256 pixels.
- We enforce and require XYZ (top-left origin) tiling schema (even for TMS).
Password protection
-------------------
HTTP Simple Authentication can be easily added to the server.
Edit the .htaccess and add these lines:
AuthUserFile /full/path/to/.htpasswd
AuthType Basic
AuthName "Secure WMTS"
Require valid-user
Create a file called .htpasswd with user:password format.
You can use a command-line utility:
$ htpasswd -c .htpasswd [your-user-login]
Or an online service:
http://www.htaccesstools.com/htpasswd-generator/
HTTPS / SSL support
-------------------
TileServer.php can run without any problems over HTTPS, if required.
Microsoft Windows web-hosting
-----------------------------
The TileServer.php should run on Windows-powered webservers with Apache
installation if PHP 5.2+ and mod_rewrite are available.
With the IIS webserver hosting, you may need PHP and IIRF module
(http://iirf.codeplex.com/) and alter appropriately the rewrite rules.
Credits / Contributors
----------------------
Project developed initially by Klokan Technologies GmbH, Switzerland in
cooperation with National Oceanic and Atmospheric Administration - NOAA, USA.
Petr Pridal - Klokan Technologies GmbH <petr.pridal@klokantech.com>
Jason Woolard - NOAA <jason.woolard@noaa.gov>
Jon Sellars - NOAA <jon.sellars@noaa.gov>
Tested WMTS/TMS clients
-----------------------
- QuantumGIS Desktop 1.9+ - open with Layer->Add WMS layer
http://www.qgis.org/
- ESRI ArcGIS Desktop 10.1+ - native WMTS implementation supported
http://www.esri.com/software/arcgis/arcgis-for-desktop
- ArcBruTiles plugin for ArcGIS 9.3+ - via TMS endpoint
http://arcbrutile.codeplex.com/
- OpenLayers WMTS Layer - including parsing GetCapabilities
http://www.openlayers.org/
- GAIA - native WMTS (issues with 3857 to be fixed)
http://www.thecarbonproject.com/gaia.php
- MapBox.js - the loading of maps via TileJSON
BSD License
-----------
Copyright (C) 2012 Klokan Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

123
tileserver-mbtiles.php Normal file
View File

@@ -0,0 +1,123 @@
<?php
// Based on: https://github.com/Zverik/mbtiles-php
// Read: https://github.com/klokantech/tileserver-php/issues/1
// TODO: clean the code!!!
if (!is_file($_GET['tileset'])) {
header('HTTP/1.0 404 Not Found');
echo "<h1>404 Not Found</h1>";
echo "TileServer.php could not found what you requested.";
die();
// TODO: if ($_GET['ext'] == 'png') { ...
// TODO: better image 256x256px !!!
// header("Content-type: image/png");
//print("\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x06\x00\x00\x00\x1f\x15\xc4\x89\x00\x00\x00\rIDAT\x08\xd7c````\x00\x00\x00\x05\x00\x01^\xf3*:\x00\x00\x00\x00IEND\xaeB`\x82");
}
$tileset = $_GET['tileset'];
if (isset($_GET['tileset'])) {
$tileset = $_GET['tileset'];
$flip = true;
try {
$db = new PDO('sqlite:' . $tileset, '', '', array(PDO::ATTR_PERSISTENT => true));
if (!isset($db)) {
header('Content-type: text/plain');
print 'Incorrect tileset name: ' . $_GET['tileset'];
exit;
}
// http://c.tile.openstreetmap.org/12/2392/1190.png
$z = floatval($_GET['z']);
$y = floatval($_GET['y']);
$x = floatval($_GET['x']);
if ($flip) {
$y = pow(2, $z) - 1 - $y;
}
if ($_GET['ext'] != 'grid' && $_GET['ext'] != 'json') {
$result = $db->query('select tile_data as t from tiles where zoom_level=' . $z . ' and tile_column=' . $x . ' and tile_row=' . $y);
$data = $result->fetchColumn();
if (!isset($data) || $data === FALSE) {
// TODO: Put here ready to use empty tile!!!
$png = imagecreatetruecolor(256, 256);
imagesavealpha($png, true);
$trans_colour = imagecolorallocatealpha($png, 0, 0, 0, 127);
imagefill($png, 0, 0, $trans_colour);
header('Content-type: image/png');
imagepng($png);
//header($_SERVER['SERVER_PROTOCOL'].' 404 Not Found');
} else {
$result = $db->query('select value from metadata where name="format"');
$resultdata = $result->fetchColumn();
$format = isset($resultdata) && $resultdata !== FALSE ? $resultdata : 'png';
if ($format == 'jpg')
$format = 'jpeg';
header('Content-type: image/' . $format);
print $data;
}
} elseif ($_GET['ext'] == 'grid' || $_GET['ext'] == 'json') {
//Get and return UTFgrid
$result = $db->query('SELECT grid FROM grids WHERE tile_column = ' . $x . ' AND tile_row = ' . $y . ' AND zoom_level = ' . $z);
$data = $result->fetchColumn();
if (!isset($data) || $data === FALSE) {
// if not exists grid data return empty json
header('Access-Control-Allow-Origin: *');
echo 'grid({});';
die;
} else {
$grid = gzuncompress($data);
$grid = substr(trim($grid), 0, -1);
//adds legend (data) to output
$grid .= ',"data":{';
$result = $db->query('SELECT key_name as key, key_json as json FROM grid_data WHERE zoom_level=' . $z . ' and tile_column=' . $x . ' and tile_row=' . $y);
while ($r = $result->fetch(PDO::FETCH_ASSOC)) {
$grid .= '"' . $r['key'] . '":' . $r['json'] . ',';
}
$grid = rtrim($grid, ',') . '}}';
// CORS headers
header('Access-Control-Allow-Origin: *');
if (isset($_GET['callback'])) {
header("Content-Type:text/javascript charset=utf-8");
echo $_GET['callback'] . '(' . $grid . ');';
} else {
header("Content-Type:text/javascript; charset=utf-8");
echo 'grid(' . $grid . ');';
}
}
}
} catch (PDOException $e) {
header('Content-type: text/plain');
print 'Error querying the database: ' . $e->getMessage();
}
}
/*
function getbaseurl() {
return 'http://'.$_SERVER['HTTP_HOST'].preg_replace('/\/(1.0.0\/)?[^\/]*$/','/',$_SERVER['REQUEST_URI']);
}
*/
function readparams($db) {
$params = array();
$result = $db->query('select name, value from metadata');
while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
$params[$row['name']] = $row['value'];
}
return $params;
}
function readzooms($db) {
$zooms = array();
$result = $db->query('select zoom_level from tiles group by zoom_level order by zoom_level');
while ($zoom = $result->fetchColumn()) {
$zooms[] = $zoom;
}
return $zooms;
}
?>

85
tileserver-tms.php Normal file
View File

@@ -0,0 +1,85 @@
<?php
/*
* TileServer.php project
* ======================
* https://github.com/klokantech/tileserver-php/
* Copyright (C) 2012 - Klokan Technologies GmbH
*/
require "tileserver.php";
header("Content-type: application/xml");
echo "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";
// Accepted GET strings
$layer = (array_key_exists('layer', $_GET)) ? $_GET['layer'] : "";
# -----------
# TMS SERVICE
# -----------
if ($layer === ""):
$maps = maps();
?>
<TileMapService version="1.0.0">
<TileMaps>
<?php
foreach ($maps as $m) {
$basename = $m['basename'];
$title = (array_key_exists('name', $m )) ? $m['name'] : $basename;
$profile = $m['profile'];
if ($profile == 'geodetic')
$srs = "EPSG:4326";
else
$srs = "EPSG:3857";
echo " <TileMap title=\"$title\" srs=\"$srs\" type=\"InvertedTMS\" profile=\"global-$profile\" href=\"$baseUrl$basename/tms\" />\n";
}
?>
</TileMaps>
</TileMapService>
<?php
die;
# ---------
# TMS LAYER
# ---------
else:
$m = layer($layer);
$basename = $m['basename'];
$title = (array_key_exists('name', $m )) ? $m['name'] : $basename;
$description = (array_key_exists('description', $m )) ? $m['description'] : "";
$bounds = $m['bounds'];
$profile = $m['profile'];
if ($profile == 'geodetic') {
$srs = "EPSG:4326";
$originx = -180.0;
$originy = -90.0;
$initialResolution = 0.703125;
}
else {
$srs = "EPSG:3857";
$originx = -20037508.342789;
$originy = -20037508.342789;
list( $minx, $miny ) = $mercator->LatLonToMeters($bounds[1], $bounds[0]);
list( $maxx, $maxy ) = $mercator->LatLonToMeters($bounds[3], $bounds[2]);
$bounds = array( $minx, $miny, $maxx, $maxy );
$initialResolution = 156543.03392804062;
}
$format = $m['format'];
$mime = ($format == 'jpg') ? 'image/jpeg' : 'image/png';
?>
<TileMap version="1.0.0" tilemapservice="<?php echo $baseUrl.$basename ?>" type="InvertedTMS">
<Title><?php echo htmlspecialchars($title) ?></Title>
<Abstract><?php echo htmlspecialchars($description) ?></Abstract>
<SRS><?php echo $srs ?></SRS>
<BoundingBox minx="<?php echo $bounds[0] ?>" miny="<?php echo $bounds[1] ?>" maxx="<?php echo $bounds[2] ?>" maxy="<?php echo $bounds[3] ?>" />
<Origin x="<?php echo $originx ?>" y="<?php echo $originy ?>"/>
<TileFormat width="256" height="256" mime-type="<?php echo $mime ?>" extension="<?php echo $format ?>"/>
<TileSets profile="global-<?php echo $profile ?>">
<?php for ($zoom = $m['minzoom']; $zoom < $m['maxzoom']+1; $zoom++ ) { ?>
<TileSet href="<?php echo $baseUrl.$basename.'/'.$zoom ?>" units-per-pixel="<?php echo $initialResolution / pow(2, $zoom) ?>" order="<?php echo $zoom ?>" />
<?php } ?>
</TileSets>
</TileMap>
<?php
endif; ?>

472
tileserver-wmts.php Normal file
View File

@@ -0,0 +1,472 @@
<?php
/*
* TileServer.php project
* ======================
* https://github.com/klokantech/tileserver-php/
* Copyright (C) 2012 - Klokan Technologies GmbH
*/
require "tileserver.php";
$maps = maps();
header("Content-type: application/xml");
echo "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"; ?>
<Capabilities xmlns="http://www.opengis.net/wmts/1.0" xmlns:ows="http://www.opengis.net/ows/1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:gml="http://www.opengis.net/gml" xsi:schemaLocation="http://www.opengis.net/wmts/1.0 http://schemas.opengis.net/wmts/1.0/wmtsGetCapabilities_response.xsd" version="1.0.0">
<!-- Service Identification -->
<ows:ServiceIdentification>
<ows:Title><?php echo $config['serverTitle'] ?></ows:Title>
<ows:ServiceType>OGC WMTS</ows:ServiceType>
<ows:ServiceTypeVersion>1.0.0</ows:ServiceTypeVersion>
</ows:ServiceIdentification>
<!-- Operations Metadata -->
<ows:OperationsMetadata>
<ows:Operation name="GetCapabilities">
<ows:DCP>
<ows:HTTP>
<ows:Get xlink:href="<?php echo $baseUrl ?>wmts/1.0.0/WMTSCapabilities.xml">
<ows:Constraint name="GetEncoding">
<ows:AllowedValues>
<ows:Value>RESTful</ows:Value>
</ows:AllowedValues>
</ows:Constraint>
</ows:Get>
<!-- add KVP binding in 10.1 -->
<ows:Get xlink:href="<?php echo $baseUrl ?>wmts?">
<ows:Constraint name="GetEncoding">
<ows:AllowedValues>
<ows:Value>KVP</ows:Value>
</ows:AllowedValues>
</ows:Constraint>
</ows:Get>
</ows:HTTP>
</ows:DCP>
</ows:Operation>
<ows:Operation name="GetTile">
<ows:DCP>
<ows:HTTP>
<ows:Get xlink:href="<?php echo $baseUrl ?>wmts/">
<ows:Constraint name="GetEncoding">
<ows:AllowedValues>
<ows:Value>RESTful</ows:Value>
</ows:AllowedValues>
</ows:Constraint>
</ows:Get>
<ows:Get xlink:href="<?php echo $baseUrl ?>wmts?">
<ows:Constraint name="GetEncoding">
<ows:AllowedValues>
<ows:Value>KVP</ows:Value>
</ows:AllowedValues>
</ows:Constraint>
</ows:Get>
</ows:HTTP>
</ows:DCP>
</ows:Operation>
</ows:OperationsMetadata>
<Contents>
<!--Layer-->
<?php
foreach ($maps as $m) {
$basename = $m['basename'];
$title = (array_key_exists('name', $m )) ? $m['name'] : $basename;
$profile = $m['profile'];
$bounds = $m['bounds'];
$format = $m['format'];
$mime = ($format == 'jpg') ? 'image/jpeg' : 'image/png';
if ($profile == 'geodetic') {
$tileMatrixSet = "WGS84";
} else {
$tileMatrixSet = "GoogleMapsCompatible";
list( $minx, $miny ) = $mercator->LatLonToMeters($bounds[1], $bounds[0]);
list( $maxx, $maxy ) = $mercator->LatLonToMeters($bounds[3], $bounds[2]);
$bounds3857 = array( $minx, $miny, $maxx, $maxy );
}
?>
<Layer>
<ows:Title><?php echo $title ?></ows:Title>
<ows:Identifier><?php echo $basename ?></ows:Identifier>
<?php /* GAIA client does not like it
if ($profile == 'mercator') { ?>
<ows:BoundingBox crs="urn:ogc:def:crs:EPSG::102100">
<ows:LowerCorner><?php echo $bounds3857[1], ' ', $bounds3857[0] ?></ows:LowerCorner>
<ows:UpperCorner><?php echo $bounds3857[3], ' ', $bounds3857[2] ?></ows:UpperCorner>
</ows:BoundingBox>
<?php } */ ?>
<ows:WGS84BoundingBox crs="urn:ogc:def:crs:OGC:2:84">
<ows:LowerCorner><?php echo $bounds[0], ' ', $bounds[1] ?></ows:LowerCorner>
<ows:UpperCorner><?php echo $bounds[2], ' ', $bounds[3] ?></ows:UpperCorner>
</ows:WGS84BoundingBox>
<Style isDefault="true">
<ows:Identifier>default</ows:Identifier>
</Style>
<Format><?php echo $mime ?></Format>
<TileMatrixSetLink>
<TileMatrixSet><?php echo $tileMatrixSet ?></TileMatrixSet>
</TileMatrixSetLink>
<ResourceURL format="<?php echo $mime ?>" resourceType="tile" template="<?php echo $baseUrl ?><?php echo $basename ?>/{TileMatrixSet}/{TileMatrix}/{TileCol}/{TileRow}.<?php echo $format ?>"/>
</Layer>
<?php
}
?>
<!--TileMatrixSet-->
<TileMatrixSet>
<ows:Title>GoogleMapsCompatible</ows:Title>
<ows:Abstract>the wellknown 'GoogleMapsCompatible' tile matrix set defined by OGC WMTS specification</ows:Abstract>
<ows:Identifier>GoogleMapsCompatible</ows:Identifier>
<ows:SupportedCRS>urn:ogc:def:crs:EPSG:6.18:3:3857</ows:SupportedCRS>
<WellKnownScaleSet>urn:ogc:def:wkss:OGC:1.0:GoogleMapsCompatible</WellKnownScaleSet>
<TileMatrix>
<ows:Identifier>0</ows:Identifier>
<ScaleDenominator>559082264.0287178</ScaleDenominator>
<TopLeftCorner>-20037508.34278925 20037508.34278925</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>1</MatrixWidth>
<MatrixHeight>1</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>1</ows:Identifier>
<ScaleDenominator>279541132.0143589</ScaleDenominator>
<TopLeftCorner>-20037508.34278925 20037508.34278925</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>2</MatrixWidth>
<MatrixHeight>2</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>2</ows:Identifier>
<ScaleDenominator>139770566.0071794</ScaleDenominator>
<TopLeftCorner>-20037508.34278925 20037508.34278925</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>4</MatrixWidth>
<MatrixHeight>4</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>3</ows:Identifier>
<ScaleDenominator>69885283.00358972</ScaleDenominator>
<TopLeftCorner>-20037508.34278925 20037508.34278925</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>8</MatrixWidth>
<MatrixHeight>8</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>4</ows:Identifier>
<ScaleDenominator>34942641.50179486</ScaleDenominator>
<TopLeftCorner>-20037508.34278925 20037508.34278925</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>16</MatrixWidth>
<MatrixHeight>16</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>5</ows:Identifier>
<ScaleDenominator>17471320.75089743</ScaleDenominator>
<TopLeftCorner>-20037508.34278925 20037508.34278925</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>32</MatrixWidth>
<MatrixHeight>32</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>6</ows:Identifier>
<ScaleDenominator>8735660.375448715</ScaleDenominator>
<TopLeftCorner>-20037508.34278925 20037508.34278925</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>64</MatrixWidth>
<MatrixHeight>64</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>7</ows:Identifier>
<ScaleDenominator>4367830.187724357</ScaleDenominator>
<TopLeftCorner>-20037508.34278925 20037508.34278925</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>128</MatrixWidth>
<MatrixHeight>128</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>8</ows:Identifier>
<ScaleDenominator>2183915.093862179</ScaleDenominator>
<TopLeftCorner>-20037508.34278925 20037508.34278925</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>256</MatrixWidth>
<MatrixHeight>256</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>9</ows:Identifier>
<ScaleDenominator>1091957.546931089</ScaleDenominator>
<TopLeftCorner>-20037508.34278925 20037508.34278925</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>512</MatrixWidth>
<MatrixHeight>512</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>10</ows:Identifier>
<ScaleDenominator>545978.7734655447</ScaleDenominator>
<TopLeftCorner>-20037508.34278925 20037508.34278925</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>1024</MatrixWidth>
<MatrixHeight>1024</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>11</ows:Identifier>
<ScaleDenominator>272989.3867327723</ScaleDenominator>
<TopLeftCorner>-20037508.34278925 20037508.34278925</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>2048</MatrixWidth>
<MatrixHeight>2048</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>12</ows:Identifier>
<ScaleDenominator>136494.6933663862</ScaleDenominator>
<TopLeftCorner>-20037508.34278925 20037508.34278925</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>4096</MatrixWidth>
<MatrixHeight>4096</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>13</ows:Identifier>
<ScaleDenominator>68247.34668319309</ScaleDenominator>
<TopLeftCorner>-20037508.34278925 20037508.34278925</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>8192</MatrixWidth>
<MatrixHeight>8192</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>14</ows:Identifier>
<ScaleDenominator>34123.67334159654</ScaleDenominator>
<TopLeftCorner>-20037508.34278925 20037508.34278925</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>16384</MatrixWidth>
<MatrixHeight>16384</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>15</ows:Identifier>
<ScaleDenominator>17061.83667079827</ScaleDenominator>
<TopLeftCorner>-20037508.34278925 20037508.34278925</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>32768</MatrixWidth>
<MatrixHeight>32768</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>16</ows:Identifier>
<ScaleDenominator>8530.918335399136</ScaleDenominator>
<TopLeftCorner>-20037508.34278925 20037508.34278925</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>65536</MatrixWidth>
<MatrixHeight>65536</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>17</ows:Identifier>
<ScaleDenominator>4265.459167699568</ScaleDenominator>
<TopLeftCorner>-20037508.34278925 20037508.34278925</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>131072</MatrixWidth>
<MatrixHeight>131072</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>18</ows:Identifier>
<ScaleDenominator>2132.729583849784</ScaleDenominator>
<TopLeftCorner>-20037508.34278925 20037508.34278925</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>262144</MatrixWidth>
<MatrixHeight>262144</MatrixHeight>
</TileMatrix>
</TileMatrixSet>
<TileMatrixSet>
<ows:Identifier>WGS84</ows:Identifier>
<ows:Title>GoogleCRS84Quad</ows:Title>
<ows:SupportedCRS>urn:ogc:def:crs:EPSG:6.3:4326</ows:SupportedCRS>
<ows:BoundingBox crs="urn:ogc:def:crs:EPSG:6.3:4326">
<LowerCorner>-180.000000 -90.000000</LowerCorner>
<UpperCorner>180.000000 90.000000</UpperCorner>
</ows:BoundingBox>
<WellKnownScaleSet>urn:ogc:def:wkss:OGC:1.0:GoogleCRS84Quad</WellKnownScaleSet>
<TileMatrix>
<ows:Identifier>0</ows:Identifier>
<ScaleDenominator>279541132.01435887813568115234</ScaleDenominator>
<TopLeftCorner>90.000000 -180.000000</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>2</MatrixWidth>
<MatrixHeight>1</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>1</ows:Identifier>
<ScaleDenominator>139770566.00717943906784057617</ScaleDenominator>
<TopLeftCorner>90.000000 -180.000000</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>4</MatrixWidth>
<MatrixHeight>2</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>2</ows:Identifier>
<ScaleDenominator>69885283.00358971953392028809</ScaleDenominator>
<TopLeftCorner>90.000000 -180.000000</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>8</MatrixWidth>
<MatrixHeight>4</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>3</ows:Identifier>
<ScaleDenominator>34942641.50179485976696014404</ScaleDenominator>
<TopLeftCorner>90.000000 -180.000000</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>16</MatrixWidth>
<MatrixHeight>8</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>4</ows:Identifier>
<ScaleDenominator>17471320.75089742988348007202</ScaleDenominator>
<TopLeftCorner>90.000000 -180.000000</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>32</MatrixWidth>
<MatrixHeight>16</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>5</ows:Identifier>
<ScaleDenominator>8735660.37544871494174003601</ScaleDenominator>
<TopLeftCorner>90.000000 -180.000000</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>64</MatrixWidth>
<MatrixHeight>32</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>6</ows:Identifier>
<ScaleDenominator>4367830.18772435747087001801</ScaleDenominator>
<TopLeftCorner>90.000000 -180.000000</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>128</MatrixWidth>
<MatrixHeight>64</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>7</ows:Identifier>
<ScaleDenominator>2183915.09386217873543500900</ScaleDenominator>
<TopLeftCorner>90.000000 -180.000000</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>256</MatrixWidth>
<MatrixHeight>128</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>8</ows:Identifier>
<ScaleDenominator>1091957.54693108936771750450</ScaleDenominator>
<TopLeftCorner>90.000000 -180.000000</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>512</MatrixWidth>
<MatrixHeight>256</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>9</ows:Identifier>
<ScaleDenominator>545978.77346554468385875225</ScaleDenominator>
<TopLeftCorner>90.000000 -180.000000</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>1024</MatrixWidth>
<MatrixHeight>512</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>10</ows:Identifier>
<ScaleDenominator>272989.38673277234192937613</ScaleDenominator>
<TopLeftCorner>90.000000 -180.000000</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>2048</MatrixWidth>
<MatrixHeight>1024</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>11</ows:Identifier>
<ScaleDenominator>136494.69336638617096468806</ScaleDenominator>
<TopLeftCorner>90.000000 -180.000000</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>4096</MatrixWidth>
<MatrixHeight>2048</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>12</ows:Identifier>
<ScaleDenominator>68247.34668319308548234403</ScaleDenominator>
<TopLeftCorner>90.000000 -180.000000</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>8192</MatrixWidth>
<MatrixHeight>4096</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>13</ows:Identifier>
<ScaleDenominator>34123.67334159654274117202</ScaleDenominator>
<TopLeftCorner>90.000000 -180.000000</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>16384</MatrixWidth>
<MatrixHeight>8192</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>14</ows:Identifier>
<ScaleDenominator>17061.83667079825318069197</ScaleDenominator>
<TopLeftCorner>90.000000 -180.000000</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>32768</MatrixWidth>
<MatrixHeight>16384</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>15</ows:Identifier>
<ScaleDenominator>8530.91833539912659034599</ScaleDenominator>
<TopLeftCorner>90.000000 -180.000000</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>65536</MatrixWidth>
<MatrixHeight>32768</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>16</ows:Identifier>
<ScaleDenominator>4265.45916769956329517299</ScaleDenominator>
<TopLeftCorner>90.000000 -180.000000</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>131072</MatrixWidth>
<MatrixHeight>65536</MatrixHeight>
</TileMatrix>
<TileMatrix>
<ows:Identifier>17</ows:Identifier>
<ScaleDenominator>2132.72958384978574031265</ScaleDenominator>
<TopLeftCorner>90.000000 -180.000000</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>262144</MatrixWidth>
<MatrixHeight>131072</MatrixHeight>
</TileMatrix>
</TileMatrixSet>
</Contents>
<ServiceMetadataURL xlink:href="<?php echo $baseUrl ?>wmts/1.0.0/WMTSCapabilities.xml"/>
</Capabilities>

415
tileserver.php Normal file
View File

@@ -0,0 +1,415 @@
<?php
/*
* TileServer.php project
* ======================
* https://github.com/klokantech/tileserver-php/
* Copyright (C) 2012 - Klokan Technologies GmbH
*/
# Set you own config values here:
$config = array(
"baseUrls" => array("http://localhost/"),
"serverTitle" => "TileServer.php v0.1",
);
# PHP debugging
ini_set("error_reporting", "true");
error_reporting(E_ALL|E_STRCT);
# Global variable + ccepted GET / POST variables from the outside world
$baseUrl = $config['baseUrls'][0];
// TODO: We can detext the baseUrl as well - for defined requests
$service = (array_key_exists('service', $_GET)) ? $_GET['service'] : "";
$layer = (array_key_exists('layer', $_GET)) ? $_GET['layer'] : "";
$callback = (array_key_exists('callback', $_GET)) ? $_GET['callback'] : "";
# CORS header
header('Access-Control-Allow-Origin: *');
# ------------
# TEST SERVICE
# ------------
if ($service == 'test') {
header("Content-Type: text/plain; charset=utf-8");
echo "TileServer.php (", $config['serverTitle'], ') at ', $baseUrl;
die();
}
# ------------
# HTML SERVICE
# ------------
if ($service == 'html'):
$maps = maps();
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title><?php echo $config['serverTitle'] ?></title>
<link rel="stylesheet" type="text/css" href="http://maptilercdn.s3.amazonaws.com/tileserver.css" />
<script src="http://maptilercdn.s3.amazonaws.com/tileserver.js"></script>
<body>
<script>tileserver(null,'<?php echo $baseUrl ?>tms','<?php echo $baseUrl ?>wmts');</script>
<h1>Welcome to <?php echo $config['serverTitle'] ?></h1>
<p>
This server distributes maps to desktop, web, and mobile applications.
</p>
<p>
The mapping data are available as OpenGIS Web Map Tiling Service (OGC WMTS), OSGEO Tile Map Service (TMS), and popular XYZ urls described with TileJSON metadata.
</p>
<?php
# Test if the config value for baseUrls is set correctly
if ((strpos($baseUrl, $_SERVER['HTTP_HOST']) === false) or ($baseUrl[strlen($baseUrl)-1] !== '/')) {
// TODO: Make a test with ?service=test and suggest the value - or is there any way how to get URL for actual directory with mod_rewrite in use ?
?>
<h3 style="color:darkred;">Wrong configuration of the "BaseURL" in tileserver.php</h3>
<p style="color:darkred; font-style: italic;">
Please modify 'tileserver.php' file and replace the '<?php echo $baseUrl ?>' in the $config variable with '<?php echo selfUrl(); ?>' (with slash in the end) or other correct address to your server. Multiple CNAME can be used for better performance - more in <a href="readme.md">readme.txt</a>.
</p>
<?php }
# Are there some maps on the server?
if (count($maps) == 0) { ?>
<h3 style="color:darkred;">No maps available yet</h3>
<p style="color:darkred; font-style: italic;">
Ready to go - just upload some maps into directory: <?php echo getcwd(); ?>/ on this server.
</p>
<p>
Note: The maps can be a directory with tiles in XYZ format with metadata.json file.<br/>
You can easily convert existing geodata (GeoTIFF, ECW, MrSID, etc) to this tile structure with <a href="http://www.maptiler.com">MapTiler Cluster</a> or open-source projects such as <a href="http://www.klokan.cz/projects/gdal2tiles/">GDAL2Tiles</a> or <a href="http://www.maptiler.org/">MapTiler</a> or simply upload any maps in MBTiles format made by <a href="http://www.tilemill.com/">TileMill</a>. Helpful is also the <a href="https://github.com/mapbox/mbutil">mbutil</a> tool. Serving directly from .mbtiles files is supported, but with decreased performance.
</p>
<?php
# Print the available maps
} else {
// print_r($maps);
echo "<h3>Available maps</h3>";
echo "<ul>";
foreach ($maps as $map) {
// echo "<li><a href=\"".$map['basename']."\">".$map['name']."</a>" ;
echo "<li>".$map['name'];
}
echo "</ul>";
}
?>
</body>
</html>
<?php
die;
endif;
# ------------
# JSON SERVICE
# ------------
if ($service == 'json'):
header("Content-Type:application/json; charset=utf-8");
if ($layer) {
$output = metadataTileJson(layer($layer));
} else {
$maps = maps();
$tilejsons = array();
foreach ($maps as $map)
$tilejsons[] = metadataTileJson($map);
$output = $tilejsons;
}
$output = json_encode($output);
$output = str_replace("\\/","/",$output);
if ($callback) echo "$callback($output);";
else echo $output;
die;
endif;
# INTERNAL FUNCTIONS:
function maps() {
$maps = array();
# Scan all directories with metadata.json
$mjs = glob('*/metadata.json');
if ($mjs) foreach ($mjs as $mj) $maps[] = metadataFromMetadataJson($mj);
# Scan all mbtiles
$mbts = glob('*.mbtiles');
if ($mbts) foreach ($mbts as $mbt) $maps[] = metadataFromMbtiles($mbt);
return $maps;
}
function layer( $layer ) {
if (strpos($layer, '.mbtiles') === false)
return metadataFromMetadataJson($layer.'/metadata.json');
else
return metadataFromMbtiles($layer);
}
function metadataFromMetadataJson( $jsonFileName ) {
$metadata = json_decode( file_get_contents($jsonFileName), true );
$metadata = metadataValidation($metadata);
$metadata['basename'] = str_replace('/metadata.json', '', $jsonFileName);
return $metadata;
}
function metadataFromMbtiles( $mbt ) {
$metadata = array();
$db = new PDO('sqlite:'.$mbt,'','',array(PDO::ATTR_PERSISTENT => true));
if (isset($db)) {
$result = $db->query('select * from metadata');
$resultdata = $result->fetchAll();
foreach ($resultdata as $r) {
$metadata[$r['name']] = $r['value'];
}
$metadata = metadataValidation($metadata);
$metadata['basename'] = $mbt;
}
return $metadata;
}
function metadataValidation( $metadata ) {
if (array_key_exists('bounds', $metadata )) {
// TODO: Calculate bounds from tiles if bounds is missing - with GlobalMercator
$metadata['bounds'] = array_map( 'floatval', explode(',', $metadata['bounds'] ));
}
if (!array_key_exists('profile', $metadata )) {
$metadata['profile'] = 'mercator';
}
// TODO: detect format, minzoom, maxzoom, thumb
// scandir() for directory / SQL for mbtiles
if (array_key_exists('minzoom', $metadata ))
$metadata['minzoom'] = intval( $metadata['minzoom'] );
else
$metadata['minzoom'] = 0;
if (array_key_exists('maxzoom', $metadata ))
$metadata['maxzoom'] = intval( $metadata['maxzoom'] );
else
$metadata['maxzoom'] = 18;
if (!array_key_exists('format', $metadata )) {
$metadata['format'] = 'png';
}
/*
if (!array_key_exists('thumb', $metadata )) {
$metadata['profile'] = 'mercator';
}
*/
return $metadata;
}
function metadataTileJson( $metadata ) {
global $config;
$metadata['tilejson'] = '2.0.0';
$metadata['scheme'] = 'xyz';
$tiles = array();
foreach($config['baseUrls'] as $url)
$tiles[] = $url.$metadata['basename'].'/{z}/{x}/{y}.'.$metadata['format'];
#print_r($tiles);
$metadata['tiles'] = $tiles;
return $metadata;
}
function selfUrl( $serverOnly = false ) {
if(!isset($_SERVER['REQUEST_URI'])){
$serverrequri = $_SERVER['PHP_SELF'];
}else{
$serverrequri = $_SERVER['REQUEST_URI'];
}
$s = empty($_SERVER["HTTPS"]) ? '' : ($_SERVER["HTTPS"] == "on") ? "s" : "";
$port = ($_SERVER["SERVER_PORT"] == "80") ? "" : (":".$_SERVER["SERVER_PORT"]);
if ($serverOnly) return 'http'.$s.'://'.$_SERVER['SERVER_NAME'].$port."/";
return 'http'.$s.'://'.$_SERVER['SERVER_NAME'].$port.$serverrequri;
}
function doConditionalGet($timestamp) {
$last_modified = gmdate('D, d M Y H:i:s \G\M\T', $timestamp);
$etag = '"'.md5($last_modified).'"';
// Send the headers
header("Last-Modified: $last_modified");
header("ETag: $etag");
// See if the client has provided the required headers
$if_modified_since = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ?
stripslashes($_SERVER['HTTP_IF_MODIFIED_SINCE']) :
false;
$if_none_match = isset($_SERVER['HTTP_IF_NONE_MATCH']) ?
stripslashes($_SERVER['HTTP_IF_NONE_MATCH']) :
false;
if (!$if_modified_since && !$if_none_match) {
return;
}
// At least one of the headers is there - check them
if ($if_none_match && $if_none_match != $etag) {
return; // etag is there but doesn't match
}
if ($if_modified_since && $if_modified_since != $last_modified) {
return; // if-modified-since is there but doesn't match
}
// Nothing has changed since their last request - serve a 304 and exit
header('HTTP/1.0 304 Not Modified');
exit;
}
/*
TODO: https://github.com/klokantech/tileserver-php/issues/2
function geoToMercTile($lon, $lat, $zoom) {
$xtile = floor((($lon + 180) / 360) * pow(2, $zoom));
$ytile = floor((1 - log(tan(deg2rad($lat)) + 1 / cos(deg2rad($lat))) / pi()) /2 * pow(2, $zoom));
return array($xtile, $ytile);
}
function geoToGeoTile($lon, $lat, $zoom) {
$res = 180 / 256.0 / pow(2, $zoom);
$xtile = floor( ceil( (180 + $lat) / $res / 256.0 ) - 1 );
$ytile = floor( ceil( (90 + $lon) / $res / 256.0 ) - 1 );
return array($xtile, $ytile);
}
function tileRange($bounds, $minzoom, $maxzoom) {
for ($z=$minzoom; $z < $maxzoom+1; $z++) {
print "$z\n";
list($minx, $miny) = geoToMercTile($bounds[0], $bounds[1], $z);
list($maxx, $maxy) = geoToMercTile($bounds[2], $bounds[3], $z);
print_r( array($minx, $miny, $maxx, $maxy) );
}
}
// Better use the port of Klokans' GlobalMapTiles:
*/
/*
GlobalMapTiles - part of Aggregate Map Tools
Version 1.0
Copyright (c) 2009 The Bivings Group
All rights reserved.
Author: John Bafford
http://www.bivings.com/
http://bafford.com/softare/aggregate-map-tools/
Based on GDAL2Tiles / globalmaptiles.py
Original python version Copyright (c) 2008 Klokan Petr Pridal. All rights reserved.
http://www.klokan.cz/projects/gdal2tiles/
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
class GlobalMercator
{
var $tileSize;
var $initialResolution;
var $originShift;
//Initialize the TMS Global Mercator pyramid
function __construct($tileSize = 256)
{
$this->tileSize = $tileSize;
$this->initialResolution = 2 * M_PI * 6378137 / $this->tileSize;
# 156543.03392804062 for tileSize 256 Pixels
$this->originShift = 2 * M_PI * 6378137 / 2.0;
# 20037508.342789244
}
//Converts given lat/lon in WGS84 Datum to XY in Spherical Mercator EPSG:900913
function LatLonToMeters($lat, $lon)
{
$mx = $lon * $this->originShift / 180.0;
$my = log( tan((90 + $lat) * M_PI / 360.0 )) / (M_PI / 180.0);
$my *= $this->originShift / 180.0;
return array($mx, $my);
}
//Converts XY point from Spherical Mercator EPSG:900913 to lat/lon in WGS84 Datum
function MetersToLatLon($mx, $my)
{
$lon = ($mx / $this->originShift) * 180.0;
$lat = ($my / $this->originShift) * 180.0;
$lat = 180 / M_PI * (2 * atan( exp( $lat * M_PI / 180.0)) - M_PI / 2.0);
return array($lat, $lon);
}
//Converts pixel coordinates in given zoom level of pyramid to EPSG:900913
function PixelsToMeters($px, $py, $zoom)
{
$res = $this->Resolution($zoom);
$mx = $px * $res - $this->originShift;
$my = $py * $res - $this->originShift;
return array($mx, $my);
}
//Converts EPSG:900913 to pyramid pixel coordinates in given zoom level
function MetersToPixels($mx, $my, $zoom)
{
$res = $this->Resolution( $zoom );
$px = ($mx + $this->originShift) / $res;
$py = ($my + $this->originShift) / $res;
return array($px, $py);
}
//Returns a tile covering region in given pixel coordinates
function PixelsToTile($px, $py)
{
$tx = ceil( $px / $this->tileSize ) - 1;
$ty = ceil( $py / $this->tileSize ) - 1;
return array($tx, $ty);
}
//Returns tile for given mercator coordinates
function MetersToTile($mx, $my, $zoom)
{
list($px, $py) = $this->MetersToPixels($mx, $my, $zoom);
return $this->PixelsToTile($px, $py);
}
//Returns bounds of the given tile in EPSG:900913 coordinates
function TileBounds($tx, $ty, $zoom)
{
list($minx, $miny) = $this->PixelsToMeters( $tx*$this->tileSize, $ty*$this->tileSize, $zoom );
list($maxx, $maxy) = $this->PixelsToMeters( ($tx+1)*$this->tileSize, ($ty+1)*$this->tileSize, $zoom );
return array($minx, $miny, $maxx, $maxy);
}
//Returns bounds of the given tile in latutude/longitude using WGS84 datum
function TileLatLonBounds($tx, $ty, $zoom)
{
$bounds = $this->TileBounds($tx, $ty, $zoom);
list($minLat, $minLon) = $this->MetersToLatLon($bounds[0], $bounds[1]);
list($maxLat, $maxLon) = $this->MetersToLatLon($bounds[2], $bounds[3]);
return array($minLat, $minLon, $maxLat, $maxLon);
}
//Resolution (meters/pixel) for given zoom level (measured at Equator)
function Resolution($zoom)
{
return $this->initialResolution / (1 << $zoom);
}
}
$mercator = new GlobalMercator();
?>