I have a map component that loads data from a geoJSON file and displays each datapoint on an openstreetmap map. The library is use for that is the 'react-leaflet' library. And I also use the 'react-leaflet-cluster' library to cluster the markers.
In the dataset I use it is common for multiple records to have the same exact location. When that is the case I want to change the coordinates of the markers so that the markers are neatly placed next to each other in a row:
export function MapOld() {
const [jsonContent, setJsonContent] = useState()
const [isMapReady, setIsMapReady] = useState(false)
const mapRef = useRef()
const geojsonRef = useRef()
const markersRef = useRef([])
useEffect(() => {
useFetchGeoJson()
.then(data => setJsonContent(data))
.catch(err => console.log(`error reading data ${err}`)) // eslint-disable-line no-console
}, [])
useEffect(() => {
if (mapRef.current && jsonContent) {
mapRef.current.fitBounds(geojsonRef.current?.getBounds())
geojsonRef.current?.eachLayer(layer => layer.openPopup())
}
}, [isMapReady, jsonContent])
const pointToLayer = useCallback((feature, latlng) => {
if (feature.properties && feature.properties.icon) {
return L.marker(latlng, {
icon: L.icon(feature.properties && feature.properties.icon),
})
}
const existingMarkers = markersRef.current
const samePositionMarkers = existingMarkers.filter(marker =>
marker.getLatLng().equals(latlng)
)
const newIcon = L.divIcon({
html: ReactDOMServer.renderToString(<SignEmpty />),
iconSize: [25, 25],
iconAnchor: [12, 12],
})
if (samePositionMarkers.length > 0) {
// const lastMarker = samePositionMarkers[samePositionMarkers.length - 1]
const offsetX = 40 * samePositionMarkers.length
const newLatLng = L.latLng(latlng.lat, latlng.lng + offsetX)
const newMarker = L.marker(newLatLng, {
icon: newIcon,
})
newMarker.addTo(mapRef.current)
newMarker.setLatLng(newLatLng)
newMarker.bindPopup(feature.properties.name)
console.log(newMarker) // eslint-disable-line no-console
existingMarkers.push(newMarker)
return null // returning null here prevents Leaflet from creating a marker for the original latlng
}
const marker = L.marker(latlng, {
icon: newIcon,
})
marker.bindPopup(feature.properties.name)
// marker.on('click', () => onMarkerClick(marker))
existingMarkers.push(marker)
return marker
}, [])
const onEachFeature = useCallback((feature, layer) => {
if (feature.properties && feature.properties.popupContent) {
layer.bindPopup(feature.properties.popupContent, {
autoClose: false,
})
}
}, [])
return (
<MapContainer
className="geoMapContainer"
style={{
position: 'absolute',
height: 'Calc(100vh - 75px)',
width: 'Calc(100% - 30px)',
left: '30px',
top: '50px',
zIndex: -1,
}}
ref={mapRef}
zoom={7}
center={[52.04627, 4.51326]}
whenReady={() => setIsMapReady(true)}
>
<TileLayer
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
attribution='© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
maxNativeZoom={18}
maxZoom={21}
/>
<MarkerClusterGroup
spiderLegPolylineOptions={{ weight: 0 }}
disableClusteringAtZoom={18}
>
{jsonContent && (
<GeoJSON
data={jsonContent}
pointToLayer={pointToLayer}
onEachFeature={onEachFeature}
ref={geojsonRef}
/>
)}
</MarkerClusterGroup>
</MapContainer>
)
}
The problem is that the pages crashes, when I try to open it. The browser shows an alert prompt saying 'page does not respond'. I'm suspecting an infinte loop or something in my code, that completely uses up the memory.
So my question is, is there something wrong with my code and how can I fix this? Or maybe (which is what I suspect) this isn't the correct way of achieving the desired result. In that case can you please tell me what the correct way of doing it?
pointToLayer
is called.pointToLayer
then adds a marker, which callspointToLayer
, which adds a marker, etc. What you probably want is to apply the offset to your points outside of yourpointToLayer
, in yourjsonContent
before passing it to<GeoJSON/>