66Classes for drawing maps.
77"""
88import warnings
9+ import json
10+ from collections import OrderedDict
911
1012from 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
170175class 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+
216261class 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+ """ )
0 commit comments