6
6
Classes for drawing maps.
7
7
"""
8
8
import warnings
9
+ import json
10
+ from collections import OrderedDict
9
11
10
12
from jinja2 import Template
11
13
@@ -85,6 +87,8 @@ def __init__(self, location=None, width='100%', height='100%',
85
87
self .location = location
86
88
self .zoom_start = zoom_start
87
89
90
+ Figure ().add_children (self )
91
+
88
92
# Map Size Parameters.
89
93
self .width = _parse_size (width )
90
94
self .height = _parse_size (height )
@@ -108,6 +112,7 @@ def __init__(self, location=None, width='100%', height='100%',
108
112
height: {{this.height[0]}}{{this.height[1]}};
109
113
left: {{this.left[0]}}{{this.left[1]}};
110
114
top: {{this.top[0]}}{{this.top[1]}};
115
+ }
111
116
</style>
112
117
{% endmacro %}
113
118
{% macro html(this, kwargs) %}
@@ -169,7 +174,7 @@ def add_tile_layer(self, tiles='OpenStreetMap', name=None,
169
174
170
175
class TileLayer (MacroElement ):
171
176
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 ):
173
178
"""TODO docstring here
174
179
Parameters
175
180
----------
@@ -181,6 +186,8 @@ def __init__(self, tiles='OpenStreetMap', name=None,
181
186
self .min_zoom = min_zoom
182
187
self .max_zoom = max_zoom
183
188
189
+ self .overlay = overlay
190
+
184
191
self .tiles = '' .join (tiles .lower ().strip ().split ())
185
192
if self .tiles in ('cloudmade' , 'mapbox' ) and not API_key :
186
193
raise ValueError ('You must pass an API key if using Cloudmade'
@@ -197,6 +204,8 @@ def __init__(self, tiles='OpenStreetMap', name=None,
197
204
if not attr :
198
205
raise ValueError ('Custom tiles must'
199
206
' also be passed an attribution' )
207
+ if isinstance (attr , binary_type ):
208
+ attr = text_type (attr , 'utf8' )
200
209
self .attr = attr
201
210
202
211
self ._template = Template (u"""
@@ -213,6 +222,42 @@ def __init__(self, tiles='OpenStreetMap', name=None,
213
222
{% endmacro %}
214
223
""" )
215
224
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
+
216
261
class Icon (MacroElement ):
217
262
def __init__ (self , color = 'blue' , icon = 'info-sign' , angle = 0 ):
218
263
"""TODO : docstring here"""
@@ -265,6 +310,10 @@ def __init__(self, location, popup=None, icon=None):
265
310
super (Marker , self ).__init__ ()
266
311
self ._name = 'Marker'
267
312
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 )
268
317
269
318
self ._template = Template (u"""
270
319
{% macro script(this, kwargs) %}
@@ -292,7 +341,7 @@ def __init__(self, html, max_width=300):
292
341
self .script ._parent = self
293
342
294
343
if isinstance (html , Element ):
295
- self .html . add_children (html )
344
+ self .add_children (html )
296
345
elif isinstance (html , text_type ) or isinstance (html ,binary_type ):
297
346
self .html .add_children (Html (text_type (html )))
298
347
@@ -324,3 +373,50 @@ def render(self, **kwargs):
324
373
325
374
figure .script .add_children (Element (\
326
375
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