Implement polygon filter
This commit is contained in:
parent
caf09ac644
commit
c877d0b964
@ -19,6 +19,7 @@ const ALLOWED_DATAVIEW_QUERY_PARAMS = [
|
|||||||
'no_filters', // 0, 1
|
'no_filters', // 0, 1
|
||||||
'bbox', // w,s,e,n
|
'bbox', // w,s,e,n
|
||||||
'circle', // json
|
'circle', // json
|
||||||
|
'polygon', // json
|
||||||
'start', // number
|
'start', // number
|
||||||
'end', // number
|
'end', // number
|
||||||
'column_type', // string
|
'column_type', // string
|
||||||
|
@ -4,6 +4,7 @@ var _ = require('underscore');
|
|||||||
var PSQL = require('cartodb-psql');
|
var PSQL = require('cartodb-psql');
|
||||||
var BBoxFilter = require('../models/filter/bbox');
|
var BBoxFilter = require('../models/filter/bbox');
|
||||||
const CircleFilter = require('../models/filter/circle');
|
const CircleFilter = require('../models/filter/circle');
|
||||||
|
const PolygonFilter = require('../models/filter/polygon');
|
||||||
var DataviewFactory = require('../models/dataview/factory');
|
var DataviewFactory = require('../models/dataview/factory');
|
||||||
var DataviewFactoryWithOverviews = require('../models/dataview/overviews/factory');
|
var DataviewFactoryWithOverviews = require('../models/dataview/overviews/factory');
|
||||||
const dbParamsFromReqParams = require('../utils/database-params');
|
const dbParamsFromReqParams = require('../utils/database-params');
|
||||||
@ -90,6 +91,9 @@ function getQueryWithFilters (dataviewDefinition, params) {
|
|||||||
} else if (params.circle) {
|
} else if (params.circle) {
|
||||||
const circleFilter = new CircleFilter({ column: 'the_geom_webmercator', srid: 3857 }, { circle: params.circle });
|
const circleFilter = new CircleFilter({ column: 'the_geom_webmercator', srid: 3857 }, { circle: params.circle });
|
||||||
query = circleFilter.sql(query);
|
query = circleFilter.sql(query);
|
||||||
|
} else if (params.polygon) {
|
||||||
|
const polygonFilter = new PolygonFilter({ column: 'the_geom_webmercator', srid: 3857 }, { polygon: params.polygon });
|
||||||
|
query = polygonFilter.sql(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
return query;
|
return query;
|
||||||
@ -204,6 +208,9 @@ function getQueryWithOwnFilters (dataviewDefinition, params) {
|
|||||||
} else if (params.circle) {
|
} else if (params.circle) {
|
||||||
const circleFilter = new CircleFilter({ column: 'the_geom', srid: 4326 }, { circle: params.circle });
|
const circleFilter = new CircleFilter({ column: 'the_geom', srid: 4326 }, { circle: params.circle });
|
||||||
query = circleFilter.sql(query);
|
query = circleFilter.sql(query);
|
||||||
|
} else if (params.polygon) {
|
||||||
|
const polygonFilter = new PolygonFilter({ column: 'the_geom', srid: 4326 }, { polygon: params.polygon });
|
||||||
|
query = polygonFilter.sql(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
return query;
|
return query;
|
||||||
|
63
lib/models/filter/polygon.js
Normal file
63
lib/models/filter/polygon.js
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const assert = require('assert');
|
||||||
|
const debug = require('debug')('windshaft:filter:polygon');
|
||||||
|
function filterQueryTpl ({ sql, column, srid, geojson } = {}) {
|
||||||
|
return `
|
||||||
|
SELECT
|
||||||
|
*
|
||||||
|
FROM (${sql}) _cdb_polygon_filter
|
||||||
|
WHERE
|
||||||
|
ST_Intersects(
|
||||||
|
${column},
|
||||||
|
ST_Transform(
|
||||||
|
ST_SetSRID(ST_GeomFromGeoJSON('${JSON.stringify(geojson)}'), 4326),
|
||||||
|
${srid}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = class PolygonFilter {
|
||||||
|
constructor (filterDefinition, filterParams) {
|
||||||
|
const { polygon } = filterParams;
|
||||||
|
|
||||||
|
if (!polygon) {
|
||||||
|
throw new Error('Polygon filter expects to have a "polygon" param');
|
||||||
|
}
|
||||||
|
|
||||||
|
const geojson = JSON.parse(polygon);
|
||||||
|
|
||||||
|
if (geojson.type !== 'Polygon') {
|
||||||
|
throw new Error('Invalid type of geometry. Valid ones: "Polygon"');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Array.isArray(geojson.coordinates)) {
|
||||||
|
throw new Error('Invalid geometry, it must be an array of coordinates (long/lat)');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const length = geojson.coordinates.length;
|
||||||
|
assert.deepStrictEqual(geojson.coordinates[0], geojson.coordinates[length - 1]);
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error('Invalid geometry, it must be a closed polygon');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.column = filterDefinition.column || 'the_geom_webmercator';
|
||||||
|
this.srid = filterDefinition.srid || 3857;
|
||||||
|
this.geojson = geojson;
|
||||||
|
}
|
||||||
|
|
||||||
|
sql (rawSql) {
|
||||||
|
const polygonSql = filterQueryTpl({
|
||||||
|
sql: rawSql,
|
||||||
|
column: this.column,
|
||||||
|
srid: this.srid,
|
||||||
|
geojson: this.geojson
|
||||||
|
});
|
||||||
|
|
||||||
|
debug(polygonSql);
|
||||||
|
|
||||||
|
return polygonSql;
|
||||||
|
}
|
||||||
|
};
|
@ -5,7 +5,7 @@ require('../../support/test-helper');
|
|||||||
const assert = require('../../support/assert');
|
const assert = require('../../support/assert');
|
||||||
const TestClient = require('../../support/test-client');
|
const TestClient = require('../../support/test-client');
|
||||||
|
|
||||||
describe('circle filter', function () {
|
describe('spatial filters', function () {
|
||||||
const mapConfig = {
|
const mapConfig = {
|
||||||
version: '1.8.0',
|
version: '1.8.0',
|
||||||
layers: [
|
layers: [
|
||||||
@ -118,6 +118,49 @@ describe('circle filter', function () {
|
|||||||
{ category: 'category_1', value: 1, agg: false }
|
{ category: 'category_1', value: 1, agg: false }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
params: {
|
||||||
|
polygon: JSON.stringify({
|
||||||
|
type: 'Polygon',
|
||||||
|
coordinates: [
|
||||||
|
[
|
||||||
|
[
|
||||||
|
-9.312286,
|
||||||
|
37.907367
|
||||||
|
],
|
||||||
|
[
|
||||||
|
11.969604,
|
||||||
|
6.487254
|
||||||
|
],
|
||||||
|
[
|
||||||
|
-32.217407,
|
||||||
|
6.957781
|
||||||
|
],
|
||||||
|
[
|
||||||
|
-9.312286,
|
||||||
|
37.907367
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
})
|
||||||
|
},
|
||||||
|
expected: {
|
||||||
|
type: 'aggregation',
|
||||||
|
aggregation: 'sum',
|
||||||
|
count: 3,
|
||||||
|
nulls: 0,
|
||||||
|
nans: 0,
|
||||||
|
infinities: 0,
|
||||||
|
min: 1,
|
||||||
|
max: 4,
|
||||||
|
categoriesCount: 3,
|
||||||
|
categories: [
|
||||||
|
{ category: 'category_4', value: 4, agg: false },
|
||||||
|
{ category: 'category_2', value: 2, agg: false },
|
||||||
|
{ category: 'category_1', value: 1, agg: false }
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
@ -485,7 +485,7 @@ TestClient.prototype.getDataview = function (dataviewName, params, callback) {
|
|||||||
urlParams.own_filter = params.own_filter;
|
urlParams.own_filter = params.own_filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
['bbox', 'circle', 'bins', 'start', 'end', 'aggregation', 'offset', 'categories'].forEach(function (extraParam) {
|
['bbox', 'circle', 'polygon', 'bins', 'start', 'end', 'aggregation', 'offset', 'categories'].forEach(function (extraParam) {
|
||||||
if (Object.prototype.hasOwnProperty.call(params, extraParam)) {
|
if (Object.prototype.hasOwnProperty.call(params, extraParam)) {
|
||||||
urlParams[extraParam] = params[extraParam];
|
urlParams[extraParam] = params[extraParam];
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user