Skip to content

Commit 4315bd5

Browse files
committed
Merge pull request #207 from BibMartin/elements
Plug elements in `0.1.*` API
2 parents 725c0bb + 5d665c5 commit 4315bd5

33 files changed

+1492
-1738
lines changed

folium/element.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
ENV = Environment(loader=PackageLoader('folium', 'templates'))
1212
from collections import OrderedDict
1313
import json
14+
import base64
1415

1516
from .six import urlopen
1617
from .utilities import _camelify, _parse_size
@@ -266,7 +267,7 @@ def _repr_html_(self, **kwargs):
266267

267268
iframe = '<iframe src="{html}" width="{width}px" height="{height}px"></iframe>'\
268269
.format(\
269-
html = "data:text/html;base64,"+html.encode('utf8').encode('base64'),
270+
html = b"data:text/html;base64,"+base64.b64encode(html.encode('utf8')),
270271
#html = self.HTML.replace('"','&quot;'),
271272
width = int(60.*width),
272273
height= int(60.*height),

folium/features.py

Lines changed: 349 additions & 27 deletions
Large diffs are not rendered by default.

folium/folium.py

Lines changed: 236 additions & 755 deletions
Large diffs are not rendered by default.

folium/map.py

Lines changed: 98 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
Classes for drawing maps.
77
"""
88
import warnings
9+
import json
10+
from collections import OrderedDict
911

1012
from jinja2 import Template
1113

@@ -85,6 +87,8 @@ def __init__(self, location=None, width='100%', height='100%',
8587
self.location = location
8688
self.zoom_start = zoom_start
8789

90+
Figure().add_children(self)
91+
8892
# Map Size Parameters.
8993
self.width = _parse_size(width)
9094
self.height = _parse_size(height)
@@ -108,6 +112,7 @@ def __init__(self, location=None, width='100%', height='100%',
108112
height: {{this.height[0]}}{{this.height[1]}};
109113
left: {{this.left[0]}}{{this.left[1]}};
110114
top: {{this.top[0]}}{{this.top[1]}};
115+
}
111116
</style>
112117
{% endmacro %}
113118
{% macro html(this, kwargs) %}
@@ -169,7 +174,7 @@ def add_tile_layer(self, tiles='OpenStreetMap', name=None,
169174

170175
class TileLayer(MacroElement):
171176
def __init__(self, tiles='OpenStreetMap', name=None,
172-
min_zoom=1, max_zoom=18, attr=None, API_key=None):
177+
min_zoom=1, max_zoom=18, attr=None, API_key=None, overlay = False):
173178
"""TODO docstring here
174179
Parameters
175180
----------
@@ -181,6 +186,8 @@ def __init__(self, tiles='OpenStreetMap', name=None,
181186
self.min_zoom = min_zoom
182187
self.max_zoom = max_zoom
183188

189+
self.overlay = overlay
190+
184191
self.tiles = ''.join(tiles.lower().strip().split())
185192
if self.tiles in ('cloudmade', 'mapbox') and not API_key:
186193
raise ValueError('You must pass an API key if using Cloudmade'
@@ -197,6 +204,8 @@ def __init__(self, tiles='OpenStreetMap', name=None,
197204
if not attr:
198205
raise ValueError('Custom tiles must'
199206
' also be passed an attribution')
207+
if isinstance(attr, binary_type):
208+
attr = text_type(attr, 'utf8')
200209
self.attr = attr
201210

202211
self._template = Template(u"""
@@ -213,6 +222,42 @@ def __init__(self, tiles='OpenStreetMap', name=None,
213222
{% endmacro %}
214223
""")
215224

225+
class LayerControl(MacroElement):
226+
"""Adds a layer control to the map."""
227+
def __init__(self):
228+
"""Creates a LayerControl object to be added on a folium map.
229+
230+
Parameters
231+
----------
232+
"""
233+
super(LayerControl, self).__init__()
234+
self._name = 'LayerControl'
235+
236+
self.base_layers = OrderedDict()
237+
self.overlays = OrderedDict()
238+
239+
self._template = Template("""
240+
{% macro script(this,kwargs) %}
241+
var {{this.get_name()}} = {
242+
base_layers : { {% for key,val in this.base_layers.items() %}"{{key}}" : {{val}},{% endfor %} },
243+
overlays : { {% for key,val in this.overlays.items() %}"{{key}}" : {{val}},{% endfor %} }
244+
};
245+
L.control.layers(
246+
{{this.get_name()}}.base_layers,
247+
{{this.get_name()}}.overlays
248+
).addTo({{this._parent.get_name()}});
249+
{% endmacro %}
250+
""")
251+
252+
def render(self, **kwargs):
253+
"""TODO : docstring here."""
254+
self.base_layers = OrderedDict([(val.tile_name,val.get_name()) \
255+
for key,val in self._parent._children.items() if isinstance(val,TileLayer) and not val.overlay])
256+
self.overlays = OrderedDict([(val.tile_name,val.get_name()) \
257+
for key,val in self._parent._children.items() if isinstance(val,TileLayer) and val.overlay])
258+
259+
super(LayerControl, self).render()
260+
216261
class Icon(MacroElement):
217262
def __init__(self, color='blue', icon='info-sign', angle=0):
218263
"""TODO : docstring here"""
@@ -265,6 +310,10 @@ def __init__(self, location, popup=None, icon=None):
265310
super(Marker, self).__init__()
266311
self._name = 'Marker'
267312
self.location = location
313+
if icon is not None:
314+
self.add_children(icon)
315+
if popup is not None:
316+
self.add_children(popup)
268317

269318
self._template = Template(u"""
270319
{% macro script(this, kwargs) %}
@@ -292,7 +341,7 @@ def __init__(self, html, max_width=300):
292341
self.script._parent = self
293342

294343
if isinstance(html, Element):
295-
self.html.add_children(html)
344+
self.add_children(html)
296345
elif isinstance(html, text_type) or isinstance(html,binary_type):
297346
self.html.add_children(Html(text_type(html)))
298347

@@ -324,3 +373,50 @@ def render(self, **kwargs):
324373

325374
figure.script.add_children(Element(\
326375
self._template.render(this=self, kwargs=kwargs)), name=self.get_name())
376+
377+
class FitBounds(MacroElement):
378+
def __init__(self, bounds, padding_top_left=None,
379+
padding_bottom_right=None, padding=None, max_zoom=None):
380+
"""Fit the map to contain a bounding box with the maximum zoom level possible.
381+
382+
Parameters
383+
----------
384+
bounds: list of (latitude, longitude) points
385+
Bounding box specified as two points [southwest, northeast]
386+
padding_top_left: (x, y) point, default None
387+
Padding in the top left corner. Useful if some elements in
388+
the corner, such as controls, might obscure objects you're zooming
389+
to.
390+
padding_bottom_right: (x, y) point, default None
391+
Padding in the bottom right corner.
392+
padding: (x, y) point, default None
393+
Equivalent to setting both top left and bottom right padding to
394+
the same value.
395+
max_zoom: int, default None
396+
Maximum zoom to be used.
397+
398+
"""
399+
super(FitBounds, self).__init__()
400+
self._name = 'FitBounds'
401+
self.bounds = json.loads(json.dumps(bounds))
402+
options = {
403+
'maxZoom': max_zoom,
404+
'paddingTopLeft': padding_top_left,
405+
'paddingBottomRight': padding_bottom_right,
406+
'padding': padding,
407+
}
408+
self.fit_bounds_options = json.dumps({key:val for key,val in options.items() if val},
409+
sort_keys=True)
410+
411+
self._template = Template(u"""
412+
{% macro script(this, kwargs) %}
413+
{% if this.autobounds %}
414+
var autobounds = L.featureGroup({{ this.features }}).getBounds()
415+
{% endif %}
416+
417+
{{this._parent.get_name()}}.fitBounds(
418+
{% if this.bounds %}{{ this.bounds }}{% else %}"autobounds"{% endif %},
419+
{{ this.fit_bounds_options }}
420+
);
421+
{% endmacro %}
422+
""")

folium/plugins/__init__.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,4 @@
99
from .scroll_zoom_toggler import ScrollZoomToggler
1010
from .terminator import Terminator
1111
from .boat_marker import BoatMarker
12-
from .layer import Layer, LayerControl
13-
from .geo_json import GeoJson
1412
from .timestamped_geo_json import TimestampedGeoJson

folium/plugins/boat_marker.py

Lines changed: 24 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@
66
Creates a marker shaped like a boat. Optionally you can append a wind direction.
77
"""
88
import json
9+
from jinja2 import Template
910

10-
from .plugin import Plugin
11+
from folium.element import JavascriptLink, MacroElement, Figure
1112

12-
class BoatMarker(Plugin):
13+
class BoatMarker(MacroElement):
1314
"""Adds a BoatMarker layer on the map."""
1415
def __init__(self, position=None, heading=0, wind_heading=None, wind_speed=0, **kwargs):
1516
"""Creates a BoatMarker plugin to append into a map with
@@ -32,30 +33,28 @@ def __init__(self, position=None, heading=0, wind_heading=None, wind_speed=0, **
3233
Speed of the wind in knots.
3334
"""
3435
super(BoatMarker, self).__init__()
35-
self.plugin_name = 'BoatMarker'
36+
self._name = 'BoatMarker'
3637
self.position = None if position is None else tuple(position)
3738
self.heading = heading
3839
self.wind_heading = wind_heading
3940
self.wind_speed = wind_speed
40-
self.kwargs = kwargs.copy()
41-
42-
def render_header(self, nb):
43-
"""Generates the HTML part of the plugin."""
44-
return """
45-
<script src="https://thomasbrueggemann.github.io/leaflet.boatmarker/js/leaflet.boatmarker.min.js"></script>
46-
""" if nb==0 else ""
47-
48-
def render_js(self, nb):
49-
"""Generates the Javascript part of the plugin."""
50-
kwargs_str = "{%s}" % ",".join(["%s : %s" % (key,json.dumps(val)) for (key,val) in self.kwargs.items()])
51-
position_str = "map.getCenter()" if self.position is None else "[%.12f,%.12f]"%self.position
52-
out = 'var boatMarker_%s = L.boatMarker(%s, %s).addTo(map);' % (nb,position_str,kwargs_str)
53-
54-
if self.wind_heading is None:
55-
out += "boatMarker_%s.setHeading(%s);" % (nb,int(self.heading))
56-
else:
57-
out += "boatMarker_%s.setHeadingWind(%s, %s, %s);"%(nb,int(self.heading),
58-
int(self.wind_speed),
59-
int(self.wind_heading),
60-
)
61-
return out
41+
self.kwargs = json.dumps(kwargs)
42+
43+
self._template = Template(u"""
44+
{% macro script(this, kwargs) %}
45+
var {{this.get_name()}} = L.boatMarker(
46+
[{{this.position[0]}},{{this.position[1]}}],
47+
{{this.kwargs}}).addTo({{this._parent.get_name()}});
48+
{{this.get_name()}}.setHeadingWind({{this.heading}}, {{this.wind_speed}}, {{this.wind_heading}});
49+
{% endmacro %}
50+
""")
51+
def render(self,**kwargs):
52+
super(BoatMarker,self).render(**kwargs)
53+
54+
figure = self.get_root()
55+
assert isinstance(figure,Figure), ("You cannot render this Element "
56+
"if it's not in a Figure.")
57+
58+
figure.header.add_children(\
59+
JavascriptLink("https://thomasbrueggemann.github.io/leaflet.boatmarker/js/leaflet.boatmarker.min.js"),
60+
name='markerclusterjs')

folium/plugins/geo_json.py

Lines changed: 0 additions & 50 deletions
This file was deleted.

folium/plugins/layer.py

Lines changed: 0 additions & 77 deletions
This file was deleted.

folium/plugins/leaflet-dvf.markers.min.js

Lines changed: 0 additions & 6 deletions
This file was deleted.

0 commit comments

Comments
 (0)