在底图中填充海洋 [英] Fill oceans in basemap
问题描述
我正在尝试在matplotlib.Basemap
上绘制1x1度数据,并且我想用白色填充海洋.但是,为了使海洋的边界遵循matplotlib
绘制的海岸线,白海面具的分辨率应该比我的数据分辨率高得多.
I am trying to plot 1x1 degree data on a matplotlib.Basemap
, and I want to fill the ocean with white. However, in order for the boundaries of the ocean to follow the coastlines drawn by matplotlib
, the resolution of the white ocean mask should be much higher than the resolution of my data.
长时间搜索后,我尝试了两种可能的解决方案:
After searching around for a long time I tried the two possible solutions:
(1)maskoceans()
和is_land()
函数,但是由于我的数据分辨率低于底图绘制的地图,因此边缘看起来不佳.我也不想将数据插值到更高的分辨率.
(1) maskoceans()
and is_land()
functions, but since my data is lower resolution than the map drawn by basemap it does not look good on the edges. I do not want to interpolate my data to higher resolution either.
(2)m.drawlsmask()
,但是由于无法指定zorder,因此pcolormesh图始终覆盖蒙版.
(2) m.drawlsmask()
, but since zorder cannot be assigned the pcolormesh plot always overlays the mask.
此代码
import numpy as np
import matplotlib.pyplot as plt
import mpl_toolkits.basemap as bm
#Make data
lon = np.arange(0,360,1)
lat = np.arange(-90,91,1)
data = np.random.rand(len(lat),len(lon))
#Draw map
plt.figure()
m = bm.Basemap(resolution='i',projection='laea', width=1500000, height=2900000, lat_ts=60, lat_0=72, lon_0=319)
m.drawcoastlines(linewidth=1, color='white')
data, lon = bm.addcyclic(data,lon)
x,y = m(*np.meshgrid(lon,lat))
plt.pcolormesh(x,y,data)
plt.savefig('1.png',dpi=300)
产生此图像:
添加m.fillcontinents(color='white')
会产生以下图像,这是我需要的,但要填充海洋而不是土地.
Adding m.fillcontinents(color='white')
produces the following image, which is what I need but to fill the ocean and not the land.
修改:
m.drawmapboundary(fill_color='lightblue')
也会填满土地,因此无法使用.
m.drawmapboundary(fill_color='lightblue')
also fills over land and can therefore not be used.
理想的结果是海洋是白色的,而我用plt.pcolormesh(x,y,data)
绘制的图则显示在陆地上.
The desired outcome is that the oceans are white, while what I plotted with plt.pcolormesh(x,y,data)
shows up over the lands.
推荐答案
我找到了一个更好的解决方案,该问题使用地图中海岸线定义的多边形生成覆盖海洋区域的matplotlib.PathPatch
.该解决方案具有更好的分辨率,并且速度更快:
I found a much nicer solution to the problem which uses the polygons defined by the coastlines in the map to produce a matplotlib.PathPatch
that overlays the ocean areas. This solution has a much better resolution and is much faster:
from matplotlib import pyplot as plt
from mpl_toolkits import basemap as bm
from matplotlib import colors
import numpy as np
import numpy.ma as ma
from matplotlib.patches import Path, PathPatch
fig, ax = plt.subplots()
lon_0 = 319
lat_0 = 72
##some fake data
lons = np.linspace(lon_0-60,lon_0+60,10)
lats = np.linspace(lat_0-15,lat_0+15,5)
lon, lat = np.meshgrid(lons,lats)
TOPO = np.sin(np.pi*lon/180)*np.exp(lat/90)
m = bm.Basemap(resolution='i',projection='laea', width=1500000, height=2900000, lat_ts=60, lat_0=lat_0, lon_0=lon_0, ax = ax)
m.drawcoastlines(linewidth=0.5)
x,y = m(lon,lat)
pcol = ax.pcolormesh(x,y,TOPO)
##getting the limits of the map:
x0,x1 = ax.get_xlim()
y0,y1 = ax.get_ylim()
map_edges = np.array([[x0,y0],[x1,y0],[x1,y1],[x0,y1]])
##getting all polygons used to draw the coastlines of the map
polys = [p.boundary for p in m.landpolygons]
##combining with map edges
polys = [map_edges]+polys[:]
##creating a PathPatch
codes = [
[Path.MOVETO] + [Path.LINETO for p in p[1:]]
for p in polys
]
polys_lin = [v for p in polys for v in p]
codes_lin = [c for cs in codes for c in cs]
path = Path(polys_lin, codes_lin)
patch = PathPatch(path,facecolor='white', lw=0)
##masking the data:
ax.add_patch(patch)
plt.show()
输出看起来像这样:
原始解决方案:
您可以在basemap.maskoceans
中使用分辨率更高的数组,以使分辨率适合大陆轮廓.然后,您只需反转遮罩并将遮罩的阵列绘制在数据之上即可.
You can use an array with greater resolution in basemap.maskoceans
, such that the resolution fits the continent outlines. Afterwards, you can just invert the mask and plot the masked array on top of your data.
以某种方式,当我使用整个地图范围时(例如,经度从-180到180,纬度从-90到90),只有basemap.maskoceans
可以工作.鉴于需要相当高的分辨率才能使其看起来不错,因此计算需要一段时间:
Somehow I only got basemap.maskoceans
to work when I used the full range of the map (e.g. longitudes from -180 to 180 and latitudes from -90 to 90). Given that one needs quite a high resolution to make it look nice, the computation takes a while:
from matplotlib import pyplot as plt
from mpl_toolkits import basemap as bm
from matplotlib import colors
import numpy as np
import numpy.ma as ma
fig, ax = plt.subplots()
lon_0 = 319
lat_0 = 72
##some fake data
lons = np.linspace(lon_0-60,lon_0+60,10)
lats = np.linspace(lat_0-15,lat_0+15,5)
lon, lat = np.meshgrid(lons,lats)
TOPO = np.sin(np.pi*lon/180)*np.exp(lat/90)
m = bm.Basemap(resolution='i',projection='laea', width=1500000, height=2900000, lat_ts=60, lat_0=lat_0, lon_0=lon_0, ax = ax)
m.drawcoastlines(linewidth=0.5)
x,y = m(lon,lat)
pcol = ax.pcolormesh(x,y,TOPO)
##producing a mask -- seems to only work with full coordinate limits
lons2 = np.linspace(-180,180,10000)
lats2 = np.linspace(-90,90,5000)
lon2, lat2 = np.meshgrid(lons2,lats2)
x2,y2 = m(lon2,lat2)
pseudo_data = np.ones_like(lon2)
masked = bm.maskoceans(lon2,lat2,pseudo_data)
masked.mask = ~masked.mask
##plotting the mask
cmap = colors.ListedColormap(['w'])
pcol = ax.pcolormesh(x2,y2,masked, cmap=cmap)
plt.show()
结果如下:
这篇关于在底图中填充海洋的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!