How to use Leaflet’s FeatureGroup with OverlappingMarkerSpiderfier in react-leaflet?

I’m having trouble integrating OverlappingMarkerSpiderfier with React-Leaflet and using FeatureGroup for panning on the map. FeatureGroups are really useful when you have complicated panning logic.

Here is a plain JS demo. The markers spiderfy and the pan button works with no issues.

Here is the react-leaflet demo. The panning will fail with the following error if spiderfy is enabled/checked in the UI. This is based on this SO post.

layer.getLatLng is not a function

I think the problem is that with regular JS, I can add the markers array to both oms and the featureGroup imperatively but with React-leaflet, I don’t see how I can achieve the same result.

Is the error because <Spiderfy> layer does not have a getLatLng function for some reason even though I am extending the MapLayer.

I’m unsure of what needs fixing, the JS version seems to work so the OverlappingMarkerSpiderfier library likely doesn’t need changes. It could be a React-leaflet specific problem/limitation that can possibly be fixed with a custom FeatureGroup/MapLayer?

Answer

It appears the error

layer.getLatLng is not a function

occurs since FeatureGroup.getBounds function expects underlying layers to implement either getBounds or getLatLng methods which is not the case with custom Spiderfy.js component where container (layerContainer prop) is of L.layerGroup type.

One option to consider would be to refactor Spiderfy.js component:

  • abandon L.layerGroup as a layer container
  • since a recent React version is utilized (16.8 or above) replace ES6 class component with function to implement Spiderfy component

Spiderfy.js component

import React, { useEffect } from "react";
import { withLeaflet, MapLayer, useLeaflet } from "react-leaflet";
import L from "leaflet";
import "overlapping-marker-spiderfier-leaflet/dist/oms";

function Spiderfy(props) {
  const { map, layerContainer } = useLeaflet();
  const oms = new window.OverlappingMarkerSpiderfier(map);
  oms.addListener("spiderfy", (markers) => {
    markers.forEach((m) => m.closePopup()); //force to close popup
    if (props.onSpiderfy) props.onSpiderfy(markers);
  });
  oms.addListener("unspiderfy", (markers) => {
    if (props.onUnspiderfy) props.onUnspiderfy(markers);
  });
  oms.addListener("click", (marker) => {
    if (props.onClick) props.onClick(marker);
  });

  useEffect(() => {
    layerContainer.eachLayer((layer) => {
      if (layer instanceof L.Marker) {
        oms.addMarker(layer);
      }
    });
  }, [oms,layerContainer]);

  return <div>{props.children}</div>;
}

export default withLeaflet(Spiderfy);

This way FeatureGroup.getBounds function should return the expected result, identical to provided in plain JS demo.

Forked example

Leave a Reply

Your email address will not be published. Required fields are marked *