In the old @google/markercluster
class, you could pass in a styles
variable, which could contain multiple cluster styles which could then be passed into a markerCluster
constructor, like below.
var clusterStyles = [
{
textColor: 'white',
url: '../Images/MapIcons/m1.png',
height: 53,
width: 53,
},
{
textColor: 'white',
url: '../Images/MapIcons/m2.png',
height: 57,
width: 57,
},
{
textColor: 'white',
url: '../Images/MapIcons/m3.png',
height: 66,
width: 66,
},
{
textColor: 'white',
url: '../Images/MapIcons/m4.png',
height: 78,
width: 78,
},
{
textColor: 'white',
url: '../Images/MapIcons/m5.png',
height: 89,
width: 89,
}
markerCluster = new MarkerClusterer(map, markers,mcOptions);
The cluster would then handle zoom levels by changing which icon it was using, as well as the size of the icon in question. The icon would shrink as it got further zoomed in so that they didn't overlap.
However, the new implementation of MarkerClusterer
replaces this with a Renderer
object, which as far as I can tell, only allows a single definition, not multiple, so it won't handle zooming in.
let renderer = {
render: ({ count, position }) =>
new google.maps.Marker({
label: {
text: String(count),
color: "white",
},
position,
icon: {
url: '../Images/MapIcons/m5.png',
height: 89,
width: 89,
},
zIndex: Number(google.maps.Marker.MAX_ZINDEX) + count,
}
),
};
let algorithm = new markerClusterer.SuperClusterAlgorithm({ maxZoom: 15 });
map.setOptions({ minZoom: 5, maxZoom: 20 });
markerCluster = new markerClusterer.MarkerClusterer({
map: map,
markers: markers,
renderer: renderer,
algorithm: algorithm });
Is there a way to handle multiple styles so that the icon and height and width change as it gets more zoomed in?
As you can see, I've tried converting one style and it works well, but the icons stay the same size relative to the screen the more you zoom in, until you get in close enough and it's just a mess of overlapping clusters. I can always downsize it if need be, but if the text size gets big enough, it overflows off the cluster, and I'd like to have something a little more elegant in mind anyway, closer to what the old version of the markerClusterer
library had before.
You can have any logic in the renderer. Since you want to change the cluster icon based on the zoom level, you can simply add this bit of logic and change the marker icon accordingly.
I would also suggest using SVG for your markers (some examples here: https://googlemaps.github.io/js-markerclusterer/public/renderers/) or HTML in combination with Advanced Markers.
The below example uses Advanced Markers with HTML elements. Zoom in to see the cluster icon change.
async function initMap() {
// Request needed libraries.
const { Map } = await google.maps.importLibrary("maps");
const { AdvancedMarkerElement } = await google.maps.importLibrary("marker");
const center = {
lat: 0,
lng: 0
};
const map = new Map(document.getElementById("map"), {
center: center,
zoom: 2,
mapId: "4504f8b37365c3d0",
zoomControl: true
});
const markers = [];
// Define the max latitude on a mercator projection
var maxLat = Math.atan(Math.sinh(Math.PI)) * 180 / Math.PI;
// Loop and create many markers
for (let i = 0; i < 2000; i++) {
// Calculate a random lat and lng
const lat = Math.floor(Math.random() * (maxLat * 2)) - maxLat;
const lng = Math.floor(Math.random() * 360) - 180;
const marker = new AdvancedMarkerElement({
map: map,
position: new google.maps.LatLng(lat, lng),
title: "AdvancedMarkerElement"
});
markers.push(marker);
}
let algorithm = new markerClusterer.SuperClusterAlgorithm({
maxZoom: 15
});
// Marker clusterer
const cluster = new markerClusterer.MarkerClusterer({
map: map,
markers: markers,
algorithm: algorithm,
renderer: {
render: ({
count,
position
}, stats, map) => {
// Create a custom cluster HTML element to be used with an AdvancedMarker
const el = document.createElement("div");
// Change appearance based on current zoom
el.className = map.getZoom() > 2 ? 'cluster red' : 'cluster';
// Set content
el.textContent = String(count);
// Return AdvancedMarkerElement
return new AdvancedMarkerElement({
position: position,
content: el,
title: "AdvancedMarkerElement Cluster"
});
}
}
});
}
initMap();
#map {
height: 180px;
}
.cluster {
font-size: 1.5em;
background-color: yellow;
padding: .25em .5em;
transform: rotate(5deg);
}
.red {
color: white;
font-size: 2em;
font-weight: bold;
background-color: red;
transform: rotate(-5deg);
}
<div id="map"></div>
<script src="https://unpkg.com/@googlemaps/markerclusterer/dist/index.min.js"></script>
<!-- prettier-ignore -->
<script>(g=>{var h,a,k,p="The Google Maps JavaScript API",c="google",l="importLibrary",q="__ib__",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement("script"));e.set("libraries",[...r]+"");for(k in g)e.set(k.replace(/[A-Z]/g,t=>"_"+t[0].toLowerCase()),g[k]);e.set("callback",c+".maps."+q);a.src=`https://maps.${c}apis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+" could not load."));a.nonce=m.querySelector("script[nonce]")?.nonce||"";m.head.append(a)}));d[l]?console.warn(p+" only loads once. Ignoring:",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))})
({key: "AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk", v: "beta"});</script>