Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feat] add polygon filter based on mean centers for GeoJsonLayer #2476

Merged
merged 4 commits into from
Dec 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/constants/src/layers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -516,5 +516,6 @@ export const EDITOR_AVAILABLE_LAYERS: string[] = [
LAYER_TYPES.hexagon,
LAYER_TYPES.arc,
LAYER_TYPES.line,
LAYER_TYPES.hexagonId
LAYER_TYPES.hexagonId,
LAYER_TYPES.geojson
];
2 changes: 2 additions & 0 deletions src/layers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@
"@nebula.gl/edit-modes": "1.0.2-alpha.1",
"@nebula.gl/layers": "1.0.2-alpha.1",
"@turf/bbox": "^6.0.1",
"@turf/center": "^6.0.1",
"@turf/helpers": "^6.1.4",
"@turf/boolean-within": "^6.0.1",
"@types/geojson": "^7946.0.7",
"@types/keymirror": "^0.1.1",
"@types/lodash.memoize": "^4.1.7",
Expand Down
28 changes: 25 additions & 3 deletions src/layers/src/geojson-layer/geojson-layer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
// THE SOFTWARE.

import * as arrow from 'apache-arrow';
import {Feature} from 'geojson';
import {point as turfPoint} from '@turf/helpers';
import booleanWithin from '@turf/boolean-within';
import {Feature, Polygon} from 'geojson';
import uniq from 'lodash.uniq';
import {DATA_TYPES} from 'type-analyzer';
import Layer, {
Expand Down Expand Up @@ -209,6 +211,7 @@ export default class GeoJsonLayer extends Layer {
dataContainer: DataContainerInterface | null = null;
filteredIndex: Uint8ClampedArray | null = null;
filteredIndexTrigger: number[] | null = null;
centroids: Array<number[] | null> = [];

constructor(props) {
super(props);
Expand Down Expand Up @@ -417,6 +420,23 @@ export default class GeoJsonLayer extends Layer {
};
}

isInPolygon(data: DataContainerInterface, index: number, polygon: Feature<Polygon>): Boolean {
if (this.centroids.length === 0 || !this.centroids[index]) {
return false;
}
const isReactangleSearchBox = polygon.properties?.shape === 'Rectangle';
const point = this.centroids[index];
// if no valid centroid, return false
if (!point) return false;
// quick check if centroid is within the query rectangle
if (isReactangleSearchBox && polygon.properties?.bbox) {
const [minX, minY, maxX, maxY] = polygon.properties?.bbox;
return point[0] >= minX && point[0] <= maxX && point[1] >= minY && point[1] <= maxY;
}
// use turf.js to check if centroid is within query polygon
return booleanWithin(turfPoint(point), polygon);
}

updateLayerMeta(dataContainer) {
this.dataContainer = dataContainer;

Expand All @@ -429,21 +449,23 @@ export default class GeoJsonLayer extends Layer {
if (this.dataToFeature.length < dataContainer.numChunks()) {
// for incrementally loading data, we only load and render the latest batch; otherwise, we will load and render all batches
const isIncrementalLoad = dataContainer.numChunks() - this.dataToFeature.length === 1;
const {dataToFeature, bounds, fixedRadius, featureTypes} = getGeojsonLayerMetaFromArrow({
const {dataToFeature, bounds, fixedRadius, featureTypes, centroids} = getGeojsonLayerMetaFromArrow({
dataContainer,
getGeoColumn,
getGeoField,
...(isIncrementalLoad ? {chunkIndex: this.dataToFeature.length} : null)
});
if (centroids) this.centroids = this.centroids.concat(centroids);
this.updateMeta({bounds, fixedRadius, featureTypes});
this.dataToFeature = [...this.dataToFeature, ...dataToFeature];
}
} else {
if (this.dataToFeature.length === 0) {
const {dataToFeature, bounds, fixedRadius, featureTypes} = getGeojsonLayerMeta({
const {dataToFeature, bounds, fixedRadius, featureTypes, centroids} = getGeojsonLayerMeta({
dataContainer,
getFeature
});
if (centroids) this.centroids = centroids;
this.dataToFeature = dataToFeature;
this.updateMeta({bounds, fixedRadius, featureTypes});
}
Expand Down
19 changes: 18 additions & 1 deletion src/layers/src/geojson-layer/geojson-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
import {Feature, BBox} from 'geojson';
import normalize from '@mapbox/geojson-normalize';
import bbox from '@turf/bbox';
import center from '@turf/center';
import {AllGeoJSON} from '@turf/helpers'
import {parseSync} from '@loaders.gl/core';
import {WKBLoader, WKTLoader} from '@loaders.gl/wkt';
import {binaryToGeometry} from '@loaders.gl/gis';
Expand Down Expand Up @@ -90,11 +92,26 @@ export function getGeojsonLayerMeta({
// keep a record of what type of geometry the collection has
const featureTypes = getGeojsonFeatureTypes(dataToFeature);

const meanCenters: Array<number[] | null> = [];
for (let i = 0; i < dataToFeature.length; i++) {
const feature = dataToFeature[i];
if (feature) {
try {
// TODO: use line interpolate to get center of line for LineString
const cent = center(feature as AllGeoJSON);
meanCenters.push(cent.geometry.coordinates);
} catch (e) {
meanCenters.push(null);
}
}
}

return {
dataToFeature,
bounds,
fixedRadius,
featureTypes
featureTypes,
centroids: meanCenters
};
}

Expand Down
9 changes: 6 additions & 3 deletions src/layers/src/layer-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export type GeojsonLayerMetaProps = {
featureTypes: DeckGlGeoTypes;
bounds: BBox | null;
fixedRadius: boolean;
centroids?: Array<number[] | null>;
};

export function getGeojsonLayerMetaFromArrow({
Expand All @@ -74,10 +75,11 @@ export function getGeojsonLayerMetaFromArrow({
chunkOffset: geoColumn.data[0].length * chunkIndex
}
: {}),
triangulate: true
triangulate: true,
calculateMeanCenters: true
};
// create binary data from arrow data for GeoJsonLayer
const {binaryGeometries, featureTypes, bounds} = getBinaryGeometriesFromArrow(
const {binaryGeometries, featureTypes, bounds, meanCenters} = getBinaryGeometriesFromArrow(
geoColumn,
encoding,
options
Expand All @@ -90,7 +92,8 @@ export function getGeojsonLayerMetaFromArrow({
dataToFeature: binaryGeometries,
featureTypes,
bounds,
fixedRadius
fixedRadius,
centroids: meanCenters
};
}

Expand Down
1 change: 1 addition & 0 deletions src/reducers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"@kepler.gl/types": "3.0.0-alpha.1",
"@kepler.gl/utils": "3.0.0-alpha.1",
"@loaders.gl/loader-utils": "^4.1.0-alpha.4",
"@turf/bbox": "^6.0.1",
"@types/lodash.clonedeep": "^4.5.7",
"@types/lodash.flattendeep": "^4.4.7",
"@types/lodash.get": "^4.4.6",
Expand Down
5 changes: 5 additions & 0 deletions src/reducers/src/vis-state-updaters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import bbox from '@turf/bbox';
import {console as Console} from 'global/window';
import {disableStackCapturing, withTask} from 'react-palm/tasks';
import cloneDeep from 'lodash.clonedeep';
Expand Down Expand Up @@ -2575,6 +2576,8 @@ export function setFeaturesUpdater(
// if feature is part of a filter
const filterId = feature && getFilterIdInFeature(feature);
if (filterId && feature) {
// add bbox for polygon filter to speed up filtering
if (feature.properties) feature.properties.bbox = bbox(feature);
const featureValue = featureToFilterValue(feature, filterId);
const filterIdx = state.filters.findIndex(fil => fil.id === filterId);
// @ts-ignore
Expand All @@ -2596,6 +2599,8 @@ export const setSelectedFeatureUpdater = (
state: VisState,
{feature, selectionContext}: VisStateActions.SetSelectedFeatureUpdaterAction
): VisState => {
// add bbox for polygon filter to speed up filtering
if (feature && feature.properties) feature.properties.bbox = bbox(feature);
return {
...state,
editor: {
Expand Down
4 changes: 4 additions & 0 deletions src/utils/src/filter-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,10 @@ export const getPolygonFilterFunctor = (layer, filter, dataContainer) => {
const pos = getCentroid({id});
return pos.every(Number.isFinite) && isInPolygon(pos, filter.value);
};
case LAYER_TYPES.geojson:
return data => {
return layer.isInPolygon(data, data.index, filter.value);
};
default:
return () => true;
}
Expand Down
2 changes: 1 addition & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3210,7 +3210,7 @@
d3-geo "1.7.1"
turf-jsts "*"

"@turf/center@>=4.0.0", "@turf/center@^6.5.0":
"@turf/center@>=4.0.0", "@turf/center@^6.0.1", "@turf/center@^6.5.0":
version "6.5.0"
resolved "https://registry.yarnpkg.com/@turf/center/-/center-6.5.0.tgz#3bcb6bffcb8ba147430cfea84aabaed5dbdd4f07"
integrity sha512-T8KtMTfSATWcAX088rEDKjyvQCBkUsLnK/Txb6/8WUXIeOZyHu42G7MkdkHRoHtwieLdduDdmPLFyTdG5/e7ZQ==
Expand Down