Nature风格的北半球投影图,几行代码就能绘制...

Python优雅地绘制北半球投影地图

经常在Nature正刊和子刊中看到北半球或南半球投影的球形地图,非常美观~

这种图ArcGIS往往不方便绘制

在子刊***Nat. Clim. Chang.***中,通过北极俯视投影表示NAO和AO的EOF分解。

alt

Hamouda, M.E., Pasquero, C. & Tziperman, E. Decoupling of the Arctic Oscillation and North Atlantic Oscillation in a warmer climate. Nat. Clim. Chang. 11, 137–142 (2021). https://doi.org/10.1038/s41558-020-00966-8

如子刊Nature Geosci中表示了海冰的漂移,还添加了DEM底图,非常美观。

alt

Darby, D., Ortiz, J., Grosch, C. et al. 1,500-year cycle in the Arctic Oscillation identified in Holocene Arctic sea-ice drift. Nature Geosci 5, 897–900 (2012). https://doi.org/10.1038/ngeo1629

接下来让我们复现一下试试

数据

以海温数据为例,ERA5月温度平均,是一张721*1440的0.25degTIF

import rioxarray as rxr
file_name='/home/mw/project/SST.tif' 
ds=rxr.open_rasterio(file_name)
data = ds[0]

如果你有其它nc数据,也可以通过xarray读取

投影设置

其实cartopy提供了一些投影使用

如AzimuthalEquidistant

这是一种等距方位投影,是指使图上面积和相应的实际地面面积相等的方位投影,分为正轴,横轴、斜轴投影。其中纬线为同心圆弧,经线为圆的半径。该投影提供了围绕中心位置的精确角度和距离。其他角度、距离或区域可能会变形。

北半球的投影叫做AzimuthalEquidistant cartopy的投影列表是有这个投影的: https://scitools.org.uk/cartopy/docs/latest/reference/projections.html alt

import matplotlib.pyplot as plt
import cartopy.crs as ccrs

plt.figure(figsize=(6, 6))
ax = plt.axes(projection=ccrs.AzimuthalEquidistant(central_latitude=90))
ax.coastlines()
ax.gridlines()
ax.stock_img()
alt

绘图代码

接下来我们把数据添加

import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import numpy as np
import cartopy.feature as cfeature
import matplotlib.path as mpath
import mplotutils as mpu
from utils import plot

fig = plt.figure()

proj=ccrs.AzimuthalEquidistant(central_latitude=90)
ax = fig.add_subplot(111, projection=proj)
levels = np.linspace(270, 300, num=31)
data_renamed = data.rename({'x''lon''y''lat'})
h = plot.one_map_flat(data_renamed, ax, levels=levels, cmap="BrBG_r", mask_ocean=False, add_coastlines=True, add_land=False, plotfunc="contourf")

其中plot.one_map_flat是我之前撰写的绘图函数,感兴趣的同学可见相关推送

alt

现在的结果看起来不错,但太大了,我们如果只需要北半球的数据,则需要指定经纬度。

我们使用ax.set_extent([-180, 180, 30, 90])将经纬度指定在30°-90°

import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import numpy as np
import cartopy.feature as cfeature
import matplotlib.path as mpath
import mplotutils as mpu

fig = plt.figure()

proj=ccrs.AzimuthalEquidistant(central_latitude=90)
ax = fig.add_subplot(111, projection=proj)
levels = np.linspace(270, 300, num=31)
data_renamed = data.rename({'x''lon''y''lat'})
h = plot.one_map_flat(data_renamed, ax, levels=levels, cmap="BrBG_r", mask_ocean=False, add_coastlines=True, add_land=False, plotfunc="contourf")

ax.set_extent([-180, 180, 30, 90], crs=ccrs.PlateCarree())
image-20240613155059641
image-20240613155059641

再次添加更多要素:

import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import numpy as np
import cartopy.feature as cfeature
import matplotlib.path as mpath
import mplotutils as mpu

fig = plt.figure()

proj=ccrs.AzimuthalEquidistant(central_latitude=90)
ax = fig.add_subplot(111, projection=proj)
levels = np.linspace(270, 300, num=31)
data_renamed = data.rename({'x''lon''y''lat'})
h = plot.one_map_flat(data_renamed, ax, levels=levels, cmap="BrBG_r", mask_ocean=False, add_coastlines=True, add_land=False, plotfunc="contourf")

ax.set_extent([-180, 180, 30, 90], crs=ccrs.PlateCarree())

ax.add_feature(cfeature.OCEAN)
ax.add_feature(cfeature.LAND, edgecolor='black')
ax.add_feature(cfeature.LAKES, edgecolor='black')
ax.add_feature(cfeature.RIVERS)
ax.stock_img()
alt
  • proj=ccrs.AzimuthalEquidistant(central_latitude=90), ax = plt.axes(projection=proj)仅在定义图层的时候设置为该投影,其余设置content,绘制contourf都是PlateCaree
  • 绘制完结果也是正方形,和ArcGIS差不多,见之前的推文
  • cartopy灵活的地方在于适配matplotlib,因此可以设置一圆形进行相交:
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import numpy as np
import cartopy.feature as cfeature
import matplotlib.path as mpath
import mplotutils as mpu

fig = plt.figure()

proj=ccrs.AzimuthalEquidistant(central_latitude=90)
ax = fig.add_subplot(111, projection=proj)
levels = np.linspace(270, 300, num=31)
data_renamed = data.rename({'x''lon''y''lat'})
h = plot.one_map_flat(data_renamed, ax, levels=levels, cmap="BrBG_r", mask_ocean=False, add_coastlines=True, add_land=False, plotfunc="contourf")

ax.set_extent([-180, 180, 30, 90], crs=ccrs.PlateCarree())
ax.add_feature(cfeature.OCEAN)
ax.add_feature(cfeature.LAND, edgecolor='black')
ax.add_feature(cfeature.LAKES, edgecolor='black')
ax.add_feature(cfeature.RIVERS)
ax.stock_img()

theta = np.linspace(0, 2*np.pi, 100)
center, radius = [0.5, 0.5], 0.5
verts = np.vstack([np.sin(theta), np.cos(theta)]).T
circle = mpath.Path(verts * radius + center)
# 将该Path设置为GeoAxes的边界
ax.set_boundary(circle, transform=ax.transAxes)

cbar = mpu.colorbar(
    h, ax1=ax, size=0.05, #height
            shrink=0.05, #width
            pad=0.1, orientation='horizontal'
)

cbar.ax.tick_params(labelsize='small')
alt

现在的结果相当不错了,只需要设置一下经纬度线

import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import numpy as np
import cartopy.feature as cfeature
import matplotlib.path as mpath
import mplotutils as mpu

fig = plt.figure()

proj=ccrs.AzimuthalEquidistant(central_latitude=90)
ax = fig.add_subplot(111, projection=proj)
levels = np.linspace(270, 300, num=31)
data_renamed = data.rename({'x''lon''y''lat'})
h = plot.one_map_flat(data_renamed, ax, levels=levels, cmap="BrBG_r", mask_ocean=False, add_coastlines=True, add_land=False, plotfunc="contourf")


ax.set_extent([-180, 180, 30, 90], crs=ccrs.PlateCarree())
# 添加各种特征
ax.add_feature(cfeature.OCEAN)
ax.add_feature(cfeature.LAND, edgecolor='black')
ax.add_feature(cfeature.LAKES, edgecolor='black')
ax.add_feature(cfeature.RIVERS)
#ax.add_feature(cfeature.BORDERS)
ax.stock_img()

theta = np.linspace(0, 2*np.pi, 100)
center, radius = [0.5, 0.5], 0.5
verts = np.vstack([np.sin(theta), np.cos(theta)]).T
circle = mpath.Path(verts * radius + center)
# 将该Path设置为GeoAxes的边界
ax.set_boundary(circle, transform=ax.transAxes)

cbar = mpu.colorbar(
    h, ax1=ax, size=0.05, #height
            shrink=0.05, #width
            pad=0.1, orientation='horizontal'
)

cbar.ax.tick_params(labelsize='small')

gls = ax.gridlines(draw_labels=True, crs=ccrs.PlateCarree(), 
                   color='k', linestyle='dashed', linewidth=0.3, 
                   dms=True, x_inline=False, y_inline=False,
                   xlocs=np.arange(-180, 180 + 60, 60), ylocs=np.arange(0, 90 + 15, 15)
                  )

image-20240613155303429
image-20240613155303429

非常完美

感兴趣的同学可以后台回复【北半球】下载数据和代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

地学万事屋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值