simplify serve_data code
split response function into standard express middleware stack - each middleware is now responsible for a single task only
This commit is contained in:
parent
49a779970e
commit
07e03deedb
1 changed files with 138 additions and 80 deletions
|
@ -18,6 +18,24 @@ try {
|
|||
|
||||
var utils = require('./utils');
|
||||
|
||||
function isGzipped(data) {
|
||||
return data[0] === 0x1f && data[1] === 0x8b;
|
||||
}
|
||||
|
||||
function unzipTile(tile) {
|
||||
if (tile.isGzipped) {
|
||||
tile.data = zlib.gunzipSync(tile.data);
|
||||
tile.isGzipped = false;
|
||||
}
|
||||
}
|
||||
|
||||
function zipTile(tile) {
|
||||
if (!tile.isGzipped) {
|
||||
tile.data = zlib.gzipSync(tile.data);
|
||||
tile.isGzipped = true;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = function(options, repo, params, id, styles) {
|
||||
var app = express().disable('x-powered-by');
|
||||
|
||||
|
@ -28,10 +46,29 @@ module.exports = function(options, repo, params, id, styles) {
|
|||
|
||||
var shrinkers = {};
|
||||
|
||||
function lookupShrinker(style) {
|
||||
if (shrinkers[style]) {
|
||||
return shrinkers[style];
|
||||
}
|
||||
var styleJSON = styles[style];
|
||||
if (!styleJSON) {
|
||||
return;
|
||||
}
|
||||
var sourceName = null;
|
||||
for (var sourceName_ in styleJSON.sources) {
|
||||
var source = styleJSON.sources[sourceName_];
|
||||
if (source && source.type == 'vector' && source.url.endsWith('/' + id + '.json')) {
|
||||
sourceName = sourceName_;
|
||||
}
|
||||
}
|
||||
shrinkers[style] = tileshrinkGl.createPBFShrinker(styleJSON, sourceName);
|
||||
return shrinkers[style];
|
||||
}
|
||||
|
||||
repo[id] = tileJSON;
|
||||
|
||||
var mbtilesFileStats = fs.statSync(mbtilesFile);
|
||||
if (!mbtilesFileStats.isFile() || mbtilesFileStats.size == 0) {
|
||||
if (!mbtilesFileStats.isFile() || mbtilesFileStats.size === 0) {
|
||||
throw Error('Not valid MBTiles file: ' + mbtilesFile);
|
||||
}
|
||||
var source = new mbtiles(mbtilesFile, function(err) {
|
||||
|
@ -53,7 +90,7 @@ module.exports = function(options, repo, params, id, styles) {
|
|||
|
||||
var tilePattern = '/' + id + '/:z(\\d+)/:x(\\d+)/:y(\\d+).:format([\\w.]+)';
|
||||
|
||||
app.get(tilePattern, function(req, res, next) {
|
||||
function checkParams(req, res, next) {
|
||||
var z = req.params.z | 0,
|
||||
x = req.params.x | 0,
|
||||
y = req.params.y | 0;
|
||||
|
@ -70,87 +107,108 @@ module.exports = function(options, repo, params, id, styles) {
|
|||
x >= Math.pow(2, z) || y >= Math.pow(2, z)) {
|
||||
return res.status(404).send('Out of bounds');
|
||||
}
|
||||
source.getTile(z, x, y, function(err, data, headers) {
|
||||
req.params.format = format;
|
||||
req.params.z = z;
|
||||
req.params.x = x;
|
||||
req.params.y = y;
|
||||
next();
|
||||
}
|
||||
|
||||
function getTile(req, res, next) {
|
||||
var p = req.params;
|
||||
source.getTile(p.z, p.x, p.y, function(err, data, headers) {
|
||||
if (err) {
|
||||
if (/does not exist/.test(err.message)) {
|
||||
return res.status(404).send(err.message);
|
||||
} else {
|
||||
return res.status(500).send(err.message);
|
||||
}
|
||||
} else {
|
||||
if (data == null) {
|
||||
return res.status(404).send('Not found');
|
||||
} else {
|
||||
if (tileJSON['format'] == 'pbf') {
|
||||
var isGzipped = data.slice(0,2).indexOf(
|
||||
new Buffer([0x1f, 0x8b])) === 0;
|
||||
var style = req.query.style;
|
||||
if (style && tileshrinkGl) {
|
||||
if (!shrinkers[style]) {
|
||||
var styleJSON = styles[style];
|
||||
if (styleJSON) {
|
||||
var sourceName = null;
|
||||
for (var sourceName_ in styleJSON.sources) {
|
||||
var source = styleJSON.sources[sourceName_];
|
||||
if (source &&
|
||||
source.type == 'vector' &&
|
||||
source.url.endsWith('/' + id + '.json')) {
|
||||
sourceName = sourceName_;
|
||||
}
|
||||
}
|
||||
shrinkers[style] = tileshrinkGl.createPBFShrinker(styleJSON, sourceName);
|
||||
}
|
||||
}
|
||||
if (shrinkers[style]) {
|
||||
if (isGzipped) {
|
||||
data = zlib.unzipSync(data);
|
||||
isGzipped = false;
|
||||
}
|
||||
data = shrinkers[style](data, z, tileJSON.maxzoom);
|
||||
//console.log(shrinkers[style].getStats());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (format == 'pbf') {
|
||||
headers['Content-Type'] = 'application/x-protobuf';
|
||||
} else if (format == 'geojson') {
|
||||
headers['Content-Type'] = 'application/json';
|
||||
|
||||
if (isGzipped) {
|
||||
data = zlib.unzipSync(data);
|
||||
isGzipped = false;
|
||||
}
|
||||
|
||||
var tile = new VectorTile(new pbf(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);
|
||||
|
||||
if (!isGzipped) {
|
||||
data = zlib.gzipSync(data);
|
||||
isGzipped = true;
|
||||
}
|
||||
|
||||
return res.status(200).send(data);
|
||||
}
|
||||
var status = /does not exist/.test(err.message) ? 404 : 500;
|
||||
return res.status(status).send(err.message);
|
||||
}
|
||||
if (data == null) {
|
||||
return res.status(404).send('Not found');
|
||||
}
|
||||
req.tile = {
|
||||
data: data,
|
||||
headers: headers,
|
||||
contentType: 'application/x-protobuf',
|
||||
isGzipped: isGzipped(data)
|
||||
};
|
||||
next();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function shrinkTile(req, res, next) {
|
||||
if (!tileshrinkGl) {
|
||||
return next();
|
||||
}
|
||||
if (tileJSON['format'] !== 'pbf') {
|
||||
return next();
|
||||
}
|
||||
var style = req.query.style;
|
||||
if (!style) {
|
||||
return next();
|
||||
}
|
||||
var tile = req.tile;
|
||||
var shrinker = lookupShrinker(style);
|
||||
if (shrinker) {
|
||||
unzipTile(tile);
|
||||
tile.data = shrinker(tile.data, req.params.z, tileJSON.maxzoom);
|
||||
//console.log(shrinker.getStats());
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
function formatTile(req, res, next) {
|
||||
var format = req.params.format;
|
||||
if (format !== 'geojson') {
|
||||
return next();
|
||||
}
|
||||
var tile = req.tile;
|
||||
var x = req.params.x,
|
||||
y = req.params.y,
|
||||
z = req.params.z;
|
||||
|
||||
tile.contentType = 'application/json';
|
||||
unzipTile(tile);
|
||||
|
||||
var vectorTile = new VectorTile(new pbf(tile.data));
|
||||
var geojson = {
|
||||
"type": "FeatureCollection",
|
||||
"features": []
|
||||
};
|
||||
|
||||
for (var layerName in vectorTile.layers) {
|
||||
var layer = vectorTile.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);
|
||||
}
|
||||
}
|
||||
|
||||
tile.data = JSON.stringify(geojson);
|
||||
|
||||
next();
|
||||
}
|
||||
|
||||
function sendTile(req, res) {
|
||||
var headers = req.tile.headers;
|
||||
|
||||
delete headers['ETag']; // do not trust the tile ETag -- regenerate
|
||||
headers['Content-Type'] = req.tile.contentType;
|
||||
headers['Content-Encoding'] = 'gzip';
|
||||
res.set(headers);
|
||||
|
||||
zipTile(req.tile);
|
||||
|
||||
return res.status(200).send(req.tile.data);
|
||||
}
|
||||
|
||||
app.get(tilePattern,
|
||||
checkParams,
|
||||
getTile,
|
||||
shrinkTile,
|
||||
formatTile,
|
||||
sendTile
|
||||
);
|
||||
|
||||
app.get('/' + id + '.json', function(req, res, next) {
|
||||
var info = clone(tileJSON);
|
||||
|
|
Loading…
Reference in a new issue