makinacorpus/Leaflet.Snap

How to make features drawn with leaflet.draw always snap to other features dynamically drawn?

scratch85 opened this issue · 12 comments

Hi,

during drawing with leaflet.draw, the features will snap. But if they are drawn and made editable they won't. How to solve this? Do I really have to manage all objects to refresh the guideLayers to make them snapping again?

// Create a map in the "map" div, set the view to a given place and zoom
var map = L.map('map').setView([48.48922, 1.40033], 13);

// Add scale
L.control.scale().addTo(map);

// Add an OpenStreetMap tile layer
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
    attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);

// Initialise the FeatureGroup to store editable layers
var drawnItems = new L.FeatureGroup();
map.addLayer(drawnItems);

// Snapping
guideLayers = new Array();

// Initialise the draw control and pass it the FeatureGroup of editable layers
var drawControl = new L.Control.Draw({
    draw: {
        polyline: { guideLayers: guideLayers },
        marker: { guideLayers: guideLayers },
        polygon: false,
        rectangle: false,
        circle: false
    },
    edit: {
        featureGroup: drawnItems,
        edit: false
        // Delete still remains useable
    }
});
map.addControl(drawControl);

map.on('draw:created', function (event) {
    var type = event.layerType;
    var layer = event.layer;
    if(type === 'marker') {
        // Make marker draggable
        layer.options.draggable = true;
    }
    else {
        // Make line editable
        layer.editing.enable();
        // Activate snapping - does not work ... :(
        layer.snapediting = new L.Handler.PolylineSnap(map,layer);
        layer.snapediting.addGuideLayer(guideLayers);
        layer.snapediting.enable();
    }

    // Add to drawnItems
    drawnItems.addLayer(layer);
    // Add newly drawn feature to list of snappable features
    guideLayers.push(layer);
});

Tried it with adding a for-loop at the end of the draw:created event but sadly it doesn't work

    for(var i = 0;i < guideLayers.length;i++) {
        console.log('layer ' + i + ' refreshed');
        guideLayers[i].snapediting.addGuideLayer(guideLayers);
    }

Thanks

Curious, it looks like I do this all the time... Could you put it in a jsfiddle or something similar to share the whole code? Thanks

Hi,

new to the fiddle stuff, but it shows the whole code.
http://jsfiddle.net/8xEq6/

I used the latest version I could download from all the githubs and leaflet ...

Thanks

I read your code, I think there is a confusion between the global variable (guideLayers) that stores and the activation of snapping...

Something as simple as that should do it :

    // Activate snapping
    layer.snapediting = new L.Handler.PolylineSnap(map,layer);
    for(var i = 0;i < guideLayers.length; i++) {
        layer.snapediting.addGuideLayer(guideLayers[i]);
    }
    layer.snapediting.enable();

Indeed, that addGuideLayer() takes a layer, not a list of layers.

In addition of new lines being snapped to the previous ones, do you also want previously drawn lines to be snapped to the new ones ?

Yes, I wanna make every item (lines and markers/already drawn or during draw) snappable to each other. Thanks for your example, I will try if that works for me.

Tried the code today.
During draw all my lines and markers snap to each other perfectly. But while I'm editing snapping is not working. Don't get the clue why :(
Thanks for any help.

Just a short notice: upgrading all components to the newest available github versions breaks it all. No snapping anymore.

This is the full code i used:

<!DOCTYPE html>
<html>
<head>
    <title>Leaflet Example</title>

    <link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.2/leaflet.css" />
    <link rel="stylesheet" href="leaflet.draw.css" />

    <script src="http://code.jquery.com/jquery-1.10.2.min.js"></script>

    <script src="http://cdn.leafletjs.com/leaflet-0.7.2/leaflet.js?2"></script>
    <script src="leaflet.draw.js"></script>
    <script src="leaflet.geometryutil.js"></script>
    <script src="leaflet.snap.js"></script>

</head>
<body>
<div id="map" style="width: 1200px; height: 800px;"></div>

<script>

// Create a map in the "map" div, set the view to a given place and zoom
var map = L.map('map').setView([48.48922, 1.40033], 13);

// Add scale
L.control.scale().addTo(map);

// Add an OpenStreetMap tile layer
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
    attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);

// Initialise the FeatureGroup to store editable layers
var drawnItems = new L.FeatureGroup();
map.addLayer(drawnItems);

// Snapping
var guideLayers = new Array();

// Initialise the draw control and pass it the FeatureGroup of editable layers
var drawControl = new L.Control.Draw({
    draw: {
        polyline: { guideLayers: guideLayers },
        marker: { guideLayers: guideLayers },
        polygon: false,
        rectangle: false,
        circle: false
    },
    edit: {
        featureGroup: drawnItems,
        edit: false
        // Delete still remains useable
    }
});
map.addControl(drawControl);

map.on('draw:created', function (event) {
    var type = event.layerType;
    var layer = event.layer;
    if(type === 'marker') {
        // Make marker draggable
        layer.options.draggable = true;
    }
    else {
        // Make line editable
        layer.editing.enable();
    }
    // Activate snapping
    layer.snapediting = new L.Handler.PolylineSnap(map,layer);
    for(var i = 0;i < guideLayers.length; i++) {
        // Add every already drawn layer to snap list
        layer.snapediting.addGuideLayer(guideLayers[i]);
        // Add the currently drawn layer to the snap list of the already drawn layers
        guideLayers[i].snapediting.addGuideLayer(layer);
        guideLayers[i].snapediting.enable();
    }
    layer.snapediting.enable();

    // Add to drawnItems
    drawnItems.addLayer(layer);
    // Add newly drawn feature to list of snappable features
    guideLayers.push(layer);
});

</script>

</body>
</html>

I guess you need to enable the right handler depending on the layer type (leaflet does not manage markers like vector layers)

    if (type === 'marker') {
        layer.snapediting = new L.Handler.MarkerSnap(map, layer);
    }
    else {
        layer.snapediting = new L.Handler.PolylineSnap(map, layer);
    }
    for(var i = 0;i < guideLayers.length; i++) {
         layer.snapediting.addGuideLayer(guideLayers[i]);
    }
    layer.snapediting.enable();
    guideLayers.push(layer);

The demo example shows something similar...

Updated my code, sadly it doesn't work ... result is the same. Could it be possible that this won't work with Leaflet 0.7.2?
I used the L.Draw and L.GeomUtil js files from the L.Snap github repo.

I would say it's related to leaflet at all.

Try to track what happens with console.trace() or something

I have the same problem as described above. My code looks similar to that of scratch85, and there is no snapping at all.
Does Leaflet.Snap depend on a specific version of Leaflet?

There's another issue with some explanation and a hack: #11

i think #30 resolve this

Hi to all,
I have had the same problem and found solutio using L.Draw. There a few things which have to be observed in next code:

map.on(L.Draw.Event.CREATED, function (event) {
	var type = event.layerType;
	layer = event.layer;
	layer.addTo(map);
	if (type === 'marker') {
		// Do marker specific actions			  
		layer.options.draggable = true;
		layer.snapediting = new L.Handler.MarkerSnap(map, layer);
	} 
	else
	{
		// Make line editable
		layer.editing.enable();		
		// Activate snapping
		layer.snapediting = new L.Handler.PolylineSnap(map, layer);				
	}
		// Add the currently drawn layer to the snap list of the already drawn layers
		for(var i = 0;i < guideLayers.length; i++) {			
			layer.snapediting.addGuideLayer(guideLayers[i]);
		}
		// Enable snap editing
		layer.snapediting.enable();					
			
		// add new drawn feature to list od drawnItems features
		drawnItems.addLayer(layer);
		// Add newly drawn feature to list of snappable features
		guideLayers.push(layer);
			
		// Do whatever else you need to. (save to db; add to map etc)	
});

The main hint to solve mentionde problem was line 4: layer.addTo(map);. To be able to snap with already existing layers ( when we Edit newly created layer) we have to add him to map.
The other lines are important to but were metioned earlire on current topic.

P.S. the rest of code, abow code metionde earlier, was:

var map = L.map('map', { center: new L.LatLng(48.48988, 1.39638), zoom: 14 });
// Leaflet Draw
		var drawnItems = new L.FeatureGroup();
        map.addLayer(drawnItems);             
        
        var guideLayer =
            L.polyline([
                [48.505431207150885, 1.3999843597412107],
                [48.50335551764662, 1.398911476135254],
                [48.50173471468476, 1.3994693756103516],
                [48.49974418399956, 1.3991689682006836],
                [48.49684355649577, 1.3993835449218748],
                [48.4956206932084, 1.398611068725586],
                [48.49465375716902, 1.3980531692504883],
                [48.49419872206354, 1.3975811004638672],
                [48.492406981637345, 1.3971948623657227],
                [48.49156797030711, 1.396486759185791],
                [48.49067206152607, 1.3961219787597656],
                [48.48988, 1.39638],
                [48.489342389949364, 1.394963264465332],
                [48.48864554279267, 1.3944590091705322],
                [48.487628697617744, 1.3940191268920896],
                [48.485666057669334, 1.3944482803344727],
                [48.48541005555473, 1.3942551612854002],
                [48.48461359626773, 1.3942766189575195],
                [48.483489998505746, 1.3933539390563965],
                [48.48164098598135, 1.3928818702697754],
                [48.480232846617845, 1.3912296295166016],
                [48.479450530080534, 1.3906073570251463],
                [48.478511734309954, 1.3902640342712402],
                [48.47714618217502, 1.389319896697998],
                [48.47600819398379, 1.388998031616211]
            ], {
                    weight: 5,
                    color: 'red',
                    opacity: 1.0
                }).addTo(map);

        var marker = L.marker([48.488, 1.395]).addTo(map);
        marker.snapediting = new L.Handler.MarkerSnap(map, marker);
        marker.snapediting.addGuideLayer(guideLayer);
        marker.snapediting.enable();
        var road = L.polyline([
            [48.48922, 1.40033],
            [48.48935, 1.39981],
            [48.48948, 1.3976],
            [48.48986, 1.39634]
        ], {
            color: 'green',
            opacity: 1.0
        }).addTo(map);

        road.snapediting = new L.Handler.PolylineSnap(map, road);
        road.snapediting.addGuideLayer(guideLayer);
        road.snapediting.enable();
        marker.snapediting.addGuideLayer(road);

        var guideLayers = [guideLayer, road];
        drawnItems.addLayer(guideLayer);
        drawnItems.addLayer(road);
        
        var drawControl = new L.Control.Draw({			
		position: 'topleft',
            draw: {
                polyline: true,
                polygon: true,
                circle: false,
                marker: true,               
                poly: {
				allowIntersection: false
			}
            },           
            edit: 
            {
                featureGroup: drawnItems,                               
                remove: true
            }
        });    
        map.addControl(drawControl);
        
        drawControl.setDrawingOptions({
            polyline: { guideLayers: guideLayers},
            polygon: { guideLayers: guideLayers, snapDistance: 5 },
            marker: { guideLayers: guideLayers, snapVertices: false },
            rectangle: false,
            circle: false
        });