Xarray Roll与Cartopy add_cyclic_point函数冲突问题求助
解决跨0度经线SST绘图的白线问题 & 非均匀经度下的循环点处理
我明白你遇到的问题了——当用roll调整经度范围后,不仅数据和坐标的匹配容易出错,还会导致经度数组非均匀,没法用add_cyclic_point补全0度附近的缺口。这里有两个更可靠的解决方案,帮你搞定这个问题:
方案一:转换经度到-180~180范围(最推荐)
NOAA的sst.mnmean.nc默认经度是0360格式,直接转成-180180后,选180W到20E的区域会非常直观,而且经度保持均匀间隔,完美适配add_cyclic_point:
import matplotlib.pyplot as plt import cartopy.crs as ccrs import cartopy.feature as cf from cartopy.mpl.ticker import LongitudeFormatter, LatitudeFormatter from cartopy.util import add_cyclic_point import xarray as xr # 读取数据并转换经度格式 dset = xr.open_dataset('sst.mnmean.nc') # 把0~360经度转成-180~180 dset = dset.assign_coords(lon=(((dset.lon + 180) % 360) - 180)) # 直接选择180W(-180)到20E(20)的区域 var_sub = dset.sst.sel(lon=slice(-180, 20)) lat_sub = dset.lat lon_sub = dset.lon.sel(lon=slice(-180, 20)) # 添加循环点,解决0度附近的白线问题 # 这里axis=2是因为sst的维度是(time, lat, lon),根据你的数据维度调整 var_cyclic, lon_cyclic = add_cyclic_point(var_sub.values, coord=lon_sub.values, axis=2) # 绘图部分 fig = plt.figure(figsize=(12, 6)) ax = plt.axes(projection=ccrs.PlateCarree()) ax.add_feature(cf.COASTLINE, linewidth=0.8) ax.add_feature(cf.BORDERS, linestyle=':', linewidth=0.5) # 绘制SST填色图(这里取最后一个时间步做示例) im = ax.contourf(lon_cyclic, lat_sub, var_cyclic[-1, :, :], transform=ccrs.PlateCarree(), cmap='coolwarm', levels=20) # 设置刻度和格式 ax.set_xticks([-180, -120, -60, 0, 20], crs=ccrs.PlateCarree()) ax.set_yticks([-60, -30, 0, 30, 60], crs=ccrs.PlateCarree()) lon_formatter = LongitudeFormatter(zero_direction_label=True) lat_formatter = LatitudeFormatter() ax.xaxis.set_major_formatter(lon_formatter) ax.yaxis.set_major_formatter(lat_formatter) # 添加色标 plt.colorbar(im, orientation='horizontal', pad=0.05, label='Sea Surface Temperature (°C)') plt.title('Sea Surface Temperature (180°W to 20°E)') plt.tight_layout() plt.show()
这个方法的核心是先统一经度格式,避免了roll操作带来的坐标混乱,而且经度始终是均匀间隔的,add_cyclic_point可以正常工作,彻底解决白线问题。
方案二:手动补全循环点(适合坚持用0~360经度的场景)
如果你不想转换经度格式,可以手动拼接数据和坐标,补全0度附近的重复点,绕过add_cyclic_point对均匀经度的要求:
import matplotlib.pyplot as plt import cartopy.crs as ccrs import cartopy.feature as cf from cartopy.mpl.ticker import LongitudeFormatter, LatitudeFormatter import xarray as xr import numpy as np dset = xr.open_dataset('sst.mnmean.nc') # 拆分并拼接目标区域:180~360(对应180W~0°) + 0~20(对应0°~20E) dset_part1 = dset.sel(lon=slice(180, 360)) dset_part2 = dset.sel(lon=slice(0, 20)) dset_combined = xr.concat([dset_part1, dset_part2], dim='lon') # 提取数据和坐标 var_combined = dset_combined.sst.values lon_combined = dset_combined.lon.values lat_combined = dset_combined.lat.values # 手动添加循环点:把第一个经度值+360,同时复制第一个数据点到末尾 # 这样Cartopy会把360°和0°识别为同一个位置,消除白线 lon_cyclic = np.append(lon_combined, lon_combined[0] + 360) # 注意维度:sst是(time, lat, lon),所以要在lon维度(axis=2)拼接 var_cyclic = np.concatenate([var_combined, var_combined[:, :, [0]]], axis=2) # 绘图 fig = plt.figure(figsize=(12, 6)) ax = plt.axes(projection=ccrs.PlateCarree()) ax.add_feature(cf.COASTLINE, linewidth=0.8) ax.add_feature(cf.BORDERS, linestyle=':', linewidth=0.5) im = ax.contourf(lon_cyclic, lat_combined, var_cyclic[-1, :, :], transform=ccrs.PlateCarree(), cmap='coolwarm', levels=20) # 设置刻度,显示180W、120W、60W、0°、20E ax.set_xticks([180, 240, 300, 360, 20], crs=ccrs.PlateCarree()) ax.set_yticks([-60, -30, 0, 30, 60], crs=ccrs.PlateCarree()) lon_formatter = LongitudeFormatter(zero_direction_label=True) lat_formatter = LatitudeFormatter() ax.xaxis.set_major_formatter(lon_formatter) ax.yaxis.set_major_formatter(lat_formatter) plt.colorbar(im, orientation='horizontal', pad=0.05, label='Sea Surface Temperature (°C)') plt.title('Sea Surface Temperature (180°W to 20°E)') plt.tight_layout() plt.show()
这个方法不需要依赖add_cyclic_point,手动补全了0度附近的数据连续性,同样能消除白线。
内容的提问来源于stack exchange,提问作者whagi
相关产品推荐
相关产品推荐

