diff --git a/examples/PolyLineTextPath_AntPath.ipynb b/examples/PolyLineTextPath_AntPath.ipynb
new file mode 100644
index 000000000..2894c52e8
--- /dev/null
+++ b/examples/PolyLineTextPath_AntPath.ipynb
@@ -0,0 +1,218 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "0.6.0+69.ga61365c.dirty\n"
+ ]
+ }
+ ],
+ "source": [
+ "import os\n",
+ "import folium\n",
+ "\n",
+ "print(folium.__version__)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# PolylineTextPath plugin"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "
"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 2,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "from folium import plugins\n",
+ "\n",
+ "m = folium.Map([30, 0], zoom_start=3)\n",
+ "\n",
+ "wind_locations = [\n",
+ " [59.35560, -31.992190],\n",
+ " [55.178870, -42.89062],\n",
+ " [47.754100, -43.94531],\n",
+ " [38.272690, -37.96875],\n",
+ " [27.059130, -41.13281],\n",
+ " [16.299050, -36.56250],\n",
+ " [8.4071700, -30.23437],\n",
+ " [1.0546300, -22.50000],\n",
+ " [-8.754790, -18.28125],\n",
+ " [-21.61658, -20.03906],\n",
+ " [-31.35364, -24.25781],\n",
+ " [-39.90974, -30.93750],\n",
+ " [-43.83453, -41.13281],\n",
+ " [-47.75410, -49.92187],\n",
+ " [-50.95843, -54.14062],\n",
+ " [-55.97380, -56.60156]\n",
+ "]\n",
+ "\n",
+ "wind_line = folium.PolyLine(\n",
+ " wind_locations,\n",
+ " weight=15,\n",
+ " color='#8EE9FF'\n",
+ ").add_to(m)\n",
+ "\n",
+ "attr = {'fill': '#007DEF', 'font-weight': 'bold', 'font-size': '24'}\n",
+ "\n",
+ "plugins.PolyLineTextPath(\n",
+ " wind_line,\n",
+ " ') ',\n",
+ " repeat=True,\n",
+ " offset=7,\n",
+ " attributes=attr\n",
+ ").add_to(m)\n",
+ "\n",
+ "danger_line = folium.PolyLine(\n",
+ " [[-40.311, -31.952],\n",
+ " [-12.086, -18.727]],\n",
+ " weight=10,\n",
+ " color='orange',\n",
+ " opacity=0.8\n",
+ ").add_to(m)\n",
+ "\n",
+ "attr = {'fill': 'red'}\n",
+ "\n",
+ "plugins.PolyLineTextPath(\n",
+ " danger_line,\n",
+ " '\\u25BA',\n",
+ " repeat=True,\n",
+ " offset=6,\n",
+ " attributes=attr\n",
+ ").add_to(m)\n",
+ "\n",
+ "plane_line = folium.PolyLine(\n",
+ " [[-49.38237, -37.26562],\n",
+ " [-1.75754, -14.41406],\n",
+ " [51.61802, -23.20312]],\n",
+ " weight=1,\n",
+ " color='black'\n",
+ ").add_to(m)\n",
+ "\n",
+ "attr = {'font-weight': 'bold', 'font-size': '24'}\n",
+ "\n",
+ "plugins.PolyLineTextPath(\n",
+ " plane_line,\n",
+ " '\\u2708 ',\n",
+ " repeat=True,\n",
+ " offset=8,\n",
+ " attributes=attr\n",
+ ").add_to(m)\n",
+ "\n",
+ "\n",
+ "line_to_new_delhi = folium.PolyLine(\n",
+ " [[46.67959447, 3.33984375],\n",
+ " [46.5588603, 29.53125],\n",
+ " [42.29356419, 51.328125],\n",
+ " [35.74651226, 68.5546875],\n",
+ " [28.65203063, 76.81640625]]\n",
+ ").add_to(m)\n",
+ "\n",
+ "\n",
+ "line_to_hanoi = folium.PolyLine(\n",
+ " [[28.76765911, 77.60742188],\n",
+ " [27.83907609, 88.72558594],\n",
+ " [25.68113734, 97.3828125],\n",
+ " [21.24842224, 105.77636719]]\n",
+ ").add_to(m)\n",
+ "\n",
+ "\n",
+ "plugins.PolyLineTextPath(\n",
+ " line_to_new_delhi,\n",
+ " 'To New Delhi',\n",
+ " offset=-5\n",
+ ").add_to(m)\n",
+ "\n",
+ "\n",
+ "plugins.PolyLineTextPath(\n",
+ " line_to_hanoi,\n",
+ " 'To Hanoi',\n",
+ " offset=-5\n",
+ ").add_to(m)\n",
+ "\n",
+ "m.save(os.path.join('results', 'PolyLineTextPath.html'))\n",
+ "\n",
+ "m"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ ""
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 3,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "m = folium.Map()\n",
+ "\n",
+ "folium.plugins.AntPath(\n",
+ " locations=wind_locations,\n",
+ " reverse='True',\n",
+ " dash_array=[20, 30]\n",
+ ").add_to(m)\n",
+ "\n",
+ "m.fit_bounds(m.get_bounds())\n",
+ "\n",
+ "m.save(os.path.join('results', 'AntPath.html'))\n",
+ "\n",
+ "m"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.7.0"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 1
+}
diff --git a/examples/Polyline_text_path.ipynb b/examples/Polyline_text_path.ipynb
deleted file mode 100644
index 79160adc6..000000000
--- a/examples/Polyline_text_path.ipynb
+++ /dev/null
@@ -1,183 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": 1,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "0.5.0+27.g2d457b0.dirty\n"
- ]
- }
- ],
- "source": [
- "import os\n",
- "import folium\n",
- "\n",
- "print(folium.__version__)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Demostrate PolylineTextPath plugin"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/html": [
- ""
- ],
- "text/plain": [
- ""
- ]
- },
- "execution_count": 2,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "from folium import plugins\n",
- "\n",
- "m = folium.Map([30, 0], zoom_start=3)\n",
- "\n",
- "wind_locations = [\n",
- " [59.35560, -31.992190],\n",
- " [55.178870, -42.89062],\n",
- " [47.754100, -43.94531],\n",
- " [38.272690, -37.96875],\n",
- " [27.059130, -41.13281],\n",
- " [16.299050, -36.56250],\n",
- " [8.4071700, -30.23437],\n",
- " [1.0546300, -22.50000],\n",
- " [-8.754790, -18.28125],\n",
- " [-21.61658, -20.03906],\n",
- " [-31.35364, -24.25781],\n",
- " [-39.90974, -30.93750],\n",
- " [-43.83453, -41.13281],\n",
- " [-47.75410, -49.92187],\n",
- " [-50.95843, -54.14062],\n",
- " [-55.97380, -56.60156]\n",
- "]\n",
- "\n",
- "wind_line = folium.PolyLine(\n",
- " wind_locations,\n",
- " weight=15,\n",
- " color='#8EE9FF'\n",
- ").add_to(m)\n",
- "\n",
- "attr = {'fill': '#007DEF', 'font-weight': 'bold', 'font-size': '24'}\n",
- "\n",
- "plugins.PolyLineTextPath(\n",
- " wind_line,\n",
- " ') ',\n",
- " repeat=True,\n",
- " offset=7,\n",
- " attributes=attr\n",
- ").add_to(m)\n",
- "\n",
- "danger_line = folium.PolyLine(\n",
- " [[-40.311, -31.952],\n",
- " [-12.086, -18.727]],\n",
- " weight=10,\n",
- " color='orange',\n",
- " opacity=0.8\n",
- ").add_to(m)\n",
- "\n",
- "attr = {'fill': 'red'}\n",
- "\n",
- "plugins.PolyLineTextPath(\n",
- " danger_line,\n",
- " '\\u25BA',\n",
- " repeat=True,\n",
- " offset=6,\n",
- " attributes=attr\n",
- ").add_to(m)\n",
- "\n",
- "plane_line = folium.PolyLine(\n",
- " [[-49.38237, -37.26562],\n",
- " [-1.75754, -14.41406],\n",
- " [51.61802, -23.20312]],\n",
- " weight=1,\n",
- " color='black'\n",
- ").add_to(m)\n",
- "\n",
- "attr = {'font-weight': 'bold', 'font-size': '24'}\n",
- "\n",
- "plugins.PolyLineTextPath(\n",
- " plane_line,\n",
- " '\\u2708 ',\n",
- " repeat=True,\n",
- " offset=8,\n",
- " attributes=attr\n",
- ").add_to(m)\n",
- "\n",
- "\n",
- "line_to_new_delhi = folium.PolyLine(\n",
- " [[46.67959447, 3.33984375],\n",
- " [46.5588603, 29.53125],\n",
- " [42.29356419, 51.328125],\n",
- " [35.74651226, 68.5546875],\n",
- " [28.65203063, 76.81640625]]\n",
- ").add_to(m)\n",
- "\n",
- "\n",
- "line_to_hanoi = folium.PolyLine(\n",
- " [[28.76765911, 77.60742188],\n",
- " [27.83907609, 88.72558594],\n",
- " [25.68113734, 97.3828125],\n",
- " [21.24842224, 105.77636719]]\n",
- ").add_to(m)\n",
- "\n",
- "\n",
- "plugins.PolyLineTextPath(\n",
- " line_to_new_delhi,\n",
- " 'To New Delhi',\n",
- " offset=-5\n",
- ").add_to(m)\n",
- "\n",
- "\n",
- "plugins.PolyLineTextPath(\n",
- " line_to_hanoi,\n",
- " 'To Hanoi',\n",
- " offset=-5\n",
- ").add_to(m)\n",
- "\n",
- "m.save(os.path.join('results', 'Polyline_text_path.html'))\n",
- "\n",
- "m"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.6.2"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 1
-}
diff --git a/folium/plugins/__init__.py b/folium/plugins/__init__.py
index df574d0c2..c018754f5 100644
--- a/folium/plugins/__init__.py
+++ b/folium/plugins/__init__.py
@@ -10,6 +10,7 @@
from __future__ import (absolute_import, division, print_function)
+from folium.plugins.antpath import AntPath
from folium.plugins.beautify_icon import BeautifyIcon
from folium.plugins.boat_marker import BoatMarker
from folium.plugins.draw import Draw
@@ -32,6 +33,7 @@
from folium.plugins.timestamped_wmstilelayer import TimestampedWmsTileLayers
__all__ = [
+ 'AntPath',
'BeautifyIcon',
'BoatMarker',
'Draw',
diff --git a/folium/plugins/antpath.py b/folium/plugins/antpath.py
new file mode 100644
index 000000000..4f584fe86
--- /dev/null
+++ b/folium/plugins/antpath.py
@@ -0,0 +1,79 @@
+# -*- coding: utf-8 -*-
+
+from __future__ import (absolute_import, division, print_function)
+
+import json
+
+from branca.element import Figure, JavascriptLink
+
+from folium import Marker
+from folium.vector_layers import path_options
+
+from jinja2 import Template
+
+
+class AntPath(Marker):
+ """
+ Class for drawing AntPath 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, optional
+ Display a text when hovering over the object.
+ **kwargs:
+ Polyline and AntPath options. See their Github page for the
+ available parameters.
+
+ https://github.com/rubenspgcavalcante/leaflet-ant-path/
+
+ """
+ _template = Template(u"""
+ {% macro script(this, kwargs) %}
+ {{this.get_name()}} = L.polyline.antPath(
+ {{this.location}},
+ {{ this.options }}
+ )
+ .addTo({{this._parent.get_name()}});
+ {% endmacro %}
+ """) # noqa
+
+ def __init__(self, locations, popup=None, tooltip=None, **kwargs):
+ super(AntPath, self).__init__(
+ location=locations,
+ popup=popup,
+ tooltip=tooltip,
+ )
+
+ self._name = 'AntPath'
+ # Polyline + AntPath defaults.
+ options = path_options(line=True, **kwargs)
+ options.update({
+ 'paused': kwargs.pop('paused', False),
+ 'reverse': kwargs.pop('reverse', False),
+ 'hardwareAcceleration': kwargs.pop('hardware_acceleration', False),
+ 'delay': kwargs.pop('delay', 400),
+ 'dashArray': kwargs.pop('dash_array', [10, 20]),
+ 'weight': kwargs.pop('weight', 5),
+ 'opacity': kwargs.pop('opacity', 0.5),
+ 'color': kwargs.pop('color', '#0000FF'),
+ 'pulseColor': kwargs.pop('pulse_color', '#FFFFFF'),
+ })
+ self.options = json.dumps(options, sort_keys=True, indent=2)
+
+ def render(self, **kwargs):
+ super(AntPath, self).render()
+
+ figure = self.get_root()
+ assert isinstance(figure, Figure), ('You cannot render this Element '
+ 'if it is not in a Figure.')
+
+ figure.header.add_child(
+ JavascriptLink('https://cdn.jsdelivr.net/npm/leaflet-ant-path@1.1.2/dist/leaflet-ant-path.min.js'), # noqa
+ name='antpath',
+ )
diff --git a/folium/utilities.py b/folium/utilities.py
index 971f26816..7d4f947b3 100644
--- a/folium/utilities.py
+++ b/folium/utilities.py
@@ -389,6 +389,20 @@ def iter_points(x):
return []
+def compare_rendered(obj1, obj2):
+ """
+ Return True/False if the normalized rendered version of
+ two folium map objects are the equal or not.
+
+ """
+ return _normalize(obj1) == _normalize(obj2)
+
+
+def _normalize(rendered):
+ """Return the input string as a list of stripped lines."""
+ return [line.strip() for line in rendered.splitlines() if line.strip()]
+
+
@contextmanager
def _tmp_html(data):
"""Yields the path of a temporary HTML file containing data."""
diff --git a/folium/vector_layers.py b/folium/vector_layers.py
index d9d093b6c..89e126b92 100644
--- a/folium/vector_layers.py
+++ b/folium/vector_layers.py
@@ -16,7 +16,7 @@
from jinja2 import Template
-def path_options(**kwargs):
+def path_options(line=False, radius=False, **kwargs):
"""
Contains options and constants shared between vector overlays
(Polygon, Polyline, Circle, CircleMarker, and Rectangle).
@@ -66,27 +66,15 @@ def path_options(**kwargs):
http://leafletjs.com/reference-1.2.0.html#path
"""
- valid_options = (
- 'bubbling_mouse_events',
- 'color',
- 'dash_array',
- 'dash_offset',
- 'fill',
- 'fill_color',
- 'fill_opacity',
- 'fill_rule',
- 'line_cap',
- 'line_join',
- 'opacity',
- 'stroke',
- 'weight',
- )
- non_valid = [key for key in kwargs.keys() if key not in valid_options]
- if non_valid:
- raise ValueError(
- '{non_valid} are not valid options, '
- 'expected {valid_options}'.format(non_valid=non_valid, valid_options=valid_options)
- )
+
+ extra_options = {}
+ if line:
+ extra_options = {
+ 'smoothFactor': kwargs.pop('smooth_factor', 1.0),
+ 'noClip': kwargs.pop('no_clip', False),
+ }
+ if radius:
+ extra_options.update({'radius': radius})
color = kwargs.pop('color', '#3388ff')
fill_color = kwargs.pop('fill_color', False)
@@ -96,7 +84,7 @@ def path_options(**kwargs):
fill_color = color
fill = kwargs.pop('fill', False)
- return {
+ default = {
'stroke': kwargs.pop('stroke', True),
'color': color,
'weight': kwargs.pop('weight', 3),
@@ -111,19 +99,12 @@ def path_options(**kwargs):
'fillRule': kwargs.pop('fill_rule', 'evenodd'),
'bubblingMouseEvents': kwargs.pop('bubbling_mouse_events', True),
}
+ default.update(extra_options)
+ return default
def _parse_options(line=False, radius=False, **kwargs):
- extra_options = {}
- if line:
- extra_options = {
- 'smoothFactor': kwargs.pop('smooth_factor', 1.0),
- 'noClip': kwargs.pop('no_clip', False),
- }
- if radius:
- extra_options.update({'radius': radius})
- options = path_options(**kwargs)
- options.update(extra_options)
+ options = path_options(line=line, radius=radius, **kwargs)
return json.dumps(options, sort_keys=True, indent=2)
diff --git a/tests/plugins/test_antpath.py b/tests/plugins/test_antpath.py
new file mode 100644
index 000000000..60c3da769
--- /dev/null
+++ b/tests/plugins/test_antpath.py
@@ -0,0 +1,59 @@
+# -*- coding: utf-8 -*-
+
+"""
+Test AntPath
+-------------
+"""
+
+from __future__ import (absolute_import, division, print_function)
+
+import folium
+from folium import plugins
+
+from jinja2 import Template
+
+
+def test_antpath():
+ m = folium.Map([20., 0.], zoom_start=3)
+
+ locations = [
+ [59.355600, -31.99219],
+ [55.178870, -42.89062],
+ [47.754100, -43.94531],
+ [38.272690, -37.96875],
+ [27.059130, -41.13281],
+ [16.299050, -36.56250],
+ [8.4071700, -30.23437],
+ [1.0546300, -22.50000],
+ [-8.754790, -18.28125],
+ [-21.61658, -20.03906],
+ [-31.35364, -24.25781],
+ [-39.90974, -30.93750],
+ [-43.83453, -41.13281],
+ [-47.75410, -49.92187],
+ [-50.95843, -54.14062],
+ [-55.97380, -56.60156]
+ ]
+
+ antpath = plugins.AntPath(locations=locations)
+ antpath.add_to(m)
+
+ m._repr_html_()
+ out = m._parent.render()
+
+ # We verify that the script import is present.
+ script = '' # noqa
+ assert script in out
+
+ # We verify that the script part is correct.
+ tmpl = Template("""
+ {{this.get_name()}} = L.polyline.antPath(
+ {{this.location}},
+ {{ this.options }}
+ )
+ .addTo({{this._parent.get_name()}});
+ """) # noqa
+
+ expected_rendered = tmpl.render(this=antpath)
+ rendered = antpath._template.module.script(antpath)
+ assert folium.utilities.compare_rendered(expected_rendered, rendered)