我提供一个如下的maskout.py代码(这份代码由于经过多位大佬们的完善,具体出自谁手我已经不太清楚了,反正感谢大佬们辛苦开发),大家使用前直接运行一下下面的代码或者import maskout即可。
import shapefile
from matplotlib.path import Path
from matplotlib.patches import PathPatch
from shapely.geometry import Point as ShapelyPoint
from shapely.geometry import Polygon as ShapelyPolygon
from collections.abc import Sized, Container, Iterable
def shp2clip(originfig,ax,region_shpfile,clabel=None,vcplot=None):
sf = shapefile.Reader(region_shpfile)
for shape_rec in sf.shapeRecords():
vertices = []
codes = []
pts = shape_rec.shape.points
prt = list(shape_rec.shape.parts) + [len(pts)]
for i in range(len(prt) - 1):
for j in range(prt[i], prt[i+1]):
vertices.append((pts[j][0], pts[j][1]))
codes += [Path.MOVETO]
codes += [Path.LINETO] * (prt[i+1] - prt[i] -2)
codes += [Path.CLOSEPOLY]
clip = Path(vertices, codes)
clip = PathPatch(clip, transform=ax.transData)
if vcplot:
if isinstance(originfig,Iterable):
for ivec in originfig:
ivec.set_clip_path(clip)
else:
originfig.set_clip_path(clip)
else:
for contour in originfig.collections:
contour.set_clip_path(clip)
if clabel:
clip_map_shapely = ShapelyPolygon(vertices)
for text_object in clabel:
if not clip_map_shapely.contains(ShapelyPoint(text_object.get_position())):
text_object.set_visible(False)
return clip
cnmpas是Clarmy吱声公众号的大佬开发的,非常好用!安装方法如下:
conda install -c conda-forge cnmaps
唯一一个小小的问题,就是在使用过程中必须保持shapely=1.8.5,不然部分函数在使用时候会出现报错(如下)。
遇到这种报错直接运行如下命令再重启内核即可解决。
pip install shapely==1.8.5
salem是github上fmaussion大佬开发的,也非常好用,安装方法如下:
conda install -c conda-forge salem
但安装好后,第一次import salem的时候,会要求联网下载salem-sample-data。而这个东西比较难下载,很少直接下成功。推荐自己直接去fmaussion的github主页上download下来,然后把压缩包重新命名后放在~/.salem_cache文件夹下面即可。
下载后放在~/.salem_cache文件夹下。
注意:请注意下载下来解压后文件夹的命名。具体如何命名,可以根据import salem下面的报错,找到这个~/anaconda3/envs/cnmaps/lib/python3.10/site-packages/salem/utils.py文件,加入print(ofile)看看具体的命名。
这是报错。
这是根据报错找到文件加入print(ofile)。
这是修改后再次运行import salem,会告诉我们应该修改的具体名字。
先给个亲身体验后的总结:
综上,如果不想麻烦,只需要裁剪自己感兴趣的区域,直接上maskout就行,但注意不要使用central_longitude等使图片偏移的参数;如果处理省市县级数据,建议使用cnmaps;salem可以作为cnmaps的补充,处理那些不在默认shp内的区域,比如青藏高原。
具体使用方法可以看本文最前面的几篇推送,介绍比较详细,下面放几个代码示例:
import xarray as xr
import numpy as np
import cartopy.crs as ccrs
import cartopy.feature as cfeat
from cartopy.mpl.ticker import LongitudeFormatter,LatitudeFormatter
import matplotlib.pyplot as plt
ds = xr.open_dataset('analysis/data.nc')
fig, ax = plt.subplots(1, 1, figsize=(16, 12), dpi=200, subplot_kw={'projection': ccrs.PlateCarree(central_longitude=180)})
fig.subplots_adjust(hspace=0.3, wspace=0.3)
ax.coastlines(lw=0.7)
clevs = np.arange(-10, 10, 1)
im = ax.contourf(ds.lon, ds.lat, ds['t2m'],
clevs, cmap='coolwarm', extend='both', transform=ccrs.PlateCarree())
ax.set_xticks([ 60,120,180,240,300], crs=ccrs.PlateCarree())
ax.set_yticks([ -90,-60, -30, 0, 30, 60,90], crs=ccrs.PlateCarree())
lon_formatter = LongitudeFormatter(zero_direction_label=False)
lat_formatter = LatitudeFormatter()
ax.xaxis.set_major_formatter(lon_formatter)
ax.yaxis.set_major_formatter(lat_formatter)
plt.show()
import shapefile
from matplotlib.path import Path
from matplotlib.patches import PathPatch
from shapely.geometry import Point as ShapelyPoint
from shapely.geometry import Polygon as ShapelyPolygon
from collections.abc import Sized, Container, Iterable
def shp2clip(originfig,ax,region_shpfile,clabel=None,vcplot=None):
sf = shapefile.Reader(region_shpfile)
for shape_rec in sf.shapeRecords():
vertices = []
codes = []
pts = shape_rec.shape.points
prt = list(shape_rec.shape.parts) + [len(pts)]
for i in range(len(prt) - 1):
for j in range(prt[i], prt[i+1]):
vertices.append((pts[j][0], pts[j][1]))
codes += [Path.MOVETO]
codes += [Path.LINETO] * (prt[i+1] - prt[i] -2)
codes += [Path.CLOSEPOLY]
clip = Path(vertices, codes)
clip = PathPatch(clip, transform=ax.transData)
if vcplot:
if isinstance(originfig,Iterable):
for ivec in originfig:
ivec.set_clip_path(clip)
else:
originfig.set_clip_path(clip)
else:
for contour in originfig.collections:
contour.set_clip_path(clip)
if clabel:
clip_map_shapely = ShapelyPolygon(vertices)
for text_object in clabel:
if not clip_map_shapely.contains(ShapelyPoint(text_object.get_position())):
text_object.set_visible(False)
return clip
#处理画图----------------------------------------------------
import xarray as xr
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.feature as cfeature
from cartopy.mpl.ticker import LongitudeFormatter,LatitudeFormatter
import numpy as np
TP_shp = '/work/home/zhxia/my_tool/TP_shp/TPBoundary_new_2021/TPBoundary_new(2021).shp'
ds = xr.open_dataset('analysis/Pangu_ERA5_fct360_winter.nc')
lon = ds.lon.values
lat = ds.lat.values
lons, lats = np.meshgrid(lon, lat)
fig, ax = plt.subplots(1, 1, figsize=(16, 12), dpi=200, subplot_kw={'projection': ccrs.PlateCarree()}) #不能使用central_longitude=180,否则裁剪的位置不对
fig.subplots_adjust(hspace=0.3, wspace=0.3)
ax.coastlines(lw=0.7)
clevs = np.arange(-10, 10, 1)
im = ax.contourf(lons, lats, ds['t2m'],
clevs, cmap='coolwarm', extend='both', transform=ccrs.PlateCarree())
ax.set_xticks([ -120, -60, 0, 60, 120], crs=ccrs.PlateCarree()) #不使用central_longitude=180后,这里相应修改一下方便显示
ax.set_yticks([ -90,-60, -30, 0, 30, 60,90], crs=ccrs.PlateCarree())
lon_formatter = LongitudeFormatter(zero_direction_label=False)
lat_formatter = LatitudeFormatter()
ax.xaxis.set_major_formatter(lon_formatter)
ax.yaxis.set_major_formatter(lat_formatter)
clip1=shp2clip(im,ax,TP_shp) #这一步裁剪
plt.show()
import xarray as xr
import numpy as np
from xarray.backends import NetCDF4DataStore
import salem
from datetime import datetime
import cartopy.crs as ccrs
import cartopy.feature as cfeat
from cartopy.mpl.ticker import LongitudeFormatter,LatitudeFormatter
from cartopy.io.shapereader import Reader, natural_earth
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
import geopandas
ds = xr.open_dataset('data.nc')
TP_shp = '/work/home/zhxia/my_tool/TP_shp/TPBoundary_new_2021/TPBoundary_new(2021).shp'
shp = geopandas.read_file(TP_shp)
t = ds['t2m'].salem.roi(shape=shp)
fig, ax = plt.subplots(1, 1, figsize=(16, 12), dpi=200, subplot_kw={'projection': ccrs.PlateCarree(central_longitude=180)})
fig.subplots_adjust(hspace=0.3, wspace=0.3)
ax.coastlines(lw=0.7)
clevs = np.arange(-10, 10, 1)
im = ax.contourf(ds.lon, ds.lat, t,
clevs, cmap='coolwarm', extend='both', transform=ccrs.PlateCarree())
ax.set_xticks([ 60,120,180,240,300], crs=ccrs.PlateCarree())
ax.set_yticks([ -90,-60, -30, 0, 30, 60,90], crs=ccrs.PlateCarree())
lon_formatter = LongitudeFormatter(zero_direction_label=False)
lat_formatter = LatitudeFormatter()
ax.xaxis.set_major_formatter(lon_formatter)
ax.yaxis.set_major_formatter(lat_formatter)
plt.show()
通过裁剪图片的方法
import numpy as np
from cnmaps import get_adm_maps
import matplotlib.pyplot as plt
map_polygon = get_adm_maps(country='中华人民共和国', record='first', only_polygon=True)
ds = xr.open_dataset('data.nc')
fig, ax = plt.subplots(1, 1, figsize=(16, 12), dpi=200, subplot_kw={'projection': ccrs.PlateCarree(central_longitude=180)})
fig.subplots_adjust(hspace=0.3, wspace=0.3)
ax.coastlines(lw=0.7)
clevs = np.arange(-10, 10, 1)
im = ax.contourf(ds.lon, ds.lat, ds['t2m'],
clevs, cmap='coolwarm', extend='both', transform=ccrs.PlateCarree())
ax.set_xticks([ 60,120,180,240,300], crs=ccrs.PlateCarree())
ax.set_yticks([ -90,-60, -30, 0, 30, 60,90], crs=ccrs.PlateCarree())
lon_formatter = LongitudeFormatter(zero_direction_label=False)
lat_formatter = LatitudeFormatter()
ax.xaxis.set_major_formatter(lon_formatter)
ax.yaxis.set_major_formatter(lat_formatter)
clip_contours_by_map(im, map_polygon)
draw_map(map_polygon, color='k', linewidth=1)
ax.set_extent(map_polygon.get_extent(buffer=1))
plt.show()
通过掩膜数据的方法
import numpy as np
from cnmaps import get_adm_maps
import matplotlib.pyplot as plt
ds = xr.open_dataset('data.nc')
lon = ds.lon.values
lat = ds.lat.values
lons, lats = np.meshgrid(lon, lat)
map_polygon = get_adm_maps(country='中华人民共和国', record='first', only_polygon=True)
maskout_data = map_polygon.maskout(lons, lats, ds['t2m'])
fig, ax = plt.subplots(1, 1, figsize=(16, 12), dpi=200, subplot_kw={'projection': ccrs.PlateCarree(central_longitude=180)})
fig.subplots_adjust(hspace=0.3, wspace=0.3)
ax.coastlines(lw=0.7)
clevs = np.arange(-10, 10, 1)
im = ax.contourf(lons, lats, maskout_data,
clevs, cmap='coolwarm', extend='both', transform=ccrs.PlateCarree())
ax.set_xticks([ 60,120,180,240,300], crs=ccrs.PlateCarree())
ax.set_yticks([ -90,-60, -30, 0, 30, 60,90], crs=ccrs.PlateCarree())
lon_formatter = LongitudeFormatter(zero_direction_label=False)
lat_formatter = LatitudeFormatter()
ax.xaxis.set_major_formatter(lon_formatter)
ax.yaxis.set_major_formatter(lat_formatter)
clip_contours_by_map(im, map_polygon)
draw_map(map_polygon, color='k', linewidth=1)
ax.set_extent(map_polygon.get_extent(buffer=1))
plt.show()