1

I'm developing a Laravel app with Leaflet, and trying to implement some funcionalities with the Leaflet Draw plugin.

What I'm trying to do is this: Drawing a polygon, rectangle or circle with Leaflet Draw. After the shape has been drawn, I should be able to click over it, and get a Popup with two links: "Descargar" (Download) and "Mapa Filtrado" (filtered map).

The first option allows me to download a '.json' file with the drawed shape in GeoJSON format. The second one generates that GeoJSON object, and after that it pass it to another view, which filters the generated markers on a layer applied to the map object, if those ones are placed within the drawed polygon.

Here's two screenshots ilustrating that behaviour:

Popup generated in my drawed polygon: polygon_popup

Filtered map view: filtrado

Despite those functionalities are working fine, there's something I'm not being able to achieve.

I would like to see that popup when clicking inside the recently drawed shape, but to be erased it when clicking in a point of the map outside of it.

Before implementing the Leaflet Draw functionalities, I had an event listnener which generated me a marker with a popup when clicking in some point of the map.

I could achive to, when first clicking in the Leaflet Draw control icon, to avoid generating that marker with the popup while I'm drawing the shape, and then to have the shape erased (and the marker with popup being generated), when clicking in a point outside that drawed shape.

However, in subsequents attempts, when I'm trying to close some polygon I'm drawing, it just gets erased before I'm being abled to click on it.

At the moment, I attempted a flag-approach to solve this issue, by setting a boolean flag when the 'draw:drawstart' and 'draw:drawstop' events are fired, but it's not working well. Despite it works fine when first clicking the Leaflet Draw control icons before clicking somewhere else in the map, it has that unwanted behaivour after that first draw.

My code is this one:

@extends('layouts.app')

@section('content')

<div class="card">

    <div class="card-body" id="mapid"></div>
</div>
@endsection

@section('styles')
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css"
integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="
crossorigin=""/>
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/MarkerCluster.css" />
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/MarkerCluster.Default.css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.css" />

<style>
    #mapid { min-height: 500px; }
</style>
@endsection

@push('scripts')

<!-- Make sure you put this AFTER Leaflet's CSS -->
<script src="https://unpkg.com/[email protected]/dist/leaflet.js"
integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo="
crossorigin=""></script>
<script src="https://unpkg.com/[email protected]/dist/leaflet.markercluster.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.js"></script>   


<script language="javascript" type="text/javascript">

    //Map generation
    var map = L.map('mapid')      
                .setView( 
                    [
                        {{ config('leaflet.map_center_latitude') }},
                        {{ config('leaflet.map_center_longitude') }}
                    ],
                    {{ config('leaflet.zoom_level') }}
                );

    //Adding TileLayer
    L.tileLayer(
        'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
        {attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'}
    ).addTo(map);
    
    
    //Generating form to send GeoJSON via POST request
    function PostObjectToUri(uri, obj) {
        "use strict";
        var form, input;
        form = document.createElement("form");
        form.method = "post";
        form.action = uri;
        form.insertAdjacentHTML('afterbegin', '{{ csrf_field() }}');
        input = document.createElement("input");
        input.setAttribute("name", "json");
        input.setAttribute("value", obj);
        form.appendChild(input);
        document.body.appendChild(form);
        form.submit();
    };

    
    //Generating FeatureGroup layer
    var editableLayers =  L.featureGroup();
    map.addLayer(editableLayers);


    //Generating Leaflet Draw control with options
    var drawPluginOptions = {
        position: 'topright',
        draw: {
            polygon: {
                allowIntersection: false, // Restricts shapes to simple polygons
                drawError: {
                    color: '#e1e100', // Color the shape will turn when intersects
                    message: '<strong>Oh snap!<strong> you can\'t draw that!' // Message that will show when intersect
                },
                shapeOptions: {
                    color: '#97009c'
                }
            },
            // disable toolbar item by setting it to false
            polyline: false,
            marker: false,
            circlemarker:false
        },
        edit: {
            featureGroup: editableLayers, //REQUIRED!!
            remove: false
        }
    };


    var drawControl = new L.Control.Draw(drawPluginOptions);
    map.addControl(drawControl);

    

    //Flag-approach I've been trying :
    var dibujando=false;

    map.on('draw:drawstart', function(e) {
        alert("Start");
        dibujando=true;
        map.on('click', L.DomEvent.stopPropagation(e));
        
    });

    map.on('draw:drawstop', function(e) {
        alert("Stop");
        dibujando=false;
        map.off('click', L.DomEvent.stopPropagation(e));
    });

    drawControl.setDrawingOptions({
        polygon: {
            shapeOptions: {
                clickable: false
            }
        }
    });

    
    //Generate popup content when clicking the drawn shape (allows to download the GeoJSON file or redirect to the filtered map view)
    map.on('draw:created', function(e) {
    
        var type = e.layerType,
        layer = e.layer;
            var coords=layer._latlngs[0].map(function (latlng) {
                return [latlng.lng,latlng.lat];    
            })
            coords.push([layer._latlngs[0][0].lng,layer._latlngs[0][0].lat]);
            polygon = turf.polygon([coords], { name: 'zona' });
            geojson_polygon=JSON.stringify(polygon);
            var enlaceDescarga = document.createElement("a");
            var blob_geojson=new Blob([geojson_polygon], {type: "application/json"});
            enlaceDescarga.href = URL.createObjectURL(blob_geojson);
            enlaceDescarga.download = "archivo.geojson";
            enlaceDescarga.textContent = 'Descargar';
            var enlacePost = document.createElement("a");
            enlacePost.href = "javascript:PostObjectToUri(\'{{ route('outlet.filter') }}\',geojson_polygon);"
            enlacePost.textContent = 'Mapa Filtrado';
            var contPopup = document.createElement("div");
            contPopup.appendChild(enlaceDescarga);
            contPopup.appendChild(document.createElement("br"));
            contPopup.appendChild(enlacePost);
            document.body.appendChild(contPopup);
            console.log(contPopup);
            layer.bindPopup(contPopup).openPopup();
            editableLayers.addLayer(layer);
    });

 

    //Generate markers layer from GeoJSON and add it to map
    var markers = L.markerClusterGroup();    

    axios.get('{{ route('api.outlets.index') }}')
    .then(function (response) {
        var marker = L.geoJSON(response.data, {
            pointToLayer: function(geoJsonPoint, latlng) {
                return L.marker(latlng).bindPopup(function (layer) {
                    return layer.feature.properties.map_popup_content;
                });
            }
        });
        markers.addLayer(marker);
    })
    .catch(function (error) {
        console.log(error);
    });
    map.addLayer(markers);

    //Behaviour when clicking a point in the mapa outside the drawed polygon
    @can('create', 'App\Outlet')
        var theMarker;
        map.on(
            function onClick(e) {
                if(dibujando==false){
                    alert("Dispara");

                let latitude = e.latlng.lat.toString().substring(0, 17);  
                let longitude = e.latlng.lng.toString().substring(0, 17);
                if (theMarker != undefined) {
                    map.removeLayer(theMarker);
                };
                
                var popupContent = "Your location : " + latitude + ", " + longitude + ".";
                const url=  '{{ route('outlets.create') }}' + '?latitude=' + latitude + '&longitude=' +longitude;

                popupContent += '<br><a href="' + url + '">Add new outlet here</a>';
                theMarker = L.marker([latitude, longitude]).addTo(map);
                

                theMarker.on('click',function onClick(e){
                        map.removeLayer(theMarker);
                    }
                );

                theMarker.bindPopup(popupContent).openPopup();

            }
            if(dibujando==false){
                alert("Se remueve");
                map.removeLayer(editableLayers);
            }
        }
        );
        
    @endcan

</script>

@endpush

I've placed some 'alert(...)' into the event handler's just to see what I get. When I try to close a polygon after the first draw, I see a "Stop" alert (coded in 'draw:drawstop' handler), but despite the shape is erased just after that alert (meaning that the 'map.removeLayer(editableLayers);' was excecuted), I'm not seeing the "Se Remueve" ("It Removes") alert coded a just line above it.

Does anyone could suggest a better approach to solve this? Or some idea of what's happening and I'm not being able to see properly?

If there's some more code or information needed, please tell me.

I'll later try to implement some MySQL spatial funcionalities in my app, but first of all I need to solve this issue of the view, since I'm pretty stuck on it.

Thanks a lot!

Greetings from Argentina

EDIT: You can also notice that in the code, I've also tried to solve the issue via the 'stopPropagation' method within the 'draw:drawstart' and 'draw:drawstop' handlers, but also didn't worked. I wonder if that would be a better approach to the issue, but not properly implemented.

EDIT 2: Added a comment line in the code, which indicates the section where the wanted behaviour, when clicking outside the drawed polygon, is implemented

1 Answer 1

0

I've found the mistake.

I had on the 'map.on('click',...)' handler:

map.on('click',
    function onClick(e) {
        ...
        if(dibujando==false){
            alert("Se remueve");
            map.removeLayer(editableLayers);
        }
    }
);

I was removing from the map, the Layer containing every shape drawn with Leaflet Draw, instead of removing the wanted shape.

Now I have in 'map.on('draw:created', ...)' handler above on my code:

var poligono_anterior;

map.on('draw:created', function(e) {
   ...
   layer = e.layer;
   poligono_anterior=layer;
   ...
}

And in the 'map.on('click', ...)' handler:

map.on('click',
        function onClick(e) {
            ...
            if(dibujando==false){
                if(poligono_anterior)
                editableLayers.removeLayer(poligono_anterior);
            }
        }
);

Not the answer you're looking for? Browse other questions tagged or ask your own question.