Skip to content
Closed
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
efaaea5
Optimize cross tile index searching
bradymadden97 Oct 31, 2025
c834bd2
newline
bradymadden97 Oct 31, 2025
1c0d4f2
clarify comments
bradymadden97 Nov 1, 2025
4600214
Fix iteration logic
bradymadden97 Nov 1, 2025
6dda944
Revert
bradymadden97 Nov 1, 2025
77a9904
Merge branch 'bmadden/cross-tile-symbol-index-optimization' of https:…
bradymadden97 Nov 1, 2025
91ad4b2
Add check to test
bradymadden97 Nov 1, 2025
88ae4e0
Merge branch 'bmadden/add-check-to-cross-tile-symbol-index-test' into…
bradymadden97 Nov 1, 2025
0c0f587
Merge branch 'main' into bmadden/cross-tile-symbol-index-optimization
bradymadden97 Nov 1, 2025
392f7d3
Merge branch 'main' into bmadden/cross-tile-symbol-index-optimization
HarelM Nov 3, 2025
c1dc44f
Merge branch 'main' into bmadden/cross-tile-symbol-index-optimization
bradymadden97 Nov 5, 2025
99c7530
Merge branch 'bmadden/cross-tile-symbol-index-optimization' of https:…
bradymadden97 Nov 6, 2025
b0e7909
update for comments & add changelog
bradymadden97 Nov 6, 2025
1b92449
Merge branch 'main' into bmadden/cross-tile-symbol-index-optimization
bradymadden97 Nov 6, 2025
83c84a8
better perf
bradymadden97 Nov 7, 2025
a98ee11
update to test indexing code path for existing tests
bradymadden97 Nov 7, 2025
3404589
Merge branch 'main' into bmadden/cross-tile-symbol-index-optimization
bradymadden97 Nov 7, 2025
b6a37ef
Merge branch 'main' into bmadden/cross-tile-symbol-index-optimization
bradymadden97 Nov 10, 2025
1134558
refactor for clarity
bradymadden97 Nov 10, 2025
6574aee
indent
bradymadden97 Nov 10, 2025
4cf39a2
Remove double space
HarelM Nov 10, 2025
f4d0304
Merge branch 'main' into bmadden/cross-tile-symbol-index-optimization
bradymadden97 Nov 10, 2025
4beaad2
rename and types
bradymadden97 Nov 10, 2025
386288a
Merge branch 'bmadden/cross-tile-symbol-index-optimization' of https:…
bradymadden97 Nov 10, 2025
578fdd8
longer comment
bradymadden97 Nov 10, 2025
20eca82
pull out
bradymadden97 Nov 10, 2025
964916c
xy map
bradymadden97 Nov 10, 2025
4989773
add tests specifically for indexing
bradymadden97 Nov 10, 2025
a954ea8
Update CHANGELOG.md
bradymadden97 Nov 10, 2025
5c2c793
rename
bradymadden97 Nov 10, 2025
880833a
Merge branch 'main' into bmadden/cross-tile-symbol-index-optimization
bradymadden97 Nov 10, 2025
f729a85
pull out to method
bradymadden97 Nov 10, 2025
3d218f3
toBeDefined
bradymadden97 Nov 10, 2025
8ae7b9c
Merge branch 'main' into bmadden/cross-tile-symbol-index-optimization
bradymadden97 Jan 9, 2026
7f28e73
bump bundle size
bradymadden97 Jan 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions src/symbol/cross_tile_symbol_index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ describe('CrossTileSymbolIndex.addLayer', () => {

});

test('indexes data for findMatches perf', () => {
test('matches ids when indexing', () => {
const index = new CrossTileSymbolIndex();

const mainID = new OverscaledTileID(6, 0, 6, 8, 8);
Expand All @@ -223,9 +223,10 @@ describe('CrossTileSymbolIndex.addLayer', () => {
const mainInstances: any[] = [];
const childInstances: any[] = [];

for (let i = 0; i < KDBUSH_THRESHHOLD + 1; i++) {
mainInstances.push(makeSymbolInstance(0, 0, ''));
childInstances.push(makeSymbolInstance(0, 0, ''));
const INSTANCE_COUNT = KDBUSH_THRESHHOLD + 1;
for (let i = 0; i < INSTANCE_COUNT; i++) {
mainInstances.push(makeSymbolInstance(i, i, ''));
childInstances.push(makeSymbolInstance(i, i, ''));
}
const mainTile = makeTile(mainID, mainInstances);
const childTile = makeTile(childID, childInstances);
Expand All @@ -234,7 +235,10 @@ describe('CrossTileSymbolIndex.addLayer', () => {

// check that we matched the parent tile
expect(childInstances[0].crossTileID).toBe(1);

// all child instances matched a crossTileID from the parent, otherwise
// we would have generated a new crossTileID, and the number would
// exceed INSTANCE_COUNT
expect(Math.max(...childInstances.map(i => i.crossTileID))).toBe(INSTANCE_COUNT);
});
});

Expand Down
80 changes: 62 additions & 18 deletions src/symbol/cross_tile_symbol_index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ class TileLayerIndex {
}) {
const tolerance = this.tileID.canonical.z < newTileID.canonical.z ? 1 : Math.pow(2, this.tileID.canonical.z - newTileID.canonical.z);

const symbolKeyToScaledCoordinatesToSymbolInstanceMap = new Map<number, Map<number, SymbolInstance[]>>();

for (let i = 0; i < symbolInstances.length; i++) {
const symbolInstance = symbolInstances.get(i);
if (symbolInstance.crossTileID) {
Expand All @@ -120,25 +122,21 @@ class TileLayerIndex {
const scaledSymbolCoord = this.getScaledCoordinates(symbolInstance, newTileID);

if (entry.index) {
// Return any symbol with the same keys whose coordinates are within 1
// grid unit. (with a 4px grid, this covers a 12px by 12px area)
const indexes = entry.index.range(
scaledSymbolCoord.x - tolerance,
scaledSymbolCoord.y - tolerance,
scaledSymbolCoord.x + tolerance,
scaledSymbolCoord.y + tolerance).sort();

for (const i of indexes) {
const crossTileID = entry.crossTileIDs[i];

if (!zoomCrossTileIDs[crossTileID]) {
// Once we've marked ourselves duplicate against this parent symbol,
// don't let any other symbols at the same zoom level duplicate against
// the same parent (see issue #5993)
zoomCrossTileIDs[crossTileID] = true;
symbolInstance.crossTileID = crossTileID;
break;
// Build a map keyed by common symbol instance keys, which are also
// used to key indexes. For each symbol index key, build a reverse map
// of the scaled coordinates back to a set of SymbolInstance that
// resolve to that coordinate.
const scaledCoordinateId = scaledSymbolCoord.x << 16 | scaledSymbolCoord.y;
Comment thread
HarelM marked this conversation as resolved.
Outdated
const existingSymbolKey = symbolKeyToScaledCoordinatesToSymbolInstanceMap.get(symbolInstance.key);
if (existingSymbolKey) {
const existingSymbolsAtCoordinate = existingSymbolKey.get(scaledCoordinateId);
if (existingSymbolsAtCoordinate) {
existingSymbolsAtCoordinate.push(symbolInstance);
} else {
existingSymbolKey.set(scaledCoordinateId, [symbolInstance]);
}
} else {
symbolKeyToScaledCoordinatesToSymbolInstanceMap.set(symbolInstance.key, new Map([[scaledCoordinateId, [symbolInstance]]]));
}
} else if (entry.positions) {
for (let i = 0; i < entry.positions.length; i++) {
Expand All @@ -160,6 +158,52 @@ class TileLayerIndex {
}
}
}

for (const [symbolKey, coordinateMap] of symbolKeyToScaledCoordinatesToSymbolInstanceMap.entries()) {
Comment thread
HarelM marked this conversation as resolved.
Outdated
Comment thread
HarelM marked this conversation as resolved.
Outdated
const entry = this._symbolsByKey[symbolKey];
if (!entry) {
// Shouldn't happen, as we only populate keys in this
Comment thread
bradymadden97 marked this conversation as resolved.
Outdated
// map above when an entry exists.
continue;
}

if (!entry.index){
// Shouldn't happen, as we only populate keys in this
// map above when the entry has an index built.
continue;
}

for (const [coordinateId, symbolInstancesAtCoordinate] of coordinateMap.entries()) {
const x = coordinateId >>> 16;
const y = coordinateId % (1 << 16);
const indexes = entry.index.range(
Comment thread
HarelM marked this conversation as resolved.
x - tolerance,
y - tolerance,
x + tolerance,
y + tolerance);

// Iterate through cross tile entries at this quadrant _and_
// symbol instances and pair them up until one of the lists
// runs out. This is faster than re-running a range query
// for every symbol instance, as each of these symbol
// instances already have the same key, and thus can be
// paired with any entry in the index.
let i = 0;
let j = 0;
while (i < entry.crossTileIDs.length && j < Math.min(indexes.length, symbolInstancesAtCoordinate.length)) {
const crossTileID = entry.crossTileIDs[i];
if (!zoomCrossTileIDs[crossTileID]) {
// Once we've marked ourselves duplicate against this parent symbol,
// don't let any other symbols at the same zoom level duplicate against
// the same parent (see issue #5993)
zoomCrossTileIDs[crossTileID] = true;
symbolInstancesAtCoordinate[j].crossTileID = crossTileID;
j++;
}
i++;
}
}
}
}

getCrossTileIDsLists() {
Expand Down