Wildhoney / Leaflet.FreeDraw

:earth_asia: FreeDraw allows the free-hand drawing of shapes on your Leaflet.js map layer – providing an intuitive and familiar UX for creating geospatial boundaries similar to Zoopla and others. Included out-of-the-box is the concaving of polygons, polygon merging and simplifying, as well as the ability to add edges and modify existing shapes.
https://freedraw.herokuapp.com/
MIT License
540 stars 102 forks source link

FreeDraw/Pather integration (+removeLayer/dynamic mode setting) #138

Open felluksch opened 5 years ago

felluksch commented 5 years ago

Hi again @Wildhoney ,

I'm also trying to use Pather in conjunction with FreeDraw. I want to allow users to switch modes (polygon/line) by clicking on a button.

var freeDraw = new FreeDraw({
  concavePolygon: false,
  mergePolygons: false
});

var pather = new L.Pather();

map.on('layeradd', event => {
console.log('Layer added!')
});

var toggle = L.easyButton({
  states: [{
    stateName: 'FreeDraw',
    icon: '<img alt="draw lines" src="img/line.png"/>',
    title: 'Draw lines',
    onClick: function(control) {
      map.removeLayer(freeDraw);
      map.addLayer(pather);
      console.log('new layer:  Pather');
      //freeDraw.off('mousemove touchmove mouseup touchend');
      //pather.on('mousemove touchmove mouseup touchend');
      //freeDraw.mode(NONE);
      //pather.setMode(L.Pather.MODE.ALL);
      control.state('Pather');
      activeState = control.state();
    }
  }, {
    stateName: 'Pather',
      icon: '<img alt="draw polygons" src="img/draw-polygons.svg"/>',
      title: 'Draw polygons',
      onClick: function(control) {
        map.removeLayer(pather);
        map.addLayer(freeDraw);
        console.log('new layer: FreeDraw');
        //pather.off('mousemove touchmove mouseup touchend');
        //freeDraw.on('mousemove touchmove mouseup touchend');
        //pather.setMode(L.Pather.MODE.VIEW);
        //freeDraw.mode(ALL);
        control.state('FreeDraw');
        activeState = control.state();
      },
  }]
});
toggle.addTo(map);

The first click on the button adds Pather to the map, the second removes Pather and adds Freedraw. When I click the button again, however, the previously drawn FreeDraw polygons remain visible. Console says: "Uncaught TypeError: Cannot read property 'size' of undefined at leaflet-freedraw.web.js". map.removeLayer(freeDraw) behaves the same when freeDraw is added to the map outside the EasyButton function:

var freeDraw = new FreeDraw({
  concavePolygon: false,
  mergePolygons: false
}).addTo(map);

L.easyButton('<img>', function () {
  map.removeLayer(freeDraw);
}).addTo(map);

As I've said, removeLayer(pather) works fine. My code is more or less equivalent to the 'changing states' example in the EasyButton doc, so I guess it's FreeDraw that exhibits weird behaviour? Yet, the remove and add layer procedure is not my preferred way to switch between modes since I want all polygons and lines to be visible regardless of the active layer. Options I tried to achieve this goal:

  1. Deactivating FreeDraw/Pather by telling them to stop listening for events that produce polygons/lines (see comments in code).
  2. I also had the idea that you maybe could achieve the same goal by setting the mode of FreeDraw/Pather to NONE/ALL dynamically to prevent interference. But freeDraw.mode(NONE) produces an error ("NONE is undefined") - no idea why.
  3. I also found this thread but had trouble reproducing the results/integrating into my project.

A whole bunch of questions again, sorry for that. Would be great if you could give some suggestions but please take your time. Cheers!

felluksch commented 5 years ago

Hi @Wildhoney, I could solve the problem or at least I figured out what needs to be done for this. Firstly, FreeDraw and Pather (both added to the map) don't interfere with each other when FreeDraw's mode is set to NONE and Pather's mode ist set to default. Switching between Pather and FreeDraw could easily be achieved by just resetting the modes and without removing/adding layers. However, freeDraw's mode() method doesn't take NONE as an argument (other than stated in the readme). freeDraw.mode(0) works though. Likewise, mode() returns 15 instead of ALL. Looks like a bug that needs to be fixed (and if not, at least the documentation should reflect this behaviour). // EDIT: Okay, thanks to the Heroku app I learnt about bitwise operators. Maybe it should be mentioned in the doc so others don't get confused? FreeDraw/Pather integration doesn't work the other way around (i.e. FreeDraw activated, Pather deactivated). I assumed that VIEW (which can be found in the source code of Pather) is equivalent to NONE. Yet with Pather in view mode and freeDraw in ALL it doesn't work. So, I'd like to ask you if it would be possible to add FreeDraw's NONE mode to Pather. As far as I can see, everything should work then. And while you're at it, maybe harmonise the names of the mode setting methods/parameters (it's a bit confusing right now). It would also be great to have methods such as clear(), remove() and all() in Pather. I completely understand that you're busy and probably don't have much time -- so if you won't be able to fix it, please tell me what I can do to help. I don't fully understand the code yet but if you could point me in the right direction I will be happy to contribute. Thank you!