cross_doctor/app/GeoFunc.py

402 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import math
# 定义点
class MyPoint:
def __init__(self, x, y):
self.X = x
self.Y = y
@classmethod
def makeOne(cls, pt):
return MyPoint(pt.lng, pt.lat)
def get_Manhattan_dist(self, other):
return math.fabs(self.X - other.X) + math.fabs(self.Y - other.Y)
def get_dist(self, other):
return math.sqrt((self.X - other.X)*(self.X - other.X) + (self.Y - other.Y)*(self.Y - other.Y))
def calc_left_pt(self, target_pt: 'MyPoint', left_dist: float):
"""计算中间插值点"""
dist = self.get_dist(target_pt)
mid_pt = MyPoint(0, 0)
mid_pt.X = self.X + left_dist/dist*(target_pt.X - self.X)
mid_pt.Y = self.Y + left_dist / dist * (target_pt.Y - self.Y)
return mid_pt
# 定义一系列全局变量
PI = 3.1415926535897932384626433832795
PI_120D = 2.0943951023931954923084289221863
PI_90D = 1.5707963267948966192313216916398
PI_80D = 1.3962634015954636615389526147909
PI_30D = 0.52333333333
g_srcDir_to_angle_map = {'N': 180, 'NE': 225, 'E': 270, 'SE': 315, 'S': 0, 'SW': 45, 'W': 90, 'NW':135}
class GeoFunc:
@classmethod
def calc_vec_radian(cls, pA: MyPoint, pB: MyPoint):
dx = pB.X - pA.X
dy = pB.Y - pA.Y
if dx == 0:
if dy > 0:
return PI_90D
elif dy < 0:
return -1 * PI_90D
else:
# 两点重合,暂未处理这种异常情况
return 0
if dy == 0:
if dx > 0:
return 0
elif dx < 0:
return PI
else:
# 两点重合,暂未处理这种异常情况
return 0
dK = dy / dx
dA = math.atan(dK)
# 如果在第二项限
if (pB.Y > pA.Y and dA < 0):
dA += PI
# 如果在第三项限
if (pB.Y < pA.Y and dA > 0):
dA -= PI
return dA
@classmethod
def calc_turn_radian(cls, pA: MyPoint, pB: MyPoint, pC: MyPoint, pD: MyPoint):
"""
计算AB->CD转弯的弧度。右转为正值左转为负值
:param pA:
:param pB:
:param pC:
:param pD:
:return: [-PI, PI]
"""
fa = GeoFunc.calc_vec_radian(pA, pB)
fb = GeoFunc.calc_vec_radian(pC, pD)
fSign = fa * fb
if fSign >= 0:
return fa - fb
else:
ft = math.fabs(fa - fb)
if ft >= PI:
r = 2 * PI - ft
if fa - fb < 0:
return r
else:
return -1 * r
else:
return fa - fb
@classmethod
def radian2angle(cls, radian):
"""将弧度转换为角度"""
return radian * 180 / PI
@classmethod
def judge_turn_type(cls, turn_angle):
turn_type = -1
if math.fabs(turn_angle) <= 30:
turn_type = 0 # straight
elif -150 < turn_angle < -30:
turn_type = 1 # turn left
elif 30 < turn_angle < 150:
turn_type = 2 # turn left
else: # means: abs(turn_angle) >= 150
turn_type = 3 # turn back
return turn_type
@classmethod
def calc_turn_type(cls, pA, pB, pC, pD):
turn_radian = GeoFunc.calc_turn_radian(pA, pB, pC, pD)
turn_angle = GeoFunc.radian2angle(turn_radian)
return GeoFunc.judge_turn_type(turn_angle)
@classmethod
def calc_turn_type_ex(cls, pA, pB, pC, pD):
"""
计算AB->CD的转向类型附加偏差角度
@param pA:
@param pB:
@param pC:
@param pD:
@return: turn_type, diff
"""
turn_radian = GeoFunc.calc_turn_radian(pA, pB, pC, pD)
turn_angle = GeoFunc.radian2angle(turn_radian)
turn_type = GeoFunc.judge_turn_type(turn_angle)
diff = 0
if turn_type == 0:
diff = abs(abs(turn_angle))
elif turn_type == 3:
diff = abs(180 - abs(turn_angle))
else:
diff = abs(90 - abs(turn_angle))
return turn_type, diff
# 根据向量角度,判定来源方向
@classmethod
def vecAngle_to_srcDir(cls, vec_angle):
srcDir = ''
if math.fabs(vec_angle) <= 22.5:
srcDir = 'W'
elif 22.5 < vec_angle <= 67.5:
srcDir = 'SW'
elif 67.5 < vec_angle <= 112.5:
srcDir = 'S'
elif 112.5 < vec_angle <= 157.5:
srcDir = 'SE'
elif math.fabs(vec_angle) > 157.5:
srcDir = 'E'
elif -157.5 <= vec_angle <= -112.5:
srcDir = 'NE'
elif -112.5 < vec_angle <= -67.5:
srcDir = 'N'
elif -67.5 < vec_angle <= -22.5:
srcDir = 'NW'
return srcDir
@classmethod
def calc_srcDir(cls, pA: MyPoint, pB: MyPoint):
vec_radian = GeoFunc.calc_vec_radian(pA, pB)
vec_angle = GeoFunc.radian2angle(vec_radian)
return GeoFunc.vecAngle_to_srcDir(vec_angle)
@classmethod
def calc_pole_angle(cls, pA: MyPoint, pB: MyPoint):
"""
计算gps角度以正北为0度顺时针方向角度增大
:param pA:
:param pB:
:return: [0, 360)
"""
vec_radian = GeoFunc.calc_vec_radian(pA, pB)
vec_angle = GeoFunc.radian2angle(vec_radian)
pole_angle = (90 - vec_angle + 360) % 360
return pole_angle
@classmethod
def diff_pole_angle(cls, a, b):
"""
计算两个gps方位角的差值
:param a:
:param b:
:return:
"""
diff = abs(a -b)
if diff > 180:
diff = 360 - diff
return diff
@classmethod
def calc_srcDir_tail(cls, pt_list):
"""
计算点串末段的车流来向
:param pt_list:
:return:
"""
num = len(pt_list)
if num < 2:
return 'unknown'
pB = MyPoint.makeOne(pt_list[num - 1])
cur_pA_index = num - 2
while True:
pA = MyPoint.makeOne(pt_list[cur_pA_index])
if cur_pA_index <= 0 or pA.get_Manhattan_dist(pB) >= 0.0005:
return GeoFunc.calc_srcDir(pA, pB)
cur_pA_index -= 1
# 保底措施
if cur_pA_index < 0:
break
# 不应该走到的分支
return 'unknown'
@classmethod
def calc_srcDir_head(cls, pt_list):
"""
计算点串起始段的车流来向
:param pt_list:
:return:
"""
num = len(pt_list)
if num < 2:
return 'unknown'
pA = MyPoint.makeOne(pt_list[0])
cur_pB_index = 1
while True:
pB = MyPoint.makeOne(pt_list[cur_pB_index])
if cur_pB_index >= num-1 or pA.get_Manhattan_dist(pB) >= 0.0005:
return GeoFunc.calc_srcDir(pA, pB)
cur_pB_index += 1
# 不应该走到的分支
return 'unknown'
@classmethod
def calc_angle_head(cls, pt_list):
"""
计算点串起始段的车流来向
:param pt_list:
:return:
"""
num = len(pt_list)
if num < 2:
return 'unknown'
pA = MyPoint.makeOne(pt_list[0])
cur_pB_index = 1
while True:
pB = MyPoint.makeOne(pt_list[cur_pB_index])
if cur_pB_index >= num-1 or pA.get_Manhattan_dist(pB) >= 0.0005:
return GeoFunc.calc_pole_angle(pA, pB)
cur_pB_index += 1
# 不应该走到的分支
return 'unknown'
@classmethod
def flip_gps_angle(self, original_angle):
flipped_angle = (original_angle + 180) % 360
return flipped_angle
@classmethod
def calc_angle_tail(cls, pt_list):
"""
计算点串末段的车流来向
:param pt_list:
:return:
"""
num = len(pt_list)
if num < 2:
return 'unknown'
pB = MyPoint.makeOne(pt_list[num - 1])
cur_pA_index = num - 2
while True:
pA = MyPoint.makeOne(pt_list[cur_pA_index])
if cur_pA_index <= 0 or pA.get_Manhattan_dist(pB) >= 0.0005:
return GeoFunc.calc_pole_angle(pA, pB)
cur_pA_index -= 1
# 保底措施
if cur_pA_index < 0:
break
# 不应该走到的分支
return 'unknown'
@classmethod
def diff_srcDir(cls, a: str, b: str):
"""
计算两个车流来向的角度差
:param a:
:param b:
:return: 0/45/90/135/180
"""
if a == b:
return 0
angle_a = g_srcDir_to_angle_map[a]
angle_b = g_srcDir_to_angle_map[b]
diff_angle = abs(angle_a - angle_b)
if diff_angle > 180:
diff_angle = 360 - diff_angle
return diff_angle
@classmethod
def pick_path_head(cls, pt_list) -> (MyPoint, MyPoint):
"""
从点序列中提取满足50米长度的开头的一段
:param pt_list: Pt的数组
:return: pA, pB
"""
num = len(pt_list)
if num < 2:
return None, None
if num == 2:
return MyPoint.makeOne(pt_list[0]), MyPoint.makeOne(pt_list[1])
pA = MyPoint.makeOne(pt_list[0])
cur_pB_index = 1
while True:
pB = MyPoint.makeOne(pt_list[cur_pB_index])
if cur_pB_index >= num-1 or pA.get_dist(pB) >= 0.0005:
return pA, pB
cur_pB_index += 1
# 保底措施
if cur_pB_index > num-1:
break
# 不应该走到的分支
return None, None
@classmethod
def pick_path_tail(cls, pt_list) -> (MyPoint, MyPoint):
"""
从点序列中提取满足50米长度的末尾的一段
:param pt_list: Pt的数组
:return: pA, pB
"""
num = len(pt_list)
if num < 2:
return None, None
if num == 2:
return MyPoint.makeOne(pt_list[0]), MyPoint.makeOne(pt_list[1])
pB = MyPoint.makeOne(pt_list[num - 1])
cur_pA_index = num - 2
while True:
pA = MyPoint.makeOne(pt_list[cur_pA_index])
if cur_pA_index <= 0 or pA.get_dist(pB) >= 0.0005:
return pA, pB
cur_pA_index -= 1
# 保底措施
if cur_pA_index < 0:
break
# 不应该走到的分支
return None, None
@classmethod
def calc_earth_dist(cls, x1, y1, x2, y2):
"""
计算两个经纬度坐标之间的球面距离
"""
_PI_ = 3.14159265358979323846264
EARTHS_RADIUS = 6371008.8
RAD_TO_DEG = 180.0 / _PI_
DEG_TO_RAD = _PI_ / 180.0
radLat1 = y1 * DEG_TO_RAD
radLat2 = y2 * DEG_TO_RAD
a = radLat1 - radLat2
b = (x1 - x2) * DEG_TO_RAD
s = 2 * math.asin(math.sqrt(pow(math.sin(a / 2), 2) + math.cos(radLat1) * math.cos(radLat2) * pow(math.sin(b / 2), 2)))
return s * EARTHS_RADIUS
if __name__ == '__main__':
# for a in g_srcDir_to_angle_map.keys():
# for b in g_srcDir_to_angle_map.keys():
# diff_angle = GeoFunc.diff_srcDir(a, b)
# print('%s %s => %d' % (a, b, diff_angle))
geo = '98.327637,27.994401,118.366699,41.508577'
ss = geo.split(',')
x1 = float(ss[0])
y1 = float(ss[1])
x2 = float(ss[2])
y2 = float(ss[3])
dist = GeoFunc.calc_earth_dist(x1, y1, x2, y2)
print(dist)