I've been banging my head against the wall with google maps. See my initial issue here
I fixed the issue of using a react component as a map marker, by creating a portal with the OverlayView into a div. But now I'm faced with a bump in a rug now where I am now unsure how to create clustered markers?
Previously I used @googlemaps/markerclustererplus to create a cluster from an array of of markers created from a class CustomMarker that generates an OverlayView with the code snippet below.
const markers = mapMarkers.map((m) => {
const { position, icon, draggable } = m;
const marker = new CustomMarker(
position,
draggable,
icon,
map,
onChildMouseUp
);
return marker;
});
// Add a marker clusterer to manage the markers.
new MarkerClusterer(map, markers, {
calculator: (gMarkers, numStyles) => {
let index = 0;
const count = gMarkers.length;
let dv = count;
while (dv !== 0) {
dv = parseInt(dv / 10, 10);
index += 1;
}
index = Math.min(index, numStyles);
return {
text: `${count}`,
index
};
},
gridSize: 50,
styles: [
{
className: "clusterMarker",
width: 50,
height: 18
}
]
});
Now I'm confused how to populate the 2nd argument in MarkerClusterer with the markers now I add markers in the components return statement
<Map id="map" mapHeight={height} mapWidth={width}>
{gmap && mapMarkers?.length > 0 ? (
<>
{mapMarkers.map((m, i) => {
return (
<CustomMarkerNew key={i} position={m.position} googleMap={gmap}>
{m.icon}
</CustomMarkerNew>
);
})}
</>
) : null}
</Map>
This is how the CustomMarkerNew component looks
CustomMarkerNew.js
import React, { useState, useEffect, useCallback } from "react";
import ReactDOM from "react-dom";
const OverlayView = (props) => {
const { google } = window;
const { googleMap, position, children } = props;
const [latlng, setLatlng] = useState(position);
const [container, setContainer] = useState(document.createElement("div"));
const [overlay, setOverlay] = useState(undefined);
const initOverlay = useCallback(() => {
const overlayView = new google.maps.OverlayView();
overlayView.setMap(googleMap);
overlayView.getDraggable = () => true;
setOverlay(overlayView);
setLatlng(position);
google.maps.OverlayView.preventMapHitsFrom(container);
}, [googleMap, container]);
const onAdd = useCallback(() => {
// only draw marker if it has not been assigned the customMarker class
if (!container.classList.contains("customMarker")) {
container.classList.add("customMarker");
container.style.position = "absolute";
const panes = overlay.getPanes();
panes.overlayImage.appendChild(container);
}
overlay.setPosition();
}, [overlay]);
const draw = useCallback(() => {
container.style.position = "absolute";
container.draggable = true;
overlay.getPanes().floatPane.appendChild(container);
overlay.setPosition();
}, [overlay, container, latlng]);
const onRemove = useCallback(() => {
ReactDOM.unmountComponentAtNode(container);
setContainer(undefined);
overlay.setMap(null);
}, [container, overlay]);
const setPosition = (pos) => {
// update global latlng value with current position of marker
if (pos) setLatlng(pos);
const projection = overlay.getProjection();
if (!projection) return;
// convert latlng value to pixel equivalent
const point = projection.fromLatLngToDivPixel(latlng);
// set left and top values from values from point variable to place marker correctly on map
if (point && point.x && point.y) {
container.style.left = `${point.x}px`;
container.style.top = `${point.y}px`;
}
};
const bindFunctions = useCallback(() => {
overlay.onAdd = onAdd.bind(this);
overlay.draw = draw.bind(this);
overlay.onRemove = onRemove.bind(this);
overlay.setPosition = setPosition.bind(this);
overlay.getDraggable = () => true;
}, [overlay]);
useEffect(() => {
if (overlay) {
bindFunctions();
}
}, [overlay]);
useEffect(() => {
initOverlay();
}, []);
return (
(overlay &&
children &&
container &&
ReactDOM.createPortal(children, container)) ||
null
);
};
export default React.memo(OverlayView);
Here is a CodeSandbox as an example
Any help would be much appreciated.