Skip to content

Commit 032a7b0

Browse files
committed
Merge pull request #1 from shankari/master
Add support for leaflet div markers to folium
2 parents accaa21 + 37e06b8 commit 032a7b0

File tree

4 files changed

+100
-0
lines changed

4 files changed

+100
-0
lines changed

folium/folium.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ def __init__(self, location=None, width='100%', height='100%',
192192
'stamenterrain', 'stamentoner',
193193
'stamenwatercolor',
194194
'cartodbpositron', 'cartodbdark_matter']
195+
195196
self.tile_types = {}
196197
for tile in self.default_tiles:
197198
tile_path = 'tiles/%s' % tile
@@ -357,6 +358,58 @@ def simple_marker(self, location=None, popup=None,
357358
append = (icon, marker, popup_out, add_mark)
358359
self.template_vars.setdefault(name, []).append(append)
359360

361+
@iter_obj('div_mark')
362+
def div_markers(self, locations=None, popups=None, marker_size=10, popup_width=300):
363+
"""Create a simple div marker on the map, with optional
364+
popup text or Vincent visualization. Useful for marking points along a
365+
line.
366+
367+
Parameters
368+
----------
369+
locations: list of locations, where each location is an array
370+
Latitude and Longitude of Marker (Northing, Easting)
371+
popup: list of popups, each popup should be a string or tuple, default 'Pop Text'
372+
Input text or visualization for object. Can pass either text,
373+
or a tuple of the form (Vincent object, 'vis_path.json')
374+
It is possible to adjust the width of text/HTML popups
375+
using the optional keywords `popup_width`. (Leaflet default is 300px.)
376+
marker_size
377+
default is 5
378+
379+
Returns
380+
-------
381+
Marker names and HTML in obj.template_vars
382+
383+
Example
384+
-------
385+
>>>map.div_markers(locations=[[37.421114, -122.128314], [37.391637, -122.085416], [37.388832, -122.087709]], popups=['1437494575531', '1437492135937', '1437493590434'])
386+
387+
"""
388+
call_cnt = self.mark_cnt['div_mark']
389+
if locations is None or popups is None:
390+
raise RuntimeError("Both locations and popups are mandatory")
391+
for (point_cnt, (location, popup)) in enumerate(zip(locations, popups)):
392+
marker_num = 'div_marker_{0}_{1}'.format(call_cnt, point_cnt)
393+
394+
icon_temp = self.env.get_template('static_div_icon.js')
395+
icon_name = marker_num+"_icon"
396+
icon = icon_temp.render({'icon_name': icon_name,
397+
'size': marker_size})
398+
399+
mark_temp = self.env.get_template('simple_marker.js')
400+
# Get marker and popup.
401+
marker = mark_temp.render({'marker': marker_num,
402+
'lat': location[0],
403+
'lon': location[1],
404+
'icon': "{'icon':"+icon_name+"}"
405+
})
406+
407+
popup_out = self._popup_render(popup=popup, mk_name='div_marker_{0}_'.format(call_cnt),
408+
count=point_cnt, width=popup_width)
409+
add_mark = 'map.addLayer(div_marker_{0}_{1})'.format(call_cnt, point_cnt)
410+
append = (icon, marker, popup_out, add_mark)
411+
self.template_vars.setdefault('div_markers', []).append(append)
412+
360413
@iter_obj('line')
361414
def line(self, locations,
362415
line_color=None, line_opacity=None, line_weight=None,

folium/templates/fol_template.html

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,13 @@
135135
{{ add_mark }}
136136
{% endfor %}
137137

138+
{% for icon, mark, popup, add_mark in div_markers %}
139+
{{ icon }}
140+
{{ mark }}
141+
{{ popup }}
142+
{{ add_mark }}
143+
{% endfor %}
144+
138145
{% for mark, popup, add_mark in markers %}
139146
{{ mark }}
140147
{{ popup }}

folium/templates/static_div_icon.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
var {{icon_name}} = L.divIcon({className: 'leaflet-div-icon',
2+
'iconSize': [{{size}},{{size}}]});

tests/folium_tests.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,44 @@ def test_simple_marker(self):
169169
nopopup = ''
170170
assert self.map.template_vars['custom_markers'][2][2] == nopopup
171171

172+
def test_div_markers(self):
173+
'''Test div marker list addition'''
174+
175+
icon_templ = self.env.get_template('static_div_icon.js')
176+
mark_templ = self.env.get_template('simple_marker.js')
177+
popup_templ = self.env.get_template('simple_popup.js')
178+
179+
# Test with popups (expected use case)
180+
self.map.div_markers(locations=[[37.421114, -122.128314], [37.391637, -122.085416], [37.388832, -122.087709]], popups=['1437494575531', '1437492135937', '1437493590434'])
181+
icon_1 = icon_templ.render({'icon_name': 'div_marker_1_0_icon', 'size': 10})
182+
mark_1 = mark_templ.render({'marker': 'div_marker_1_0', 'lat': 37.421114,
183+
'lon': -122.128314,
184+
'icon': "{'icon':div_marker_1_0_icon}"})
185+
popup_1 = popup_templ.render({'pop_name': 'div_marker_1_0',
186+
'pop_txt': '"1437494575531"',
187+
'width': 300})
188+
nt.assert_equals(self.map.mark_cnt['div_mark'], 1)
189+
nt.assert_equals(self.map.template_vars['div_markers'][0][0], icon_1)
190+
nt.assert_equals(self.map.template_vars['div_markers'][0][1], mark_1)
191+
nt.assert_equals(self.map.template_vars['div_markers'][0][2], popup_1)
192+
193+
# Second set of markers with popups to test the numbering
194+
self.map.div_markers(locations=[[37.421114, -122.128314], [37.391637, -122.085416], [37.388832, -122.087709]], popups=['1437494575531', '1437492135937', '1437493590434'])
195+
icon_2 = icon_templ.render({'icon_name': 'div_marker_2_1_icon', 'size': 10})
196+
mark_2 = mark_templ.render({'marker': 'div_marker_2_1', 'lat': 37.391637,
197+
'lon': -122.085416,
198+
'icon': "{'icon':div_marker_2_1_icon}"})
199+
popup_2 = popup_templ.render({'pop_name': 'div_marker_2_1',
200+
'pop_txt': '"1437492135937"',
201+
'width': 300})
202+
nt.assert_equals(self.map.mark_cnt['div_mark'], 2)
203+
nt.assert_equals(self.map.template_vars['div_markers'][4][0], icon_2)
204+
nt.assert_equals(self.map.template_vars['div_markers'][4][1], mark_2)
205+
nt.assert_equals(self.map.template_vars['div_markers'][4][2], popup_2)
206+
207+
# Test no popup. If there are no popups, then we should get a runtimeerror.
208+
nt.assert_raises(RuntimeError, self.map.div_markers, [[45.60, -122.8]])
209+
172210
def test_circle_marker(self):
173211
"""Test circle marker additions."""
174212

0 commit comments

Comments
 (0)