上期文章《论文复现 | 如何对冷锋进行自动识别》发出后,有读者在评论区我说冷锋的箭头(三角形)方向是反的。

image
我仔细一看,确实反了。
冷锋的蓝色三角形,按气象学惯例,应该指向暖空气一侧(也就是冷锋推进的方向,通常是东南方向)。但我们画出来的三角形,却指向了西北方向——也就是冷空气那一侧。
这个问题出在哪?我只用了 ColdFront(size=6, spacing=1.5),没设置任何方向参数,为什么方向会是反的?
带着这个疑问,我打开了 MetPy 的源码。
MetPy 的冷锋绘制功能在 metpy/plots/patheffects.py 中。文档里没有明确说清楚方向是怎么判定的,需要自己读代码。
ColdFront 继承自 Front 基类,其三角形符号定义如下:
class ColdFront(Front):
"""Draw a path as a cold front, with (default blue) pips/triangles along the path."""
_symbol = mpath.Path([[0, 0], [1, 1], [2, 0], [0, 0]],
[mpath.Path.MOVETO, mpath.Path.LINETO, mpath.Path.LINETO,
mpath.Path.CLOSEPOLY])
def __init__(self, color='blue', **kwargs):
super().__init__(color, **kwargs)
注意看这几个坐标点:(0,0) → (1,1) → (2,0) → (0,0)。
画出来就是一个底边在 X 轴上、尖端朝上(+Y 方向) 的三角形。
三角形沿路径摆放时,需要根据路径的切线方向来旋转。MetPy 是这么算的:
@staticmethod
def _process_path(path, path_trans):
"""Transform the main path into pixel coordinates; calculate the needed components."""
path_points = path.transformed(path_trans).interpolated(500).vertices
deltas = (path_points[1:] - path_points[:-1]).T
pt_offsets = np.concatenate(([0], np.hypot(*deltas).cumsum()))
angles = np.arctan2(deltas[-1], deltas[0]) # ← 关键!
return path_points, pt_offsets, angles
angles = np.arctan2(deltas[-1], deltas[0]),这里 deltas[-1] 是 dy,deltas[0] 是 dx,所以这个 angle 就是 arctan2(dy, dx)——即路径切线方向的角度。
简单来说:如果你从南往北画线,dy > 0,dx ≈ 0,angle ≈ 90°;如果从北往南画线,angle ≈ -90°。
Front 基类的构造函数有个 flip 参数:
class Front(mpatheffects.AbstractPathEffect):
def __init__(self, color, size=10, spacing=1, flip=False, filled=True):
super().__init__()
self.size = size
self.spacing = spacing
self.color = mcolors.to_rgba(color)
self.flip = flip # ← 就是这个参数
self.filled = filled
默认 flip=False。

1278771d5b89f73ec9f69a5852541fc9
三角形符号最终摆放的位置和方向,由 _get_symbol_transform 方法决定:
def _get_symbol_transform(self, renderer, offset, line_shift, angle, start):
scalex = self._size_pixels(renderer)
scaley, line_shift = (-scalex, -line_shift) if self.flip else (scalex, line_shift)
return mtransforms.Affine2D().scale(scalex, scaley).translate(
offset - self.symbol_width * self._size_pixels(renderer) / 2,
line_shift).rotate(angle).translate(*start)
注意这一行:
scaley, line_shift = (-scalex, -line_shift) if self.flip else (scalex, line_shift)
当 flip=False 时:scaley = scalex(正值),三角形不翻转,尖端朝上。然后经过 rotate(angle) 旋转后,三角形尖端指向路径方向的左侧。
当 flip=True 时:scaley = -scalex(负值),三角形在 Y 方向翻转,尖端朝下。旋转后,三角形尖端指向路径方向的右侧。
line_shift 也跟着变号:flip=False 时为正(三角形在路径左侧),flip=True 时为负(三角形在路径右侧)。
flip 参数 | 三角形指向 | 三角形位于 |
|---|---|---|
False(默认) | 路径方向的左侧 | 路径左侧 |
True | 路径方向的右侧 | 路径右侧 |
回到我们的代码。在 fit_final_fronts 函数中,锋面点的排序方式是:
sort_idx = np.argsort(pts_lat) # 按纬度从小到大排序
pts_lat = pts_lat[sort_idx]
pts_lon = pts_lon[sort_idx]
coeffs = np.polyfit(pts_lat, pts_lon, 2)
fit_lat = np.linspace(pts_lat.min(), pts_lat.max(), 50) # 从南到北
fit_lon = np.polyval(coeffs, fit_lat)
front_lines.append((fit_lon, fit_lat))
路径是从南→北绘制的。
当路径方向是"南→北"时,路径方向的左侧是西,右侧是东。
而我们用的是默认 flip=False,所以三角形指向了西侧——也就是冷空气那一侧。
但气象学惯例是:冷锋的三角形应指向暖空气一侧(即冷锋移动的前方,通常是东侧或东南侧)。
所以方向反了。
修复很简单,给 ColdFront() 加上 flip=True 就行了。
修复前:
ax.plot(flon, flat, 'b-', linewidth=2,
path_effects=[ColdFront(size=6, spacing=1.5)])
修复后:
ax.plot(flon, flat, 'b-', linewidth=2,
path_effects=[ColdFront(size=6, spacing=1.5, flip=True)])
就这么一行,问题解决。
那就不需要加 flip=True 了。因为路径方向是"北→南"时,左侧就是东,默认 flip=False 刚好指向暖空气侧。
所以关键在于:你的路径绘制方向决定了是否需要 flip。
路径方向 | flip=False(默认) | flip=True |
|---|---|---|
南→北 | 指向西(冷空气侧)❌ | 指向东(暖空气侧)✅ |
北→南 | 指向东(暖空气侧)✅ | 指向西(冷空气侧)❌ |
一句话总结:如果你按纬度从低到高(南→北)画锋面线,就必须加 flip=True;如果从高到低(北→南)画,则不需要。
我设置了filp为True后,方向就正常了

a914c32c5c72e4ff274b7d215974e660
这次踩坑的教训是:用第三方库画气象符号时马虎不得,还得理解背后的方向逻辑。MetPy 的 ColdFront 方向完全由两个因素决定:
ax.plot 时 lon/lat 数组的排列顺序)除了修改flip参数外,也可以把坐标反着排
官网文档没写这一坐标要求,只能靠读源码来发现。
希望这篇文章能帮到同样遇到冷锋箭头方向问题的同学。
如果对你有帮助还请多点赞收藏。