Cartopy 背景色(数据域外)
Cartopy background color (outside of data domain)
在 Cartopy 地图中,我希望未被任何数据(在我的域之外)覆盖的区域着色为例如浅灰色。
玩过 background_patch
并查看了这个示例 但仍然无法弄清楚如何做我想做的事。
这是一个人为的例子,我用红线使域边界可见。相反,我希望将红线以外的区域涂成浅灰色。
非常感谢!
编辑:
将投影更改为 LambertConformal 以证明下面提出的解决方案 () 仅适用于矩形网格。请参阅下面的其他数字。
import matplotlib.pyplot as plt
import matplotlib as mpl
import cartopy.crs as ccrs
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER
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
thedata = np.zeros_like(lats)
#some 'cloud' in the data
thedata[5:8,7:13] = 1
#theproj = ccrs.Mercator()
theproj = ccrs.LambertConformal() #choose another projection to obtain non-rectangular grid
ef, axar = plt.subplots(1,1, subplot_kw={'projection': theproj})#, 'axisbg': 'w'
ef.subplots_adjust(hspace=0.,wspace=0.,bottom=0.05,top=0.95,left=0.03,right=0.98)
axar.coastlines()
mycmap = mpl.colors.ListedColormap(['white', 'black'])
bounds=[0,0.5,1]
norm = mpl.colors.BoundaryNorm(bounds, mycmap.N)
im = axar.pcolormesh(lons,lats,thedata,cmap=mycmap, transform=ccrs.PlateCarree())
im.set_norm(norm)
#make the extent larger to see a margin outside of the domain
axar.set_extent([lons[0,0]-1,lons[-1,-1]+1,lats[0,0]-1,lats[-1,-1]+1])
#for illustration: make the domain bounds visible
#but instead of the red domain bounds I would like to have the background (outside of the domain) in some color (lightgrey)
axar.plot(lons[:,0],lats[:,0],'r', transform=ccrs.PlateCarree())
axar.plot(lons[:,-1],lats[:,0],'r', transform=ccrs.PlateCarree())
axar.plot(lons[0,:],lats[0,:],'r', transform=ccrs.PlateCarree())
axar.plot(lons[0,:],lats[-1,:],'r', transform=ccrs.PlateCarree())
#some decoration to see where we are
gl = axar.gridlines(crs=ccrs.PlateCarree(), draw_labels=True,
linewidth=2, color='gray', alpha=0.5, linestyle='--')
gl.xformatter = LONGITUDE_FORMATTER
gl.yformatter = LATITUDE_FORMATTER
plt.show()
当然,如果你不使用 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 图上实际看到的东西)来将该路径添加到轴上。
我们可以以显而易见的方式在 cartopy 图形中使用此技术:
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())
在 Cartopy 地图中,我希望未被任何数据(在我的域之外)覆盖的区域着色为例如浅灰色。
玩过 background_patch
并查看了这个示例
这是一个人为的例子,我用红线使域边界可见。相反,我希望将红线以外的区域涂成浅灰色。
非常感谢!
编辑:
将投影更改为 LambertConformal 以证明下面提出的解决方案 (
import matplotlib.pyplot as plt
import matplotlib as mpl
import cartopy.crs as ccrs
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER
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
thedata = np.zeros_like(lats)
#some 'cloud' in the data
thedata[5:8,7:13] = 1
#theproj = ccrs.Mercator()
theproj = ccrs.LambertConformal() #choose another projection to obtain non-rectangular grid
ef, axar = plt.subplots(1,1, subplot_kw={'projection': theproj})#, 'axisbg': 'w'
ef.subplots_adjust(hspace=0.,wspace=0.,bottom=0.05,top=0.95,left=0.03,right=0.98)
axar.coastlines()
mycmap = mpl.colors.ListedColormap(['white', 'black'])
bounds=[0,0.5,1]
norm = mpl.colors.BoundaryNorm(bounds, mycmap.N)
im = axar.pcolormesh(lons,lats,thedata,cmap=mycmap, transform=ccrs.PlateCarree())
im.set_norm(norm)
#make the extent larger to see a margin outside of the domain
axar.set_extent([lons[0,0]-1,lons[-1,-1]+1,lats[0,0]-1,lats[-1,-1]+1])
#for illustration: make the domain bounds visible
#but instead of the red domain bounds I would like to have the background (outside of the domain) in some color (lightgrey)
axar.plot(lons[:,0],lats[:,0],'r', transform=ccrs.PlateCarree())
axar.plot(lons[:,-1],lats[:,0],'r', transform=ccrs.PlateCarree())
axar.plot(lons[0,:],lats[0,:],'r', transform=ccrs.PlateCarree())
axar.plot(lons[0,:],lats[-1,:],'r', transform=ccrs.PlateCarree())
#some decoration to see where we are
gl = axar.gridlines(crs=ccrs.PlateCarree(), draw_labels=True,
linewidth=2, color='gray', alpha=0.5, linestyle='--')
gl.xformatter = LONGITUDE_FORMATTER
gl.yformatter = LATITUDE_FORMATTER
plt.show()
当然,如果你不使用 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 图上实际看到的东西)来将该路径添加到轴上。
我们可以以显而易见的方式在 cartopy 图形中使用此技术:
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())