mapbox / mapbox-gl-native

Interactive, thoroughly customizable maps in native Android, iOS, macOS, Node.js, and Qt applications, powered by vector tiles and OpenGL
https://mapbox.com/mobile
Other
4.37k stars 1.33k forks source link

Implement Spiderifier for same coordinates markers #13259

Closed moorthysubu closed 5 years ago

moorthysubu commented 5 years ago

I've clustered all the markers using the symbol layer GeoJsonOptions, when the user clicks on the count of the group of coordinates (here all markers have exactly same coordinates ex: 8 images are pinned in same coordinates) I need to show the spiderifed markers around the counted markers like Leaflet showing on web, Any Inbuild support is mapbox given to implement this effect in Android Thanks in advance

tobrun commented 5 years ago

@moorthysubu such a UX interaction will be possible with the addition of some new supercluster APIs happening in https://github.com/mapbox/mapbox-gl-native/pull/12726 (getChilderen/getLeaves). This PR is still a WIP so don't have an eta for that feature just yet. Closing as answered, thank you for reaching out!

ravenfeld commented 1 year ago

Hello, I can't find any documentation to set up the spiderifier on Android. I understood that it was with the cluster PR but I don't see anything about the spiderifier

Thank you in advance

georgiecasey commented 1 year ago

@ravenfeld did you ever find a way to use spiderifier on Android?

ravenfeld commented 1 year ago

no, I recoded something myself

georgiecasey commented 1 year ago

@ravenfeld could you share some pointers on what you did? I notice a lot of the cluster render stuff is in the C++ code so I'm not sure how to code something up in Java/Kotlin. Only thing I can think of is to slightly change the lat,long of Feature in a FeatureCollection if I see they're the same coordinates

ravenfeld commented 1 year ago
internal object SpideriFierAlgorithm {
    private var layer = 0
    private var numberOfMarkersInLayer = 0
    private var numberOfMarkersMaxInLayer = 0
    private var indexInLayer = 0
    private var distanceTimesLayer = 0.0
    private var polygon: Polygon? = null

    fun spiderify(markerClusters: List<MarkerCluster>): List<MarkerCluster> {
        numberOfMarkersMaxInLayer = markerClusters.size
        val firstMarker = markerClusters.first()
        setLayer(layer = 1, center = firstMarker.latLng.toPoint())

        return markerClusters.map {
            val marker = MarkerCluster(latLng = getLatLng(indexInLayer), properties = it.properties)
            indexInLayer++
            if (indexInLayer == numberOfMarkersInLayer) {
                setLayer(layer = layer + 1, center = firstMarker.latLng.toPoint())
            }
            marker
        }
    }

    private fun setLayer(layer: Int, center: Point) {
        SpideriFierAlgorithm.layer = layer
        numberOfMarkersInLayer = numberOfPinsInLayer(layer)
        indexInLayer = 0
        distanceTimesLayer = layer * RADIUS_CIRCLE_IN_METER
        polygon = TurfTransformation.circle(
            center,
            distanceTimesLayer,
            numberOfMarkersMaxInLayer,
            TurfConstants.UNIT_METRES
        )
    }

    private fun numberOfPinsInLayer(layer: Int): Int {
        return numberOfMarkersMaxInLayer * layer
    }

    private fun getLatLng(position: Int) =
        polygon?.coordinates()?.firstOrNull()?.get(position)?.toLatLng()
            ?: error("Polygon don't null")
}

Here is my algo to make a spiderifier. I don't use it anymore because it can cause problems. I can display an element in the same place as another or close to it and it becomes weird. I use the cluster and keep a state in cluster and when I click on it I get back the elements and make a render attached to the map.