Leaflet layerGroup controls - javascript

I have a series of Leaflet FeatureGroups that are made up of a series of GeoJSON layers. The FeatureGroups are conceptually similar but I need them to be separate for certain control reasons. I also need to be able to turn them all on and off at once. Is there a way to do this?
I looked in the documentation and couldn't find an event that fires when the FeatureGroup is switched on and off. There is also no documented way of lumping the FeatureGroups into some kind of superGroup.
For those who want to picture it, here is the workflow:
GeoJSON gets data that is turned into layers in Leaflet. This is of different administration boundaries (e.g. States, Counties, etc...). Each of the layers goes into a different FeatureGroup based on its type (e.g. Arkansas and New York go into the State FeatureGroup, Ford and Lincoln counties go into the County FeatureGroup). This way I have control over opacity and styling for the different FeatureGroups (e.g. when I'm looking at the Counties of a state, I can lower the opacity of all the other states). I also need a way of turning all of this off and back on again. Leaflet provides the ability to do that on a FeatureGroup by FeatureGroup basis, but not a super set of that.
Any ideas on how to achieve this?
New version of question:
What is the event that fires when turning a LayerGroup On and Off? Is there anyway to hook into that?

I ended up having my usual LayerGroups or FeatureGroups, and then a FeatureGroup that contains all the layers on all the groups. So when you add a shape or layer to a FeatureGroup, also add it to the FeatureGroup that you are using to keep track of everything. And of course if you remove don't forget to remove it.
You can add this base FeatureGroup to the map along with the other groups and it should be fine.

I don't think Leaflet currently provides an event when a LayerGroup is turned on or off (you are talking about with the L.Control, right?). I agree that it would be useful. For now, you can just extend the code to do whatever you need to be done. For example:
var customLayerControl = L.Control.Layer.extend({
_onInputClick: function(Layer, name){
// This is just like calling super() if this confuses you!
L.Control.Layers.prototype._onInputClick.call(this,Layer,name);
// Do stuff
}
});
Then instead of using the L.Control.Layers, use your custom layer control:
map.addControl(new customLayerControl({}, {'Custom Layer':customLayer},{}));
I hope this helps.

Related

Leaflet: How can I veto turning on an overlay layer?

My leaflet app has two overlay layers. One of them has a lot of data, so that if the user selects it when not zoomed in pretty far, there is too much data to display without severely impacting the performance of the app (and the data is not useful at wide zoom levels anyway).
So when the user selects the layer in the layer control, the app checks how much data would be displayed at that zoom level. If it's beyond a certain threshold, an alert is given that they need to zoom in more to turn on that layer. The app has essentially vetoed the layer selection, but there seems to be no way to uncheck the checkbox input so that the UI is in sync with the internal state of the app wrt the activation of that layer.
I can programmatically remove the layer altogether, but I don't see any way to uncheck the checkbox input for the layer. Even a brute force approach by getting the layer control HTML container and walking the DOM doesn't seem to be feasible, as there are no IDs or unique attribute values that I can use to get a reference to the checkbox for the desired layer. I also tried removing the layer and re-adding it, but it comes back still checked.
Is there a way to programmatically uncheck the layer checkbox?
EDIT: Here's how I'm creating the overlay layer:
baseMaps = ...
map = ...
myBaseLayer = ...
myOverlayLayer = L.featureGroup();
myOverlayLayer.id = "theId";
let overlayMaps = {
"Base Objects": myBaseLayer,
"Overlay Layer": myOverlayLater
}
L.controlLayer.layers(baseMaps, overlayMaps).addTo(map)
Programmatically removing / adding your layer from/to map should automatically untick/tick the checkbox in the default Leaflet Layers Control.
What you describe sounds to need usage of minZoom option on your overlay layer. If I remember correctly it should also automatically reflect in the Layers Control when the layer is removed due to minZoom threshold.

Change layer z-index in leaflet

I'm using leaflet to trace underground pipes which I create using the polyline methods.
Since the pipes/lines can sometimes overlap I need to be able to highlight the selected line which requires switching the z-index of the line.
Now it seems that leaflet does offer a setZIndex method, but it's not available for individual layers - so I can change it over an entire featureGroup, but that is less helfpul - and making every line into its own separate featureGroup feels like an overkill.
Any suggestions?
You could create another layer with a superior z-index that would be empty at start. When the mouse is going over a feature, copy the feature into that layer. When the mouse is going out, just remove it from the layer.
It might be a little bit cpu demanding, so I suggest you to debounce the mouseover function in order to make the mouse to wait 1-2 seconds over a feature before copying it into the top layer.
As IvanSanchez noted in a comment, the correct answer seems to be a method called bringToFront.

How to activate multiple L.markerClusterGroup() using L.control.layers in leafletjs, instead of just toggling? [duplicate]

Is there an easy way to make a leaflet layers control (L.control.layers) use checkboxes rather than radio buttons?
I have multiple WMS tile layers, and I'd like to be able to have more than one on the map at the same time. For context, the WMS tile layers include bathymetry and contours (topo lines), so it'd be more informative to visualize both at the same time, rather than just having some lines floating in the ocean.
In the leaflet example it says the layers control is "smart enough" to know which to assign radio buttons and which checkboxes, but it'd be nice to have more customized control.
Relevant code:
L.control.layers(WMS, null, {collapsed: false}).addTo(map);
where WMS is multiple L.tileLayer.wms layers.
Pass your WMS as the 2nd argument (i.e. as overlays) instead of the 1st (basemaps) of L.control.layers.
Overlays use check boxes, whereas basemaps use radio buttons.
Here i create an example to demonstrate what ghybs said.
L.control.layers(null, mixed).addTo(map);
Checkout my jsfiddle http://jsfiddle.net/iofirag/Ltub5bgv/18/.

How to avoid that several MultiPolygon GeoJSON layers overlap within leaflet javascript library?

I'm writing a web-site to show different isochrone over city-map:
http://130.192.68.210:8080/citychrone/citychrone.html
I have several MultiPolygon GeoJSON layer in my Leafletjs library based project. I would like that when a layer overlap another only one of this can be visible in the overlapping region. Each of this layer has an opacity less than 1, so is not enough to set the ordering of the layer on the map. I'm wondering if it is possible to give a priority to layer, so when they overlap just one of them is visible.
Another possibility is to make one layer an "hole" of another layer, it is possible?
Turfjs is great for doing geometric operations like this in Javascript.
In your case, you can create holes in a polygon by "erasing" parts of it, using turf-erase: http://turfjs.org/static/docs/module-turf_erase.html. The example on that page shows something similar to what you want to do.

Can polygon.setPath(); be used to completely redefine polygon?

Google Maps JavaScript API V3.
My mapping has me dealing with the idea of polygons, and I'm trying to develop a strategy before I dive into the code.
I'm never going to have more than one polygon on the map at a time, so I'm hoping I can define one polygon and reuse it as you can with markers.
Is my understanding correct that the polygons setPath(); function will move the polygon to represent a new array of points? The documentation says... "Inserting or removing LatLngs from the MVCArray will automatically update the polygon on the map." but it doesn't come right out and say that you can use setPath(); to give it an entirely new array of points.
I'm thinking in psuedocode...
// some event fires
polygon.setPath(latlngArray);
bounds = new google.maps.LatLngBounds();
$.each(latlngArray, function(key, ll){
bounds.extend(ll);
});
polygon.setMap(MyMap);
MyMap.fitBounds(bounds);
//a different event fires
polygon.setMap(null);
// build a new latlngArray
// do it again
Am I correct in thinking that I can reuse the same polygon object in this fasion, or do I need to rethink my strategy?
Thanks.
Skip
EDIT: The answer is yes it can. I'm going to hash out my code better and try to provide a well written answer, that shows the caveats I've come across. Such as, the map must be visible for map.fitBounds(); to give cogent results.
Yes it is quite possible to reuse the same polygon object...
I really don't have any code to offer. The psuedocode I listed in the question basically works.
These are the things I learned or chose in fleshing out my solution...
As stated earlier, the map can't be style="display: none;" for map.fitBounds(); to work as expected.
I already keep a container object that holds my markers. I created a container object for the polygon paths, and add a 'polygon' attribute to markers that are associated with a polygon, so multiple markers can reference the same polygon path.
Even for polygons with a single path, I chose to embed my path array, within another array, then use polygon.setPaths(); This way the code will scale easier if I expand to polygons with multiple paths.
When initially parsing the polygon path build a LatLngBounds object, and then save its bounds.getSouthWest(); & bounds.getNorthEast(); along with the path. This allows for quick and easy map.fitBounds(); at display time, and keeps from recalculating the same bounds multiple times.
Anyhow, yes it is certainly possible, and I think quite efficient to reuse the same polygon object with different paths.
San Francisco & Oakland recycling the same polygon object...
That's all i got!
Skip
UPDATE: I found some polygon data with multiple paths. It plugged right in.
yes, you are both correct in your assumption that redefining the setPaths will redefine the polygon, BUT you must setMap afterwards for the polygon to be redrawn. the polygon may be outside the api window, that is irrelevant. for ease of use, once "repointed", since the polygon may be outside your viewing window, it would be good USABILITY practice to redefine the center of the window using the setCenter method on the map. This code is a chunk that i removed from an application that allows users to draw a polyline and then convert it into a polygon (since user may want to fix perimeter, i allow her to go back and forth between polyline and polygon). i always use the same polygon and simply setPathSSSSSSSSS (there is an S in the end for polygons):
function confirmClosePolyline() {
var pathArrayTemp = areaPerimeterPath.getPath();
var decision = confirm("Change POLYGON?");
if (decision) {
areaMapPolygon.setMap(null); //makes polygon "invisible"/ removes
areaMapPolygon.setPaths(pathArrayTemp);
areaMapPolygon.setMap(map);
}
}
Currently, in 2020 Dec, its sufficiently to use only:
myOldPolygon.setPath(newLatLngs)
to redraw one with new path without resetting map property.
Have tried and this works ok.

Categories

Resources