diff --git a/examples/Features.ipynb b/examples/Features.ipynb
index 1755605469..a0f75880b1 100644
--- a/examples/Features.ipynb
+++ b/examples/Features.ipynb
@@ -30,10 +30,159 @@
{
"data": {
"text/html": [
- "
Make this Notebook Trusted to load map: File -> Trust Notebook
"
+ "Make this Notebook Trusted to load map: File -> Trust Notebook
"
],
"text/plain": [
- ""
+ ""
]
},
"execution_count": 2,
@@ -75,10 +224,107 @@
{
"data": {
"text/html": [
- "Make this Notebook Trusted to load map: File -> Trust Notebook
"
+ "Make this Notebook Trusted to load map: File -> Trust Notebook
"
],
"text/plain": [
- ""
+ ""
]
},
"execution_count": 3,
@@ -114,10 +360,123 @@
{
"data": {
"text/html": [
- "Make this Notebook Trusted to load map: File -> Trust Notebook
"
+ "Make this Notebook Trusted to load map: File -> Trust Notebook
"
],
"text/plain": [
- ""
+ ""
]
},
"execution_count": 4,
@@ -168,17 +527,126 @@
"name": "stderr",
"output_type": "stream",
"text": [
- "/home/filipe/miniconda3/envs/FOLIUM/lib/python3.9/site-packages/altair/utils/deprecation.py:65: AltairDeprecationWarning: load_dataset is deprecated. Use the vega_datasets package instead.\n",
+ "C:\\Users\\frank\\anaconda3\\envs\\folium\\lib\\site-packages\\altair\\utils\\deprecation.py:65: AltairDeprecationWarning: load_dataset is deprecated. Use the vega_datasets package instead.\n",
" warnings.warn(message, AltairDeprecationWarning)\n"
]
},
{
"data": {
"text/html": [
- "Make this Notebook Trusted to load map: File -> Trust Notebook
"
+ "Make this Notebook Trusted to load map: File -> Trust Notebook
"
],
"text/plain": [
- ""
+ ""
]
},
"execution_count": 5,
@@ -237,10 +705,154 @@
{
"data": {
"text/html": [
- "Make this Notebook Trusted to load map: File -> Trust Notebook
"
+ "Make this Notebook Trusted to load map: File -> Trust Notebook
"
],
"text/plain": [
- ""
+ ""
]
},
"execution_count": 6,
@@ -315,10 +927,157 @@
{
"data": {
"text/html": [
- "Make this Notebook Trusted to load map: File -> Trust Notebook
"
+ "Make this Notebook Trusted to load map: File -> Trust Notebook
"
],
"text/plain": [
- ""
+ ""
]
},
"execution_count": 7,
@@ -398,10 +1157,100 @@
{
"data": {
"text/html": [
- "Make this Notebook Trusted to load map: File -> Trust Notebook
"
+ "Make this Notebook Trusted to load map: File -> Trust Notebook
"
],
"text/plain": [
- ""
+ ""
]
},
"execution_count": 8,
@@ -450,10 +1299,134 @@
{
"data": {
"text/html": [
- "Make this Notebook Trusted to load map: File -> Trust Notebook
"
+ "Make this Notebook Trusted to load map: File -> Trust Notebook
"
],
"text/plain": [
- ""
+ ""
]
},
"execution_count": 9,
@@ -498,10 +1471,102 @@
{
"data": {
"text/html": [
- "Make this Notebook Trusted to load map: File -> Trust Notebook
"
+ "Make this Notebook Trusted to load map: File -> Trust Notebook
"
],
"text/plain": [
- ""
+ ""
]
},
"execution_count": 10,
@@ -513,9 +1578,9 @@
"m = folium.Map(tiles=None)\n",
"\n",
"folium.raster_layers.TileLayer(\"OpenStreetMap\").add_to(m)\n",
- "folium.raster_layers.TileLayer(\"stamentoner\").add_to(m)\n",
+ "folium.raster_layers.TileLayer(\"stamentoner\", show=False).add_to(m)\n",
"\n",
- "folium.LayerControl().add_to(m)\n",
+ "folium.LayerControl(collapsed=False).add_to(m)\n",
"\n",
"m"
]
@@ -523,7 +1588,7 @@
],
"metadata": {
"kernelspec": {
- "display_name": "Python 3",
+ "display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
@@ -537,7 +1602,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.0"
+ "version": "3.9.13"
}
},
"nbformat": 4,
diff --git a/folium/features.py b/folium/features.py
index 748b631db9..b4468dfae1 100644
--- a/folium/features.py
+++ b/folium/features.py
@@ -479,7 +479,7 @@ class GeoJson(Layer):
control : bool, default True
Whether the Layer will be included in LayerControls
show: bool, default True
- Whether the layer will be shown on opening (only for overlays).
+ Whether the layer will be shown on opening.
smooth_factor: float, default None
How much to simplify the polyline on each zoom level. More means
better performance and smoother look, and less means more accurate
@@ -608,8 +608,7 @@ class GeoJson(Layer):
function {{ this.get_name() }}_add (data) {
{{ this.get_name() }}
- .addData(data)
- .addTo({{ this._parent.get_name() }});
+ .addData(data);
}
{%- if this.embed %}
{{ this.get_name() }}_add({{ this.data|tojson }});
@@ -890,7 +889,7 @@ class TopoJson(JSCSSMixin, Layer):
control : bool, default True
Whether the Layer will be included in LayerControls.
show: bool, default True
- Whether the layer will be shown on opening (only for overlays).
+ Whether the layer will be shown on opening.
smooth_factor: float, default None
How much to simplify the polyline on each zoom level. More means
better performance and smoother look, and less means more accurate
@@ -1410,7 +1409,7 @@ class Choropleth(FeatureGroup):
control : bool, default True
Whether the Layer will be included in LayerControls.
show: bool, default True
- Whether the layer will be shown on opening (only for overlays).
+ Whether the layer will be shown on opening.
Returns
-------
diff --git a/folium/map.py b/folium/map.py
index ce640c99fb..b091a064f3 100644
--- a/folium/map.py
+++ b/folium/map.py
@@ -14,6 +14,7 @@
TypeJsonValue,
camelize,
escape_backticks,
+ get_and_assert_figure_root,
parse_options,
validate_location,
)
@@ -33,7 +34,7 @@ class Layer(MacroElement):
control : bool, default True
Whether the Layer will be included in LayerControls.
show: bool, default True
- Whether the layer will be shown on opening (only for overlays).
+ Whether the layer will be shown on opening.
"""
def __init__(
@@ -49,6 +50,26 @@ def __init__(
self.control = control
self.show = show
+ def render(self, **kwargs):
+ super().render(**kwargs)
+ if self.show:
+ self._add_layer_to_map()
+
+ def _add_layer_to_map(self, **kwargs):
+ """Show the layer on the map by adding it to its parent in JS."""
+ template = Template(
+ """
+ {%- macro script(this, kwargs) %}
+ {{ this.get_name() }}.addTo({{ this._parent.get_name() }});
+ {%- endmacro %}
+ """
+ )
+ script = template.module.__dict__["script"]
+ figure = get_and_assert_figure_root(self)
+ figure.script.add_child(
+ Element(script(self, kwargs)), name=self.get_name() + "_add"
+ )
+
class FeatureGroup(Layer):
"""
@@ -68,7 +89,7 @@ class FeatureGroup(Layer):
control: bool, default True
Whether the layer will be included in LayerControls.
show: bool, default True
- Whether the layer will be shown on opening (only for overlays).
+ Whether the layer will be shown on opening.
**kwargs
Additional (possibly inherited) options. See
https://leafletjs.com/reference.html#featuregroup
@@ -80,7 +101,7 @@ class FeatureGroup(Layer):
{% macro script(this, kwargs) %}
var {{ this.get_name() }} = L.featureGroup(
{{ this.options|tojson }}
- ).addTo({{ this._parent.get_name() }});
+ );
{% endmacro %}
"""
)
@@ -150,10 +171,6 @@ class LayerControl(MacroElement):
{{ this.options|tojson }}
).addTo({{this._parent.get_name()}});
- {%- for val in this.layers_untoggle.values() %}
- {{ val }}.remove();
- {%- endfor %}
-
{% endmacro %}
"""
)
@@ -172,12 +189,10 @@ def __init__(
)
self.base_layers: OrderedDict[str, str] = OrderedDict()
self.overlays: OrderedDict[str, str] = OrderedDict()
- self.layers_untoggle: OrderedDict[str, str] = OrderedDict()
def reset(self) -> None:
self.base_layers = OrderedDict()
self.overlays = OrderedDict()
- self.layers_untoggle = OrderedDict()
def render(self, **kwargs) -> None:
"""Renders the HTML representation of the element."""
@@ -188,12 +203,8 @@ def render(self, **kwargs) -> None:
key = item.layer_name
if not item.overlay:
self.base_layers[key] = item.get_name()
- if len(self.base_layers) > 1:
- self.layers_untoggle[key] = item.get_name()
else:
self.overlays[key] = item.get_name()
- if not item.show:
- self.layers_untoggle[key] = item.get_name()
super().render()
diff --git a/folium/plugins/fast_marker_cluster.py b/folium/plugins/fast_marker_cluster.py
index 7b5fa3f68e..0a55ab37cc 100644
--- a/folium/plugins/fast_marker_cluster.py
+++ b/folium/plugins/fast_marker_cluster.py
@@ -33,7 +33,7 @@ class FastMarkerCluster(MarkerCluster):
control : bool, default True
Whether the Layer will be included in LayerControls.
show: bool, default True
- Whether the layer will be shown on opening (only for overlays).
+ Whether the layer will be shown on opening.
icon_create_function : string, default None
Override the default behaviour, making possible to customize
markers colors and sizes.
diff --git a/folium/plugins/feature_group_sub_group.py b/folium/plugins/feature_group_sub_group.py
index 994cc96792..ae07e03200 100644
--- a/folium/plugins/feature_group_sub_group.py
+++ b/folium/plugins/feature_group_sub_group.py
@@ -23,7 +23,7 @@ class FeatureGroupSubGroup(JSCSSMixin, Layer):
control : bool, default True
Whether the Layer will be included in LayerControls.
show: bool, default True
- Whether the layer will be shown on opening (only for overlays).
+ Whether the layer will be shown on opening.
Examples
-------
@@ -61,7 +61,6 @@ class FeatureGroupSubGroup(JSCSSMixin, Layer):
var {{ this.get_name() }} = L.featureGroup.subGroup(
{{ this._group.get_name() }}
);
- {{ this.get_name() }}.addTo({{ this._parent.get_name() }});
{% endmacro %}
"""
)
diff --git a/folium/plugins/heat_map.py b/folium/plugins/heat_map.py
index f17c4a0c03..cc6a981d5c 100644
--- a/folium/plugins/heat_map.py
+++ b/folium/plugins/heat_map.py
@@ -41,7 +41,7 @@ class HeatMap(JSCSSMixin, Layer):
control : bool, default True
Whether the Layer will be included in LayerControls.
show: bool, default True
- Whether the layer will be shown on opening (only for overlays).
+ Whether the layer will be shown on opening.
"""
_template = Template(
@@ -50,7 +50,7 @@ class HeatMap(JSCSSMixin, Layer):
var {{ this.get_name() }} = L.heatLayer(
{{ this.data|tojson }},
{{ this.options|tojson }}
- ).addTo({{ this._parent.get_name() }});
+ );
{% endmacro %}
"""
)
diff --git a/folium/plugins/heat_map_withtime.py b/folium/plugins/heat_map_withtime.py
index 876b6139f0..9a556773c3 100644
--- a/folium/plugins/heat_map_withtime.py
+++ b/folium/plugins/heat_map_withtime.py
@@ -55,7 +55,7 @@ class HeatMapWithTime(JSCSSMixin, Layer):
control : bool, default True
Whether the Layer will be included in LayerControls.
show: bool, default True
- Whether the layer will be shown on opening (only for overlays).
+ Whether the layer will be shown on opening.
"""
@@ -102,8 +102,7 @@ class HeatMapWithTime(JSCSSMixin, Layer):
defaultWeight: 1,
{% if this.gradient %}gradient: {{ this.gradient }}{% endif %}
}
- })
- .addTo({{this._parent.get_name()}});
+ });
{% endmacro %}
"""
diff --git a/folium/plugins/marker_cluster.py b/folium/plugins/marker_cluster.py
index 664e0c50ed..b6460e0882 100644
--- a/folium/plugins/marker_cluster.py
+++ b/folium/plugins/marker_cluster.py
@@ -24,7 +24,7 @@ class MarkerCluster(JSCSSMixin, Layer):
control : bool, default True
Whether the Layer will be included in LayerControls.
show: bool, default True
- Whether the layer will be shown on opening (only for overlays).
+ Whether the layer will be shown on opening.
icon_create_function : string, default None
Override the default behaviour, making possible to customize
markers colors and sizes.
@@ -54,7 +54,6 @@ class MarkerCluster(JSCSSMixin, Layer):
{{ this.get_name() }}.options.iconCreateFunction =
{{ this.icon_create_function.strip() }};
{%- endif %}
- {{ this._parent.get_name() }}.addLayer({{ this.get_name() }});
{% endmacro %}
"""
)
diff --git a/folium/plugins/time_slider_choropleth.py b/folium/plugins/time_slider_choropleth.py
index 141920163f..c8ee4ec945 100644
--- a/folium/plugins/time_slider_choropleth.py
+++ b/folium/plugins/time_slider_choropleth.py
@@ -23,7 +23,7 @@ class TimeSliderChoropleth(JSCSSMixin, Layer):
control : bool, default True
Whether the Layer will be included in LayerControls.
show: bool, default True
- Whether the layer will be shown on opening (only for overlays).
+ Whether the layer will be shown on opening.
init_timestamp: int, default 0
Initial time-stamp index on the slider. Must be in the range
`[-L, L-1]`, where `L` is the maximum number of time stamps in
@@ -131,6 +131,10 @@ class TimeSliderChoropleth(JSCSSMixin, Layer):
{{ this._parent.get_name() }}.on('overlayadd', onOverlayAdd);
onOverlayAdd(); // fill map as layer is loaded
+
+ {%- if not this.show %}
+ {{ this.get_name() }}.remove();
+ {%- endif %}
{% endmacro %}
"""
)
diff --git a/folium/plugins/vectorgrid_protobuf.py b/folium/plugins/vectorgrid_protobuf.py
index 78fdbfff94..a26472323b 100644
--- a/folium/plugins/vectorgrid_protobuf.py
+++ b/folium/plugins/vectorgrid_protobuf.py
@@ -104,7 +104,7 @@ class VectorGridProtobuf(JSCSSMixin, Layer):
{{ this.options if this.options is string else this.options|tojson }})
.addTo({{ this._parent.get_name() }});
{% else %}
- {{ this.options }}).addTo({{ this._parent.get_name() }});
+ {{ this.options }});
{% endif %}
{%- endmacro %}
"""
diff --git a/folium/raster_layers.py b/folium/raster_layers.py
index fff7e8fc42..71120b28f3 100644
--- a/folium/raster_layers.py
+++ b/folium/raster_layers.py
@@ -63,7 +63,9 @@ class TileLayer(Layer):
control : bool, default True
Whether the Layer will be included in LayerControls.
show: bool, default True
- Whether the layer will be shown on opening (only for overlays).
+ Whether the layer will be shown on opening.
+ When adding multiple base layers, use this parameter to select which one
+ should be shown when opening the map, by not showing the others.
subdomains: list of strings, default ['abc']
Subdomains of the tile service.
tms: bool, default False
@@ -82,7 +84,7 @@ class TileLayer(Layer):
var {{ this.get_name() }} = L.tileLayer(
{{ this.tiles|tojson }},
{{ this.options|tojson }}
- ).addTo({{ this._parent.get_name() }});
+ );
{% endmacro %}
"""
)
@@ -187,7 +189,7 @@ class WmsTileLayer(Layer):
control : bool, default True
Whether the Layer will be included in LayerControls.
show: bool, default True
- Whether the layer will be shown on opening (only for overlays).
+ Whether the layer will be shown on opening.
**kwargs : additional keyword arguments
Passed through to the underlying tileLayer.wms object and can be used
for setting extra tileLayer.wms parameters or as extra parameters in
@@ -202,7 +204,7 @@ class WmsTileLayer(Layer):
var {{ this.get_name() }} = L.tileLayer.wms(
{{ this.url|tojson }},
{{ this.options|tojson }}
- ).addTo({{ this._parent.get_name() }});
+ );
{% endmacro %}
"""
) # noqa
@@ -279,7 +281,7 @@ class ImageOverlay(Layer):
control : bool, default True
Whether the Layer will be included in LayerControls.
show: bool, default True
- Whether the layer will be shown on opening (only for overlays).
+ Whether the layer will be shown on opening.
See https://leafletjs.com/reference.html#imageoverlay for more
options.
@@ -293,7 +295,7 @@ class ImageOverlay(Layer):
{{ this.url|tojson }},
{{ this.bounds|tojson }},
{{ this.options|tojson }}
- ).addTo({{ this._parent.get_name() }});
+ );
{% endmacro %}
"""
)
@@ -378,7 +380,7 @@ class VideoOverlay(Layer):
control : bool, default True
Whether the Layer will be included in LayerControls.
show: bool, default True
- Whether the layer will be shown on opening (only for overlays).
+ Whether the layer will be shown on opening.
**kwargs:
Other valid (possibly inherited) options. See:
https://leafletjs.com/reference.html#videooverlay
@@ -392,7 +394,7 @@ class VideoOverlay(Layer):
{{ this.video_url|tojson }},
{{ this.bounds|tojson }},
{{ this.options|tojson }}
- ).addTo({{ this._parent.get_name() }});
+ );
{% endmacro %}
"""
)
diff --git a/folium/utilities.py b/folium/utilities.py
index 9b8d6b8763..cc65134f93 100644
--- a/folium/utilities.py
+++ b/folium/utilities.py
@@ -26,7 +26,7 @@
from urllib.parse import urlparse, uses_netloc, uses_params, uses_relative
import numpy as np
-from branca.element import Element
+from branca.element import Element, Figure
# import here for backwards compatibility
from branca.utilities import ( # noqa F401
@@ -499,3 +499,12 @@ def escape_double_quotes(text: str) -> str:
def javascript_identifier_path_to_array_notation(path: str) -> str:
"""Convert a path like obj1.obj2 to array notation: ["obj1"]["obj2"]."""
return "".join(f'["{escape_double_quotes(x)}"]' for x in path.split("."))
+
+
+def get_and_assert_figure_root(obj: Element) -> Figure:
+ """Return the root element of the tree and assert it's a Figure."""
+ figure = obj.get_root()
+ assert isinstance(
+ figure, Figure
+ ), "You cannot render this Element if it is not in a Figure."
+ return figure
diff --git a/tests/plugins/test_feature_group_sub_group.py b/tests/plugins/test_feature_group_sub_group.py
index 61522ee606..6a97ca7f8c 100644
--- a/tests/plugins/test_feature_group_sub_group.py
+++ b/tests/plugins/test_feature_group_sub_group.py
@@ -38,8 +38,11 @@ def test_feature_group_sub_group():
var {{ this.get_name() }} = L.featureGroup.subGroup(
{{ this._group.get_name() }}
);
- {{ this.get_name() }}.addTo({{ this._parent.get_name() }});
"""
)
assert normalize(tmpl.render(this=g1)) in out
assert normalize(tmpl.render(this=g2)) in out
+
+ tmpl = Template("{{ this.get_name() }}.addTo({{ this._parent.get_name() }});")
+ assert normalize(tmpl.render(this=g1)) in out
+ assert normalize(tmpl.render(this=g2)) in out
diff --git a/tests/plugins/test_grouped_layer_control.py b/tests/plugins/test_grouped_layer_control.py
index 0e80a7c029..18166e1739 100644
--- a/tests/plugins/test_grouped_layer_control.py
+++ b/tests/plugins/test_grouped_layer_control.py
@@ -3,7 +3,7 @@
from folium.utilities import normalize
-def test_feature_group_sub_group():
+def test_grouped_layer_control():
m = folium.Map([40.0, 70.0], zoom_start=6)
fg1 = folium.FeatureGroup(name="g1")
fg2 = folium.FeatureGroup(name="g2")
diff --git a/tests/plugins/test_heat_map_withtime.py b/tests/plugins/test_heat_map_withtime.py
index e72e712a12..6d6e7bddde 100644
--- a/tests/plugins/test_heat_map_withtime.py
+++ b/tests/plugins/test_heat_map_withtime.py
@@ -75,8 +75,8 @@ def test_heat_map_with_time():
defaultWeight: 1,
{% if this.gradient %}gradient: {{ this.gradient }}{% endif %}
}
- })
- .addTo({{this._parent.get_name()}});
+ });
+ {{ this.get_name() }}.addTo({{ this._parent.get_name() }});
"""
)
diff --git a/tests/plugins/test_marker_cluster.py b/tests/plugins/test_marker_cluster.py
index 99f8343a20..5eb586ac23 100644
--- a/tests/plugins/test_marker_cluster.py
+++ b/tests/plugins/test_marker_cluster.py
@@ -49,7 +49,6 @@ def test_marker_cluster():
{{ this.get_name() }}.options.iconCreateFunction =
{{ this.icon_create_function.strip() }};
{%- endif %}
- {{this._parent.get_name()}}.addLayer({{this.get_name()}});
{% for marker in this._children.values() %}
var {{marker.get_name()}} = L.marker(
@@ -57,6 +56,8 @@ def test_marker_cluster():
{}
).addTo({{this.get_name()}});
{% endfor %}
+
+ {{ this.get_name() }}.addTo({{ this._parent.get_name() }});
"""
)
expected = normalize(tmpl.render(this=mc))
diff --git a/tests/test_raster_layers.py b/tests/test_raster_layers.py
index 1419b30ccf..d369fa9118 100644
--- a/tests/test_raster_layers.py
+++ b/tests/test_raster_layers.py
@@ -103,7 +103,8 @@ def test_image_overlay():
"{{ this.url }}",
{{ this.bounds }},
{{ this.options }}
- ).addTo({{this._parent.get_name()}});
+ );
+ {{ this.get_name() }}.addTo({{this._parent.get_name()}});
"""
)
assert normalize(tmpl.render(this=io)) in normalize(out)