Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Duplicate coordinates throwing No cluster with the specified ID #103

Open
ollieh-m opened this issue Oct 22, 2018 · 16 comments
Open

Duplicate coordinates throwing No cluster with the specified ID #103

ollieh-m opened this issue Oct 22, 2018 · 16 comments

Comments

@ollieh-m
Copy link

We are using Supercluster wrapped in https://github.com/novalabio/react-native-maps-super-cluster

On loading our map, we've experienced the No cluster with the specified ID error when clicking on a cluster. This was happening within the following call to getLeaves(), where clusteringEngine is Supercluster.

clusteringEngine
    .getLeaves(clusterId, 100)
    .map(c => c.properties.item);

It turns out the error happened when a cluster had two points that shared exactly the same coordinates, leading to the below stack trace, where I log out from the getChildren() function. As you can see, it thinks the child of the cluster is also a cluster at the same coordinates, and the child of that cluster is also apparently a cluster at the same coordinates, but that 'cluster' doesn't have any children IDs so the error is thrown.

I'm wondering:

  1. whether Supercluster is already meant to handle multiple points at the same coordinates, in which case is this a bug?; and
  2. whether in the event that getChildren cannot find the children for a cluster, an empty array should be returned rather than an error thrown. Or perhaps _appendLeaves should set children to an empty array if the error gets thrown, like this:
_appendLeaves: function (result, clusterId, limit, offset, skipped) {
        try {
          var children = this.getChildren(clusterId);
        } catch(error){
          if (error.message.includes('No cluster with the specified id')) {
            children = []
          } else {
            throw error
         }
        }
     ...

Stack trace:

2018-10-19 21:27:40.887 [info][tid:com.facebook.react.JavaScript] ------------------------ get children
2018-10-19 21:27:40.887170+0100 Refill[68457:2682417] ------------------------ get children
2018-10-19 21:27:40.887 [info][tid:com.facebook.react.JavaScript] 'clusterId', 368017
2018-10-19 21:27:40.887494+0100 Refill[68457:2682417] 'clusterId', 368017
2018-10-19 21:27:40.888 [info][tid:com.facebook.react.JavaScript] '------------------------ ids', [ 11500 ]
2018-10-19 21:27:40.887822+0100 Refill[68457:2682417] '------------------------ ids', [ 11500 ]
2018-10-19 21:27:40.888 [info][tid:com.facebook.react.JavaScript] 'c.parentId', 368017
2018-10-19 21:27:40.888069+0100 Refill[68457:2682417] 'c.parentId', 368017
2018-10-19 21:27:40.888 [info][tid:com.facebook.react.JavaScript] push into children array
2018-10-19 21:27:40.888282+0100 Refill[68457:2682417] push into children array
2018-10-19 21:27:40.889 [info][tid:com.facebook.react.JavaScript] 'children', [ { type: 'Feature',
    properties: 
     { cluster: true,
       cluster_id: 385970,
       point_count: 2,
       point_count_abbreviated: 2 },
    geometry: 
     { type: 'Point',
       coordinates: [ -5.538370000000008, 50.11913999999999 ] } } ]
2018-10-19 21:27:40.888730+0100 Refill[68457:2682417] 'children', [ { type: 'Feature',
    properties: 
     { cluster: true,
       cluster_id: 385970,
       point_count: 2,
       point_count_abbreviated: 2 },
    geometry: 
     { type: 'Point',
       coordinates: [ -5.538370000000008, 50.11913999999999 ] } } ]
2018-10-19 21:27:40.889 [info][tid:com.facebook.react.JavaScript] ------------------------ get children
2018-10-19 21:27:40.888954+0100 Refill[68457:2682417] ------------------------ get children
2018-10-19 21:27:40.889 [info][tid:com.facebook.react.JavaScript] 'clusterId', 385970
2018-10-19 21:27:40.889217+0100 Refill[68457:2682417] 'clusterId', 385970
2018-10-19 21:27:40.889 [info][tid:com.facebook.react.JavaScript] '------------------------ ids', [ 12061 ]
2018-10-19 21:27:40.889467+0100 Refill[68457:2682417] '------------------------ ids', [ 12061 ]
2018-10-19 21:27:40.890 [info][tid:com.facebook.react.JavaScript] 'c.parentId', 385970
2018-10-19 21:27:40.889677+0100 Refill[68457:2682417] 'c.parentId', 385970
2018-10-19 21:27:40.890 [info][tid:com.facebook.react.JavaScript] push into children array
2018-10-19 21:27:40.889931+0100 Refill[68457:2682417] push into children array
2018-10-19 21:27:40.890 [info][tid:com.facebook.react.JavaScript] 'children', [ { type: 'Feature',
    properties: 
     { cluster: true,
       cluster_id: 388691,
       point_count: 2,
       point_count_abbreviated: 2 },
    geometry: 
     { type: 'Point',
       coordinates: [ -5.538370000000008, 50.11913999999999 ] } } ]
2018-10-19 21:27:40.890418+0100 Refill[68457:2682417] 'children', [ { type: 'Feature',
    properties: 
     { cluster: true,
       cluster_id: 388691,
       point_count: 2,
       point_count_abbreviated: 2 },
    geometry: 
     { type: 'Point',
       coordinates: [ -5.538370000000008, 50.11913999999999 ] } } ]
2018-10-19 21:27:40.891 [info][tid:com.facebook.react.JavaScript] ------------------------ get children
2018-10-19 21:27:40.890627+0100 Refill[68457:2682417] ------------------------ get children
2018-10-19 21:27:40.891 [info][tid:com.facebook.react.JavaScript] 'clusterId', 388691
2018-10-19 21:27:40.890848+0100 Refill[68457:2682417] 'clusterId', 388691
2018-10-19 21:27:40.891 [info][tid:com.facebook.react.JavaScript] '------------------------ ids', []
2018-10-19 21:27:40.891095+0100 Refill[68457:2682417] '------------------------ ids', []
2018-10-19 21:27:40.891 [info][tid:com.facebook.react.JavaScript] 'children', []
2018-10-19 21:27:40.891329+0100 Refill[68457:2682417] 'children', []
2018-10-19 21:27:42.898 [warn][tid:com.facebook.react.JavaScript] Possible Unhandled Promise Rejection (id: 0):
Error: No cluster with the specified id.
@mourner
Copy link
Member

mourner commented Oct 22, 2018

@ollieh-m thanks for the report! In theory, duplicate points shouldn't be a problem for the library. Would it be possible to set up a minimal reproducible test case so that I could investigate what's going on under the hood?

@mourner
Copy link
Member

mourner commented Nov 1, 2018

Closing because of not being able to reproduce this. Will reopen if there's a minimal reproducible use case.

@mourner mourner closed this as completed Nov 1, 2018
@djibarian
Copy link

I’m having the same problem and is clearly because of points with exactly the same coordinates inside the cluster. Initially I set the clusterMaxZoom value to 99 because I wanted those clusters to return a zoom value of 100 in getClusterExpansionZoom(), but that doesn’t seem to work.

Playing with clusterMaxZoom values it looks that there is a hard limit for the biggest zoom level returned to be 31 (probably as a safety check for infinite recursion, or maybe because a zoom level bigger than 31 can’t be specified nowhere in Mapbox?), so clusterMaxZoom has to have a maximum value of 30 to avoid the error. This way single-coordinate clusters can be recognised because they return a zoom value of 31.

For the record, but it would be good to fix the getClusterExpansionZoom() function to return a more descriptive error or else document this behavior.

(By the way, amazed that nobody else complained in more than two years!)

@mourner mourner reopened this May 6, 2021
@mourner
Copy link
Member

mourner commented May 6, 2021

@djibarian thanks for bringing this up! Would you be able to set up a minimal test case for this so that I could promptly provide a fix?

@ozaik
Copy link

ozaik commented May 6, 2021

We had the same problem (with a lot of duplicate coordinate), we set the cluster maxZoom to 30 and everything works.

@djibarian
Copy link

@mourner How do I do that? Do you have a sandbox or playground I can set up?

@mourner
Copy link
Member

mourner commented May 6, 2021

@djibarian you could just use https://jsfiddle.net/ or https://jsbin.com/

@ozaik
Copy link

ozaik commented May 6, 2021

To reproduce the error :

  • Just click on the cluster with 2 points on the same place, this throws an error.

To fix it adjust the maxZoom to a value under 31

The code I used


import React, { useRef, useState } from 'react'
import GoogleMapReact from 'google-map-react'
import useSupercluster from 'use-supercluster'

function MainMap (props) {
    const mapRef = useRef(null)
    const [bounds, setBounds] = useState(null)
    const [zoom, setZoom] = useState(10)
    const Marker = ({ children }) => children

    const points = [
        {
            type: 'Feature',
            geometry: {
                type: 'Point',
                coordinates: [
                    6.59874,
                    46.55043
                ]
            },
            properties: {
                nickname: 'Doris'
            }
        },
        {
            type: 'Feature',
            geometry: {
                type: 'Point',
                coordinates: [
                    6.59874,
                    46.55043
                ]
            },
            properties: {
                nickname: 'Doris'
            }
        }
    ]

    const { clusters, supercluster } = useSupercluster({
        points,
        bounds,
        zoom,
        // Switch maxZoom to 30 to fix the error
        options: { radius: 100, maxZoom: 31 }
    })

    const defaultProps = {
        center: {
            lat: 46.657505,
            lng: 7.099246
        },
        zoom: 9
    }

    return (
        <div style={{ height: '100vh', width: '100%' }}>
            <GoogleMapReact
                bootstrapURLKeys={{ /* key:  YourKey */ }}
                defaultCenter={defaultProps.center}
                defaultZoom={defaultProps.zoom}
                onGoogleApiLoaded={({ map }) => {
                    console.log(map)
                    mapRef.current = map
                }}
                onChange={({ zoom, bounds }) => {
                    setZoom(zoom)
                    setBounds([
                        bounds.nw.lng,
                        bounds.se.lat,
                        bounds.se.lng,
                        bounds.nw.lat
                    ])
                }}
            >
                {clusters.map(cluster => {
                    const [longitude, latitude] = cluster.geometry.coordinates
                    const {
                        cluster: isCluster,
                        point_count: pointCount
                    } = cluster.properties

                    if (isCluster) {
                        return (
                            <Marker
                                key={`cluster-${cluster.id}`}
                                lat={latitude}
                                lng={longitude}
                            >
                                <div
                                    style={{
                                        width: `${10 + (pointCount / points.length) * 20}px`,
                                        height: `${10 + (pointCount / points.length) * 20}px`,
                                        color: '#fff',
                                        background: '#1978c8',
                                        borderRadius: '50%',
                                        padding: '10px',
                                        display: 'flex',
                                        justifyContent: 'center',
                                        alignItems: 'center'
                                    }}
                                    onClick={() => {
                                        console.log(cluster)
                                        console.log(cluster.id)
                                        console.log(supercluster.getLeaves(cluster.id, Infinity))
                                    }}
                                >
                                    {pointCount}
                                </div>
                            </Marker>
                        )
                    } else {
                        return (
                            <Marker
                                key={cluster.id}
                                lat={latitude}
                                lng={longitude}
                            >
                                <button
                                    style={{
                                        color: '#fff',
                                        background: '#1978c8',
                                        borderRadius: '50%',
                                        padding: '5px',
                                        display: 'flex',
                                        justifyContent: 'center',
                                        alignItems: 'center'
                                    }}
                                    onClick={() => { console.log(cluster) }}>
                                </button>
                            </Marker>
                        )
                    }
                })}
            </GoogleMapReact>
        </div>
    )
}
export default MainMap

@mourner
Copy link
Member

mourner commented May 10, 2021

@ozaik would you mind turning this into a minimal (purely Supercluster without other libraries), live (a link to JSFiddle/JSBin) test case?

@fjorgemota
Copy link

Hello!

I copied the example made by @ozaik into a codesandbox and adapted it so we can see the issue without any external library. Only supercluster here. Here's the link to the codesandbox: https://codesandbox.io/s/supercluster-no-cluster-with-the-specified-id-rtiqk?file=/src/index.js - I know it's not JSFiddle/JSbin, but I hope it's sufficient as a test case (as you don't need an account to mess with it either)

By the way. I'm having the same issue while using supercluster with the ol-supercluster: I get an apparently valid cluster_id (9) by scanning the output of getClusters, but for some reason when I call getLeaves(cluster_id, Infinity) I get that weird error. However, the maxZoom computed by OpenLayers is..28, so I don't think the issue is only the thing that affects if the error will happen or not here.

@Oryss
Copy link

Oryss commented Oct 21, 2021

release 7.1.4 fixed it for me

@mourner
Copy link
Member

mourner commented Oct 21, 2021

Fantastic! So this might have been the same issue as #189. 🎉 Let's close this one for now then, unless anyone is still experiencing problems on the latest version.

@mourner mourner closed this as completed Oct 21, 2021
@fjorgemota
Copy link

@mourner Before closing the issue, could you please have checked the example I posted in the comment above?

For some weird scenarios, this issue still happens, even on version 7.1.4. Here's the updated codesandbox with the code still failing on 7.1.4: https://codesandbox.io/s/supercluster-no-cluster-with-the-specified-id-forked-r8jc6?file=/src/index.js

Thanks.

@Oryss
Copy link

Oryss commented Oct 21, 2021

(My issue wasn't related to zoom level, but it did fix the exception thrown when iterating over clusters and trying to use getLeaves on a cluster)

@mourner mourner reopened this Oct 21, 2021
@jeremy-ellis-tech
Copy link

Also having this issue with 7.1.5 with maxZoom > 30

@semoal
Copy link

semoal commented Jul 23, 2023

On my case, if this helps someone was:

 const [points, supercluster] = useClusterer(
    debouncedDataToBeMerged ? debouncedDataToBeMerged : [],
    SCREEN,
    mapCoordsDebounced,
    SUPERCLUSTER_OPTS
  );

I have this hook that creates a supercluster instance, but forgot to add the dep into onClusterPress:

  const onClusterPress = useCallback(
    async (clusterId: number) => {
      {
        const regionToAnimate = supercluster.expandCluster(clusterId);
        const zoom = supercluster.getClusterExpansionZoom(clusterId);
        mapRef.current?.animateCamera({
          center: {
            ...regionToAnimate,
          },
          zoom: zoom + 2,
        });
      }
    },
-  [],
+ [supercluster]
  );

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants