From 3191c20bcdc1b758620c95a336b8ce3a5bc37013 Mon Sep 17 00:00:00 2001 From: Talbert Date: Fri, 21 Sep 2018 10:19:47 -0600 Subject: [PATCH 01/12] Add inital minimap plugin --- examples/MiniMap.ipynb | 106 ++++++++++++++++++++++++++++ folium/plugins/__init__.py | 2 + folium/plugins/minimap.py | 129 ++++++++++++++++++++++++++++++++++ tests/plugins/test_minimap.py | 32 +++++++++ 4 files changed, 269 insertions(+) create mode 100644 examples/MiniMap.ipynb create mode 100644 folium/plugins/minimap.py create mode 100644 tests/plugins/test_minimap.py diff --git a/examples/MiniMap.ipynb b/examples/MiniMap.ipynb new file mode 100644 index 000000000..0d18a66a2 --- /dev/null +++ b/examples/MiniMap.ipynb @@ -0,0 +1,106 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import folium\n", + "from folium.plugins import MiniMap" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m = folium.Map(location=(30, 20), zoom_start=4)\n", + "minimap = MiniMap()\n", + "minimap.add_to(m)\n", + "m.save(r\"c:\\temp\\test.html\")\n", + "m" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "from folium.raster_layers import TileLayer\n", + "t = TileLayer(\"Stamen Toner\")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m = folium.Map(location=(30, 20), zoom_start=4)\n", + "minimap = MiniMap(tile_layer=t)\n", + "minimap.add_to(m)\n", + "m.save(r\"c:\\temp\\test.html\")\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.6" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/folium/plugins/__init__.py b/folium/plugins/__init__.py index ecf650f12..3de244cde 100644 --- a/folium/plugins/__init__.py +++ b/folium/plugins/__init__.py @@ -29,6 +29,7 @@ from folium.plugins.timestamped_geo_json import TimestampedGeoJson from folium.plugins.timestamped_wmstilelayer import TimestampedWmsTileLayers from folium.plugins.search import Search +from folium.plugins.minimap import MiniMap __all__ = [ 'BeautifyIcon', @@ -50,4 +51,5 @@ 'TimestampedGeoJson', 'TimestampedWmsTileLayers', 'Search', + 'MiniMap' ] diff --git a/folium/plugins/minimap.py b/folium/plugins/minimap.py new file mode 100644 index 000000000..e0fde0ef8 --- /dev/null +++ b/folium/plugins/minimap.py @@ -0,0 +1,129 @@ +# -*- coding: utf-8 -*- + +from __future__ import (absolute_import, division, print_function) + +import json + +from branca.element import CssLink, Figure, JavascriptLink, MacroElement +from folium.raster_layers import TileLayer + +from jinja2 import Template + + +class MiniMap(MacroElement): + """Add a minimap (locator) to an existing map. + + Uses the Leaflet plugin by Norkart under BSD 2-Clause "Simplified" License. + https://github.com/Norkart/Leaflet-MiniMap + + Parameters + ---------- + tile_layer: folium tilelayer, str (Optional) + + position : str, default 'bottomright' + The standard Control position parameter for the widget. + + + width: int, default 150 + The width of the minimap in pixels. + + height: int, default 150 + The height of the minimap in pixels. + + collapsed_width: int, default 25 + The width of the toggle marker and the minimap when collapsed, in pixels. + + collapsed_height: int, default 25 + The height of the toggle marker and the minimap when collapsed + + zoom_level_offset: int, defalut -5 + The offset applied to the zoom in the minimap compared to the zoom of the main map. Can be positive or negative. + + zoom_level_fixed: int, default None + Overrides the offset to apply a fixed zoom level to the minimap regardless of the main map zoom. + Set it to any valid zoom level, if unset zoomLevelOffset is used instead. + + center_fixed: bool, default False + Applies a fixed position to the minimap regardless of the main map's view / position. P + revents panning the minimap, but does allow zooming (both in the minimap and the main map). + If the minimap is zoomed, it will always zoom around the centerFixed point. + You can pass in a LatLng-equivalent object. + + zoom_animation: bool, default False + Sets whether the minimap should have an animated zoom. + (Will cause it to lag a bit after the movement of the main map.) + + toggle_display: bool, default + Sets whether the minimap should have a button to minimise it. + + auto_toggle_display: bool, default + Sets whether the minimap should hide automatically + if the parent map bounds does not fit within the minimap bounds. + Especially useful when 'zoomLevelFixed' is set. + + minimized: bool, default + Sets whether the minimap should start in a minimized position. + + Examples + -------- + >>> MiniMap(tile_layer='Stamen WaterColor', position='bottomleft') + """ + + _template = Template(""" + {% macro script(this, kwargs) %} + + var {{ this.tile_layer.get_name() }} = L.tileLayer( + '{{ this.tile_layer.tiles }}', + {{ this.tile_layer.options }} ); + + var {{ this.get_name() }} = new L.Control.MiniMap( {{this.tile_layer.get_name()}}, + {{ this.options }}); + {{ this._parent.get_name() }}.addControl({{ this.get_name() }}); + + {% endmacro %} + """) # noqa + + def __init__(self, tile_layer=None, position='bottomright', width=150, + height=150, collapsed_width=25, collapsed_height=25, + + zoom_level_offset=-5, zoom_level_fixed=None, + center_fixed=False, zoom_animation=False, + toggle_display=False, auto_toggle_display=False, + minimized=False): + + super(MiniMap, self).__init__() + self._name = 'MiniMap' + + if tile_layer is None: + self.tile_layer = TileLayer() + elif isinstance(tile_layer, TileLayer): + self.tile_layer = tile_layer + else: + self.tile_layer = TileLayer(tile_layer) + + options = { + 'position': position, + 'width': width, + 'height': height, + 'collapsedWidth': collapsed_width, + 'collapsedHeight': collapsed_height, + 'zoomLevelOffset': zoom_level_offset, + 'zoomLevelFixed': zoom_level_fixed, + 'centerFixed': center_fixed, + 'zoomAnimation': zoom_animation, + 'toggleDisplay': toggle_display, + 'autoToggleDisplay': auto_toggle_display, + 'minimized': minimized, + } + self.options = json.dumps(options, sort_keys=True, indent=2) + + def render(self, **kwargs): + figure = self.get_root() + assert isinstance(figure, Figure), ('You cannot render this Element ' + 'if it is not in a Figure.') + super(MiniMap, self).render() + + figure.header.add_child(JavascriptLink('https://cdnjs.cloudflare.com/ajax/libs/leaflet-minimap/3.6.1/Control.MiniMap.js')) # noqa + + figure.header.add_child(CssLink('https://cdnjs.cloudflare.com/ajax/libs/leaflet-minimap/3.6.1/Control.MiniMap.css')) # noqa + figure.add_child(self.tile_layer) \ No newline at end of file diff --git a/tests/plugins/test_minimap.py b/tests/plugins/test_minimap.py new file mode 100644 index 000000000..7cbd23f3a --- /dev/null +++ b/tests/plugins/test_minimap.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- + +""" +Test FloatImage +--------------- +""" + +from __future__ import (absolute_import, division, print_function) + +import folium + +from folium import plugins + + +def test_minimap(): + m = folium.Map(location=(30, 20), zoom_start=4) + + minimap = plugins.MiniMap() + m.add_child(minimap) + + out = m._parent.render() + + # Verify that a new minimap is getting created. + assert 'new L.Control.MiniMap' in out + + m = folium.Map(location=(30, 20), zoom_start=4) + minimap = plugins.MiniMap(tile_layer="Stamen Toner") + minimap.add_to(m) + + out = m._parent.render() + # verify that Stamen Toner tiles are being used + assert 'https://stamen-tiles' in out From 604f9b53d27d12d79d129b2ace064210624bffb1 Mon Sep 17 00:00:00 2001 From: Talbert Date: Fri, 21 Sep 2018 10:25:56 -0600 Subject: [PATCH 02/12] correction for stickler-ci error. --- folium/plugins/minimap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/folium/plugins/minimap.py b/folium/plugins/minimap.py index e0fde0ef8..f21b4177a 100644 --- a/folium/plugins/minimap.py +++ b/folium/plugins/minimap.py @@ -126,4 +126,4 @@ def render(self, **kwargs): figure.header.add_child(JavascriptLink('https://cdnjs.cloudflare.com/ajax/libs/leaflet-minimap/3.6.1/Control.MiniMap.js')) # noqa figure.header.add_child(CssLink('https://cdnjs.cloudflare.com/ajax/libs/leaflet-minimap/3.6.1/Control.MiniMap.css')) # noqa - figure.add_child(self.tile_layer) \ No newline at end of file + figure.add_child(self.tile_layer) From ea1140682e70e51b0d83d662a4f4288f65a7ed0d Mon Sep 17 00:00:00 2001 From: Talbert Date: Fri, 21 Sep 2018 10:28:47 -0600 Subject: [PATCH 03/12] Add the correct demo notebook --- examples/MiniMap.ipynb | 165 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 150 insertions(+), 15 deletions(-) diff --git a/examples/MiniMap.ipynb b/examples/MiniMap.ipynb index 0d18a66a2..ccbece517 100644 --- a/examples/MiniMap.ipynb +++ b/examples/MiniMap.ipynb @@ -1,5 +1,12 @@ { "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Demo of a Minimap in Folium" + ] + }, { "cell_type": "code", "execution_count": 1, @@ -18,10 +25,10 @@ { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ - "" + "" ] }, "execution_count": 2, @@ -31,27 +38,50 @@ ], "source": [ "m = folium.Map(location=(30, 20), zoom_start=4)\n", + "\n", "minimap = MiniMap()\n", - "minimap.add_to(m)\n", - "m.save(r\"c:\\temp\\test.html\")\n", + "m.add_child(minimap)\n", "m" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "metadata": {}, - "outputs": [], - "source": [] + "source": [ + "### Make the minimap collapsable" + ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "
" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "from folium.raster_layers import TileLayer\n", - "t = TileLayer(\"Stamen Toner\")" + "m = folium.Map(location=(30, 20), zoom_start=4)\n", + "minimap = MiniMap(toggle_display=True)\n", + "minimap.add_to(m)\n", + "m" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Change the minimap tile layer" ] }, { @@ -62,10 +92,10 @@ { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ - "" + "" ] }, "execution_count": 4, @@ -75,11 +105,116 @@ ], "source": [ "m = folium.Map(location=(30, 20), zoom_start=4)\n", - "minimap = MiniMap(tile_layer=t)\n", + "minimap = MiniMap(tile_layer=\"Stamen Toner\")\n", + "minimap.add_to(m)\n", + "m" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Change the minimap position" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m = folium.Map(location=(30, 20), zoom_start=4)\n", + "minimap = MiniMap(position=\"topleft\")\n", "minimap.add_to(m)\n", - "m.save(r\"c:\\temp\\test.html\")\n", "m" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Change the minimap size" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m = folium.Map(location=(30, 20), zoom_start=4)\n", + "minimap = MiniMap(width=400, height=100)\n", + "minimap.add_to(m)\n", + "m" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Change the zoom offset" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m = folium.Map(location=(30, 20), zoom_start=8)\n", + "minimap = MiniMap(zoom_level_offset=-8)\n", + "minimap.add_to(m)\n", + "m" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { From 34010e546ca5f35a3d067328b7376402dea6086f Mon Sep 17 00:00:00 2001 From: Talbert Date: Fri, 12 Oct 2018 16:52:39 -0600 Subject: [PATCH 04/12] Addressing PR comments. --- examples/Plugins.ipynb | 84 +++++++++++++++++++++++++---------- folium/plugins/__init__.py | 2 +- folium/plugins/minimap.py | 17 +++---- tests/plugins/test_minimap.py | 2 +- 4 files changed, 72 insertions(+), 33 deletions(-) diff --git a/examples/Plugins.ipynb b/examples/Plugins.ipynb index 53b45914a..3bcbba274 100644 --- a/examples/Plugins.ipynb +++ b/examples/Plugins.ipynb @@ -9,7 +9,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "0.5.0+114.gd5c3342.dirty\n" + "0.6.0+23.g2e3a79e.dirty\n" ] } ], @@ -61,10 +61,10 @@ { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ - "" + "" ] }, "execution_count": 3, @@ -104,10 +104,10 @@ { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ - "" + "" ] }, "execution_count": 4, @@ -153,10 +153,10 @@ { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ - "" + "" ] }, "execution_count": 5, @@ -189,10 +189,10 @@ { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ - "" + "" ] }, "execution_count": 6, @@ -240,10 +240,10 @@ { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ - "" + "" ] }, "execution_count": 7, @@ -299,10 +299,10 @@ { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ - "" + "" ] }, "execution_count": 8, @@ -340,10 +340,10 @@ { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ - "" + "" ] }, "execution_count": 9, @@ -450,10 +450,10 @@ { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ - "" + "" ] }, "execution_count": 10, @@ -594,10 +594,10 @@ { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ - "" + "" ] }, "execution_count": 11, @@ -647,10 +647,10 @@ { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ - "" + "" ] }, "execution_count": 12, @@ -682,6 +682,44 @@ "\n", "m" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Minimap\n", + "\n", + "Adds a locator minimap to a folium document" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from folium.plugins import MiniMap\n", + "\n", + "m = folium.Map(location=(30, 20), zoom_start=4)\n", + "\n", + "minimap = MiniMap()\n", + "m.add_child(minimap)\n", + "m" + ] } ], "metadata": { @@ -700,9 +738,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.5" + "version": "3.6.6" } }, "nbformat": 4, - "nbformat_minor": 1 + "nbformat_minor": 2 } diff --git a/folium/plugins/__init__.py b/folium/plugins/__init__.py index 3de244cde..ff6b47ef0 100644 --- a/folium/plugins/__init__.py +++ b/folium/plugins/__init__.py @@ -51,5 +51,5 @@ 'TimestampedGeoJson', 'TimestampedWmsTileLayers', 'Search', - 'MiniMap' + 'MiniMap', ] diff --git a/folium/plugins/minimap.py b/folium/plugins/minimap.py index f21b4177a..965b272e7 100644 --- a/folium/plugins/minimap.py +++ b/folium/plugins/minimap.py @@ -18,12 +18,13 @@ class MiniMap(MacroElement): Parameters ---------- - tile_layer: folium tilelayer, str (Optional) + tile_layer: folium TileLayer object or str, optional + Provide a folium TileLayer object or the wanted tiles as string. + If not provided it will use the default of `TileLayer`. position : str, default 'bottomright' The standard Control position parameter for the widget. - width: int, default 150 The width of the minimap in pixels. @@ -36,7 +37,7 @@ class MiniMap(MacroElement): collapsed_height: int, default 25 The height of the toggle marker and the minimap when collapsed - zoom_level_offset: int, defalut -5 + zoom_level_offset: int, defalaut -5 The offset applied to the zoom in the minimap compared to the zoom of the main map. Can be positive or negative. zoom_level_fixed: int, default None @@ -44,8 +45,8 @@ class MiniMap(MacroElement): Set it to any valid zoom level, if unset zoomLevelOffset is used instead. center_fixed: bool, default False - Applies a fixed position to the minimap regardless of the main map's view / position. P - revents panning the minimap, but does allow zooming (both in the minimap and the main map). + Applies a fixed position to the minimap regardless of the main map's view / position. + Prevents panning the minimap, but does allow zooming (both in the minimap and the main map). If the minimap is zoomed, it will always zoom around the centerFixed point. You can pass in a LatLng-equivalent object. @@ -53,15 +54,15 @@ class MiniMap(MacroElement): Sets whether the minimap should have an animated zoom. (Will cause it to lag a bit after the movement of the main map.) - toggle_display: bool, default + toggle_display: bool, default False Sets whether the minimap should have a button to minimise it. - auto_toggle_display: bool, default + auto_toggle_display: bool, default False Sets whether the minimap should hide automatically if the parent map bounds does not fit within the minimap bounds. Especially useful when 'zoomLevelFixed' is set. - minimized: bool, default + minimized: bool, default False Sets whether the minimap should start in a minimized position. Examples diff --git a/tests/plugins/test_minimap.py b/tests/plugins/test_minimap.py index 7cbd23f3a..a8b58d45f 100644 --- a/tests/plugins/test_minimap.py +++ b/tests/plugins/test_minimap.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """ -Test FloatImage +Test MiniMap --------------- """ From 4453b2332d7737fd9450b06b4e1f040e0d805ae5 Mon Sep 17 00:00:00 2001 From: Talbert Date: Mon, 15 Oct 2018 08:51:39 -0600 Subject: [PATCH 05/12] Addressing PR review comments v2 --- folium/plugins/minimap.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/folium/plugins/minimap.py b/folium/plugins/minimap.py index 965b272e7..c0d2df547 100644 --- a/folium/plugins/minimap.py +++ b/folium/plugins/minimap.py @@ -18,9 +18,9 @@ class MiniMap(MacroElement): Parameters ---------- - tile_layer: folium TileLayer object or str, optional + tile_layer: folium TileLayer object or str, default = Provide a folium TileLayer object or the wanted tiles as string. - If not provided it will use the default of `TileLayer`. + If not provided it will use the default of 'TileLayer', currently OpenStreetMap. position : str, default 'bottomright' The standard Control position parameter for the widget. @@ -86,7 +86,6 @@ class MiniMap(MacroElement): def __init__(self, tile_layer=None, position='bottomright', width=150, height=150, collapsed_width=25, collapsed_height=25, - zoom_level_offset=-5, zoom_level_fixed=None, center_fixed=False, zoom_animation=False, toggle_display=False, auto_toggle_display=False, @@ -127,4 +126,3 @@ def render(self, **kwargs): figure.header.add_child(JavascriptLink('https://cdnjs.cloudflare.com/ajax/libs/leaflet-minimap/3.6.1/Control.MiniMap.js')) # noqa figure.header.add_child(CssLink('https://cdnjs.cloudflare.com/ajax/libs/leaflet-minimap/3.6.1/Control.MiniMap.css')) # noqa - figure.add_child(self.tile_layer) From d13fa15bedc8199d47bdea8f5f19beac88e16944 Mon Sep 17 00:00:00 2001 From: Frank <33519926+Conengmo@users.noreply.github.com> Date: Mon, 15 Oct 2018 21:36:15 +0200 Subject: [PATCH 06/12] Entry in changelog --- CHANGES.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 820ae15d1..0cb937ce1 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -4,12 +4,14 @@ - Update leaflet to 1.3.4 (ocefpaf #939) - More options (tms, opacity, kwargs) in TileLayer (mpickering #948) - Add MousePosition plugin (btozer #916) +- Add Minimap plugin (talbertc-usgs #968) Bug Fixes - Fix wrong default value for fmt argument of WmsTileLayer (conengmo #950) - Fix icon_create_function argument in MarkerCluster (conengmo #954) - +- Update stylesheet url in TimestampedGeoJson (frodebjerke #963) +- Use Javascript template literals in Tooltip and Popup (jtbaker #955 #962) 0.6.0 ~~~~~ From 3e9ac112cd3cac2874564375eafc4f07370881fa Mon Sep 17 00:00:00 2001 From: Talbert Date: Fri, 21 Sep 2018 10:19:47 -0600 Subject: [PATCH 07/12] Add inital minimap plugin --- examples/MiniMap.ipynb | 106 ++++++++++++++++++++++++++++ folium/plugins/__init__.py | 2 + folium/plugins/minimap.py | 129 ++++++++++++++++++++++++++++++++++ tests/plugins/test_minimap.py | 32 +++++++++ 4 files changed, 269 insertions(+) create mode 100644 examples/MiniMap.ipynb create mode 100644 folium/plugins/minimap.py create mode 100644 tests/plugins/test_minimap.py diff --git a/examples/MiniMap.ipynb b/examples/MiniMap.ipynb new file mode 100644 index 000000000..0d18a66a2 --- /dev/null +++ b/examples/MiniMap.ipynb @@ -0,0 +1,106 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import folium\n", + "from folium.plugins import MiniMap" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m = folium.Map(location=(30, 20), zoom_start=4)\n", + "minimap = MiniMap()\n", + "minimap.add_to(m)\n", + "m.save(r\"c:\\temp\\test.html\")\n", + "m" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "from folium.raster_layers import TileLayer\n", + "t = TileLayer(\"Stamen Toner\")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m = folium.Map(location=(30, 20), zoom_start=4)\n", + "minimap = MiniMap(tile_layer=t)\n", + "minimap.add_to(m)\n", + "m.save(r\"c:\\temp\\test.html\")\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.6" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/folium/plugins/__init__.py b/folium/plugins/__init__.py index ecf650f12..3de244cde 100644 --- a/folium/plugins/__init__.py +++ b/folium/plugins/__init__.py @@ -29,6 +29,7 @@ from folium.plugins.timestamped_geo_json import TimestampedGeoJson from folium.plugins.timestamped_wmstilelayer import TimestampedWmsTileLayers from folium.plugins.search import Search +from folium.plugins.minimap import MiniMap __all__ = [ 'BeautifyIcon', @@ -50,4 +51,5 @@ 'TimestampedGeoJson', 'TimestampedWmsTileLayers', 'Search', + 'MiniMap' ] diff --git a/folium/plugins/minimap.py b/folium/plugins/minimap.py new file mode 100644 index 000000000..e0fde0ef8 --- /dev/null +++ b/folium/plugins/minimap.py @@ -0,0 +1,129 @@ +# -*- coding: utf-8 -*- + +from __future__ import (absolute_import, division, print_function) + +import json + +from branca.element import CssLink, Figure, JavascriptLink, MacroElement +from folium.raster_layers import TileLayer + +from jinja2 import Template + + +class MiniMap(MacroElement): + """Add a minimap (locator) to an existing map. + + Uses the Leaflet plugin by Norkart under BSD 2-Clause "Simplified" License. + https://github.com/Norkart/Leaflet-MiniMap + + Parameters + ---------- + tile_layer: folium tilelayer, str (Optional) + + position : str, default 'bottomright' + The standard Control position parameter for the widget. + + + width: int, default 150 + The width of the minimap in pixels. + + height: int, default 150 + The height of the minimap in pixels. + + collapsed_width: int, default 25 + The width of the toggle marker and the minimap when collapsed, in pixels. + + collapsed_height: int, default 25 + The height of the toggle marker and the minimap when collapsed + + zoom_level_offset: int, defalut -5 + The offset applied to the zoom in the minimap compared to the zoom of the main map. Can be positive or negative. + + zoom_level_fixed: int, default None + Overrides the offset to apply a fixed zoom level to the minimap regardless of the main map zoom. + Set it to any valid zoom level, if unset zoomLevelOffset is used instead. + + center_fixed: bool, default False + Applies a fixed position to the minimap regardless of the main map's view / position. P + revents panning the minimap, but does allow zooming (both in the minimap and the main map). + If the minimap is zoomed, it will always zoom around the centerFixed point. + You can pass in a LatLng-equivalent object. + + zoom_animation: bool, default False + Sets whether the minimap should have an animated zoom. + (Will cause it to lag a bit after the movement of the main map.) + + toggle_display: bool, default + Sets whether the minimap should have a button to minimise it. + + auto_toggle_display: bool, default + Sets whether the minimap should hide automatically + if the parent map bounds does not fit within the minimap bounds. + Especially useful when 'zoomLevelFixed' is set. + + minimized: bool, default + Sets whether the minimap should start in a minimized position. + + Examples + -------- + >>> MiniMap(tile_layer='Stamen WaterColor', position='bottomleft') + """ + + _template = Template(""" + {% macro script(this, kwargs) %} + + var {{ this.tile_layer.get_name() }} = L.tileLayer( + '{{ this.tile_layer.tiles }}', + {{ this.tile_layer.options }} ); + + var {{ this.get_name() }} = new L.Control.MiniMap( {{this.tile_layer.get_name()}}, + {{ this.options }}); + {{ this._parent.get_name() }}.addControl({{ this.get_name() }}); + + {% endmacro %} + """) # noqa + + def __init__(self, tile_layer=None, position='bottomright', width=150, + height=150, collapsed_width=25, collapsed_height=25, + + zoom_level_offset=-5, zoom_level_fixed=None, + center_fixed=False, zoom_animation=False, + toggle_display=False, auto_toggle_display=False, + minimized=False): + + super(MiniMap, self).__init__() + self._name = 'MiniMap' + + if tile_layer is None: + self.tile_layer = TileLayer() + elif isinstance(tile_layer, TileLayer): + self.tile_layer = tile_layer + else: + self.tile_layer = TileLayer(tile_layer) + + options = { + 'position': position, + 'width': width, + 'height': height, + 'collapsedWidth': collapsed_width, + 'collapsedHeight': collapsed_height, + 'zoomLevelOffset': zoom_level_offset, + 'zoomLevelFixed': zoom_level_fixed, + 'centerFixed': center_fixed, + 'zoomAnimation': zoom_animation, + 'toggleDisplay': toggle_display, + 'autoToggleDisplay': auto_toggle_display, + 'minimized': minimized, + } + self.options = json.dumps(options, sort_keys=True, indent=2) + + def render(self, **kwargs): + figure = self.get_root() + assert isinstance(figure, Figure), ('You cannot render this Element ' + 'if it is not in a Figure.') + super(MiniMap, self).render() + + figure.header.add_child(JavascriptLink('https://cdnjs.cloudflare.com/ajax/libs/leaflet-minimap/3.6.1/Control.MiniMap.js')) # noqa + + figure.header.add_child(CssLink('https://cdnjs.cloudflare.com/ajax/libs/leaflet-minimap/3.6.1/Control.MiniMap.css')) # noqa + figure.add_child(self.tile_layer) \ No newline at end of file diff --git a/tests/plugins/test_minimap.py b/tests/plugins/test_minimap.py new file mode 100644 index 000000000..7cbd23f3a --- /dev/null +++ b/tests/plugins/test_minimap.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- + +""" +Test FloatImage +--------------- +""" + +from __future__ import (absolute_import, division, print_function) + +import folium + +from folium import plugins + + +def test_minimap(): + m = folium.Map(location=(30, 20), zoom_start=4) + + minimap = plugins.MiniMap() + m.add_child(minimap) + + out = m._parent.render() + + # Verify that a new minimap is getting created. + assert 'new L.Control.MiniMap' in out + + m = folium.Map(location=(30, 20), zoom_start=4) + minimap = plugins.MiniMap(tile_layer="Stamen Toner") + minimap.add_to(m) + + out = m._parent.render() + # verify that Stamen Toner tiles are being used + assert 'https://stamen-tiles' in out From 9a2cebb9b0c3799fe0e33db03dd3579301eb02c7 Mon Sep 17 00:00:00 2001 From: Talbert Date: Fri, 21 Sep 2018 10:25:56 -0600 Subject: [PATCH 08/12] correction for stickler-ci error. --- folium/plugins/minimap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/folium/plugins/minimap.py b/folium/plugins/minimap.py index e0fde0ef8..f21b4177a 100644 --- a/folium/plugins/minimap.py +++ b/folium/plugins/minimap.py @@ -126,4 +126,4 @@ def render(self, **kwargs): figure.header.add_child(JavascriptLink('https://cdnjs.cloudflare.com/ajax/libs/leaflet-minimap/3.6.1/Control.MiniMap.js')) # noqa figure.header.add_child(CssLink('https://cdnjs.cloudflare.com/ajax/libs/leaflet-minimap/3.6.1/Control.MiniMap.css')) # noqa - figure.add_child(self.tile_layer) \ No newline at end of file + figure.add_child(self.tile_layer) From 3cd3aae85204624e5920fcdce2f2e91400c881f4 Mon Sep 17 00:00:00 2001 From: Talbert Date: Fri, 21 Sep 2018 10:28:47 -0600 Subject: [PATCH 09/12] Add the correct demo notebook --- examples/MiniMap.ipynb | 165 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 150 insertions(+), 15 deletions(-) diff --git a/examples/MiniMap.ipynb b/examples/MiniMap.ipynb index 0d18a66a2..ccbece517 100644 --- a/examples/MiniMap.ipynb +++ b/examples/MiniMap.ipynb @@ -1,5 +1,12 @@ { "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Demo of a Minimap in Folium" + ] + }, { "cell_type": "code", "execution_count": 1, @@ -18,10 +25,10 @@ { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ - "" + "" ] }, "execution_count": 2, @@ -31,27 +38,50 @@ ], "source": [ "m = folium.Map(location=(30, 20), zoom_start=4)\n", + "\n", "minimap = MiniMap()\n", - "minimap.add_to(m)\n", - "m.save(r\"c:\\temp\\test.html\")\n", + "m.add_child(minimap)\n", "m" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "metadata": {}, - "outputs": [], - "source": [] + "source": [ + "### Make the minimap collapsable" + ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "
" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "from folium.raster_layers import TileLayer\n", - "t = TileLayer(\"Stamen Toner\")" + "m = folium.Map(location=(30, 20), zoom_start=4)\n", + "minimap = MiniMap(toggle_display=True)\n", + "minimap.add_to(m)\n", + "m" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Change the minimap tile layer" ] }, { @@ -62,10 +92,10 @@ { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ - "" + "" ] }, "execution_count": 4, @@ -75,11 +105,116 @@ ], "source": [ "m = folium.Map(location=(30, 20), zoom_start=4)\n", - "minimap = MiniMap(tile_layer=t)\n", + "minimap = MiniMap(tile_layer=\"Stamen Toner\")\n", + "minimap.add_to(m)\n", + "m" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Change the minimap position" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m = folium.Map(location=(30, 20), zoom_start=4)\n", + "minimap = MiniMap(position=\"topleft\")\n", "minimap.add_to(m)\n", - "m.save(r\"c:\\temp\\test.html\")\n", "m" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Change the minimap size" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m = folium.Map(location=(30, 20), zoom_start=4)\n", + "minimap = MiniMap(width=400, height=100)\n", + "minimap.add_to(m)\n", + "m" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Change the zoom offset" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m = folium.Map(location=(30, 20), zoom_start=8)\n", + "minimap = MiniMap(zoom_level_offset=-8)\n", + "minimap.add_to(m)\n", + "m" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { From 4d644e397d15ecff704c9cc8a1a7308dbc842f72 Mon Sep 17 00:00:00 2001 From: Talbert Date: Fri, 12 Oct 2018 16:52:39 -0600 Subject: [PATCH 10/12] Addressing PR comments. --- examples/Plugins.ipynb | 84 +++++++++++++++++++++++++---------- folium/plugins/__init__.py | 2 +- folium/plugins/minimap.py | 17 +++---- tests/plugins/test_minimap.py | 2 +- 4 files changed, 72 insertions(+), 33 deletions(-) diff --git a/examples/Plugins.ipynb b/examples/Plugins.ipynb index 53b45914a..3bcbba274 100644 --- a/examples/Plugins.ipynb +++ b/examples/Plugins.ipynb @@ -9,7 +9,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "0.5.0+114.gd5c3342.dirty\n" + "0.6.0+23.g2e3a79e.dirty\n" ] } ], @@ -61,10 +61,10 @@ { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ - "" + "" ] }, "execution_count": 3, @@ -104,10 +104,10 @@ { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ - "" + "" ] }, "execution_count": 4, @@ -153,10 +153,10 @@ { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ - "" + "" ] }, "execution_count": 5, @@ -189,10 +189,10 @@ { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ - "" + "" ] }, "execution_count": 6, @@ -240,10 +240,10 @@ { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ - "" + "" ] }, "execution_count": 7, @@ -299,10 +299,10 @@ { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ - "" + "" ] }, "execution_count": 8, @@ -340,10 +340,10 @@ { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ - "" + "" ] }, "execution_count": 9, @@ -450,10 +450,10 @@ { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ - "" + "" ] }, "execution_count": 10, @@ -594,10 +594,10 @@ { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ - "" + "" ] }, "execution_count": 11, @@ -647,10 +647,10 @@ { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ - "" + "" ] }, "execution_count": 12, @@ -682,6 +682,44 @@ "\n", "m" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Minimap\n", + "\n", + "Adds a locator minimap to a folium document" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from folium.plugins import MiniMap\n", + "\n", + "m = folium.Map(location=(30, 20), zoom_start=4)\n", + "\n", + "minimap = MiniMap()\n", + "m.add_child(minimap)\n", + "m" + ] } ], "metadata": { @@ -700,9 +738,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.5" + "version": "3.6.6" } }, "nbformat": 4, - "nbformat_minor": 1 + "nbformat_minor": 2 } diff --git a/folium/plugins/__init__.py b/folium/plugins/__init__.py index 3de244cde..ff6b47ef0 100644 --- a/folium/plugins/__init__.py +++ b/folium/plugins/__init__.py @@ -51,5 +51,5 @@ 'TimestampedGeoJson', 'TimestampedWmsTileLayers', 'Search', - 'MiniMap' + 'MiniMap', ] diff --git a/folium/plugins/minimap.py b/folium/plugins/minimap.py index f21b4177a..965b272e7 100644 --- a/folium/plugins/minimap.py +++ b/folium/plugins/minimap.py @@ -18,12 +18,13 @@ class MiniMap(MacroElement): Parameters ---------- - tile_layer: folium tilelayer, str (Optional) + tile_layer: folium TileLayer object or str, optional + Provide a folium TileLayer object or the wanted tiles as string. + If not provided it will use the default of `TileLayer`. position : str, default 'bottomright' The standard Control position parameter for the widget. - width: int, default 150 The width of the minimap in pixels. @@ -36,7 +37,7 @@ class MiniMap(MacroElement): collapsed_height: int, default 25 The height of the toggle marker and the minimap when collapsed - zoom_level_offset: int, defalut -5 + zoom_level_offset: int, defalaut -5 The offset applied to the zoom in the minimap compared to the zoom of the main map. Can be positive or negative. zoom_level_fixed: int, default None @@ -44,8 +45,8 @@ class MiniMap(MacroElement): Set it to any valid zoom level, if unset zoomLevelOffset is used instead. center_fixed: bool, default False - Applies a fixed position to the minimap regardless of the main map's view / position. P - revents panning the minimap, but does allow zooming (both in the minimap and the main map). + Applies a fixed position to the minimap regardless of the main map's view / position. + Prevents panning the minimap, but does allow zooming (both in the minimap and the main map). If the minimap is zoomed, it will always zoom around the centerFixed point. You can pass in a LatLng-equivalent object. @@ -53,15 +54,15 @@ class MiniMap(MacroElement): Sets whether the minimap should have an animated zoom. (Will cause it to lag a bit after the movement of the main map.) - toggle_display: bool, default + toggle_display: bool, default False Sets whether the minimap should have a button to minimise it. - auto_toggle_display: bool, default + auto_toggle_display: bool, default False Sets whether the minimap should hide automatically if the parent map bounds does not fit within the minimap bounds. Especially useful when 'zoomLevelFixed' is set. - minimized: bool, default + minimized: bool, default False Sets whether the minimap should start in a minimized position. Examples diff --git a/tests/plugins/test_minimap.py b/tests/plugins/test_minimap.py index 7cbd23f3a..a8b58d45f 100644 --- a/tests/plugins/test_minimap.py +++ b/tests/plugins/test_minimap.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """ -Test FloatImage +Test MiniMap --------------- """ From 95c53fd1f41dbf81814f16dff9fdbd1046801811 Mon Sep 17 00:00:00 2001 From: Talbert Date: Mon, 15 Oct 2018 08:51:39 -0600 Subject: [PATCH 11/12] Addressing PR review comments v2 --- folium/plugins/minimap.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/folium/plugins/minimap.py b/folium/plugins/minimap.py index 965b272e7..c0d2df547 100644 --- a/folium/plugins/minimap.py +++ b/folium/plugins/minimap.py @@ -18,9 +18,9 @@ class MiniMap(MacroElement): Parameters ---------- - tile_layer: folium TileLayer object or str, optional + tile_layer: folium TileLayer object or str, default = Provide a folium TileLayer object or the wanted tiles as string. - If not provided it will use the default of `TileLayer`. + If not provided it will use the default of 'TileLayer', currently OpenStreetMap. position : str, default 'bottomright' The standard Control position parameter for the widget. @@ -86,7 +86,6 @@ class MiniMap(MacroElement): def __init__(self, tile_layer=None, position='bottomright', width=150, height=150, collapsed_width=25, collapsed_height=25, - zoom_level_offset=-5, zoom_level_fixed=None, center_fixed=False, zoom_animation=False, toggle_display=False, auto_toggle_display=False, @@ -127,4 +126,3 @@ def render(self, **kwargs): figure.header.add_child(JavascriptLink('https://cdnjs.cloudflare.com/ajax/libs/leaflet-minimap/3.6.1/Control.MiniMap.js')) # noqa figure.header.add_child(CssLink('https://cdnjs.cloudflare.com/ajax/libs/leaflet-minimap/3.6.1/Control.MiniMap.css')) # noqa - figure.add_child(self.tile_layer) From 1ad01161e5f90de4ebc07139faa3e2f8b28ee1a7 Mon Sep 17 00:00:00 2001 From: Frank <33519926+Conengmo@users.noreply.github.com> Date: Mon, 15 Oct 2018 21:36:15 +0200 Subject: [PATCH 12/12] Entry in changelog --- CHANGES.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.txt b/CHANGES.txt index ffd379703..0cb937ce1 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -4,6 +4,7 @@ - Update leaflet to 1.3.4 (ocefpaf #939) - More options (tms, opacity, kwargs) in TileLayer (mpickering #948) - Add MousePosition plugin (btozer #916) +- Add Minimap plugin (talbertc-usgs #968) Bug Fixes