找到点与线之间的距离,并获得该点在一条线中的投影距离

时间:2019-06-17 12:48:15

标签: python math gis distance

我是python的新手。但是我有一个艰巨的任务: 我有带GPS坐标的CSV文件和1个带参考轨迹(坐标点)的文件,它们代表了车辆的轨迹。

所以任务是对所有点计算到参考轨迹的垂直距离,并定义该点在参考轨迹的哪一公里

The task looks like this

我试图用QGIS解决它,但问题是数据太多,QGIS一直崩溃。 我正在使用距离矩阵。

因此参考轨迹数据如下:

    datetime            lon_deg     lat_deg     ki1ometers  
0   27.03.2018 15:07    14.34559621 48.28282695 0   
1   27.03.2018 15:07    14.34539589 48.283579   0.08492765648897423 
2   27.03.2018 15:08    14.34509878 48.28437137 0.17573647034625345 
3   27.03.2018 15:08    14.34476681 48.28520735 0.2718837851891085  
4   27.03.2018 15:09    14.34440297 48.28607467 0.372012272777317   
5   27.03.2018 15:10    14.34355387 48.28785601 0.5798125761498747  
6   27.03.2018 15:10    14.34312139 48.28876254 0.6855708866782635  
7   27.03.2018 15:11    14.34267986 48.28966368 0.7909635418697577  
8   27.03.2018 15:11    14.34235909 48.29057934 0.895509507334529   
9   27.03.2018 15:12    14.34193015 48.29147634 1.000178064181187   
10  27.03.2018 15:12    14.34158939 48.2923968  1.1055875957864745  
11  27.03.2018 15:13    14.34125444 48.29332421 1.2116463089787737  
12  27.03.2018 15:13    14.34084938 48.29424082 1.31788253222638    
13  27.03.2018 15:14    14.34041673 48.29515665 1.4246295164890292  
14  27.03.2018 15:14    14.34001362 48.29608703 1.532295241219843   
15  27.03.2018 15:15    14.33959522 48.29702238 1.6408091272201002  
16  27.03.2018 15:15    14.33917898 48.29796904 1.7504838454702525  
17  27.03.2018 15:16    14.33875624 48.29892358 1.8611345768980705  
18  27.03.2018 15:16    14.33832484 48.29988211 1.9723928345544686  
19  27.03.2018 15:17    14.337844699999998  48.30083163 2.083788039109954   
20  27.03.2018 15:17    14.33733187 48.30177414 2.1952441083077696  
21  27.03.2018 15:18    14.33680756 48.30271439 2.3067561380904458  
22  27.03.2018 15:18    14.33637327 48.30366977 2.4177398933361665  
23  27.03.2018 15:19    14.33579109 48.30456609 2.5263104564169723  

计算参考轨迹上的距离和位置所需的数据:

datetime                lon_deg             lat_deg
2018-01-29 00:00:00.000 13.535165989333333  48.58077572716667
29.01.2018 0:00         13.535166009        48.580775726166664
2018-01-29 00:00:01.000 13.535165977166667  48.580775749
29.01.2018 0:00         13.5351658175       48.58077575
2018-01-29 00:00:02.000 13.535165976833333  48.58077567466667
29.01.2018 0:00         13.535165988166666  48.58077563316667
2018-01-29 00:00:03.000 13.535165978333334  48.580775599
29.01.2018 0:00         13.535166127833334  48.5807756575
2018-01-29 00:00:04.000 13.535166430833334  48.5807757935
29.01.2018 0:00         13.535166510166666  48.580775819
2018-01-29 00:00:05.000 13.5351665845       48.5807758835
29.01.2018 0:00         13.5351665215       48.580775906
2018-01-29 00:00:06.000 13.535166549166666  48.58077594583333
29.01.2018 0:00         13.535166521333334  48.58077594466667
2018-01-29 00:00:07.000 13.535166487        48.580775927666664
29.01.2018 0:00         13.5351670905       48.58077611433333
2018-01-29 00:00:08.000 13.5351669075       48.5807760195
29.01.2018 0:00         13.535166444166666  48.580775919

因此,我期望的输出是计算到直线轨迹的距离,以及该点在轨迹的特定公里数内。

我很高兴听到任何想法,因为我确实陷在这个问题中

1 个答案:

答案 0 :(得分:0)

在这么小的距离(例如2.5公里的路程)的情况下,您可能可以使用平面近似值并使用非常简单的方法:对于距离,您可以使用公式来计算三角形的高度,例如与semiperimeter合作。然后,您仍然必须计算该点是否在给定的线段中(在其上构建的垂直带的一部分),在这里您可以使用向量的标量(点)的projection property

因此对于线段AB和点C(所有点都是元组/列表),您将需要以下内容:

pdist=lambda A,B:((A[0]-B[0])**2+(A[1]-B[1])**2)**(1/2)

def dist(A,B,C):
  c=pdist(A,B)
  rat=((C[0]-A[0])*(B[0]-A[0])+(C[1]-A[1])*(B[1]-A[1]))/c/c
  if rat<0 or rat>1:
    return None,None
  a=pdist(B,C)
  b=pdist(A,C)
  s=(a+b+c)/2
  alt=2*(s*(s-a)*(s-b)*(s-c))**(1/2)/c
  return alt,rat

如果C的垂直基点在AB之外,或者垂直距离(C与AB段的距离)的元组和0 ... 1之间的比率(可以用作权重),则返回None,None计算两个相邻点之间的“旅行位置”。

然后对示例数据进行一些解析:

import re
rawtour='''0   27.03.2018 15:07    14.34559621 48.28282695 0   
1   27.03.2018 15:07    14.34539589 48.283579   0.08492765648897423 
2   27.03.2018 15:08    14.34509878 48.28437137 0.17573647034625345 
3   27.03.2018 15:08    14.34476681 48.28520735 0.2718837851891085  
4   27.03.2018 15:09    14.34440297 48.28607467 0.372012272777317   
5   27.03.2018 15:10    14.34355387 48.28785601 0.5798125761498747  
6   27.03.2018 15:10    14.34312139 48.28876254 0.6855708866782635  
7   27.03.2018 15:11    14.34267986 48.28966368 0.7909635418697577  
8   27.03.2018 15:11    14.34235909 48.29057934 0.895509507334529   
9   27.03.2018 15:12    14.34193015 48.29147634 1.000178064181187   
10  27.03.2018 15:12    14.34158939 48.2923968  1.1055875957864745  
11  27.03.2018 15:13    14.34125444 48.29332421 1.2116463089787737  
12  27.03.2018 15:13    14.34084938 48.29424082 1.31788253222638    
13  27.03.2018 15:14    14.34041673 48.29515665 1.4246295164890292  
14  27.03.2018 15:14    14.34001362 48.29608703 1.532295241219843   
15  27.03.2018 15:15    14.33959522 48.29702238 1.6408091272201002  
16  27.03.2018 15:15    14.33917898 48.29796904 1.7504838454702525  
17  27.03.2018 15:16    14.33875624 48.29892358 1.8611345768980705  
18  27.03.2018 15:16    14.33832484 48.29988211 1.9723928345544686  
19  27.03.2018 15:17    14.337844699999998  48.30083163 2.083788039109954   
20  27.03.2018 15:17    14.33733187 48.30177414 2.1952441083077696  
21  27.03.2018 15:18    14.33680756 48.30271439 2.3067561380904458  
22  27.03.2018 15:18    14.33637327 48.30366977 2.4177398933361665  
23  27.03.2018 15:19    14.33579109 48.30456609 2.5263104564169723  '''
tour=list(map(lambda line:list(map(lambda x:float(x),re.match(r'[^\s]+\s+[^\s]+\s+[^\s]+\s+([\d\.]+)\s+([\d\.]+)\s+([\d\.]+)\s*',line).groups())),rawtour.split('\n')))
rawmarks='''2018-01-29 00:00:00.000 13.535165989333333  48.58077572716667
29.01.2018 0:00         13.535166009        48.580775726166664
2018-01-29 00:00:01.000 13.535165977166667  48.580775749
29.01.2018 0:00         13.5351658175       48.58077575
2018-01-29 00:00:02.000 13.535165976833333  48.58077567466667
29.01.2018 0:00         13.535165988166666  48.58077563316667
2018-01-29 00:00:03.000 13.535165978333334  48.580775599
29.01.2018 0:00         13.535166127833334  48.5807756575
2018-01-29 00:00:04.000 13.535166430833334  48.5807757935
29.01.2018 0:00         13.535166510166666  48.580775819
2018-01-29 00:00:05.000 13.5351665845       48.5807758835
29.01.2018 0:00         13.5351665215       48.580775906
2018-01-29 00:00:06.000 13.535166549166666  48.58077594583333
29.01.2018 0:00         13.535166521333334  48.58077594466667
2018-01-29 00:00:07.000 13.535166487        48.580775927666664
29.01.2018 0:00         13.5351670905       48.58077611433333
2018-01-29 00:00:08.000 13.5351669075       48.5807760195
29.01.2018 0:00         13.535166444166666  48.580775919'''
marks=list(map(lambda line:list(map(lambda x:float(x),re.match(r'[^\s]+\s+[^\s]+\s+([\d\.]+)\s+([\d\.]+)\s*',line).groups())),rawmarks.split('\n')))

然后[[dist(A,B,C) for A,B in zip(tour,tour[1:])] for C in marks]生成距离矩阵,在这种情况下,距离矩阵完全为空,因为两个坐标集与建立在线段上的垂直窄带相关的距离太远。经度约为48度时,一度经度约为111公里/秒。虽然整个示例游览仅2.5公里长。

为了加快处理速度,可以在pdist上使用https://docs.python.org/3/library/functools.html#functools.lru_cache,或者可以明确地预先计算c -s(行程段的长度)并将其存储在某个位置, /c/c除法可以推迟,temprat<0 or temprat>c**2可以在if中使用,其中c**2也可以预先计算。当我注意到距离问题时,我只是失去了动力。


我认为您也必须检查点到点的距离。参见图的上部:蓝点落在灰色矩形的外面(当然,它们是无限的,并且向两个方向延伸,我只是将它们加盖以获得更好的视觉效果),但是恰好在红色矩形的内部,因此是正交的-distance-only方法会说最右边的线段是最接近所讨论点的线段,而查看点到点的距离可能会发现绿点是最近的位置。

但是图像的下部是用来说明点对点的距离不能用作替换,它甚至也不能真正地“驱动”正交计算,因为红色点是最接近的角点到蓝色的那一个并不意味着一个片段不可能更近,而红色的那个甚至都不是那个片段的终点。

为了使它与经纬度坐标配合使用,存在许多令人毛骨悚然的公式,我现在不敢为您选择一个。 https://en.wikipedia.org/wiki/Geographical_distance可能是一个很好的起点。您可以依靠的一种捷径是您的轨迹包含以km-s为单位的距离,因此您可以将线段中某个点的行程距离作为其端点处行程距离的加权总和来计算(而不是直接从经纬度计算距离) -lon坐标)。