Skip to content

Commit a5d81e9

Browse files
authored
Merge pull request #417 from matsuken92/develop
Add Rectangle Marker and Polygon.
2 parents ec58d0d + 0fd90e3 commit a5d81e9

File tree

6 files changed

+211
-3
lines changed

6 files changed

+211
-3
lines changed

folium/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -725,6 +725,110 @@ def __init__(self, location, radius=500, color='black',
725725
""")
726726

727727

728+
class RectangleMarker(Marker):
729+
def __init__(self, bounds, color='black', weight=1, fill_color='black',
730+
fill_opacity=0.6, popup=None):
731+
"""Creates a RectangleMarker object for plotting on a Map.
732+
733+
Parameters
734+
----------
735+
bounds: tuple or list, default None
736+
Latitude and Longitude of Marker (southWest and northEast)
737+
color: string, default ('black')
738+
Edge color of a rectangle.
739+
weight: float, default (1)
740+
Edge line width of a rectangle.
741+
fill_color: string, default ('black')
742+
Fill color of a rectangle.
743+
fill_opacity: float, default (0.6)
744+
Fill opacity of a rectangle.
745+
popup: string or folium.Popup, default None
746+
Input text or visualization for object.
747+
748+
Returns
749+
-------
750+
folium.features.RectangleMarker object
751+
752+
Example
753+
-------
754+
>>> RectangleMarker(bounds=[[35.681, 139.766], [35.691, 139.776]],
755+
color="blue", fill_color="red", popup='Tokyo, Japan')
756+
"""
757+
super(RectangleMarker, self).__init__(bounds, popup=popup)
758+
self._name = 'RectangleMarker'
759+
self.color = color
760+
self.weight = weight
761+
self.fill_color = fill_color
762+
self.fill_opacity = fill_opacity
763+
self._template = Template(u"""
764+
{% macro script(this, kwargs) %}
765+
var {{this.get_name()}} = L.rectangle([[{{this.location[0]}}, {{this.location[1]}}],
766+
[{{this.location[2]}}, {{this.location[3]}}]],
767+
{
768+
color:'{{ this.color }}',
769+
fillColor:'{{ this.fill_color }}',
770+
fillOpacity:{{ this.fill_opacity }},
771+
weight:{{ this.weight }}
772+
}).addTo({{this._parent.get_name()}});
773+
774+
{% endmacro %}
775+
""")
776+
777+
778+
class Polygon(Marker):
779+
def __init__(self, locations, color='black', weight=1, fill_color='black',
780+
fill_opacity=0.6, popup=None, latlon=True):
781+
"""Creates a Polygon object for plotting on a Map.
782+
783+
Parameters
784+
----------
785+
locations: tuple or list, default None
786+
Latitude and Longitude of Polygon
787+
color: string, default ('black')
788+
Edge color of a polygon.
789+
weight: float, default (1)
790+
Edge line width of a polygon.
791+
fill_color: string, default ('black')
792+
Fill color of a polygon.
793+
fill_opacity: float, default (0.6)
794+
Fill opacity of a polygon.
795+
popup: string or folium.Popup, default None
796+
Input text or visualization for object.
797+
798+
Returns
799+
-------
800+
folium.features.Polygon object
801+
802+
Example
803+
-------
804+
>>> loc= [[35.6762, 139.7795],
805+
[35.6718, 139.7831],
806+
[35.6767, 139.7868],
807+
[35.6795, 139.7824],
808+
[35.6787, 139.7791]]
809+
Polygon(loc, color="blue", weight=10, fill_color="red",
810+
fill_opacity=0.5, popup="Tokyo, Japan"))
811+
"""
812+
super(Polygon, self).__init__((_locations_mirror(locations) if not latlon else
813+
_locations_tolist(locations)), popup=popup)
814+
self._name = 'Polygon'
815+
self.color = color
816+
self.weight = weight
817+
self.fill_color = fill_color
818+
self.fill_opacity = fill_opacity
819+
self._template = Template(u"""
820+
{% macro script(this, kwargs) %}
821+
var {{this.get_name()}} = L.polygon({{this.location}},
822+
{
823+
color: '{{ this.color }}',
824+
fillColor: '{{ this.fill_color }}',
825+
fillOpacity: {{ this.fill_opacity }},
826+
weight: {{ this.weight }}
827+
}).addTo({{this._parent.get_name()}});
828+
{% endmacro %}
829+
""")
830+
831+
728832
class LatLngPopup(MacroElement):
729833
"""
730834
When one clicks on a Map that contains a LatLngPopup,

folium/folium.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
from branca.six import text_type, binary_type
1818

1919
from .map import LegacyMap, Icon, Marker, Popup, FitBounds
20-
from .features import (WmsTileLayer, RegularPolygonMarker, Vega, GeoJson,
21-
CircleMarker, LatLngPopup,
20+
from .features import (WmsTileLayer, RegularPolygonMarker, Vega,
21+
GeoJson, CircleMarker, 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+
});

folium/templates/rectangle_marker.js

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: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
import branca.element
2626

2727
from folium.map import Popup, Marker, FitBounds, FeatureGroup
28-
from folium.features import GeoJson, TopoJson, PolyLine, MultiPolyLine
28+
from folium.features import GeoJson, TopoJson, PolyLine, MultiPolyLine, RectangleMarker, Polygon
2929
from folium.plugins import ImageOverlay
3030

3131
rootpath = os.path.abspath(os.path.dirname(__file__))
@@ -238,6 +238,93 @@ 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+
def test_rectangle_marker(self):
242+
"""Test rectangle marker additions."""
243+
244+
self.map = folium.Map(location=[45.60, -122.8])
245+
rect_templ = self.env.get_template('rectangle_marker.js')
246+
247+
# Single Rectangle marker.
248+
bounds = [45.60, -122.8, 45.61, -122.7]
249+
self.map.add_child(RectangleMarker(bounds=bounds, 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+
bounds = [45.70, -122.9, 45.75, -122.5]
262+
self.map.add_child(RectangleMarker(bounds=bounds, popup='Hi'))
263+
marker = list(self.map._children.values())[-1]
264+
rect_2 = rect_templ.render({'RectangleMarker': marker.get_name(),
265+
'location': [45.70, -122.9, 45.75, -122.5],
266+
'color': 'black',
267+
'fill_color': 'black',
268+
'fill_opacity': 0.6,
269+
'weight': 1})
270+
assert (''.join(rect_2.split())[:-1] in
271+
''.join(self.map.get_root().render().split()))
272+
273+
bounds = self.map.get_bounds()
274+
assert bounds == [[45.6, -122.9], [45.7, -122.8]], bounds
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(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(Polygon(locations=locations, color='red',
312+
fill_color='red', fill_opacity=0.7,
313+
weight=3, popup='Hi'))
314+
marker = list(self.map._children.values())[-1]
315+
polygon_2 = polygon_templ.render({'Polygon': marker.get_name(),
316+
'location': locations,
317+
'color': 'red',
318+
'fill_color': 'red',
319+
'fill_opacity': 0.7,
320+
'weight': 3})
321+
assert (''.join(polygon_2.split())[:-1] in
322+
''.join(self.map.get_root().render().split()))
323+
324+
bounds = self.map.get_bounds()
325+
assert bounds == [[[35.5636, 138.7634], [35.5629, 138.7664]],
326+
[[35.6636, 139.7634], [35.6629, 139.7664]]], bounds
327+
241328
def test_poly_marker(self):
242329
"""Test polygon marker."""
243330

0 commit comments

Comments
 (0)