From 4eae936b80fb496819f44f21aa29940d419ac00d Mon Sep 17 00:00:00 2001 From: Hans Then Date: Tue, 12 Mar 2024 16:53:10 +0100 Subject: [PATCH 1/6] Make a base Control class 1. Created a root control class which can load any Leaflet control 2. Made all items that generate a leaflet Control in plugins inherit from Control instead of MacroElement. 3. Still TODO: It would be nice if Control itself inherited from JSCSSMixin. But for that we'd need to revamp loading of css and js. 4. Probably many of the plugins could be rewritten more simply by using features from Control and JsCode instead of using custom templates. 5. map.LayerControl does not inherit from Control due to issues with circular imports. (features import map, so map cannot import features). We'd need to move LayerControl for that to work, but that is breaking change. --- folium/features.py | 57 +++++++++++++++++++++- folium/plugins/draw.py | 5 +- folium/plugins/fullscreen.py | 4 +- folium/plugins/geocoder.py | 4 +- folium/plugins/groupedlayercontrol.py | 4 +- folium/plugins/locate_control.py | 4 +- folium/plugins/measure_control.py | 4 +- folium/plugins/minimap.py | 4 +- folium/plugins/mouse_position.py | 4 +- folium/plugins/realtime.py | 10 ++-- folium/plugins/search.py | 5 +- folium/plugins/tag_filter_button.py | 4 +- folium/plugins/timestamped_geo_json.py | 4 +- folium/plugins/timestamped_wmstilelayer.py | 4 +- 14 files changed, 86 insertions(+), 31 deletions(-) diff --git a/folium/features.py b/folium/features.py index bca5c6d70..f550566e0 100644 --- a/folium/features.py +++ b/folium/features.py @@ -7,7 +7,18 @@ import json import operator import warnings -from typing import Any, Callable, Dict, Iterable, List, Optional, Sequence, Tuple, Union +from typing import ( + Any, + Callable, + Dict, + Iterable, + List, + Literal, + Optional, + Sequence, + Tuple, + Union, +) import numpy as np import requests @@ -20,6 +31,7 @@ from folium.folium import Map from folium.map import FeatureGroup, Icon, Layer, Marker, Popup, Tooltip from folium.utilities import ( + JsCode, TypeJsonValue, TypeLine, TypePathOptions, @@ -1973,3 +1985,46 @@ def __init__( out.setdefault(cm(color), []).append([[lat1, lng1], [lat2, lng2]]) for key, val in out.items(): self.add_child(PolyLine(val, color=key, weight=weight, opacity=opacity)) + + +class Control(MacroElement): + """ + Add a Leaflet Control object to the map + """ + + _template = Template( + """ + {% macro script(this, kwargs) %} + var {{ this.get_name() }}_options = {{ this.options|tojson }}; + {% for key, value in this.functions.items() %} + {{ this.get_name() }}_options["{{key}}"] = {{ value }}; + {% endfor %} + + var {{ this.get_name() }} = new L.Control.{{this._name}}( + {{ this.get_name() }}_options + ).addTo({{ this._parent.get_name() }}); + {% endmacro %} + """ + ) + + def __init__( + self, + control: str = None, + position: Literal[ + "bottomright", "bottomleft", "topright", "topleft" + ] = "bottomright", + **kwargs, + ): + super().__init__() + if control: + self._name = control + kwargs["position"] = position + + # extract JsCode objects + self.functions = {} + for key, value in list(kwargs.items()): + if isinstance(value, JsCode): + self.functions[camelize(key)] = value.js_code + kwargs.pop(key) + + self.options = parse_options(**kwargs) diff --git a/folium/plugins/draw.py b/folium/plugins/draw.py index 95155f8e1..2e8faff05 100644 --- a/folium/plugins/draw.py +++ b/folium/plugins/draw.py @@ -1,10 +1,11 @@ -from branca.element import Element, Figure, MacroElement +from branca.element import Element, Figure from jinja2 import Template from folium.elements import JSCSSMixin +from folium.features import Control -class Draw(JSCSSMixin, MacroElement): +class Draw(JSCSSMixin, Control): """ Vector drawing and editing plugin for Leaflet. diff --git a/folium/plugins/fullscreen.py b/folium/plugins/fullscreen.py index 84ab7e730..3b78a79bb 100644 --- a/folium/plugins/fullscreen.py +++ b/folium/plugins/fullscreen.py @@ -1,11 +1,11 @@ -from branca.element import MacroElement from jinja2 import Template from folium.elements import JSCSSMixin +from folium.features import Control from folium.utilities import parse_options -class Fullscreen(JSCSSMixin, MacroElement): +class Fullscreen(JSCSSMixin, Control): """ Adds a fullscreen button to your map. diff --git a/folium/plugins/geocoder.py b/folium/plugins/geocoder.py index 3d267a05d..a1611aaee 100644 --- a/folium/plugins/geocoder.py +++ b/folium/plugins/geocoder.py @@ -1,13 +1,13 @@ from typing import Optional -from branca.element import MacroElement from jinja2 import Template from folium.elements import JSCSSMixin +from folium.features import Control from folium.utilities import parse_options -class Geocoder(JSCSSMixin, MacroElement): +class Geocoder(JSCSSMixin, Control): """A simple geocoder for Leaflet that by default uses OSM/Nominatim. Please respect the Nominatim usage policy: diff --git a/folium/plugins/groupedlayercontrol.py b/folium/plugins/groupedlayercontrol.py index 6745f8aa4..848ce9238 100644 --- a/folium/plugins/groupedlayercontrol.py +++ b/folium/plugins/groupedlayercontrol.py @@ -1,11 +1,11 @@ -from branca.element import MacroElement from jinja2 import Template from folium.elements import JSCSSMixin +from folium.features import Control from folium.utilities import parse_options -class GroupedLayerControl(JSCSSMixin, MacroElement): +class GroupedLayerControl(JSCSSMixin, Control): """ Create a Layer Control with groups of overlays. diff --git a/folium/plugins/locate_control.py b/folium/plugins/locate_control.py index fb1c825d1..aa02ced53 100644 --- a/folium/plugins/locate_control.py +++ b/folium/plugins/locate_control.py @@ -3,14 +3,14 @@ Based on leaflet plugin: https://github.com/domoritz/leaflet-locatecontrol """ -from branca.element import MacroElement from jinja2 import Template from folium.elements import JSCSSMixin +from folium.features import Control from folium.utilities import parse_options -class LocateControl(JSCSSMixin, MacroElement): +class LocateControl(JSCSSMixin, Control): """Control plugin to geolocate the user. This plugins adds a button to the map, and when it's clicked shows the current diff --git a/folium/plugins/measure_control.py b/folium/plugins/measure_control.py index 4b543dc2f..262af6bd5 100644 --- a/folium/plugins/measure_control.py +++ b/folium/plugins/measure_control.py @@ -1,11 +1,11 @@ -from branca.element import MacroElement from jinja2 import Template from folium.elements import JSCSSMixin +from folium.features import Control from folium.utilities import parse_options -class MeasureControl(JSCSSMixin, MacroElement): +class MeasureControl(JSCSSMixin, Control): """Add a measurement widget on the map. Parameters diff --git a/folium/plugins/minimap.py b/folium/plugins/minimap.py index 999cabd25..21d2c0dbe 100644 --- a/folium/plugins/minimap.py +++ b/folium/plugins/minimap.py @@ -1,12 +1,12 @@ -from branca.element import MacroElement from jinja2 import Template from folium.elements import JSCSSMixin +from folium.features import Control from folium.raster_layers import TileLayer from folium.utilities import parse_options -class MiniMap(JSCSSMixin, MacroElement): +class MiniMap(JSCSSMixin, Control): """Add a minimap (locator) to an existing map. Uses the Leaflet plugin by Norkart under BSD 2-Clause "Simplified" License. diff --git a/folium/plugins/mouse_position.py b/folium/plugins/mouse_position.py index 2c1b1b6f3..afdb60e4a 100644 --- a/folium/plugins/mouse_position.py +++ b/folium/plugins/mouse_position.py @@ -1,11 +1,11 @@ -from branca.element import MacroElement from jinja2 import Template from folium.elements import JSCSSMixin +from folium.features import Control from folium.utilities import parse_options -class MousePosition(JSCSSMixin, MacroElement): +class MousePosition(JSCSSMixin, Control): """Add a field that shows the coordinates of the mouse position. Uses the Leaflet plugin by Ardhi Lukianto under MIT license. diff --git a/folium/plugins/realtime.py b/folium/plugins/realtime.py index d7f99594d..83245b30a 100644 --- a/folium/plugins/realtime.py +++ b/folium/plugins/realtime.py @@ -1,14 +1,14 @@ from typing import Optional, Union -from branca.element import MacroElement from jinja2 import Template from folium.elements import JSCSSMixin -from folium.map import Layer +from folium.features import GeoJson +from folium.map import FeatureGroup from folium.utilities import JsCode, camelize, parse_options -class Realtime(JSCSSMixin, MacroElement): +class Realtime(JSCSSMixin, FeatureGroup): """Put realtime data on a Leaflet map: live tracking GPS units, sensor data or just about anything. @@ -42,7 +42,7 @@ class Realtime(JSCSSMixin, MacroElement): remove_missing: bool, default False Should missing features between updates been automatically removed from the layer - container: Layer, default GeoJson + container: FeatureGroup, default GeoJson The container will typically be a `FeatureGroup`, `MarkerCluster` or `GeoJson`, but it can be anything that generates a javascript L.LayerGroup object, i.e. something that has the methods @@ -109,7 +109,7 @@ def __init__( get_feature_id: Union[JsCode, str, None] = None, update_feature: Union[JsCode, str, None] = None, remove_missing: bool = False, - container: Optional[Layer] = None, + container: Optional[Union[FeatureGroup, GeoJson]] = None, **kwargs ): super().__init__() diff --git a/folium/plugins/search.py b/folium/plugins/search.py index b477456b5..618a74ff4 100644 --- a/folium/plugins/search.py +++ b/folium/plugins/search.py @@ -1,14 +1,13 @@ -from branca.element import MacroElement from jinja2 import Template from folium import Map from folium.elements import JSCSSMixin -from folium.features import FeatureGroup, GeoJson, TopoJson +from folium.features import Control, FeatureGroup, GeoJson, TopoJson from folium.plugins import MarkerCluster from folium.utilities import parse_options -class Search(JSCSSMixin, MacroElement): +class Search(JSCSSMixin, Control): """ Adds a search tool to your map. diff --git a/folium/plugins/tag_filter_button.py b/folium/plugins/tag_filter_button.py index 4fe342a89..b776a1c24 100644 --- a/folium/plugins/tag_filter_button.py +++ b/folium/plugins/tag_filter_button.py @@ -1,11 +1,11 @@ -from branca.element import MacroElement from jinja2 import Template from folium.elements import JSCSSMixin +from folium.features import Control from folium.utilities import parse_options -class TagFilterButton(JSCSSMixin, MacroElement): +class TagFilterButton(JSCSSMixin, Control): """ Creates a Tag Filter Button to filter elements based on criteria (https://github.com/maydemirx/leaflet-tag-filter-button) diff --git a/folium/plugins/timestamped_geo_json.py b/folium/plugins/timestamped_geo_json.py index 3fe66ac04..d99d657fc 100644 --- a/folium/plugins/timestamped_geo_json.py +++ b/folium/plugins/timestamped_geo_json.py @@ -1,14 +1,14 @@ import json -from branca.element import MacroElement from jinja2 import Template from folium.elements import JSCSSMixin +from folium.features import Control from folium.folium import Map from folium.utilities import get_bounds, parse_options -class TimestampedGeoJson(JSCSSMixin, MacroElement): +class TimestampedGeoJson(JSCSSMixin, Control): """ Creates a TimestampedGeoJson plugin from timestamped GeoJSONs to append into a map with Map.add_child. diff --git a/folium/plugins/timestamped_wmstilelayer.py b/folium/plugins/timestamped_wmstilelayer.py index 6f1147638..375b7490b 100644 --- a/folium/plugins/timestamped_wmstilelayer.py +++ b/folium/plugins/timestamped_wmstilelayer.py @@ -1,12 +1,12 @@ -from branca.element import MacroElement from jinja2 import Template from folium.elements import JSCSSMixin +from folium.features import Control from folium.raster_layers import WmsTileLayer from folium.utilities import parse_options -class TimestampedWmsTileLayers(JSCSSMixin, MacroElement): +class TimestampedWmsTileLayers(JSCSSMixin, Control): """ Creates a TimestampedWmsTileLayer that takes a WmsTileLayer and adds time control with the Leaflet.TimeDimension plugin. From feb09306604cf2a83c990246906f17b32d289e5f Mon Sep 17 00:00:00 2001 From: Hans Then Date: Tue, 12 Mar 2024 19:19:56 +0100 Subject: [PATCH 2/6] WIP: add a css parameter to add styling --- folium/features.py | 64 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/folium/features.py b/folium/features.py index f550566e0..216da098c 100644 --- a/folium/features.py +++ b/folium/features.py @@ -1990,6 +1990,18 @@ def __init__( class Control(MacroElement): """ Add a Leaflet Control object to the map + + Examples + -------- + + >>> import folium + >>> from folium.features import Control, Marker + >>> from folium.plugins import Geocoder + + >>> m = folium.Map( + ... location=[46.603354, 1.8883335], attr=None, zoom_control=False, zoom_start=5 + ... ) + >>> Control("Zoom", position="topleft").add_to(m) """ _template = Template( @@ -2028,3 +2040,55 @@ def __init__( kwargs.pop(key) self.options = parse_options(**kwargs) + + +class CustomControl(Control): + """Display static html and switch together with parent layer. + + Examples + -------- + >>> m = folium.Map( + ... location=[46.603354, 1.8883335], zoom_control=False, zoom_start=5 + ... ) + >>> CustomControl("This is my custom control", position="topleft").add_to(m) + """ + + _template = Template( + """ + {% macro script(this, kwargs) %} + + var {{ this.get_name() }} = L.control({ + position: "{{ this.position }}", + }); + {{ this.get_name() }}.onAdd = function(map) { + let div = L.DomUtil.create('div', class_{{this.get_name}}); + div.innerHTML = `{{ this.html }}`; + return div; + } + {{ this.get_name() }}.addTo({{ this.parent_map.get_name() }}); + + {%- if this.switch %} + {{ this._parent.get_name() }}.on('add', function(e) { + {{ this.get_name() }}.addTo({{ this.parent_map.get_name() }}); + }); + {{ this._parent.get_name() }}.on('remove', function(e) { + e.target._map.removeControl({{ this.get_name() }}); + }); + {%- endif %} + + {% endmacro %} + """ + ) + + def __init__(self, html, css=None, position="bottomleft"): + super().__init__() + self._name = "custom_control" + self.html = escape_backticks(html) + self.position = position + self.parent_map = None + self.switch = None + + def render(self, **kwargs): + self.parent_map = get_obj_in_upper_tree(self, Map) + self.switch = isinstance(self._parent, Layer) and self._parent.control + super().render(**kwargs) From d20567ed3edf3aa6ea569451394c5c8f7066f0c1 Mon Sep 17 00:00:00 2001 From: Hans Then Date: Tue, 12 Mar 2024 19:49:14 +0100 Subject: [PATCH 3/6] Add documentation and fix small errors --- folium/features.py | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/folium/features.py b/folium/features.py index 216da098c..b02516c72 100644 --- a/folium/features.py +++ b/folium/features.py @@ -1991,6 +1991,13 @@ class Control(MacroElement): """ Add a Leaflet Control object to the map + Parameters + ---------- + control: str + The javascript class name of the control to be rendered. + position: str + One of "bottomright", "bottomleft", "topright", "topleft" + Examples -------- @@ -2021,10 +2028,10 @@ class Control(MacroElement): def __init__( self, - control: str = None, + control: Optional[str] = None, position: Literal[ "bottomright", "bottomleft", "topright", "topleft" - ] = "bottomright", + ] = "bottomleft", **kwargs, ): super().__init__() @@ -2045,6 +2052,15 @@ def __init__( class CustomControl(Control): """Display static html and switch together with parent layer. + Parameters + ---------- + html: str + The html to be rendered + style: str + The css style to be applied to this element + position: str + One of "bottomright", "bottomleft", "topright", "topleft" + Examples -------- >>> m = folium.Map( @@ -2055,13 +2071,21 @@ class CustomControl(Control): _template = Template( """ + {% macro header(this,kwargs) %} + {%- if this.style %} + + {%- endif %} + {% endmacro %} + {% macro script(this, kwargs) %} var {{ this.get_name() }} = L.control({ position: "{{ this.position }}", }); {{ this.get_name() }}.onAdd = function(map) { - let div = L.DomUtil.create('div', class_{{this.get_name}}); + let div = L.DomUtil.create('div', 'class_{{this.get_name()}}'); div.innerHTML = `{{ this.html }}`; return div; } @@ -2080,9 +2104,10 @@ class CustomControl(Control): """ ) - def __init__(self, html, css=None, position="bottomleft"): + def __init__(self, html, style=None, position="bottomleft"): super().__init__() self._name = "custom_control" + self.style = style self.html = escape_backticks(html) self.position = position self.parent_map = None From d020cc50387380ee0e96e772c216aa31876e7a64 Mon Sep 17 00:00:00 2001 From: Hans Then Date: Sun, 2 Jun 2024 20:11:12 +0200 Subject: [PATCH 4/6] Rewrite mouse_position to demonstrate Control Most of the code of mouse_position has been removed. Instead it simply calls the constructor of Control with the typed parameters. --- folium/plugins/beautify_icon.py | 4 +-- folium/plugins/fullscreen.py | 4 +-- folium/plugins/geocoder.py | 4 +-- folium/plugins/heat_map.py | 4 +-- folium/plugins/marker_cluster.py | 2 +- folium/plugins/measure_control.py | 4 +-- folium/plugins/minimap.py | 4 +-- folium/plugins/mouse_position.py | 54 ++++++++-------------------- folium/plugins/pattern.py | 4 +-- folium/plugins/polyline_text_path.py | 4 +-- folium/plugins/realtime.py | 2 +- folium/plugins/semicircle.py | 2 +- folium/plugins/tag_filter_button.py | 4 +-- folium/plugins/timeline.py | 4 +-- folium/plugins/treelayercontrol.py | 2 +- folium/vector_layers.py | 8 ++--- 16 files changed, 43 insertions(+), 67 deletions(-) diff --git a/folium/plugins/beautify_icon.py b/folium/plugins/beautify_icon.py index d45e9eb58..99a8e1e6c 100644 --- a/folium/plugins/beautify_icon.py +++ b/folium/plugins/beautify_icon.py @@ -95,7 +95,7 @@ def __init__( inner_icon_style="", spin=False, number=None, - **kwargs + **kwargs, ): super().__init__() self._name = "BeautifyIcon" @@ -111,5 +111,5 @@ def __init__( spin=spin, isAlphaNumericIcon=number is not None, text=number, - **kwargs + **kwargs, ) diff --git a/folium/plugins/fullscreen.py b/folium/plugins/fullscreen.py index 3b78a79bb..ce2812254 100644 --- a/folium/plugins/fullscreen.py +++ b/folium/plugins/fullscreen.py @@ -56,7 +56,7 @@ def __init__( title="Full Screen", title_cancel="Exit Full Screen", force_separate_button=False, - **kwargs + **kwargs, ): super().__init__() self._name = "Fullscreen" @@ -65,5 +65,5 @@ def __init__( title=title, title_cancel=title_cancel, force_separate_button=force_separate_button, - **kwargs + **kwargs, ) diff --git a/folium/plugins/geocoder.py b/folium/plugins/geocoder.py index a1611aaee..b950f24f2 100644 --- a/folium/plugins/geocoder.py +++ b/folium/plugins/geocoder.py @@ -78,7 +78,7 @@ def __init__( zoom: Optional[int] = 11, provider: str = "nominatim", provider_options: dict = {}, - **kwargs + **kwargs, ): super().__init__() self._name = "Geocoder" @@ -89,5 +89,5 @@ def __init__( zoom=zoom, provider=provider, provider_options=provider_options, - **kwargs + **kwargs, ) diff --git a/folium/plugins/heat_map.py b/folium/plugins/heat_map.py index cc6a981d5..5d022db30 100644 --- a/folium/plugins/heat_map.py +++ b/folium/plugins/heat_map.py @@ -74,7 +74,7 @@ def __init__( overlay=True, control=True, show=True, - **kwargs + **kwargs, ): super().__init__(name=name, overlay=overlay, control=control, show=show) self._name = "HeatMap" @@ -96,7 +96,7 @@ def __init__( radius=radius, blur=blur, gradient=gradient, - **kwargs + **kwargs, ) def _get_self_bounds(self): diff --git a/folium/plugins/marker_cluster.py b/folium/plugins/marker_cluster.py index b6460e088..a1dcbfe74 100644 --- a/folium/plugins/marker_cluster.py +++ b/folium/plugins/marker_cluster.py @@ -87,7 +87,7 @@ def __init__( show=True, icon_create_function=None, options=None, - **kwargs + **kwargs, ): if options is not None: kwargs.update(options) # options argument is legacy diff --git a/folium/plugins/measure_control.py b/folium/plugins/measure_control.py index 262af6bd5..c28546fa2 100644 --- a/folium/plugins/measure_control.py +++ b/folium/plugins/measure_control.py @@ -68,7 +68,7 @@ def __init__( secondary_length_unit="miles", primary_area_unit="sqmeters", secondary_area_unit="acres", - **kwargs + **kwargs, ): super().__init__() self._name = "MeasureControl" @@ -79,5 +79,5 @@ def __init__( secondary_length_unit=secondary_length_unit, primary_area_unit=primary_area_unit, secondary_area_unit=secondary_area_unit, - **kwargs + **kwargs, ) diff --git a/folium/plugins/minimap.py b/folium/plugins/minimap.py index 21d2c0dbe..a00410af5 100644 --- a/folium/plugins/minimap.py +++ b/folium/plugins/minimap.py @@ -103,7 +103,7 @@ def __init__( toggle_display=False, auto_toggle_display=False, minimized=False, - **kwargs + **kwargs, ): super().__init__() self._name = "MiniMap" @@ -128,5 +128,5 @@ def __init__( toggle_display=toggle_display, auto_toggle_display=auto_toggle_display, minimized=minimized, - **kwargs + **kwargs, ) diff --git a/folium/plugins/mouse_position.py b/folium/plugins/mouse_position.py index afdb60e4a..ef8b8b895 100644 --- a/folium/plugins/mouse_position.py +++ b/folium/plugins/mouse_position.py @@ -1,8 +1,6 @@ -from jinja2 import Template - from folium.elements import JSCSSMixin from folium.features import Control -from folium.utilities import parse_options +from folium.utilities import JsCode class MousePosition(JSCSSMixin, Control): @@ -45,34 +43,6 @@ class MousePosition(JSCSSMixin, Control): """ - _template = Template( - """ - {% macro script(this, kwargs) %} - var {{ this.get_name() }} = new L.Control.MousePosition( - {{ this.options|tojson }} - ); - {{ this.get_name() }}.options["latFormatter"] = - {{ this.lat_formatter }}; - {{ this.get_name() }}.options["lngFormatter"] = - {{ this.lng_formatter }}; - {{ this._parent.get_name() }}.addControl({{ this.get_name() }}); - {% endmacro %} - """ - ) - - default_js = [ - ( - "Control_MousePosition_js", - "https://cdn.jsdelivr.net/gh/ardhi/Leaflet.MousePosition/src/L.Control.MousePosition.min.js", - ) - ] - default_css = [ - ( - "Control_MousePosition_css", - "https://cdn.jsdelivr.net/gh/ardhi/Leaflet.MousePosition/src/L.Control.MousePosition.min.css", - ) - ] - def __init__( self, position="bottomright", @@ -83,19 +53,25 @@ def __init__( prefix="", lat_formatter=None, lng_formatter=None, - **kwargs + **kwargs, ): - super().__init__() - self._name = "MousePosition" - - self.options = parse_options( + super().__init__( + control="MousePosition", position=position, separator=separator, empty_string=empty_string, lng_first=lng_first, num_digits=num_digits, prefix=prefix, - **kwargs + lat_formatter=JsCode(lat_formatter) if lat_formatter else None, + lng_formatter=JsCode(lng_formatter) if lng_formatter else None, + **kwargs, + ) + self.add_js_link( + "Control_MousePosition_js", + "https://cdn.jsdelivr.net/gh/ardhi/Leaflet.MousePosition/src/L.Control.MousePosition.min.js", + ) + self.add_css_link( + "Control_MousePosition_css", + "https://cdn.jsdelivr.net/gh/ardhi/Leaflet.MousePosition/src/L.Control.MousePosition.min.css", ) - self.lat_formatter = lat_formatter or "undefined" - self.lng_formatter = lng_formatter or "undefined" diff --git a/folium/plugins/pattern.py b/folium/plugins/pattern.py index 2f90d32df..d5d139b33 100644 --- a/folium/plugins/pattern.py +++ b/folium/plugins/pattern.py @@ -55,7 +55,7 @@ def __init__( space_color="#ffffff", opacity=0.75, space_opacity=0.0, - **kwargs + **kwargs, ): super().__init__() self._name = "StripePattern" @@ -67,7 +67,7 @@ def __init__( space_color=space_color, opacity=opacity, space_opacity=space_opacity, - **kwargs + **kwargs, ) self.parent_map = None diff --git a/folium/plugins/polyline_text_path.py b/folium/plugins/polyline_text_path.py index 660d15909..826193be2 100644 --- a/folium/plugins/polyline_text_path.py +++ b/folium/plugins/polyline_text_path.py @@ -63,7 +63,7 @@ def __init__( offset=0, orientation=0, attributes=None, - **kwargs + **kwargs, ): super().__init__() self._name = "PolyLineTextPath" @@ -76,5 +76,5 @@ def __init__( offset=offset, orientation=orientation, attributes=attributes, - **kwargs + **kwargs, ) diff --git a/folium/plugins/realtime.py b/folium/plugins/realtime.py index 83245b30a..6405e9e5a 100644 --- a/folium/plugins/realtime.py +++ b/folium/plugins/realtime.py @@ -110,7 +110,7 @@ def __init__( update_feature: Union[JsCode, str, None] = None, remove_missing: bool = False, container: Optional[Union[FeatureGroup, GeoJson]] = None, - **kwargs + **kwargs, ): super().__init__() self._name = "Realtime" diff --git a/folium/plugins/semicircle.py b/folium/plugins/semicircle.py index 6e85b1b1b..9b7edcdd3 100644 --- a/folium/plugins/semicircle.py +++ b/folium/plugins/semicircle.py @@ -68,7 +68,7 @@ def __init__( stop_angle=None, popup=None, tooltip=None, - **kwargs + **kwargs, ): super().__init__(location, popup=popup, tooltip=tooltip) self._name = "SemiCircle" diff --git a/folium/plugins/tag_filter_button.py b/folium/plugins/tag_filter_button.py index b776a1c24..dbd7fcd37 100644 --- a/folium/plugins/tag_filter_button.py +++ b/folium/plugins/tag_filter_button.py @@ -82,7 +82,7 @@ def __init__( clear_text="clear", filter_on_every_click=True, open_popup_on_hover=False, - **kwargs + **kwargs, ): super().__init__() self._name = "TagFilterButton" @@ -92,5 +92,5 @@ def __init__( clear_text=clear_text, filter_on_every_click=filter_on_every_click, open_popup_on_hover=open_popup_on_hover, - **kwargs + **kwargs, ) diff --git a/folium/plugins/timeline.py b/folium/plugins/timeline.py index 77ba81cb2..d9d2fbccd 100644 --- a/folium/plugins/timeline.py +++ b/folium/plugins/timeline.py @@ -112,7 +112,7 @@ def __init__( self, data: Union[dict, str, TextIO], get_interval: Optional[JsCode] = None, - **kwargs + **kwargs, ): super().__init__(data) self._name = "Timeline" @@ -232,7 +232,7 @@ def __init__( show_ticks: bool = True, steps: int = 1000, playback_duration: int = 10000, - **kwargs + **kwargs, ): super().__init__() self._name = "TimelineSlider" diff --git a/folium/plugins/treelayercontrol.py b/folium/plugins/treelayercontrol.py index ff1af6994..59d3c7756 100644 --- a/folium/plugins/treelayercontrol.py +++ b/folium/plugins/treelayercontrol.py @@ -146,7 +146,7 @@ def __init__( collapse_all: str = "", expand_all: str = "", label_is_selector: str = "both", - **kwargs + **kwargs, ): super().__init__() self._name = "TreeLayerControl" diff --git a/folium/vector_layers.py b/folium/vector_layers.py index 9f535467a..be7a1decb 100644 --- a/folium/vector_layers.py +++ b/folium/vector_layers.py @@ -230,7 +230,7 @@ def __init__( locations: TypeMultiLine, popup: Union[Popup, str, None] = None, tooltip: Union[Tooltip, str, None] = None, - **kwargs: TypePathOptions + **kwargs: TypePathOptions, ): super().__init__(locations, popup=popup, tooltip=tooltip) self._name = "Polygon" @@ -272,7 +272,7 @@ def __init__( bounds: TypeLine, popup: Union[Popup, str, None] = None, tooltip: Union[Tooltip, str, None] = None, - **kwargs: TypePathOptions + **kwargs: TypePathOptions, ): super().__init__() self._name = "rectangle" @@ -333,7 +333,7 @@ def __init__( radius: float = 50, popup: Union[Popup, str, None] = None, tooltip: Union[Tooltip, str, None] = None, - **kwargs: TypePathOptions + **kwargs: TypePathOptions, ): super().__init__(location, popup=popup, tooltip=tooltip) self._name = "circle" @@ -379,7 +379,7 @@ def __init__( radius: float = 10, popup: Union[Popup, str, None] = None, tooltip: Union[Tooltip, str, None] = None, - **kwargs: TypePathOptions + **kwargs: TypePathOptions, ): super().__init__(location, popup=popup, tooltip=tooltip) self._name = "CircleMarker" From c5ce38108bff438a8167ee3317d233dc01dbaa7d Mon Sep 17 00:00:00 2001 From: Hans Then Date: Sun, 2 Jun 2024 20:14:53 +0200 Subject: [PATCH 5/6] Removed CustomControl from this PR --- folium/features.py | 70 ---------------------------------------------- 1 file changed, 70 deletions(-) diff --git a/folium/features.py b/folium/features.py index b02516c72..085c45a2f 100644 --- a/folium/features.py +++ b/folium/features.py @@ -2047,73 +2047,3 @@ def __init__( kwargs.pop(key) self.options = parse_options(**kwargs) - - -class CustomControl(Control): - """Display static html and switch together with parent layer. - - Parameters - ---------- - html: str - The html to be rendered - style: str - The css style to be applied to this element - position: str - One of "bottomright", "bottomleft", "topright", "topleft" - - Examples - -------- - >>> m = folium.Map( - ... location=[46.603354, 1.8883335], zoom_control=False, zoom_start=5 - ... ) - >>> CustomControl("This is my custom control", position="topleft").add_to(m) - """ - - _template = Template( - """ - {% macro header(this,kwargs) %} - {%- if this.style %} - - {%- endif %} - {% endmacro %} - - {% macro script(this, kwargs) %} - - var {{ this.get_name() }} = L.control({ - position: "{{ this.position }}", - }); - {{ this.get_name() }}.onAdd = function(map) { - let div = L.DomUtil.create('div', 'class_{{this.get_name()}}'); - div.innerHTML = `{{ this.html }}`; - return div; - } - {{ this.get_name() }}.addTo({{ this.parent_map.get_name() }}); - - {%- if this.switch %} - {{ this._parent.get_name() }}.on('add', function(e) { - {{ this.get_name() }}.addTo({{ this.parent_map.get_name() }}); - }); - {{ this._parent.get_name() }}.on('remove', function(e) { - e.target._map.removeControl({{ this.get_name() }}); - }); - {%- endif %} - - {% endmacro %} - """ - ) - - def __init__(self, html, style=None, position="bottomleft"): - super().__init__() - self._name = "custom_control" - self.style = style - self.html = escape_backticks(html) - self.position = position - self.parent_map = None - self.switch = None - - def render(self, **kwargs): - self.parent_map = get_obj_in_upper_tree(self, Map) - self.switch = isinstance(self._parent, Layer) and self._parent.control - super().render(**kwargs) From 1f550892d19910ed21d10a71f2a0e88db9c5a93e Mon Sep 17 00:00:00 2001 From: Hans Then Date: Sun, 2 Jun 2024 20:19:45 +0200 Subject: [PATCH 6/6] Undo typing changes in realtime --- folium/plugins/realtime.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/folium/plugins/realtime.py b/folium/plugins/realtime.py index 6405e9e5a..69af3c217 100644 --- a/folium/plugins/realtime.py +++ b/folium/plugins/realtime.py @@ -4,11 +4,11 @@ from folium.elements import JSCSSMixin from folium.features import GeoJson -from folium.map import FeatureGroup +from folium.map import Layer from folium.utilities import JsCode, camelize, parse_options -class Realtime(JSCSSMixin, FeatureGroup): +class Realtime(JSCSSMixin, Layer): """Put realtime data on a Leaflet map: live tracking GPS units, sensor data or just about anything. @@ -109,7 +109,7 @@ def __init__( get_feature_id: Union[JsCode, str, None] = None, update_feature: Union[JsCode, str, None] = None, remove_missing: bool = False, - container: Optional[Union[FeatureGroup, GeoJson]] = None, + container: Optional[GeoJson] = None, **kwargs, ): super().__init__()