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:
Damian Krzeminski 2017-04-12 10:32:31 -07:00
parent 49a779970e
commit 07e03deedb

View file

@ -18,6 +18,24 @@ try {
var utils = require('./utils'); 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) { module.exports = function(options, repo, params, id, styles) {
var app = express().disable('x-powered-by'); var app = express().disable('x-powered-by');
@ -28,10 +46,29 @@ module.exports = function(options, repo, params, id, styles) {
var shrinkers = {}; 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; repo[id] = tileJSON;
var mbtilesFileStats = fs.statSync(mbtilesFile); var mbtilesFileStats = fs.statSync(mbtilesFile);
if (!mbtilesFileStats.isFile() || mbtilesFileStats.size == 0) { if (!mbtilesFileStats.isFile() || mbtilesFileStats.size === 0) {
throw Error('Not valid MBTiles file: ' + mbtilesFile); throw Error('Not valid MBTiles file: ' + mbtilesFile);
} }
var source = new mbtiles(mbtilesFile, function(err) { 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.]+)'; 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, var z = req.params.z | 0,
x = req.params.x | 0, x = req.params.x | 0,
y = req.params.y | 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)) { x >= Math.pow(2, z) || y >= Math.pow(2, z)) {
return res.status(404).send('Out of bounds'); 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 (err) {
if (/does not exist/.test(err.message)) { var status = /does not exist/.test(err.message) ? 404 : 500;
return res.status(404).send(err.message); return res.status(status).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);
}
} }
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) { app.get('/' + id + '.json', function(req, res, next) {
var info = clone(tileJSON); var info = clone(tileJSON);