Optimize static endpoints (remove abaculus dependency) (closes #2)

This commit is contained in:
Petr Sloup 2016-03-03 16:51:52 +01:00
parent e39ae90bc8
commit 4f9b8c1a1b
2 changed files with 44 additions and 119 deletions

View file

@ -13,9 +13,7 @@
"dependencies": { "dependencies": {
"async": "1.5.2", "async": "1.5.2",
"async-lock": "0.3.8", "async-lock": "0.3.8",
"abaculus": "1.3.0",
"clone": "1.0.2", "clone": "1.0.2",
"concat-stream": "1.5.1",
"cors": "2.7.1", "cors": "2.7.1",
"express": "4.13.4", "express": "4.13.4",
"mapbox-gl-native": "3.0.2-earcut", "mapbox-gl-native": "3.0.2-earcut",

View file

@ -8,9 +8,7 @@ var async = require('async'),
util = require('util'), util = require('util'),
zlib = require('zlib'); zlib = require('zlib');
var abaculus = require('abaculus'), var clone = require('clone'),
clone = require('clone'),
concat = require('concat-stream'),
express = require('express'), express = require('express'),
mercator = new (require('sphericalmercator'))(), mercator = new (require('sphericalmercator'))(),
mbgl = require('mapbox-gl-native'), mbgl = require('mapbox-gl-native'),
@ -180,24 +178,16 @@ module.exports = function(maps, options, prefix) {
.replace('{y}', ':y(\\d+)') .replace('{y}', ':y(\\d+)')
.replace('{format}', ':format([\\w\\.]+)'); .replace('{format}', ':format([\\w\\.]+)');
var getTile = function(z, x, y, scale, format, callback) { var respondImage = function(z, lon, lat, width, height, scale, format, res, next) {
if (format == 'png' || format == 'webp') { if (format == 'png' || format == 'webp') {
} else if (format == 'jpg' || format == 'jpeg') { } else if (format == 'jpg' || format == 'jpeg') {
format = 'jpeg'; format = 'jpeg';
} else { } else {
return callback(null, null); return res.status(404).send('Invalid format');
} }
var mbglZ = Math.max(0, z - 1); var mbglZ = Math.max(0, z - 1);
var tileSize = 256;
var tileCenter = mercator.ll([
((x + 0.5) / (1 << z)) * (256 << z),
((y + 0.5) / (1 << z)) * (256 << z)
], z);
var renderer = map.renderers[scale]; var renderer = map.renderers[scale];
var params = { var params = {
/* /*
debug: { debug: {
@ -208,9 +198,9 @@ module.exports = function(maps, options, prefix) {
}, },
*/ */
zoom: mbglZ, zoom: mbglZ,
center: tileCenter, center: [lon, lat],
width: tileSize, width: width,
height: tileSize height: height
}; };
if (z == 0) { if (z == 0) {
params.width *= 2; params.width *= 2;
@ -221,51 +211,32 @@ module.exports = function(maps, options, prefix) {
done(); done();
if (err) console.log(err); if (err) console.log(err);
if (z == 0) { var image = sharp(data, {
// HACK: when serving zoom 0, resize the 0 tile from 512 to 256
var data_ = clone(data);
var dataSize_ = 2 * tileSize * scale;
var newSize_ = dataSize_ / 2;
data = new Buffer(4 * newSize_ * newSize_);
for (var x = 0; x < newSize_; x++) {
for (var y = 0; y < newSize_; y++) {
for (var b = 0; b < 4; b++) {
data[4 * (x * newSize_ + y) + b] = (
data_[4 * (2 * x * dataSize_ + 2 * y) + b] +
data_[4 * (2 * x * dataSize_ + (2 * y + 1)) + b] +
data_[4 * ((2 * x + 1) * dataSize_ + 2 * y) + b] +
data_[4 * ((2 * x + 1) * dataSize_ + (2 * y + 1)) + b]
) / 4;
}
}
}
}
sharp(data, {
raw: { raw: {
width: tileSize * scale, width: params.width * scale,
height: tileSize * scale, height: params.height * scale,
channels: 4 channels: 4
} }
}).toFormat(format) });
if (z == 0) {
// HACK: when serving zoom 0, resize the 0 tile from 512 to 256
image.resize(width * scale, height * scale);
}
image.toFormat(format)
.compressionLevel(9) .compressionLevel(9)
.toBuffer(function(err, buffer, info) { .toBuffer(function(err, buffer, info) {
if (!buffer) { if (!buffer) {
return callback(null, null); return res.status(404).send('Not found');
} }
var md5 = crypto.createHash('md5').update(buffer).digest('base64'); var md5 = crypto.createHash('md5').update(buffer).digest('base64');
var headers = { res.set({
'content-md5': md5, 'content-md5': md5,
'content-type': 'image/' + format 'content-type': 'image/' + format
}; });
/* return res.status(200).send(buffer);
if (format === 'pbf') {
headers['content-type'] = 'application/x-protobuf';
headers['content-encoding'] = 'gzip';
}
*/
return callback(null, buffer, headers);
}); });
}); });
}); });
@ -277,59 +248,15 @@ module.exports = function(maps, options, prefix) {
y = req.params.y | 0, y = req.params.y | 0,
scale = getScale(req.params.scale), scale = getScale(req.params.scale),
format = req.params.format; format = req.params.format;
return getTile(z, x, y, scale, format, function(err, data, headers) { var tileSize = 256;
if (err) { var tileCenter = mercator.ll([
return next(err); ((x + 0.5) / (1 << z)) * (256 << z),
} ((y + 0.5) / (1 << z)) * (256 << z)
if (headers) { ], z);
res.set(headers); return respondImage(z, tileCenter[0], tileCenter[1], tileSize, tileSize,
} scale, format, res, next);
if (data == null) {
return res.status(404).send('Not found');
} else {
return res.status(200).send(data);
}
}, res, next);
}); });
var processStaticMap = function(areaParams, req, res, next) {
var scale = getScale(req.params.scale),
format = req.params.format,
params = {
zoom: req.params.z | 0,
scale: scale,
bbox: areaParams.bbox,
center: areaParams.center,
format: format,
limit: 4097,
getTile: function(z, x, y, callback) {
return getTile(z, x, y, scale, format, function(err, data, headers) {
if (!err && data == null) {
err = new Error('Not found');
err.status = 404;
}
callback(err, data, headers);
});
}
};
try {
return abaculus(params, function(err, data, headers) {
if (err && !err.status) {
return next(err);
}
if (headers) {
headers['Content-Type'] = 'image/' + format;
res.set(headers);
}
res.status((err && err.status) || 200);
return res.send((err && err.message) || data);
});
} catch (e) {
res.status(400);
return res.send(e.message);
}
};
var staticPattern = var staticPattern =
'/static/%s:scale(' + SCALE_PATTERN + ')?\.:format([\\w\\.]+)'; '/static/%s:scale(' + SCALE_PATTERN + ')?\.:format([\\w\\.]+)';
@ -338,14 +265,14 @@ module.exports = function(maps, options, prefix) {
FLOAT_PATTERN, FLOAT_PATTERN); FLOAT_PATTERN, FLOAT_PATTERN);
app.get(util.format(staticPattern, centerPattern), function(req, res, next) { app.get(util.format(staticPattern, centerPattern), function(req, res, next) {
return processStaticMap({ var z = req.params.z | 0,
center: { x = +req.params.lon,
x: +req.params.lon, y = +req.params.lat,
y: +req.params.lat, w = req.params.width | 0,
w: req.params.width | 0, h = req.params.height | 0,
h: req.params.height | 0 scale = getScale(req.params.scale),
} format = req.params.format;
}, req, res, next); return respondImage(z, x, y, w, h, scale, format, res, next);
}); });
var boundsPattern = var boundsPattern =
@ -353,14 +280,14 @@ module.exports = function(maps, options, prefix) {
FLOAT_PATTERN, FLOAT_PATTERN, FLOAT_PATTERN, FLOAT_PATTERN); FLOAT_PATTERN, FLOAT_PATTERN, FLOAT_PATTERN, FLOAT_PATTERN);
app.get(util.format(staticPattern, boundsPattern), function(req, res, next) { app.get(util.format(staticPattern, boundsPattern), function(req, res, next) {
return processStaticMap({ var z = req.params.z | 0,
bbox: [ x = ((+req.params.minx) + (+req.params.maxx)) / 2,
+req.params.minx, y = ((+req.params.miny) + (+req.params.maxy)) / 2,
+req.params.miny, w = req.params.width | 0,
+req.params.maxx, h = req.params.height | 0,
+req.params.maxy scale = getScale(req.params.scale),
] format = req.params.format;
}, req, res, next); return respondImage(z, x, y, w, h, scale, format, res, next);
}); });
app.get('/index.json', function(req, res, next) { app.get('/index.json', function(req, res, next) {