Skip to content

Commit a7fd814

Browse files
committed
Improved docstring and changed the defaults.
1 parent 0af50c2 commit a7fd814

File tree

3 files changed

+69
-60
lines changed

3 files changed

+69
-60
lines changed

folium/folium.py

Lines changed: 61 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -951,71 +951,82 @@ def json_style(style_cnt, line_color, line_weight, line_opacity,
951951
self.template_vars.setdefault('gjson_layers', []).append(layer)
952952

953953
@iter_obj('image_overlay')
954-
def image_overlay(self, data, opacity=0.75, min_lat=-90.0, max_lat=90.0,
954+
def image_overlay(self, data, image_opacity=1, min_lat=-90.0, max_lat=90.0,
955955
min_lon=-180.0, max_lon=180.0, image_name=None,
956-
filename=None, data_projection='mercator'):
957-
"""Simple image overlay of raster data from a numpy array. This is a
958-
lightweight way to overlay geospatial data on top of a map.
959-
If your data is high res, consider implementing a WMS server
960-
and adding a WMS layer.
961-
962-
This function works by generating a PNG file from a numpy
963-
array. If you do not specify a filename, it will embed the
964-
image inline. Otherwise, it saves the file in the current
965-
directory, and then adds it as an image overlay layer in
966-
leaflet.js. By default, the image is placed and stretched
967-
using bounds that cover the entire globe. By default, we
968-
assume that your data is in geodetic projection and thus
969-
project it to web mercator for display purposes. If you are
970-
overlaying a non-georeferenced image, set data_projection to
971-
None.
956+
filename=None):
957+
"""
958+
Simple image overlay of raster `data` from an image or a NumPy array.
959+
This is a lightweight way to overlay geospatial data on top of a map.
960+
The default lon, lat corners are the whole globe. Adjust it to your
961+
image.
962+
963+
If `data` is is a NumPy array a PNG file will be created for you.
964+
If you do not specify a filename the image will be embedded inline.
965+
966+
NOTE: The image must be projected as Web Mercator
967+
(https://en.wikipedia.org/wiki/Web_Mercator). See the examples below
968+
for re-projection.
972969
973970
Parameters
974971
----------
975-
data: numpy array OR url string, required.
976-
if numpy array, must be a image format,
977-
i.e., NxM (mono), NxMx3 (RGB), or NxMx4 (RGBA)
978-
if url, must be a valid url to a image (local or external)
979-
opacity: float, default 0.75
980-
Image layer opacity in range 0 (transparent) to 1 (opaque)
981-
min_lat: float, default -90.0
982-
max_lat: float, default 90.0
983-
min_lon: float, default -180.0
984-
max_lon: float, default 180.0
972+
data: NumPy array OR image.
973+
The NumPy array can NxM (mono), NxMx3 (RGB), or NxMx4 (RGBA)
974+
image_opacity: float, default 1
975+
Image layer image_opacity in range 0 (transparent) to 1 (opaque)
976+
min_lat: float (default: -90)
977+
max_lat: float (default: 90)
978+
min_lon: float (default: -180)
979+
max_lon: float (default" 180)
985980
image_name: string, default None
986981
The name of the layer object in leaflet.js
987982
filename: string or None, default None
988983
Optional file name of output.png for image overlay.
989984
If None, we use a inline PNG.
990-
data_projection: string or None, default 'mercator'
991-
Used to specify projection of image. If None, do no projection
992985
993986
Output
994987
------
995988
Image overlay data layer in obj.template_vars
996989
997990
Examples
998991
-------
999-
# Assumes a map object `m` has been created.
1000992
>>> import numpy as np
1001-
>>> data = np.random.random((180,360))
1002-
1003-
# Place the data over all of the globe (will be pretty pixelated!)
1004-
>>> m.image_overlay(data)
1005-
1006-
# Put it only over a single city (Paris)
1007-
>>> m.image_overlay(data, min_lat=48.80418, max_lat=48.90970,
1008-
... min_lon=2.25214, max_lon=2.44731)
993+
>>> import matplotlib
994+
>>> def sample_data(shape=(73, 145)):
995+
... nlats, nlons = shape
996+
... lats = np.linspace(-np.pi / 2, np.pi / 2, nlats)
997+
... lons = np.linspace(0, 2 * np.pi, nlons)
998+
... lons, lats = np.meshgrid(lons, lats)
999+
... wave = 0.75 * (np.sin(2 * lats) ** 8) * np.cos(4 * lons)
1000+
... mean = 0.5 * np.cos(2 * lats) * ((np.sin(2 * lats)) ** 2 + 2)
1001+
... lats = np.rad2deg(lats)
1002+
... lons = np.rad2deg(lons)
1003+
... data = wave + mean
1004+
... return lons, lats, data
1005+
>>> # Lets create some data,
1006+
>>> lon, lat, data = sample_data(shape=(73, 145))
1007+
>>> lon -= 180
1008+
>>> # and color it.
1009+
>>> cm = matplotlib.cm.get_cmap('cubehelix')
1010+
>>> normed_data = (data - data.min()) / (data.max() - data.min())
1011+
>>> colored_data = cm(normed_data)
1012+
>>> # First no projection (wrong).
1013+
>>> map = folium.Map(location=[lat.mean(), lon.mean()], zoom_start=1)
1014+
>>> map.image_overlay(colored_data,
1015+
... min_lat=lat.min(), max_lat=lat.max(),
1016+
... min_lon=lon.min(), max_lon=lon.max(),
1017+
... image_opacity=0.25)
1018+
>>> # Now lets project the data as to Web Mercator (correct).
1019+
>>> map = folium.Map(location=[lat.mean(), lon.mean()], zoom_start=1)
1020+
>>> project = geodetic_to_mercator(colored_data)
1021+
>>> map.image_overlay(projected,
1022+
... min_lat=lat.min(), max_lat=lat.max(),
1023+
... min_lon=lon.min(), max_lon=lon.max(),
1024+
... image_opacity=0.25)
10091025
10101026
"""
10111027
if isinstance(data, str):
10121028
filename = data
10131029
else:
1014-
assert data_projection in [None, 'mercator']
1015-
# This assumes a lat x long array.
1016-
# with 2x as many points in long as lat dims.
1017-
if data_projection is 'mercator':
1018-
data = utilities.geodetic_to_mercator(data)
10191030
try:
10201031
png_str = utilities.write_png(data)
10211032
except Exception as e:
@@ -1030,19 +1041,20 @@ def image_overlay(self, data, opacity=0.75, min_lat=-90.0, max_lat=90.0,
10301041

10311042
if image_name not in self.added_layers:
10321043
if image_name is None:
1044+
# FIXME: This will fails with multiple overlays!
10331045
image_name = "Image_Overlay"
10341046
else:
1047+
# FIXME: We should write a more robust `name_normalizer()`.
10351048
image_name = image_name.replace(" ", "_")
10361049
image_url = filename
10371050
image_bounds = [[min_lat, min_lon], [max_lat, max_lon]]
1038-
image_opacity = opacity
10391051

1040-
image_temp = self.env.get_template('image_layer.js')
1052+
image_templ = self.env.get_template('image_layer.js')
10411053

1042-
image = image_temp.render({'image_name': image_name,
1043-
'image_url': image_url,
1044-
'image_bounds': image_bounds,
1045-
'image_opacity': image_opacity})
1054+
image = image_templ.render({'image_name': image_name,
1055+
'image_url': image_url,
1056+
'image_bounds': image_bounds,
1057+
'image_opacity': image_opacity})
10461058

10471059
self.template_vars['image_layers'].append(image)
10481060
self.added_layers.append(image_name)

folium/utilities.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,7 @@ def write_png(array):
366366
array_full = array_full.astype('uint8')
367367
height, width = array_full.shape[:2]
368368
array_full = np.flipud(array_full)
369-
369+
370370
array_full = array_full.tobytes()
371371

372372
# Reverse the vertical line order and add null bytes at the start.
@@ -408,12 +408,12 @@ def geodetic_to_mercator(geodetic):
408408
409409
Parameters
410410
----------
411-
geodetic: numpy image array
411+
geodetic: NumPy image array
412412
Latitude x Longitude array, in mono (NxM), RGB (NxMx3) or RGBA (NxMx4)
413413
414414
Returns
415415
-------
416-
mercator: projected numpy image array
416+
mercator: projected NumPy image array
417417
418418
"""
419419
geo = np.repeat(np.atleast_3d(geodetic), 2, axis=0)

tests/folium_tests.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -485,7 +485,7 @@ def test_fit_bounds(self):
485485
def test_image_overlay(self):
486486
"""Test image overlay."""
487487
from numpy.random import random
488-
from folium.utilities import write_png, geodetic_to_mercator
488+
from folium.utilities import write_png
489489
import base64
490490

491491
data = random((100, 100))
@@ -495,18 +495,16 @@ def test_image_overlay(self):
495495

496496
image_url = 'data.png'
497497
inline_image_url = ("data:image/png;base64," +
498-
base64.b64encode(write_png(
499-
geodetic_to_mercator(data))).decode('utf-8'))
498+
base64.b64encode(write_png(data)).decode('utf-8'))
500499

501500
image_tpl = self.env.get_template('image_layer.js')
502501
image_name = 'Image_Overlay'
503-
image_opacity = 0.75
502+
image_opacity = 1
504503

505504
min_lon, max_lon, min_lat, max_lat = -90.0, 90.0, -180.0, 180.0
506505
image_bounds = [[min_lon, min_lat], [max_lon, max_lat]]
507506

508-
# Test the external png.
509-
507+
# Test the external PNG.
510508
image_rendered = image_tpl.render({'image_name': image_name,
511509
'image_url': image_url,
512510
'image_bounds': image_bounds,
@@ -515,8 +513,7 @@ def test_image_overlay(self):
515513
self.map.image_overlay(data, filename=image_url)
516514
assert image_rendered in self.map.template_vars['image_layers']
517515

518-
# Test the inline png.
519-
516+
# Test the inline PNG.
520517
image_rendered = image_tpl.render({'image_name': image_name,
521518
'image_url': inline_image_url,
522519
'image_bounds': image_bounds,

0 commit comments

Comments
 (0)