3

Assume a data.frame has column "group" with unique values "group1" and "group2". In leaflet, we can assign these groups a color with ColorFactor(). We can also change markerCluster colors with minimal CSS.

How can we assign distinct colors to the markerClusters of each group? In other words, I want all marker clusters for points within "group1" to be "navy", and all marker clusters for points within "group2" to be "red" at all levels of zoom, even down to the individual points.

In a Rmd file:

---
output: html_document
---

<style>
.marker-cluster-small {
background-color: green;
}
.marker-cluster-small div {
background-color: green;
}
.marker-cluster-medium {
background-color: green;
}
.marker-cluster-medium div {
background-color: green;
}
.marker-cluster-large {
background-color: green;
}
.marker-cluster-large div {
background-color: green;
}
</style>

```{r}
library(leaflet)
library(magrittr)
quakes$group <- sample(c("group1", "group2"), 1000, replace = TRUE)
pal_group <- colorFactor(c("navy", "red"), c("group1", "group2"))

leaflet() %>% 
  addTiles() %>% 
  addCircleMarkers(
    lng = quakes$long, 
    lat = quakes$lat, 
    clusterOptions = markerClusterOptions(),
    color = pal_group(quakes$group)
  )


Rich Pauloo
  • 7,734
  • 4
  • 37
  • 69
  • 1
    Are your group1 and group2 points clustering together? I.e. are there some clusters containing points of both groups? – ghybs Mar 10 '21 at 03:31
  • 2
    [markerCluster subgroups](https://github.com/ghybs/Leaflet.FeatureGroup.SubGroup) seem a good start, but don't seem to be yet integrated into R Leaflet. – Waldi Mar 10 '21 at 17:12
  • @ghybs, in the example above, yes. But we can easily separate them into their own cluster groups. However, the div class ids remain the same for each distinct group such that markerclusters are the same color at all zoom levels. One way to solve this may be to customize the div class ids for each markercluster group. Within R, you can pass a JS function to the leaflet, which may hold the key https://rdrr.io/cran/htmlwidgets/man/onRender.html – Rich Pauloo Mar 10 '21 at 19:01

1 Answers1

0

Seems like you might be interested in that answer: Prevent multiple markerClusterGroup icons from overlapping in Leaflet

Using 2 separate MarkerClusterGroups may not be appropriate, typically in case some points are on nearby locations, but for different groups. In that case, Leaflet.markercluster may display 2 separate Clusters on the same position, overlapping each other (which is precisely what we want to avoid by using clustering in the first place).

To still display clusters that give a hint of number of child Markers from each category, we have to customise the Clusters icon. The linked answer proposes such custom function:

Screenshot of Leaflet.markercluster with customized cluster icons for each category

function customClusterIcon(cluster) {
  // Count 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;
    }
  }
  // Generate the cluster icon depending 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/leaflet@1.3.4/dist/leaflet.css" integrity="sha512-puBpdR0798OZvTTbP4A8Ix/l+A4dHDD0DGqYW6RQ+9jxkRFclaxxQb/SJAWZfWAkuyeQUytO7+7N4QKrDh+drA==" crossorigin="" />
<script src="https://unpkg.com/leaflet@1.3.4/dist/leaflet-src.js" integrity="sha512-+ZaXMZ7sjFMiCigvm8WjllFy6g3aou3+GZngAtugLzrmPFKFK7yjSri0XnElvCTu/PrifAYQuxZTybAEkA8VOA==" crossorigin=""></script>

<!-- Leaflet.markercluster assets -->
<link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster@1.4.1/dist/MarkerCluster.css">
<link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster@1.4.1/dist/MarkerCluster.Default.css">
<script src="https://unpkg.com/leaflet.markercluster@1.4.1/dist/leaflet.markercluster-src.js"></script>


<div id="map"></div>
ghybs
  • 47,565
  • 6
  • 74
  • 99