diff --git a/docs/endpoints.rst b/docs/endpoints.rst index f7bca27..7e8893d 100644 --- a/docs/endpoints.rst +++ b/docs/endpoints.rst @@ -49,6 +49,10 @@ Source data =========== * Source data are served at ``/data/{mbtiles}/{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 arrays diff --git a/package.json b/package.json index 8b10ae5..f8d5995 100644 --- a/package.json +++ b/package.json @@ -31,10 +31,12 @@ "morgan": "1.7.0", "node-pngquant-native": "1.0.4", "nomnom": "1.8.1", + "pbf": "3.0.5", "request": "2.75.0", "sharp": "0.16.0", "sphericalmercator": "1.0.5", - "tileserver-gl-styles": "0.3.0" + "tileserver-gl-styles": "0.3.0", + "vector-tile": "1.3.0" }, "optionalDependencies": { "tileshrink-gl": "./plugins/tileshrink-gl" diff --git a/src/serve_data.js b/src/serve_data.js index 0fc6b17..74ce946 100644 --- a/src/serve_data.js +++ b/src/serve_data.js @@ -1,11 +1,14 @@ 'use strict'; var fs = require('fs'), - path = require('path'); + path = require('path'), + zlib = require('zlib'); var clone = require('clone'), express = require('express'), - mbtiles = require('mbtiles'); + mbtiles = require('mbtiles'), + pbf = require('pbf'), + VectorTile = require('vector-tile').VectorTile; var tileshrinkGl; try { @@ -53,7 +56,8 @@ module.exports = function(options, repo, params, id, styles) { var z = req.params.z | 0, x = req.params.x | 0, y = req.params.y | 0; - if (req.params.format != tileJSON.format) { + if (req.params.format != tileJSON.format && + !(req.params.format == 'geojson' && tileJSON.format == 'pbf')) { return res.status(404).send('Invalid format'); } if (z < tileJSON.minzoom || 0 || x < 0 || y < 0 || @@ -73,9 +77,6 @@ module.exports = function(options, repo, params, id, styles) { return res.status(404).send('Not found'); } else { if (tileJSON['format'] == 'pbf') { - headers['Content-Type'] = 'application/x-protobuf'; - headers['Content-Encoding'] = 'gzip'; - var style = req.query.style; if (style && tileshrinkGl) { if (!shrinkers[style]) { @@ -94,15 +95,37 @@ module.exports = function(options, repo, params, id, styles) { } } if (shrinkers[style]) { - data = shrinkers[style](data, z, tileJSON.maxzoom); + data = shrinkers[style](zlib.unzipSync(data), z, tileJSON.maxzoom); //console.log(shrinkers[style].getStats()); } } } + if (req.params.format == 'pbf') { + headers['Content-Type'] = 'application/x-protobuf'; + } else if (req.params.format == 'geojson') { + headers['Content-Type'] = 'application/json'; + + var tile = new VectorTile(new pbf(zlib.unzipSync(data))); + var geojson = { + "type": "FeatureCollection", + "features": [] + }; + for (var layerName in tile.layers) { + var layer = tile.layers[layerName]; + for (var i = 0; i < layer.length; i++) { + var feature = layer.feature(i); + var featureGeoJSON = feature.toGeoJSON(x, y, z); + featureGeoJSON.properties.layer = layerName; + geojson.features.push(featureGeoJSON); + } + } + data = JSON.stringify(geojson); + } delete headers['ETag']; // do not trust the tile ETag -- regenerate + headers['Content-Encoding'] = 'gzip'; res.set(headers); - return res.status(200).send(data); + return res.status(200).send(zlib.gzipSync(data)); } } });