Skip to content

Commit 1c4d25a

Browse files
committed
Merge pull request #148 from BibMartin/plugins
plugin objects definition, with two examples
2 parents a7fd9b9 + 159337c commit 1c4d25a

File tree

5 files changed

+172
-3
lines changed

5 files changed

+172
-3
lines changed

folium/folium.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,16 @@ def fit_bounds(self, bounds, padding_top_left=None,
652652

653653
self.template_vars.update({'fit_bounds': fit_bounds_str})
654654

655+
def add_plugin(self, plugin):
656+
"""Adds a plugin to the map.
657+
658+
Parameters
659+
----------
660+
plugin: folium.plugins object
661+
A plugin to be added to the map. It has to implement the methods
662+
`render_html`, `render_css` and `render_js`.
663+
"""
664+
self.plugins[plugin.name] = plugin
655665

656666
def _auto_bounds(self):
657667
if 'fit_bounds' in self.template_vars:
@@ -963,7 +973,7 @@ def _build_map(self, html_templ=None, templ_type='string'):
963973
if templ_type == 'string':
964974
html_templ = self.env.from_string(html_templ)
965975

966-
self.HTML = html_templ.render(self.template_vars)
976+
self.HTML = html_templ.render(self.template_vars, plugins=self.plugins)
967977

968978
def create_map(self, path='map.html', plugin_data_out=True, template=None):
969979
"""Write Map output to HTML and data output to JSON if available.

folium/plugins/__init__.py

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,124 @@
11
# -*- coding: utf-8 -*-
2+
3+
from uuid import uuid4
4+
import json
5+
6+
class Plugin():
7+
"""Basic plugin object that does nothing.
8+
Other plugins may inherit from this one."""
9+
def __init__(self):
10+
"""Creates a plugin to append into a map with Map.add_plugin. """
11+
self.name = "Plugin_"+uuid4().hex
12+
13+
def render_html(self):
14+
"""Generates the HTML part of the plugin."""
15+
return ""
16+
17+
def render_css(self):
18+
"""Generates the CSS part of the plugin."""
19+
return ""
20+
21+
def render_js(self):
22+
"""Generates the Javascript part of the plugin."""
23+
return ""
24+
def render_header(self):
25+
"""Generates the Header part of the plugin."""
26+
return ""
27+
28+
29+
class ScrollZoomToggler(Plugin):
30+
"""Adds a button to enable/disable zoom scrolling."""
31+
def __init__(self, zoom_enabled=False):
32+
"""Creates a ScrollZoomToggler plugin to append into a map with
33+
Map.add_plugin.
34+
35+
Parameters
36+
----------
37+
zoom_enabled: bool, default False
38+
Whether the zoom scrolling shall be enabled at display.
39+
"""
40+
self.zoom_enabled = zoom_enabled
41+
self.name = "ScrollZoomToggler_"+uuid4().hex
42+
43+
def render_html(self):
44+
"""Generates the HTML part of the plugin."""
45+
return """<img id="{}" alt="scroll"
46+
src="https://cdnjs.cloudflare.com/ajax/libs/ionicons/1.5.2/png/512/arrow-move.png"
47+
onclick="toggleScroll()"></img>""".format(self.name)
48+
49+
def render_css(self):
50+
"""Generates the CSS part of the plugin."""
51+
return """
52+
#"""+self.name+""" {
53+
position:absolute;
54+
width:35px;
55+
bottom:10px;
56+
height:35px;
57+
left:10px;
58+
background-color:#fff;
59+
text-align:center;
60+
line-height:35px;
61+
vertical-align: middle;
62+
}
63+
"""
64+
65+
def render_js(self):
66+
"""Generates the Javascript part of the plugin."""
67+
out = """
68+
map.scrollEnabled = true;
69+
70+
var toggleScroll = function() {
71+
if (map.scrollEnabled) {
72+
map.scrollEnabled = false;
73+
map.scrollWheelZoom.disable();
74+
}
75+
else {
76+
map.scrollEnabled = true;
77+
map.scrollWheelZoom.enable();
78+
}
79+
};
80+
"""
81+
if not self.zoom_enabled:
82+
out += "\n toggleScroll();"
83+
return out
84+
85+
class MarkerCluster(Plugin):
86+
"""Adds a MarkerCluster layer on the map."""
87+
def __init__(self, data):
88+
"""Creates a MarkerCluster plugin to append into a map with
89+
Map.add_plugin.
90+
91+
Parameters
92+
----------
93+
data: list of list or array of shape (n,3).
94+
Data points of the form [[lat, lng, popup]].
95+
"""
96+
self.data = [tuple(x) for x in data]
97+
self.name = "MarkerCluster_"+uuid4().hex
98+
99+
def render_header(self):
100+
"""Generates the HTML part of the plugin."""
101+
return """
102+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet.markercluster/0.4.0/MarkerCluster.css" />
103+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet.markercluster/0.4.0/MarkerCluster.Default.css" />
104+
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.markercluster/0.4.0/leaflet.markercluster.js"></script>
105+
"""
106+
107+
def render_js(self):
108+
"""Generates the Javascript part of the plugin."""
109+
out = """
110+
var addressPoints = """+json.dumps(self.data)+""";
111+
112+
var markers = L.markerClusterGroup();
113+
114+
for (var i = 0; i < addressPoints.length; i++) {
115+
var a = addressPoints[i];
116+
var title = a[2];
117+
var marker = L.marker(new L.LatLng(a[0], a[1]), { title: title });
118+
marker.bindPopup(title);
119+
markers.addLayer(marker);
120+
}
121+
122+
map.addLayer(markers);
123+
"""
124+
return out

folium/templates/fol_template.html

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@
2323

2424
<link rel="stylesheet" href="https://birdage.github.io/Leaflet.awesome-markers/dist/leaflet.awesome.rotate.css">
2525

26+
{% for name, plugin in plugins.items() %}
27+
{{plugin.render_header()}}
28+
{% endfor %}
29+
2630
{{ dvf_js }}
2731
{{ d3 }}
2832
{{ vega }}
@@ -45,13 +49,21 @@
4549
left:0;
4650
}
4751

52+
{% for name, plugin in plugins.items() %}
53+
{{plugin.render_css()}}
54+
{% endfor %}
55+
4856
</style>
4957
</head>
5058

5159
<body>
5260

5361
<div class="folium-map" id="{{ map_id }}" {{ size }}></div>
5462

63+
{% for name, plugin in plugins.items() %}
64+
{{plugin.render_html()}}
65+
{% endfor %}
66+
5567
<script>
5668

5769
{{ vega_parse }}
@@ -151,6 +163,10 @@
151163

152164
{% if fit_bounds %}{{ fit_bounds }}{% endif %}
153165

166+
{% for name, plugin in plugins.items() %}
167+
{{plugin.render_js()}}
168+
{% endfor %}
169+
154170
</script>
155171

156172
</body>

setup.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ def walk_subpkg(name):
4343
'templates/*.html',
4444
'templates/*.js',
4545
'templates/*.txt'] + walk_subpkg('templates/tiles')}
46+
pkgs = ['folium',
47+
'folium.plugins',
48+
]
4649

4750
LICENSE = read('LICENSE.txt')
4851
version = find_version('folium', '__init__.py')
@@ -62,7 +65,7 @@ def walk_subpkg(name):
6265
'Programming Language :: Python :: 3.3',
6366
'Programming Language :: Python :: 3.4',
6467
'License :: OSI Approved :: MIT License'],
65-
packages=['folium'],
68+
packages=pkgs,
6669
package_data=pkg_data,
6770
zip_safe=False)
6871

tests/folium_tests.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import vincent
1414
import folium
1515
from folium.six import PY3
16+
from folium.plugins import ScrollZoomToggler, MarkerCluster
1617

1718

1819
def setup_data():
@@ -372,7 +373,7 @@ def test_map_build(self):
372373
'max_lat': 90,
373374
'min_lon': -180,
374375
'max_lon': 180}
375-
HTML = html_templ.render(tmpl)
376+
HTML = html_templ.render(tmpl, plugins={})
376377

377378
assert self.map.HTML == HTML
378379

@@ -483,3 +484,19 @@ def test_fit_bounds(self):
483484
self.map.fit_bounds(bounds, max_zoom=15, padding=(3, 3))
484485
assert self.map.template_vars['fit_bounds'] == fit_bounds_rendered
485486

487+
def test_scroll_zoom_toggler_plugin(self):
488+
"test ScrollZoomToggler plugin"""
489+
a_map = folium.Map([45,3], zoom_start=4)
490+
a_map.add_plugin(ScrollZoomToggler(False))
491+
a_map._build_map()
492+
493+
def test_marker_cluster_plugin(self):
494+
"test MarkerCluster plugin"""
495+
data = [(35,-12,"lower left"),
496+
(35, 30,"lower right"),
497+
(60,-12,"upper left"),
498+
(60, 30,"upper right"),
499+
]
500+
a_map = folium.Map([0,0], zoom_start=0)
501+
a_map.add_plugin(MarkerCluster(data))
502+
a_map._build_map()

0 commit comments

Comments
 (0)