Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
76 changes: 76 additions & 0 deletions patches/kdbush+4.0.2.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
diff --git a/node_modules/kdbush/index.d.ts b/node_modules/kdbush/index.d.ts
index b874ea6..f9142d9 100644
--- a/node_modules/kdbush/index.d.ts
+++ b/node_modules/kdbush/index.d.ts
@@ -41,6 +41,7 @@ export default class KDBush {
* @returns {number[]} An array of indices correponding to the found items.
*/
range(minX: number, minY: number, maxX: number, maxY: number): number[];
+ rangeSome(minX: number, minY: number, maxX: number, maxY: number, visit: (value: number) => boolean): void;
/**
* Search the index for items within a given radius.
* @param {number} qx
diff --git a/node_modules/kdbush/index.js b/node_modules/kdbush/index.js
index d9a8225..94c9e67 100644
--- a/node_modules/kdbush/index.js
+++ b/node_modules/kdbush/index.js
@@ -165,6 +165,59 @@ export default class KDBush {
return result;
}

+ // NOTE: This method was added using patch-package to solve https://github.com/maplibre/maplibre-gl-js/issues/6192
+ /**
+ * Search the index for items within a given bounding box, allows stopping on the once finding a match.
+ * @param {number} minX
+ * @param {number} minY
+ * @param {number} maxX
+ * @param {number} maxY
+ * @param {(value: number) => boolean} visit A function that is called for each item found. If it returns `true`, the search is aborted.
+ */
+ rangeSome(minX, minY, maxX, maxY, visit) {
+ if (!this._finished) throw new Error('Data not yet indexed - call index.finish().');
+
+ const { ids, coords, nodeSize } = this;
+ const stack = [0, ids.length - 1, 0];
+
+ // recursively search for items in range in the kd-sorted arrays
+ while (stack.length) {
+ const axis = stack.pop() || 0;
+ const right = stack.pop() || 0;
+ const left = stack.pop() || 0;
+
+ // if we reached "tree node", search linearly
+ if (right - left <= nodeSize) {
+ for (let i = left; i <= right; i++) {
+ const x = coords[2 * i];
+ const y = coords[2 * i + 1];
+ if (x >= minX && x <= maxX && y >= minY && y <= maxY && visit(ids[i])) return;
+ }
+ continue;
+ }
+
+ // otherwise find the middle index
+ const m = (left + right) >> 1;
+
+ // include the middle item if it's in range
+ const x = coords[2 * m];
+ const y = coords[2 * m + 1];
+ if (x >= minX && x <= maxX && y >= minY && y <= maxY && visit(ids[m])) return;
+
+ // queue search in halves that intersect the query
+ if (axis === 0 ? minX <= x : minY <= y) {
+ stack.push(left);
+ stack.push(m - 1);
+ stack.push(1 - axis);
+ }
+ if (axis === 0 ? maxX >= x : maxY >= y) {
+ stack.push(m + 1);
+ stack.push(right);
+ stack.push(1 - axis);
+ }
+ }
+ }
+
/**
* Search the index for items within a given radius.
* @param {number} qx
22 changes: 11 additions & 11 deletions src/symbol/cross_tile_symbol_index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,24 +122,24 @@
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(
entry.index.rangeSome(

Check failure on line 125 in src/symbol/cross_tile_symbol_index.ts

View workflow job for this annotation

GitHub Actions / Code Hygiene

Property 'rangeSome' does not exist on type 'KDBush'.
scaledSymbolCoord.x - tolerance,
scaledSymbolCoord.y - tolerance,
scaledSymbolCoord.x + tolerance,
scaledSymbolCoord.y + tolerance).sort();
scaledSymbolCoord.y + tolerance, (i) => {
const crossTileID = entry.crossTileIDs[i];

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

if (!zoomCrossTileIDs[crossTileID]) {
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;
}
}
zoomCrossTileIDs[crossTileID] = true;
symbolInstance.crossTileID = crossTileID;
return true;
}

return false;
});
} else if (entry.positions) {
for (let i = 0; i < entry.positions.length; i++) {
const thisTileSymbol = entry.positions[i];
Expand Down
88 changes: 88 additions & 0 deletions test/examples/many-overlapping-symbols.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Many overlapping symbols</title>
<meta property="og:description" content="Many overlapping symbols." />
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="../../dist/maplibre-gl.css" />
<script src="../../dist/maplibre-gl-dev.js"></script>
<style>
body {
margin: 0;
padding: 0;
}

html,
body,
#map {
height: 100%;
}
</style>
</head>

<body>
<div id="map"></div>
<script>
const map = new maplibregl.Map({
container: "map",
style: "https://tiles.openfreemap.org/styles/liberty",
center: [-74.5, 40],
zoom: 2,
});

function generateRandomPoints(count, bounds) {
const features = [];

for (let i = 0; i < count; i++) {
const lng =
bounds.west +
Math.random() * (bounds.east - bounds.west);
const lat =
bounds.south +
Math.random() * (bounds.north - bounds.south);

features.push({
type: "Feature",
geometry: {
type: "Point",
coordinates: [lng, lat],
},
properties: {
image: Math.random() > 0.5 ? "marker" : "airport",
},
});
}

return {
type: "FeatureCollection",
features: features,
};
}

map.on("load", async () => {
const pointData = generateRandomPoints(10000, {
west: -75.5,
east: -73.5,
south: 39.5,
north: 41.5,
});

map.addSource("random-points", {
type: "geojson",
data: pointData,
});

map.addLayer({
id: "symbols",
type: "symbol",
source: "random-points",
layout: {
"icon-image": ["get", "image"],
"icon-allow-overlap": true,
},
});
});
</script>
</body>
</html>
Loading