轨迹压缩算法(Polyline encoding algorithm)探究

前言

文中的代码表达采用Java,如果不想看原理,可以直接跳到博客末尾,有我写的一个Java版的demo 一般来说,轨迹是由若干个轨迹点组成的数组来表达的,每个轨迹点可以表达为 p = ( x , y , t , A ) p = (x, y, t, A) p=(x,y,t,A)。其中 x , y x, y x,y 是其空间坐标(一般来说,空间坐标用经纬度表示,即 x x x一般对应经度(longitude ), y y y一般对应纬度(latitude )), t t t是记录这个轨迹点的时间戳(Timestamp ), A A A则是这个轨迹点的属性。如果只想保留一个轨迹点最基本的信息,则只需要保留其空间信息和时间信息,即可以将一个轨迹点表示为 p = ( x , y , t ) p = (x, y, t) p=(x,y,t)。在写代码时,一个轨迹点的类型可以定义如下:

Java 复制代码
class PointTs {
    double lng;
    double lat;
    long timestamp;
}

而这样保存一个轨迹点, x x x和 y y y一般使用double 类型, t t t一般使用long 类型,再加上至少3个字符的分隔符(分隔x,y,t和每个轨迹点),一个轨迹点就要占 Double.SIZE * 2 + Long.SIZE + Character.SIZE * 3 == 27 * Byte.SIZE, 也就是说保存一个轨迹点至少需要占27个字节。在如今轨迹数量越来越大的情况下,有必要使用轨迹压缩的算法来解决轨迹数据量过大的问题。在查阅了资料后,我在Google地图开发平台中看到了Google采用的轨迹数据压缩算法,名为Polyline encoding algorithm(需要科学上网),可以将若干个轨迹点编码成一串字符串,并且实现轨迹的压缩。这是一个有损的压缩算法,并且可以压缩有符号的数字。 接下来我将从编码和解码两个方面解析这个算法,并给出示例代码。

编码

Polyline encoding算法的核心思想是对一个浮点数进行压缩,比如说,有一个经度为: -179.9832104

对于这样的浮点型数字,首先需要做的是想办法去掉其小数点。所以,可以预设一个10的整数倍的值来与这个浮点数相乘,并取得整数。

  1. 在这里,我预设这个值为 1 0 5 10^5 105,将这个经度乘以 1 0 5 10^5 105,得到: -17998321

  2. 将上一步得到的整数转换成二进制,由于我们的值是负数,得到的是17998321的补码: 11111110 11101101 01011110 00001111

  3. 将二进制数左移一位: 11111101 11011010 10111100 00011110

  4. 如果原来的数是负数,则取反码: 00000010 00100101 01000011 11100001 3、4两步的目的是为了区分正负数,这样做后,负数的最后一位都是1,正数的最后一位都是0

  5. 从右到左,每5位分为一组(一组称为一个chunk): 00001 00010 01010 10000 11111 00001 这样可以舍弃高位的0,节约空间。

  6. 将这些分组顺序反转(reverse order): 00001 11111 10000 01010 00010 00001 这样做的目的是在写代码时可以每次从低位取数据,更适合代码实现。因为一般而言,写代码读二进制时,读高位比较复杂,而读低位较简单(例如,读最后五位只需要 &0x1F),读到最后几位后可以直接append到结果中,不用插入到结果的前端,效率更高。

  7. 除了最后一个chunk,其他chunk和0x20进行或操作: 100001 111111 110000 101010 100010 000001 这一步的目的是保证只有最后一个chunk是小于0x20的。起到了分隔的作用。

  8. 分别将每个二进制的chunk转化为十进制的数字: 33 63 48 42 34 1

  9. 对每个数字都加上63: 96 126 111 105 97 64 由于ASCII码前63位中有很多不可显示的控制符(比如NULL、换行符之类的),并且第127个是删除符,所以这一步将数字范围控制在了64~126之间。

  10. 将数字转化成ASCII表中对应的字符:

txt 复制代码
`~oia@

综上,一个浮点数被编码成了一串字符串,并且自带分隔符。对于时间戳这样的长整型来说,不进行第1步也可以编码为一串字符串,那么,将经度、纬度和时间戳的编码拼接起来就能够组成一个轨迹点的编码,还不用额外的分隔符。 其实如果单独对一个数字来说,这个方法的压缩率并不高,有时候还会得不偿失。比如上述的数字-179.9832104,使用这种方法压缩后成为5个字符,占5个字节。如果将直接乘以 1 0 5 10^5 105,可以直接用有符号的整型表示,还只占4个字节呢!所以,这个算法还有一个思想就是:后一个值用其与前一个值的差值表示,由于差值比较小,所以在上述的第5步时可以将高位的零舍弃掉,节省空间。下面是Google地图开发平台中给出的例子(这个例子中没有带时间戳):

可以看到,这个例子中除了第一个轨迹点存储时全量信息(完整的经纬度数据)之外,后面的轨迹点都是存储的相对于前一个轨迹点偏移量。

解码

解码其实就比较简单了,相当于把编码步骤倒过来执行一遍。编码中的步骤4可以使得我们在解码时知道这个数字是正数还是负数,步骤7可以让我们知道数字的边界在哪,所以还是比较方便解码的,我在此就不再赘述解码过程了。

Talk is cheap, show me the code

原理知道了之后,代码就比较简单了,贴一个我写的代码: SimpleTrajectoryCompressor

相关推荐
GlobalTradercrb4 天前
独立站SEO先写博客还是先优化产品页?看站点阶段
数据库·人工智能·google·pdf·go·seo
智码看视界5 天前
开源大模型追踪:TurboQuant量化指南:128K上下文不再爆显存,KV缓存3-bit免重训
google·开源大模型·llama.cpp·显存优化·长上下文推理·turboquant·本地部署 开源大模型
Frag0ut8 天前
获取Chrome官方安装包:离线版安装风险避坑指南
chrome·google·谷歌浏览器·安装包·chromesetup.exe·在线安装包·离线版
用户38481618018368 天前
写 Go 命令行小工具时我常用的几个习惯
google
知之则吱吱11 天前
Android 开发者验证机制迎来新变化:核心要求、实施时间与开发者应对指南
android·google
Frag0ut13 天前
Chrome数据备份指南:本地备份还原书签密码与扩展配置工具
chrome·google·谷歌浏览器·数据同步·备份工具·配置迁移·书签密码备份
GlobalAIagent13 天前
外贸站上线前的技术自查清单:漏一项询盘可能消失
google·typescript·ar·go语言·seo
知昂七昂14 天前
00-01:AOSP 源码仓库结构与 repo 工作流源码剖析(Android 16 / aosp-main)
android·google
Frag0ut17 天前
Chrome安全隐私设置指南:5个原生功能筑牢浏览器数字防线
chrome·google·谷歌浏览器·权限管理·隐私保护·安全设置·安全浏览
Frag0ut20 天前
Chrome Dev 最新功能更新:AI 智能体开发工具、HTML-in-Canvas 与双周更新节奏
chrome·google·dev·webmcp·最新功能·双周更新