Python - 降采样点列表,给定坐标和相邻点之间的距离

1 投票
3 回答
2754 浏览
提问于 2025-04-17 06:50

我真心希望有人能帮我解决这个问题!我想让这个脚本按照我的想法运行,但我就是搞不懂怎么做。

这个需要处理的数据文件是从GPS获取的,内容大概是这样的:

Line 20081002-1119.nmea
$GPGGA,094406.00,5849.40174,N,01738.15828,E,2,08,00.9,00003.26,M,0024.93,M,005,0734*62
$GPGGA,094407.00,5849.40177,N,01738.15827,E,2,08,00.9,00003.22,M,0024.93,M,005,0734*6B
$GPGGA,094408.00,5849.40174,N,01738.15826,E,2,08,00.9,00003.00,M,0024.93,M,006,0734*65
$GPGGA,094409.00,5849.40171,N,01738.15831,E,2,08,00.9,00003.24,M,0024.93,M,005,0734*62
$GPGGA,094410.00,5849.40176,N,01738.15833,E,2,08,00.9,00003.29,M,0024.93,M,006,0734*61
$GPGGA,094411.00,5849.40172,N,01738.15831,E,2,08,00.9,00003.31,M,0024.93,M,004,0734*6D
$GPGGA,094412.00,5849.40172,N,01738.15830,E,2,08,00.9,00003.15,M,0024.93,M,005,0734*68
$GPGGA,094413.00,5849.40175,N,01738.15834,E,2,08,00.9,00003.18,M,0024.93,M,005,0734*67
$GPGGA,094414.00,5849.40173,N,01738.15835,E,2,08,00.9,00003.16,M,0024.93,M,006,0734*6A
EOL

我希望输出的文件看起来像这样(这里的距离是我随便编的,只是为了说明我想要的效果):

Line 20081002-1119.nmea
58.853952   17.643113   102.15 
58.853946   17.643243   101.63 
58.853939   17.643372   105.93 
58.853933   17.643503   104.01 
58.853927   17.643633   104.25 
...
EOL

文件的列包括:经度、纬度、到上一个点的距离。

我该怎么做才能把数据按一定的间隔(在我的例子中是100米)进行下采样呢?

到目前为止我写的脚本是:

indata=open('C:/nav.nmea', 'r')
outdata=open('C:/nav_out.txt', 'w')

from math import *

coords_list=[]
coords=[]

def distance(coords_list):
    for (longi2,lati2) in coords_list:
        for (longi1,lati1) in coords_list:
            a = sin(lati1) * sin(lati2)+cos(longi1-longi2)*cos(lati1) * cos(lati2)
            c= 2* asin(sqrt(a))

            s= (6367* c)/100000 # For results in meters

        if s<100:
            # Here I want to discard current line if not s<100 and jump to the next line
        else:
            "Return the valid lines"
    return s


for line in indata:

    if line.startswith('$GPGGA'):

        data=line.split(",")
        # Import only coordinates from input file

        LON=float(data[2])/100

        LAT=float(data[4])/100

        # Convert coordinates from DDMM.MMMM to DD.DDDDDD

        lon=((LON-int(LON))/60)*100+int(LON)

        lat=((LAT-int(LAT))/60)*100+int(LAT)

        coords_list.append((lon,lat))

        outdata.writelines("%0.6f\t" %lon)

        outdata.writelines("%0.6f\t" %lat)
        outdata.writelines("%s \n" %distance(coords_list))


    elif line.startswith('EOL'):

        outdata.writelines("EOL")

    elif line.startswith('Line'):

        LineID=line

        outdata.writelines('\n%s' %LineID)


indata.close()     

outdata.close() 

`

3 个回答

0
def generate_survexpath(points, tz=None, distance=10):
"""Generate a survey path from a list of points. 
The survey path will consist of a series of points spaced at a given distance = 10 metres.     
This has to cope with interpolating new points,
but also skipping existing points if they are too close.
"""
survexpath = []
i = 0
while i < len(points) - 2:
    i = i + 1
    p1 = points[i]
    j = i + 1
    p2 = points[j]

    # Calculate the distance between successive points.
    distance_between_points = calculate_distance(p1, p2)

    # Calculate the number of points to include in the survey path between the two points.
    num_points = int(distance_between_points / distance)

    #  Step through the track until the gap is > 10m
    while num_points == 0 and j < len(points) - 1:
        j = j + 1
        p2 = points[j]  
        distance_between_points = calculate_distance(p1, p2)
        num_points = int(distance_between_points / distance)
    # re set the start point for looking for a gap > 10m
    i = j
    # Generate the survey path points where the gap is > 10m
    if num_points > 0:
        for k in range(num_points + 1):
            t = k / num_points
            lat = p1.lat + (p2.lat - p1.lat) * t
            lon = p1.lon + (p2.lon - p1.lon) * t
            ele = p1.ele + (p2.ele - p1.ele) * t
            time =""
            if k == 0 and p1.time:
                time = p1.time.astimezone(tz=tz)

            p = Point(lat, lon, ele, name=f"{i}-{k}", time=time)
            survexpath.append(p)
if points:
    # file may have no trkpts, only waypoints
    last = points[-1]
    if not last.name:
        last.name = len(points)
    if last.time:
        last.time =last.time.astimezone(tz=tz)
    survexpath.append(last) # and the last one anyway.
return survexpath

这个是针对GPX格式的,不是针对NMEA格式的,但逻辑是一样的。这个代码既可以减少数据点的数量,也可以增加数据点的数量,确保每段的长度大约是10米

完整的代码可以在这里找到。

2

这里有一个比较简单的方法来减少数据的数量:coord1 用来存储上一个坐标。每次循环时,会计算 coord1 和新的坐标 coord2 之间的距离。当这个距离小于100时,后面的循环就会跳过。

import math
import itertools

def distance(coord1,coord2):
    longi1,lati1=coord1
    longi2,lati2=coord2
    a = (math.sin(lati1)*math.sin(lati2)
         +math.cos(longi1-longi2)*math.cos(lati1)*math.cos(lati2))
    c = 2*math.asin(math.sqrt(a))
    s = (6367*c)/100000 # For results in meters
    return s

with open('nav.nmea', 'r') as indata:
    with open('nav_out.txt', 'w') as outdata:
        coords=[]
        coord1=None
        for line in indata:
            if line.startswith('$GPGGA'):
                data=line.split(",")
                # Import only coordinates from input file
                LON=float(data[2])/100
                LAT=float(data[4])/100
                # Convert coordinates from DDMM.MMMM to DD.DDDDDD
                lon=((LON-int(LON))/60)*100+int(LON)
                lat=((LAT-int(LAT))/60)*100+int(LAT)
                coords.append((lon,lat))
                if coord1 is None:
                    # The first time through the loop `coord1` is None
                    outdata.write('%0.6f\t%0.6f\t%s \n'%(lon,lat,0))
                    coord1=(lon,lat)
                else:
                    coord2=(lon,lat)
                    dist=distance(coord1,coord2)
                    if dist<100:
                        # go back to the beginning of the loop
                        continue
                    outdata.write('%0.6f\t%0.6f\t%s \n'%(lon,lat,dist))
                    coord1=coord2
            elif line.startswith('EOL'):
                outdata.writelines("EOL")
            elif line.startswith('Line'):
                LineID=line
                outdata.writelines('\n%s' %LineID)
4

如果你想在曲线上减少点的数量,可能会想用 Ramer–Douglas–Peucker 算法。这个算法可以保持曲线的大致形状,但会去掉一些细节,去掉的程度可以通过一个参数来控制。

需要注意的是,链接到的维基百科页面上的代码是伪代码,并不是 Python 代码。


我找到了一份 用 Python 实现的 DP 线简化算法,不过我还没有测试过,不能保证它的正确性。

撰写回答