Skip to content

Commit

Permalink
CARTO: Re-aggregate properties on change (#9078)
Browse files Browse the repository at this point in the history
  • Loading branch information
felixpalmer authored Aug 8, 2024
1 parent 3f1d8da commit 6971bc6
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 6 deletions.
12 changes: 10 additions & 2 deletions modules/carto/src/layers/cluster-tile-layer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,18 +122,26 @@ class ClusterGeoJsonLayer<

const properties = extractAggregationProperties(visibleTiles[0]);
const data = [] as ClusteredFeaturePropertiesT<FeaturePropertiesT>[];
let needsUpdate = false;
for (const tile of visibleTiles) {
// Calculate aggregation based on viewport zoom
const overZoom = Math.round(zoom - tile.zoom);
const aggregationLevels = Math.round(clusterLevel) - overZoom;
aggregateTile(tile, aggregationLevels, properties, getPosition, getWeight);
const didAggregate = aggregateTile(
tile,
aggregationLevels,
properties,
getPosition,
getWeight
);
needsUpdate ||= didAggregate;
data.push(...tile.userData![aggregationLevels]);
}

data.sort((a, b) => Number(b.count - a.count));

const clusterIds = data?.map((tile: any) => tile.id);
const needsUpdate = !deepEqual(clusterIds, this.state.clusterIds, 1);
needsUpdate ||= !deepEqual(clusterIds, this.state.clusterIds, 1);
this.setState({clusterIds});

if (needsUpdate) {
Expand Down
25 changes: 21 additions & 4 deletions modules/carto/src/layers/cluster-utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {cellToParent} from 'quadbin';
import {_Tile2DHeader as Tile2DHeader} from '@deck.gl/geo-layers';
import {Accessor} from '@deck.gl/core';
import {Accessor, log} from '@deck.gl/core';
import {BinaryFeatureCollection} from '@loaders.gl/schema';

export type Aggregation = 'any' | 'average' | 'count' | 'min' | 'max' | 'sum';
Expand All @@ -17,18 +17,33 @@ export type ClusteredFeaturePropertiesT<FeaturePropertiesT> = FeaturePropertiesT
export type ParsedQuadbinCell<FeaturePropertiesT> = {id: bigint; properties: FeaturePropertiesT};
export type ParsedQuadbinTile<FeaturePropertiesT> = ParsedQuadbinCell<FeaturePropertiesT>[];

/**
* Aggregates tile by specified properties, caching result in tile.userData
*
* @returns true if data was aggregated, false if cache used
*/
export function aggregateTile<FeaturePropertiesT>(
tile: Tile2DHeader<ParsedQuadbinTile<FeaturePropertiesT>>,
aggregationLevels: number,
properties: AggregationProperties<FeaturePropertiesT> = [],
getPosition: Accessor<ParsedQuadbinCell<FeaturePropertiesT>, [number, number]>,
getWeight: Accessor<ParsedQuadbinCell<FeaturePropertiesT>, number>
): void {
if (!tile.content) return;
): boolean {
if (!tile.content) return false;

// Aggregate on demand and cache result
if (!tile.userData) tile.userData = {};
if (tile.userData[aggregationLevels]) return;
const cell0 = tile.userData[aggregationLevels]?.[0];
if (cell0) {
// Have already aggregated this tile
if (properties.every(property => property.name in cell0)) {
// Use cached result
return false;
}

// Aggregated properties have changed, re-aggregate
tile.userData = {};
}

const out: Record<number, any> = {};
for (const cell of tile.content) {
Expand Down Expand Up @@ -80,6 +95,7 @@ export function aggregateTile<FeaturePropertiesT>(
}

tile.userData[aggregationLevels] = Object.values(out);
return true;
}

export function extractAggregationProperties<FeaturePropertiesT extends {}>(
Expand All @@ -90,6 +106,7 @@ export function extractAggregationProperties<FeaturePropertiesT extends {}>(
for (const name of Object.keys(tile.content![0].properties)) {
let aggregation = name.split('_').pop()!.toLowerCase() as Aggregation;
if (!validAggregations.includes(aggregation)) {
log.warn(`No valid aggregation present in ${name} property`)();
aggregation = 'any';
}
properties.push({name: name as keyof FeaturePropertiesT, aggregation});
Expand Down

0 comments on commit 6971bc6

Please sign in to comment.