我在Jupyter笔记本中表示一些地理数据:温度,海浪高度等。我有numpy数组,这些数组具有这些变量的纬度,经度和值。我想在地理地图上显示这些变量,最好使用ipyleaflet(因为这就是我已经在使用的)。我正在尝试获得类似于热图的结果。
我尝试使用ipyleaflet热图,但在我看来,它设计来表示点的集合而不是标量均匀数组,因为我无法正确显示结果。我认为ipyleaflet可能缺少表示此类数据的功能,但似乎很奇怪,因为它具有非常好的Velocity函数来表示矢量变量。
我想到的唯一方法是使用matplotlib生成图像,然后将其添加到图像层中的地图中,但是我觉得那不是正确的方法。
答案 0 :(得分:1)
对于表示热图,我建议将Cartopy与Matplotlib结合使用。 这是我为海岸线上的世界投影制作的即用型脚本:
import numpy as np
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.feature as cfeature
from cartopy.util import add_cyclic_point
# Set x y z variables
x = longitude_data
y = latitude_data
z = heat_map_data
# Set up figure and projection
z, x = add_cyclic_point(z, coord=x)
fig = plt.figure()
ax = fig.add_subplot(1,1,1, projection=ccrs.PlateCarree() )
# Set data range and colourmap
levels = np.arange(min,max,steps)
plt.contourf(x, y, z,levels = levels,transform=ccrs.PlateCarree(),cmap="rainbow")
# Set axes, extent (world) and labels
ax.set_xticks(np.linspace(-180,180,num=7), crs=ccrs.PlateCarree())
ax.set_yticks(np.linspace(-60,60,num=5), crs=ccrs.PlateCarree())
ax.add_feature(cfeature.COASTLINE) #Add coastline
ax.set_global()
ax.set_title('Heatmap')
ax.set_xlabel('Longitude')
ax.set_ylabel('Latitude')
# Add colorbar
plt.colorbar(ax=ax,shrink=0.7,orientation="vertical")
fig.show()
有了Cartopy和Matplotlib文档,您现在应该能够创建一些地图。
答案 1 :(得分:1)
正如您所说,您可以生成图像并将其覆盖在地图上。这是我得到的建议when I asked about this on Github。
有一个示例笔记本here。 不像matplotlib那样简单,但是您可以获得ipyleaflet的所有不错的交互性!
这是笔记本的当前版本,以防链接更改(通过jupyter-nbconvert --to markdown Numpy.ipynb
转换为markdown)
此笔记本显示了如何在IPyLeaflet中显示一些栅格地理数据。数据是一个NumPy数组,这意味着您可以使用Python科学堆栈的所有功能来处理它。
需要以下库:
* requests
* tqdm
* rasterio
* numpy
* scipy
* pillow
* matplotlib
* ipyleaflet
推荐的方法是先尝试conda install
,如果找不到,则pip install
。
import requests
import os
from tqdm import tqdm
import zipfile
import rasterio
from affine import Affine
import numpy as np
import scipy.ndimage
from rasterio.warp import reproject, Resampling
import PIL
import matplotlib.pyplot as plt
from base64 import b64encode
try:
from StringIO import StringIO
py3 = False
except ImportError:
from io import StringIO, BytesIO
py3 = True
from ipyleaflet import Map, ImageOverlay, basemap_to_tiles, basemaps
下载代表南美流量累积的栅格文件。这给出了河网的概念。
url = 'https://edcintl.cr.usgs.gov/downloads/sciweb1/shared/hydrosheds/sa_30s_zip_grid/sa_acc_30s_grid.zip'
filename = os.path.basename(url)
name = filename[:filename.find('_grid')]
adffile = name + '/' + name + '/w001001.adf'
if not os.path.exists(adffile):
r = requests.get(url, stream=True)
with open(filename, 'wb') as f:
total_length = int(r.headers.get('content-length'))
for chunk in tqdm(r.iter_content(chunk_size=1024), total=(total_length/1024) + 1):
if chunk:
f.write(chunk)
f.flush()
zip = zipfile.ZipFile(filename)
zip.extractall('.')
我们对数据进行了一些转换,以使河流显得更浓。
dataset = rasterio.open(adffile)
acc_orig = dataset.read()[0]
acc = np.where(acc_orig<0, 0, acc_orig)
shrink = 1 # if you are out of RAM try increasing this number (should be a power of 2)
radius = 5 # you can play with this number to change the width of the rivers
circle = np.zeros((2*radius+1, 2*radius+1)).astype('uint8')
y, x = np.ogrid[-radius:radius+1,-radius:radius+1]
index = x**2 + y**2 <= radius**2
circle[index] = 1
acc = np.sqrt(acc)
acc = scipy.ndimage.maximum_filter(acc, footprint=circle)
acc[acc_orig<0] = np.nan
acc = acc[::shrink, ::shrink]
原始数据在WGS 84投影中,但是Leaflet使用Web Mercator,因此我们需要重新投影。
# At this point if GDAL complains about not being able to open EPSG support file gcs.csv, try in the terminal:
# export GDAL_DATA=`gdal-config --datadir`
with rasterio.Env():
rows, cols = acc.shape
src_transform = list(dataset.transform)
src_transform[0] *= shrink
src_transform[4] *= shrink
src_transform = Affine(*src_transform[:6])
src_crs = {'init': 'EPSG:4326'}
source = acc
dst_crs = {'init': 'EPSG:3857'}
dst_transform, width, height = rasterio.warp.calculate_default_transform(src_crs, dst_crs, cols, rows, *dataset.bounds)
dst_shape = height, width
destination = np.zeros(dst_shape)
reproject(
source,
destination,
src_transform=src_transform,
src_crs=src_crs,
dst_transform=dst_transform,
dst_crs=dst_crs,
resampling=Resampling.nearest)
acc_web = destination
让我们将NumPy数组转换为图像。为此,我们必须指定一个颜色图(此处为plt.cm.jet
)。
acc_norm = acc_web - np.nanmin(acc_web)
acc_norm = acc_norm / np.nanmax(acc_norm)
acc_norm = np.where(np.isfinite(acc_web), acc_norm, 0)
acc_im = PIL.Image.fromarray(np.uint8(plt.cm.jet(acc_norm)*255))
acc_mask = np.where(np.isfinite(acc_web), 255, 0)
mask = PIL.Image.fromarray(np.uint8(acc_mask), mode='L')
im = PIL.Image.new('RGBA', acc_norm.shape[::-1], color=None)
im.paste(acc_im, mask=mask)
图像作为PNG文件嵌入到URL中,因此可以将其发送到浏览器。
if py3:
f = BytesIO()
else:
f = StringIO()
im.save(f, 'png')
data = b64encode(f.getvalue())
if py3:
data = data.decode('ascii')
imgurl = 'data:image/png;base64,' + data
Not quite as easy as matplotlib, but you get all the nice interactivity of ipyleaflet!
最后,我们可以覆盖我们的图像,如果一切正常,则应该完全在南美。
b = dataset.bounds
bounds = [(b.bottom, b.left), (b.top, b.right)]
io = ImageOverlay(url=imgurl, bounds=bounds)
center = [-10, -60]
zoom = 2
m = Map(center=center, zoom=zoom, interpolation='nearest')
m
tile = basemap_to_tiles(basemaps.Esri.WorldStreetMap)
m.add_layer(tile)
您可以使用不透明度滑块并检查数据文件中的河流是否与OpenStreetMap上的河流匹配。
m.add_layer(io)
io.interact(opacity=(0.0,1.0,0.01))