diff --git a/docs/endpoints.rst b/docs/endpoints.rst
index d025e46..7562813 100644
--- a/docs/endpoints.rst
+++ b/docs/endpoints.rst
@@ -13,11 +13,12 @@ Styles
Rendered tiles
==============
-* Rendered tiles are served at ``/styles/{id}/{z}/{x}/{y}[@2x].{format}``
+* Rendered tiles are served at ``/styles/{id}[/{tileSize}]/{z}/{x}/{y}[@2x].{format}``
- * The optional ``@2x`` (or ``@3x``, ``@4x``) part can be used to render HiDPI (retina) tiles
+ * The optional ratio ``@2x`` (ex. ``@2x``, ``@3x``, ``@4x``) part can be used to render HiDPI (retina) tiles
+ * The optional tile size ``/{tileSize}`` (ex. ``/256``, ``/512``). if omitted, tileSize defaults to 256.
* Available formats: ``png``, ``jpg`` (``jpeg``), ``webp``
- * TileJSON at ``/styles/{id}.json``
+ * TileJSON at ``/styles[/{tileSize}]/{id}.json``
* The rendered tiles are not available in the ``tileserver-gl-light`` version.
@@ -91,13 +92,13 @@ Static images
Source data
===========
-* Source data are served at ``/data/{mbtiles}/{z}/{x}/{y}.{format}``
+* Source data are served at ``/data/{id}/{z}/{x}/{y}.{format}``
* Format depends on the source file (usually ``png`` or ``pbf``)
* ``geojson`` is also available (useful for inspecting the tiles) in case the original format is ``pbf``
- * TileJSON at ``/data/{mbtiles}.json``
+ * TileJSON at ``/data/{id}.json``
TileJSON arrays
===============
diff --git a/package-lock.json b/package-lock.json
index d892ff9..f27b567 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "tileserver-gl",
- "version": "4.8.0",
+ "version": "4.9.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "tileserver-gl",
- "version": "4.8.0",
+ "version": "4.9.0",
"license": "BSD-2-Clause",
"dependencies": {
"@mapbox/glyph-pbf-composite": "0.0.3",
diff --git a/package.json b/package.json
index 4c776ba..0088df7 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "tileserver-gl",
- "version": "4.8.0",
+ "version": "4.9.0",
"description": "Map tile server for JSON GL styles - vector and server side generated raster tiles",
"main": "src/main.js",
"bin": "src/main.js",
diff --git a/public/templates/data.tmpl b/public/templates/data.tmpl
index 7aa3ddf..06d4938 100644
--- a/public/templates/data.tmpl
+++ b/public/templates/data.tmpl
@@ -124,6 +124,7 @@
for (tile_url in tile_urls) {
L.tileLayer(tile_urls[tile_url], {
+ tileSize: 256,
minZoom: tile_minzoom,
maxZoom: tile_maxzoom,
attribution: tile_attribution
diff --git a/public/templates/index.tmpl b/public/templates/index.tmpl
index 250d056..0f0b6f0 100644
--- a/public/templates/index.tmpl
+++ b/public/templates/index.tmpl
@@ -38,7 +38,7 @@
GL Style
{{/if}}
{{#if serving_rendered}}
- {{#if serving_data}}| {{/if}}TileJSON
+ {{#if serving_data}}| {{/if}}TileJSON
{{/if}}
{{#if serving_rendered}}
| WMTS
diff --git a/public/templates/viewer.tmpl b/public/templates/viewer.tmpl
index 5f81eae..66abc24 100644
--- a/public/templates/viewer.tmpl
+++ b/public/templates/viewer.tmpl
@@ -77,11 +77,11 @@
selectThreshold: 5
}));
} else {
- var map = L.map('map', { zoomControl: false });
+ var map = L.map('map', { minZoom: 1, zoomControl: false });
new L.Control.Zoom({ position: 'topright' }).addTo(map);
var tile_urls = [], tile_attribution, tile_minzoom, tile_maxzoom;
- var url = '{{public_url}}styles/{{id}}.json' + keyParam;
+ var url = '{{public_url}}styles/512/{{id}}.json' + keyParam;
var req = new XMLHttpRequest();
req.overrideMimeType("application/json");
req.open('GET', url, true);
@@ -107,6 +107,7 @@
for (tile_url in tile_urls) {
L.tileLayer(tile_urls[tile_url], {
+ tileSize: 512,
minZoom: tile_minzoom,
maxZoom: tile_maxzoom,
attribution: tile_attribution
diff --git a/public/templates/wmts.tmpl b/public/templates/wmts.tmpl
index 0db7349..7a2a5dd 100644
--- a/public/templates/wmts.tmpl
+++ b/public/templates/wmts.tmpl
@@ -36,8 +36,8 @@
-
- {{name}}
+
+ {{name}}-256
{{id}}
-180 -85.051128779807
@@ -48,13 +48,30 @@
image/png
- GoogleMapsCompatible
+ GoogleMapsCompatible_256
-
-
- GoogleMapsCompatible
- GoogleMapsCompatible EPSG:3857
- GoogleMapsCompatible
+
+
+
+ {{name}}-512
+ {{id}}
+
+ -180 -85.051128779807
+ 180 85.051128779807
+
+
+ image/png
+
+ GoogleMapsCompatible_512
+
+
+
+
+ GoogleMapsCompatible_256
+ GoogleMapsCompatible_256 EPSG:3857
+ GoogleMapsCompatible_256
urn:ogc:def:crs:EPSG::3857
0
@@ -226,10 +243,189 @@
256
262144
262144
-
- WGS84
- WGS84 EPSG:4326
- WGS84
+
+
+
+ GoogleMapsCompatible_512
+ GoogleMapsCompatible_512 EPSG:3857
+ GoogleMapsCompatible_512
+ urn:ogc:def:crs:EPSG::3857
+
+ 0
+ 279541132.0143589
+ -20037508.34 20037508.34
+ 512
+ 512
+ 1
+ 1
+
+
+ 1
+ 139770566.0071794
+ -20037508.34 20037508.34
+ 512
+ 512
+ 2
+ 2
+
+
+ 2
+ 69885283.00358972
+ -20037508.34 20037508.34
+ 512
+ 512
+ 4
+ 4
+
+
+ 3
+ 34942641.501795
+ -20037508.34 20037508.34
+ 512
+ 512
+ 8
+ 8
+
+
+ 4
+ 17471320.750897
+ -20037508.34 20037508.34
+ 512
+ 512
+ 16
+ 16
+
+
+ 5
+ 8735660.3754487
+ -20037508.34 20037508.34
+ 512
+ 512
+ 32
+ 32
+
+
+ 6
+ 4367830.1877244
+ -20037508.34 20037508.34
+ 512
+ 512
+ 64
+ 64
+
+
+ 7
+ 2183915.0938622
+ -20037508.34 20037508.34
+ 512
+ 512
+ 128
+ 128
+
+
+ 8
+ 1091957.5469311
+ -20037508.34 20037508.34
+ 512
+ 512
+ 256
+ 256
+
+
+ 9
+ 545978.77346554
+ -20037508.34 20037508.34
+ 512
+ 512
+ 512
+ 512
+
+
+ 10
+ 272989.38673277
+ -20037508.34 20037508.34
+ 512
+ 512
+ 1024
+ 1024
+
+
+ 11
+ 136494.69336639
+ -20037508.34 20037508.34
+ 512
+ 512
+ 2048
+ 2048
+
+
+ 12
+ 68247.346683193
+ -20037508.34 20037508.34
+ 512
+ 512
+ 4096
+ 4096
+
+
+ 13
+ 34123.673341597
+ -20037508.34 20037508.34
+ 512
+ 512
+ 8192
+ 8192
+
+
+ 14
+ 17061.836670798
+ -20037508.34 20037508.34
+ 512
+ 512
+ 16384
+ 16384
+
+
+ 15
+ 8530.9183353991
+ -20037508.34 20037508.34
+ 512
+ 512
+ 32768
+ 32768
+
+
+ 16
+ 4265.4591676996
+ -20037508.34 20037508.34
+ 512
+ 512
+ 65536
+ 65536
+
+
+ 17
+ 2132.7295838498
+ -20037508.34 20037508.34
+ 512
+ 512
+ 131072
+ 131072
+
+
+ 18
+ 1066.364791924892
+ -20037508.34 20037508.34
+ 512
+ 512
+ 262144
+ 262144
+
+
+
+ WGS84_256
+ WGS84_256 EPSG:4326
+ WGS84_256
urn:ogc:def:crs:EPSG::4326
0
@@ -401,7 +597,185 @@
256
524288
262144
-
+
+
+
+ WGS84_512
+ WGS84_512 EPSG:4326
+ WGS84_512
+ urn:ogc:def:crs:EPSG::4326
+
+ 0
+ 139770566.00718
+ 90 -180
+ 512
+ 512
+ 2
+ 1
+
+
+ 1
+ 69885283.00359
+ 90 -180
+ 512
+ 512
+ 4
+ 2
+
+
+ 2
+ 34942641.501795
+ 90 -180
+ 512
+ 512
+ 8
+ 4
+
+
+ 3
+ 17471320.750897
+ 90 -180
+ 512
+ 512
+ 16
+ 8
+
+
+ 4
+ 8735660.3754487
+ 90 -180
+ 512
+ 512
+ 32
+ 16
+
+
+ 5
+ 4367830.1877244
+ 90 -180
+ 512
+ 512
+ 64
+ 32
+
+
+ 6
+ 2183915.0938622
+ 90 -180
+ 512
+ 512
+ 128
+ 64
+
+
+ 7
+ 1091957.5469311
+ 90 -180
+ 512
+ 512
+ 256
+ 128
+
+
+ 8
+ 545978.77346554
+ 90 -180
+ 512
+ 512
+ 512
+ 256
+
+
+ 9
+ 272989.38673277
+ 90 -180
+ 512
+ 512
+ 1024
+ 512
+
+
+ 10
+ 136494.69336639
+ 90 -180
+ 512
+ 512
+ 2048
+ 1024
+
+
+ 11
+ 68247.346683193
+ 90 -180
+ 512
+ 512
+ 4096
+ 2048
+
+
+ 12
+ 34123.673341597
+ 90 -180
+ 512
+ 512
+ 8192
+ 4096
+
+
+ 13
+ 17061.836670798
+ 90 -180
+ 512
+ 512
+ 16384
+ 8192
+
+
+ 14
+ 8530.9183353991
+ 90 -180
+ 512
+ 512
+ 32768
+ 16384
+
+
+ 15
+ 4265.4591676996
+ 90 -180
+ 512
+ 512
+ 65536
+ 32768
+
+
+ 16
+ 2132.7295838498
+ 90 -180
+ 512
+ 512
+ 131072
+ 65536
+
+
+ 17
+ 1066.3647919249
+ 90 -180
+ 512
+ 512
+ 262144
+ 131072
+
+
+ 18
+ 533.182
+ 90 -180
+ 512
+ 512
+ 524288
+ 262144
+
+
diff --git a/src/serve_data.js b/src/serve_data.js
index 4a95b1f..099b698 100644
--- a/src/serve_data.js
+++ b/src/serve_data.js
@@ -173,11 +173,13 @@ export const serve_data = {
if (!item) {
return res.sendStatus(404);
}
+ const tileSize = undefined;
const info = clone(item.tileJSON);
info.tiles = getTileUrls(
req,
info.tiles,
`data/${req.params.id}`,
+ tileSize,
info.format,
item.publicUrl,
{
diff --git a/src/serve_rendered.js b/src/serve_rendered.js
index ec590b6..0068ac0 100644
--- a/src/serve_rendered.js
+++ b/src/serve_rendered.js
@@ -532,7 +532,7 @@ export const serve_rendered = {
const app = express().disable('x-powered-by');
app.get(
- `/:id/:z(\\d+)/:x(\\d+)/:y(\\d+):scale(${scalePattern})?.:format([\\w]+)`,
+ `/:id/(:tileSize(256|512)/)?:z(\\d+)/:x(\\d+)/:y(\\d+):scale(${scalePattern})?.:format([\\w]+)`,
(req, res, next) => {
const item = repo[req.params.id];
if (!item) {
@@ -552,6 +552,8 @@ export const serve_rendered = {
const y = req.params.y | 0;
const scale = getScale(req.params.scale);
const format = req.params.format;
+ const tileSize = parseInt(req.params.tileSize, 10) || 256;
+
if (
z < 0 ||
x < 0 ||
@@ -562,11 +564,10 @@ export const serve_rendered = {
) {
return res.status(404).send('Out of bounds');
}
- const tileSize = 256;
const tileCenter = mercator.ll(
[
- ((x + 0.5) / (1 << z)) * (256 << z),
- ((y + 0.5) / (1 << z)) * (256 << z),
+ ((x + 0.5) / (1 << z)) * (tileSize << z),
+ ((y + 0.5) / (1 << z)) * (tileSize << z),
],
z,
);
@@ -821,16 +822,18 @@ export const serve_rendered = {
);
}
- app.get('/:id.json', (req, res, next) => {
+ app.get('/(:tileSize(256|512)/)?:id.json', (req, res, next) => {
const item = repo[req.params.id];
if (!item) {
return res.sendStatus(404);
}
+ const tileSize = parseInt(req.params.tileSize, 10) || undefined;
const info = clone(item.tileJSON);
info.tiles = getTileUrls(
req,
info.tiles,
`styles/${req.params.id}`,
+ tileSize,
info.format,
item.publicUrl,
);
diff --git a/src/server.js b/src/server.js
index 2b382a0..5e7019f 100644
--- a/src/server.js
+++ b/src/server.js
@@ -356,6 +356,7 @@ function start(opts) {
const addTileJSONs = (arr, req, type) => {
for (const id of Object.keys(serving[type])) {
+ const tileSize = 256;
const info = clone(serving[type][id].tileJSON);
let path = '';
if (type === 'rendered') {
@@ -367,6 +368,7 @@ function start(opts) {
req,
info.tiles,
path,
+ tileSize,
info.format,
opts.publicUrl,
{
@@ -454,20 +456,19 @@ function start(opts) {
if (style.serving_rendered) {
const { center } = style.serving_rendered.tileJSON;
if (center) {
- style.viewer_hash = `#${center[2]}/${center[1].toFixed(
- 5,
- )}/${center[0].toFixed(5)}`;
+ style.viewer_hash = `#${center[2]}/${center[1].toFixed(5)}/${center[0].toFixed(5)}`;
const centerPx = mercator.px([center[0], center[1]], center[2]);
- style.thumbnail = `${center[2]}/${Math.floor(
- centerPx[0] / 256,
- )}/${Math.floor(centerPx[1] / 256)}.png`;
+ // Set thumbnail default size to be 256px x 256px
+ style.thumbnail = `${center[2]}/${Math.floor(centerPx[0] / 256)}/${Math.floor(centerPx[1] / 256)}.png`;
}
+ const tileSize = 512;
style.xyz_link = getTileUrls(
req,
style.serving_rendered.tileJSON.tiles,
`styles/${id}`,
+ tileSize,
style.serving_rendered.tileJSON.format,
opts.publicUrl,
)[0];
@@ -493,22 +494,23 @@ function start(opts) {
if (!data.is_vector) {
if (center) {
const centerPx = mercator.px([center[0], center[1]], center[2]);
- data.thumbnail = `${center[2]}/${Math.floor(
- centerPx[0] / 256,
- )}/${Math.floor(centerPx[1] / 256)}.${tileJSON.format}`;
+ data.thumbnail = `${center[2]}/${Math.floor(centerPx[0] / 256)}/${Math.floor(centerPx[1] / 256)}.${tileJSON.format}`;
}
-
- data.xyz_link = getTileUrls(
- req,
- tileJSON.tiles,
- `data/${id}`,
- tileJSON.format,
- opts.publicUrl,
- {
- pbf: options.pbfAlias,
- },
- )[0];
}
+
+ const tileSize = undefined;
+ data.xyz_link = getTileUrls(
+ req,
+ tileJSON.tiles,
+ `data/${id}`,
+ tileSize,
+ tileJSON.format,
+ opts.publicUrl,
+ {
+ pbf: options.pbfAlias,
+ },
+ )[0];
+
if (data.filesize) {
let suffix = 'kB';
let size = parseInt(tileJSON.filesize, 10) / 1024;
diff --git a/src/utils.js b/src/utils.js
index 4fdf43a..2a516bc 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -26,7 +26,15 @@ export const getPublicUrl = (publicUrl, req) => {
return getUrlObject(req).toString();
};
-export const getTileUrls = (req, domains, path, format, publicUrl, aliases) => {
+export const getTileUrls = (
+ req,
+ domains,
+ path,
+ tileSize,
+ format,
+ publicUrl,
+ aliases,
+) => {
const urlObject = getUrlObject(req);
if (domains) {
if (domains.constructor === String && domains.length > 0) {
@@ -67,15 +75,20 @@ export const getTileUrls = (req, domains, path, format, publicUrl, aliases) => {
format = aliases[format];
}
+ let tileParams = `{z}/{x}/{y}`;
+ if (tileSize && ['png', 'jpg', 'jpeg', 'webp'].includes(format)) {
+ tileParams = `${tileSize}/{z}/{x}/{y}`;
+ }
+
const uris = [];
if (!publicUrl) {
for (const domain of domains) {
uris.push(
- `${req.protocol}://${domain}/${path}/{z}/{x}/{y}.${format}${query}`,
+ `${req.protocol}://${domain}/${path}/${tileParams}.${format}${query}`,
);
}
} else {
- uris.push(`${publicUrl}${path}/{z}/{x}/{y}.${format}${query}`);
+ uris.push(`${publicUrl}${path}/${tileParams}.${format}${query}`);
}
return uris;
diff --git a/test/tiles_rendered.js b/test/tiles_rendered.js
index fb2fcc5..6f7f438 100644
--- a/test/tiles_rendered.js
+++ b/test/tiles_rendered.js
@@ -1,8 +1,30 @@
-const testTile = function (prefix, z, x, y, format, status, scale, type) {
+var testTile = function (
+ prefix,
+ tileSize = 256,
+ z,
+ x,
+ y,
+ format,
+ status,
+ scale,
+ type,
+) {
if (scale) y += '@' + scale + 'x';
- const path = '/styles/' + prefix + '/' + z + '/' + x + '/' + y + '.' + format;
+ var path =
+ '/styles/' +
+ prefix +
+ '/' +
+ tileSize +
+ '/' +
+ z +
+ '/' +
+ x +
+ '/' +
+ y +
+ '.' +
+ format;
it(path + ' returns ' + status, function (done) {
- const test = supertest(app).get(path);
+ var test = supertest(app).get(path);
test.expect(status);
if (type) test.expect('Content-Type', type);
test.end(done);
@@ -14,33 +36,40 @@ const prefix = 'test-style';
describe('Raster tiles', function () {
describe('valid requests', function () {
describe('various formats', function () {
- testTile(prefix, 0, 0, 0, 'png', 200, undefined, /image\/png/);
- testTile(prefix, 0, 0, 0, 'jpg', 200, undefined, /image\/jpeg/);
- testTile(prefix, 0, 0, 0, 'jpeg', 200, undefined, /image\/jpeg/);
- testTile(prefix, 0, 0, 0, 'webp', 200, undefined, /image\/webp/);
+ testTile(prefix, 256, 0, 0, 0, 'png', 200, undefined, /image\/png/);
+ testTile(prefix, 512, 0, 0, 0, 'png', 200, undefined, /image\/png/);
+ testTile(prefix, 256, 0, 0, 0, 'jpg', 200, undefined, /image\/jpeg/);
+ testTile(prefix, 512, 0, 0, 0, 'jpg', 200, undefined, /image\/jpeg/);
+ testTile(prefix, 256, 0, 0, 0, 'jpeg', 200, undefined, /image\/jpeg/);
+ testTile(prefix, 512, 0, 0, 0, 'jpeg', 200, undefined, /image\/jpeg/);
+ testTile(prefix, 256, 0, 0, 0, 'webp', 200, undefined, /image\/webp/);
+ testTile(prefix, 512, 0, 0, 0, 'webp', 200, undefined, /image\/webp/);
});
describe('different coordinates and scales', function () {
- testTile(prefix, 1, 1, 1, 'png', 200);
-
- testTile(prefix, 0, 0, 0, 'png', 200, 2);
- testTile(prefix, 0, 0, 0, 'png', 200, 3);
- testTile(prefix, 2, 1, 1, 'png', 200, 3);
+ testTile(prefix, 256, 1, 0, 0, 'png', 200);
+ testTile(prefix, 512, 1, 0, 0, 'png', 200);
+ testTile(prefix, 256, 0, 0, 0, 'png', 200, 2);
+ testTile(prefix, 512, 0, 0, 0, 'png', 200, 2);
+ testTile(prefix, 256, 0, 0, 0, 'png', 200, 3);
+ testTile(prefix, 512, 0, 0, 0, 'png', 200, 3);
+ testTile(prefix, 256, 2, 1, 1, 'png', 200, 3);
+ testTile(prefix, 512, 2, 1, 1, 'png', 200, 3);
});
});
describe('invalid requests return 4xx', function () {
- testTile('non_existent', 0, 0, 0, 'png', 404);
- testTile(prefix, -1, 0, 0, 'png', 404);
- testTile(prefix, 25, 0, 0, 'png', 404);
- testTile(prefix, 0, 1, 0, 'png', 404);
- testTile(prefix, 0, 0, 1, 'png', 404);
- testTile(prefix, 0, 0, 0, 'gif', 400);
- testTile(prefix, 0, 0, 0, 'pbf', 400);
+ testTile('non_existent', 256, 0, 0, 0, 'png', 404);
+ testTile(prefix, 256, -1, 0, 0, 'png', 404);
+ testTile(prefix, 256, 25, 0, 0, 'png', 404);
+ testTile(prefix, 256, 0, 1, 0, 'png', 404);
+ testTile(prefix, 256, 0, 0, 1, 'png', 404);
+ testTile(prefix, 256, 0, 0, 0, 'gif', 400);
+ testTile(prefix, 256, 0, 0, 0, 'pbf', 400);
- testTile(prefix, 0, 0, 0, 'png', 404, 1);
- testTile(prefix, 0, 0, 0, 'png', 404, 5);
+ testTile(prefix, 256, 0, 0, 0, 'png', 404, 1);
+ testTile(prefix, 256, 0, 0, 0, 'png', 404, 5);
- // testTile('hybrid', 0, 0, 0, 'png', 404); //TODO: test this
+ testTile(prefix, 300, 0, 0, 0, 'png', 404);
});
});