0

I am using React Leaflet to render Leaflet map and its GeoJSON component to render polygons. I am trying to implement dragging multiple polygons.

I added Leaflet.Path.Drag library and tried to reuse this code. I am able to get transformation matrix which is in parent's state. If I want to apply this matrix to multiple polygons with _transform method, it doesn't work. I think the reason is that matrix is not applied to correct layers, but I have no idea how to fix this.

codesandbox.io

App.js

import React from "react";
import { MapContainer, GeoJSON, TileLayer } from "react-leaflet";
import { geoJson, latLngBounds } from "leaflet";
import "./styles.css";
import "leaflet/dist/leaflet.css";

import { GeoJsonContainer } from "./GeoJsonContainer";

const objects = [
  {
    polygon: {
      type: "FeatureCollection",
      features: [
        {
          type: "Feature",
          properties: {},
          geometry: {
            type: "Polygon",
            coordinates: [
              [
                [-104.98569488525392, 39.63431579014969],
                [-104.98569488525392, 39.64165260123419],
                [-104.97161865234376, 39.64165260123419],
                [-104.97161865234376, 39.63431579014969]
              ]
            ]
          }
        }
      ]
    }
  },
  {
    polygon: {
      type: "FeatureCollection",
      features: [
        {
          type: "Feature",
          properties: {},
          geometry: {
            type: "Polygon",
            coordinates: [
              [
                [-105.02964019775392, 39.6206315500488],
                [-105.02964019775392, 39.65685252543906],
                [-104.99067306518556, 39.65685252543906],
                [-104.99067306518556, 39.6206315500488]
              ]
            ]
          }
        }
      ]
    }
  }
];

const getPolygonPointFromBounds = (latLngBounds) => {
  const center = latLngBounds.getCenter();
  const latlngs = [];

  latlngs.push(latLngBounds.getSouthWest()); //bottom left
  latlngs.push({ lat: latLngBounds.getSouth(), lng: center.lng }); //bottom center
  latlngs.push(latLngBounds.getSouthEast()); //bottom right
  latlngs.push({ lat: center.lat, lng: latLngBounds.getEast() }); // center right
  latlngs.push(latLngBounds.getNorthEast()); //top right
  latlngs.push({
    lat: latLngBounds.getNorth(),
    lng: latLngBounds.getCenter().lng
  }); //top center
  latlngs.push(latLngBounds.getNorthWest()); //top left
  latlngs.push({
    lat: latLngBounds.getCenter().lat,
    lng: latLngBounds.getWest()
  }); //center left

  return latlngs;
};

export default function App() {
  const [matrix, setMatrix] = React.useState(null);

  let newBounds = [];
  let selectBoundingBox = [];

  objects.forEach((building) => {
    const polygonBounds = geoJson(building.polygon).getBounds();
    newBounds = [...newBounds, polygonBounds];
  });

  const polygonPoints = getPolygonPointFromBounds(latLngBounds(newBounds));
  const convertedData = polygonPoints.map((point) => [point.lng, point.lat]);
  convertedData.push([polygonPoints[0].lng, polygonPoints[0].lat]);
  selectBoundingBox = convertedData;

  let selectBoxData = null;

  if (selectBoundingBox) {
    selectBoxData = {
      type: "FeatureCollection",
      features: [
        {
          type: "Feature",
          properties: {},
          geometry: {
            type: "Polygon",
            coordinates: [selectBoundingBox]
          }
        }
      ]
    };
  }

  const handleFeature = (layer) => {
    layer.makeDraggable();
    layer.dragging.enable();

    layer.on("drag", function (e) {
      setMatrix(layer.dragging._matrix);
    });
  };

  return (
    <MapContainer center={[39.63563779557324, -104.99234676361085]} zoom={12}>
      <TileLayer
        attribution='&amp;copy <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
        url="https://{s}.tile.osm.org/{z}/{x}/{y}.png"
      />
      {objects.map((object, i) => (
        <GeoJsonContainer data={object} key={i} matrix={matrix} />
      ))}
      <GeoJSON
        data={selectBoxData}
        style={() => ({
          color: "green",
          weight: 3,
          opacity: 0.5
        })}
        draggable={true}
        onEachFeature={(feature, layer) => handleFeature(layer)}
      ></GeoJSON>
    </MapContainer>
  );
}

GeoJsonContainer.js

import React from "react";
import { GeoJSON } from "react-leaflet";

require("leaflet-path-drag");

export const GeoJsonContainer = (props) => {
  const geoJSONRef = React.useRef(null);
  const layerRef = React.useRef(null);

  React.useEffect(() => {
    if (geoJSONRef?.current?._layers) {
      console.log("mount layers", geoJSONRef.current?._layers);
    }
  }, []);

  React.useEffect(() => {
    if (geoJSONRef?.current._layers && props.matrix) {
      console.log("transform layers", geoJSONRef.current._layers);
      const key = Object.keys(geoJSONRef.current._layers)[0];
      const geoJSONElementLayer = geoJSONRef.current._layers[key];
      if (geoJSONElementLayer) {
        console.log("geoJSONElementLayer", geoJSONElementLayer);
        console.log("layerRef.current", layerRef.current);
        geoJSONElementLayer._transform(props.matrix);
        layerRef.current._transform(props.matrix);
      }
    }
  }, [props.matrix]);

  const handleFeature = (layer) => {
    console.log("handleFeature layer", layer);
    layerRef.current = layer;
    layer.makeDraggable();
    layer.dragging.enable();
  };

  return (
    <GeoJSON
      ref={geoJSONRef}
      data={props.data.polygon}
      style={() => ({
        color: "#3388ff",
        weight: 3,
        opacity: 1
      })}
      dragging={true}
      onEachFeature={(feature, layer) => handleFeature(layer)}
    ></GeoJSON>
  );
};
5
  • "If I want to apply this matrix to multiple polygons with _transform method, it doesn't work" - you mean you want to drag the polygons together at once, as a group? – Seth Lutske Jan 23 at 18:53
  • @SethLutske yes, exactly – Matt Jan 24 at 4:24
  • @Matt have you simply thought about using turf.js transformTranslate, you could probably get the values of direction and distance using turf.js centroid on the polygon at dragstart and dragend and using turf.js distance and turf.js bearing – Dror Bogin Jan 26 at 7:57
  • Thanks, I didn't try this. I don't have an issue with getting transformation values. The issue is that GeoJSON layers don't transform. I think there is something wrong with react-leaflet component. – Matt Jan 26 at 8:13
  • Cross-posted as stackoverflow.com/questions/65857656/… – PolyGeo Jan 27 at 23:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.