From bf40b240d3d796241fa18f7404e52cab09d9bdb0 Mon Sep 17 00:00:00 2001 From: Raul Ochoa Date: Wed, 3 Jan 2018 16:54:45 +0100 Subject: [PATCH] Return tilejson in metadata It returns tilejson for each individual layer and also for all vector and raster layers. --- lib/cartodb/controllers/map.js | 69 +++++++++++ test/acceptance/tilejson.js | 204 +++++++++++++++++++++++++++++++++ 2 files changed, 273 insertions(+) create mode 100644 test/acceptance/tilejson.js diff --git a/lib/cartodb/controllers/map.js b/lib/cartodb/controllers/map.js index 185a4f8d..3e17f7bc 100644 --- a/lib/cartodb/controllers/map.js +++ b/lib/cartodb/controllers/map.js @@ -90,6 +90,7 @@ MapController.prototype.composeCreateMapMiddleware = function (useTemplate = fal this.setAnalysesMetadataToLayergroup(includeQuery), this.setTurboCartoMetadataToLayergroup(), this.setAggregationMetadataToLayergroup(), + this.setTilejsonMetadataToLayergroup(), this.setSurrogateKeyHeader(), this.sendResponse(), this.augmentError({ label, addContext }) @@ -313,6 +314,74 @@ MapController.prototype.augmentLayergroupData = function () { }; }; + +function getTilejson(tiles, grids) { + const tilejson = { + tilejson: '2.2.0', + tiles: tiles.https || tiles.http + }; + + if (grids) { + tilejson.grids = grids.https || grids.http; + } + + return tilejson; +} + +MapController.prototype.setTilejsonMetadataToLayergroup = function () { + return function augmentLayergroupDataMiddleware (req, res, next) { + const { layergroup, user, mapconfig } = res.locals; + + const isVectorOnlyMapConfig = mapconfig.isVectorOnlyMapConfig(); + let hasMapnikLayers = false; + layergroup.metadata.layers.forEach((layerMetadata, index) => { + const layerId = mapconfig.getLayerId(index); + const rasterResource = `${layergroup.layergroupid}/${layerId}/{z}/{x}/{y}.png`; + if (mapconfig.layerType(index) === 'mapnik') { + hasMapnikLayers = true; + const vectorResource = `${layergroup.layergroupid}/${layerId}/{z}/{x}/{y}.mvt`; + const layerTilejson = { + vector: getTilejson(this.resourceLocator.getTileUrls(user, vectorResource)) + }; + if (!isVectorOnlyMapConfig) { + let grids = null; + const layer = mapconfig.getLayer(index); + if (layer.options.interactivity) { + const gridResource = `${layergroup.layergroupid}/${layerId}/{z}/{x}/{y}.grid.json`; + grids = this.resourceLocator.getTileUrls(user, gridResource); + } + layerTilejson.raster = getTilejson( + this.resourceLocator.getTileUrls(user, rasterResource), + grids + ); + } + layerMetadata.tilejson = layerTilejson; + } else { + layerMetadata.tilejson = { + raster: getTilejson(this.resourceLocator.getTileUrls(user, rasterResource)) + }; + } + }); + + const tilejson = {}; + + if (hasMapnikLayers) { + tilejson.vector = getTilejson( + this.resourceLocator.getTileUrls(user, `${layergroup.layergroupid}/{z}/{x}/{y}.mvt`) + ); + if (!isVectorOnlyMapConfig) { + tilejson.raster = getTilejson( + this.resourceLocator.getTileUrls(user, `${layergroup.layergroupid}/{z}/{x}/{y}.png`) + ); + } + } + + layergroup.metadata.tilejson = tilejson; + + next(); + }.bind(this); +}; + MapController.prototype.getAffectedTables = function () { return function getAffectedTablesMiddleware (req, res, next) { const { dbname, layergroup, user, mapconfig } = res.locals; diff --git a/test/acceptance/tilejson.js b/test/acceptance/tilejson.js new file mode 100644 index 00000000..56070e42 --- /dev/null +++ b/test/acceptance/tilejson.js @@ -0,0 +1,204 @@ +require('../support/test_helper'); + +const assert = require('../support/assert'); +const TestClient = require('../support/test-client'); + +describe('tilejson', function() { + + function tilejsonValidation(tilejson, shouldHaveGrid = false) { + assert.equal(tilejson.tilejson, '2.2.0'); + + assert.ok(Array.isArray(tilejson.tiles)); + assert.ok(tilejson.tiles.length > 0); + + if (shouldHaveGrid) { + assert.ok(Array.isArray(tilejson.grids)); + assert.ok(tilejson.grids.length > 0); + } + + } + + const sql = 'SELECT * FROM populated_places_simple_reduced'; + const cartocss = TestClient.CARTOCSS.POINTS; + const cartocss_version = '3.0.12'; + + const RASTER_LAYER = { + options: { + sql, cartocss, cartocss_version + } + }; + const RASTER_INTERACTIVITY_LAYER = { + options: { + sql, cartocss, cartocss_version, + interactivity: ['cartodb_id'] + } + }; + const VECTOR_LAYER = { + options: { + sql + } + }; + const PLAIN_LAYER = { + type: 'plain', + options: { + color: '#000000' + } + }; + + function mapConfig(layers) { + return { + version: '1.7.0', + layers: Array.isArray(layers) ? layers : [layers] + }; + } + + describe('per layer', function() { + it('should expose raster + vector tilejson for raster layers', function(done) { + var testClient = new TestClient(mapConfig(RASTER_LAYER)); + + testClient.getLayergroup(function(err, layergroupResult) { + assert.ok(!err, err); + const metadata = layergroupResult.metadata; + assert.ok(metadata); + + assert.equal(metadata.layers.length, 1); + + const layer = metadata.layers[0]; + assert.deepEqual(Object.keys(layer.tilejson), ['vector', 'raster']); + + Object.keys(layer.tilejson).forEach(k => { + tilejsonValidation(layer.tilejson[k]); + }); + + testClient.drain(done); + }); + }); + + it('should expose just the vector tilejson vector only layers', function(done) { + var testClient = new TestClient(mapConfig(VECTOR_LAYER)); + + testClient.getLayergroup(function(err, layergroupResult) { + assert.ok(!err, err); + const metadata = layergroupResult.metadata; + assert.ok(metadata); + + assert.equal(metadata.layers.length, 1); + + const layer = metadata.layers[0]; + assert.deepEqual(Object.keys(layer.tilejson), ['vector']); + + Object.keys(layer.tilejson).forEach(k => { + tilejsonValidation(layer.tilejson[k]); + }); + + testClient.drain(done); + }); + }); + + it('should expose just the raster tilejson plain layers', function(done) { + var testClient = new TestClient(mapConfig(PLAIN_LAYER)); + + testClient.getLayergroup(function(err, layergroupResult) { + assert.ok(!err, err); + const metadata = layergroupResult.metadata; + assert.ok(metadata); + + assert.equal(metadata.layers.length, 1); + + const layer = metadata.layers[0]; + assert.deepEqual(Object.keys(layer.tilejson), ['raster']); + + Object.keys(layer.tilejson).forEach(k => { + tilejsonValidation(layer.tilejson[k]); + }); + + testClient.drain(done); + }); + }); + + it('should expose grids for the raster layer with interactivity', function(done) { + var testClient = new TestClient(mapConfig(RASTER_INTERACTIVITY_LAYER)); + + testClient.getLayergroup(function(err, layergroupResult) { + assert.ok(!err, err); + const metadata = layergroupResult.metadata; + assert.ok(metadata); + + assert.equal(metadata.layers.length, 1); + + const layer = metadata.layers[0]; + assert.deepEqual(Object.keys(layer.tilejson), ['vector', 'raster']); + + tilejsonValidation(layer.tilejson.vector); + tilejsonValidation(layer.tilejson.raster, true); + + testClient.drain(done); + }); + }); + + it('should work with several layers', function(done) { + var testClient = new TestClient(mapConfig([RASTER_LAYER, RASTER_INTERACTIVITY_LAYER])); + + testClient.getLayergroup(function(err, layergroupResult) { + assert.ok(!err, err); + const metadata = layergroupResult.metadata; + assert.ok(metadata); + + assert.equal(metadata.layers.length, 2); + + assert.deepEqual(Object.keys(metadata.layers[0].tilejson), ['vector', 'raster']); + tilejsonValidation(metadata.layers[0].tilejson.vector); + tilejsonValidation(metadata.layers[0].tilejson.raster); + + assert.deepEqual(Object.keys(metadata.layers[1].tilejson), ['vector', 'raster']); + tilejsonValidation(metadata.layers[1].tilejson.vector); + tilejsonValidation(metadata.layers[1].tilejson.raster, true); + + testClient.drain(done); + }); + }); + }); + + describe('root tilejson', function() { + + it('should expose just the `vector` tilejson when for vector only mapnik layers', function(done) { + var testClient = new TestClient(mapConfig(VECTOR_LAYER)); + + testClient.getLayergroup(function(err, layergroupResult) { + assert.ok(!err, err); + const metadata = layergroupResult.metadata; + assert.ok(metadata); + + const tilejson = metadata.tilejson; + assert.deepEqual(Object.keys(tilejson), ['vector']); + + Object.keys(tilejson).forEach(k => { + tilejsonValidation(tilejson[k]); + }); + + testClient.drain(done); + }); + }); + + it('should expose just the `vector` and `raster` tilejson for mapnik layers', function(done) { + var testClient = new TestClient(mapConfig(RASTER_LAYER)); + + testClient.getLayergroup(function(err, layergroupResult) { + assert.ok(!err, err); + const metadata = layergroupResult.metadata; + assert.ok(metadata); + + const tilejson = metadata.tilejson; + assert.deepEqual(Object.keys(tilejson), ['vector', 'raster']); + + Object.keys(tilejson).forEach(k => { + tilejsonValidation(tilejson[k]); + }); + + testClient.drain(done); + }); + }); + + }); + +});