mourner / kdbush

A fast static index for 2D points
ISC License
637 stars 69 forks source link

Deterministic queries #42

Closed Enkosz closed 1 year ago

Enkosz commented 1 year ago

Hello,

I've been trying to use kdbsuh (v3) as an index for geographical points to be shown in a Leaflet map for a thesis. Given the amount of markers to display I'm trying to implement a pseudoclustering by myself. I tried already different libraries like supercluster but the I didn't like the result. My use case is that the stored points can change by fetching more and merging with the already existing one when the user move on the map.

My solution was to create an index every time the markers changed (use case is to have an upper bound of maximum of 150k markers but it's kinda a really low probability use case) and compute the visible points in such way:

function getVisibleMarkers(index, markers, bounds, zoom) {
             const markersInBounds = index.range(bounds.sw.lng, bounds.sw.lat, bounds.ne.lng, bounds.ne.lat)
                                           .map((index) =>  index.points[index]);

             const overlappingMarkers = {};
             const markersToRender  = [];

           markersInBounds.forEach((marker) => {
               if(marker.id in overlappingMarkers) return;

              // we start with a 1 mil. meters radius, this is relative to the level of zoom and a dispersion factor
              // more details and more zoom means a lower radius to detect overlapping markers

               const radius = (1.000.000 / Math.pow(2, zoom * -1)) * 2.4;
               const nearbyPoints = geokdbush.around(index, marker.lng, marker.lat, Infinity, radius / 1000)  // convert meters to km
               nearbyPoints.forEach(nearbyPoint) => markersToIgnore[nearbyPoint.id] = nearbyPoint);
               markersToRender.push(marker);
   }

   return markersToRender;
 }

This is computed when the bounds change or the new markers are merged with the current one, it seems to work quite fine and fast but it's producing different results. For example, when retrieving new markers the previously shown markers are changing in favor of others even when calculating the visible markers in the same order.

I was wondering if could be that when rebuilding the index the order changes, if that's the case what could be a solution? Thanks!

mourner commented 1 year ago

My guess is that you could add determinism by sorting the results in some way (e.g. by index or x/y), but I can't say for sure because it's pretty domain-specific and depends on the details of the app. Looks like this is not an issue with the library per se, but rather a question about solving an app issue, so closing.