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

Cartopy中OpenStreetMap地图偏移箭头无法显示问题求助

解决Cartopy中OSM地图上方位角箭头不显示的问题

问题概述

正在基于OpenStreetMap瓦片服务构建地图,需展示控制点、两条不同颜色的剖面线,并在每个剖面点以箭头形式展示方位角(单位:度)和偏移值。但执行代码时箭头始终无法显示,调整scale_factor参数也无效果,尝试三个版本的代码均未解决问题。

核心问题分析

三个版本的代码均存在坐标系统不匹配或偏移计算逻辑错误:

  • 版本1:直接使用经纬度坐标创建FancyArrowPatch,但Cartopy的投影轴要求补丁使用投影后的坐标,导致箭头位置完全错误,无法在可视区域内显示。
  • 版本2:虽然转换了起点的投影坐标,但直接将经纬度增量加到投影坐标上——投影坐标的单位并非度,这种计算方式完全不符合投影坐标系的规则。
  • 版本3:转换终点坐标时,是在经纬度上直接添加偏移后再投影,未考虑地图投影的变形,导致箭头方向和长度失真;同时scale_factor取值过小,箭头尺寸微不可察。

解决方案

正确的实现逻辑需遵循以下步骤:

  1. 在**地理坐标系(PlateCarree)**中计算箭头终点的经纬度(注意地理方位角是从正北顺时针计算,需转换为数学坐标系的角度规则)。
  2. 将起点和终点的经纬度统一转换为当前轴的投影坐标。
  3. 明确指定箭头样式,确保箭头图形正确渲染。

修正后的代码

import numpy as np
import matplotlib.pyplot as plt
from cartopy import crs as ccrs
import shapefile
from cartopy.io.img_tiles import OSM
from matplotlib.patches import FancyArrowPatch

# 读取Shapefile数据
shpfile = shapefile.Reader("/Users/Desktop/tdata/tdata")
fields = [x[0] for x in shpfile.fields[1:]]

points = {'base': [], 'profile1': [], 'profile2': []}
for item in shpfile.iterShapeRecords():
    kind = item.record[fields.index('kind')]
    position = item.shape.points[0]
    points[kind].append((position, dict(zip(fields, item.record))))

# 初始化OSM瓦片地图
tiles_provider = OSM()
plt.figure(figsize=(16, 16))
ax = plt.axes(projection=tiles_provider.crs)

# 设置地图显示范围,基于Shapefile边界扩展0.1度
ax.set_extent([shpfile.bbox[0]-0.1, shpfile.bbox[2]+0.1,
               shpfile.bbox[1]-0.1, shpfile.bbox[3]+0.1], ccrs.PlateCarree())
ax.add_image(tiles_provider, 11)

def draw_arrow(ax, lon, lat, angle, length, color, scale_factor=0.0001):
    # 地理方位角转数学坐标系角度:正北(0°)对应数学坐标系的90°,顺时针旋转对应角度递减
    math_angle = 90 - angle
    angle_rad = np.deg2rad(math_angle)
    
    # 在地理坐标系中计算箭头终点的经纬度
    end_lon = lon + scale_factor * length * np.cos(angle_rad)
    end_lat = lat + scale_factor * length * np.sin(angle_rad)
    
    # 将起点、终点经纬度转换为当前轴的投影坐标
    start_proj = ccrs.PlateCarree().transform_point(lon, lat, ax.projection)
    end_proj = ccrs.PlateCarree().transform_point(end_lon, end_lat, ax.projection)
    
    # 创建箭头补丁,指定样式和尺寸
    arrow = FancyArrowPatch(start_proj, end_proj, 
                           arrowstyle='->', 
                           mutation_scale=15, 
                           linewidth=2, 
                           color=color)
    ax.add_patch(arrow)

# 绘制红色控制点
for point, data in points['base']:
    ax.plot(point[0], point[1], marker='o', markersize=8, color='red', transform=ccrs.PlateCarree())

# 绘制蓝色剖面线及箭头
for i, (point, data) in enumerate(points['profile1']):
    if i > 0:
        prev_point = points['profile1'][i-1][0]
        ax.plot([prev_point[0], point[0]], [prev_point[1], point[1]], color='blue', linewidth=2, transform=ccrs.PlateCarree())
    # 根据实际数据调整scale_factor,确保箭头尺寸合适
    draw_arrow(ax, point[0], point[1], data['az'], data['value'], color='blue', scale_factor=0.0002)

# 绘制绿色剖面线及箭头
for i, (point, data) in enumerate(points['profile2']):
    if i > 0:
        prev_point = points['profile2'][i-1][0]
        ax.plot([prev_point[0], point[0]], [prev_point[1], point[1]], color='green', linewidth=2, transform=ccrs.PlateCarree())
    draw_arrow(ax, point[0], point[1], data['az'], data['value'], color='green', scale_factor=0.0002)

plt.show()

关键修改说明

  • 方位角转换:将地理方位角(正北顺时针)转换为数学坐标系角度(x轴逆时针),确保箭头指向与实际方位角一致。
  • 坐标计算逻辑:在地理坐标系中完成终点偏移计算,再统一转换为投影坐标,避免投影变形导致的方向、长度错误。
  • 箭头样式配置:指定arrowstyle='->'明确箭头样式,通过mutation_scale控制箭头头部大小,确保箭头清晰可见。
  • scale_factor适配:根据实际数据的偏移值范围调整scale_factor,保证箭头尺寸在地图上合适显示。

内容的提问来源于stack exchange,提问作者TryingToSolve

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 17:24:58