cartopy气象绘图

2732 字
14 分钟
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() # 北极极射投影
投影代码适用场景
PlateCarreeccrs.PlateCarree(central_longitude=180)等距圆柱,气象最常用
Mercatorccrs.Mercator()墨卡托,低纬度/航海
LambertConformalccrs.LambertConformal()兰勃特,中纬度地区
Robinsonccrs.Robinson()全球地图
Orthographicccrs.Orthographic()球面视角
NorthPolarStereoccrs.NorthPolarStereo()北极地区
Mollweideccrs.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() 参数说明:

参数说明
geometriesshapereader 读取的几何对象
crs几何对象所在的坐标系(通常 ccrs.PlateCarree()
linewidth线宽
edgecolor边界颜色
facecolor填充颜色,'none' 表示透明(只画边界)

5.2 自定义 Feature 方式(推荐 — 可复用)#

import cartopy.feature as cfeature
import 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() 参数:

参数说明
featureFeature 对象
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() 表示刻度值对应的是经纬度坐标
minorTrue 设置小刻度(不常用)

技巧:南北边界 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()经度格式:, 30°E, 150°W
LatitudeFormatter()纬度格式:, 30°N, 30°S

高级参数:

参数说明
zero_direction_label=False0° 不显示方向(默认 False
dateline_direction_label=True180°(日期线)显示方向

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()

注意3600 在等经纬度投影中是同一条经线,为避免显示异常,常用 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_valuep 值数组
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 ccrs
import cartopy.feature as cfeature
from 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 ccrs
import cartopy.io.shapereader as shpreader
import 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 xr
import numpy as np
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.feature as cfeature
from cartopy.io import shapereader
from 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)
# 各子图独立 colorbar
for 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 循环统一设置地图要素,避免代码重复。


十三、注意事项#

  1. Shapefile 来源:国界、省界、九段线必须使用官方标准数据(如自然资源部发布版本),cartopy 内置的 cfeature.BORDERS 不包含中国主张国界。

  2. 图层顺序(zorder)

    • 等值线填色图(contourf):默认最底层(一般不设 zorder)
    • 地图要素(国界、省界等):zorder=2
    • 陆地/海洋遮罩:zorder=10(必须在 contourf 之后绘制,且 zorder 大于数据图层)
  3. transform 参数contourfcontourquiverscatterplot 等绘制数据时都必须指定 transform=ccrs.PlateCarree(),否则数据坐标与投影坐标不匹配。

  4. add_geometries vs add_feature

    • add_geometries:直接传入 shapefile 几何对象,适合一次性使用
    • add_feature:传入 cfeature.Feature 对象(含 cfeature.ShapelyFeature 封装),适合复用
  5. 避免重复 IO:当多个子图共用同一 shapefile 时,先用 china_map_feature() 函数封装,在循环中复用,避免每次重复读取硬盘。

  6. 海陆遮罩替代方案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_mapax.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)

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

cartopy气象绘图
http://liuhuangzao.top/posts/python/cartopy/
作者
硫磺皂
发布于
2026-07-15
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
硫磺皂
干嘛
公告
欢迎来到我的博客!这是一则示例公告。
音乐
封面

音乐

暂未播放

0:000:00
暂无歌词
分类
标签
站点统计
文章
8
分类
2
标签
5
总字数
13,914
运行时长
0
最后活动
0 天前
站点信息
构建平台
Local
博客版本
Firefly v6.13.10
文章许可
CC BY-NC-SA 4.0