From e6bfccb0e15ac0c8fa5d94d82d29185f728e7451 Mon Sep 17 00:00:00 2001 From: Sam Willis Date: Mon, 4 Jan 2016 19:35:00 +0000 Subject: [PATCH] Allow arbitrary scale to be specified, e.g. @4.2x (particularly useful for static api) This allows an arbitrary scale to be specified rather than just @1x, @2x, or @3x (actually @1x cant currently be explicitly be set). This is particularly useful for the static api when using it to generate maps for printing. This also implements a cache of 100 sourceURIs (randomly evicted) so that it isn't re-creating the sourceURI for each tile drawn. If we were to save the sourceURIs permanently the list could grow infinitely over time. scales 1, 2 and 3 are never evicted from the cache. --- lib/app.js | 46 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/lib/app.js b/lib/app.js index f66b6e1..07a0242 100644 --- a/lib/app.js +++ b/lib/app.js @@ -18,7 +18,7 @@ var tessera = require("./index"); debug = debug("tessera"); var FLOAT_PATTERN = "[+-]?(?:\\d+|\\d+\.?\\d+)"; -var SCALE_PATTERN = "@[23]x"; +var SCALE_PATTERN = "@(?:\\d+|\\d+\.?\\d+)x"; // TODO a more complete implementation of this exists...somewhere var getExtension = function(format) { @@ -33,7 +33,7 @@ var getExtension = function(format) { }; var getScale = function(scale) { - return (scale || "@1x").slice(1, 2) | 0; + return parseFloat((scale || "@1x").slice(1)); }; var normalizeHeaders = function(headers) { @@ -121,18 +121,50 @@ module.exports = function(tilelive, options) { 1: uri }; - [2, 3].forEach(function(scale) { - var retinaURI = clone(uri); + // We cache 100 sourceURIs, randomly evict when over limit + var sourceURIsCacheKeys = []; + var sourceURIsCacheSizeLimit = 100; + + var makeScaledSourceURI = function(scale, permanent) { + var retinaURI = clone(uri), + randomIndex; retinaURI.query.scale = scale; // explicitly tell tilelive-mapnik to use larger tiles - retinaURI.query.tileSize = scale * 256; - + retinaURI.query.tileSize = (scale * 256) | 0; + + if (!permanent) { + // Save the URI to the uri cache, but limit its size + while (sourceURIsCacheKeys.length > sourceURIsCacheSizeLimit) { + // Delete random sourceURI + randomIndex = Math.floor(Math.random()*sourceURIsCacheKeys.length); + delete sourceURIs[sourceURIsCacheKeys[randomIndex]]; + sourceURIsCacheKeys.splice(randomIndex, 1); + } + sourceURIsCacheKeys.push(scale); + } sourceURIs[scale] = retinaURI; + + return retinaURI; + }; + + // make scaled source urls for retina @2x and @3x + [2, 3].forEach(function(scale) { + // these are not added to sourceURIsCacheKeys so that they are not ever removed from the cache + makeScaledSourceURI(scale, true); }); + var getSourceURI = function(scale) { + if (scale in sourceURIs) { + return sourceURIs[scale]; + } else { + // scaled uri does not exist so we make one + return makeScaledSourceURI(scale); + } + }; + var getTile = function(z, x, y, scale, format, callback) { - var sourceURI = sourceURIs[scale], + var sourceURI = getSourceURI(scale), params = { tile: { zoom: z,