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

使用Cartopy绘制等距方位投影大圆路径与指定坐标错位问题

解决Cartopy中等距方位投影上大圆路径偏移问题

问题描述

使用Python的Cartopy和Matplotlib库,在等距方位投影(Azimuthal Equidistant projection)上绘制开普敦与霍巴特两点间的大圆路径时,代表两点的标记位置正确,但生成的大圆路径出现偏移,无法准确连接两点。

原代码如下:

import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import numpy as np

def great_circle_points(lat1, lon1, lat2, lon2, num_points):
    lat1, lon1, lat2, lon2 = np.radians([lat1, lon1, lat2, lon2])
    dlat = lat2 - lat1
    dlon = lon2 - lon1
    fractions = np.linspace(0, 1, num_points)
    a = np.sin(dlat/2)**2 + np.cos(lat1) * np.cos(lat2) * np.sin(dlon/2)**2
    delta_sigma = 2 * np.arctan2(np.sqrt(a), np.sqrt(1-a))

    lat = np.arctan2(
        np.sin(lat1) * np.cos(delta_sigma * fractions) + np.sin(lat2) * np.sin(delta_sigma * fractions),
        np.sqrt((np.cos(lat1) + np.cos(lat2) * np.cos(delta_sigma * fractions))**2 + (np.cos(lat2) * np.sin(delta_sigma * fractions))**2)
    )
    lon = lon1 + np.arctan2(
        np.sin(delta_sigma * fractions) * np.sin(lon2 - lon1),
        np.cos(lat1) * np.cos(delta_sigma * fractions) - np.sin(lat1) * np.sin(delta_sigma * fractions) * np.cos(lon2 - lon1)
    )

    lat = np.degrees(lat)
    lon = np.degrees(lon)

    return lat, lon

lat1, lon1 = -33.9715, 18.6021  # Cape Town
lat2, lon2 = -42.8364, 147.5075  # Hobart
num_points = 1001

latitudes, longitudes = great_circle_points(lat1, lon1, lat2, lon2, num_points)

plt.figure(figsize=[10, 10])
#ccrs.Orthographic(), ccrs.Stereographic(), ccrs.LambertAzimuthalEqualArea(), or ccrs.Gnomonic().
ax = plt.axes(projection=ccrs.AzimuthalEquidistant(central_latitude=90, central_longitude=0))
ax.set_global()
ax.coastlines()

# Plotting the great circle points.
ax.plot(longitudes, latitudes, 'r', transform=ccrs.Geodetic())
plt.plot(lon1, lat1, 'bo', markersize=7, transform=ccrs.Geodetic())
plt.plot(lon2, lat2, 'ro', markersize=7, transform=ccrs.Geodetic())

plt.show()

解决方案

问题根源是自定义的great_circle_points函数中,大圆坐标的计算逻辑有误,导致生成的路径点偏离实际大圆轨迹。以下提供两种修复方式:

方法一:使用Cartopy内置功能绘制大圆路径(推荐)

Cartopy已内置处理大圆路径的功能,无需手动计算坐标,直接通过ax.plot并指定transform=ccrs.Geodetic()即可自动生成两点间的准确大圆路径,代码更简洁可靠。

修改后的代码:

import matplotlib.pyplot as plt
import cartopy.crs as ccrs

lat1, lon1 = -33.9715, 18.6021  # Cape Town
lat2, lon2 = -42.8364, 147.5075  # Hobart

plt.figure(figsize=[10, 10])
ax = plt.axes(projection=ccrs.AzimuthalEquidistant(central_latitude=90, central_longitude=0))
ax.set_global()
ax.coastlines()

# 直接绘制两点间的大圆路径
ax.plot([lon1, lon2], [lat1, lat2], 'r', transform=ccrs.Geodetic())
# 绘制端点标记
ax.plot(lon1, lat1, 'bo', markersize=7, transform=ccrs.Geodetic())
ax.plot(lon2, lat2, 'ro', markersize=7, transform=ccrs.Geodetic())

plt.show()

方法二:修复自定义的大圆坐标计算函数

若需自行实现大圆坐标计算,需修正函数中的球面插值公式,使用正确的球面线性插值(SLERP)逻辑:

修正后的完整代码:

import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import numpy as np

def great_circle_points(lat1, lon1, lat2, lon2, num_points):
    # 将坐标转换为弧度
    lat1, lon1, lat2, lon2 = np.radians([lat1, lon1, lat2, lon2])
    fractions = np.linspace(0, 1, num_points)
    
    # 计算两点间的圆心角
    cos_delta = np.sin(lat1)*np.sin(lat2) + np.cos(lat1)*np.cos(lat2)*np.cos(lon2 - lon1)
    delta = np.arccos(np.clip(cos_delta, -1, 1))  # 限制范围避免数值误差
    
    # 球面线性插值计算路径点
    sin_delta = np.sin(delta)
    sin_1_minus_t_delta = np.sin((1 - fractions)*delta)
    sin_t_delta = np.sin(fractions*delta)
    
    lat = np.arctan2(
        sin_1_minus_t_delta*np.sin(lat1) + sin_t_delta*np.sin(lat2),
        sin_1_minus_t_delta*np.cos(lat1) + sin_t_delta*np.cos(lat2)*np.cos(lon2 - lon1)
    )
    lon = lon1 + np.arctan2(
        sin_t_delta*np.cos(lat2)*np.sin(lon2 - lon1),
        sin_1_minus_t_delta*np.cos(lat1) + sin_t_delta*np.cos(lat2)*np.cos(lon2 - lon1)
    )
    
    # 转换回角度坐标
    lat = np.degrees(lat)
    lon = np.degrees(lon)
    
    return lat, lon

lat1, lon1 = -33.9715, 18.6021  # Cape Town
lat2, lon2 = -42.8364, 147.5075  # Hobart
num_points = 1001

latitudes, longitudes = great_circle_points(lat1, lon1, lat2, lon2, num_points)

plt.figure(figsize=[10, 10])
ax = plt.axes(projection=ccrs.AzimuthalEquidistant(central_latitude=90, central_longitude=0))
ax.set_global()
ax.coastlines()

ax.plot(longitudes, latitudes, 'r', transform=ccrs.Geodetic())
ax.plot(lon1, lat1, 'bo', markersize=7, transform=ccrs.Geodetic())
ax.plot(lon2, lat2, 'ro', markersize=7, transform=ccrs.Geodetic())

plt.show()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 12:08:07