From 22d313aa44f1b54a680726c9f93a972e863a924a Mon Sep 17 00:00:00 2001 From: Filipe Fernandes Date: Thu, 8 Nov 2018 15:36:44 -0200 Subject: [PATCH 1/8] refactor parse options and don't validated the kw --- folium/vector_layers.py | 47 ++++++++++++----------------------------- 1 file changed, 14 insertions(+), 33 deletions(-) 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) From f9906929a05c2560476be934b6cdf8f66c4f61eb Mon Sep 17 00:00:00 2001 From: Filipe Fernandes Date: Thu, 8 Nov 2018 15:37:12 -0200 Subject: [PATCH 2/8] add AntPath plugin --- folium/plugins/__init__.py | 2 + folium/plugins/antpath.py | 75 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 folium/plugins/antpath.py 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..848c86f3e --- /dev/null +++ b/folium/plugins/antpath.py @@ -0,0 +1,75 @@ +# -*- 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. + + + https://github.com/rubenspgcavalcante/leaflet-ant-path/ + + """ + _template = Template(u""" + {% macro script(this, kwargs) %} + var {{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', + ) From 36122923c36e65922238410a3c047a0226090780 Mon Sep 17 00:00:00 2001 From: Filipe Fernandes Date: Thu, 8 Nov 2018 16:04:47 -0200 Subject: [PATCH 3/8] util to test rendered objects --- folium/utilities.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/folium/utilities.py b/folium/utilities.py index 2cf2fbfef..142fec751 100644 --- a/folium/utilities.py +++ b/folium/utilities.py @@ -385,3 +385,15 @@ def iter_points(x): return [x] else: 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 [line.strip() for line in rendered.splitlines() if line.strip()] From ed1bc4fd271f1825cfec5370c009f7d6438932cc Mon Sep 17 00:00:00 2001 From: Filipe Fernandes Date: Thu, 8 Nov 2018 16:05:29 -0200 Subject: [PATCH 4/8] antpath plugin --- examples/PolyLineTextPath_AntPath.ipynb | 218 ++++++++++++++++++++++++ examples/Polyline_text_path.ipynb | 183 -------------------- folium/plugins/antpath.py | 10 +- tests/plugins/test_antpath.py | 59 +++++++ 4 files changed, 282 insertions(+), 188 deletions(-) create mode 100644 examples/PolyLineTextPath_AntPath.ipynb delete mode 100644 examples/Polyline_text_path.ipynb create mode 100644 tests/plugins/test_antpath.py 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/antpath.py b/folium/plugins/antpath.py index 848c86f3e..8d30b60b5 100644 --- a/folium/plugins/antpath.py +++ b/folium/plugins/antpath.py @@ -31,11 +31,11 @@ class AntPath(Marker): """ _template = Template(u""" {% macro script(this, kwargs) %} - var {{this.get_name()}} = L.polyline.antPath( - {{this.location}}, - {{ this.options }} - ) - .addTo({{this._parent.get_name()}}); + {{this.get_name()}} = L.polyline.antPath( + {{this.location}}, + {{ this.options }} + ) + .addTo({{this._parent.get_name()}}); {% endmacro %} """) # noqa 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) From c98913c1f92390f54972ccce56502d5ab1409db1 Mon Sep 17 00:00:00 2001 From: Filipe Fernandes Date: Thu, 15 Nov 2018 10:21:38 -0200 Subject: [PATCH 5/8] minor fixes --- folium/utilities.py | 1 + 1 file changed, 1 insertion(+) diff --git a/folium/utilities.py b/folium/utilities.py index 142fec751..931d2c494 100644 --- a/folium/utilities.py +++ b/folium/utilities.py @@ -386,6 +386,7 @@ def iter_points(x): else: return [] + def compare_rendered(obj1, obj2): """ Return True/False if the normalized rendered version of From 4d72867b7dc274c0add15daecf10f2ffb0aade8e Mon Sep 17 00:00:00 2001 From: Frank <33519926+Conengmo@users.noreply.github.com> Date: Mon, 24 Dec 2018 16:06:35 -0200 Subject: [PATCH 6/8] Update folium/plugins/antpath.py Co-Authored-By: ocefpaf --- folium/plugins/antpath.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/folium/plugins/antpath.py b/folium/plugins/antpath.py index 8d30b60b5..4f584fe86 100644 --- a/folium/plugins/antpath.py +++ b/folium/plugins/antpath.py @@ -24,7 +24,11 @@ class AntPath(Marker): 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/ From ca55250eb71f1b09f55e2ca2bc663b0f5a679705 Mon Sep 17 00:00:00 2001 From: Frank <33519926+Conengmo@users.noreply.github.com> Date: Mon, 24 Dec 2018 16:07:20 -0200 Subject: [PATCH 7/8] Update folium/utilities.py Co-Authored-By: ocefpaf --- folium/utilities.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/folium/utilities.py b/folium/utilities.py index 155c0b0ba..ad8a06d66 100644 --- a/folium/utilities.py +++ b/folium/utilities.py @@ -399,7 +399,8 @@ def compare_rendered(obj1, obj2): def _normalize(rendered): - return [line.strip() for line in rendered.splitlines() if line.strip()] + """Return the input string as a list of stripped lines.""" + return [line.strip() for line in rendered.splitlines() if line.strip()] @contextmanager From 1d33032ae02e6c6d344653024d36b5d38534470c Mon Sep 17 00:00:00 2001 From: Filipe Fernandes Date: Mon, 24 Dec 2018 20:20:51 -0200 Subject: [PATCH 8/8] fix lint --- folium/utilities.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/folium/utilities.py b/folium/utilities.py index ad8a06d66..7d4f947b3 100644 --- a/folium/utilities.py +++ b/folium/utilities.py @@ -400,7 +400,7 @@ def compare_rendered(obj1, 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()] + return [line.strip() for line in rendered.splitlines() if line.strip()] @contextmanager