How to change polygon pane in Leaflet (custom pane) - javascript

I figured out how to create a polygon in a custom pane using the Working with Map Panes tutorial and this Stack Overflow question with its associated Fiddle.
Can I switch the polygon to a different (custom) pane after it has been created?
I create a polygon in a custom pane like so:
// create custom pane
mymap.createPane('polygonView');
var polygonViewPane = mymap.getPane('polygonView');
polygonViewPane.style.zIndex = 300;
// with custom renderer
var myrenderer = L.svg({
pane: polygonViewPane
});
// create polygon in a custom pane (note the `renderer:myrenderer` or `pane:polygonViewPane` both work)
var myPoly = L.polygon([
[51.509, -0.08],
[51.503, -0.06],
[51.51, -0.047]
], {
fillOpacity: 1,
pane: polygonViewPane
}).addTo(mymap);
It would seem like I should be able to switch the pane with something like:
// changes pane in options.pane, but appearance on the map is same
myPoly.setStyle({pane: polygonViewPaneTop});
// also does not work (command fails without output in console?)
myPoly.setStyle({renderer: myrenderer2});
but neither of these are working. I can switch the zIndex of the whole pane with polygonViewPane.style.zIndex = 800;, but I will usually have multiple polygons on that main pane, and I only want to bring one to the front of all of my layers.
https://jsfiddle.net/kbkxf220/
EDIT:
Updated Fiddle incorporating IvanSanchez's answer:
https://jsfiddle.net/kbkxf220/3/
// THIS WORKS; polygon switches pane and `myPoly.options.pane` shows new pane.
myPoly.removeFrom(mymap);
myPoly.setStyle({pane: polygonViewPaneTop, renderer: myrenderer2});
myPoly.addTo(mymap);
Note that you need to switch renderer for the polygon to switch panes on the map view (see inspect element) and switch pane for myPoly.options.pane to properly display the new pane.

No, Leaflet doesn't allow to change panes on the fly.
Remove the layer from the map, change its options, then re-add it.
If your problem is bringing lines/polygons on top of each other, remember that you can call bringToFront() on them.

Related

leaflet layer interrupting interactivity of base layer

I am working on a project based on the Leaflet Choropleth Tutorial.
In my map, I have three base layers that the user switches between
var basemaps= {
"layer1": layer1,
"layer2": layer 2,
"layer3": layer 3
};
Each of these layers highlights and displays popup information just like in the Choropleth template.
I also have layers that display labels(values) for each region, depending which one is active.
var activelabel = L.layerGroup();
var showlabels = {
"labels": activelabel
};
map.on('baselayerchange', function (event) {
activelabel.clearLayers();
switch (event.layer) {
case layer1:
label1.addTo(activelabel);
break;
case layer2:
label2.addTo(activelabel);
break;
case layer3:
label3.addTo(activelabel);
break;
}
});
My problem, is that when the labels are active, if the user hovers over the part of the region where the label is, the highlight feature and info box do not display any information.
I am trying to find a way to make the activelabel layer invisible, so that the hover still works.
I found the tutorial on Map Panes, which seems like the solution to my problem, but it doesn't seem to do anything.
I'm not sure if i'm putting the pane information in the wrong spot, or if this doesn't actually work like I think it should? I've tried changing the xIndex everywhere from 0 to 700 and it doesn't seem to make a difference?
map.createPane('labels');
map.getPane('labels').style.zIndex = 700;
map.getPane('labels').style.pointerevents = 'none';
I've tried putting the pane: 'labels' code here, where I format my labels
var createLabelIcon = function(labelClass,labelText){
return L.divIcon({
className: labelClass,
html: labelText
})
};
as well as here, where I create the layer group.
var label1 = new L.layerGroup();
makelabel1();
Any help would be appreciated. Thank you.
I figured out a solution to this. I have three separate .js files where I add all the layers containing labels to my layergroups.
If I add {interactive: false} to each instance of .addLayer in each file, the labels to not interrupt the mouse hover.
I'm not sure why this doesnt work when I add interactive:false to the layer group as opposed to each addLayer line, but it seems to be working.

Leaflet map with popup above legend

I have a situation where I have a map with a custom legend formatted as either an SVG or a PNG. The legend is always placed in the bottom left corner but can be quite large (user can turn it off and on).
The map also has many markers. Each marker will have a tooltip, which can also be large-ish. Tooltips show when the mouse is hovering over the marker. The problem arises when a user hovers over a marker close to the legend - the tooltip appears behind the legends. I'd like to make it so the popups appear above the legend. So, from bottom to top: marker, legend, marker popup.
Here is a JSFiddle https://jsfiddle.net/e51mydwa/9/ to describe what I mean. I add the legends in the same way, although the < div id="legend"> tag contains a < img> or < svg> in reality.
<div id="map">
<div id="legend">
I am Legend
</div>
</div>
I've had a look at http://leafletjs.com/examples/choropleth/ , but as you can see by inspecting the DOM, this will suffer the same problem, as the legend is added into the same div as the leaflet controls, which is always above the map layers (as it should be, controls should always be at the top).
I've also tried inserting the legend into a div which is on a sibling layer to the popup containing layer. This fixes the z-index issue, however the parent div of both of these contains a transform which changes as the user drags the map around - meaning the legends change places and aren't static.
Any and all suggestions appreciated.
This requires some heavy hacking, due to the architecture of the Leaflet layers and controls.
One possible approach is to make a custom layer class which stays in a static position, by repositioning its pixel offset at every change of the map's view.
I heartily recommend reading the Leaflet tutorials, in particular the one about map panes and the one about custom layers, to understand how this works.
// Create a 'static' map pane
L.Map.addInitHook(function(){
this.createPane('static');
this.getPane('static').style.zIndex = 675;
});
// Define a custom layer class
L.Layer.StaticOverlay = L.Layer.extend({
onAdd: function(map) {
this._map = map;
var pane = map.getPane('static');
this._container = L.DomUtil.create('div');
pane.appendChild(this._container);
// styling, content, etc
this._container.style.background = 'white';
this._container.style.width = '100px';
this._container.style.height = '50px';
this._container.innerHTML = 'Hi!'
map.on('move zoom viewreset zoomend moveend', this._update, this);
this._update();
},
onRemove: function(map) {
L.DomUtil.remove(this._container);
map.off('move zoom viewreset zoomend moveend', this._update, this);
},
_update: function() {
// Calculate the offset of the top-left corner of the map, relative to
// the [0,0] coordinate of the DOM container for the map's main pane
var offset = map.containerPointToLayerPoint([0, 0]);
// Add some offset so our overlay appears more or less in the middle of the map
offset = offset.add([340, 220]);
L.DomUtil.setPosition(this._container, offset);
}
});
When that's defined, you can simply
var static = new L.Layer.StaticOverlay().addTo(map);
Obviously there are some bits missing, such as how to position the overlay properly (get the map pixel size with getSize(), do the proper arithmetic), and how to set the contents of the overlay with some custom options in the layer constructor.
These are left as an exercise to the reader :-)
See a working example here.

Leaflet overlay order (points, lines, and polygons)

I'm working with the Leaflet.StyledLayerControl plugin and would like to set my layers so that polygons always get pushed to the back, lines above the polygons, and points above the lines.
I have searched for solutions here but most refer to tilelayers or map panes (which I think only work with another version of leaflet 1.0).
I want to be able to toggle lines on and off and have them always be below points (same with polygons below polylines).
I'm guessing I have to do something with setZIndex or bringToFront() but i'm not sure where to start.
Any help would be appreciated. Thanks.
The easy solution is really to use Leaflet 1.0, which provides you with map.createPane("paneName") functionality. So you create like "polygonsPane", "linesPane" and "pointsPane", that you specify to each of your vector / shape layers using their pane option.
Once set, you can add / remove them to / from map, and they will always be inserted in the specified pane, hence respecting the pane's order.
// Create necessary panes in correct order (i.e. "bottom-most" first).
map.createPane("polygonsPane");
map.createPane("linesPane");
map.createPane("pointsPane");
L.polygon([/* coords */], { pane: "polygonsPane" }).addTo(map);
L.polyline([/* coords */], { pane: "linesPane" }).addTo(map);
L.circleMarker([/* coords */], { pane: "pointsPane" }).addTo(map);
Demo: https://jsfiddle.net/3v7hd2vx/51/
With Leaflet 0.7, you know that all vector layers are part of the same SVG container, being appended when added to map, hence they appear on top of all previously added vectors. Then you have to use bringToFront() and/or bringToBack() to re-sort them to whatever order you need.
You might be interested in that post for that case: https://gis.stackexchange.com/questions/166252/geojson-layer-order-in-leaflet-0-7-5/167904#167904

leafletjs overlay layer got hidden

I have 3 layers in my map. All layers are imageOverlay type in order to show a big image instead of tile style.
The first 2 layers are the base image which i can switch between (radio) and the third layer is the overlay that should be displayed on top of them if checked (checkbox).
My problem is that when i choose one of the base layers the overlay layer (third layer) got hidden instead of showing on top of it.
this is how i initiate the layers:
var image1Layer = L.imageOverlay(image1Url, imageBounds);
var image2Layer = L.imageOverlay(image2Url, imageBounds);
var image3Layer = L.imageOverlay(image3Url, imageBounds);
L.control.layers({
'New': image2Layer,
'Old': image1Layer
},
{
'Changes': image3Layer
},
{collapsed:false}).addTo(map);
You can check to see if the overlayLayer is present on map, and if it is, bring it to front whenever a base layer is changed.
map.on('baselayerchange', function() {
if (map.hasLayer(image3Layer)) {
image3Layer.bringToFront();
}
});

How do I programatically change layers in Leaflet?

I don't want to the show the layers control on the map, but I want to put some buttons somewhere else to change between layers. Is this possible to change the layer programmatically?
Suppose you have a map:
var map = L.map('worldmap-map').setView([37.8, -96], 4);
To remove a layer, layer1:
map.removeLayer(layer1)
To remove a control layer, ctrlLayer,
map.removeControl(ctrlLayer)
Or you want to add a layer1 to map:
layer1.addTo(map)
For an example, there is a Leaflet example : http://leafletjs.com/examples/choropleth-example.html
You could use firebug or chrome dev tools to see its source.
From https://stackoverflow.com/a/33762133/4355695 : Just myTileLayer.addTo(map) does the job of changing background layer (without adding on top), if it is already part of the base layers. And you don't need to explicitly remove the previously selected background layer.

Categories

Resources