Clustering custom html markers with mapbox-gl-js
Asked Answered
A

2

11

I'm using the mapbox-gl-js API and I'm using it with react to create some custom markers as follows:

        let div = document.createElement('div');
        let marker = new mapboxgl.Marker(div, {
            offset: [ -20, 80 ]
        });

        marker.setLngLat(person.geometry.coordinates);

        render(
            <MapPersonIcon />,
            div,
            () => {
                marker.addTo(map);
            }
        );

result

This worked great. However I would now like to cluster these markers, producing the same affect as the functionality found with layers i.e.

https://www.mapbox.com/mapbox-gl-js/example/cluster/

clusters

Does anyone know whether this is possible (hopefully with custom clusters too) or whether it will be available in an upcoming release?

Aekerly answered 17/8, 2016 at 16:32 Comment(0)
W
8

This feature is now in Mapbox GL js - https://docs.mapbox.com/mapbox-gl-js/example/cluster-html/

Key takeaways:

When setting your data source using map.addSource, make sure you define cluster: true and clusterRadius: int, like so:

map.addSource( 'sourceName', {
    type: "geojson",
    data: {
        type: 'FeatureCollection',
        features: [JSON]
    },
    cluster: true,
    clusterRadius: 80,
});

That will push mapbox to cluster your icons, but you need to tell mapbox what to do when it clusters those icons:

map.on( 'moveend', updateMarkers ); // moveend also considers zoomend

The business (trimmed down for relevance):

function updateMarkers(){
    var features = map.querySourceFeatures( 'sourceName' );

    for ( var i = 0; i < features.length; i++ ) {
        var coords = features[ i ].geometry.coordinates;
        var props = features[ i ].properties;
            
        if ( props.cluster ){ // this property is only present when the feature is clustered
            // generate your clustered icon using props.point_count
            var el = document.createElement( 'div' );
            el.classList.add( 'mapCluster' );
            el.innerText = props.point_count;
            marker = new mapboxgl.Marker( { element: el } ).setLngLat( coords );
                
        } else { // feature is not clustered, create an icon for it
            var el = new Image();
            el.src = 'icon.png';
            el.classList.add( 'mapMarker' );
            el.dataset.type = props.type; // you can use custom data if you have assigned it in the GeoJSON data
            marker = new mapboxgl.Marker( { element: el } ).setLngLat( coords );
        }
            
        marker.addTo( map );
    }
}

NOTE: Don't copy paste this code, rather use it in conjunction with https://docs.mapbox.com/mapbox-gl-js/example/cluster-html/ to get the whole picture. Hope this helps!

Won answered 25/4, 2019 at 3:4 Comment(0)
A
2

Answering own question:

At current it seems that this isn't possible as per mapbox's github: enter image description here

If you would like to cluster your markers you will need to use mapbox's native maki icons (please see above example picture & URL) until a plugin is available for your custom HTML markers.

Aekerly answered 22/8, 2016 at 9:56 Comment(4)
When I was using Leaflet, I was able to navigate around this issue using css; I would override the default marker css and use my own background-image with styling to add a marker, and it worked really well. It is a bit frustrating that I cannot do the same as with GL as they use a canvas, and add "native" objects to the map via the Geojson/Javascript, but reject the addition of non-native elements, sadly. I will have a look through their source code and see how they add markers and if something obvious stands out and try to implement a work around. Thanks for the investigation.Automat
@AppDevGuy that would be hugely appreciated!!Aekerly
@AppDevGuy I am wondering how you got that to work... Do you have an example somewhere?Trotter
@Trotter I didn't and could not. The time cost to implement wasn't available in my scope of work.The best I could come up with was multi-layering - basically creates a border look when it's one layer on top of anotherAutomat

© 2022 - 2024 — McMap. All rights reserved.