在Cartopy地图中,我希望该区域不被任何数据(我的域之外)覆盖,例如浅灰色。
玩了REngine
并看过这个例子Change the background colour of a projected Matplotlib axis,但仍然无法做出我想做的事。
这是一个人为的例子,我用红线表示域边界。相反,我想让红线以外的区域在浅灰色中着色。
非常感谢!
修改 将投影更改为LambertConformal以证明下面提出的解决方案(Cartopy background color (outside of data domain))仅适用于矩形网格。请参见下面的其他图表
background_patch
答案 0 :(得分:2)
当然,如果您不使用cartopy,可以单独用matplotlib来实现:
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
import matplotlib.patches as mpatches
from matplotlib.path import Path
import numpy as np
# Create some lons/lats
lats = np.linspace(20,40,50)
lons = np.linspace(110,130,50)
lons,lats = np.meshgrid(lons,lats)
# Some data with 'cloud'.
thedata = np.zeros_like(lats)
thedata[5:8, 7:13] = 1
ax = plt.axes()
mycmap = mcolors.ListedColormap(['white', 'black'])
bounds=[0, 0.5, 1]
norm = mcolors.BoundaryNorm(bounds, mycmap.N)
im = ax.pcolormesh(lons, lats, thedata, cmap=mycmap,
norm=norm)
data_extent = np.array((lons[0,0], lons[-1,-1], lats[0,0], lats[-1,-1]))
# Make the extent larger to see a margin outside of the domain
ax.set_xlim(data_extent[:2] + [-1, 1])
ax.set_ylim(data_extent[2:] + [-1, 1])
# Create a path which has the exterior of the map, with an interior of the data we care about.
path_with_hole = Path([[-180, 90],
[180, 90],
[180, -90],
[-180, -90],
[-180, 90],
[data_extent[0], data_extent[2]],
[data_extent[1], data_extent[2]],
[data_extent[1], data_extent[3]],
[data_extent[0], data_extent[3]],
[data_extent[0], data_extent[2]]],
codes=[Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO, Path.LINETO,
Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO, Path.LINETO])
geom = mpatches.PathPatch(path_with_hole, facecolor='lightgrey',
edgecolor='white',
hatch='xxxx', alpha=0.6)
ax.add_patch(geom, )
plt.show()
关键是我们生成一个Path,它将地图作为外部,我们感兴趣的域作为内部。我们可以通过将它转换为补丁(你在matplotlib图中实际看到的东西)将该路径添加到轴上。
我们可以用一种明显的方式在图表中使用这种技术:
import cartopy.crs as ccrs
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
import matplotlib.patches as mpatches
from matplotlib.path import Path
import numpy as np
# Create some lons/lats
lats = np.linspace(20,40,50)
lons = np.linspace(110,130,50)
lons,lats = np.meshgrid(lons,lats)
# Some data with 'cloud'.
thedata = np.zeros_like(lats)
thedata[5:8, 7:13] = 1
pc = ccrs.PlateCarree()
ax = plt.axes(projection=ccrs.Mercator())
ax.coastlines()
# Some decoration to see where we are
gl = ax.gridlines(crs=pc,
draw_labels=True,
linewidth=2, color='gray', alpha=0.5,
linestyle='--')
gl.xformatter = LONGITUDE_FORMATTER
gl.yformatter = LATITUDE_FORMATTER
mycmap = mcolors.ListedColormap(['white', 'black'])
bounds=[0, 0.5, 1]
norm = mcolors.BoundaryNorm(bounds, mycmap.N)
im = ax.pcolormesh(lons, lats, thedata, cmap=mycmap,
norm=norm, transform=pc)
proj_extent = np.array(list(pc.x_limits) + list(pc.y_limits))
data_extent = np.array((lons[0,0], lons[-1,-1], lats[0,0], lats[-1,-1]))
# Make the extent larger to see a margin outside of the domain
ax.set_extent(data_extent + [-2, 2, -2, 2])
# Create a path which has the exterior of the map, with an interior of the data we care about.
path_with_hole = Path([[proj_extent[0], proj_extent[3]],
[proj_extent[1], proj_extent[3]],
[proj_extent[1], proj_extent[2]],
[proj_extent[0], proj_extent[2]],
[proj_extent[0], proj_extent[3]],
[data_extent[0], data_extent[2]],
[data_extent[1], data_extent[2]],
[data_extent[1], data_extent[3]],
[data_extent[0], data_extent[3]],
[data_extent[0], data_extent[2]]],
codes=[Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO, Path.LINETO,
Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO, Path.LINETO])
geom = mpatches.PathPatch(path_with_hole, facecolor='lightgrey',
edgecolor='white',
hatch='xxxx', alpha=0.6, transform=pc)
ax.add_patch(geom)
plt.show()
HTH
编辑:您的问题特别提及LambertConformal,以及此解决方案似乎不起作用的事实。事实证明,问题不在于解决方案,而是Cartopy的LambertConformal定义本身具有太低的分辨率。
解决方法非常严重:必须覆盖LambertConformal投影并修改幻数阈值。这将在未来变得更加容易。
class BetterLambertConformal(ccrs.LambertConformal):
def __init__(self, *args, **kwargs):
ccrs.LambertConformal.__init__(self, *args, **kwargs)
self._threshold = 1e4
@property
def threshold(self):
return self._threshold
ax = plt.axes(projection=BetterLambertConformal())