cartopy气象绘图
Cartopy 地图绘制方法指南
本文档整理了 cartopy 库在地图投影、海岸线、国界/省界/九段线、经纬度标记和区域选择等方面的使用方法。
一、地图投影 — Projection
1.1 导入与基本设置
import cartopy.crs as ccrs
# 等经纬度投影(最常用),central_longitude 设置中心经度ax = fig.add_subplot(projection=ccrs.PlateCarree(central_longitude=180))
# 或用 subplots 方式fig, ax = plt.subplots(subplot_kw={'projection': ccrs.PlateCarree(central_longitude=180)})| 参数 | 说明 |
|---|---|
central_longitude | 地图中心经度。180 表示太平洋为中心(气象常用),0 表示大西洋为中心。默认 0 |
1.2 其他常用投影
ccrs.PlateCarree() # 等经纬度(最常用)ccrs.PlateCarree(central_longitude=180) # 太平洋中心ccrs.Robinson() # Robinson(全球)ccrs.Mollweide() # Mollweide(全球)ccrs.Mercator() # 墨卡托(航海)ccrs.LambertConformal() # 兰伯特等角(中纬度)ccrs.Orthographic(central_longitude=110, central_latitude=35) # 正射投影(球面)ccrs.NorthPolarStereo() # 北极极射投影| 投影 | 代码 | 适用场景 |
|---|---|---|
| PlateCarree | ccrs.PlateCarree(central_longitude=180) | 等距圆柱,气象最常用 |
| Mercator | ccrs.Mercator() | 墨卡托,低纬度/航海 |
| LambertConformal | ccrs.LambertConformal() | 兰勃特,中纬度地区 |
| Robinson | ccrs.Robinson() | 全球地图 |
| Orthographic | ccrs.Orthographic() | 球面视角 |
| NorthPolarStereo | ccrs.NorthPolarStereo() | 北极地区 |
| Mollweide | ccrs.Mollweide() | 全球等面积 |
除
central_longitude外,PlateCarree还支持central_latitude参数设定中心纬度。
二、坐标转换 — transform
绘图时必须指定数据的坐标系,通常数据是等经纬度的,使用 ccrs.PlateCarree():
ax.contourf(lon, lat, data, transform=ccrs.PlateCarree())ax.quiver(lon, lat, u, v, transform=ccrs.PlateCarree())ax.scatter(lon, lat, transform=ccrs.PlateCarree())ax.plot(lon, lat, transform=ccrs.PlateCarree())重要:几乎所有的数据绘制方法都需要加
transform=ccrs.PlateCarree(),否则 cartopy 无法正确将数据坐标映射到地图投影上。
三、海岸线 — coastlines
ax.coastlines(resolution='110m', color='grey', linewidth=1.5, zorder=1)ax.coastlines() # 默认参数| 参数 | 说明 |
|---|---|
resolution | 精度:'10m'(最高), '50m', '110m'(最低,默认) |
color | 颜色 |
linewidth / lw | 线宽 |
zorder | 图层顺序,数值大的在上层 |
也可以通过 cfeature 方式指定分辨率:
ax.add_feature(cfeature.COASTLINE.with_scale('110m'), linewidth=0.8)四、地图要素 — Features
4.1 海洋/陆地遮罩
遮罩陆地(只显示海洋数据):
# 先画数据,再覆盖陆地ax.contourf(lon, lat, data, levels, cmap='RdBu_r', transform=ccrs.PlateCarree())ax.add_feature(cfeature.LAND, facecolor='white', edgecolor='none', zorder=10)遮罩海洋(只显示陆地数据):
ax.contourf(lon, lat, data, levels, cmap='RdBu_r', transform=ccrs.PlateCarree())ax.add_feature(cfeature.OCEAN, facecolor='white', edgecolor='none', zorder=10)| 参数 | 说明 |
|---|---|
facecolor | 遮罩填充色,通常 'white' 或 'lightgray' |
edgecolor | 'none' 避免遮罩边界出现线条 |
zorder | 必须设为 10(大于 contourf 的默认层级),确保遮罩覆盖在数据之上 |
alpha | 透明度,可实现半透明遮罩效果 |
注意:
cfeature.LAND/OCEAN精度有限(基于 Natural Earth),对严格科研绘图推荐使用 陆地海洋 mask 文件 配合np.ma.masked_where实现精确遮罩。
常用 feature:
| Feature | 说明 |
|---|---|
cfeature.OCEAN | 海洋区域 |
cfeature.LAND | 陆地区域 |
cfeature.COASTLINE | 海岸线 |
cfeature.RIVERS | 河流 |
cfeature.LAKES | 湖泊 |
cfeature.BORDERS | 国界线(自然地球数据,不含中国标准) |
注意:
cfeature.BORDERS使用的是 Natural Earth 数据,不包含中国主张的国界线。如需正确国界,请用下面的自定义方式。
五、中国地图要素
5.1 直接读取 shp 文件画国界
import cartopy.io.shapereader as shpreader
shp_dir = 'F:/SynologyDrive/Python_data/shpfile/'
# 读取国界 shp 文件shps = shpreader.Reader(shp_dir + '国家矢量.shp')ax.add_geometries(shps.geometries(), ccrs.PlateCarree(), linewidth=0.5, edgecolor='k', facecolor='none')add_geometries() 参数说明:
| 参数 | 说明 |
|---|---|
geometries | shapereader 读取的几何对象 |
crs | 几何对象所在的坐标系(通常 ccrs.PlateCarree()) |
linewidth | 线宽 |
edgecolor | 边界颜色 |
facecolor | 填充颜色,'none' 表示透明(只画边界) |
5.2 自定义 Feature 方式(推荐 — 可复用)
import cartopy.feature as cfeatureimport cartopy.io.shapereader as shpreader
def china_map_feature(): provinces = cfeature.ShapelyFeature( shpreader.Reader(shp_dir + '2022省矢量.shp').geometries(), ccrs.PlateCarree(), edgecolor='k', facecolor='none') nine_lines = cfeature.ShapelyFeature( shpreader.Reader(shp_dir + '九段线.shp').geometries(), ccrs.PlateCarree(), edgecolor='k', facecolor='none') return provinces, nine_lines
provinces, nine_lines = china_map_feature()ax.add_feature(provinces, linewidth=0.3, zorder=2)ax.add_feature(nine_lines, linewidth=0.5, zorder=2)cfeature.ShapelyFeature 参数:
| 参数 | 说明 |
|---|---|
geometries | 几何对象集合 |
crs | 坐标系 |
edgecolor | 边界颜色(默认 black) |
facecolor | 填充颜色(默认 steelblue) |
ax.add_feature() 参数:
| 参数 | 说明 |
|---|---|
feature | Feature 对象 |
linewidth | 线宽 |
edgecolor | 边界颜色(可覆盖 Feature 默认值) |
facecolor | 填充颜色(可覆盖) |
zorder | 图层顺序 |
六、经纬度标记
6.1 设置刻度位置
# X 轴(经度)刻度ax.set_xticks(range(0, 370, 60), crs=ccrs.PlateCarree()) # 每60度一个ax.set_xticks([0, 60, 120, 180, 240, 300, 360], crs=ccrs.PlateCarree())ax.set_xticks(np.arange(0, 361, 15), crs=ccrs.PlateCarree()) # 每15度一个
# Y 轴(纬度)刻度ax.set_yticks(range(-90, 100, 30), crs=ccrs.PlateCarree()) # 每30度一个ax.set_yticks([-90, -60, -30, 0, 30, 60, 90], crs=ccrs.PlateCarree())ax.set_yticks(np.arange(-60, 61, 10), crs=ccrs.PlateCarree()) # 每10度一个set_xticks() / set_yticks() 参数:
| 参数 | 说明 |
|---|---|
ticks | 刻度位置列表 |
crs | 必须指定 ccrs.PlateCarree() 表示刻度值对应的是经纬度坐标 |
minor | True 设置小刻度(不常用) |
技巧:南北边界
range需多设 1°(如range(-90, 91, 30)而非range(-90, 90, 30)),否则地图最边缘的经纬度网格线不会显示。
6.2 格式化刻度标签(自动加 °E/°W、°N/°S)
from cartopy.mpl.ticker import LongitudeFormatter, LatitudeFormatter
ax.xaxis.set_major_formatter(LongitudeFormatter())ax.yaxis.set_major_formatter(LatitudeFormatter())| Formatter | 效果 |
|---|---|
LongitudeFormatter() | 经度格式:0°, 30°E, 150°W |
LatitudeFormatter() | 纬度格式:0°, 30°N, 30°S |
高级参数:
| 参数 | 说明 |
|---|---|
zero_direction_label=False | 0° 不显示方向(默认 False) |
dateline_direction_label=True | 180°(日期线)显示方向 |
6.3 调整刻度样式
ax.tick_params(labelsize=12) # 刻度标签字体大小ax.tick_params(which='major', length=6) # 主刻度线长度6.4 坐标轴标签
ax.set_xlabel("Longitude", fontsize=15)ax.set_ylabel("Latitude", fontsize=15)七、选定经纬度范围 — set_extent
# 全球范围(太平洋中心)ax.set_extent([0, 358, -80, 80], crs=ccrs.PlateCarree())
# 中国及周边ax.set_extent([70, 140, 0, 60], crs=ccrs.PlateCarree())
# 热带区域ax.set_extent([0, 360, -60, 60], crs=ccrs.PlateCarree())参数格式: [lon_min, lon_max, lat_min, lat_max]
| 参数 | 说明 |
|---|---|
extents | [west_lon, east_lon, south_lat, north_lat] |
crs | 范围所采用的坐标系,通常 ccrs.PlateCarree() |
注意:
360和0在等经纬度投影中是同一条经线,为避免显示异常,常用358作为右边界或在参数中使用0~360范围。
消除白边技巧:当数据格点不是整数经纬度时,地图边缘可能出现白边。解决方法:(1) 读取数据时在目标范围基础上多读一个格点;(2)
set_extent的边界值比目标范围多设 1°(如目标 30~180 则设为[30, 181, ...])。
八、网格线 — gridlines
ax.gridlines()九、sacpy.Map 地图增强方法
sacpy.Map 在 cartopy 基础上提供了快捷的地图设置方法:
9.1 init_map — 初始化地图
ax.init_map(stepx=50, stepy=20, smallx=10, smally=5)| 参数 | 说明 |
|---|---|
stepx | 经度主刻度间隔 |
stepy | 纬度主刻度间隔 |
smallx | 经度次刻度间隔 |
smally | 纬度次刻度间隔 |
init_map自动设置刻度位置、格式化长纬度标签、画海岸线等。
9.2 sig_plot — 显著性打点
ax.sig_plot(lon, lat, p_value, thrshd=0.10, color='gray')| 参数 | 说明 |
|---|---|
lon, lat | 经纬度坐标 |
p_value | p 值数组 |
thrshd | 显著性阈值(如 0.05, 0.10) |
color | 点颜色 |
sig_plot会在 p 值小于阈值的格点上打点,表示通过显著性检验的区域。
9.3 scontourf + scontour — 增强填色图
m = ax.scontourf(lon, lat, data, cmap='RdBu_r', levels=np.arange(-1.2, 1.4, 0.2), extend='both')ax.scontour(m, colors='black')
scontour自动提取m的 levels 画等值线,比手动contour更简洁。
十、完整示例模板
10.1 全球地图(太平洋中心)
import cartopy.crs as ccrsimport cartopy.feature as cfeaturefrom cartopy.mpl.ticker import LongitudeFormatter, LatitudeFormatter
fig = plt.figure(figsize=(8, 5))ax = fig.add_subplot(projection=ccrs.PlateCarree(central_longitude=180))
# 画数据cf = ax.contourf(lon, lat, data, levels, cmap='RdBu_r', extend='both', transform=ccrs.PlateCarree())
# 海岸线ax.coastlines(lw=1.5, resolution='110m', color='grey', zorder=1)
# 经纬度刻度ax.set_xticks([0, 60, 120, 180, 240, 300, 360], crs=ccrs.PlateCarree())ax.set_yticks([-90, -60, -30, 0, 30, 60, 90], crs=ccrs.PlateCarree())ax.xaxis.set_major_formatter(LongitudeFormatter())ax.yaxis.set_major_formatter(LatitudeFormatter())ax.set_extent([0, 356, -80, 80], crs=ccrs.PlateCarree())
# 色标cb = fig.colorbar(cf, location='bottom', shrink=0.7)10.2 中国区域地图(含国界、省界、九段线)
import cartopy.crs as ccrsimport cartopy.io.shapereader as shpreaderimport cartopy.feature as cfeature
fig = plt.figure(figsize=(8, 5))ax = fig.add_subplot(projection=ccrs.PlateCarree(central_longitude=180))
# 画数据cf = ax.contourf(lon, lat, data, levels, cmap='RdBu_r', extend='both', transform=ccrs.PlateCarree())
# 国界shps = shpreader.Reader(shp_dir + '国家矢量.shp')ax.add_geometries(shps.geometries(), ccrs.PlateCarree(), linewidth=0.5, edgecolor='k', facecolor='none')
# 省界 & 九段线(自定义 Feature)provinces = cfeature.ShapelyFeature( shpreader.Reader(shp_dir + '2022省矢量.shp').geometries(), ccrs.PlateCarree(), edgecolor='k', facecolor='none')nine_lines = cfeature.ShapelyFeature( shpreader.Reader(shp_dir + '九段线.shp').geometries(), ccrs.PlateCarree(), edgecolor='k', facecolor='none')ax.add_feature(provinces, linewidth=0.3, zorder=2)ax.add_feature(nine_lines, linewidth=0.5, zorder=2)
# 海岸线 & 海洋填充ax.coastlines(resolution='10m', color='k', linewidth=0.5, zorder=2)ax.add_feature(cfeature.OCEAN, color='w', zorder=1)
# 经纬度刻度(精细)ax.set_xticks(range(0, 370, 10), crs=ccrs.PlateCarree())ax.set_yticks(range(-90, 100, 10), crs=ccrs.PlateCarree())ax.xaxis.set_major_formatter(LongitudeFormatter())ax.yaxis.set_major_formatter(LatitudeFormatter())ax.set_extent([70, 140, 0, 60], crs=ccrs.PlateCarree())十一、完整绘图流程(中国区域)
import xarray as xrimport numpy as npimport matplotlib.pyplot as pltimport cartopy.crs as ccrsimport cartopy.feature as cfeaturefrom cartopy.io import shapereaderfrom cartopy.mpl.ticker import LongitudeFormatter, LatitudeFormatter
# ========== 1. 中文字体 ==========plt.rcParams['font.sans-serif'] = ['SimHei']plt.rcParams['axes.unicode_minus'] = False
# ========== 2. 路径定义 ==========shp_dir = 'F:/SynologyDrive/Python_data/shpfile/'dat_dir = 'F:/SynologyDrive/Python_data/data/'
# ========== 3. 读取数据 ==========ds = xr.open_dataset(dat_dir + 'air.2m.mon.mean.nc')air = ds['air'].sel(time='2018-06-01')lon, lat = air['lon'], air['lat']
# ========== 4. 中国地图要素函数 ==========def china_map_feature(): provinces = cfeature.ShapelyFeature( shapereader.Reader(shp_dir + '2022省矢量.shp').geometries(), ccrs.PlateCarree(), edgecolor='k', facecolor='none') nine_lines = cfeature.ShapelyFeature( shapereader.Reader(shp_dir + '九段线.shp').geometries(), ccrs.PlateCarree(), edgecolor='k', facecolor='none') return provinces, nine_lines
# ========== 5. 创建画布 ==========fig = plt.figure(figsize=(14, 6))ax = plt.axes(projection=ccrs.PlateCarree(central_longitude=120))
# ========== 6. 绘制地图要素 ==========shps = shapereader.Reader(shp_dir + '国家矢量.shp')ax.add_geometries(shps.geometries(), ccrs.PlateCarree(), linewidth=0.5, edgecolor='k', facecolor='none')provinces, nine_lines = china_map_feature()ax.add_feature(provinces, linewidth=0.3, zorder=2)ax.add_feature(nine_lines, linewidth=0.5, zorder=2)ax.coastlines(resolution='110m', linewidth=0.8)
# ========== 7. 地图范围与刻度 ==========ax.set_extent([30, 182, -10, 81], crs=ccrs.PlateCarree())ax.set_xticks(range(30, 182, 10), crs=ccrs.PlateCarree())ax.set_yticks(range(-10, 81, 10), crs=ccrs.PlateCarree())ax.tick_params(labelsize=12)ax.xaxis.set_major_formatter(LongitudeFormatter())ax.yaxis.set_major_formatter(LatitudeFormatter())
# ========== 8. 绘制数据 ==========cf = ax.contourf(lon, lat, air, levels=np.linspace(267, 317, 25), cmap=plt.cm.RdBu_r, transform=ccrs.PlateCarree(), extend='both')
# ========== 9. 色标 ==========cbar = plt.colorbar(cf, ax=ax, orientation='horizontal', pad=0.05, shrink=0.85)cbar.set_label('Temperature (K)')
# ========== 10. 标题与保存 ==========ax.set_title('2m Air Temperature (2018-06-01)', fontsize=14)plt.tight_layout()plt.savefig('china_t2m.png', dpi=300, bbox_inches='tight')plt.show()流程总结:字体 → 路径 → 读数据 → 定义要素函数 → 画布 → 地图要素 → 范围/刻度 → 数据 → 色标 → 保存。
十二、多子图示例(含陆地/海洋遮罩)
fig, axes = plt.subplots( 3, 1, figsize=(16, 18), subplot_kw={'projection': ccrs.PlateCarree(central_longitude=120)})
# 统一设置所有子图的地图要素for ax in axes: shps = shapereader.Reader(shp_dir + '国家矢量.shp') ax.add_geometries(shps.geometries(), ccrs.PlateCarree(), linewidth=0.5, edgecolor='k', facecolor='none') provinces, nine_lines = china_map_feature() ax.add_feature(provinces, linewidth=0.3, zorder=2) ax.add_feature(nine_lines, linewidth=0.5, zorder=2) ax.set_extent([30, 181, -10, 81], crs=ccrs.PlateCarree()) ax.coastlines(resolution='110m', linewidth=0.8) ax.set_xticks(range(30, 181, 10), crs=ccrs.PlateCarree()) ax.set_yticks(range(-10, 81, 10), crs=ccrs.PlateCarree()) ax.tick_params(labelsize=12) ax.xaxis.set_major_formatter(LongitudeFormatter()) ax.yaxis.set_major_formatter(LatitudeFormatter())
level = np.linspace(267, 317, 25)
# 子图 1:全图cf1 = axes[0].contourf(lon, lat, air, levels=level, cmap=plt.cm.RdBu_r, transform=ccrs.PlateCarree(), extend='both')axes[0].set_title('Global Map', fontsize=14)
# 子图 2:遮罩陆地,只显示海洋cf2 = axes[1].contourf(lon, lat, air, levels=level, cmap=plt.cm.RdBu_r, transform=ccrs.PlateCarree(), extend='both')axes[1].add_feature(cfeature.LAND, facecolor='white', edgecolor='none', zorder=10)axes[1].set_title('Ocean Only (Land Masked)', fontsize=14)
# 子图 3:遮罩海洋,只显示陆地cf3 = axes[2].contourf(lon, lat, air, levels=level, cmap=plt.cm.RdBu_r, transform=ccrs.PlateCarree(), extend='both')axes[2].add_feature(cfeature.OCEAN, facecolor='white', edgecolor='none', zorder=10)axes[2].set_title('Land Only (Ocean Masked)', fontsize=14)
# 各子图独立 colorbarfor ax, cf in zip(axes, [cf1, cf2, cf3]): cbar = fig.colorbar(cf, ax=ax, orientation='horizontal', pad=0.08, shrink=0.5) cbar.set_label('Temperature (K)')
plt.tight_layout()plt.show()多子图的关键:用
for ax in axes循环统一设置地图要素,避免代码重复。
十三、注意事项
-
Shapefile 来源:国界、省界、九段线必须使用官方标准数据(如自然资源部发布版本),cartopy 内置的
cfeature.BORDERS不包含中国主张国界。 -
图层顺序(zorder):
- 等值线填色图(contourf):默认最底层(一般不设 zorder)
- 地图要素(国界、省界等):
zorder=2 - 陆地/海洋遮罩:
zorder=10(必须在 contourf 之后绘制,且 zorder 大于数据图层)
-
transform 参数:
contourf、contour、quiver、scatter、plot等绘制数据时都必须指定transform=ccrs.PlateCarree(),否则数据坐标与投影坐标不匹配。 -
add_geometriesvsadd_feature:add_geometries:直接传入 shapefile 几何对象,适合一次性使用add_feature:传入cfeature.Feature对象(含cfeature.ShapelyFeature封装),适合复用
-
避免重复 IO:当多个子图共用同一 shapefile 时,先用
china_map_feature()函数封装,在循环中复用,避免每次重复读取硬盘。 -
海陆遮罩替代方案:
cfeature.LAND/OCEAN精度有限(基于 Natural Earth),对于严格科研绘图,推荐使用 陆地海洋 mask 文件 配合np.ma.masked_where实现精确遮罩。
快速参考速查表
| 需求 | 代码 |
|---|---|
| 地图投影 | fig.add_subplot(projection=ccrs.PlateCarree(central_longitude=180)) |
| 坐标系转换 | transform=ccrs.PlateCarree() |
| 海岸线 | ax.coastlines(resolution='110m', color='grey', lw=1.5) |
| 海岸线 (Feature) | ax.add_feature(cfeature.COASTLINE.with_scale('110m'), lw=0.8) |
| 海洋填充 | ax.add_feature(cfeature.OCEAN, facecolor='white', edgecolor='none', zorder=10) |
| 陆地填充 | ax.add_feature(cfeature.LAND, facecolor='white', edgecolor='none', zorder=10) |
| 国界线(shp) | ax.add_geometries(shps.geometries(), ccrs.PlateCarree(), ...) |
| 省界线 | cfeature.ShapelyFeature(...) + ax.add_feature(...) |
| 九段线 | cfeature.ShapelyFeature(...) + ax.add_feature(...) |
| 经度刻度 | ax.set_xticks(range(...), crs=ccrs.PlateCarree()) |
| 纬度刻度 | ax.set_yticks(range(...), crs=ccrs.PlateCarree()) |
| 刻度格式化 | ax.xaxis.set_major_formatter(LongitudeFormatter()) |
| 设定范围 | ax.set_extent([w, e, s, n], crs=ccrs.PlateCarree()) |
| 消除白边 | 读数据时多读一格,set_extent 多设 1° |
| 网格线 | ax.gridlines() |
| 多子图统一设置 | for ax in axes: ... + china_map_feature() |
| 避免重复IO | 封装 china_map_feature() 函数 |
| init_map | ax.init_map(stepx=50, stepy=20) |
| 显著性打点 | ax.sig_plot(lon, lat, p_value, thrshd=0.05) |
| 显著性打点 | ax.sig_plot(lon, lat, p_value, thrshd=0.05) |
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!



