""" Wraps leaflet Polyline, Polygon, Rectangle, Circle, and CircleMarker """ from branca.element import MacroElement from jinja2 import Template from folium.map import Marker, Popup, Tooltip from folium.utilities import camelize, get_bounds, validate_locations def path_options(line=False, radius=False, **kwargs): """ Contains options and constants shared between vector overlays (Polygon, Polyline, Circle, CircleMarker, and Rectangle). Parameters ---------- stroke: Bool, True Whether to draw stroke along the path. Set it to false to disable borders on polygons or circles. color: str, '#3388ff' Stroke color. weight: int, 3 Stroke width in pixels. opacity: float, 1.0 Stroke opacity. line_cap: str, 'round' (lineCap) A string that defines shape to be used at the end of the stroke. https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-linecap line_join: str, 'round' (lineJoin) A string that defines shape to be used at the corners of the stroke. https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-linejoin dash_array: str, None (dashArray) A string that defines the stroke dash pattern. Doesn't work on Canvas-powered layers in some old browsers. https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-dasharray dash_offset:, str, None (dashOffset) A string that defines the distance into the dash pattern to start the dash. Doesn't work on Canvas-powered layers in some old browsers. https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-dashoffset fill: Bool, False Whether to fill the path with color. Set it to false to disable filling on polygons or circles. fill_color: str, default to `color` (fillColor) Fill color. Defaults to the value of the color option. fill_opacity: float, 0.2 (fillOpacity) Fill opacity. fill_rule: str, 'evenodd' (fillRule) A string that defines how the inside of a shape is determined. https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/fill-rule bubbling_mouse_events: Bool, True (bubblingMouseEvents) When true a mouse event on this path will trigger the same event on the map (unless L.DomEvent.stopPropagation is used). gradient: bool, default None When a gradient on the stroke and fill is available, allows turning it on or off. Note that the presence of `fill_color` will override `fill=False`. This function accepts both snake_case and lowerCamelCase equivalents. See https://leafletjs.com/reference.html#path """ kwargs = {camelize(key): value for key, value in kwargs.items()} extra_options = {} if line: extra_options = { "smoothFactor": kwargs.pop("smoothFactor", 1.0), "noClip": kwargs.pop("noClip", False), } if radius: extra_options.update({"radius": radius}) color = kwargs.pop("color", "#3388ff") fill_color = kwargs.pop("fillColor", False) if fill_color: fill = True elif not fill_color: fill_color = color fill = kwargs.pop("fill", False) gradient = kwargs.pop("gradient", None) if gradient is not None: extra_options.update({"gradient": gradient}) default = { "stroke": kwargs.pop("stroke", True), "color": color, "weight": kwargs.pop("weight", 3), "opacity": kwargs.pop("opacity", 1.0), "lineCap": kwargs.pop("lineCap", "round"), "lineJoin": kwargs.pop("lineJoin", "round"), "dashArray": kwargs.pop("dashArray", None), "dashOffset": kwargs.pop("dashOffset", None), "fill": fill, "fillColor": fill_color, "fillOpacity": kwargs.pop("fillOpacity", 0.2), "fillRule": kwargs.pop("fillRule", "evenodd"), "bubblingMouseEvents": kwargs.pop("bubblingMouseEvents", True), } default.update(extra_options) return default class BaseMultiLocation(MacroElement): """Base class for vector classes with multiple coordinates. Not for direct consumption """ def __init__(self, locations, popup=None, tooltip=None): super().__init__() self.locations = validate_locations(locations) if popup is not None: self.add_child(popup if isinstance(popup, Popup) else Popup(str(popup))) if tooltip is not None: self.add_child( tooltip if isinstance(tooltip, Tooltip) else Tooltip(str(tooltip)) ) def _get_self_bounds(self): """Compute the bounds of the object itself.""" return get_bounds(self.locations) class PolyLine(BaseMultiLocation): """Draw polyline overlays on a map. See :func:`folium.vector_layers.path_options` for the `Path` options. Parameters ---------- locations: list of points (latitude, longitude) Latitude and Longitude of line (Northing, Easting) popup: str or folium.Popup, default None Input text or visualization for object displayed when clicking. tooltip: str or folium.Tooltip, default None Display a text when hovering over the object. smooth_factor: float, default 1.0 How much to simplify the polyline on each zoom level. More means better performance and smoother look, and less means more accurate representation. no_clip: Bool, default False Disable polyline clipping. **kwargs Other valid (possibly inherited) options. See: https://leafletjs.com/reference.html#polyline """ _template = Template( """ {% macro script(this, kwargs) %} var {{ this.get_name() }} = L.polyline( {{ this.locations|tojson }}, {{ this.options|tojson }} ).addTo({{this._parent.get_name()}}); {% endmacro %} """ ) def __init__(self, locations, popup=None, tooltip=None, **kwargs): super().__init__(locations, popup=popup, tooltip=tooltip) self._name = "PolyLine" self.options = path_options(line=True, **kwargs) class Polygon(BaseMultiLocation): """Draw polygon overlays on a map. See :func:`folium.vector_layers.path_options` for the `Path` options. Parameters ---------- locations: list of points (latitude, longitude) Latitude and Longitude of line (Northing, Easting) popup: string or folium.Popup, default None Input text or visualization for object displayed when clicking. tooltip: str or folium.Tooltip, default None Display a text when hovering over the object. **kwargs Other valid (possibly inherited) options. See: https://leafletjs.com/reference.html#polygon """ _template = Template( """ {% macro script(this, kwargs) %} var {{ this.get_name() }} = L.polygon( {{ this.locations|tojson }}, {{ this.options|tojson }} ).addTo({{this._parent.get_name()}}); {% endmacro %} """ ) def __init__(self, locations, popup=None, tooltip=None, **kwargs): super().__init__(locations, popup=popup, tooltip=tooltip) self._name = "Polygon" self.options = path_options(line=True, **kwargs) class Rectangle(BaseMultiLocation): """Draw rectangle overlays on a map. See :func:`folium.vector_layers.path_options` for the `Path` options. Parameters ---------- bounds: list of points (latitude, longitude) Latitude and Longitude of line (Northing, Easting) popup: string or folium.Popup, default None Input text or visualization for object displayed when clicking. tooltip: str or folium.Tooltip, default None Display a text when hovering over the object. **kwargs Other valid (possibly inherited) options. See: https://leafletjs.com/reference.html#rectangle """ _template = Template( """ {% macro script(this, kwargs) %} var {{this.get_name()}} = L.rectangle( {{ this.locations|tojson }}, {{ this.options|tojson }} ).addTo({{this._parent.get_name()}}); {% endmacro %} """ ) def __init__(self, bounds, popup=None, tooltip=None, **kwargs): super().__init__(bounds, popup=popup, tooltip=tooltip) self._name = "rectangle" self.options = path_options(line=True, **kwargs) class Circle(Marker): """ Class for drawing circle overlays on a map. It's an approximation and starts to diverge from a real circle closer to the poles (due to projection distortion). See :func:`folium.vector_layers.path_options` for the `Path` options. Parameters ---------- location: tuple[float, float] Latitude and Longitude pair (Northing, Easting) popup: string or folium.Popup, default None Input text or visualization for object displayed when clicking. tooltip: str or folium.Tooltip, default None Display a text when hovering over the object. radius: float Radius of the circle, in meters. **kwargs Other valid (possibly inherited) options. See: https://leafletjs.com/reference.html#circle """ _template = Template( """ {% macro script(this, kwargs) %} var {{ this.get_name() }} = L.circle( {{ this.location|tojson }}, {{ this.options|tojson }} ).addTo({{ this._parent.get_name() }}); {% endmacro %} """ ) def __init__(self, location=None, radius=50, popup=None, tooltip=None, **kwargs): super().__init__(location, popup=popup, tooltip=tooltip) self._name = "circle" self.options = path_options(line=False, radius=radius, **kwargs) class CircleMarker(Marker): """ A circle of a fixed size with radius specified in pixels. See :func:`folium.vector_layers.path_options` for the `Path` options. Parameters ---------- location: tuple[float, float] Latitude and Longitude pair (Northing, Easting) popup: string or folium.Popup, default None Input text or visualization for object displayed when clicking. tooltip: str or folium.Tooltip, default None Display a text when hovering over the object. radius: float, default 10 Radius of the circle marker, in pixels. **kwargs Other valid (possibly inherited) options. See: https://leafletjs.com/reference.html#circlemarker """ _template = Template( """ {% macro script(this, kwargs) %} var {{ this.get_name() }} = L.circleMarker( {{ this.location|tojson }}, {{ this.options|tojson }} ).addTo({{ this._parent.get_name() }}); {% endmacro %} """ ) def __init__(self, location=None, radius=10, popup=None, tooltip=None, **kwargs): super().__init__(location, popup=popup, tooltip=tooltip) self._name = "CircleMarker" self.options = path_options(line=False, radius=radius, **kwargs)