Cartopy地理绘图中色彩映射的适配问题与优化需求
解决Cartopy气温场绘图中TwoSlopeNorm适配不均衡正负温的问题
问题根源
TwoSlopeNorm默认以0为中心对称分配颜色区间,当正温范围远大于负温时,仅有的微负温会被拉伸到整个蓝色色段的最底端,导致显示为深蓝色。局部放大正常是因为放大后数据范围缩小,区间分配重新适配。
解决方案
1. 动态加权调整色标分段
手动计算正负温区间的比例,拆分RdBu_r色标,让负温区间的颜色分段数匹配其数据范围占比,避免小范围负温占用全部蓝色区间:
import matplotlib.pyplot as plt from matplotlib.colors import TwoSlopeNorm, ListedColormap import cartopy.crs as ccrs import numpy as np # 模拟不均衡气温数据(正温占比大,少量微负温) temp_data = np.random.randn(100, 100) * 12 + 18 temp_data[temp_data < 0] *= 0.3 # 计算数据极值与中心 vmin, vmax = temp_data.min(), temp_data.max() vcenter = 0 # 计算正负区间长度比例 neg_range = abs(vmin - vcenter) pos_range = vmax - vcenter total_range = neg_range + pos_range neg_ratio = neg_range / total_range if total_range != 0 else 0.5 # 拆分RdBu_r色标,按比例分配正负温颜色段 cmap_base = plt.get_cmap('RdBu_r') # 负温部分用对应比例的色阶,正温部分保留更多细节 neg_colors = cmap_base(np.linspace(0, neg_ratio, int(50 * neg_ratio) or 1)) pos_colors = cmap_base(np.linspace(neg_ratio, 1, 100 - len(neg_colors))) custom_cmap = ListedColormap(np.vstack((neg_colors, pos_colors))) # 应用归一化与自定义色标 norm = TwoSlopeNorm(vmin=vmin, vcenter=vcenter, vmax=vmax) # Cartopy绘图 fig, ax = plt.subplots(subplot_kw={'projection': ccrs.PlateCarree()}) im = ax.pcolormesh(temp_data, norm=norm, cmap=custom_cmap, transform=ccrs.PlateCarree()) fig.colorbar(im, ax=ax, label='Temperature (°C)') plt.show()
2. 场景自适应切换归一化逻辑
自动识别数据是否全正、全负或混合,匹配对应的归一化方式与色标,避免单一规则失效:
def get_adaptive_norm_and_cmap(temp_data): has_neg = np.any(temp_data < 0) has_pos = np.any(temp_data > 0) vmin, vmax = temp_data.min(), temp_data.max() vcenter = 0 if not has_neg: # 全正温场景:用Reds色标+普通归一化 return plt.Normalize(vmin=vmin, vmax=vmax), plt.get_cmap('Reds') elif not has_pos: # 全负温场景:用Blues_r色标+普通归一化 return plt.Normalize(vmin=vmin, vmax=vmax), plt.get_cmap('Blues_r') else: # 混合场景:加权色标+TwoSlopeNorm neg_range = abs(vmin - vcenter) pos_range = vmax - vcenter neg_ratio = neg_range / (neg_range + pos_range) cmap_base = plt.get_cmap('RdBu_r') neg_steps = int(50 * neg_ratio) if neg_ratio > 0 else 1 pos_steps = 100 - neg_steps neg_colors = cmap_base(np.linspace(0, neg_ratio, neg_steps)) pos_colors = cmap_base(np.linspace(neg_ratio, 1, pos_steps)) custom_cmap = ListedColormap(np.vstack((neg_colors, pos_colors))) return TwoSlopeNorm(vmin=vmin, vcenter=vcenter, vmax=vmax), custom_cmap # 使用示例 norm, cmap = get_adaptive_norm_and_cmap(temp_data) fig, ax = plt.subplots(subplot_kw={'projection': ccrs.PlateCarree()}) im = ax.pcolormesh(temp_data, norm=norm, cmap=cmap, transform=ccrs.PlateCarree()) fig.colorbar(im, ax=ax, label='Temperature (°C)') plt.show()
3. 极端不均衡场景:对数变换归一化
如果正温范围极大(比如几十倍于负温),可对温度取对数变换压缩正温区间,同时保留负温细节:
# 对温度做符号保留的对数变换(避免0值报错,加微小偏移) temp_transformed = np.sign(temp_data) * np.log1p(np.abs(temp_data)) vmin_trans, vmax_trans = temp_transformed.min(), temp_transformed.max() # 用TwoSlopeNorm适配变换后的数据 norm = TwoSlopeNorm(vmin=vmin_trans, vcenter=0, vmax=vmax_trans) cmap = plt.get_cmap('RdBu_r') # 绘图并还原色标刻度 fig, ax = plt.subplots(subplot_kw={'projection': ccrs.PlateCarree()}) im = ax.pcolormesh(temp_transformed, norm=norm, cmap=cmap, transform=ccrs.PlateCarree()) # 自定义色标刻度,还原原温度值 cbar = fig.colorbar(im, ax=ax) cbar.set_ticks(np.sign(cbar.get_ticks()) * (np.exp(np.abs(cbar.get_ticks())) - 1)) cbar.set_label('Temperature (°C)') plt.show()
内容的提问来源于stack exchange,提问作者Outlaw
相关产品推荐
相关产品推荐

