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

How to cluster custom markers ? #8072

Closed
mighty-nyaina opened this issue Mar 24, 2019 · 12 comments
Closed

How to cluster custom markers ? #8072

mighty-nyaina opened this issue Mar 24, 2019 · 12 comments

Comments

@mighty-nyaina
Copy link

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

@mourner
Copy link
Member

mourner commented Mar 24, 2019

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 mourner closed this as completed Mar 24, 2019
@NeonCreativeStudios
Copy link

NeonCreativeStudios commented Jun 12, 2019

@mighty-nyaina and anyone else in need of help please see my answer here.
Custom HTML clusters and markers with clean-up code
#4491 (comment)

@luchux
Copy link

luchux commented Oct 5, 2019

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.

@Beee4life
Copy link

@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.

@luchux
Copy link

luchux commented Nov 13, 2019

@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;
};
    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().

@Beee4life
Copy link

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

@Beee4life
Copy link

@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.

@vedmant
Copy link

vedmant commented Aug 31, 2020

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

@lukashakkarainenvayqer
Copy link

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.

@Motoxpro
Copy link

Motoxpro commented Oct 3, 2022

Found this video/article/code that explains it perfectly https://www.leighhalliday.com/mapbox-clustering

@siyomari
Copy link

siyomari commented Oct 2, 2023

In case someone is still looking for a solution/ workaround without the need of an additional package:

You can combine adding a clusters layer with adding markers via symbol layer.

That is, on loading your map instance, you add your data source as well as a custom image, as described here https://docs.mapbox.com/mapbox-gl-js/example/geojson-markers/:

[...]

map.on('load', () => {

  map.addSource('points', {
  'type': 'geojson',
  'data': <<your data>>,
  cluster: true
  });

  map.loadImage(
  <<your asset url>> // e.g. 'https://docs.mapbox.com/mapbox-gl-js/assets/custom_marker.png',
  (error, image) => {
  if (error) throw error;
  map.addImage('<<custom name>>', image);
  });

});

[...]

Then you add your clusters layers as described in https://docs.mapbox.com/mapbox-gl-js/example/cluster/. The important part is the addLayer() function for the unclustered points:

[...]

map.addLayer( {
  id: 'unclustered-points',
  type: 'symbol',
  source: 'points',
  filter: [ '!', [ 'has', 'point_count' ] ],
  layout: {
      'icon-image': '<<custom name>>',
      'icon-size': 0.5,
  },
} );

[...]

Make sure that the custom name in the icon-image property matches the name provided in the map.addImage() function.

The Mapbox API and its filters per layer should now resolve each unclustered data point of your data to the marker image you provided.

@fredrikmoger
Copy link

Looking for something similar to this, where I can combine markers and clusters dynamically: https://svelte-maplibre.vercel.app/examples/custom_marker_clusters

The examples above seems to use LabelStyling, which doesn't enable the same visual effect. Something I'm missing here?

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

No branches or pull requests

10 participants