4、GPX文件解析:用Python把轨迹数据“榨”出来
说实话,我第一次接触GPX文件时,还以为它是什么高深的二进制格式。结果用记事本打开一看——嚯,这不就是XML嘛!
GPX全称是GPS Exchange Format,说白了就是GPS数据的通用交换格式。你从佳明手表、两步路、甚至手机上的运动App导出的轨迹,十有八九都是这个格式。我当年做户外轨迹分析项目时,光是解析不同设备导出的GPX就折腾了好几天——各家厂商的“方言”还真不一样。
核心要点:GPX本质是XML,解析它就是在解析一棵XML树。我们关心的数据藏在 <trkpt> 标签里。
4.1 为什么选gpxpy?
Python里解析GPX有好几种路子:
- 自己写XML解析——用ElementTree硬啃,灵活但累
- 用gpxpy——专门干这活的库,省心
- 用geopandas——杀鸡用牛刀,适合批量处理
我个人习惯用gpxpy。为什么?因为它把时间解析、坐标转换、海拔提取这些脏活都包了。你想想看,要是自己处理时区、处理不同版本的GPX命名空间……嗯,那画面太美我不敢看。
安装命令:pip install gpxpy
就一行,没别的依赖。清爽。
4.2 解析流程:三步走
解析GPX其实就三步:读文件 → 解析 → 提取数据。我画了张图帮你理清思路:
4.3 实战代码:从文件到数据
来,直接上代码。这是我项目里常用的模板,稍微简化了一下:
import gpxpy
# 第一步:读文件
with open('my_track.gpx', 'r', encoding='utf-8') as f:
gpx_content = f.read()
# 第二步:解析
gpx = gpxpy.parse(gpx_content)
# 第三步:提取轨迹点
points = []
for track in gpx.tracks:
for segment in track.segments:
for point in segment.points:
points.append({
'lat': point.latitude,
'lon': point.longitude,
'time': point.time,
'elevation': point.elevation,
'speed': point.speed if hasattr(point, 'speed') else None
})
print(f"共提取 {len(points)} 个轨迹点")
print(f"第一个点:{points[0]}")
⚠️ 注意:不是所有GPX文件都包含速度信息。有些设备不记录速度,需要你自己根据距离和时间计算。我曾经遇到过一批从某国产手表导出的GPX,速度字段全是None——当时排查了半天才发现是设备本身就没记录。
4.4 提取的信息详解
每个轨迹点里藏着哪些宝贝?我整理了一张表:
| 字段 | 类型 | 说明 | 常见问题 |
|---|---|---|---|
| latitude | float | 纬度,范围 -90 ~ 90 | 注意WGS84坐标系 |
| longitude | float | 经度,范围 -180 ~ 180 | 东经为正,西经为负 |
| time | datetime | UTC时间戳 | 记得转本地时间 |
| elevation | float | 海拔高度(米) | 部分设备不准 |
| speed | float | 速度(m/s) | 可能缺失 |
小技巧:如果速度字段缺失,可以用前后两个点的距离除以时间差来估算。公式很简单:speed = distance / delta_time。我一般用 geopy.distance 算距离,精度够用。
4.5 避坑指南:我踩过的三个坑
做GPX解析这几年,我踩过的坑真不少。挑三个最典型的说说:
- 编码问题——有些国产软件导出的GPX是GBK编码,直接open会报错。我后来统一用
encoding='utf-8'先试,不行就换gbk。 - 时间格式——GPX标准要求ISO 8601格式,但有些设备会带毫秒、有些带时区偏移。gpxpy能处理大部分情况,但偶尔也会翻车。我遇到过一种奇葩格式:时间后面跟了个"Z"但实际是北京时间……
- 空轨迹段——有些GPX文件有track但没有segment,或者segment里没有points。遍历前一定要判空,不然
for segment in track.segments直接报错。
我的经验:拿到一个GPX文件后,先别急着解析。用文本编辑器打开看一眼——看看有没有 <trkpt> 标签,看看时间格式长什么样。这一步花不了30秒,但能省下后面半小时的调试时间。
4.6 完整示例:解析并打印轨迹摘要
最后,给你一个可以直接跑的完整示例。我平时做数据质量检查就用这个:
import gpxpy
from datetime import datetime
def parse_gpx_summary(filepath):
with open(filepath, 'r', encoding='utf-8') as f:
gpx = gpxpy.parse(f.read())
total_points = 0
start_time = None
end_time = None
max_ele = float('-inf')
min_ele = float('inf')
for track in gpx.tracks:
for segment in track.segments:
for point in segment.points:
total_points += 1
if start_time is None or point.time < start_time:
start_time = point.time
if end_time is None or point.time > end_time:
end_time = point.time
if point.elevation and point.elevation > max_ele:
max_ele = point.elevation
if point.elevation and point.elevation < min_ele:
min_ele = point.elevation
duration = (end_time - start_time).total_seconds() if start_time and end_time else 0
print(f"轨迹点数量:{total_points}")
print(f"开始时间:{start_time}")
print(f"结束时间:{end_time}")
print(f"持续时间:{duration:.0f} 秒 ({duration/60:.1f} 分钟)")
print(f"最高海拔:{max_ele:.1f} 米")
print(f"最低海拔:{min_ele:.1f} 米")
return {
'points': total_points,
'start': start_time,
'end': end_time,
'duration': duration,
'max_elevation': max_ele,
'min_elevation': min_ele
}
# 使用示例
summary = parse_gpx_summary('my_track.gpx')
嗯,这个函数我用了两年多了,从没出过问题。你拿去改改就能用。
解析GPX其实没那么玄乎。记住核心思路:读文件 → 解析 → 遍历提取。剩下的就是根据你的业务需求,从每个轨迹点里捞出你需要的信息。经纬度、时间、海拔、速度——这四个字段够你玩出花来了。