2. 数据格式与解析

大家好,我是老张。今天咱们聊聊轨迹数据那些事儿。

说实话,我见过太多人花大把时间调算法、优化渲染,结果最后发现——数据格式读错了。嗯,这感觉就像你修了半天车,结果发现油箱里加的是水。所以这一讲,咱们把数据格式和解析这块地基打牢。

一、常见轨迹格式:各有各的脾气

做轨迹回放这么多年,我接触过的格式少说也有十几种。但真正常用的,其实就四种:GPX、KML、CSV、JSON。每种格式都有自己的「性格」,咱们一个一个说。

1. GPX —— GPS界的通用语

GPX 全称 GPS Exchange Format,说白了就是 GPS 数据的「普通话」。它基于 XML,结构清晰,但写起来有点啰嗦。

我个人习惯用 GPX 做户外运动轨迹的存储。为什么?因为它原生支持时间戳、海拔、速度这些字段,而且几乎所有运动手表都认它。

核心结构: GPX 文件里最重要的三个标签是 <trk>(轨迹)、<trkseg>(轨迹段)、<trkpt>(轨迹点)。
<?xml version="1.0" encoding="UTF-8"?>
<gpx version="1.1" creator="myApp">
  <trk>
    <name>晨跑轨迹</name>
    <trkseg>
      <trkpt lat="39.9042" lon="116.4074">
        <ele>52.3</ele>
        <time>2024-01-15T06:30:00Z</time>
      </trkpt>
      <trkpt lat="39.9045" lon="116.4078">
        <ele>52.5</ele>
        <time>2024-01-15T06:30:05Z</time>
      </trkpt>
    </trkseg>
  </trk>
</gpx>
我的小技巧: 解析 GPX 时,别用正则硬啃。用现成的 XML 解析库(比如 Python 的 xml.etree.ElementTree),省心又稳定。我在项目里吃过正则的亏,后来全换了。

2. KML —— Google Earth 的亲儿子

KML 也是 XML 格式,但它是 Google Earth 的「专属语言」。跟 GPX 比,KML 更擅长表达可视化信息——比如轨迹的颜色、线宽、图标。

我记得有一次做无人机航迹回放,甲方要求轨迹在地图上显示为红色虚线,而且每个航点要带编号。用 KML 做这个,简直不要太顺手。

<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
  <Document>
    <Placemark>
      <name>无人机航迹</name>
      <LineString>
        <coordinates>
          116.4074,39.9042,52.3
          116.4078,39.9045,52.5
          116.4082,39.9048,52.7
        </coordinates>
      </LineString>
    </Placemark>
  </Document>
</kml>
注意: KML 的坐标顺序是「经度,纬度,海拔」,跟 GPX 的「纬度,经度」正好相反。我曾经因为这个顺序搞反,轨迹画到了非洲去……嗯,别问我怎么知道的。

3. CSV —— 简单粗暴,但坑也多

CSV 是最「朴实」的格式。没有花哨的标签,就是一行行逗号分隔的数据。但越简单的东西,越容易出幺蛾子。

你想想看,CSV 没有标准字段名,没有数据类型定义。同一个文件,有人写 lat,lon,有人写 latitude,longitude,还有人写 y,x。解析的时候,全靠猜。

timestamp,lat,lon,altitude,speed
2024-01-15 06:30:00,39.9042,116.4074,52.3,0.0
2024-01-15 06:30:05,39.9045,116.4078,52.5,1.2
2024-01-15 06:30:10,39.9048,116.4082,52.7,1.5
避坑指南: 解析 CSV 时,我建议你第一件事就是检查表头。写个自动检测函数,把常见的字段名映射表准备好。比如 lat、latitude、y 都映射到纬度字段。这招帮我省了至少一半的调试时间。

4. JSON —— 现代应用的最爱

JSON 是目前 Web 应用和移动端最流行的格式。结构清晰,解析快,而且天然支持嵌套数据。比如一个轨迹点可以同时包含位置、传感器数据、状态信息。

{
  "name": "通勤路线",
  "points": [
    {
      "timestamp": "2024-01-15T08:00:00Z",
      "lat": 39.9042,
      "lon": 116.4074,
      "alt": 52.3,
      "speed": 0.0,
      "accuracy": 3.5
    },
    {
      "timestamp": "2024-01-15T08:00:05Z",
      "lat": 39.9045,
      "lon": 116.4078,
      "alt": 52.5,
      "speed": 1.2,
      "accuracy": 3.2
    }
  ]
}

我个人最喜欢 JSON 的一点是——它可以直接被 JavaScript 消费。做前端轨迹回放时,后端返回 JSON,前端直接 JSON.parse(),一气呵成。

二、坐标系基础:你在哪个「地球」上说话?

聊完格式,咱们说说坐标系。这玩意儿看着简单,但搞错了,轨迹能偏出十万八千里。

1. WGS84 —— 全球通用的「地球模型」

WGS84 是目前最通用的地理坐标系。GPS 卫星用的就是它。说白了,你手机里拿到的经纬度,默认就是 WGS84 坐标。

它的核心参数:地球长半轴 6378137 米,扁率 1/298.257223563。嗯,这些数字你不需要背,但要知道——不同坐标系之间转换时,这些参数决定了精度。

关键点: WGS84 是「地理坐标系」,单位是度。它描述的是球面上的位置,不是平面上的位置。

2. UTM —— 把球面「拍扁」成平面

UTM 全称 Universal Transverse Mercator,通用横轴墨卡托投影。它把地球分成 60 个带,每个带 6 度经度宽,然后投影到平面上。

为什么要用 UTM?因为 WGS84 的经纬度算距离很麻烦——1 度经度的长度在不同纬度不一样。而 UTM 坐标是米为单位,算距离直接勾股定理就行。

我记得做车辆轨迹回放时,需要计算车辆偏离路线的距离。用 WGS84 算,得用 Haversine 公式,麻烦。转成 UTM 后,直接 sqrt(dx² + dy²),简单粗暴。

坐标系 类型 单位 适用场景
WGS84 地理坐标系 度 (°) 全球定位、数据存储
UTM 投影坐标系 米 (m) 距离计算、局部区域分析
注意: UTM 在不同带之间不连续。如果你的轨迹跨越两个 UTM 带(比如东西跨度超过 6 度),需要做带间转换。我有个项目就是没注意这个,轨迹在带边界处断开了,排查了半天。

三、数据清洗入门:脏数据是回放的天敌

数据清洗,说白了就是「去伪存真」。我见过太多轨迹回放项目,算法再牛,数据一脏,全白搭。

常见的脏数据问题

  • 漂移点: GPS 信号不好时,点位会突然跳到几百米外。比如你在路口等红灯,轨迹却显示你飞到了对面楼顶。
  • 重复点: 同一时间戳出现多个相同位置的点。这通常是设备缓存导致的。
  • 时间戳乱序: 后采集的点时间戳比前面的还早。嗯,设备时钟不同步的锅。
  • 缺失值: 某些字段为空,比如海拔或速度。

我的清洗三板斧

第一斧:去重。按时间戳排序,删除时间戳完全相同的重复点。

第二斧:滤波。用卡尔曼滤波或简单的速度阈值过滤漂移点。比如,如果两个相邻点之间的速度超过 200 km/h,那大概率是漂移。

第三斧:插值。对于缺失值,用前后点的线性插值补全。但要注意——如果缺失段太长(比如超过 10 秒),就别插了,直接断开成两条轨迹。

我曾经踩过的坑: 有一次做马拉松轨迹回放,选手的 GPS 手表在隧道里丢了信号,出来时时间戳乱了。我没做时间戳排序,直接按原始顺序渲染,结果轨迹在隧道出口处画了个「8」字形。后来我加了一步——先按时间戳排序,再清洗,问题解决。

四、知识体系总览

下面这张图,是我自己总结的「轨迹数据格式与解析」知识体系。你可以把它当作本章的思维导图。

轨迹数据格式与解析 常见轨迹格式 GPX (XML) KML (XML) CSV (文本) JSON 坐标系基础 WGS84 (地理坐标) UTM (投影坐标) 数据清洗入门 去重(时间戳排序) 滤波(速度/卡尔曼) 插值(线性补全) 核心原则:先理解格式,再选对坐标系 最后清洗数据——顺序不能乱

好了,这一讲的内容就到这里。数据格式和坐标系是轨迹回放的「基础设施」,基础不牢,地动山摇。下一讲咱们会深入解析器的实现,到时候这些知识都会用上。

可可精品资料库