geoman-io / leaflet-geoman

🍂🗺️ The most powerful leaflet plugin for drawing and editing geometry layers
https://geoman.io
MIT License
2.21k stars 433 forks source link

Adjust circle radius helper layers when programatically updating circle currently being edited #1274

Closed slutske22 closed 1 year ago

slutske22 commented 1 year ago

I have a scenario where I need to coordinate editing geometry between the map, and a form. Using leaflet geoman, its easy to listen to pm:edit events on the layer, and then get the resulting specs from the shape and update a form. However, using a form to update the layer currently being edited is challenging, because the helper layers do not update. For example

map.on('pm:create', function(e) {
  if (e.shape === 'Circle') {
    const circleToEdit = e.layer

    // Function to update form values based on the value of the layer
    updateForm(e.layer)

    circleToEdit.pm.enable()

    circleToEdit.on('pm:edit', e => {
      updateForm(e.layer)
    })

    // Once shape is created, add event listeners to HTML input to update the layer
    radiusInput.addEventListener('input', (e) => {
      circleToEdit.setRadius(Number(e.target.value))
    })
  }
});

This is working. When the user updates the input, the circle being edited changes its radius. But the helper layers (for the center, edge, and line between) do not update. I end up with something like this:

Screen Shot 2022-12-20 at 2 15 05 PM

jsfiddle demonstrating the issue

I see on the layer instance, there is a ._helperLayers properly, which has a ._layers property, which contains layers for the marker at the center, the marker at the edge, and the line between:

Screen Shot 2022-12-20 at 2 16 04 PM

I can potentially grab these manually and udpate them myself. But digging into the internals like this seems unreliable, and will require a lot of potentially buggy and not-future-proof code to manually update the position of the 2 markers and the polyline.

Is there a better way to access and update the helper layers for the radius line when editing a circle?

Falke-Design commented 1 year ago

You can disable and enable the layer to rerender the helpers:

    radiusInput.addEventListener('input', (e) => {
      circleToEdit.setRadius(Number(e.target.value))
      circleToEdit.pm.disable();
      circleToEdit.pm.enable();
    })

or you use the internal function _initMarkers:

    radiusInput.addEventListener('input', (e) => {
      circleToEdit.setRadius(Number(e.target.value))
      circleToEdit.pm._initMarkers();
    })
slutske22 commented 1 year ago

This did it! Thank you!

slutske22 commented 1 year ago

@Falke-Design, sorry to bug again. In the same vein of the initial issue, I have a new requirement. It requires that I access the helper marker and update its tooltip when creating a new circle, between the moments of pm:drawstart and pm:change. (After user makes initial click and begins dragging out the radius, I need to continuously update the tooltip that usually says "Click to finish circle" to have some custom formatted content with info about the circle). I try to access the workingLayer in this state, and there is nothing in the _helperLayers._layers property:

map.on("pm:drawstart", ({ workingLayer, shape }) => {
  if (shape === "Circle") {
    workingLayer.on("pm:centerplaced", () => {
      console.log(workingLayer);
      circleBeingEditedChange(workingLayer);
    });
  }
});

function circleBeingEditedChange(circle){
      const helperLayers: (L.Marker | L.Polyline)[] = Object.values(
        circle.pm._helperLayers._layers // <--------------- issue here
      );

      // do stuff with helperLayers
}

I no longer see where to access to my helper layers. Where I log the workingLayer above, I see the ._helperLayers._layers object is empty:

Screen Shot 2023-01-03 at 8 22 36 AM

Digging around in the circle instance in between those moments, I can't seem to find the reference to the helper markers and polyline. They only seem to be there once the pm:create event fires. Am I missing it? How can I access these in this initial interim state?

Falke-Design commented 1 year ago

You can use this code:

  map.on("pm:drawstart", ({ workingLayer, shape }) => {
    if (shape === "Circle") {
      workingLayer.on("pm:centerplaced", () => {
        console.log(workingLayer);
        workingLayer.on('pm:change',(e)=>{
          map.pm.Draw.Circle._hintMarker.getTooltip().setContent("Test "+workingLayer.getRadius());
        })
      });
    }
  });