diff --git a/lib/cartodb/backends/dataview.js b/lib/cartodb/backends/dataview.js index 84a47cf5..d18e1529 100644 --- a/lib/cartodb/backends/dataview.js +++ b/lib/cartodb/backends/dataview.js @@ -136,13 +136,17 @@ DataviewBackend.prototype.getDataview = function (mapConfigProvider, user, param ); } } - var dataviewFactory = DataviewFactoryWithOverviews.getFactory(overviewsQueryRewriter, queryRewriteData); if (params.bbox) { var bboxFilter = new BBoxFilter({column: 'the_geom', srid: 4326}, {bbox: params.bbox}); query = bboxFilter.sql(query); + queryRewriteData = _.extend(queryRewriteData, { bbox_filter: bboxFilter }); } + var dataviewFactory = DataviewFactoryWithOverviews.getFactory( + overviewsQueryRewriter, queryRewriteData, { bbox: params.bbox } + ); + var overrideParams = _.reduce(_.pick(params, 'start', 'end', 'bins'), function castNumbers(overrides, val, k) { overrides[k] = Number.isFinite(+val) ? +val : val; diff --git a/lib/cartodb/models/dataview/overviews/factory.js b/lib/cartodb/models/dataview/overviews/factory.js index b5ce8dd3..b63b004a 100644 --- a/lib/cartodb/models/dataview/overviews/factory.js +++ b/lib/cartodb/models/dataview/overviews/factory.js @@ -1,9 +1,10 @@ var parentFactory = require('../factory'); var dataviews = require('./'); -function OverviewsDataviewFactory(queryRewriter, queryRewriteData) { +function OverviewsDataviewFactory(queryRewriter, queryRewriteData, options) { this.queryRewriter = queryRewriter; this.queryRewriteData = queryRewriteData; + this.options = options; } OverviewsDataviewFactory.prototype.getDataview = function(query, dataviewDefinition) { @@ -12,7 +13,9 @@ OverviewsDataviewFactory.prototype.getDataview = function(query, dataviewDefinit if ( !this.queryRewriter || !this.queryRewriteData || !dataviews[type] ) { return parentFactory.getDataview(query, dataviewDefinition); } - return new dataviews[type](query, dataviewDefinition.options, this.queryRewriter, this.queryRewriteData); + return new dataviews[type]( + query, dataviewDefinition.options, this.queryRewriter, this.queryRewriteData, this.options + ); }; var OverviewsDataviewMetaFactory = { @@ -21,8 +24,8 @@ var OverviewsDataviewMetaFactory = { return allDataviews; }, {}), - getFactory: function(queryRewriter, queryRewriteData) { - return new OverviewsDataviewFactory(queryRewriter, queryRewriteData); + getFactory: function(queryRewriter, queryRewriteData, options) { + return new OverviewsDataviewFactory(queryRewriter, queryRewriteData, options); }, }; diff --git a/lib/cartodb/models/dataview/overviews/formula.js b/lib/cartodb/models/dataview/overviews/formula.js index 1144f5dd..db305e55 100644 --- a/lib/cartodb/models/dataview/overviews/formula.js +++ b/lib/cartodb/models/dataview/overviews/formula.js @@ -1,3 +1,4 @@ +var _ = require('underscore'); var BaseWidget = require('../base'); var BaseDataview = require('../formula'); @@ -27,13 +28,14 @@ var formulaQueryTpls = { ].join('\n')), }; -function Formula(query, options, queryRewriter, queryRewriteData) { +function Formula(query, options, queryRewriter, queryRewriteData, params) { this.base_dataview = new BaseDataview(query, options); this.query = query; this.column = options.column || '1'; this.operation = options.operation; this.queryRewriter = queryRewriter; this.queryRewriteData = queryRewriteData; + this.options = params; } Formula.prototype = new BaseWidget(); @@ -41,21 +43,39 @@ Formula.prototype.constructor = Formula; module.exports = Formula; +var zoom_level_factor = 100.0; + +// Compute zoom level so that the the resolution grid size of the +// selected overview is smaller (zoom_level_factor times smaller at least) +// than the bounding box size. +function zoom_level_for_bbox(bbox) { + var px_per_tile = 256.0; + var earth_width = 360.0; + // TODO: now we assume overviews are computed for 1-pixel tolerance; + // should use extended overviews metadata to compute this properly. + if ( bbox ) { + var bbox_values = _.map(bbox.split(','), function(v) { return +v; }); + // TODO: look at possible crossing-180ยบ issues + var w = Math.abs(bbox_values[2]-bbox_values[0]); + var h = Math.abs(bbox_values[3]-bbox_values[1]); + var max_dim = Math.min(w, h); + + // Find minimum suitable z + // note that the QueryRewirter will use the minimum level overview + // of level >= z if it exists, and otherwise the base table + var z = Math.ceil(-Math.log(max_dim*px_per_tile/earth_width/zoom_level_factor)/Math.log(2.0)); + return Math.max(z, 0); + } + return 0; +} + Formula.prototype.sql = function(psql, filters, override, callback) { var _query = this.query; var formulaQueryTpl = formulaQueryTpls[this.operation]; if ( formulaQueryTpl ) { // supported formula for use with overviews - - // TODO: determine zoom level using bounding box so that the resolution - // (grid size) of the overview much smaller thatn the bounding box. - // This could be left to be computed by the queryRewriter passing the - // bounding box to it. - // The bounding box could be passed to the dataview constructor, or - // it could b extracted from the query. - var zoom_level = 0; - + var zoom_level = zoom_level_for_bbox(this.options.bbox); _query = this.queryRewriter.query(_query, this.queryRewriteData, { zoom_level: zoom_level }); var formulaSql = formulaQueryTpl({ _query: _query, @@ -64,6 +84,7 @@ Formula.prototype.sql = function(psql, filters, override, callback) { }); debug(formulaSql); callback = callback || override; + return callback(null, formulaSql); } diff --git a/lib/cartodb/utils/overviews_query_rewriter.js b/lib/cartodb/utils/overviews_query_rewriter.js index 4999073b..7efff2ed 100644 --- a/lib/cartodb/utils/overviews_query_rewriter.js +++ b/lib/cartodb/utils/overviews_query_rewriter.js @@ -3,11 +3,14 @@ var TableNameParser = require('./table_name_parser'); // this is meant as a hack for development var queryBuilder = require('camshaft/lib/filter/query-builder'); -function apply_filters_to_query(query, filters) { +function apply_filters_to_query(query, filters, bbox_filter) { // TODO: implement filter application here if ( filters && !_.isEmpty(filters)) { query = queryBuilder.getSql(query, filters); } + if ( bbox_filter ) { + query = bbox_filter.sql(query); + } return query; } @@ -227,6 +230,7 @@ OverviewsQueryRewriter.prototype.query = function(query, data, options) { var overviews = data && data.overviews; var unfiltered_query = data && data.unfiltered_query; var filters = data && data.filters; + var bbox_filter = data && data.bbox_filter; if ( !unfiltered_query ) { unfiltered_query = query; @@ -252,7 +256,7 @@ OverviewsQueryRewriter.prototype.query = function(query, data, options) { // Applicability of the filters should depend both on the expected // row count of the result and on the columns to be filter and the // way they're agregated in overviews. - rewritten_query = apply_filters_to_query(rewritten_query, filters); + rewritten_query = apply_filters_to_query(rewritten_query, filters, bbox_filter); } return rewritten_query;