Skip to content

Commit 0d2eece

Browse files
author
Martin Journois
committed
Fix test_multi_polyline
1 parent 00a1dfe commit 0d2eece

5 files changed

Lines changed: 127 additions & 60 deletions

File tree

folium/features.py

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from jinja2 import Template
99
import json
1010

11-
from .utilities import color_brewer, _parse_size, legend_scaler
11+
from .utilities import color_brewer, _parse_size, legend_scaler, _locations_mirror, _locations_tolist
1212

1313
from .element import Element, Figure, JavascriptLink, CssLink, Div, MacroElement
1414
from .map import Map, TileLayer, Icon, Marker, Popup
@@ -479,20 +479,10 @@ def __init__(self, locations, color=None, weight=None, opacity=None, latlon=True
479479
Whether locations are given in the form [[lat,lon]] or not ([[lon,lat]] if False).
480480
Note that the default GeoJson format is latlon=False,
481481
while Leaflet polyline's default is latlon=True.
482-
483-
examples :
484-
# providing file
485-
GeoJson(open('foo.json'))
486-
487-
# providing dict
488-
GeoJson(json.load(open('foo.json')))
489-
490-
# providing string
491-
GeoJson(open('foo.json').read())
492482
"""
493483
super(PolyLine, self).__init__()
494484
self._name = 'PolyLine'
495-
self.data = [[x[1],x[0]] for x in locations] if not latlon else locations
485+
self.data = _locations_mirror(locations) if not latlon else _locations_tolist(locations)
496486
self.color = color
497487
self.weight = weight
498488
self.opacity = opacity
@@ -509,3 +499,39 @@ def __init__(self, locations, color=None, weight=None, opacity=None, latlon=True
509499
{{this._parent.get_name()}}.addLayer({{this.get_name()}});
510500
{% endmacro %}
511501
""")
502+
503+
class MultiPolyLine(MacroElement):
504+
def __init__(self, locations, color=None, weight=None, opacity=None, latlon=True):
505+
"""Creates a MultiPolyLine object to append into a map with Map.add_children.
506+
507+
Parameters
508+
----------
509+
locations: list of points (latitude, longitude)
510+
Latitude and Longitude of line (Northing, Easting)
511+
color: string, default Leaflet's default ('#03f')
512+
weight: float, default Leaflet's default (5)
513+
opacity: float, default Leaflet's default (0.5)
514+
latlon: bool, default True
515+
Whether locations are given in the form [[lat,lon]] or not ([[lon,lat]] if False).
516+
Note that the default GeoJson format is latlon=False,
517+
while Leaflet polyline's default is latlon=True.
518+
"""
519+
super(MultiPolyLine, self).__init__()
520+
self._name = 'MultiPolyLine'
521+
self.data = _locations_mirror(locations) if not latlon else _locations_tolist(locations)
522+
self.color = color
523+
self.weight = weight
524+
self.opacity = opacity
525+
526+
self._template = Template(u"""
527+
{% macro script(this, kwargs) %}
528+
var {{this.get_name()}} = L.multiPolyline(
529+
{{this.data}},
530+
{
531+
{% if this.color != None %}color: '{{ this.color }}',{% endif %}
532+
{% if this.weight != None %}weight: {{ this.weight }},{% endif %}
533+
{% if this.opacity != None %}opacity: {{ this.opacity }},{% endif %}
534+
});
535+
{{this._parent.get_name()}}.addLayer({{this.get_name()}});
536+
{% endmacro %}
537+
""")

folium/folium.py

Lines changed: 55 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
from .element import Element, Figure, JavascriptLink, CssLink, Div, MacroElement
2727
from .map import Map, TileLayer, Icon, Marker, Popup
2828
from .features import WmsTileLayer, RegularPolygonMarker, Vega, GeoJson, GeoJsonStyle, MarkerCluster, DivIcon,\
29-
CircleMarker, LatLngPopup, ClickForMarker, ColorScale, TopoJson, PolyLine
29+
CircleMarker, LatLngPopup, ClickForMarker, ColorScale, TopoJson, PolyLine, MultiPolyLine
3030
from .utilities import color_brewer
3131
#import sys
3232
#import base64
@@ -423,38 +423,60 @@ def line(self, locations,
423423
p.add_children(Popup(popup, max_width=popup_width))
424424

425425
self.add_children(p)
426-
# @iter_obj('multiline')
427-
# def multiline(self, locations, line_color=None, line_opacity=None,
428-
# line_weight=None):
429-
# """Add a multiPolyline to the map with optional styles.
430-
#
431-
# A multiPolyline is single layer that consists of several polylines that
432-
# share styling/popup.
433-
#
434-
# Parameters
435-
# ----------
436-
# locations: list of lists of points (latitude, longitude)
437-
# Latitude and Longitude of line (Northing, Easting)
438-
# line_color: string, default Leaflet's default ('#03f')
439-
# line_opacity: float, default Leaflet's default (0.5)
440-
# line_weight: float, default Leaflet's default (5)
441-
#
442-
# Note: If the optional styles are omitted, they will not be included
443-
# in the HTML output and will obtain the Leaflet defaults listed above.
444-
#
445-
# Example
446-
# -------
447-
# # FIXME: Add another example.
448-
# >>> m.multiline(locations=[[(45.5236, -122.675), (45.5236, -122.675)],
449-
# [(45.5237, -122.675), (45.5237, -122.675)],
450-
# [(45.5238, -122.675), (45.5238, -122.675)]])
451-
# >>> m.multiline(locations=[[(45.5236, -122.675), (45.5236, -122.675)],
452-
# [(45.5237, -122.675), (45.5237, -122.675)],
453-
# [(45.5238, -122.675), (45.5238, -122.675)]],
454-
# line_color='red', line_weight=2,
455-
# line_opacity=1.0)
456-
# """
457-
#
426+
427+
def multiline(self, locations, line_color=None, line_opacity=None,
428+
line_weight=None,
429+
popup=None, popup_width=300, latlon=True):
430+
"""Add a multiPolyline to the map with optional styles.
431+
432+
A multiPolyline is single layer that consists of several polylines that
433+
share styling/popup.
434+
435+
Parameters
436+
----------
437+
locations: list of lists of points (latitude, longitude)
438+
Latitude and Longitude of line (Northing, Easting)
439+
line_color: string, default Leaflet's default ('#03f')
440+
line_opacity: float, default Leaflet's default (0.5)
441+
line_weight: float, default Leaflet's default (5)
442+
popup: string or tuple, default 'Pop Text'
443+
Input text or visualization for object. Can pass either text,
444+
or a tuple of the form (Vincent object, 'vis_path.json')
445+
It is possible to adjust the width of text/HTML popups
446+
using the optional keywords `popup_width` (default is 300px).
447+
latlon: bool, default True
448+
Whether locations are given in the form [[lat,lon]] or not ([[lon,lat]] if False).
449+
Note that the default GeoJson format is latlon=False,
450+
while Leaflet polyline's default is latlon=True.
451+
452+
Note: If the optional styles are omitted, they will not be included
453+
in the HTML output and will obtain the Leaflet defaults listed above.
454+
455+
Example
456+
-------
457+
# FIXME: Add another example.
458+
>>> m.multiline(locations=[[(45.5236, -122.675), (45.5236, -122.675)],
459+
[(45.5237, -122.675), (45.5237, -122.675)],
460+
[(45.5238, -122.675), (45.5238, -122.675)]])
461+
>>> m.multiline(locations=[[(45.5236, -122.675), (45.5236, -122.675)],
462+
[(45.5237, -122.675), (45.5237, -122.675)],
463+
[(45.5238, -122.675), (45.5238, -122.675)]],
464+
line_color='red', line_weight=2,
465+
line_opacity=1.0)
466+
"""
467+
468+
p = MultiPolyLine(locations,
469+
color=line_color,
470+
weight=line_weight,
471+
opacity=line_opacity,
472+
latlon=latlon,
473+
)
474+
475+
if popup is not None:
476+
p.add_children(Popup(popup, max_width=popup_width))
477+
478+
self.add_children(p)
479+
458480
# count = self.mark_cnt['multiline']
459481
#
460482
# multiline_temp = self.env.get_template('multi_polyline.js')

folium/templates/multi_polyline.js

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,5 @@
1-
var latLngs = [
2-
{% for location in locations %}
3-
[
4-
{% for loc in location %}
5-
[{{ loc[0] }}, {{ loc[1] }}],
6-
{% endfor %}
7-
],
8-
{% endfor %}];
9-
10-
var {{ multiline }} = L.multiPolyline(latLngs,{
1+
var {{ this.get_name() }} = L.multiPolyline({{locations}},
2+
{
113
{% if options.color != None %}color: '{{ options.color }}',{% endif %}
124
{% if options.weight != None %}weight: {{ options.weight }},{% endif %}
135
{% if options.opacity != None %}opacity: {{ options.opacity }},{% endif %}

folium/utilities.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,3 +408,23 @@ def _parse_size(value):
408408
msg = "Cannot parse value {!r} as {!r}".format
409409
raise ValueError(msg(value, value_type))
410410
return value, value_type
411+
412+
def _locations_mirror(x):
413+
"""Mirrors the points in a list-of-list-of-...-of-list-of-points.
414+
For example _locations_mirror([[[1,2],[3,4]],[5,6],[7,8]]) = [[[2, 1], [4, 3]], [6, 5], [8, 7]]
415+
"""
416+
if hasattr(x, '__iter__'):
417+
if hasattr(x[0], '__iter__'):
418+
return list(map(_locations_mirror,x))
419+
else:
420+
return list(x[::-1])
421+
else:
422+
return x
423+
424+
def _locations_tolist(x):
425+
"""Transforms recusively a list of iterables into a list of list.
426+
"""
427+
if hasattr(x, '__iter__'):
428+
return list(map(_locations_tolist,x))
429+
else:
430+
return x

tests/test_folium.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from folium.element import Html
2121
from folium.map import Popup, Marker, Icon
2222
from folium.features import DivIcon, CircleMarker, LatLngPopup, GeoJson,\
23-
GeoJsonStyle, ColorScale, TopoJson, PolyLine
23+
GeoJsonStyle, ColorScale, TopoJson, PolyLine, MultiPolyLine
2424

2525

2626
rootpath = os.path.abspath(os.path.dirname(__file__))
@@ -586,15 +586,22 @@ def test_multi_polyline(self):
586586
locations = [[[45.5236, -122.6750], [45.5236, -122.6751]],
587587
[[45.5237, -122.6750], [45.5237, -122.6751]],
588588
[[45.5238, -122.6750], [45.5238, -122.6751]]]
589-
multiline_rendered = multiline_temp.render({'multiline': 'multiline_1',
590-
'locations': locations,
591-
'options': multiline_opts})
592589

590+
self.setup()
593591
self.map.multiline(locations=locations,
594592
line_color=multiline_opts['color'],
595593
line_weight=multiline_opts['weight'],
596594
line_opacity=multiline_opts['opacity'])
597-
assert self.map.template_vars['multilines'][0][0] == multiline_rendered
595+
multipolyline = [val for key,val in self.map._children.items()\
596+
if isinstance(val,MultiPolyLine)][0]
597+
out = self.map._parent.render()
598+
599+
multiline_rendered = multiline_temp.render({'multiline': 'multiline_1',
600+
'this' : multipolyline,
601+
'locations': locations,
602+
'options': multiline_opts})
603+
604+
assert ''.join(multiline_rendered.split()) in ''.join(out.split())
598605

599606
def test_fit_bounds(self):
600607
"""Test fit_bounds."""

0 commit comments

Comments
 (0)