Avoid the issue of markers clustering in Leaflet to have distinct markerClusterGroup icons without overlap

Is there a way to prevent two separate markerClusterGroups from overlapping in Leaflet? I have one with a custom Icon to differentiate between the two clusters, but for simplicity's sake, I've omitted that part in this example.

var map = L.map("map");

L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
  attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);

map.setView([48.85, 2.35], 12);
var mcg = L.markerClusterGroup().addTo(map);
var mcg2 = L.markerClusterGroup().addTo(map);

L.marker([48.85, 2.35]).addTo(mcg);
L.marker([48.85, 2.34]).addTo(mcg);
  
for(var i=0;i<40;i++){
    L.marker([48.85, 2.34091]).addTo(mcg2);
}

For reference, you can see an example here:

http://plnkr.co/edit/yqIhI7RMsp9A7I3AwGnY?p=preview

https://i.sstatic.net/VKI1S.png

I need the markers in category 1 and 2 to cluster separately on the map without overlap while still being displayed together.

Answer №1

Is there a method to avoid this issue?

Using multiple Leaflet.markercluster groups as you currently have will not resolve the problem.

Consider this: how would the cluster icons' positions be calculated when one group lacks information about another group?

There are various potential solutions and alternate libraries that might better suit your requirements, eliminating the need to develop a new clustering algorithm from scratch.

One popular option for displaying different categories while clustering is the PruneCluster plugin:

PruneCluster is a high-speed real-time marker clustering library.

It serves as an alternative to Leaflet.markercluster with Leaflet integration.

https://i.sstatic.net/N3N1w.png Extract taken from PruneCluster home page

Another workaround could involve combining all categories into a single Marker Cluster Group but customizing the cluster icon of the latter so it resembles the layout in the PruneCluster screenshot above, or even generating fake icons for each category:

https://i.sstatic.net/Wwk7D.png

function customClusterIcon(cluster) {
  // Calculate number of markers from each category.
  var markers = cluster.getAllChildMarkers();
  var cat1count = 0;
  var cat2count = 0;
  for (var marker of markers) {
    var category = marker.options.category;
    if (category && category === 'cat2') {
      cat2count += 1;
    } else {
      cat1count += 1;
    }
  }
  // Determine the cluster icon based on presence of Markers from different categories.
  if (cat2count === 0) {
    return L.divIcon({
      html: cat1count,
      className: 'cat1cluster cluster',
      iconSize: [20, 20]
    });
  } else if (cat1count === 0) {
    return L.divIcon({
      html: cat2count,
      className: 'cat2cluster cluster',
      iconSize: [20, 20]
    });
  } else {
    return L.divIcon({
      html: `
        <div class="cat1cluster cluster">${cat1count}</div>
        <div class="cat2cluster cluster">${cat2count}</div>
      `,
      className: '',
      iconSize: [45, 20]
    });
  }
}

var paris = [48.86, 2.35];
var parisLeft = [48.86, 2.25];
var parisRight = [48.86, 2.45];
var map = L.map('map', {
  maxZoom: 18
}).setView(paris, 11);

var mcg = L.markerClusterGroup({
  iconCreateFunction: customClusterIcon
}).addTo(map);
var category1 = L.layerGroup();
var category2 = L.layerGroup();

var cat2style = {
  color: 'red',
  category: 'cat2'
};

var markerA = L.circleMarker(paris).addTo(category1);
var markerB = L.circleMarker(paris).addTo(category1);
var markerC = L.circleMarker(paris, cat2style).addTo(category2);
var markerD = L.circleMarker(paris, cat2style).addTo(category2);

var markerE = L.circleMarker(parisLeft).addTo(category1);
var markerF = L.circleMarker(parisLeft).addTo(category1);

var markerG = L.circleMarker(parisRight, cat2style).addTo(category2);
var markerH = L.circleMarker(parisRight, cat2style).addTo(category2);

mcg.addLayers([category1, category2]);


L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
  attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
html,
body,
#map {
  height: 100%;
  margin: 0;
}

.cat1cluster {
  background-color: #3388ff;
}

.cat2cluster {
  background-color: red;
}

.cluster {
  width: 20px;
  height: 20px;
  display: inline-block;
  text-align: center;
}
<!-- Leaflet assets -->
<link rel="stylesheet" href="https://unpkg.com/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="a1cdc4c0c7cdc4d5e1908f928f95">[email protected]</a>/dist/leaflet.css" integrity="sha512-puBpdR0798OZvTTbP4A8Ix/l+A4dHDD0DGqYW6RQ+9jxkRFclaxxQb/SJAWZfWAkuyeQUytO7+7N4QKrDh+drA==" crossorigin="" />
<script src="https://unpkg.com/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="c4a8a1a5a2a8a1b084f5eaf7eaf0">[email protected]</a>/dist/leaflet-src.js" integrity="sha512-+ZaXMZ7sjFMiCigvm8WjllFy6g3aou3+GZngAtugLzrmPFKFK7yjSri0XnElvCTu/PrifAYQuxZTybAEkA8VOA==" crossorigin=""></script>

<!-- Leaflet.markercluster assets -->
<link rel="stylesheet" href="https://unpkg.com/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="523e3733343e37267c3f3320393720313e272126372012637c667c63">[email protected]</a>/dist/MarkerCluster.css">
<link rel="stylesheet" href="https://unpkg.com/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="4b272e2a2d272e3f65262a39202e3928273e383f2e390b7a657f657a">[email protected]</a>/dist/MarkerCluster.Default.css">
<script src="https://unpkg.com/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="f39f9692959f9687dd9e9281989681909f8680879681b3c2ddc7ddc2">[email protected]</a>/dist/leaflet.markercluster-src.js"></script>


<div id="map"></div>

You can then further modify the spiderification process to only display Markers from the clicked category cluster icon, and do similar adjustments for the hovering polygon if desired.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Filtering out section boxes does not eliminate empty spaces

Link to Fiddle I've run into a bit of a roadblock while trying to filter my section box for a project I'm currently working on. The issue I'm facing is that instead of collapsing the first section box to display only the filtered options, i ...

Capture any clicks that fall outside of the specified set

I am facing an issue with my navigation drop down menu. Currently, the pure CSS functionality requires users to click the link again to close the sub-menu. What I want is for the sub-menu to close whenever a click occurs outside of it. I attempted a solu ...

Visual traceroute, like the one on "yougetsignal.com", provides a way to update a div element either on demand or periodically

This is my very first question on a forum, yay! I will do my best to ask clearly and concisely. I am currently working on creating a visual traceroute similar to the one found on yougetsignal.com by Kirk Ouimet. My project is up and running using bash co ...

What is the best way to reset Owl Carousel following an ajax request?

I am attempting to reinitialize the owl carousel following a successful ajax call. The ajax call will update the data while retaining the same view. I am encountering an issue where the carousel structure in the view does not reinitialize, and I am unsure ...

"Guidance on jQuery syntax: Use a textbox to filter out and hide select options with each keystroke

Is there a way to modify my existing code so that it can show or hide elements based on a filter condition, specifically for Internet Explorer? I want to wrap the unmatched elements in a SPAN tag and hide them if the browser is IE, and vice versa by remo ...

The stubborn Node and Passport are refusing to update the password using user.setPassword

Currently, I am working on setting up my program to update a user's password only when the old one is confirmed as correct. Most of the code functions as expected except for one specific line: router.put("/:id/edit_password", isLoggedIn, isAdministra ...

What is the best way to retrieve form values on the server in nextJs?

I'm facing an issue with passing form data to my API endpoint in nextJS. I have set up the form using Formik and Yup for validation. Oddly, when I check the console on the client side, I can see the user input values just fine, but on the server side, ...

What is the best way to horizontally align three animated progress bars alongside images?

Check out this jsfiddle that shows a vertical alignment of the progress bars. How can I modify it to align them horizontally and centered? https://jsfiddle.net/bLe0jLar/ .wrapper { width: 100px; height: 100px; } .wrapper > #bar, #bar2, #bar3 { ...

Displaying random divs and subsequently animating them downwards using JavaScript

I am in the process of creating a random appearing div that then falls down. Here is the code I have so far: $(document).ready(function(){ $('#test').animate({top: 80 + '%'},900); }); <div id="test" style="background:#98bf21;heigh ...

Is it possible to create a React Component without using a Function or Class

At times, I've come across and written React code that looks like this: const text = ( <p> Some text </p> ); While this method does work, are there any potential issues with it? I understand that I can't use props in this s ...

Automatically trigger the expansion of all panels within Vuetify using code

I'm attempting to use Vuetify 2.3.5 to programmatically control the opening and closing of expansion panels. <v-expansion-panels accordion> <v-expansion-panel v-for="(item,i) in faqs" :key="i"> <div class ...

The loading cursor in IE7 flickers incessantly, causing the webpage to lag and become un

When I move my cursor and click in text fields, the page becomes unresponsive and shows a wait cursor. If you're curious to see this issue in action, check out this video. This problem is specific to IE7. I've attempted to identify any ajax re ...

Transforming JSON data from JavaScript to Python JSON format

I am working with JavaScript JSON data: var data = '{a: 10, b: 20}' Now I need to convert this into Python JSON. Any suggestions on how to achieve this? Current Scenario: I have a script that extracts text from a website. The data on the webs ...

Error caught: The `onClick` listener was anticipated to be a function, but instead, it was found to be of type `object` in

While attempting to delete a customer entry with a specific id, I encountered an issue in my customers.js file. Despite adding a delete function, the entry was not being removed successfully. The console displayed the following error: caught Error: Expec ...

The hot loader is replicating code multiple times instead of conducting hot swapping

Every time I make a change in a component, webpack recompiles and React hot swaps the module over. However, I've noticed that my code runs multiple times after each hot module swapping occurrence. For example, if I make a change once, the functions ru ...

Error: The variable "THREE" has not been declared or defined

Attempting to bring in the SSAO shader from three (node modules) with the following syntax: import {SSAOShader} from 'three/examples/js/shaders/SSAOShader'` Unfortunately, encountering the error message: ReferenceError: THREE is not defined ...

Joi has decided against incorporating custom operators into their extended features

I am having trouble extending the joi class with custom operators. My goal is to validate MongoDB Ids, but when I try to use the extended object, I encounter the following error: error: uncaughtException: JoiObj.string(...).objectId is not a function TypeE ...

How to sync two carousels in Bootstrap 5 to slide simultaneously with just one click on the next and previous buttons

I am trying to implement dual sliding carousels using Bootstrap 5, but I am encountering issues with getting them to slide simultaneously. Despite incorporating data-bs-target=".carousel", the synchronization isn't working as intended in my ...

JavaScript animation for sequencing traffic lights

I have been working on completing a task that was assigned to me. (iii) Explain the organization of an array that could manage the traffic light sequence. (iv) Develop a script that utilizes the array outlined in part (iii) to create an animated display ...

Click on the checkbox to activate it using JavaScript

I'm trying to use JavaScript to toggle the checkbox status when clicking a button. The first button is supposed to uncheck the box and it's working correctly: function clear() { document.getElementById("check").checked = ""; } However, I al ...