Search code examples
reactjsleafletreact-leaflet

Get all markers visible on the map with react-leaflet


I'm using leaflet in React to reproduce an effect like Airbnb. I don't really know the best practices, but I was thinking of: storing the markers visible on the map in an array and having this array update each time the map is moved. Then display a list (of places for example) next to the map based on the visible markers.

Here's a simple code that displays the markers on the map:

function FranceMap() {
  const { latitude, longitude }: ParisProps = ParisGpsCoordinates;

  return (
        <div
          id="map-container"
          className="..."
        >
          <MapContainer center={[latitude, longitude]} zoom={11}>
            <TileLayer
              attribution='&copy; <a href="https://stadiamaps.com/">Stadia Maps</a>, &copy; <a href="https://openmaptiles.org/">OpenMapTiles</a> &copy; <a href="http://openstreetmap.org">OpenStreetMap</a> contributors'
              url="https://tiles.stadiamaps.com/tiles/osm_bright/{z}/{x}/{y}{r}.png"
            />
            <MarkerClusterGroup chunkedLoading>
              {SitesLocation}
            </MarkerClusterGroup>
          </MapContainer>
        </div>
      </div>
  );
}

I understand that you need to use map.getBounds(), but I don't know how to integrate it into my code.


Solution

  • Assuming you have all the markers stored in an array:

    const points = [
      [54, -1.5],
      [54.5, -1.5],
      [54.1, -1.5],
      [54.2, -1.6],
    ];
    

    but as this array could be large, you only want to render the ones visible based on the current bounds on the map, you can use this code to hook into the various events in order to update the visible markers:

    useEffect(() => {
      if (!map) return;
      // Updates markers after map initially renders
      updateVisibleMarkers();
    
      map.on('dragend', function () {
        // Updates markers after user drags the map to change position
        updateVisibleMarkers();
      });
      map.on('zoomend', function () {
        // Updates markers after user zooms in/out
        updateVisibleMarkers();
      });
    }, [map]);
    

    The updateVisibleMarkers() function gets the current bounds from the map, and iterates over the points array checking whether each marker is within the bounds. If it is, we add it to the newMarkers array, then after all the points have been considered, setVisibleMarkers is called to update the visible marker state:

    const updateVisibleMarkers = () => {
      const bounds = map.getBounds();
      const newMarkers = [];
      for (let point of points) {
        if (bounds.contains(point)) {
          newMarkers.push(point);
        }
      }
      console.log(
        '!!! map bounds:',
        map.getBounds(),
        ' visible markers: ',
        newMarkers
      );
      setVisibleMarkers(newMarkers);
    };
    

    The visible markers are rendered as follows:

    <MapContainer ...>
      <TileLayer ...>
      {visibleMarkers.map((p) => (
        <Marker position={p}></Marker>
      ))}
    </MapContainer>
    

    There's a working StackBlitz example to showcase the functionality. You will see the markers getting added/removed as you drag or zoom the map.