mapbox-gl-draw icon indicating copy to clipboard operation
mapbox-gl-draw copied to clipboard

Snap to point and line

Open stevage opened this issue 5 years ago • 17 comments

I'm working on a snapping mode for line and polygon drawing that supports snapping moved or created vertices to edges in existing lines/polygons or existing points. (Haven't decided about snapping to vertices in existing lines/polygons yet).

I'm looking for feedback on my design to see if it would be accepted as a PR.

When instantiating the mapbox-gl-draw object, you can pass through snapLayers: ['mylayer'] to indicate that features in mylayer should be snapped to. The default is no snap layers. For that reason, my thinking is towards making this an improvement to the standard draw_line_string and draw_polygon modes (since it's completely backwardly compatible) rather than new custom modes, as suggested in #865.

On the implementation side:

  • when the gl-draw object is connected, a new layer, _mylayer_buffer is created for each provided mylayer. It has the same source and filter, but is invisible, and 40 (configurable) pixels wide (either a line or circle layer type).
  • mouseover and mouseout events are attached to the buffer layers, so that when the mouse enters, the point can be snapped.
  • Snapping is achieved by using turf's nearestPointOnLine, or just the centre of the circle for point features.
  • A snap-hover feature state is temporarily set on the hovered feature, so that the user can provide their own styling to show when a feature will be snapped to.
  • Also, a visual indicator of the exact snapping point is shown with a little circle layer.

My implementation isn't complete, but it seems to work pretty solidly so far.

Thoughts?

Screen Recording 2019-12-24 at 4 25 09 pm

stevage avatar Dec 29 '19 21:12 stevage

My branch is at https://github.com/stevage/mapbox-gl-draw/blob/snapping/src/snapping.js

I've put most of the snapping code in this single module, snapping.js. I haven't fully grokked the paradigm of the whole library, so it will probably look a bit weird at this point.

The actual touchpoint in each drawing mode is very small, like this:

DrawLineString.onMouseMove = function(state, e) {
  const lngLat = this._ctx.snapping.snapCoord(e.lngLat);

stevage avatar Dec 30 '19 03:12 stevage

This is great. We're definitely going to use this.

Plantain avatar Feb 15 '20 05:02 Plantain

One point I've noticed trying this - this blows out the side of the standalone library from ~110KB to 600KB.

Plantain avatar Feb 15 '20 05:02 Plantain

One point I've noticed trying this - this blows out the side of the standalone library from ~110KB to 600KB.

Oh yeah, because it was still just proof of concept I pulled in the whole Turf library, even though I only need a tiny bit of it.

stevage avatar Feb 15 '20 06:02 stevage

This is a great idea, but I don't understand how to implement this mode When instantiating the mapbox-gl-draw object. Can you give me an example? Thank you!

daniel-fschr avatar Apr 07 '20 08:04 daniel-fschr

Sure, good idea. I've done some more work which I haven't pushed yet, too. I realised that the library has to be able to snap to layers that didn't exist when it got instantiated.

stevage avatar Apr 07 '20 08:04 stevage

Hi Stevage, I am quite inexperienced with using custom modes in Mapbox Draw. Please have a look at my code below.

var draw = new MapboxDraw({
        snapLayers: ['Lines_data'],
        });

draw.onMouseMove = function(state, e) {
      const lngLat = this._ctx.snapping.snapCoord(e.lngLat);
 }

How do I implement your mode here and why does draw.onMouseMove not working?

Thank you for your help.

daniel-fschr avatar Apr 14 '20 08:04 daniel-fschr

Hi this a great feature. I would like to use it on a round trip line. Do you think it is possible to choose to snap my point to the going or coming line ?

amelielecoz avatar Apr 14 '20 14:04 amelielecoz

@daniel-fschr You shouldn't need the onMouseMove event. But in any case, this PR is not really complete enough for me to be providing support - you might just need to be patient for a bit.

stevage avatar Apr 14 '20 23:04 stevage

@amelielecoz Sorry, not quite sure what you mean by "the going or coming line"? Can you spell it out a bit more?

stevage avatar Apr 14 '20 23:04 stevage

Hi @setvage, let's take this exemple where I have a trace going from west to east, then going back to the starting point. I am trying to figure out how to make sure my point will be attached exactly where I want. I mean that I want to attach point only to the going line or the coming back line. I know it is a bit tricky and my English is not perfect, I hope you will understand. I am working on this problem for a bit now... If you have any thought on this I'd be happy to discuss

amelielecoz avatar Apr 15 '20 14:04 amelielecoz

Ok, I understand what you mean. I'm not sure I can think of an easy way of supporting that?

stevage avatar Apr 21 '20 11:04 stevage

Sure, good idea. I've done some more work which I haven't pushed yet, too. I realised that the library has to be able to snap to layers that didn't exist when it got instantiated.

@stevage, snapping to layers that didn't exist yet, is this implemented or not yet ? Thank you.

NourdineMazali avatar Jun 05 '20 05:06 NourdineMazali

@stevage it's looking good man I'm gonna try it here. Why isn't it at official repo? Did you try to pull request?

Thank you for sharing!

LuizAsFight avatar Jun 13 '20 20:06 LuizAsFight

This used to work really nicely, but the snapping radius seems to be wildly different between mapbox-gl-js 1.8.1 and 1.11.1 - it's way too aggressively snapping with 1.11.1. I haven't managed to determine what changed to cause that yet.

Plantain avatar Jul 10 '20 16:07 Plantain

@stevage not sure if you've had any requests for this recently, but I just came across this feature in leaflet-geoman, and that library might provide some inspiration

segheysens avatar Jan 09 '24 16:01 segheysens