@@ -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 )
0 commit comments