Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/user_guide/plugins.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Plugins
plugins/pattern
plugins/polygon_encoded
plugins/polyline_encoded
plugins/polyline_decorator
plugins/polyline_offset
plugins/polyline_textpath
plugins/realtime
Expand Down Expand Up @@ -94,6 +95,8 @@ Plugins
- Draw a polygon directly from an encoded string.
* - :doc:`Polyline Encoded <plugins/polyline_encoded>`
- Draw a polyline directly from an encoded string.
* - :doc:`Polyline Decorator <plugins/polyline_decorator>`
- Draw arrowheads, dashes or markers along polylines.
* - :doc:`Polyline Offset <plugins/polyline_offset>`
- Shift relative pixel offset, without actually changing the actual latitude longitude values.
* - :doc:`Polyline Textpath <plugins/polyline_textpath>`
Expand Down
49 changes: 49 additions & 0 deletions docs/user_guide/plugins/polyline_decorator.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
```{code-cell} ipython3
---
nbsphinx: hidden
---
import folium
import folium.plugins
```

## PolyLineDecorator

Draw arrowheads, dashes or markers along a line, for example to show the direction of travel.

```{code-cell} ipython3
m = folium.Map([46.5, 6.6], zoom_start=9)

route = folium.PolyLine(
[[46.2, 6.15], [46.52, 6.63], [46.95, 7.45]], color="#3388ff", weight=4
).add_to(m)

folium.plugins.PolyLineDecorator(
route,
[
{
"offset": 25,
"repeat": 50,
"symbol": {
"type": "arrowHead",
"pixel_size": 12,
"path_options": {"color": "#3388ff", "fill_opacity": 1, "weight": 0},
},
}
],
).add_to(m)

border = folium.PolyLine([[46.0, 6.0], [46.3, 7.2]], color="black", weight=1).add_to(m)

folium.plugins.PolyLineDecorator(
border,
[
{
"offset": 0,
"repeat": 10,
"symbol": {"type": "dash", "pixel_size": 5, "path_options": {"color": "red"}},
}
],
).add_to(m)

m
```
2 changes: 2 additions & 0 deletions folium/plugins/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from folium.plugins.mouse_position import MousePosition
from folium.plugins.overlapping_marker_spiderfier import OverlappingMarkerSpiderfier
from folium.plugins.pattern import CirclePattern, StripePattern
from folium.plugins.polyline_decorator import PolyLineDecorator
from folium.plugins.polyline_offset import PolyLineOffset
from folium.plugins.polyline_text_path import PolyLineTextPath
from folium.plugins.realtime import Realtime
Expand Down Expand Up @@ -69,6 +70,7 @@
"PolygonFromEncoded",
"PolyLineFromEncoded",
"PolyLineTextPath",
"PolyLineDecorator",
"PolyLineOffset",
"Realtime",
"ScrollZoomToggler",
Expand Down
63 changes: 63 additions & 0 deletions folium/plugins/polyline_decorator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
from folium.elements import JSCSSMixin
from folium.features import MacroElement
from folium.template import Template, tojavascript
from folium.utilities import JsCode


class PolyLineDecorator(JSCSSMixin, MacroElement):
"""
Draws patterns of symbols, such as arrowheads or dashes, along a line.

Parameters
----------
polyline: folium.PolyLine or folium.Polygon
The line to decorate.
patterns: list of dict
Each pattern has a ``symbol`` and optionally ``offset``, ``end_offset``
and ``repeat``, in pixels or as a percentage string such as "10%".
The ``symbol`` is a dict with a ``type`` of "arrowHead", "dash" or
"marker", and the options of that symbol, for example
``{"type": "arrowHead", "pixel_size": 15, "path_options": {"color": "red"}}``.

See https://github.com/bbecquet/Leaflet.PolylineDecorator for the options.

"""

_template = Template("""
{% macro script(this, kwargs) %}
var {{ this.get_name() }} = L.polylineDecorator(
{{ this.polyline.get_name() }},
{{ this.options|tojavascript }}
).addTo({{ this._parent.get_name() }});
{% endmacro %}
""")

default_js = [
(
"polylinedecorator",
"https://cdn.jsdelivr.net/npm/leaflet-polylinedecorator@1.6.0/dist/leaflet.polylineDecorator.min.js",
)
]

symbol_types = {"arrowHead", "dash", "marker"}

def __init__(self, polyline, patterns, **kwargs):
super().__init__()
self._name = "PolyLineDecorator"
self.polyline = polyline
self.options = {
"patterns": [self._to_pattern(pattern) for pattern in patterns],
**kwargs,
}

def _to_pattern(self, pattern):
pattern = dict(pattern)
symbol = dict(pattern["symbol"])
symbol_type = symbol.pop("type")
if symbol_type not in self.symbol_types:
raise ValueError(
f"symbol type should be one of {sorted(self.symbol_types)}, "
f"got {symbol_type!r}"
)
pattern["symbol"] = JsCode(f"L.Symbol.{symbol_type}({tojavascript(symbol)})")
return pattern
53 changes: 53 additions & 0 deletions tests/plugins/test_polyline_decorator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
"""
Test PolyLineDecorator
----------------------
"""

import pytest

import folium
from folium import plugins
from folium.utilities import normalize


def test_polyline_decorator():
m = folium.Map([46.5, 6.6], zoom_start=9)
line = folium.PolyLine([[46.2, 6.15], [46.52, 6.63]]).add_to(m)
decorator = plugins.PolyLineDecorator(
line,
[
{
"offset": "100%",
"repeat": 0,
"symbol": {"type": "arrowHead", "pixel_size": 15},
}
],
).add_to(m)

out = normalize(m._parent.render())

script = '<script src="https://cdn.jsdelivr.net/npm/leaflet-polylinedecorator@1.6.0/dist/leaflet.polylineDecorator.min.js"></script>' # noqa
assert script in out
expected = normalize(f"""
var {decorator.get_name()} = L.polylineDecorator(
{line.get_name()},
{{
"patterns": [
{{
"offset": "100%",
"repeat": 0,
"symbol": L.Symbol.arrowHead({{
"pixelSize": 15,
}}),
}},
],
}}
).addTo({m.get_name()});
""")
assert expected in out


def test_polyline_decorator_unknown_symbol():
line = folium.PolyLine([[0, 0], [1, 1]])
with pytest.raises(ValueError):
plugins.PolyLineDecorator(line, [{"symbol": {"type": "arrow"}}])
Loading