From 6971bc6dcaa81a1ab5963bfdac85c7b13d22c3ef Mon Sep 17 00:00:00 2001 From: felixpalmer Date: Thu, 8 Aug 2024 14:27:00 +0200 Subject: [PATCH] CARTO: Re-aggregate properties on change (#9078) --- .../carto/src/layers/cluster-tile-layer.ts | 12 +++++++-- modules/carto/src/layers/cluster-utils.ts | 25 ++++++++++++++++--- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/modules/carto/src/layers/cluster-tile-layer.ts b/modules/carto/src/layers/cluster-tile-layer.ts index 4b9aba1b77a..1b35694c5d8 100644 --- a/modules/carto/src/layers/cluster-tile-layer.ts +++ b/modules/carto/src/layers/cluster-tile-layer.ts @@ -122,18 +122,26 @@ class ClusterGeoJsonLayer< const properties = extractAggregationProperties(visibleTiles[0]); const data = [] as ClusteredFeaturePropertiesT[]; + 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) { diff --git a/modules/carto/src/layers/cluster-utils.ts b/modules/carto/src/layers/cluster-utils.ts index d561df24585..39b69265529 100644 --- a/modules/carto/src/layers/cluster-utils.ts +++ b/modules/carto/src/layers/cluster-utils.ts @@ -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'; @@ -17,18 +17,33 @@ export type ClusteredFeaturePropertiesT = FeaturePropertiesT export type ParsedQuadbinCell = {id: bigint; properties: FeaturePropertiesT}; export type ParsedQuadbinTile = ParsedQuadbinCell[]; +/** + * Aggregates tile by specified properties, caching result in tile.userData + * + * @returns true if data was aggregated, false if cache used + */ export function aggregateTile( tile: Tile2DHeader>, aggregationLevels: number, properties: AggregationProperties = [], getPosition: Accessor, [number, number]>, getWeight: Accessor, 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 = {}; for (const cell of tile.content) { @@ -80,6 +95,7 @@ export function aggregateTile( } tile.userData[aggregationLevels] = Object.values(out); + return true; } export function extractAggregationProperties( @@ -90,6 +106,7 @@ export function extractAggregationProperties( 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});