如何使用python提取.gpx数据

Posted

技术标签:

【中文标题】如何使用python提取.gpx数据【英文标题】:How to extract .gpx data with python 【发布时间】:2012-06-21 18:28:09 【问题描述】:

我是一个新的 linux/python 用户,拥有 .gpx 文件(由 GPS 跟踪软件制作的输出文件),需要将值提取到 csv/txt 中以用于 GIS 程序。我在我的 Python 入门书籍、本网站和在线中查找了字符串和切片等。我使用了 .gpx 到 .txt 的转换器,可以将经度和纬度提取到文本文件中。我需要提取高程数据。该文件顶部有六行文本,我只知道如何在 emacs 中打开此文件(除了在网站上上传)这是从第 7 行开始的文件。

理想情况下,我想知道如何通过 python(或 Perl)将所有值提取到 csv 或 txt 文件中。如果有人知道网站教程或示例脚本,将不胜感激。

<metadata>
<time>2012-06-13T01:51:08Z</time>
</metadata>
<trk>
<name>Track 2012-06-12 19:51</name>
<trkseg>
<trkpt lat="43.49670697" lon="-112.03380961">
<ele>1403.0</ele>
<time>2012-06-13T01:53:44Z</time>
<extensions>
<ogt10:accuracy>34.0</ogt10:accuracy></extensions>
</trkpt>
<trkpt lat="43.49796612" lon="-112.03970968">
<ele>1410.9000244140625</ele>
<time>2012-06-13T01:57:10Z</time>
<extensions>
<gpx10:speed>3.75</gpx10:speed>
<ogt10:accuracy>13.0</ogt10:accuracy>
<gpx10:course>293.20001220703125</gpx10:course></extensions>
</trkpt>
<trkpt lat="43.49450857" lon="-112.04477274">
<ele>1406.5</ele>
<time>2012-06-13T02:02:24Z</time>
<extensions>
<ogt10:accuracy>12.0</ogt10:accuracy></extensions>
</trkpt>
</trkseg>
<trkseg>
<trkpt lat="43.49451057" lon="-112.04480354">
<ele>1398.9000244140625</ele>
<time>2012-06-13T02:54:55Z</time>
<extensions>
<ogt10:accuracy>10.0</ogt10:accuracy></extensions>
</trkpt>
<trkpt lat="43.49464813" lon="-112.04472215">
<ele>1414.9000244140625</ele>
<time>2012-06-13T02:56:06Z</time>
<extensions>
<ogt10:accuracy>7.0</ogt10:accuracy></extensions>
</trkpt>
<trkpt lat="43.49432573" lon="-112.04489684">
<ele>1410.9000244140625</ele>
<time>2012-06-13T02:57:27Z</time>
<extensions>
<gpx10:speed>3.288236618041992</gpx10:speed>
<ogt10:accuracy>21.0</ogt10:accuracy>
<gpx10:course>196.1999969482422</gpx10:course></extensions>
</trkpt>
<trkpt lat="43.49397445" lon="-112.04505216">
<ele>1421.699951171875</ele>
<time>2012-06-13T02:57:30Z</time>
<extensions>
<gpx10:speed>3.0</gpx10:speed>
<ogt10:accuracy>17.0</ogt10:accuracy>
<gpx10:course>192.89999389648438</gpx10:course></extensions>
</trkpt>
<trkpt lat="43.49428702" lon="-112.04265923">
<ele>1433.0</ele>
<time>2012-06-13T02:58:46Z</time>
<extensions>
<gpx10:speed>4.5</gpx10:speed>
<ogt10:accuracy>18.0</ogt10:accuracy>
<gpx10:course>32.400001525878906</gpx10:course></extensions>
</trkpt>
<trkpt lat="43.49444603" lon="-112.04263691">
<ele>1430.199951171875</ele>
<time>2012-06-13T02:58:50Z</time>
<extensions>
<gpx10:speed>4.5</gpx10:speed>
<ogt10:accuracy>11.0</ogt10:accuracy>
<gpx10:course>29.299999237060547</gpx10:course></extensions>
</trkpt>
<trkpt lat="43.49456961" lon="-112.04260058">
<ele>1430.4000244140625</ele>
<time>2012-06-13T02:58:52Z</time>
<extensions>
<gpx10:speed>4.5</gpx10:speed>
<ogt10:accuracy>8.0</ogt10:accuracy>
<gpx10:course>28.600000381469727</gpx10:course></extensions>
</trkpt>
<trkpt lat="43.49570131" lon="-112.04001132">
<ele>1418.199951171875</ele>
<time>2012-06-13T03:00:08Z</time>
<extensions>

【问题讨论】:

出于好奇:你有没有想过这个问题? 【参考方案1】:

你可以安装 GPXpy

sudo pip install gpxpy

然后就使用库:

import gpxpy 
import gpxpy.gpx 

 gpx_file = open('input_file.gpx', 'r') 

    gpx = gpxpy.parse(gpx_file) \
    for track in gpx.tracks: 
      for segment in track.segments: 
    for point in segment.points: 
      print 'Point at (0,1) -> 2'.format(point.latitude, point.longitude, point.elevation) 

    for waypoint in gpx.waypoints: 
      print 'waypoint 0 -> (1,2)'.format(waypoint.name, waypoint.latitude, waypoint.longitude) 

    for route in gpx.routes: 
      print 'Route:' 

欲了解更多信息:https://pypi.python.org/pypi/gpxpy

问候

【讨论】:

sudo pip 几乎从来都不是一个好主意(例如,参见this)。【参考方案2】:

GPX is an XML format,所以使用像lxml这样的拟合模块或包含的ElementTree XML API来解析数据,然后使用pythoncsv module输出到CSV。

涵盖这些概念的教程:

lxml.etree tutorial ElementTree tutorial Dive into Python 3 on XML processing Handling CSV Files in Python

我还发现了一个名为 gpxpy 的 python GPX 解析库,它可能为 GPX 文件中包含的数据提供了更高级别的接口。

【讨论】:

我会试试这个。有人还向我建议,Perl 可能是一种提取这些的方法。由于我对两者都是新手,因此我将首先查看您提到的教程。谢谢你,Martijn! Perl 同样适合这项任务;有 Perl XML 解析器和 CSV 库,就像 python 一样。但是,您可能会发现 Python 更容易学习。在我个人看来,Perl 太容易演变成线路噪音。【参考方案3】:

自从 Martijn 发布了一个 Python 答案并说 Perl 会变成线路噪音,我觉得也需要一个 Perl 答案。

在CPAN,Perl 模块目录中,有一个名为Geo::Gpx 的模块。正如 Martijn 所说,GPX 是一种 XML 格式。但幸运的是,已经有人把它做成了一个为我们处理解析的模块。我们所要做的就是加载那个模块。

有几个模块可用于处理 CSV,但这个 XML 文件中的数据相当简单,所以我们真的不需要。我们可以使用内置功能自行完成。

请考虑以下脚本。一会我会解释的。

use strict;
use warnings;
use Geo::Gpx;
use DateTime;
# Open the GPX file
open my $fh_in, '<', 'fells_loop.gpx';
# Parse GPX
my $gpx = Geo::Gpx->new( input => $fh_in );
# Close the GPX file
close $fh_in;

# Open an output file
open my $fh_out, '>', 'fells_loop.csv';
# Print the header line to the file
print $fh_out "time,lat,lon,ele,name,sym,type,desc\n";

# The waypoints-method of the GEO::GPX-Object returns an array-ref
# which we can iterate in a foreach loop
foreach my $wp ( @ $gpx->waypoints()  ) 
  # Some fields seem to be optional so they are missing in the hash.
  # We have to add an empty string by iterating over all the possible
  # hash keys to put '' in them.
  $wp->$_ ||= '' for qw( time lat lon ele name sym type desc );

  # The time is a unix timestamp, which is hard to read.
  # We can make it an ISO8601 date with the DateTime module.
  # We only do it if there already is a time, though.
  if ($wp->'time') 
    $wp->'time' = DateTime->from_epoch( epoch => $wp->'time' )
                             ->iso8601();
  
  # Join the fields with a comma and print them to the output file
  print $fh_out join(',', (
    $wp->'time',
    $wp->'lat',
    $wp->'lon',
    $wp->'ele',
    $wp->'name',
    $wp->'sym',
    $wp->'type',
    $wp->'desc',
  )), "\n"; # Add a newline at the end

# Close the output file
close $fh_out;

让我们分步进行:

use strictuse warnings 执行声明变量等规则,并告诉您最难发现的常见错误。 use Geo::Gpxuse DateTime 是我们使用的模块。 Geo::Gpx 将为我们处理解析。我们需要 DateTime 将 unix 时间戳变成可读的日期和时间。 open 函数打开一个文件。 $fh_in 是保存文件句柄的变量。我们要读取的 GPX 文件是 fells_loop.gpx,我冒昧地从 topografix.com 借来了它。您可以在perlopentut 中找到有关open 的更多信息。 我们创建一个名为$gpx 的新Geo::Gpx 对象并使用我们的文件句柄$fh_in 告诉它从哪里读取XML 数据。 new-方法由所有具有面向对象接口的 Perl 模块提供。 close 关闭文件句柄。 下一个open 有一个&gt; 告诉Perl 我们要写入这个文件句柄。 我们将print 作为print 的第一个参数添加到文件句柄。请注意,文件句柄后没有逗号。 \n 是换行符。

foreach loop 获取Geo::Gpx 对象的waypoints 方法的返回值。该值是一个数组引用。将此视为一个包含数组的数组(如果您想了解有关引用的更多信息,请参阅perlref)。在循环的每次迭代中,该数组 ref 的下一个元素(表示 GPX 数据中的航点)将被放入$wp。如果用Data::Dumper 打印,它看起来像这样:

$VAR1 = 
      'ele' => '64.008000',
      'lat' => '42.455956',
      'time' => 991452424,
      'name' => 'SOAPBOX',
      'sym' => 'Cemetery',
      'desc' => 'Soap Box Derby Track',
      'lon' => '-71.107483',
      'type' => 'Intersection'
    ;

现在后缀 for 有点棘手。正如我们刚刚看到的,hashref 中有 8 个键。不幸的是,其中一些有时会丢失。因为我们有use warnings,所以如果我们尝试访问这些缺失值之一,我们会收到警告。我们必须创建这些键并在其中放置一个空字符串''

foreachfor 在 Perl 中是完全可以互换的,并且两者也可以在单个表达式后面的 postfix 语法中使用。我们使用qw-运算符创建for 将迭代的列表。 qwquoted words 的缩写,它就是这样做的:它返回其中的字符串列表,但被引用。我们也可以说('time', 'lat', 'long'... )

在表达式中,我们访问$wp 的每个键。 $_ 是循环变量。在第一次迭代中,它将保持“时间”,然后是“纬度”,依此类推。由于$wp 是一个hashref,我们需要-&gt; 来访问它的键。花括号表明它是一个 hashref。 ||= operator 仅在我们的哈希引用元素不是真值时才为其分配一个值。

现在,如果有一个时间值(如果没有设置日期,我们刚刚分配的空字符串被认为是“没有”),我们将 unix 时间戳替换为适当的日期。 DateTime 帮助我们做到这一点。 from_epoch 方法获取 unix 时间戳作为参数。它返回一个DateTime 对象,我们可以直接使用它来调用它上面的iso8601 函数。

这称为链接。有些模块可以做到。它类似于 jQuery 的 javascript 对象所做的。我们 hashref 中的 unix 时间戳被替换为 DateTime 操作的结果。

现在我们再次print 到我们的文件句柄。 join 用于在值之间放置逗号。我们还在末尾添加了一个换行符。 完成循环后,我们close 文件句柄。 现在我们完成了! :)

总而言之,我想说这很简单,也很可读,不是吗?我试图使它成为一个健康的混合过于冗长的语法和 _Perl_ish 风格。

【讨论】:

感谢您的脚本!我去了 CPAN,看了@ readme 并有错误。 perl Makefile.PL 命令导致: Optional ExtUtils::MakeMaker::Coverage not available Argument "6.57_05" is not numeric in numeric ge (>=) at Makefile.PL line 34. 检查您的套件是否完整。 . 看起来不错 警告:未找到先决条件 DateTime::Format::ISO8601 0。警告:未找到先决条件 html::Entities 0。警告:未找到先决条件 XML::Descent 1.01。为 Geo::Gpx 编写 Makefile 编写 MYMETA.yml 进行了 w/make test & 8/10 tests & 3/3 subtests 失败。试图只运行 lat,lon,elev, w/noluck 所以我在 make 测试中有 4 页错误,尽管试图从文本中删除时间和所有其他字段,除了 lat、lon、elev 并无论如何都运行它,但没有运气。我昨天阅读了 perl 入门书的前 3 章,所以我希望有简单的修复,我也尝试在 sudo 下重新安装,但没有运气。该脚本很有意义,我也很欣赏解释部分。作为一个新手,我现在正在摸不着头脑。 您是否阅读过有关如何install cpan modules 的手册?还是您尝试从 CPAN 网站下载它?如果使用命令行工具,它将安装所有依赖项。 啊,在 Beginning Perl 中,它应该在第 2 章中告诉你所有关于 cpan 的信息。如果你在 Windows 上使用 ActivePerl,还有一个名为 ppm 的程序这将为您提供一个很好的 GUI 来安装模块。您可以使用其中任何一个来一次性获取所有依赖项所需的模块。 听起来不错,我会调查一下,你是对的,我确实从网站下载了,但我会在 bash/ubuntu 的命令提示符下下载。【参考方案4】:

每次我尝试这样做时,我都会在互联网上搜索解决方案并最终编写自己的正则表达式解析器。

import re
import numpy as np

GPXfile='Lunch_Walk.gpx'
data = open(GPXfile).read()

lat = np.array(re.findall(r'lat="([^"]+)',data),dtype=float)
lon = np.array(re.findall(r'lon="([^"]+)',data),dtype=float)
time = re.findall(r'<time>([^\<]+)',data)


combined = np.array(list(zip(lat,lon,time)))

这给出了格式的数组:

array([['51.504613', '-0.141894', '2020-12-26T12:43:14Z'],
       ['51.504624', '-0.141901', '2020-12-26T13:10:26Z'],
       ['51.504633', '-0.141906', '2020-12-26T13:10:28Z'],
       ...)

然后,您可以随心所欲地使用它。

【讨论】:

【参考方案5】:

虽然gpxpy 是流行的 python 答案,我自己找到了这个答案并尝试了它,但我发现很难获得像心率这样的扩展类型数据,如果不是不可能的话,而且仍然需要遍历各种嵌套的xml祖先/子,所以我写了gpxcsv。

简单到:

from gpxcsv import gpxtolist
import pandas as pd

df = pd.DataFrame(
    pxtolist('myfile.gpx'))

对于数据框,或命令行工具仅用于创建 csv 或 json 文件,使用标签作为列名在跟踪点中保留尽可能多的列。

项目源代码on github。

【讨论】:

以上是关于如何使用python提取.gpx数据的主要内容,如果未能解决你的问题,请参考以下文章

如何从谷歌地图路线中提取路线 GPX?

如何在 gpx 文件中找到第 10 个跟踪点

如何将.gpx文件附加到使用Google Apps脚本发送的邮件中?

连接 GPX 跟踪点

如何在保留属性的同时将 GPX 文件直接转换为线的 SpatVector?

使用 Python 中的列表理解读取 GPX 文件时出现 UTF 错误