Search code examples
javascriptjqueryleafletangular-leaflet-directive

Binding drawn shapes/markers with specific toggling layer : Leaflet


I am working with the application Beta_Here which uses leaflet plugins, all libraries are local except for few(css related)

Usage of application live

First View:This application get input from user and set the distance calculation formula accordingly....

Second View : After entering input e.g 9, second view will be loaded where we can draw shapes....

Introduction

I have setup the script which will load two imageoverlays(layers) and we can toggle them from top right and we can draw or measure from bottom left....

Problem

When we draw shapes or put markers on an image, controls work nearly perfect but when we toggle the layers, there starts the problem.... all shapes go to the background or (it seems they disappeared)

Main Question

How can we bind the drawings and marker to the specific layer(imageoverlay) if there is a way as we can see the drawing are not bind with the images but the map container..... (Pardon me if you feel i am doing something stupid because i have limited knowledge about layers so i came up with my question here....

If someone has idea about how to solve this problem, please do help or any kind of reference will be appreciated... Thanks for your time

Working Script

var map = L.map('map', {
                    minZoom: 1,
                    maxZoom: 4,
                    center: [0, 0],
                    zoom: 0,
                    crs: L.CRS.Simple
                });

                // dimensions of the image
                var w = 3200,
                    h = 1900,
                    mainurl = 'assets/img/isbimg.jpg';
                childurl = 'assets/img/fjmap.png';
                // calculate the edges of the image, in coordinate space
                var southWest = map.unproject([0, h], map.getMaxZoom() - 1);
                var northEast = map.unproject([w, 0], map.getMaxZoom() - 1);
                var bounds = new L.LatLngBounds(southWest, northEast);

                var featureGroup = L.featureGroup().addTo(map);

                var drawControl = new L.Control.Draw({
                    edit: {
                        featureGroup: featureGroup
                    },
                    draw: {
                        polygon: true,
                        polyline: true,
                        rectangle: true,
                        circle: true,
                        marker: true
                    }
                }).addTo(map);

                map.on('draw:created', showPolygonArea);
                map.on('draw:edited', showPolygonAreaEdited);
                // add the image overlay,so that it covers the entire map
                L.control.layers({
                    Main: L.imageOverlay(mainurl, bounds),
                    Child: L.imageOverlay(childurl, bounds)
                }, null, { collapsed: false }).addTo(map);

                L.control.nanomeasure({ nanometersPerPixel: 10000 }).addTo(map);

                // tell leaflet that the map is exactly as big as the image
                map.setMaxBounds(bounds);

                L.tileLayer({
                    attribution: '<a href="http://smartminds.co">SmartMinds</a>',
                    maxZoom: 18
                }).addTo(map);

                //polygon area customization
                function showPolygonAreaEdited(e) {
                    e.layers.eachLayer(function (layer) {
                        showPolygonArea({ layer: layer });
                    });
                }
                function showPolygonArea(e) {
                    var userInputCustom = prompt("Please enter image name : choose between a to f");
                    featureGroup.addLayer(e.layer);
                    e.layer.bindPopup("<div style='width:200px;height:200px;background-image: url(assets/img/" + userInputCustom + ".png);background-size: 195px 195px;;background-repeat: no-repeat;'></div>");
                    e.layer.openPopup();
                }

            });

Solution

  • I would contain those FeatureGroup and ImageOverlay pairs into L.LayerGroup's. Then you can switch between those groups. Then you can keep track of the currently selected group, and add your features to the featurelayer of that group. I can explain it better with code through comments:

    Basic map, nothing special:

    var map = L.map('map', {
      'center': [0, 0],
      'zoom': 1,
      'layers': [
        L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
          'attribution': 'Map data &copy; OpenStreetMap contributors'
        })
      ]
    });
    
    // Bounds for the map and imageoverlays    
    var bounds = L.latLngBounds([[40.712216, -74.22655],[40.773941, -74.12544]]);
    
    // Set bounds on the map
    map.fitBounds(bounds);
    

    The grouping part:

    // New layergroup, note it's not added to the map yet
    var layerGroup = new L.LayerGroup(),
        imageOverlayUrl = 'https://placeholdit.imgix.net/~text?txtsize=33&txt=Overlay 1&w=294&h=238',
        // New imageoverlay added to the layergroup
        imageOverlay = new L.ImageOverlay(imageOverlayUrl, bounds).addTo(layerGroup),
        // New featuregroup added to the layergroup
        featureGroup = new L.FeatureGroup().addTo(layerGroup);
    
    // Second layergroup not added to the map yet
    var layerGroup2 = new L.LayerGroup(),
        imageOverlayUrl2 = 'https://placeholdit.imgix.net/~text?txtsize=33&txt=Overlay 2&w=294&h=238',
        // New imageoverlay added to the second layergroup
        imageOverlay2 = new L.imageOverlay(imageOverlayUrl2, bounds).addTo(layerGroup2),
        // New featuregroup added to the second layergroup
        featureGroup2 = new L.FeatureGroup().addTo(layerGroup2);
    

    Default drawcontrol and layercontrol with both layergroups added as baselayers:

    var layerControl = new L.control.layers({
      'Group 1': layerGroup,
      'Group 2': layerGroup2
    }).addTo(map);
    
    var drawControl = new L.Control.Draw().addTo(map);
    

    Here's where the magic happens ;) :

    // Variable to hold the selected layergroup's featuregroup.
    var currentFeatureGroup;
    
    // Catch the layer change event
    map.on('baselayerchange', function (layersControlEvent) {
      // Loop over the layers contained in the current group
      layersControlEvent.layer.eachLayer(function (layer) {
        // If it's the imageoverlay make sure it's in the background
        if (layer instanceof L.ImageOverlay) {
          layer.bringToBack();
        // If not then it's the featuregroup, reference with variable.
        } else {
          currentFeatureGroup = layer;
        }
      });
    });
    
    // Catch draw created event    
    map.on('draw:created', function (e) {
        // Store created feature into the current featuregroup
        currentFeatureGroup.addLayer(e.layer);
    });
    

    That's it. Pretty basic just meant as an example but it does what you want it to do. A real implementation would look different, with errorhandling because for instance when you draw and have no baselayer/overlay selected it fail etc. Here's a working example on Plunker to play with: http://plnkr.co/edit/6cGceX?p=preview