Skip to content

Commit 95da71e

Browse files
authored
Merge pull request #3 from python-visualization/master
Update master
2 parents 0e527a3 + 8595240 commit 95da71e

File tree

27 files changed

+1198
-787
lines changed

27 files changed

+1198
-787
lines changed

CHANGES.txt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,27 @@
11
0.9.0
22
~~~~~
33

4+
This version drops support for Python 2.7. (#1100, #1104, #1111)
5+
Python 3.5+ is required.
6+
47
- Geojson separate style mapping (conengmo #1058)
8+
- Warn for wrong color values in Icon (conengmo #1099)
9+
- Use Jinja2's `tojson` filter to convert data (conengmo #1101)
10+
- Pass **kwargs as options to Leaflet classes (conengmo #1101)
11+
- Explicit coordinate validation (conengmo #1090)
12+
- Add `icon_create_function` arg to `FastMarkerCluster` plugin (Gordonei #1109)
13+
- Add `PolyLineOffset` plugin (FabeG #1091)
14+
- Add Locate Control plugin (fullonic #1116)
15+
- Add Leaflet `CustomPane` class (Ipkirwin #1094)
16+
17+
API changes
18+
19+
- Remove `add_tile_layer` method from `Map` (conengmo #1127)
20+
- Remove args from `Map`, if needed use `TileLayer` instead (conengmo #1127)
21+
22+
Bug fixes
23+
24+
- Fix broken attribution for built-in tiles (FabeG #1128)
525

626
0.8.3
727
~~~~~

examples/CustomPanes.ipynb

Lines changed: 222 additions & 0 deletions
Large diffs are not rendered by default.

examples/Plugins.ipynb

Lines changed: 777 additions & 752 deletions
Large diffs are not rendered by default.

folium/map.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,3 +476,45 @@ def __init__(self, bounds, padding_top_left=None,
476476
padding_bottom_right=padding_bottom_right,
477477
padding=padding,
478478
)
479+
480+
481+
class CustomPane(MacroElement):
482+
"""
483+
Creates a custom pane to hold map elements.
484+
485+
Behavior is as in https://leafletjs.com/examples/map-panes/
486+
487+
Parameters
488+
----------
489+
name: string
490+
Name of the custom pane. Other map elements can be added
491+
to the pane by specifying the 'pane' kwarg when constructing
492+
them.
493+
z_index: int or string, default 625
494+
The z-index that will be associated with the pane, and will
495+
determine which map elements lie over/under it. The default
496+
(625) corresponds to between markers and tooltips. Default
497+
panes and z-indexes can be found at
498+
https://leafletjs.com/reference-1.4.0.html#map-pane
499+
pointer_events: bool, default False
500+
Whether or not layers in the pane should interact with the
501+
cursor. Setting to False will prevent interfering with
502+
pointer events associated with lower layers.
503+
"""
504+
_template = Template(u"""
505+
{% macro script(this, kwargs) %}
506+
var {{ this.get_name() }} = {{ this._parent.get_name() }}.createPane(
507+
{{ this.name|tojson }});
508+
{{ this.get_name() }}.style.zIndex = {{ this.z_index|tojson }};
509+
{% if not this.pointer_events %}
510+
{{ this.get_name() }}.style.pointerEvents = 'none';
511+
{% endif %}
512+
{% endmacro %}
513+
""")
514+
515+
def __init__(self, name, z_index=625, pointer_events=False):
516+
super(CustomPane, self).__init__()
517+
self._name = 'Pane'
518+
self.name = name
519+
self.z_index = z_index
520+
self.pointer_events = pointer_events

folium/plugins/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from folium.plugins.fullscreen import Fullscreen
2121
from folium.plugins.heat_map import HeatMap
2222
from folium.plugins.heat_map_withtime import HeatMapWithTime
23+
from folium.plugins.locate_control import LocateControl
2324
from folium.plugins.marker_cluster import MarkerCluster
2425
from folium.plugins.measure_control import MeasureControl
2526
from folium.plugins.minimap import MiniMap
@@ -46,6 +47,7 @@
4647
'Fullscreen',
4748
'HeatMap',
4849
'HeatMapWithTime',
50+
'LocateControl',
4951
'MarkerCluster',
5052
'MeasureControl',
5153
'MiniMap',

folium/plugins/locate_control.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
"""Add Locate control to folium Map.
2+
3+
Based on leaflet plugin: https://github.com/domoritz/leaflet-locatecontrol
4+
"""
5+
6+
from branca.element import CssLink, Figure, JavascriptLink, MacroElement
7+
8+
from jinja2 import Template
9+
from folium.utilities import parse_options
10+
11+
12+
class LocateControl(MacroElement):
13+
"""Control plugin to geolocate the user.
14+
15+
This plugins adds a button to the map, and when it's clicked shows the current
16+
user device location.
17+
18+
To work properly in production, the connection needs to be encrypted, otherwise browser will not
19+
allow users to share their location.
20+
21+
WARNING: This plugin when used with Draw plugin, it must be added to your map before Draw. See
22+
example below.
23+
24+
Parameters
25+
----------
26+
**kwargs
27+
For possible options, see https://github.com/domoritz/leaflet-locatecontrol
28+
29+
Examples
30+
--------
31+
>>> m = folium.Map()
32+
# With default settings
33+
>>> LocateControl().add_to(m)
34+
35+
# With custom options and alongside with Draw
36+
>>> LocateControl(
37+
... position="bottomright",
38+
... strings={"title": "See you current location",
39+
... "popup": "Your position"}).add_to(m))
40+
41+
>>> Draw(export=True).add_to(m)
42+
43+
For more info check:
44+
https://github.com/domoritz/leaflet-locatecontrol
45+
46+
"""
47+
48+
_template = Template("""
49+
{% macro script(this, kwargs) %}
50+
var {{this.get_name()}} = L.control.locate(
51+
{{this.options | tojson}}
52+
).addTo({{this._parent.get_name()}});
53+
{% endmacro %}
54+
""")
55+
56+
def __init__(self, **kwargs):
57+
super(LocateControl, self).__init__()
58+
self._name = 'LocateControl'
59+
self.options = parse_options(**kwargs)
60+
61+
def render(self, **kwargs):
62+
super(LocateControl, self).render(**kwargs)
63+
figure = self.get_root()
64+
assert isinstance(figure, Figure), ('You cannot render this Element '
65+
'if it is not in a Figure.')
66+
67+
figure.header.add_child(
68+
CssLink(
69+
"https://cdnjs.cloudflare.com/ajax/libs/leaflet-locatecontrol/0.66.2/L.Control.Locate.min.css")) # noqa
70+
figure.header.add_child(JavascriptLink(
71+
"https://cdnjs.cloudflare.com/ajax/libs/leaflet-locatecontrol/0.66.2/L.Control.Locate.min.js")) # noqa

folium/raster_layers.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -90,18 +90,6 @@ def __init__(self, tiles='OpenStreetMap', min_zoom=0, max_zoom=18,
9090
self._name = 'TileLayer'
9191
self._env = ENV
9292

93-
self.options = parse_options(
94-
min_zoom=min_zoom,
95-
max_zoom=max_zoom,
96-
max_native_zoom=max_native_zoom or max_zoom,
97-
no_wrap=no_wrap,
98-
attribution=attr,
99-
subdomains=subdomains,
100-
detect_retina=detect_retina,
101-
tms=tms,
102-
opacity=opacity,
103-
**kwargs
104-
)
10593
tiles_flat = ''.join(tiles.lower().strip().split())
10694
if tiles_flat in ('cloudmade', 'mapbox') and not API_key:
10795
raise ValueError('You must pass an API key if using Cloudmade'
@@ -113,12 +101,24 @@ def __init__(self, tiles='OpenStreetMap', min_zoom=0, max_zoom=18,
113101

114102
if tile_template in templates and attr_template in templates:
115103
self.tiles = self._env.get_template(tile_template).render(API_key=API_key) # noqa
116-
self.attr = self._env.get_template(attr_template).render()
104+
attr = self._env.get_template(attr_template).render()
117105
else:
118106
self.tiles = tiles
119107
if not attr:
120108
raise ValueError('Custom tiles must have an attribution.')
121-
self.attr = attr
109+
110+
self.options = parse_options(
111+
min_zoom=min_zoom,
112+
max_zoom=max_zoom,
113+
max_native_zoom=max_native_zoom or max_zoom,
114+
no_wrap=no_wrap,
115+
attribution=attr,
116+
subdomains=subdomains,
117+
detect_retina=detect_retina,
118+
tms=tms,
119+
opacity=opacity,
120+
**kwargs
121+
)
122122

123123

124124
class WmsTileLayer(Layer):
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
(c) <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors (c) <a href="http://cartodb.com/attributions">CartoDB</a>, CartoDB <a href ="http://cartodb.com/attributions">attributions</a>
1+
&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors &copy; <a href="http://cartodb.com/attributions">CartoDB</a>, CartoDB <a href ="http://cartodb.com/attributions">attributions</a>
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
(c) <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors (c) <a href="http://cartodb.com/attributions">CartoDB</a>, CartoDB <a href ="http://cartodb.com/attributions">attributions</a>
1+
&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors &copy; <a href="http://cartodb.com/attributions">CartoDB</a>, CartoDB <a href ="http://cartodb.com/attributions">attributions</a>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
(c) <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors (c) <a href="http://cartodb.com/attributions">CartoDB</a>, CartoDB <a href ="http://cartodb.com/attributions">attributions</a>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
https://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}{r}.png
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
(c) <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors (c) <a href="http://cartodb.com/attributions">CartoDB</a>, CartoDB <a href ="http://cartodb.com/attributions">attributions</a>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
https://{s}.basemaps.cartocdn.com/light_only_labels/{z}/{x}/{y}{r}.png
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Map data (c) <a href="http://openstreetmap.org">OpenStreetMap</a> contributors, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery (c) <a href="http://cloudmade.com">CloudMade</a>
1+
Map data &copy; <a href="http://openstreetmap.org">OpenStreetMap</a> contributors, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery &copy; <a href="http://cloudmade.com">CloudMade</a>
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Map tiles by <a href="http://www.mapbox.com/m">Mapbox</a> Data by <a href="http://openstreetmap.org">OpenStreetMap</a>, under <a href="http://creativecommons.org/licenses/by-sa/3.0">CC BY SA</a>.
1+
Map tiles by &copy; <a href="http://www.mapbox.com/about/maps">Mapbox</a> Data by &copy; <a href="http://openstreetmap.org">OpenStreetMap</a>, under <a href="http://creativecommons.org/licenses/by-sa/3.0">CC BY SA</a>.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Map tiles by <a href="http://www.mapbox.com/m">Mapbox</a> Data by <a href="http://openstreetmap.org">OpenStreetMap</a>, under <a href="http://creativecommons.org/licenses/by-sa/3.0">CC BY SA</a>.
1+
Map tiles by &copy; <a href="http://www.mapbox.com/about/maps">Mapbox</a> Data by &copy; <a href="http://openstreetmap.org">OpenStreetMap</a>, under <a href="http://creativecommons.org/licenses/by-sa/3.0">CC BY SA</a>.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Map tiles by <a href="http://www.mapbox.com/m">Mapbox</a> Data by <a href="http://openstreetmap.org">OpenStreetMap</a>, under <a href="http://creativecommons.org/licenses/by-sa/3.0">CC BY SA</a>.
1+
Map tiles by &copy; <a href="http://www.mapbox.com/about/maps">Mapbox</a> Data by &copy; <a href="http://openstreetmap.org">OpenStreetMap</a>, under <a href="http://creativecommons.org/licenses/by-sa/3.0">CC BY SA</a>.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Data by <a href="http://openstreetmap.org">OpenStreetMap</a>, under <a href="http://www.openstreetmap.org/copyright">ODbL</a>.
1+
Data by &copy; <a href="http://openstreetmap.org">OpenStreetMap</a>, under <a href="http://www.openstreetmap.org/copyright">ODbL</a>.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Map tiles by <a href="http://stamen.com">Stamen Design</a>, under <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a>. Data by <a href="http://openstreetmap.org">OpenStreetMap</a>, under <a href="http://creativecommons.org/licenses/by-sa/3.0">CC BY SA</a>.
1+
Map tiles by <a href="http://stamen.com">Stamen Design</a>, under <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a>. Data by &copy; <a href="http://openstreetmap.org">OpenStreetMap</a>, under <a href="http://creativecommons.org/licenses/by-sa/3.0">CC BY SA</a>.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Map tiles by <a href="http://stamen.com">Stamen Design</a>, under <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a>. Data by <a href="http://openstreetmap.org">OpenStreetMap</a>, under <a href="http://www.openstreetmap.org/copyright">ODbL</a>.
1+
Map tiles by <a href="http://stamen.com">Stamen Design</a>, under <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a>. Data by &copy; <a href="http://openstreetmap.org">OpenStreetMap</a>, under <a href="http://www.openstreetmap.org/copyright">ODbL</a>.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Map tiles by <a href="http://stamen.com">Stamen Design</a>, under <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a>. Data by <a href="http://openstreetmap.org">OpenStreetMap</a>, under <a href="http://www.openstreetmap.org/copyright">ODbL</a>.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
https://stamen-tiles-{s}.a.ssl.fastly.net/toner-background/{z}/{x}/{y}{r}.png
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Map tiles by <a href="http://stamen.com">Stamen Design</a>, under <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a>. Data by <a href="http://openstreetmap.org">OpenStreetMap</a>, under <a href="http://www.openstreetmap.org/copyright">ODbL</a>.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
https://stamen-tiles-{s}.a.ssl.fastly.net/toner-labels/{z}/{x}/{y}{r}.png
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Map tiles by <a href="http://stamen.com">Stamen Design</a>, under <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a>. Data by <a href="http://openstreetmap.org">OpenStreetMap</a>, under <a href="http://creativecommons.org/licenses/by-sa/3.0">CC BY SA</a>.
1+
Map tiles by <a href="http://stamen.com">Stamen Design</a>, under <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a>. Data by &copy; <a href="http://openstreetmap.org">OpenStreetMap</a>, under <a href="http://creativecommons.org/licenses/by-sa/3.0">CC BY SA</a>.

tests/test_folium.py

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@
1313

1414
import folium
1515
from folium.features import GeoJson, Choropleth
16-
from folium.utilities import normalize
1716

1817
import jinja2
1918
from jinja2 import Environment, PackageLoader
19+
from jinja2.utils import htmlsafe_json_dumps
2020

2121
import numpy as np
2222
import pandas as pd
@@ -114,40 +114,48 @@ def test_init(self):
114114
'name': 'TileLayer',
115115
'id': '00000000000000000000000000000000',
116116
'children': {}
117-
}
118117
}
119118
}
119+
}
120120

121121
def test_builtin_tile(self):
122122
"""Test custom maptiles."""
123123

124-
default_tiles = ['OpenStreetMap', 'Stamen Terrain', 'Stamen Toner']
124+
default_tiles = [
125+
"OpenStreetMap",
126+
"Stamen Terrain",
127+
"Stamen Toner",
128+
"Mapbox Bright",
129+
"Mapbox Control Room",
130+
"CartoDB positron",
131+
"CartoDB dark_matter",
132+
]
125133
for tiles in default_tiles:
126134
m = folium.Map(location=[45.5236, -122.6750], tiles=tiles)
127-
tiles = ''.join(tiles.lower().strip().split())
128-
url = 'tiles/{}/tiles.txt'.format
129-
attr = 'tiles/{}/attr.txt'.format
135+
tiles = "".join(tiles.lower().strip().split())
136+
url = "tiles/{}/tiles.txt".format
137+
attr = "tiles/{}/attr.txt".format
130138
url = m._env.get_template(url(tiles)).render()
131139
attr = m._env.get_template(attr(tiles)).render()
132140

133141
assert m._children[tiles].tiles == url
134-
assert m._children[tiles].attr == attr
142+
assert htmlsafe_json_dumps(attr) in m._parent.render()
135143

136144
bounds = m.get_bounds()
137145
assert bounds == [[None, None], [None, None]], bounds
138146

139147
def test_custom_tile(self):
140148
"""Test custom tile URLs."""
141149

142-
url = 'http://{s}.custom_tiles.org/{z}/{x}/{y}.png'
143-
attr = 'Attribution for custom tiles'
150+
url = "http://{s}.custom_tiles.org/{z}/{x}/{y}.png"
151+
attr = "Attribution for custom tiles"
144152

145153
with pytest.raises(ValueError):
146154
folium.Map(location=[45.5236, -122.6750], tiles=url)
147155

148156
m = folium.Map(location=[45.52, -122.67], tiles=url, attr=attr)
149157
assert m._children[url].tiles == url
150-
assert m._children[url].attr == attr
158+
assert attr in m._parent.render()
151159

152160
bounds = m.get_bounds()
153161
assert bounds == [[None, None], [None, None]], bounds

tests/test_map.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import pytest
1010

1111
from folium import Map
12-
from folium.map import Popup, Icon
12+
from folium.map import Popup, Icon, CustomPane
1313
from folium.utilities import normalize
1414

1515

@@ -98,6 +98,19 @@ def test_icon_valid_marker_colors():
9898
assert len(record) == 0
9999

100100

101+
def test_custom_pane_show():
102+
m = Map()
103+
pane = CustomPane('test-name', z_index=625, pointer_events=False).add_to(m)
104+
rendered = pane._template.module.script(this=pane, kwargs={})
105+
expected = """
106+
var {pane_name} = {map_name}.createPane("test-name");
107+
{pane_name}.style.zIndex = 625;
108+
{pane_name}.style.pointerEvents = 'none';
109+
""".format(pane_name=pane.get_name(),
110+
map_name=m.get_name())
111+
assert normalize(rendered) == normalize(expected)
112+
113+
101114
@pytest.mark.filterwarnings('ignore::UserWarning')
102115
def test_icon_invalid_marker_colors():
103116
pytest.warns(UserWarning, Icon, color='lila')

0 commit comments

Comments
 (0)