Search code examples
javascriptleafletmapboxgeojson

Leaflet GeoJSON points *behind* polygon


I have two leaflet geojson layers - they both have Point and Polygon features. I would like to be able to order them on the map, but when I do that today (attempting to order them by adding them in a certain order, using bringToBack/bringToFront, etc) the Point icons from both layers sit on top of all polygons.

The reason (from my limited experience) appears to be that they're drawn on a completely different pane in the map.

I'd very much like to have the points from a layer drawn on the same pane as the polygons from that layer, so when I order all of the layers, the points for the layer "below" are underneath the polygons on the layer "on top".

Is there an easy/obvious way to do this that I've missed, or is this impossible today?

Thanks very much!

HTML:

<body>
  <div id='map'></div>
<body>

JS:

var featColl1 = {
  type : 'FeatureCollection',
  features : [
    {
      type: "Feature",
      geometry: {
          type: "Point",
          coordinates: [-100, 48]
      }  
    },
    {
      type: "Feature",
      geometry: {
          type: "Polygon",
          coordinates: [[
            [-104.05, 48.99],
            [-97.22,  48.98],
            [-96.58,  45.94],
            [-104.03, 45.94],
            [-104.05, 48.99]
        ]]
      }  
    }
  ]
};

var featColl2 = {
  type : 'FeatureCollection',
  features : [
    {
      type: "Feature",
      geometry: {
          type: "Point",
          coordinates: [-103, 47]
      }  
    },
    {
      type: "Feature",
      geometry: {
          type: "Polygon",
          coordinates: [[
            [-104.05, 48.99],
            [-97.22,  48.98],
            [-96.58,  45.94],
            [-104.03, 45.94],
            [-104.05, 48.99]
        ]]
      }  
    }
  ]
};

var layer1Opts = {
  style :   { color : 'red' },
  pointToLayer : function(feat, latlng) {
    var opts = {
      icon : L.divIcon({ html : "1", iconSize: [15,15] })
    };
    return L.marker(latlng, opts);
  }
};
var layer2Opts = {
    pointToLayer : function(feat, latlng) {
    var opts = {
      icon : L.divIcon({ html : "2", iconSize: [15,15] })
    };
    return L.marker(latlng, opts);
  }
};


var map = new L.map('map')
  .setView([-103, 49], 5);

L.tileLayer('http://{s}.tile.stamen.com/watercolor/{z}/{x}/{y}.jpg')
  .addTo(map);

var layer1 = L.geoJson(featColl1, layer1Opts).addTo(map);
var layer2 = L.geoJson(featColl2, layer2Opts).addTo(map);

var bounds = new L.LatLngBounds();
bounds.extend(layer1.getBounds());
bounds.extend(layer2.getBounds());

map.fitBounds(bounds);

Solution

  • You can't easily do what you want to with the version of Leaflet (0.7.3) in that plunker.

    in Leaflet 1.0 (currently in Beta) you can create custom panes, set their z-index and have finer control over the drawing order of markers, layers and all elements on your map.

    See this section of the Leaflet 1.0 documentation for more information

    https://mourner.github.io/Leaflet/reference.html#map-panes

    You could create two custom panes one with a z-index below the overlay pane (which has a default z-index of 4) and one with a z-index above it.

    Then you have your choice of where the icons show up based on which pane you add them to.

    So the options for your two layers will look something like this

    var activePane = map.createPane("activePane"),
        inactivePane = map.createPane("inactivePane");
    
    var layer1Opts = {
      style :   { color : 'red' },
      pointToLayer : function(feat, latlng) {
        var opts = {
          icon : L.divIcon({ html : "1", iconSize: [15,15], pane: "inactivePane" })
        };
        return L.marker(latlng, opts);
      }
    };
    var layer2Opts = {
        pointToLayer : function(feat, latlng) {
        var opts = {
          icon : L.divIcon({ html : "2", iconSize: [15,15], pane: "activePane" })
        };
        return L.marker(latlng, opts);
      }
    };
    

    The panes are given an autogenerated class in this format

    leaflet-pane-name

    so you'll want css with something like:

    .leaflet-activePane{z-index:10;}
    .leaflet-inactivePane{z-index:3}
    

    since 3 is above the tile pane but below the overlay pane according to the documentation link above.