diff --git a/NEWS.md b/NEWS.md index d52fbd2d..a8d0fbae 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,6 +4,10 @@ Released 2016-mm-dd +New features: + - turbo-carto: mapnik substitution tokens support #455 + + ## 2.42.1 Released 2016-05-17 diff --git a/lib/cartodb/api/overviews_metadata_api.js b/lib/cartodb/api/overviews_metadata_api.js index 2cec6bbb..55b4966f 100644 --- a/lib/cartodb/api/overviews_metadata_api.js +++ b/lib/cartodb/api/overviews_metadata_api.js @@ -1,24 +1,18 @@ +var SubstitutionTokens = require('../utils/substitution-tokens'); + function OverviewsMetadataApi(pgQueryRunner) { this.pgQueryRunner = pgQueryRunner; } module.exports = OverviewsMetadataApi; -// TODO: share this with QueryTablesApi? ... or maintain independence? -var affectedTableRegexCache = { - bbox: /!bbox!/g, - scale_denominator: /!scale_denominator!/g, - pixel_width: /!pixel_width!/g, - pixel_height: /!pixel_height!/g -}; - function prepareSql(sql) { - return sql && sql - .replace(affectedTableRegexCache.bbox, 'ST_MakeEnvelope(0,0,0,0)') - .replace(affectedTableRegexCache.scale_denominator, '0') - .replace(affectedTableRegexCache.pixel_width, '1') - .replace(affectedTableRegexCache.pixel_height, '1') - ; + return sql && SubstitutionTokens.replace(sql, { + bbox: 'ST_MakeEnvelope(0,0,0,0)', + scale_denominator: '0', + pixel_width: '1', + pixel_height: '1' + }); } OverviewsMetadataApi.prototype.getOverviewsMetadata = function (username, sql, callback) { diff --git a/lib/cartodb/utils/style/turbo-carto-adapter.js b/lib/cartodb/utils/style/turbo-carto-adapter.js index 7475c44a..d1c0c81f 100644 --- a/lib/cartodb/utils/style/turbo-carto-adapter.js +++ b/lib/cartodb/utils/style/turbo-carto-adapter.js @@ -1,6 +1,7 @@ 'use strict'; var queue = require('queue-async'); +var SubstitutionTokens = require('../substitution-tokens'); function TurboCartoAdapter(turboCartoParser) { this.turboCartoParser = turboCartoParser; @@ -37,7 +38,14 @@ TurboCartoAdapter.prototype._parseCartoCss = function (username, layer, callback }); } - this.turboCartoParser.process(username, layer.options.cartocss, layer.options.sql, function (err, cartocss) { + var sql = SubstitutionTokens.replace(layer.options.sql, { + bbox: 'ST_MakeEnvelope(-20037508.34,-20037508.34,20037508.34,20037508.34,3857)', + scale_denominator: '500000001', + pixel_width: '156412', + pixel_height: '156412' + }); + + this.turboCartoParser.process(username, layer.options.cartocss, sql, function (err, cartocss) { // Only return turbo-carto errors if (err && err.name === 'TurboCartoError') { err = new Error('turbo-carto: ' + err.message); diff --git a/lib/cartodb/utils/substitution-tokens.js b/lib/cartodb/utils/substitution-tokens.js new file mode 100644 index 00000000..af09f838 --- /dev/null +++ b/lib/cartodb/utils/substitution-tokens.js @@ -0,0 +1,19 @@ +var SUBSTITUTION_TOKENS = { + bbox: /!bbox!/g, + scale_denominator: /!scale_denominator!/g, + pixel_width: /!pixel_width!/g, + pixel_height: /!pixel_height!/g +}; + +var SubstitutionTokens = { + replace: function(sql, replaceValues) { + Object.keys(replaceValues).forEach(function(token) { + if (SUBSTITUTION_TOKENS[token]) { + sql = sql.replace(SUBSTITUTION_TOKENS[token], replaceValues[token]); + } + }); + return sql; + } +}; + +module.exports = SubstitutionTokens; diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 16509d9d..7a307d00 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -1,6 +1,6 @@ { "name": "windshaft-cartodb", - "version": "2.42.1", + "version": "2.42.2", "dependencies": { "body-parser": { "version": "1.14.2", diff --git a/test/acceptance/turbo-cartocss/regressions.js b/test/acceptance/turbo-cartocss/regressions.js index b8dc439a..4f152da3 100644 --- a/test/acceptance/turbo-cartocss/regressions.js +++ b/test/acceptance/turbo-cartocss/regressions.js @@ -3,7 +3,7 @@ require('../../support/test_helper'); var assert = require('../../support/assert'); var TestClient = require('../../support/test-client'); -function makeMapconfig(cartocss) { +function makeMapconfig(sql, cartocss) { return { "version": "1.4.0", "layers": [ @@ -11,19 +11,7 @@ function makeMapconfig(cartocss) { "type": 'mapnik', "options": { "cartocss_version": '2.3.0', - "sql": [ - 'SELECT test_table.*, _prices.price FROM test_table JOIN (' + - ' SELECT 1 AS cartodb_id, 10.00 AS price', - ' UNION', - ' SELECT 2, 10.50', - ' UNION', - ' SELECT 3, 11.00', - ' UNION', - ' SELECT 4, 12.00', - ' UNION', - ' SELECT 5, 21.00', - ') _prices ON _prices.cartodb_id = test_table.cartodb_id' - ].join('\n'), + "sql": sql, "cartocss": cartocss } } @@ -33,40 +21,78 @@ function makeMapconfig(cartocss) { describe('turbo-carto regressions', function() { - var cartocss = [ - "/** simple visualization */", - "", - "Map {", - " buffer-size: 256;", - "}", - "", - "#county_points_with_population{", - " marker-fill-opacity: 0.1;", - " marker-line-color:#FFFFFF;//#CF1C90;", - " marker-line-width: 0;", - " marker-line-opacity: 0.3;", - " marker-placement: point;", - " marker-type: ellipse;", - " //marker-comp-op: overlay;", - " marker-width: [price];", - " [zoom=5]{marker-width: [price]*2;}", - " [zoom=6]{marker-width: [price]*4;}", - " marker-fill: #000000;", - " marker-allow-overlap: true;", - " ", - "", - "}" - ].join('\n'); - - beforeEach(function () { - this.testClient = new TestClient(makeMapconfig(cartocss)); - }); - afterEach(function (done) { - this.testClient.drain(done); + if (this.testClient) { + this.testClient.drain(done); + } }); it('should accept // comments', function(done) { + var cartocss = [ + "/** simple visualization */", + "", + "Map {", + " buffer-size: 256;", + "}", + "", + "#county_points_with_population{", + " marker-fill-opacity: 0.1;", + " marker-line-color:#FFFFFF;//#CF1C90;", + " marker-line-width: 0;", + " marker-line-opacity: 0.3;", + " marker-placement: point;", + " marker-type: ellipse;", + " //marker-comp-op: overlay;", + " marker-width: [cartodb_id];", + " [zoom=5]{marker-width: [cartodb_id]*2;}", + " [zoom=6]{marker-width: [cartodb_id]*4;}", + " marker-fill: #000000;", + " marker-allow-overlap: true;", + " ", + "", + "}" + ].join('\n'); + + this.testClient = new TestClient(makeMapconfig('SELECT * FROM populated_places_simple_reduced', cartocss)); + this.testClient.getLayergroup(function(err, layergroup) { + assert.ok(!err, err); + + assert.ok(layergroup.hasOwnProperty('layergroupid')); + assert.ok(!layergroup.hasOwnProperty('errors')); + + done(); + }); + }); + + it('should work with mapnik substitution tokens', function(done) { + var cartocss = [ + "#layer {", + " line-width: 2;", + " line-color: #3B3B58;", + " line-opacity: 1;", + " polygon-opacity: 0.7;", + " polygon-fill: ramp([points_count], (#E5F5F9,#99D8C9,#2CA25F))", + "}" + ].join('\n'); + + var sql = [ + 'WITH hgrid AS (', + ' SELECT CDB_HexagonGrid(', + ' ST_Expand(!bbox!, greatest(!pixel_width!,!pixel_height!) * 100),', + ' greatest(!pixel_width!,!pixel_height!) * 100', + ' ) as cell', + ')', + 'SELECT', + ' hgrid.cell as the_geom_webmercator,', + ' count(1) as points_count,', + ' count(1)/power(100 * CDB_XYZ_Resolution(CDB_ZoomFromScale(!scale_denominator!)), 2) as points_density,', + ' 1 as cartodb_id', + 'FROM hgrid, (SELECT * FROM populated_places_simple_reduced) i', + 'where ST_Intersects(i.the_geom_webmercator, hgrid.cell)', + 'GROUP BY hgrid.cell' + ].join('\n'); + + this.testClient = new TestClient(makeMapconfig(sql, cartocss)); this.testClient.getLayergroup(function(err, layergroup) { assert.ok(!err, err); diff --git a/test/support/prepare_db.sh b/test/support/prepare_db.sh index 928272fd..ceac7c78 100755 --- a/test/support/prepare_db.sh +++ b/test/support/prepare_db.sh @@ -83,7 +83,7 @@ if test x"$PREPARE_PGSQL" = xyes; then cat sql/_CDB_QueryStatements.sql | psql -v ON_ERROR_STOP=1 ${TEST_DB} || exit 1 - SQL_SCRIPTS='CDB_QueryTables CDB_CartodbfyTable CDB_TableMetadata CDB_ForeignTable CDB_UserTables CDB_ColumnNames CDB_ZoomFromScale CDB_Overviews CDB_QuantileBins CDB_JenksBins CDB_HeadsTailsBins CDB_EqualIntervalBins' + SQL_SCRIPTS='CDB_QueryTables CDB_CartodbfyTable CDB_TableMetadata CDB_ForeignTable CDB_UserTables CDB_ColumnNames CDB_ZoomFromScale CDB_Overviews CDB_QuantileBins CDB_JenksBins CDB_HeadsTailsBins CDB_EqualIntervalBins CDB_Hexagon CDB_XYZ' for i in ${SQL_SCRIPTS} do curl -L -s https://github.com/CartoDB/cartodb-postgresql/raw/master/scripts-available/$i.sql -o sql/$i.sql