Skip to content

Commit 0dd7697

Browse files
committed
Add Rectangle Marker and Polygon.
1 parent eb51514 commit 0dd7697

6 files changed

Lines changed: 207 additions & 2 deletions

File tree

folium/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from branca.colormap import (ColorMap, LinearColormap, StepColormap)
88

99

10-
from folium.folium import Map, initialize_notebook, CircleMarker
10+
from folium.folium import Map, initialize_notebook, CircleMarker, RectangleMarker, Polygon
1111
from folium.map import (FeatureGroup, FitBounds, Icon, LayerControl, Marker,
1212
Popup, TileLayer)
1313
from folium.features import (ClickForMarker, CustomIcon, DivIcon,
@@ -32,6 +32,8 @@
3232
'Map',
3333
'initialize_notebook',
3434
'CircleMarker',
35+
'RectangleMarker',
36+
'Polygon',
3537
'FeatureGroup',
3638
'FitBounds',
3739
'Icon',

folium/features.py

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,108 @@ def __init__(self, location, radius=500, color='black',
662662
""")
663663

664664

665+
class RectangleMarker(Marker):
666+
def __init__(self, bounds, color='black', weight=1, fill_color='black',
667+
fill_opacity=0.6, popup=None):
668+
"""Creates a RectangleMarker object for plotting on a Map.
669+
670+
Parameters
671+
----------
672+
bounds: tuple or list, default None
673+
Latitude and Longitude of Marker (southWest and northEast)
674+
color: string, default ('black')
675+
Edge color of a rectangle.
676+
weight: float, default (1)
677+
Edge line width of a rectangle.
678+
fill_color: string, default ('black')
679+
Fill color of a rectangle.
680+
fill_opacity: float, default (0.6)
681+
Fill opacity of a rectangle.
682+
popup: string or folium.Popup, default None
683+
Input text or visualization for object.
684+
685+
Returns
686+
-------
687+
folium.features.RectangleMarker object
688+
689+
Example
690+
-------
691+
>>> RectangleMarker(bounds=[[35.681, 139.766], [35.691, 139.776]], color="blue", fill_color="red", popup='Tokyo, Japan')
692+
"""
693+
super(RectangleMarker, self).__init__(bounds, popup=popup)
694+
self._name = 'RectangleMarker'
695+
self.color = color
696+
self.weight = weight
697+
self.fill_color = fill_color
698+
self.fill_opacity = fill_opacity
699+
self._template = Template(u"""
700+
{% macro script(this, kwargs) %}
701+
var {{this.get_name()}} = L.rectangle([[{{this.location[0]}}, {{this.location[1]}}],
702+
[{{this.location[2]}}, {{this.location[3]}}]],
703+
{
704+
color:'{{ this.color }}',
705+
fillColor:'{{ this.fill_color }}',
706+
fillOpacity:{{ this.fill_opacity }},
707+
weight:{{ this.weight }}
708+
}).addTo({{this._parent.get_name()}});
709+
710+
{% endmacro %}
711+
""")
712+
713+
714+
class Polygon(Marker):
715+
def __init__(self, locations, color='black', weight=1, fill_color='black',
716+
fill_opacity=0.6, popup=None, latlon=True):
717+
"""Creates a Polygon object for plotting on a Map.
718+
719+
Parameters
720+
----------
721+
locations: tuple or list, default None
722+
Latitude and Longitude of Polygon
723+
color: string, default ('black')
724+
Edge color of a polygon.
725+
weight: float, default (1)
726+
Edge line width of a polygon.
727+
fill_color: string, default ('black')
728+
Fill color of a polygon.
729+
fill_opacity: float, default (0.6)
730+
Fill opacity of a polygon.
731+
popup: string or folium.Popup, default None
732+
Input text or visualization for object.
733+
734+
Returns
735+
-------
736+
folium.features.Polygon object
737+
738+
Example
739+
-------
740+
>>> loc= [[35.6762, 139.7795],
741+
[35.6718, 139.7831],
742+
[35.6767, 139.7868],
743+
[35.6795, 139.7824],
744+
[35.6787, 139.7791]]
745+
Polygon(loc, color="blue", weight=10, fill_color="red",
746+
fill_opacity=0.5, popup="Tokyo, Japan"))
747+
"""
748+
super(Polygon, self).__init__((_locations_mirror(locations) if not latlon else
749+
_locations_tolist(locations)), popup=popup)
750+
self._name = 'Polygon'
751+
self.color = color
752+
self.weight = weight
753+
self.fill_color = fill_color
754+
self.fill_opacity = fill_opacity
755+
self._template = Template(u"""
756+
{% macro script(this, kwargs) %}
757+
var {{this.get_name()}} = L.polygon({{this.location}},
758+
{
759+
color: '{{ this.color }}',
760+
fillColor: '{{ this.fill_color }}',
761+
fillOpacity: {{ this.fill_opacity }},
762+
weight: {{ this.weight }}
763+
}).addTo({{this._parent.get_name()}});
764+
{% endmacro %}
765+
""")
766+
665767
class LatLngPopup(MacroElement):
666768
"""
667769
When one clicks on a Map that contains a LatLngPopup,

folium/folium.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
from .map import LegacyMap, Icon, Marker, Popup, FitBounds
2020
from .features import (WmsTileLayer, RegularPolygonMarker, Vega, GeoJson,
21-
CircleMarker, LatLngPopup,
21+
CircleMarker, RectangleMarker, Polygon, LatLngPopup,
2222
ClickForMarker, TopoJson, PolyLine, MultiPolyLine,
2323
)
2424

folium/templates/polygon.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
var {{ Polygon }} = L.polygon({{location}},
2+
{
3+
color:'{{ color }}',
4+
fillColor:'{{ fill_color }}',
5+
fillOpacity:{{ fill_opacity }},
6+
weight:{{ weight }}
7+
});
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
var {{ RectangleMarker }} = L.rectangle([[{{location[0]}}, {{location[1]}}],
2+
[{{location[2]}}, {{location[3]}}]],
3+
{
4+
color:'{{ color }}',
5+
fillColor:'{{ fill_color }}',
6+
fillOpacity:{{ fill_opacity }},
7+
weight:{{ weight }}
8+
});

tests/test_folium.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,92 @@ def test_circle_marker(self):
238238
bounds = self.map.get_bounds()
239239
assert bounds == [[45.6, -122.9], [45.7, -122.8]], bounds
240240

241+
242+
def test_rectangle_marker(self):
243+
"""Test rectangle marker additions."""
244+
245+
self.map = folium.Map(location=[45.60, -122.8])
246+
rect_templ = self.env.get_template('rectangle_marker.js')
247+
248+
# Single Rectangle marker.
249+
self.map.add_child(folium.RectangleMarker(bounds=[45.60, -122.8, 45.61, -122.7], popup='Hi'))
250+
marker = list(self.map._children.values())[-1]
251+
rect_1 = rect_templ.render({'RectangleMarker': marker.get_name(),
252+
'location': [45.60, -122.8,45.61, -122.7],
253+
'color': 'black',
254+
'fill_color': 'black',
255+
'fill_opacity': 0.6,
256+
'weight': 1})
257+
assert (''.join(rect_1.split())[:-1] in
258+
''.join(self.map.get_root().render().split()))
259+
260+
# Second Rectangle marker.
261+
self.map.add_child(folium.RectangleMarker(bounds=[45.70, -122.9, 45.75, -122.5], popup='Hi'))
262+
marker = list(self.map._children.values())[-1]
263+
rect_2 = rect_templ.render({'RectangleMarker': marker.get_name(),
264+
'location': [45.70, -122.9,45.75, -122.5],
265+
'color': 'black',
266+
'fill_color': 'black',
267+
'fill_opacity': 0.6,
268+
'weight': 1})
269+
assert (''.join(rect_2.split())[:-1] in
270+
''.join(self.map.get_root().render().split()))
271+
272+
bounds = self.map.get_bounds()
273+
assert bounds == [[45.6, -122.9], [45.7, -122.8]], bounds
274+
275+
276+
def test_polygon(self):
277+
"""Test polygon additions."""
278+
279+
self.map = folium.Map(location=[45.60, -122.8])
280+
polygon_templ = self.env.get_template('polygon.js')
281+
282+
# Single Polygon.
283+
locations=[[35.6636, 139.7634],
284+
[35.6629, 139.7664],
285+
[35.6663, 139.7706],
286+
[35.6725, 139.7632],
287+
[35.6728, 139.7627],
288+
[35.6720, 139.7606],
289+
[35.6682, 139.7588],
290+
[35.6663, 139.7627]]
291+
self.map.add_child(folium.Polygon(locations=locations, popup='Hi'))
292+
marker = list(self.map._children.values())[-1]
293+
polygon_1 = polygon_templ.render({'Polygon': marker.get_name(),
294+
'location': locations,
295+
'color': 'black',
296+
'fill_color': 'black',
297+
'fill_opacity': 0.6,
298+
'weight': 1})
299+
assert (''.join(polygon_1.split())[:-1] in
300+
''.join(self.map.get_root().render().split()))
301+
302+
# Second Polygon.
303+
locations=[[35.5636, 138.7634],
304+
[35.5629, 138.7664],
305+
[35.5663, 138.7706],
306+
[35.5725, 138.7632],
307+
[35.5728, 138.7627],
308+
[35.5720, 138.7606],
309+
[35.5682, 138.7588],
310+
[35.5663, 138.7627]]
311+
self.map.add_child(folium.Polygon(locations=locations, color='red', fill_color='red',
312+
fill_opacity=0.7, weight=3, popup='Hi'))
313+
marker = list(self.map._children.values())[-1]
314+
polygon_2 = polygon_templ.render({'Polygon': marker.get_name(),
315+
'location': locations,
316+
'color': 'red',
317+
'fill_color': 'red',
318+
'fill_opacity': 0.7,
319+
'weight': 3})
320+
assert (''.join(polygon_2.split())[:-1] in
321+
''.join(self.map.get_root().render().split()))
322+
323+
bounds = self.map.get_bounds()
324+
assert bounds == [[[35.5636, 138.7634], [35.5629, 138.7664]], [[35.6636, 139.7634], [35.6629, 139.7664]]], bounds
325+
326+
241327
def test_poly_marker(self):
242328
"""Test polygon marker."""
243329

0 commit comments

Comments
 (0)