Cartopy中OpenStreetMap地图偏移箭头无法显示问题求助
解决Cartopy中OSM地图上方位角箭头不显示的问题
问题概述
正在基于OpenStreetMap瓦片服务构建地图,需展示控制点、两条不同颜色的剖面线,并在每个剖面点以箭头形式展示方位角(单位:度)和偏移值。但执行代码时箭头始终无法显示,调整scale_factor参数也无效果,尝试三个版本的代码均未解决问题。
核心问题分析
三个版本的代码均存在坐标系统不匹配或偏移计算逻辑错误:
- 版本1:直接使用经纬度坐标创建
FancyArrowPatch,但Cartopy的投影轴要求补丁使用投影后的坐标,导致箭头位置完全错误,无法在可视区域内显示。 - 版本2:虽然转换了起点的投影坐标,但直接将经纬度增量加到投影坐标上——投影坐标的单位并非度,这种计算方式完全不符合投影坐标系的规则。
- 版本3:转换终点坐标时,是在经纬度上直接添加偏移后再投影,未考虑地图投影的变形,导致箭头方向和长度失真;同时
scale_factor取值过小,箭头尺寸微不可察。
解决方案
正确的实现逻辑需遵循以下步骤:
- 在**地理坐标系(PlateCarree)**中计算箭头终点的经纬度(注意地理方位角是从正北顺时针计算,需转换为数学坐标系的角度规则)。
- 将起点和终点的经纬度统一转换为当前轴的投影坐标。
- 明确指定箭头样式,确保箭头图形正确渲染。
修正后的代码
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
相关产品推荐
相关产品推荐

