1
0
mirror of https://github.com/klokantech/tileserver-php.git synced 2025-08-21 21:56:18 +02:00

54 Commits

Author SHA1 Message Date
mheranco
4fe14e6164 Fix security issues - thanks @mheranco (#166)
* Fix XSS

* Check if requested tile resides inside the current directory

* Add input validation for "renderTile"

* Use floatval instead of "int" for casting
2025-07-14 17:08:39 +02:00
Jiří Komárek
d0fdeaec69 grammar check 2022-07-30 02:03:50 +02:00
Dalibor Janák
cce369b3f6 Merge pull request #162 from david-eagles/master
Modified WMTS Resource URL generation to support hostnames that contain the layer name.
2021-09-24 10:55:02 +02:00
David Eagles
7800af830a Modified WMTS Resource URL generation to support hostnames that contain the layer name.
If the hostname contained the layer name (eg. https://layername.domain.com/layername/wmts) then the WMTS capabilities document contained an incorrect Resource URL because the hostname was stripped back to simply https://.  This modification simply looks for the last occurrence of the layer name instead of the first when forming the Resource URL.
2021-09-24 11:09:17 +10:00
Dalibor Janák
eb571b5d70 Merge pull request #144 from scubajorgen/master
Resolved bug in transparent png tile
2020-08-10 07:53:51 +02:00
Dalibor Janak
43e3cadb87 Remove old aray syntax 2020-07-28 22:34:22 +02:00
Jorgen
54254f8ef1 Resolved bug in transparent png tile 2019-07-11 13:27:21 +02:00
Dalibor Janák
b266dc2a6e Merge pull request #143 from CodeLingoBot/rewrite-klokantech-tileserver-php-psr2-lower-case-constant-values
Change PHP keywords to comply with PSR2
2019-04-04 07:52:45 +02:00
CodeLingoBot
4c339b8d19 Change PHP keywords to comply with PSR2
Signed-off-by: CodeLingoBot <bot@codelingo.io>
2019-04-03 11:24:50 +13:00
Dalibor Janák
287da5062e Merge pull request #141 from ibesora/master
Removes body data from http 204 response
2019-01-07 14:31:58 +01:00
Isaac Besora Vilardaga
d11963d43c Removes body data from http 204 response 2019-01-07 14:01:19 +01:00
Dalibor Janák
5ddbfa300a Updated readme with tile size, closes #129 and custom CRS 2018-07-02 11:45:46 +02:00
Dalibor Janák
2a84c6271f Fix Undefined index SERVER_PORT router error in some server configs 2018-02-19 16:31:18 +01:00
Dalibor Janák
9f6b5d5100 Merge branch 'master' of https://github.com/klokantech/tileserver-php 2018-02-19 16:20:31 +01:00
Dalibor Janák
dca0e64219 Removed unused var in router 2018-02-19 16:15:51 +01:00
Dalibor Janák
e6af62e191 Remove PHP 5.3 check from travis 2018-02-19 15:59:44 +01:00
Dalibor Janák
d3ebdec555 Strip incorrectly encoded property in OMT datasets 2018-02-19 15:52:48 +01:00
Dalibor Janák
f75b1bbfa6 Don't need trailing slash in dataroot 2017-12-05 15:31:40 +01:00
Dalibor Janák
bb823a57ff 0. 2017-04-24 15:26:57 +02:00
Dalibor Janák
380cfdeaaf Merge pull request #100 from Burke9077/master
Fixed issue if HTTP_X_FORWARDED_PROTO is unset.
2017-04-24 09:31:57 +02:00
Matt Burke
dc2452e781 Fixed issue where tiles are not loaded if HTTP_X_FORWARDED_PROTO is
unset.
2017-04-06 10:27:04 -04:00
Dalibor Janák
5f314fef28 Fixed bug in condition with X-Forwarded-Proto 2016-12-06 23:18:30 +01:00
Dalibor Janák
758b6a1e4d Fix of X-Forwarded-Proto server variable (issue #92) 2016-12-06 18:10:06 +01:00
Dalibor Janák
d7a4ad03c1 Use https with X-Forwarded-Proto (issue #92) 2016-12-06 17:58:22 +01:00
Dalibor Janák
2ead30dc4a Rename mbtilesPrefix to dataRoot 2016-08-29 19:06:51 +02:00
Dalibor Janák
682a9ee5c9 Merge pull request #88 from mtfurlan/master
Add prefix to mbtiles, return 204 on no tile
2016-08-29 19:02:15 +02:00
Mark Furland
7405ec3dc3 Add prefix to mbtiles, return 204 on no tile
This adds a configuration option for a prefix to the mbtiles, so you can
store the tiles in other locations.

It also changes the no tile found 404 to a 204, as the mapbox team will
be doing:
https://github.com/mapbox/mapbox-gl-js/issues/1800#issuecomment-236190808
2016-08-26 11:12:43 -04:00
Petr Pridal
058df85c2e Readme mentions MVT vector tiles (pbf requests) 2016-05-05 12:18:20 +02:00
Petr Pridal
f7613e04a1 Updated default title of the page 2016-05-04 19:19:33 +02:00
Dalibor Janák
e4148733ed Empty tile response for jpg and webp 2016-05-03 23:36:31 +02:00
Dalibor Janák
c28d188e0d Empty tile response for jpg and webp 2016-05-03 23:21:40 +02:00
Dalibor Janák
ef5de0607d Unpack empty png tile PHP 5.2 compatible 2016-05-03 22:48:23 +02:00
Dalibor Janák
494669f5d7 Merge pull request #79 from ramunasd/patch-1
Faster and ~70% smaller empty png tile
2016-05-03 22:13:08 +02:00
Petr Pridal
4b53b4c351 Sample MBTiles file for OpenShift deploy 2016-05-03 17:15:54 +02:00
Petr Pridal
86a1c68888 Update .travis.yml 2016-04-23 00:34:40 +02:00
Petr Pridal
f834ec7fd9 Deploy to OpenShift via travis 2016-04-23 00:08:21 +02:00
Ramūnas Dronga
ac68e90870 Faster and ~70% smaller empty png tile 2016-04-18 21:25:38 +03:00
Dalibor Janák
bb48534fe3 Removing of useless code. 2016-03-21 13:42:53 +01:00
Dalibor Janák
c158bcbe57 Minor warning with some php configurations fixed. 2016-03-21 13:40:33 +01:00
Dalibor Janák
2dfd2fcb5f Max zoom of Mercator TileMatrixSet is affected with max of layers maxzoom (closes #76) 2016-03-21 10:59:29 +01:00
Dalibor Janák
6b4fb0fec7 WMTS url to tiles simplified for fastest tile serving 2016-03-07 14:57:29 +01:00
Dalibor Janák
8f2b068c4f Custom template implementation #31 2016-03-07 14:39:28 +01:00
Dalibor Janák
709ed4ffce Revert "Fix of utfgid detection"
This reverts commit 8f6a2ced68.
2016-03-07 14:00:29 +01:00
Dalibor Janák
7320f426a1 Autodetection fix or warning in router, closes #49 2016-03-04 13:28:05 +01:00
Dalibor Janák
2e36338720 Removed dependency of GlobalMercator class closes #74 2016-03-04 13:20:46 +01:00
Dalibor Janák
70159a5b29 Layer list improved 2016-03-04 12:07:37 +01:00
Dalibor Janák
fdeb010831 Get global vars via getenv 2016-03-01 19:17:46 +01:00
Dalibor Janák
8bbfa1d367 Config from eniviroment if is provided 2016-03-01 15:05:58 +01:00
Dalibor Janák
50630fe1c7 Get config from docker volumes if is provided 2016-03-01 11:57:18 +01:00
Dalibor Janák
8206bcccc7 GD module dependency closes #53 2016-02-29 20:21:13 +01:00
Dalibor Janák
d304b89eb2 Removed unused code 2016-02-29 14:19:09 +01:00
Dalibor Janák
87906da0c5 Correct detection of format closes #67 2016-02-29 14:16:17 +01:00
Dalibor Janák
92ddcb6e66 WMTS / TileJSON for remote tiles #69 2016-02-29 12:16:24 +01:00
Dalibor Janák
7efd117884 Merge pull request #71 from klokantech/customtiles
Customtiles
2016-02-29 09:52:13 +01:00
3 changed files with 317 additions and 387 deletions

View File

@@ -1,9 +1,24 @@
language: php
php:
- 5.3
- 5.4
- 5.5
- 5.6
- 7.0
- 7.1
- hhvm
script:
- php tileserver.php
after_success:
- wget https://github.com/klokantech/tileserver-php/releases/download/v0.1/grandcanyon.mbtiles
- git config --global user.email "travis@localhost.localdomain"
- git config --global user.name "Travis CI"
- git add --all
- git commit -am "Travis change"
deploy:
provider: openshift
user: osm2vectortiles@klokantech.com
password:
secure: hiWKBaqh/CMdnQ2qxsRSKYAnk4tP/q2J50TaO+2sH09x+0Q85ygfICCDrEx457xqmGW0e4zZPsL83mMPvGt5KJO6g9DIncj6BFhpZA0ysnRJ3X1fczTiVN5hQfqNpa+/YIrQ0whu1Ur/IfdYTtvArYhuAPeigCloumGk9gNgSIQ=
domain: tileserver
app: php
skip_cleanup: true

153
README.md
View File

@@ -4,36 +4,23 @@ TileServer PHP: MapTiler and MBTiles maps via WMTS
[![Build Status](https://travis-ci.org/klokantech/tileserver-php.svg)](https://travis-ci.org/klokantech/tileserver-php)
[![Docker Hub](https://img.shields.io/badge/docker-hub-blue.svg)](https://hub.docker.com/r/klokantech/tileserver-php/)
This server distributes maps to desktop, web, and mobile applications from
a standard Apache+PHP web hosting.
This server distributes maps to desktop, web, and mobile applications from a standard Apache+PHP web hosting.
Try a live demo at: http://tileserver.maptiler.com/
It is a free and open-source project implementing the OGC WMTS standard for pre-rendered map tiles made with any [map tiling software](https://www.maptiler.com/desktop/) like MapTiler Desktop, GDAL2Tiles, or any other MBTiles file.
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 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 - copy the project files to a PHP-enabled directory along with your map data.
It is easy to install - just copy the project files to a PHP-enabled
directory along with your map data.
It comes with an online interface showing the list of the maps and step-by-step guides for online mapping libraries (Google Maps API, Leaflet, OpenLayers, OL3, MapBox JS, ArcGIS JS) and various desktop GIS software:
It comes with an online interface showing the list of the maps and step-by-step guides for online mapping libraries (Google Maps API, Leaflet, OpenLayers, OL3, MapLibre GL JS, ArcGIS JS) and various 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.
This project is developed in PHP, not because it is the best language for the development of web applications, but because it maximally simplifies the deployment on a large number of web hostings, including various free web hostings providers.
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, and are therfore slower, unless they are unpacked with mbutil.
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 is delivered via PHP. MBTiles are served via PHP and are therefore slower unless they are unpacked with mbutil.
[MapTiler](http://www.maptiler.com/) can render GeoTIFF, ECW, MrSID, GeoPDF into compatible map tiles. JPEG, PNG, GIF and TIFF with scanned maps or images without geolocation can be turned into standard map layers with the visual georeferencing functionality (http://youtu.be/eJxdCe9CNYg).
[MapTiler](http://www.maptiler.com/) can render GeoTIFF, ECW, MrSID, GeoPDF into compatible map tiles. JPEG, PNG, GIF, and TIFF with scanned maps or images without geolocation can be turned into standard map layers with the visual georeferencing functionality (http://youtu.be/eJxdCe9CNYg).
[![MapTiler - mapping tiles](https://cloud.githubusercontent.com/assets/59284/3037911/583d7810-e0c6-11e3-877c-6a7747b80dd3.jpg)](http://www.maptiler.com/)
@@ -41,7 +28,7 @@ Requirements:
-------------
- Apache webserver (with mod_rewrite / .htaccess supported)
- PHP 5.2+ with SQLite module (php5-sqlite)
- PHP 5.6+ with SQLite module (php5-sqlite)
(or another webserver implementing mod_rewrite rules and PHP)
@@ -50,35 +37,27 @@ Installation:
Download the project files as a [zip archive](https://github.com/klokantech/tileserver-php/archive/master.zip) 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.
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/).
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 open-source utility [mbutil](https://github.com/mapbox/mbutil) produces
exactly the required format.
Tiles produced by open-source GDAL2Tiles or MapTiler Desktop and tiles in .mbtiles format can be easily converted to the required structure (XYZ with top-left origin and metadata.json file). The open-source 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. The advantage is easier data management,
especially upload over FTP or similar protocols.
Direct reading of .mbtiles files is supported but with decreased performance compared to the static files in a directory. The advantage is easier data management, especially upload over FTP or similar protocols.
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:
@@ -90,32 +69,40 @@ Supported protocols:
flipped y-axis.
Exposed at http://[...]/tms
- TileJSON.js
- TileJSON
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
Exposed at http://[...]/layer.json or .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
- 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 standard point your client (desktop or web) to the URL
of 'directory' where you installed tileserver.php project with suffix "wmts".
Exposed at http://[...]/layer/z/x/y.grid.json
- MapBox Vector Tiles (for MBTiles generated by [MapBox Studio Classic](https://www.mapbox.com/mapbox-studio-classic/) or by [OSM2VectorTiles](http://osm2vectortiles.org/) project).
Example http://osm2vectortiles.tileserver.com/
TileJSON can be used in MapBox Studio Classic, MapBox SDKs/APIs, OpenLayers, etc.
Exposed at http://[...]/layer/z/x/y.pbf
- Retina / HighDPI routing with 512 tiles
Use @2x suffix in url for JSONs and tiles. For example http://tileserver.maptiler.com/grandcanyon@2x.json
To use the OGC WMTS standard, 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
If you have installed the project into a root directory of a domain, then the address is: http://www.example.com/wmts
@@ -123,54 +110,46 @@ If you have installed the project into a root directory of a domain, then the ad
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]
Other example requests are mentioned in the .htaccess.
TileServer-PHP supports all coordinates systems. You have to define it with tilejson with specification on https://github.com/klokantech/tilejson-spec/tree/custom-projection/2.2.0
Or use MapTiler to produce datasets with this specification.
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.
This can be done with DNS CNAME records pointing to your hosting. The reason for this is that traditionally browsers will not send more than two simultaneous HTTP requests 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.
In case the data are available in the form of a directory with XYZ tiles, then the 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.
This means performance is excellent, maps are delivered very fast, and a large number of concurrent visitors can be handled even with quite 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.
Mod_rewrite rules are utilized to ensure the HTTP requests defined in the OCG WMTS standard are served, and Apache preserves standard caching headers & eTag.
The performance should be significantly better then performance of any other
tile caching project (such as TileCache.org or GeoWebCache).
The performance should be significantly better than 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
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.
With intention, at this moment, the project supports only:
- We enforce and require XYZ (top-left origin) tiling schema (even for TMS).
Password protection
@@ -201,22 +180,19 @@ 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.
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.
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.
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>
- Petr Pridal - Klokan Technologies GmbH <petr.pridal@maptiler.com>
- Jason Woolard - NOAA <jason.woolard@noaa.gov>
- Jon Sellars - NOAA <jon.sellars@noaa.gov>
- Dalibor Janak - Klokan Technologies GmbH <dalibor.janak@klokantech.com>
- Dalibor Janak - Klokan Technologies GmbH <dalibor.janak@maptiler.com>
Tested WMTS/TMS clients
-----------------------
@@ -235,21 +211,26 @@ Tested WMTS/TMS clients
http://www.thecarbonproject.com/gaia.php
- MapBox.js - the loading of maps via TileJSON, interaction layer supported
https://www.mapbox.com/mapbox.js
Alternative
-----------
If you need [map server with commercial support](https://www.maptiler.com/server/), explore the possibilities provided by the MapTiler Server.
BSD License
-----------
Copyright (C) 2015 Klokan Technologies GmbH (http://www.klokantech.com/)
Copyright (C) 2020 MapTiler AG (https://www.maptiler.com/)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
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.
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.
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

534
tileserver.php Normal file → Executable file
View File

@@ -1,19 +1,22 @@
<?php
/*
* TileServer.php project
* TileServer-PHP project
* ======================
* https://github.com/klokantech/tileserver-php/
* Copyright (C) 2016 - Klokan Technologies GmbH
* https://github.com/maptiler/tileserver-php/
* Copyright (C) 2020 - MapTiler AG
*/
global $config;
$config['serverTitle'] = 'TileServer-php v1';
//$config['baseUrls'] = array('t0.server.com', 't1.server.com');
$config['serverTitle'] = 'Maps hosted with TileServer-php v2.0';
$config['availableFormats'] = ['png', 'jpg', 'jpeg', 'gif', 'webp', 'pbf', 'hybrid'];
$config['dataRoot'] = '';
//$config['template'] = 'template.php';
//$config['baseUrls'] = ['t0.server.com', 't1.server.com'];
Router::serve(array(
Router::serve([
'/' => 'Server:getHtml',
'/test' => 'Server:getInfo',
'/maps' => 'Server:getInfo',
'/html' => 'Server:getHtml',
'/:alpha/:number/:number/:number.grid.json' => 'Json:getUTFGrid',
'/:alpha.json' => 'Json:getJson',
@@ -26,7 +29,7 @@ Router::serve(array(
'/:alpha/:number/:number/:alpha' => 'Wmts:getTile',
'/tms' => 'Tms:getCapabilities',
'/tms/:alpha' => 'Tms:getLayerCapabilities',
));
]);
/**
* Server base
@@ -43,13 +46,13 @@ class Server {
* Datasets stored in file structure
* @var array
*/
public $fileLayer = array();
public $fileLayer = [];
/**
* Datasets stored in database
* @var array
*/
public $dbLayer = array();
public $dbLayer = [];
/**
* PDO database connection
@@ -62,6 +65,26 @@ class Server {
*/
public function __construct() {
$this->config = $GLOBALS['config'];
if($this->config['dataRoot'] != ''
&& substr($this->config['dataRoot'], -1) != '/' ){
$this->config['dataRoot'] .= '/';
}
//Get config from enviroment
$envServerTitle = getenv('serverTitle');
if($envServerTitle !== false){
$this->config['serverTitle'] = $envServerTitle;
}
$envBaseUrls = getenv('baseUrls');
if($envBaseUrls !== false){
$this->config['baseUrls'] = is_array($envBaseUrls) ?
$envBaseUrls : explode(',', $envBaseUrls);
}
$envTemplate = getenv('template');
if($envBaseUrls !== false){
$this->config['template'] = $envTemplate;
}
}
/**
@@ -69,7 +92,7 @@ class Server {
*/
public function setDatasets() {
$mjs = glob('*/metadata.json');
$mbts = glob('*.mbtiles');
$mbts = glob($this->config['dataRoot'] . '*.mbtiles');
if ($mjs) {
foreach (array_filter($mjs, 'is_readable') as $mj) {
$layer = $this->metadataFromMetadataJson($mj);
@@ -97,7 +120,7 @@ class Server {
$this->x = $params[1];
$file = explode('.', $params[0]);
$this->y = $file[0];
$this->ext = isset($file[1]) ? $file[1] : NULL;
$this->ext = isset($file[1]) ? $file[1] : null;
}
}
@@ -113,7 +136,7 @@ class Server {
return $value;
}
}
return FALSE;
return false;
}
/**
@@ -122,10 +145,10 @@ class Server {
* @return boolean
*/
public function isDBLayer($layer) {
if (is_file($layer . '.mbtiles')) {
return TRUE;
if (is_file($this->config['dataRoot'] . $layer . '.mbtiles')) {
return true;
} else {
return FALSE;
return false;
}
}
@@ -136,9 +159,9 @@ class Server {
*/
public function isFileLayer($layer) {
if (is_dir($layer)) {
return TRUE;
return true;
} else {
return FALSE;
return false;
}
}
@@ -149,9 +172,8 @@ class Server {
*/
public function metadataFromMetadataJson($jsonFileName) {
$metadata = json_decode(file_get_contents($jsonFileName), true);
$metadata = $this->metadataValidation($metadata);
$metadata['basename'] = str_replace('/metadata.json', '', $jsonFileName);
return $metadata;
return $this->metadataValidation($metadata);
}
/**
@@ -160,7 +182,7 @@ class Server {
* @return object
*/
public function metadataFromMbtiles($mbt) {
$metadata = array();
$metadata = [];
$this->DBconnect($mbt);
$result = $this->db->query('select * from metadata');
@@ -198,11 +220,11 @@ class Server {
$e = -180 + 360 * ((1 + $resultdata[0]['e']) / pow(2, $metadata['maxzoom']));
$n = $this->row2lat($resultdata[0]['n'], $metadata['maxzoom']);
$s = $this->row2lat($resultdata[0]['s'] - 1, $metadata['maxzoom']);
$metadata['bounds'] = implode(',', array($w, $s, $e, $n));
$metadata['bounds'] = implode(',', [$w, $s, $e, $n]);
}
$metadata = $this->metadataValidation($metadata);
$mbt = explode('.', $mbt);
$metadata['basename'] = $mbt[0];
$metadata = $this->metadataValidation($metadata);
return $metadata;
}
@@ -224,7 +246,7 @@ class Server {
*/
public function metadataValidation($metadata) {
if (!array_key_exists('bounds', $metadata)) {
$metadata['bounds'] = array(-180, -85.06, 180, 85.06);
$metadata['bounds'] = [-180, -85.06, 180, 85.06];
} elseif (!is_array($metadata['bounds'])) {
$metadata['bounds'] = array_map('floatval', explode(',', $metadata['bounds']));
}
@@ -242,14 +264,30 @@ class Server {
$metadata['maxzoom'] = 18;
}
if (!array_key_exists('format', $metadata)) {
$metadata['format'] = 'png';
if(array_key_exists('tiles', $metadata)){
$pos = strrpos($metadata['tiles'][0], '.');
$metadata['format'] = trim(substr($metadata['tiles'][0], $pos + 1));
}
}
$formats = $this->config['availableFormats'];
if(!in_array(strtolower($metadata['format']), $formats)){
$metadata['format'] = 'png';
}
if (!array_key_exists('scale', $metadata)) {
$metadata['scale'] = 1;
}
// TODO: detect thumb / SQL for mbtiles
if(!array_key_exists('tiles', $metadata)){
$tiles = [];
foreach ($this->config['baseUrls'] as $url) {
$url = '' . $this->config['protocol'] . '://' . $url . '/' .
$metadata['basename'] . '/{z}/{x}/{y}';
if(strlen($metadata['format']) <= 4){
$url .= '.' . $metadata['format'];
}
$tiles[] = $url;
}
$metadata['tiles'] = $tiles;
}
return $metadata;
}
@@ -259,7 +297,7 @@ class Server {
*/
public function DBconnect($tileset) {
try {
$this->db = new PDO('sqlite:' . $tileset, '', '', array(PDO::ATTR_PERSISTENT => true));
$this->db = new PDO('sqlite:' . $tileset, '', '', [PDO::ATTR_PERSISTENT => true]);
} catch (Exception $exc) {
echo $exc->getTraceAsString();
die;
@@ -278,16 +316,16 @@ class Server {
* @return boolean
*/
public function isModified($filename) {
$filename = $filename . '.mbtiles';
$filename = $this->config['dataRoot'] . $filename . '.mbtiles';
$lastModifiedTime = filemtime($filename);
$eTag = md5($lastModifiedTime);
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $lastModifiedTime) . ' GMT');
header('Etag:' . $eTag);
if (@strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) == $lastModifiedTime ||
@trim($_SERVER['HTTP_IF_NONE_MATCH']) == $eTag) {
return TRUE;
return true;
} else {
return FALSE;
return false;
}
}
@@ -300,33 +338,40 @@ class Server {
* @param string $ext
*/
public function renderTile($tileset, $z, $y, $x, $ext) {
//simple input validation
$z = floatval($z);
$y = floatval($y);
$x = floatval($x);
$alpharegex = '/^([a-zA-Z0-9-_@\.]*)$/';
if (!preg_match($alpharegex, $tileset) || !preg_match($alpharegex, $ext)) {
header('HTTP/1.1 400 Bad Request');
echo 'Server: Parameter validation failed.';
die;
}
if ($this->isDBLayer($tileset)) {
if ($this->isModified($tileset) == TRUE) {
if ($this->isModified($tileset) == true) {
header('Access-Control-Allow-Origin: *');
header('HTTP/1.1 304 Not Modified');
die;
}
$this->DBconnect($tileset . '.mbtiles');
$z = floatval($z);
$y = floatval($y);
$x = floatval($x);
$this->DBconnect($this->config['dataRoot'] . $tileset . '.mbtiles');
$flip = true;
if ($flip) {
$y = pow(2, $z) - 1 - $y;
}
$result = $this->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) {
if (!isset($data) || $data === false) {
//if tile doesn't exist
//select scale of tile (for retina tiles)
$result = $this->db->query('select value from metadata where name="scale"');
$resultdata = $result->fetchColumn();
$scale = isset($resultdata) && $resultdata !== FALSE ? $resultdata : 1;
$scale = isset($resultdata) && $resultdata !== false ? $resultdata : 1;
$this->getCleanTile($scale, $ext);
} else {
$result = $this->db->query('select value from metadata where name="format"');
$resultdata = $result->fetchColumn();
$format = isset($resultdata) && $resultdata !== FALSE ? $resultdata : 'png';
$format = isset($resultdata) && $resultdata !== false ? $resultdata : 'png';
if ($format == 'jpg') {
$format = 'jpeg';
}
@@ -342,15 +387,23 @@ class Server {
} elseif ($this->isFileLayer($tileset)) {
$name = './' . $tileset . '/' . $z . '/' . $x . '/' . $y;
$mime = 'image/';
if($ext != NULL){
if($ext != null){
$name .= '.' . $ext;
}
//check if the requested file is inside the current working directory
$requestedPath = realpath($name);
$allowedBasePath = realpath(getcwd());
if (strpos($requestedPath, $allowedBasePath . DIRECTORY_SEPARATOR) !== 0) {
header('HTTP/1.1 404 Not Found');
echo 'Server: Unknown or not specified dataset "' . htmlspecialchars($tileset) . '"';
die;
}
if ($fp = @fopen($name, 'rb')) {
if($ext != NULL){
if($ext != null){
$mime .= $ext;
}else{
//detect image type from file
$mimetypes = array('gif', 'jpeg', 'png');
$mimetypes = ['gif', 'jpeg', 'png'];
$mime .= $mimetypes[exif_imagetype($name) - 1];
}
header('Access-Control-Allow-Origin: *');
@@ -368,7 +421,7 @@ class Server {
$this->getCleanTile($meta->scale, $ext);
} else {
header('HTTP/1.1 404 Not Found');
echo 'Server: Unknown or not specified dataset "' . $tileset . '"';
echo 'Server: Unknown or not specified dataset "' . htmlspecialchars($tileset) . '"';
die;
}
}
@@ -380,22 +433,29 @@ class Server {
public function getCleanTile($scale = 1, $format = 'png') {
switch ($format) {
case 'pbf':
header('HTTP/1.1 404 Not Found');
header('Access-Control-Allow-Origin: *');
header('HTTP/1.1 204 No Content');
header('Content-Type: application/json; charset=utf-8');
echo '{"message":"Tile does not exist"}';
die;
break;
case 'webp':
header('Access-Control-Allow-Origin: *');
header('Content-type: image/webp');
echo base64_decode('UklGRhIAAABXRUJQVlA4TAYAAAAvQWxvAGs=');
break;
case 'jpg':
header('Access-Control-Allow-Origin: *');
header('Content-type: image/jpg');
echo base64_decode('/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=');
break;
case 'png':
default:
$tileSize = 256 * $scale;
$png = imagecreatetruecolor($tileSize, $tileSize);
imagesavealpha($png, true);
$trans_colour = imagecolorallocatealpha($png, 0, 0, 0, 127);
imagefill($png, 0, 0, $trans_colour);
header('Access-Control-Allow-Origin: *');
header('Content-type: image/png');
imagepng($png);
die;
// 256x256 transparent optimised png tile
echo pack('H*', '89504e470d0a1a0a0000000d494844520000010000000100010300000066bc3a2500000003504c5445000000a77a3dda0000000174524e530040e6d8660000001f494441541819edc1010d000000c220fba77e0e37600000000000000000e70221000001f5a2bd040000000049454e44ae426082');
break;
}
die;
}
/**
@@ -405,23 +465,23 @@ class Server {
* @param integer $y
* @param integer $x
*/
public function renderUTFGrid($tileset, $z, $y, $x, $flip = TRUE) {
public function renderUTFGrid($tileset, $z, $y, $x, $flip = true) {
if ($this->isDBLayer($tileset)) {
if ($this->isModified($tileset) == TRUE) {
if ($this->isModified($tileset) == true) {
header('HTTP/1.1 304 Not Modified');
}
if ($flip) {
$y = pow(2, $z) - 1 - $y;
}
try {
$this->DBconnect($tileset . '.mbtiles');
$this->DBconnect($this->config['dataRoot'] . $tileset . '.mbtiles');
$query = 'SELECT grid FROM grids WHERE tile_column = ' . $x . ' AND '
. 'tile_row = ' . $y . ' AND zoom_level = ' . $z;
$result = $this->db->query($query);
$data = $result->fetch(PDO::FETCH_ASSOC);
if ($data !== FALSE) {
if ($data !== false) {
$grid = gzuncompress($data['grid']);
$grid = substr(trim($grid), 0, -1);
@@ -463,29 +523,22 @@ class Server {
* Returns server info
*/
public function getInfo() {
// echo $this->config['baseUrls'][0];die;
$this->setDatasets();
$maps = array_merge($this->fileLayer, $this->dbLayer);
header('Content-Type: text/html;charset=UTF-8');
echo '<!DOCTYPE html><html><head><meta charset="UTF-8"><title>' . $this->config['serverTitle'] . '</title></head><body>';
echo '<h1>' . $this->config['serverTitle'] . '</h1>';
echo 'TileJSON service: <a href="//' . $this->config['baseUrls'][0] . '/index.json">' . $this->config['baseUrls'][0] . '/index.json</a><br>';
echo 'WMTS service: <a href="//' . $this->config['baseUrls'][0] . '/wmts">' . $this->config['baseUrls'][0] . '/wmts</a><br>';
echo 'TMS service: <a href="//' . $this->config['baseUrls'][0] . '/tms">' . $this->config['baseUrls'][0] . '/tms</a>';
echo '<!DOCTYPE html><html><head><meta charset="UTF-8"><title>' . $this->config['serverTitle'] . '</title></head><body>' .
'<h1>' . $this->config['serverTitle'] . '</h1>' .
'TileJSON service: <a href="//' . $this->config['baseUrls'][0] . '/index.json">' . $this->config['baseUrls'][0] . '/index.json</a><br>' .
'WMTS service: <a href="//' . $this->config['baseUrls'][0] . '/wmts">' . $this->config['baseUrls'][0] . '/wmts</a><br>' .
'TMS service: <a href="//' . $this->config['baseUrls'][0] . '/tms">' . $this->config['baseUrls'][0] . '/tms</a>';
foreach ($maps as $map) {
$extend = '[';
foreach ($map['bounds'] as $ext) {
$extend = $extend . ' ' . $ext;
}
$extend = $extend . ' ]';
if (strpos($map['basename'], 'mbtiles') !== false) {
echo '<p>Available MBtiles tileset: ' . $map['basename'] . '<br>';
} else {
echo '<p>Available file tileset: ' . $map['basename'] . '<br>';
}
echo 'Metadata: <a href="//' . $this->config['baseUrls'][0] . '/' . $map['basename'] . '.json">'
. $this->config['baseUrls'][0] . '/' . $map['basename'] . '.json</a><br>';
echo 'Bounds: ' . $extend . '</p>';
$extend = '[' . implode($map['bounds'], ', ') . ']';
echo '<p>Tileset: <b>' . $map['basename'] . '</b><br>' .
'Metadata: <a href="//' . $this->config['baseUrls'][0] . '/' . $map['basename'] . '.json">' .
$this->config['baseUrls'][0] . '/' . $map['basename'] . '.json</a><br>' .
'Bounds: ' . $extend ;
if(isset($map['crs'])){echo '<br>CRS: ' . $map['crs'];}
echo '</p>';
}
echo '<p>Copyright (C) 2016 - Klokan Technologies GmbH</p>';
echo '</body></html>';
@@ -497,28 +550,34 @@ class Server {
public function getHtml() {
$this->setDatasets();
$maps = array_merge($this->fileLayer, $this->dbLayer);
header('Content-Type: text/html;charset=UTF-8');
echo '<!DOCTYPE html><html><head><meta charset="UTF-8"><title>' . $this->config['serverTitle'] . '</title>';
echo '<link rel="stylesheet" type="text/css" href="//cdn.klokantech.com/tileviewer/v1/index.css" />
<script src="//cdn.klokantech.com/tileviewer/v1/index.js"></script><body>
<script>tileserver({index:"' . $this->config['protocol'] . '://' . $this->config['baseUrls'][0] . '/index.json", tilejson:"' . $this->config['protocol'] . '://' . $this->config['baseUrls'][0] . '/%n.json", tms:"' . $this->config['protocol'] . '://' . $this->config['baseUrls'][0] . '/tms", wmts:"' . $this->config['protocol'] . '://' . $this->config['baseUrls'][0] . '/wmts"});</script>
<h1>Welcome to ' . $this->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>';
if (!isset($maps)) {
echo '<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:' . 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>';
if (isset($this->config['template']) && file_exists($this->config['template'])) {
$baseUrls = $this->config['baseUrls'];
$serverTitle = $this->config['serverTitle'];
include_once $this->config['template'];
} else {
echo '<ul>';
foreach ($maps as $map) {
echo "<li>" . $map['name'] . '</li>';
header('Content-Type: text/html;charset=UTF-8');
echo '<!DOCTYPE html><html><head><meta charset="UTF-8"><title>' . $this->config['serverTitle'] . '</title>';
echo '<link rel="stylesheet" type="text/css" href="//cdn.klokantech.com/tileviewer/v1/index.css" />
<script src="//cdn.klokantech.com/tileviewer/v1/index.js"></script><body>
<script>tileserver({index:"' . $this->config['protocol'] . '://' . $this->config['baseUrls'][0] . '/index.json", tilejson:"' . $this->config['protocol'] . '://' . $this->config['baseUrls'][0] . '/%n.json", tms:"' . $this->config['protocol'] . '://' . $this->config['baseUrls'][0] . '/tms", wmts:"' . $this->config['protocol'] . '://' . $this->config['baseUrls'][0] . '/wmts"});</script>
<h1>Welcome to ' . $this->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>';
if (!isset($maps)) {
echo '<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:' . 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>';
} else {
echo '<ul>';
foreach ($maps as $map) {
echo "<li>" . $map['name'] . '</li>';
}
echo '</ul>';
}
echo '</ul>';
echo '</body></html>';
}
echo '</body></html>';
}
}
@@ -579,19 +638,9 @@ class Json extends Server {
public function metadataTileJson($metadata) {
$metadata['tilejson'] = '2.0.0';
$metadata['scheme'] = 'xyz';
$tiles = array();
foreach ($this->config['baseUrls'] as $url) {
$url = '' . $this->config['protocol'] . '://' . $url . '/' .
$metadata['basename'] . '/{z}/{x}/{y}';
if(strlen($metadata['format']) <= 4){
$url .= '.' . $metadata['format'];
}
$tiles[] = $url;
}
$metadata['tiles'] = $tiles;
if ($this->isDBLayer($metadata['basename'])) {
$this->DBconnect($metadata['basename'] . '.mbtiles');
$res = $this->db->query('SELECT * FROM grids LIMIT 1;');
$this->DBconnect($this->config['dataRoot'] . $metadata['basename'] . '.mbtiles');
$res = $this->db->query('SELECT name FROM sqlite_master WHERE name="grids";');
if ($res) {
foreach ($this->config['baseUrls'] as $url) {
$grids[] = '' . $this->config['protocol'] . '://' . $url . '/' . $metadata['basename'] . '/{z}/{x}/{y}.grid.json';
@@ -602,7 +651,9 @@ class Json extends Server {
if (array_key_exists('json', $metadata)) {
$mjson = json_decode(stripslashes($metadata['json']));
foreach ($mjson as $key => $value) {
$metadata[$key] = $value;
if ($key != 'Layer'){
$metadata[$key] = $value;
}
}
unset($metadata['json']);
}
@@ -720,7 +771,7 @@ class Wmts extends Server {
*/
public function get() {
$request = $this->getGlobal('Request');
if ($request !== FALSE && $request == 'gettile') {
if ($request !== false && $request == 'gettile') {
$this->getTile();
} else {
parent::setDatasets();
@@ -730,7 +781,7 @@ class Wmts extends Server {
/**
* Validates tilematrixset, calculates missing params
* @param Obrject $tileMatrix
* @param Object $tileMatrix
* @return Object
*/
public function parseTileMatrix($layer, $tileMatrix){
@@ -756,15 +807,15 @@ class Wmts extends Server {
$tileMatrix[$i]['pixel_size'],
$tileMatrix[$i]['tile_size']
);
$tileMatrix[$i]['matrix_size'] = array(
$tileMatrix[$i]['matrix_size'] = [
$tileExtent[2] + 1,
$tileExtent[1] + 1
);
];
}
if(!isset($tileMatrix[$i]['origin']) && isset($tileMatrix[$i]['extent'])){
$tileMatrix[$i]['origin'] = array(
$tileMatrix[$i]['origin'] = [
$tileMatrix[$i]['extent'][0], $tileMatrix[$i]['extent'][3]
);
];
}
// Origins of geographic coordinate systems are setting in opposite order
if (isset($proj4) && $proj4['proj'] === 'longlat') {
@@ -775,7 +826,7 @@ class Wmts extends Server {
}
if(!isset($tileMatrix[$i]['tile_size'])){
$tileSize = 256 * (int) $layer['scale'];
$tileMatrix[$i]['tile_size'] = array($tileSize, $tileSize);
$tileMatrix[$i]['tile_size'] = [$tileSize, $tileSize];
}
}
@@ -791,12 +842,12 @@ class Wmts extends Server {
* @return array
*/
public function tilesOfExtent($extent, $origin, $pixel_size, $tile_size) {
$tiles = array(
$tiles = [
$this->minsample($extent[0] - $origin[0], $pixel_size[0] * $tile_size[0]),
$this->minsample($extent[1] - $origin[1], $pixel_size[1] * $tile_size[1]),
$this->maxsample($extent[2] - $origin[0], $pixel_size[0] * $tile_size[0]),
$this->maxsample($extent[3] - $origin[1], $pixel_size[1] * $tile_size[1]),
);
];
return $tiles;
}
@@ -810,23 +861,24 @@ class Wmts extends Server {
/**
* Default TileMetrixSet for Pseudo Mercator projection 3857
* @param ?number $maxZoom
* @return string TileMatrixSet xml
*/
public function getMercatorTileMatrixSet(){
public function getMercatorTileMatrixSet($maxZoom = 18){
$denominatorBase = 559082264.0287178;
$extent = array(-20037508.34,-20037508.34,20037508.34,20037508.34);
$tileMatrixSet = array();
$extent = [-20037508.34,-20037508.34,20037508.34,20037508.34];
$tileMatrixSet = [];
for($i = 0; $i <= 18; $i++){
for($i = 0; $i <= $maxZoom; $i++){
$matrixSize = pow(2, $i);
$tileMatrixSet[] = array(
$tileMatrixSet[] = [
'extent' => $extent,
'id' => (string) $i,
'matrix_size' => array($matrixSize, $matrixSize),
'origin' => array($extent[0], $extent[3]),
'matrix_size' => [$matrixSize, $matrixSize],
'origin' => [$extent[0], $extent[3]],
'scale_denominator' => $denominatorBase / pow(2, $i),
'tile_size' => array(256, 256)
);
'tile_size' => [256, 256]
];
}
return $this->getTileMatrixSet('GoogleMapsCompatible', $tileMatrixSet, 'EPSG:3857');
@@ -837,26 +889,26 @@ class Wmts extends Server {
* @return string Xml
*/
public function getWGS84TileMatrixSet(){
$extent = array(-180.000000, -90.000000, 180.000000, 90.000000);
$scaleDenominators = array(279541132.01435887813568115234, 139770566.00717943906784057617,
$extent = [-180.000000, -90.000000, 180.000000, 90.000000];
$scaleDenominators = [279541132.01435887813568115234, 139770566.00717943906784057617,
69885283.00358971953392028809, 34942641.50179485976696014404, 17471320.75089742988348007202,
8735660.37544871494174003601, 4367830.18772435747087001801, 2183915.09386217873543500900,
1091957.54693108936771750450, 545978.77346554468385875225, 272989.38673277234192937613,
136494.69336638617096468806, 68247.34668319308548234403, 34123.67334159654274117202,
17061.83667079825318069197, 8530.91833539912659034599, 4265.45916769956329517299,
2132.72958384978574031265);
$tileMatrixSet = array();
2132.72958384978574031265];
$tileMatrixSet = [];
for($i = 0; $i <= count($scaleDenominators); $i++){
for($i = 0; $i <= 17; $i++){
$matrixSize = pow(2, $i);
$tileMatrixSet[] = array(
$tileMatrixSet[] = [
'extent' => $extent,
'id' => (string) $i,
'matrix_size' => array($matrixSize * 2, $matrixSize),
'origin' => array($extent[3], $extent[0]),
'matrix_size' => [$matrixSize * 2, $matrixSize],
'origin' => [$extent[3], $extent[0]],
'scale_denominator' => $scaleDenominators[$i],
'tile_size' => array(256, 256)
);
'tile_size' => [256, 256]
];
}
return $this->getTileMatrixSet('WGS84', $tileMatrixSet, 'EPSG:4326');
@@ -967,9 +1019,9 @@ class Wmts extends Server {
<Contents>';
$customtileMatrixSets = '';
$maxMercatorZoom = 18;
//layers
$mercator = new GlobalMercator();
foreach ($layers as $m) {
$basename = $m['basename'];
@@ -991,16 +1043,16 @@ class Wmts extends Server {
);
} 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);
$maxMercatorZoom = max($maxMercatorZoom, $m['maxzoom']);
}
$resourceUrlTemplate = $this->config['protocol'] . '://'
. $this->config['baseUrls'][0] . '/wmts/' . $basename . '/{TileMatrixSet}/{TileMatrix}/{TileCol}/{TileRow}';
$wmtsHost = substr($m['tiles'][0], 0, strrpos($m['tiles'][0], $m['basename']));
$resourceUrlTemplate = $wmtsHost . $basename
. '/{TileMatrix}/{TileCol}/{TileRow}';
if(strlen($format) <= 4){
$resourceUrlTemplate .= '.' . $format;
}
echo'
<Layer>
<ows:Title>' . $title . '</ows:Title>
@@ -1026,7 +1078,7 @@ class Wmts extends Server {
}
// Print PseudoMercator TileMatrixSet
echo $this->getMercatorTileMatrixSet();
echo $this->getMercatorTileMatrixSet($maxMercatorZoom);
// Print WGS84 TileMatrixSet
echo $this->getWGS84TileMatrixSet();
@@ -1042,7 +1094,7 @@ class Wmts extends Server {
public function getTile() {
$request = $this->getGlobal('Request');
if ($request) {
if (strpos('/', $_GET['Format']) !== FALSE) {
if (strpos('/', $_GET['Format']) !== false) {
$format = explode('/', $_GET['Format']);
$format = $format[1];
} else {
@@ -1117,10 +1169,12 @@ class Tms extends Server {
$srs = 'EPSG:4326';
} else {
$srs = 'EPSG:3857';
echo '<TileMap title="' . $title . '" srs="' . $srs
. '" type="InvertedTMS" ' . 'profile="global-' . $profile
. '" href="' . $this->config['protocol'] . '://' . $this->config['baseUrls'][0] . '/tms/' . $basename . '" />';
}
$url = $this->config['protocol'] . '://' . $this->config['baseUrls'][0]
. '/tms/' . $basename;
echo '<TileMap title="' . $title . '" srs="' . $srs
. '" type="InvertedTMS" ' . 'profile="global-' . $profile
. '" href="' . $url . '" />';
}
echo '</TileMaps></TileMapService>';
}
@@ -1142,31 +1196,35 @@ class Tms extends Server {
$bounds = $m['bounds'];
if ($m['profile'] == 'geodetic') {
$srs = 'EPSG:4326';
$originx = -180.0;
$originy = -90.0;
$initialResolution = 0.703125;
$initRes = 0.703125;
} elseif ($m['profile'] == 'custom') {
$srs = $m['crs'];
$bounds = $m['extent'];
if(isset($m['tile_matrix'][0]['pixel_size'][0])){
$initRes = $m['tile_matrix'][0]['pixel_size'][0];
}else{
$initRes = 1;
}
} else {
$srs = 'EPSG:3857';
$originx = -20037508.342789;
$originy = -20037508.342789;
$mercator = new GlobalMercator();
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;
$bounds = [-20037508.34,-20037508.34,20037508.34,20037508.34];
$initRes = 156543.03392804062;
}
$mime = ($m['format'] == 'jpg') ? 'image/jpeg' : 'image/png';
header("Content-type: application/xml");
echo '<TileMap version="1.0.0" tilemapservice="' . $this->config['protocol'] . '://' . $this->config['baseUrls'][0] . '/' . $m['basename'] . '" type="InvertedTMS">
$serviceUrl = $this->config['protocol'] . '://' . $this->config['baseUrls'][0] . '/' . $m['basename'];
echo '<TileMap version="1.0.0" tilemapservice="' . $serviceUrl . '" type="InvertedTMS">
<Title>' . htmlspecialchars($title) . '</Title>
<Abstract>' . htmlspecialchars($description) . '</Abstract>
<SRS>' . $srs . '</SRS>
<BoundingBox minx="' . $bounds[0] . '" miny="' . $bounds[1] . '" maxx="' . $bounds[2] . '" maxy="' . $bounds[3] . '" />
<Origin x="' . $originx . '" y="' . $originy . '"/>
<Origin x="' . $bounds[0] . '" y="' . $bounds[1] . '"/>
<TileFormat width="256" height="256" mime-type="' . $mime . '" extension="' . $m['format'] . '"/>
<TileSets profile="global-' . $m['profile'] . '">';
for ($zoom = $m['minzoom']; $zoom < $m['maxzoom'] + 1; $zoom++) {
echo '<TileSet href="' . $this->config['protocol'] . '://' . $this->config['baseUrls'][0] . '/' . $m['basename'] . '/' . $zoom . '" units-per-pixel="' . $initialResolution / pow(2, $zoom) . '" order="' . $zoom . '" />';
$res = $initRes / pow(2, $zoom);
$url = $this->config['protocol'] . '://' . $this->config['baseUrls'][0] . '/' . $m['basename'] . '/' . $zoom;
echo '<TileSet href="' . $url . '" units-per-pixel="' . $res . '" order="' . $zoom . '" />';
}
echo'</TileSets></TileMap>';
}
@@ -1177,135 +1235,6 @@ class Tms extends Server {
public function getTile() {
parent::renderTile($this->layer, $this->z, $this->y, $this->x, $this->ext);
}
}
/*
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, sublic ense,
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 abov
e 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);
}
}
/**
@@ -1317,13 +1246,18 @@ class Router {
* @param array $routes
*/
public static function serve($routes) {
$request_method = strtolower($_SERVER['REQUEST_METHOD']);
$path_info = '/';
global $config;
$config['protocol'] = ( isset($_SERVER["HTTPS"]) or $_SERVER['SERVER_PORT'] == '443') ? "https" : "http";
$xForwarded = false;
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
if ($_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') {
$xForwarded = true;
}
}
$config['protocol'] = ((isset($_SERVER['HTTPS']) or (isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443)) or $xForwarded) ? 'https' : 'http';
if (!empty($_SERVER['PATH_INFO'])) {
$path_info = $_SERVER['PATH_INFO'];
} else if (!empty($_SERVER['ORIG_PATH_INFO']) && $_SERVER['ORIG_PATH_INFO'] !== '/tileserver.php') {
} else if (!empty($_SERVER['ORIG_PATH_INFO']) && strpos($_SERVER['ORIG_PATH_INFO'], 'tileserver.php') === false) {
$path_info = $_SERVER['ORIG_PATH_INFO'];
} else if (!empty($_SERVER['REQUEST_URI']) && strpos($_SERVER['REQUEST_URI'], '/tileserver.php') !== false) {
$path_info = $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
@@ -1334,14 +1268,14 @@ class Router {
}
}
$discovered_handler = null;
$regex_matches = array();
$regex_matches = [];
if ($routes) {
$tokens = array(
$tokens = [
':string' => '([a-zA-Z]+)',
':number' => '([0-9]+)',
':alpha' => '([a-zA-Z0-9-_@\.]+)'
);
];
//global $config;
foreach ($routes as $pattern => $handler_name) {
$pattern = strtr($pattern, $tokens);
@@ -1362,7 +1296,7 @@ class Router {
$discoverered_class = explode(':', $discovered_handler);
$discoverered_method = explode(':', $discovered_handler);
$handler_instance = new $discoverered_class[0]($regex_matches);
call_user_func(array($handler_instance, $discoverered_method[1]));
call_user_func([$handler_instance, $discoverered_method[1]]);
} else {
$handler_instance = new $discovered_handler($regex_matches);
}
@@ -1371,9 +1305,9 @@ class Router {
}
} else {
if (!isset($config['baseUrls'][0])) {
$config['baseUrls'][0] = $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] . '?';
$config['baseUrls'][0] = $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
}
if (strpos($_SERVER['REQUEST_URI'], '=') != FALSE) {
if (strpos($_SERVER['REQUEST_URI'], '=') != false) {
$kvp = explode('=', $_SERVER['REQUEST_URI']);
$_GET['callback'] = $kvp[1];
$params[0] = 'index';