Skip to content

Commit 697ef75

Browse files
committed
Merge pull request #293 from BibMartin/image_overlay
Update on ImageOverlay, and example nb
2 parents de20189 + 936660a commit 697ef75

11 files changed

Lines changed: 622 additions & 230 deletions

examples/ImageOverlay.ipynb

Lines changed: 422 additions & 0 deletions
Large diffs are not rendered by default.
1.01 MB
Loading
3.14 MB
Loading

folium/__init__.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
Popup, TileLayer)
1111

1212
from folium.features import (ClickForMarker, ColorScale, CustomIcon, DivIcon,
13-
GeoJson, ImageOverlay, LatLngPopup,
13+
GeoJson, LatLngPopup,
1414
MarkerCluster, MultiPolyLine, PolyLine, Vega,
1515
RegularPolygonMarker, TopoJson, WmsTileLayer)
1616

@@ -30,7 +30,6 @@
3030
'DivIcon',
3131
'GeoJson',
3232
'GeoJsonStyle',
33-
'ImageOverlay',
3433
'LatLngPopup',
3534
'MarkerCluster',
3635
'MultiPolyLine',

folium/features.py

Lines changed: 7 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,22 @@
1313
text_type, binary_type)
1414

1515
from .element import Element, Figure, JavascriptLink, CssLink, MacroElement
16-
from .map import TileLayer, Icon, Marker, Popup
16+
from .map import Layer, Icon, Marker, Popup
1717

18-
19-
class WmsTileLayer(TileLayer):
18+
class WmsTileLayer(Layer):
2019
def __init__(self, url, name=None,
2120
format=None, layers=None, transparent=True,
22-
attr=None, overlay=True):
21+
attr=None, overlay=True, control=True):
2322
"""
2423
TODO docstring here
2524
2625
"""
27-
super(TileLayer, self).__init__()
26+
super(WmsTileLayer, self).__init__(overlay=overlay, control=control)
2827
self._name = 'WmsTileLayer'
2928
self.tile_name = name if name is not None else 'WmsTileLayer_'+self._id
3029
self.url = url
3130
self.format = format
3231
self.layers = layers
33-
self.overlay = overlay
3432
self.transparent = transparent
3533
self.attr = attr
3634

@@ -49,7 +47,6 @@ def __init__(self, url, name=None,
4947
{% endmacro %}
5048
""") # noqa
5149

52-
5350
class RegularPolygonMarker(Marker):
5451
def __init__(self, location, color='black', opacity=1, weight=2,
5552
fill_color='blue', fill_opacity=1,
@@ -338,16 +335,16 @@ def render(self, **kwargs):
338335
name='d3')
339336

340337

341-
class MarkerCluster(MacroElement):
338+
class MarkerCluster(Layer):
342339
"""Adds a MarkerCluster layer on the map."""
343-
def __init__(self):
340+
def __init__(self, overlay=True, control=True):
344341
"""Creates a MarkerCluster element to append into a map with
345342
Map.add_children.
346343
347344
Parameters
348345
----------
349346
"""
350-
super(MarkerCluster, self).__init__()
347+
super(MarkerCluster, self).__init__(overlay=overlay, control=control)
351348
self._name = 'MarkerCluster'
352349
self._template = Template(u"""
353350
{% macro script(this, kwargs) %}
@@ -610,69 +607,6 @@ def __init__(self, locations, color=None, weight=None,
610607
{% endmacro %}
611608
""") # noqa
612609

613-
614-
class ImageOverlay(MacroElement):
615-
def __init__(self, image, bounds, opacity=1., attr=None,
616-
origin='upper', colormap=None, mercator_project=False):
617-
"""
618-
Used to load and display a single image over specific bounds of
619-
the map, implements ILayer interface.
620-
621-
Parameters
622-
----------
623-
image: string, file or array-like object
624-
The data you want to draw on the map.
625-
* If string, it will be written directly in the output file.
626-
* If file, it's content will be converted as embedded in the
627-
output file.
628-
* If array-like, it will be converted to PNG base64 string
629-
and embedded in the output.
630-
bounds: list
631-
Image bounds on the map in the form [[lat_min, lon_min],
632-
[lat_max, lon_max]]
633-
opacity: float, default Leaflet's default (1.0)
634-
attr: string, default Leaflet's default ("")
635-
origin : ['upper' | 'lower'], optional, default 'upper'
636-
Place the [0,0] index of the array in the upper left or
637-
lower left corner of the axes.
638-
colormap : callable, used only for `mono` image.
639-
Function of the form [x -> (r,g,b)] or [x -> (r,g,b,a)]
640-
for transforming a mono image into RGB.
641-
It must output iterables of length 3 or 4,
642-
with values between 0 and 1.
643-
Hint : you can use colormaps from `matplotlib.cm`.
644-
mercator_project : bool, default False.
645-
Used only for array-like image. Transforms the data to
646-
project (longitude, latitude) coordinates to the
647-
Mercator projection.
648-
649-
"""
650-
super(ImageOverlay, self).__init__()
651-
self._name = 'ImageOverlay'
652-
653-
self.url = image_to_url(image, origin=origin,
654-
mercator_project=mercator_project,
655-
bounds=bounds)
656-
657-
self.bounds = json.loads(json.dumps(bounds))
658-
options = {
659-
'opacity': opacity,
660-
'attribution': attr,
661-
}
662-
self.options = json.dumps({key: val for key, val
663-
in options.items() if val},
664-
sort_keys=True)
665-
self._template = Template(u"""
666-
{% macro script(this, kwargs) %}
667-
var {{this.get_name()}} = L.imageOverlay(
668-
'{{ this.url }}',
669-
{{ this.bounds }},
670-
{{ this.options }}
671-
).addTo({{this._parent.get_name()}});
672-
{% endmacro %}
673-
""")
674-
675-
676610
class CustomIcon(Icon):
677611
def __init__(self, icon_image, icon_size=None, icon_anchor=None,
678612
shadow_image=None, shadow_size=None, shadow_anchor=None,

folium/folium.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,8 @@
1818
from .features import (WmsTileLayer, RegularPolygonMarker, Vega, GeoJson,
1919
CircleMarker, LatLngPopup,
2020
ClickForMarker, ColorScale, TopoJson, PolyLine,
21-
MultiPolyLine, ImageOverlay)
22-
from .utilities import color_brewer, write_png
23-
21+
MultiPolyLine)
22+
from .utilities import color_brewer
2423

2524
def initialize_notebook():
2625
"""Initialize the IPython notebook display elements."""
@@ -689,6 +688,11 @@ def image_overlay(self, data, opacity=0.25, min_lat=-90.0, max_lat=90.0,
689688
... min_lon=2.25214, max_lon=2.44731)
690689
691690
"""
691+
warnings.warn('This method is deprecated. Please use `Map.add_children('
692+
'folium.plugins.ImageOverlay(...))` instead.')
693+
from .plugins import ImageOverlay
694+
from .utilities import write_png
695+
692696
if filename:
693697
image = write_png(data, origin=origin, colormap=colormap)
694698
open(filename, 'wb').write(image)

folium/map.py

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -168,24 +168,43 @@ def add_tile_layer(self, tiles='OpenStreetMap', name=None,
168168
detect_retina=detect_retina)
169169
self.add_children(tile_layer, name=tile_layer.tile_name)
170170

171+
class Layer(MacroElement):
172+
"""An abstract class for everything that is a Layer on the map.
173+
It will be used to define whether an object will be included in LayerControls.
174+
"""
175+
def __init__(self, name=None, overlay=False, control=True):
176+
"""Creates a Layer instance.
171177
172-
class TileLayer(MacroElement):
178+
Parameters
179+
----------
180+
name : string, default None
181+
The name of the Layer, as it will appear in LayerControls
182+
overlay : bool, default False
183+
Whether the layer is optional (overlay) or compulsory.
184+
control : bool, default True
185+
Whether the Layer will be included in LayerControls
186+
"""
187+
super(Layer, self).__init__()
188+
self.layer_name = name if name is not None else self.get_name()
189+
self.overlay = overlay
190+
self.control = control
191+
192+
class TileLayer(Layer):
173193
def __init__(self, tiles='OpenStreetMap', name=None,
174194
min_zoom=1, max_zoom=18, attr=None, API_key=None,
175-
overlay=False, detect_retina=False):
195+
overlay=False, control=True, detect_retina=False):
176196
"""TODO docstring here
177197
Parameters
178198
----------
179199
"""
180-
super(TileLayer, self).__init__()
181-
self._name = 'TileLayer'
182200
self.tile_name = (name if name is not None else
183201
''.join(tiles.lower().strip().split()))
202+
super(TileLayer, self).__init__(name=self.tile_name, overlay=overlay, control=control)
203+
self._name = 'TileLayer'
184204

185205
self.min_zoom = min_zoom
186206
self.max_zoom = max_zoom
187207

188-
self.overlay = overlay
189208
self.detect_retina = detect_retina
190209

191210
self.tiles = ''.join(tiles.lower().strip().split())
@@ -225,8 +244,8 @@ def __init__(self, tiles='OpenStreetMap', name=None,
225244
""")
226245

227246

228-
class FeatureGroup(TileLayer):
229-
def __init__(self, name=None, overlay=True):
247+
class FeatureGroup(Layer):
248+
def __init__(self, name=None, overlay=True, control=True):
230249
"""
231250
Create a FeatureGroup layer ; you can put things in it and handle them
232251
as a single layer. For example, you can add a LayerControl to
@@ -242,13 +261,11 @@ def __init__(self, name=None, overlay=True):
242261
Whether your layer will be an overlay (ticked with a check box in
243262
LayerControls) or a base layer (ticked with a radio button).
244263
"""
245-
super(TileLayer, self).__init__()
264+
super(FeatureGroup, self).__init__(overlay=overlay, control=control)
246265
self._name = 'FeatureGroup'
247266

248267
self.tile_name = name if name is not None else self.get_name()
249268

250-
self.overlay = overlay
251-
252269
self._template = Template(u"""
253270
{% macro script(this, kwargs) %}
254271
var {{this.get_name()}} = L.featureGroup(
@@ -286,18 +303,22 @@ def __init__(self):
286303

287304
def render(self, **kwargs):
288305
"""TODO : docstring here."""
306+
# We select all Layers for which (control and not overlay).
289307
self.base_layers = OrderedDict(
290-
[(val.tile_name, val.get_name()) for key, val in
291-
self._parent._children.items() if
292-
isinstance(val, TileLayer) and not hasattr(val, 'overlay')])
308+
[(val.layer_name, val.get_name()) for key, val in
309+
self._parent._children.items() if isinstance(val, Layer)
310+
and (not hasattr(val, 'overlay') or not val.overlay)
311+
and (not hasattr(val, 'control') or val.control)
312+
])
313+
# We select all Layers for which (control and overlay).
293314
self.overlays = OrderedDict(
294-
[(val.tile_name, val.get_name()) for key, val in
295-
self._parent._children.items() if
296-
isinstance(val, TileLayer) and hasattr(val, 'overlay')])
297-
315+
[(val.layer_name, val.get_name()) for key, val in
316+
self._parent._children.items() if isinstance(val, Layer)
317+
and (hasattr(val, 'overlay') and val.overlay)
318+
and (not hasattr(val, 'control') or val.control)
319+
])
298320
super(LayerControl, self).render()
299321

300-
301322
class Icon(MacroElement):
302323
def __init__(self, color='blue', icon_color='white', icon='info-sign',
303324
angle=0, prefix='glyphicon'):

folium/plugins/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,13 @@
1212
from .boat_marker import BoatMarker
1313
from .timestamped_geo_json import TimestampedGeoJson
1414
from .heat_map import HeatMap
15+
from .image_overlay import ImageOverlay
1516

1617
__all__ = ['MarkerCluster',
1718
'ScrollZoomToggler',
1819
'Terminator',
1920
'BoatMarker',
2021
'TimestampedGeoJson',
21-
'HeatMap']
22+
'HeatMap',
23+
'ImageOverlay',
24+
]

0 commit comments

Comments
 (0)