You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 12:34:59