Skip to content

Commit aa13026

Browse files
hansthenConengmo
andauthored
Add layer control tree plugin (#1895)
* Re: #1606 Add TreeLayerControl to Folium * add custom Template class with tojavascript filter (#1912) * Drop Bootstrap 3 and use separate glyphicons css file (#1914) --------- Co-authored-by: Frank Anema <[email protected]>
1 parent 00a7dbd commit aa13026

File tree

5 files changed

+242
-1
lines changed

5 files changed

+242
-1
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ dist/
77
docs/index.html
88
docs/_build/
99
docs/quickstart.ipynb
10+
docs/**/*.ipynb
1011
examples/results/*
1112
.cache/
1213
.idea/

docs/user_guide/plugins/realtime.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import folium
66
import folium.plugins
77
```
88

9-
# Realtime plugin
9+
# Realtime
1010

1111
Put realtime data on a Leaflet map: live tracking GPS units,
1212
sensor data or just about anything.
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
```{code-cell} ipython3
2+
---
3+
nbsphinx: hidden
4+
---
5+
import folium
6+
import folium.plugins
7+
```
8+
9+
# TreeLayerControl
10+
Create a Layer Control allowing a tree structure for the layers.
11+
12+
See https://github.com/jjimenezshaw/Leaflet.Control.Layers.Tree for more
13+
information.
14+
15+
## Simple example
16+
17+
```{code-cell} ipython3
18+
import folium
19+
from folium.plugins.treelayercontrol import TreeLayerControl
20+
from folium.features import Marker
21+
22+
m = folium.Map(location=[46.603354, 1.8883335], zoom_start=5)
23+
osm = folium.TileLayer("openstreetmap").add_to(m)
24+
25+
overlay_tree = {
26+
"label": "Points of Interest",
27+
"select_all_checkbox": "Un/select all",
28+
"children": [
29+
{
30+
"label": "Europe",
31+
"select_all_checkbox": True,
32+
"children": [
33+
{
34+
"label": "France",
35+
"select_all_checkbox": True,
36+
"children": [
37+
{ "label": "Tour Eiffel", "layer": Marker([48.8582441, 2.2944775]).add_to(m) },
38+
{ "label": "Notre Dame", "layer": Marker([48.8529540, 2.3498726]).add_to(m) },
39+
{ "label": "Louvre", "layer": Marker([48.8605847, 2.3376267]).add_to(m) },
40+
]
41+
}, {
42+
"label": "Germany",
43+
"select_all_checkbox": True,
44+
"children": [
45+
{ "label": "Branderburger Tor", "layer": Marker([52.5162542, 13.3776805]).add_to(m)},
46+
{ "label": "Kölner Dom", "layer": Marker([50.9413240, 6.9581201]).add_to(m)},
47+
]
48+
}, {"label": "Spain",
49+
"select_all_checkbox": "De/seleccionar todo",
50+
"children": [
51+
{ "label": "Palacio Real", "layer": Marker([40.4184145, -3.7137051]).add_to(m)},
52+
{ "label": "La Alhambra", "layer": Marker([37.1767829, -3.5892795]).add_to(m)},
53+
]
54+
}
55+
]
56+
}, {
57+
"label": "Asia",
58+
"select_all_checkbox": True,
59+
"children": [
60+
{
61+
"label": "Jordan",
62+
"select_all_checkbox": True,
63+
"children": [
64+
{ "label": "Petra", "layer": Marker([30.3292215, 35.4432464]).add_to(m) },
65+
{ "label": "Wadi Rum", "layer": Marker([29.6233486, 35.4390656]).add_to(m) }
66+
]
67+
}, {
68+
}
69+
]
70+
}
71+
]
72+
}
73+
74+
control = TreeLayerControl(overlay_tree=overlay_tree).add_to(m)
75+
```

folium/plugins/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
from folium.plugins.time_slider_choropleth import TimeSliderChoropleth
3232
from folium.plugins.timestamped_geo_json import TimestampedGeoJson
3333
from folium.plugins.timestamped_wmstilelayer import TimestampedWmsTileLayers
34+
from folium.plugins.treelayercontrol import TreeLayerControl
3435
from folium.plugins.vectorgrid_protobuf import VectorGridProtobuf
3536

3637
__all__ = [
@@ -66,5 +67,6 @@
6667
"TimeSliderChoropleth",
6768
"TimestampedGeoJson",
6869
"TimestampedWmsTileLayers",
70+
"TreeLayerControl",
6971
"VectorGridProtobuf",
7072
]

folium/plugins/treelayercontrol.py

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
from typing import Union
2+
3+
from branca.element import MacroElement
4+
5+
from folium.elements import JSCSSMixin
6+
from folium.template import Template
7+
from folium.utilities import parse_options
8+
9+
10+
class TreeLayerControl(JSCSSMixin, MacroElement):
11+
"""
12+
Create a Layer Control allowing a tree structure for the layers.
13+
See https://github.com/jjimenezshaw/Leaflet.Control.Layers.Tree for more
14+
information.
15+
16+
Parameters
17+
----------
18+
base_tree : dict
19+
A dictionary defining the base layers.
20+
Valid elements are
21+
22+
children: list
23+
Array of child nodes for this node. Each node is a dict that has the same valid elements as base_tree.
24+
label: str
25+
Text displayed in the tree for this node. It may contain HTML code.
26+
layer: Layer
27+
The layer itself. This needs to be added to the map.
28+
name: str
29+
Text displayed in the toggle when control is minimized.
30+
If not present, label is used. It makes sense only when
31+
namedToggle is true, and with base layers.
32+
radioGroup: str, default ''
33+
Text to identify different radio button groups.
34+
It is used in the name attribute in the radio button.
35+
It is used only in the overlays layers (ignored in the base
36+
layers), allowing you to have radio buttons instead of checkboxes.
37+
See that radio groups cannot be unselected, so create a 'fake'
38+
layer (like L.layersGroup([])) if you want to disable it.
39+
Default '' (that means checkbox).
40+
collapsed: bool, default False
41+
Indicate whether this tree node should be collapsed initially,
42+
useful for opening large trees partially based on user input or
43+
context.
44+
selectAllCheckbox: bool or str
45+
Displays a checkbox to select/unselect all overlays in the
46+
sub-tree. In case of being a <str>, that text will be the title
47+
(tooltip). When any overlay in the sub-tree is clicked, the
48+
checkbox goes into indeterminate state (a dash in the box).
49+
overlay_tree: dict
50+
Similar to baseTree, but for overlays.
51+
closed_symbol: str, default '+',
52+
Symbol displayed on a closed node (that you can click to open).
53+
opened_symbol: str, default '-',
54+
Symbol displayed on an opened node (that you can click to close).
55+
space_symbol: str, default ' ',
56+
Symbol between the closed or opened symbol, and the text.
57+
selector_back: bool, default False,
58+
Flag to indicate if the selector (+ or −) is after the text.
59+
named_toggle: bool, default False,
60+
Flag to replace the toggle image (box with the layers image) with the
61+
'name' of the selected base layer. If the name field is not present in
62+
the tree for this layer, label is used. See that you can show a
63+
different name when control is collapsed than the one that appears
64+
in the tree when it is expanded.
65+
collapse_all: str, default '',
66+
Text for an entry in control that collapses the tree (baselayers or
67+
overlays). If empty, no entry is created.
68+
expand_all: str, default '',
69+
Text for an entry in control that expands the tree. If empty, no entry
70+
is created
71+
label_is_selector: str, default 'both',
72+
Controls if a label or only the checkbox/radiobutton can toggle layers.
73+
If set to `both`, `overlay` or `base` those labels can be clicked
74+
on to toggle the layer.
75+
**kwargs
76+
Additional (possibly inherited) options. See
77+
https://leafletjs.com/reference.html#control-layers
78+
79+
Examples
80+
--------
81+
>>> import folium
82+
>>> from folium.plugins.treelayercontrol import TreeLayerControl
83+
>>> from folium.features import Marker
84+
85+
>>> m = folium.Map(location=[46.603354, 1.8883335], zoom_start=5)
86+
87+
>>> marker = Marker([48.8582441, 2.2944775]).add_to(m)
88+
89+
>>> overlay_tree = {
90+
... "label": "Points of Interest",
91+
... "selectAllCheckbox": "Un/select all",
92+
... "children": [
93+
... {
94+
... "label": "Europe",
95+
... "selectAllCheckbox": True,
96+
... "children": [
97+
... {
98+
... "label": "France",
99+
... "selectAllCheckbox": True,
100+
... "children": [
101+
... {"label": "Tour Eiffel", "layer": marker},
102+
... ],
103+
... }
104+
... ],
105+
... }
106+
... ],
107+
... }
108+
109+
>>> control = TreeLayerControl(overlay_tree=overlay_tree).add_to(m)
110+
"""
111+
112+
default_js = [
113+
(
114+
"L.Control.Layers.Tree.min.js",
115+
"https://cdn.jsdelivr.net/npm/[email protected]/L.Control.Layers.Tree.min.js", # noqa
116+
),
117+
]
118+
default_css = [
119+
(
120+
"L.Control.Layers.Tree.min.css",
121+
"https://cdn.jsdelivr.net/npm/[email protected]/L.Control.Layers.Tree.min.css", # noqa
122+
)
123+
]
124+
125+
_template = Template(
126+
"""
127+
{% macro script(this,kwargs) %}
128+
L.control.layers.tree(
129+
{{this.base_tree|tojavascript}},
130+
{{this.overlay_tree|tojavascript}},
131+
{{this.options|tojson}}
132+
).addTo({{this._parent.get_name()}});
133+
{% endmacro %}
134+
"""
135+
)
136+
137+
def __init__(
138+
self,
139+
base_tree: Union[dict, list, None] = None,
140+
overlay_tree: Union[dict, list, None] = None,
141+
closed_symbol: str = "+",
142+
opened_symbol: str = "-",
143+
space_symbol: str = "&nbsp;",
144+
selector_back: bool = False,
145+
named_toggle: bool = False,
146+
collapse_all: str = "",
147+
expand_all: str = "",
148+
label_is_selector: str = "both",
149+
**kwargs
150+
):
151+
super().__init__()
152+
self._name = "TreeLayerControl"
153+
kwargs["closed_symbol"] = closed_symbol
154+
kwargs["openened_symbol"] = opened_symbol
155+
kwargs["space_symbol"] = space_symbol
156+
kwargs["selector_back"] = selector_back
157+
kwargs["named_toggle"] = named_toggle
158+
kwargs["collapse_all"] = collapse_all
159+
kwargs["expand_all"] = expand_all
160+
kwargs["label_is_selector"] = label_is_selector
161+
self.options = parse_options(**kwargs)
162+
self.base_tree = base_tree
163+
self.overlay_tree = overlay_tree

0 commit comments

Comments
 (0)