Skip to content

Commit 37e06b8

Browse files
committed
Add support for leaflet div markers to folium
This allows us to generate polylines that display intermediate points with popups. Updated the tests as well. Tests pass. bash-3.2$ PYTHONPATH=./tests ipython Python 2.7.10 |Anaconda 2.0.1 (x86_64)| (default, May 28 2015, 17:04:42) Type "copyright", "credits" or "license" for more information. IPython 2.1.0 -- An enhanced Interactive Python. Anaconda is brought to you by Continuum Analytics. Please check out: http://continuum.io/thanks and https://binstar.org ? -> Introduction and overview of IPython's features. %quickref -> Quick reference. help -> Python's own help system. object? -> Details about 'object', use 'object??' for extra details. In [1]: import folium_tests In [2]: tester = folium_tests.testFolium() In [3]: tester.setup() In [4]: tester.test_div_markers()
1 parent accaa21 commit 37e06b8

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)