Mapbox-gl-js: How to cluster custom markers ?

Created on 24 Mar 2019  路  9Comments  路  Source: mapbox/mapbox-gl-js

Hi!

How can I cluster custom markers ? I did not find anywhere how to cluster marker like Google Maps for example
https://developers.google.com/maps/documentation/javascript/marker-clustering
That is, clustering markers and when we zoom the cluster circle transforms to markers

Thanks in advance for your help

Most helpful comment

Is there a good solution for this, I also add markers manually to the map and can't find a way to cluster them.

All 9 comments

@mighty-nyaina and anyone else in need of help please see my answer here.
Custom HTML clusters and markers with clean-up code
https://github.com/mapbox/mapbox-gl-js/issues/4491#issuecomment-501442036

You're either looking for https://docs.mapbox.com/mapbox-gl-js/example/cluster/, or a more advanced https://docs.mapbox.com/mapbox-gl-js/example/cluster-html/

@mourner I don麓t think thats the case TBH. You are referring the cases where custom html Clusters are rendered, or clusters with layered points for markers.

but the person, and many of us, are looking into clustering custom markers. The problem with this approach is that we need to remove custom markers from the map when they are inside the layered generated cluster, and vice-versa.

@mighty-nyaina / @luchux Did (any of) you ever find a solution to this ? I'm having the same issue. I'm adding markers not based from a json, but from user entered values, which works, but I have 4 locations within 1 square kilometer, which I want to cluster, but can't seem to get it done with the provided examples.

@Beee4life I have applied this solution:
Note this code will not work as it is, is just parts to explain you the solution.
I add a Source spaces, and a cluster layer for it. I don麓t create markers as layer, but instead I traverse the map feature list and from those that are not cluster I create a Map.marker instance with custom html. I update those markers based on each zoom-end event.

  • Use native API clusters through source layers as explained in Mapbox docs.
map.addSource('spaces', {
      type: 'geojson',
      data: {
        type: 'FeatureCollection',
        features: parseGeoData(theSpacesData),
      },
      cluster: true,
      clusterMaxZoom: theme.clusters.maxZoom,
      clusterRadius: theme.clusters.radius,
    });
map.addLayer({
      id: 'clusters',
      type: 'circle',
      source: 'spaces',
      filter: ['has', 'point_count'],
      paint: {
        'circle-color': theme.clusters.backgroundColor,
        'circle-radius': theme.clusters.circleRadius,
      },

In this way you get the "spaces" data, and the clusters calculated when you zoom in-out by the native Mapbox APIs.

  • Then on each map zoom in-out, recalculate custom markers. What you can do is:

    1. Because features come from tiled vector data, feature geometries may be split or duplicated across tile boundaries and, as a result, features may appear multiple times in query results. I define a function to identify unique Features.
export const getUniqueFeatures = (array, comparatorProperty) => {
  var existingFeatureKeys = {};

  var uniqueFeatures = array.filter(function(el) {
    if (existingFeatureKeys[el.properties[comparatorProperty]]) {
      return false;
    } else {
      existingFeatureKeys[el.properties[comparatorProperty]] = true;
      return true;
    }
  });
  return uniqueFeatures;
};
    2.
    const features = this.map.querySourceFeatures('spaces');
    let spaces = features.filter(elem => !elem.properties.cluster);
    spaces = getUniqueFeatures(spaces, 'title');

Above, you get the spaces features from the map and you can filter those that are not clusters, (those inside clusters will not appear as features in the map).

From here on you can traverse the list of your unique features, and check if you need to add or remove those markers to your custom markers list (if they are already rendered, don麓t render them, if they are not render them, and those that shouldn麓t be rendered and are rendered remove from markers). Keep a list of them in a state, and you will be able to re render and remove on zooming behaviour.

  1. I add markers to a Set (id-> marker) as where marker= new mapboxgl.Marker() with custom html dom element. In this way I can decide if I should remove or keep each marker, based on space=getUniqueFeatures().

Thank, appreciate the feedback... I will try to look into it asap...

@luchux I have looked but can't wrap my head around it....

I have uploaded my code here to give you a better look at it (if you would like to)...

The map is built here and the (added) settings are set here.

Lines 51 - 103 of the js file are new/added and aren't needed for the map which is shown here.

Is there a good solution for this, I also add markers manually to the map and can't find a way to cluster them.

I think you will need to do this manually, there is no implented way of doing this inside mapbox. Atleast that's my experience with mapbox.

My solution to this was to check all the markers inside the camera and check if any of those collided with each other. Those who did was added into different "cluster"-arrays, and when all the collision has been checked, I looped through the array and added new markers between the ones that collided. At the same time i hid the markers that was now inside the cluster group. I hope this can help someone into the right direction.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

foundryspatial-duncan picture foundryspatial-duncan  路  3Comments

rigoneri picture rigoneri  路  3Comments

yoursweater picture yoursweater  路  3Comments

Scarysize picture Scarysize  路  3Comments

PBrockmann picture PBrockmann  路  3Comments