在现实世界中,人们经常通过使用经纬度来表示地理位置信息,但在计算机画面中,我们是通过像素坐标信息来表示坐标信息,所以针对经纬度等坐标信息,我们需要将其转换为对应的二维坐标,方便进行位置计算。
在已知经纬度信息的情况下,求两点之间的相对笛卡尔坐标关系,我们可以通过求两点之间的方位角和距离信息,即可求得两坐标之间的对应关系。
经纬度距离计算
在计算经纬度距离时,通常使用的是球面三角法中的Haversine公式。这个公式可以计算地球表面上两点之间的最短距离。
Haversine公式如下所示,其中所有角度都为弧度信息:
a = sin 2 ( Δ φ 2 ) + cos φ 1 ⋅ cos φ 2 ⋅ sin 2 ( Δ λ 2 ) c = 2 ⋅ atan2 ( a , 1 − a ) d = R ⋅ c a = \sin^2\left(\frac{\Delta \varphi}{2}\right) + \cos \varphi_1 \cdot \cos \varphi_2 \cdot \sin^2\left(\frac{\Delta \lambda}{2}\right) \\ c = 2 \cdot \text{atan2}\left(\sqrt{a}, \sqrt{1-a}\right) \\ d = R \cdot c a=sin2(2Δφ)+cosφ1⋅cosφ2⋅sin2(2Δλ)c=2⋅atan2(a,1−a)d=R⋅c
- λ 1 , φ 1 \lambda_1, \varphi_1 λ1,φ1: 是第一个点的经度和纬度,
- λ 2 , φ 2 \lambda_2, \varphi_2 λ2,φ2: 是第二个点的经度和纬度,
- Δ φ = φ 2 − φ 1 \Delta \varphi = \varphi_2 - \varphi_1 Δφ=φ2−φ1: 是纬度差,
- Δ λ = λ 2 − λ 1 \Delta \lambda = \lambda_2 - \lambda_1 Δλ=λ2−λ1: 是经度差,
- R R R: 是地球半径(平均值为6,371公里),
- d d d: 就是两点之间的距离。
Haversine公式的Python实现
import math
def haversine(lat1, lon1, lat2, lon2):
"""
计算两个经纬度坐标点之间的距离。
:param lat1: 第一个点的经度 (十进制度数)
:param lon1: 第一个点的纬度 (十进制度数)
:param lat2: 第二个点的经度 (十进制度数)
:param lat2: 第二个点的纬度 (十进制度数)
:return: 两点之间的距离(km)
"""
# 将十进制度数转化为弧度
lat1, lon1, lat2, lon2 = map(math.radians, [lat1, lon1, lat2, lon2])
# Haversine公式
dlon = lon2 - lon1
dlat = lat2 - lat1
a = math.sin(dlat / 2) ** 2 + math.cos(lat1) * math.cos(lat2) * math.sin(dlon / 2) ** 2
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
r = 6371 # 地球平均半径,单位为公里
return c * r
distance = haversine(34.259458, 108.947001, 31.23351, 121.505366)
print(f"西安钟楼到上海中心大厦之间的距离是 {distance:.2f} 公里")
# 西安钟楼到上海中心大厦之间的距离是 1220.78 公里
通过 geopy
计算经纬度距离
geopy
是 Python 的一个地理信息编码库,该库同样也提供了计算两点之间距离的功能。在 Python 中可以使用该库计算经纬度距离。
from geopy.distance import geodesic
# 定义两个地点的经纬度
location_a = (34.259458, 108.947001)
location_b = (31.23351, 121.505366)
# 计算两个地点之间的距离
distance_km = geodesic(location_a, location_b).kilometers
print(f"西安钟楼到上海中心大厦之间的距离是 {distance_km:.2f} 公里")
# 西安钟楼到上海中心大厦之间的距离是 1222.91 公里
方位角计算
在计算两个地理坐标点之间的方位角,通过使用球面三角学的公式。方位角表示从一个点出发,沿地球表面到达另一点时的初始前进方向,通常以真北为参考点,顺时针方向测量。
方位角计算公式为:
Δ λ = λ 2 − λ 1 y = sin ( Δ λ ) ⋅ cos ( φ 2 ) x = cos ( φ 1 ) ⋅ sin ( φ 2 ) − sin ( φ 1 ) ⋅ cos ( φ 2 ) ⋅ cos ( Δ λ ) θ = atan2 ( y , x ) \Delta \lambda = \lambda_2 - \lambda_1 \\ y = \sin(\Delta \lambda) \cdot \cos(\varphi_2) \\ x = \cos(\varphi_1) \cdot \sin(\varphi_2) - \sin(\varphi_1) \cdot \cos(\varphi_2) \cdot \cos(\Delta \lambda) \\ \theta = \text{atan2}(y, x) Δλ=λ2−λ1y=sin(Δλ)⋅cos(φ2)x=cos(φ1)⋅sin(φ2)−sin(φ1)⋅cos(φ2)⋅cos(Δλ)θ=atan2(y,x)
- P 1 ( λ 1 , φ 1 ) P_1(\lambda_1, \varphi_1) P1(λ1,φ1): 点1的经度和纬度
- P 2 ( λ 2 , φ 2 ) P_2(\lambda_2, \varphi_2) P2(λ2,φ2): 点2的经度和纬度
- 方位角以正北为参考点
- 方位角沿顺时针测量
- 上述的角度信息均为弧度信息
方位角计算Python实现
import math
def calculate_bearing(lat1, lon1, lat2, lon2):
"""
计算从点1到点2的初始方位角。
:param lat1: 第一个点的经度 (十进制度数)
:param lon1: 第一个点的纬度 (十进制度数)
:param lat2: 第二个点的经度 (十进制度数)
:param lat2: 第二个点的纬度 (十进制度数)
:return: 从点1到点2的初始方位角(度)
"""
# 将十进制度数转换为弧度
lat1 = math.radians(lat1)
lat2 = math.radians(lat2)
delta_lon = math.radians(lon2 - lon1)
# 使用公式计算方位角
y = math.sin(delta_lon) * math.cos(lat2)
x = math.cos(lat1) * math.sin(lat2) - math.sin(lat1) * math.cos(lat2) * math.cos(delta_lon)
initial_bearing = math.atan2(y, x)
# 将弧度转换为度,并调整范围至0-360度
initial_bearing = math.degrees(initial_bearing)
compass_bearing = (initial_bearing + 360) % 360
return compass_bearing
bearing = calculate_bearing(34.259458, 108.947001, 31.23351, 121.505366)
print(f"西安钟楼到上海中心大厦之间的初始方位角是 {bearing:.2f} 度")
# 西安钟楼到上海中心大厦之间的初始方位角是 102.52 度
相对坐标计算
根据上述公式,我们知道从A到B的距离d以及从A指向B的方位角θ(以正北方向为0度,顺时针为正方向)。我们可以使用三角函数来计算B点相对于A点的坐标。
在标准数学坐标系中,一般是以正东方向为0度,沿逆时针方向增加,所以需要将方位角转换为标准角。
θ ′ = 90 − θ Δ x = d ⋅ sin θ ′ Δ y = d ⋅ cos θ ′ \theta^{'} = 90 - \theta \\ \Delta x = d \cdot \sin{\theta^{'}} \\ \Delta y = d \cdot \cos{\theta^{'}} θ′=90−θΔx=d⋅sinθ′Δy=d⋅cosθ′
- 上述公式中,角度皆为弧度信息
- 根据上述公式,计算出B相对A分别在x轴和y轴的增量 Δ x \Delta x Δx, Δ y \Delta y Δy
然后将增量添加到起点A的坐标中,即可获得点B的相对坐标:
x 2 = x 1 + Δ x y 2 = y 1 + Δ y x_2 = x_1 + \Delta x \\ y_2 = y_1 + \Delta y x2=x1+Δxy2=y1+Δy
相对位坐标的Python实现
def calc_relative_2d_coordinates(distance, theta, origin=(0, 0), is_azimuth: bool = True):
"""
根据点2相对于点1的距离和角度计算点B的相对坐标。
:param distance: 距离
:param theta: 角度(十进制度数)
:param origin: 相对点1的坐标
:param is_azimuth: True: 方位角(以正北为参考点,顺时针测量),False: 数学角度(以正东为参考点,逆时针测量)
:return: (x2, y2): 点2相对于点1(origin)的相对坐标
"""
# 将方位角转换为数学角度
if is_azimuth:
theta = (90 - theta) % 360
# 将角度转换为弧度
theta_rad = math.radians(theta)
# 原点坐标
x1, y1 = origin
# 计算点B的坐标
x2 = x1 + distance * math.cos(theta_rad)
y2 = y1 + distance * math.sin(theta_rad)
return x2, y2