|
23 | 23 | from folium.six import text_type, binary_type, iteritems |
24 | 24 |
|
25 | 25 | import sys |
26 | | - |
| 26 | +import base64 |
27 | 27 |
|
28 | 28 | ENV = Environment(loader=PackageLoader('folium', 'templates')) |
29 | 29 |
|
@@ -221,6 +221,7 @@ def __init__(self, location=None, width='100%', height='100%', |
221 | 221 | self.added_layers = [] |
222 | 222 | self.template_vars.setdefault('wms_layers', []) |
223 | 223 | self.template_vars.setdefault('tile_layers', []) |
| 224 | + self.template_vars.setdefault('image_layers', []) |
224 | 225 |
|
225 | 226 | @iter_obj('simple') |
226 | 227 | def add_tile_layer(self, tile_name=None, tile_url=None, active=False): |
@@ -281,18 +282,14 @@ def add_layers_to_map(self): |
281 | 282 | data_string = '' |
282 | 283 | for i, layer in enumerate(self.added_layers): |
283 | 284 | name = list(layer.keys())[0] |
284 | | - data_string += '\"' |
285 | | - data_string += name |
286 | | - data_string += '\"' |
287 | | - data_string += ': ' |
288 | | - data_string += name |
289 | 285 | if i < len(self.added_layers)-1: |
290 | | - data_string += ",\n" |
| 286 | + term_string = ",\n" |
291 | 287 | else: |
292 | | - data_string += "\n" |
| 288 | + term_string += "\n" |
| 289 | + data_string += '\"{}\": {}'.format(name, name, term_string) |
293 | 290 |
|
294 | 291 | data_layers = layers_temp.render({'layers': data_string}) |
295 | | - self.template_vars.setdefault('data_layers', []).append((data_string)) |
| 292 | + self.template_vars.setdefault('data_layers', []).append((data_layers)) |
296 | 293 |
|
297 | 294 | @iter_obj('simple') |
298 | 295 | def simple_marker(self, location=None, popup=None, |
@@ -953,6 +950,92 @@ def json_style(style_cnt, line_color, line_weight, line_opacity, |
953 | 950 | self.template_vars.setdefault('geo_styles', []).append(style) |
954 | 951 | self.template_vars.setdefault('gjson_layers', []).append(layer) |
955 | 952 |
|
| 953 | + @iter_obj('image_overlay') |
| 954 | + def image_overlay(self, data, opacity=0.25, min_lat=-90.0, max_lat=90.0, |
| 955 | + min_lon=-180.0, max_lon=180.0, image_name=None, filename=None): |
| 956 | + """Simple image overlay of raster data from a numpy array. This is a lightweight |
| 957 | + way to overlay geospatial data on top of a map. If your data is high res, consider |
| 958 | + implementing a WMS server and adding a WMS layer. |
| 959 | +
|
| 960 | + This function works by generating a PNG file from a numpy array. If you do not |
| 961 | + specifiy a filename, it will embed the image inline. Otherwise, it saves the file in the |
| 962 | + current directory, and then adds it as an image overlay layer in leaflet.js. |
| 963 | + By default, the image is placed and stretched using bounds that cover the |
| 964 | + entire globe. |
| 965 | +
|
| 966 | + Parameters |
| 967 | + ---------- |
| 968 | + data: numpy array OR url string, required. |
| 969 | + if numpy array, must be a image format, i.e., NxM (mono), NxMx3 (rgb), or NxMx4 (rgba) |
| 970 | + if url, must be a valid url to a image (local or external) |
| 971 | + opacity: float, default 0.25 |
| 972 | + Image layer opacity in range 0 (completely transparent) to 1 (opaque) |
| 973 | + min_lat: float, default -90.0 |
| 974 | + max_lat: float, default 90.0 |
| 975 | + min_lon: float, default -180.0 |
| 976 | + max_lon: float, default 180.0 |
| 977 | + image_name: string, default None |
| 978 | + The name of the layer object in leaflet.js |
| 979 | + filename: string, default None |
| 980 | + Optional file name of output.png for image overlay. If None, we use a |
| 981 | + inline PNG. |
| 982 | +
|
| 983 | + Output |
| 984 | + ------ |
| 985 | + Image overlay data layer in obj.template_vars |
| 986 | +
|
| 987 | + Examples |
| 988 | + ------- |
| 989 | + # assumes a map object `m` has been created |
| 990 | + >>> import numpy as np |
| 991 | + >>> data = np.random.random((100,100)) |
| 992 | + |
| 993 | + # to make a rgba from a specific matplotlib colormap: |
| 994 | + >>> import matplotlib.cm as cm |
| 995 | + >>> cmapper = cm.cm.ColorMapper('jet') |
| 996 | + >>> data2 = cmapper.to_rgba(np.random.random((100,100))) |
| 997 | +
|
| 998 | + # place the data over all of the globe (will be pretty pixelated!) |
| 999 | + >>> m.image_overlay(data) |
| 1000 | +
|
| 1001 | + # put it only over a single city (Paris) |
| 1002 | + >>> m.image_overlay(data, min_lat=48.80418, max_lat=48.90970, min_lon=2.25214, max_lon=2.44731) |
| 1003 | + |
| 1004 | + """ |
| 1005 | + |
| 1006 | + if isinstance(data, str): |
| 1007 | + filename = data |
| 1008 | + else: |
| 1009 | + try: |
| 1010 | + png_str = utilities.write_png(data) |
| 1011 | + except Exception as e: |
| 1012 | + raise e |
| 1013 | + |
| 1014 | + if filename is not None: |
| 1015 | + with open(filename, 'wb') as fd: |
| 1016 | + fd.write(png_str) |
| 1017 | + else: |
| 1018 | + filename = "data:image/png;base64,"+base64.b64encode(png_str).decode('utf-8') |
| 1019 | + |
| 1020 | + if image_name not in self.added_layers: |
| 1021 | + if image_name is None: |
| 1022 | + image_name = "Image_Overlay" |
| 1023 | + else: |
| 1024 | + image_name = image_name.replace(" ", "_") |
| 1025 | + image_url = filename |
| 1026 | + image_bounds = [[min_lat, min_lon], [max_lat, max_lon]] |
| 1027 | + image_opacity = opacity |
| 1028 | + |
| 1029 | + image_temp = self.env.get_template('image_layer.js') |
| 1030 | + |
| 1031 | + image = image_temp.render({'image_name': image_name, |
| 1032 | + 'image_url': image_url, |
| 1033 | + 'image_bounds': image_bounds, |
| 1034 | + 'image_opacity': image_opacity}) |
| 1035 | + |
| 1036 | + self.template_vars['image_layers'].append(image) |
| 1037 | + self.added_layers.append(image_name) |
| 1038 | + |
956 | 1039 | def _build_map(self, html_templ=None, templ_type='string'): |
957 | 1040 | self._auto_bounds() |
958 | 1041 | """Build HTML/JS/CSS from Templates given current map type.""" |
|
0 commit comments