Experimental aggregation dimensions

This is not meant por public consumption (exposing SQL expressions is undesiderable)
This commit is contained in:
Javier Goizueta 2017-12-14 17:51:49 +01:00
parent 0c044636ef
commit 6b472c0b20
2 changed files with 45 additions and 14 deletions

View File

@ -7,6 +7,7 @@
* - sourceQuery
* - res
* - columns
* - dimensions
*/
const templateForOptions = (options) => {
let templateFn = aggregationQueryTemplates[options.placement];
@ -23,11 +24,13 @@ const templateForOptions = (options) => {
* aggregation cell is resolution*resolution pixels, where tiles are always 256x256 pixels
* - columns
* - placement
* - dimensions
*/
const queryForOptions = (options) => templateForOptions(options)({
sourceQuery: options.query,
res: 256/options.resolution,
columns: options.columns
columns: options.columns,
dimensions: options.dimensions
});
module.exports = queryForOptions;
@ -53,6 +56,11 @@ const SUPPORTED_AGGREGATE_FUNCTIONS = {
}
};
const sep = (list) => {
let expr = list.join(', ');
return expr ? ', ' + expr : expr;
};
const aggregateColumns = ctx => {
return Object.assign({
_cdb_feature_count: {
@ -63,12 +71,12 @@ const aggregateColumns = ctx => {
const aggregateColumnNames = ctx => {
let columns = aggregateColumns(ctx);
return Object.keys(columns).join(', ');
return sep(Object.keys(columns));
};
const aggregateColumnDefs = ctx => {
let columns = aggregateColumns(ctx);
return Object.keys(columns).map(column_name => {
return sep(Object.keys(columns).map(column_name => {
const aggregate_function = columns[column_name].aggregate_function || 'count';
const aggregate_definition = SUPPORTED_AGGREGATE_FUNCTIONS[aggregate_function];
if (!aggregate_definition) {
@ -76,10 +84,24 @@ const aggregateColumnDefs = ctx => {
}
const aggregate_expression = aggregate_definition.sql(column_name, columns[column_name]);
return `${aggregate_expression} AS ${column_name}`;
}).join(', ');
}));
};
const aggregateDimensions = ctx => ctx.dimensions || {};
const dimensionNames = ctx => {
return sep(Object.keys(aggregateDimensions(ctx)));
};
const dimensionDefs = ctx => {
let dimensions = aggregateDimensions(ctx);
return sep(Object.keys(dimensions).map(dimension_name => {
const expression = dimensions[dimension_name];
return `${expression} AS ${dimension_name}`;
}));
};
// SQL expression to compute the aggregation resolution (grid cell size).
// This is equivalent to `${256/ctx.res}*CDB_XYZ_Resolution(CDB_ZoomFromScale(!scale_denominator!))`
// This is defined by the ctx.res parameter, which is the number of grid cells per tile linear dimension
@ -106,13 +128,15 @@ const aggregationQueryTemplates = {
AVG(ST_X(_cdb_query.the_geom_webmercator)),
AVG(ST_Y(_cdb_query.the_geom_webmercator))
), 3857
) AS the_geom_webmercator,
) AS the_geom_webmercator
${dimensionDefs(ctx)}
${aggregateColumnDefs(ctx)}
FROM (${ctx.sourceQuery}) _cdb_query, _cdb_params
WHERE _cdb_query.the_geom_webmercator && _cdb_params.bbox
GROUP BY
Floor(ST_X(_cdb_query.the_geom_webmercator)/_cdb_params.res),
Floor(ST_Y(_cdb_query.the_geom_webmercator)/_cdb_params.res)
${dimensionNames(ctx)}
`,
'point-grid': ctx => `
@ -124,14 +148,16 @@ const aggregationQueryTemplates = {
_cdb_clusters AS (
SELECT
Floor(ST_X(_cdb_query.the_geom_webmercator)/_cdb_params.res)::int AS _cdb_gx,
Floor(ST_Y(_cdb_query.the_geom_webmercator)/_cdb_params.res)::int AS _cdb_gy,
Floor(ST_Y(_cdb_query.the_geom_webmercator)/_cdb_params.res)::int AS _cdb_gy
${dimensionDefs(ctx)}
${aggregateColumnDefs(ctx)}
FROM (${ctx.sourceQuery}) _cdb_query, _cdb_params
FROM (${ctx.sourceQuery(ctx)}) _cdb_query, _cdb_params
WHERE the_geom_webmercator && _cdb_params.bbox
GROUP BY _cdb_gx, _cdb_gy
GROUP BY _cdb_gx, _cdb_gy ${dimensionNames}
)
SELECT
ST_SetSRID(ST_MakePoint(_cdb_gx*(res+0.5), _cdb_gy*(res+0.5)), 3857) AS the_geom_webmercator,
ST_SetSRID(ST_MakePoint(_cdb_gx*(res+0.5), _cdb_gy*(res+0.5)), 3857) AS the_geom_webmercator
${dimensionNames(ctx)}
${aggregateColumnNames(ctx)}
FROM _cdb_clusters, _cdb_params
`,
@ -143,19 +169,22 @@ const aggregationQueryTemplates = {
!bbox! AS bbox
), _cdb_clusters AS (
SELECT
MIN(cartodb_id) AS cartodb_id,
MIN(cartodb_id) AS cartodb_id
${dimensionDefs(ctx)}
${aggregateColumnDefs(ctx)}
FROM (${ctx.sourceQuery}) _cdb_query, _cdb_params
FROM (${ctx.sourceQuery(ctx)}) _cdb_query, _cdb_params
WHERE _cdb_query.the_geom_webmercator && _cdb_params.bbox
GROUP BY
Floor(ST_X(_cdb_query.the_geom_webmercator)/_cdb_params.res),
Floor(ST_Y(_cdb_query.the_geom_webmercator)/_cdb_params.res)
${dimensionNames(ctx)}
) SELECT
_cdb_clusters.cartodb_id,
the_geom, the_geom_webmercator,
the_geom, the_geom_webmercator
${dimensionNames(ctx)}
${aggregateColumnNames(ctx)}
FROM
_cdb_clusters INNER JOIN (${ctx.sourceQuery}) _cdb_query
_cdb_clusters INNER JOIN (${ctx.sourceQuery(ctx)}) _cdb_query
ON (_cdb_clusters.cartodb_id = _cdb_query.cartodb_id)
`
};

View File

@ -9,7 +9,8 @@ module.exports = class Aggregation {
resolution = 1,
threshold = Aggregation.THRESHOLD,
placement = 'centroid',
columns = {}
columns = {},
dimensions = {}
} = {}) {
this.mapconfig = mapconfig;
this.query = query;
@ -17,6 +18,7 @@ module.exports = class Aggregation {
this.threshold = threshold;
this.placement = placement;
this.columns = columns;
this.dimensions = dimensions;
}
sql () {
return aggregationQuery(this);