C语言中的纬度/经度存储和压缩

Latitude/Longitude storage and compression in C

提问人:Justicle 提问时间:8/3/2009 最后编辑:luvieereJusticle 更新时间:11/16/2023 访问量:22310

问:

有谁知道纬度/经度坐标的最有效表示?对于消费类 GPS 设备来说,精度水平应该足够。

大多数实现似乎都用于每个单元,但我怀疑定点格式或定点格式就足够了。我很想听听任何试图压缩和/或存储这些值的大型数组的人的意见。doublefloat

编辑:

换言之,对于消费级设备,表示经度/经度所需的最低精度是多少?

C 压缩 GPS 浮动精度

评论

0赞 starblue 8/3/2009
你想做什么?你在存储路径吗?
0赞 Justicle 8/4/2009
在消费类设备上存储和传输 GPS 数据。

答:

14赞 Evan Teran 8/3/2009 #1

编辑:从注释中添加了一些要点,32位值应该能够提供足够的精度。

我将使用 32 位定点表示形式。如果值为:

42.915512,我会将存储在int32_t中(它们可以是负数)。-99.521654values * 100000

int32_t lat = 42915512;
int32_t lon = -99521654;

这是简单和准确之间的一个很好的折衷方案(小数点后 5 位通常就足够了,如果需要,您可以随时将其提高到最低点)。10000006

若要向用户显示,请按照 caf 的建议执行操作:

...向用户显示 - 使用整数 除法和取模,例如printf("Lat = %d.%06d\n", lat / 1000000, abs(lat) % 1000000)

这些也将以有效的方式进行比较/排序,因为将保留相对顺序。

编辑:另一个好处是它可以通过网络发送或以可移植的方式以二进制格式存储到磁盘。

评论

2赞 Kieveli 8/3/2009
也许要小心一点,不要抹杀 -77.521654 和 77.521654 之间差异的含义
3赞 Tal Pressman 8/3/2009
我建议使用 2 的幂乘数而不是 10,000。如果您发现必须对数字进行硬编码,那么使用 10,000 可能是人类可读的,但否则就毫无用处了。此外,如果您确实使用此方法,请始终使用宏/内联函数将 double 转换为 ints。
0赞 caf 8/3/2009
unsigned 并不多,因为它们可以是负数。此外,.0001 度可以高达 22 米,而消费者 GPS 可以比这更准确。因此,使用有符号整数,并乘以至少 1000000(最大值仍然很容易放入有符号的 32 位)。
0赞 Evan Teran 8/3/2009
谢谢,好点,我忘了考虑负值,我已经调整了我的答案。
2赞 caf 8/3/2009
哦,不要强制转换为双倍和除法以显示给用户 - 使用整数除法和模数,例如 printf(“Lat = %d.%06d\n”, lat / 1000000, lat % 1000000)
5赞 MusiGenesis 8/3/2009 #2

浮标对于存储 GPS 坐标来说绰绰有余,即使消费级 GPS 设备的精度接近它们声称的精度。如果您不相信这是真的,请尝试以下两个简单的实验:

  1. 将两个或多个 GPS 设备带到田野某处的一个点,并记下每个设备测量的坐标。回到里面,在地图上绘制每个设备的点(我认为谷歌有一些东西可以为你做到这一点)。您会惊讶于这些点之间的距离(即使它们都应该测量完全相同的点)。
  2. 拿起你(据称)最精确的设备,把它放在一个可以得到卫星修复但不会被雨淋的地方,并记录几天内进行的一系列测量。绘制所有读数(如#1所示)。同样,你会惊讶于这些点(应该都相同或几乎相同)在地图上四处游荡,有时甚至高达几百英尺。

多年来,我一直在为支持 GPS 的 PDA 编写应用程序,并且我一遍又一遍地为可疑的客户验证了这一点(我什至以这种方式赢得了赌注)。市面上有更高质量的GPS设备,可以达到比这更高的精度,但更好的精度是通过更昂贵的芯片组实现的,并且这些设备在一个地方放置数天甚至数周,读数随时间平均。

四字节浮点数比设备本身要准确得多。当然,使用双倍不会伤害你,只要 2 倍因素对你来说不是问题。

评论

0赞 Justicle 8/3/2009
好点子 - 我想这个问题可以改写为“消费类 gps 设备所需的最低精度是多少?
1赞 Evan Teran 8/3/2009
把所有的答案都投了反对票?!就我个人而言,我认为你和我都是有效的答案。
0赞 MusiGenesis 8/3/2009
现实世界中有人对我特别生气,因为我打破了GPS精度的神话(然后我拿走了他们的钱)。我在 StackOverflow 上遇到过一些人,他们认为 32 位浮点数与真空管属于同一类别。所以从某种意义上说,这个问题是一场完美的风暴。:)
13赞 Kristoffon 8/3/2009 #3

地球的周长约为 40.000 公里或 24900 英里。

您需要一米的精度(3 英尺)才能将 GPS 精度提高一个数量级。

因此,您需要 precisiton 来存储 40.000.000 个不同的值。这至少是 26 位信息。32 位 float 或 int 会做得很好。

评论

0赞 caf 8/3/2009
不,您需要存储 40,075,020 个不同的值才能获得 1 米的分辨率,这需要 26 位。
2赞 RBerteig 8/3/2009
实际上,一个 32 位 IEEE 浮点数有 23 个显式分数位(和一个假设的 1)来表示 24 个有效有效位的有效位。这只能区分所需的 4000 万个唯一值中的 1600 万个。从另一个角度来看,它可以表示赤道2.4米以内的位置,这可能仍然足够近。
4赞 RBerteig 8/3/2009
我倾向于使用定点表示,因为浮点数对于这种应用程序没有优势,并且有符号的 32 位值有大量位可用于选择方便的刻度。
0赞 starblue 8/3/2009
@RBerteig 别忘了这个符号,它给了你另一个点,因为纬度和经度的默认表示是 ±180°。由于接近零时精度会更好,因此 32 位浮点数为您提供 1m 的精度,但日期变更线附近地球的 1/5 除外。
2赞 MusiGenesis 8/4/2009
@Ken:在地狱里,民用GPS设备不可能平均精确到5米(无论如何,精度通常是在统计意义上用方差和标准差等来衡量的——一个数字不可能反映精度)。他们的意思是:设备偶尔会测量距离真实位置 5 米以内的位置——有点像坏掉的时钟每天两次正确。
17赞 caf 8/3/2009 #4

就我个人而言,我会使用 32 位十进制定点表示,根据 Evan 的回答和我的评论除以 1,000,000。

但是,如果空间确实很宝贵,这里有一些额外的想法:

  • 您可以在线路上使用 26 位定点表示。这将需要将纬度和经度编组和解组为一个大字节数组,但与 32 位值表示相比,每个位置将节省 12 位 - 几乎节省了 19%,因此这很可能是值得的。

  • 您可以利用这样一个事实,即当您靠近两极时,经度值需要的精度会降低 - 它们在赤道上只需要 26 位。因此,您可以编写一个方案,其中用于编码经度的位数取决于纬度的值。

  • 如果您的数据具有其他可压缩属性(例如,所有点通常都非常接近),则可以利用这些属性进行特定利用,例如使用增量编码方案(其中除第一个点以外的每个点都可以编码为最后一个点的增量)。

-1赞 myforwik 8/3/2009 #5

令我惊讶的是,没有人发布这样一个事实,即 long/lat 是一种在球体上存储数据的糟糕方式(有人确实提到经度在两极附近需要较低的精度)。

基本上,您可以将数据位置存储为以米为单位的 X 和 Y 坐标。想象一个完全适合地球的立方体(哈哈,好吧,几乎适合它)。你只需要存储 X 和 Y 位置,而不是所有 3 个坐标,因为第 3 个坐标可以来自地球的重度,r = 平方根[x^2 + y^2 + z^2]。

因此,将您的纬度/经度转换为以米为单位的 x/y。每个坐标总共只需要 12756200 米(即地球的直径)。因此,您的总值只需要跨越 0 到 25,512,400(其他人声称 40,000,000,因为他们使用的是 long/lat)即可精确到 +/- 0.5m。

这将导致每个位置只有 25 位。如果我是你,我会把精度提高到2米以内,每个位置使用24位,因为这是一个整洁的3个字节。

此外,如果要在路径上存储航点信息,则可以将每个航点存储为与最后一个航点的偏移量。就像从 24 位 x/y 坐标开始一样。然后有一个 16 位的“更新”,通过添加/减去 x/y 仪表来调整位置。16 位将允许航点更新超过 400 米。因此,如果您知道该设备不适用于飞机并且每隔一段时间就会更新一次,那么这也可能是可以接受的。

评论

2赞 Dietrich Epp 8/3/2009
存储球体的 X/Y 坐标是行不通的。完全。在球体与XY平面的交点附近,你损失了很多精度,而且你无法重建Z坐标--你只能得到球体的一半。如果要寻求均匀性,请使用三维笛卡尔坐标。否则,纬度/经度是存储它的好方法。
0赞 MusiGenesis 8/3/2009
哇,你应该给Garmin打个电话,向他们解释位置信息的纬度和经度是多么“可怕”。这些年来他们在想什么?
0赞 Erich Mirabal 8/4/2009
UTM 对其东移和北移坐标对使用类似的方法,因此 X/Y“坐标”确实适用于球体。这完全是投射的问题。
0赞 Erich Mirabal 8/4/2009
MyForwik:不过,你的方法仍然存在问题。正如 Dietrich 所提到的,你的 X/Y 版本不是一个好的投影。您需要展平为 2D 平面,而不是 3D 立方体。
0赞 Juan 8/4/2009
Programming Pearls (2nd Edition) (ACM Press) (Paperback) 是一本优秀的书,它讨论了如何转换为 x,y,z 以减少为地图数据的一个特定应用而发生的昂贵的三角操作的数量。
2赞 Norman Ramsey 8/3/2009 #6

经度 23 度的 179 位精度低于 10 米,这是普通 GPS 设备所能提供的最佳精度。在赤道:

% gps distance "0.0, 179.0" "0.0, $((179 * (1 + 2**-23)))"
From 0.0, 179.0 to 0.0, 179.00002133846283 is 7.79 feet E
From 0.0, 179.0 to 0.0, 179.00002133846283 is 2.38 meters E

因此,IEEE 754 单精度浮点数(C 编译器称为 )将足以表示。谨防使用浮点数进行扩展计算!四舍五入错误可能会吃掉你的午餐。请咨询数值分析师。float

0赞 Erich Mirabal 8/4/2009 #7

如果使用递归切片系统,则可以将纬度和经度值打包到单个 32 位整数中,分辨率最差为 ~2.4 米/像素(在赤道处)。使用每个级别两个位,您可以以 32 位存储 16 个级别。您可以查看这篇关于 Virtual Earth 平铺系统的文章,了解其工作原理。这使用墨卡托,所以它会给你带来极点的问题。您可以改用不同的投影,但仍然会得到非常相似的结果。

这也可用于粗略过滤器,以查找给定父图块中的任何点,因为前 N 位将相同(因此搜索变为位掩码)。

评论

2赞 Sphinxxx 11/5/2017
-1:苹果和橙子:查看文章中的表格,在第 16 级,分辨率为 2.4 米/像素,地图宽 16,777,216 像素 (2^24),因此在缩放级别 16,我们需要 24 位来存储每个纬度/经度值,即 48 位来存储两者。
3赞 KPexEA 8/4/2009 #8

在Garmin的IMG地图格式中,他们使用浮点数将坐标存储在边界框内,以设置框的边缘。框中的坐标是使用可变位数的位数定义的,这些位数在最小值和最大值之间只是线性的,具体取决于所需的精度。

例如:minlat=49.0, maxlat=50.0, minlon=122.0, maxlon=123.0, 位数=16

因此,值:
32768,32768 将转换为 49.5,122.5 16384,0 将是 49.25,122.0
如果您需要较低的精度,则可以生成相同的输出,位数=4
8,8 将转换为
49.5,122.5
4,0
将是 49.25, 122.0

3赞 hunterino 8/16/2013 #9

如果要存储这些值的大型数组,则执行增量压缩并存储增量时,可以使用一些简单的技巧来大大减小数据流的大小。 您可以从“关键点”进行增量

K D D D

k + d 让你到达任何 d 点

增量都引用前一个 K,因此要重建任何点,您需要一个 K 和一个 D

或者你可以做犯罪的三角洲

K I I I I I I I I I I K

这可能需要多次总和才能到达所需的位置。但总体数据较小。SO 重组

k+i+i+i 到达第 4 点

最后,您可以将两者结合起来

K D I I I D I I I D I I I K

这就像带有 IPB 帧的 mpeg-2,但这样一来,您到任何位置都不会超过 4 个总和,并且您可以获得 Delta 和 Incrimental Compression 的一些好处。

4赞 Jerry Jongerius 4/15/2016 #10

假设地球是一个完美的球体(它不是,但足够近),半径“R”为 3959 英里(或 ×5280 英尺/英里 = 20903520 英尺),周长为 131340690 英尺(使用 2×PI×R)。

360 度经度覆盖 131340690 英尺。180 度纬度覆盖 65670345 英尺。

如果要将纬度/液化格存储精度降低到 3 英尺,则需要能够存储43780230 (131340690/3) 经度值和21890115 (65670345/3) 纬度值。43780230需要 25.38 位 (log(43780230)/log(2)) 来存储,21890115需要 24.38 位 (log(21890115)/log(2)) 来存储 – 或略低于 50 位(或 6.25 字节)。

因此,显而易见的问题是,如果您想将纬度和经度存储在 6 个字节中,那么精度是多少?好吧,6 个字节是 48 位。这意味着纬度为 23.5 位,经度为 24.5 位(经度的值是其两倍,即只有 1 位,24.5-23.5=1 位)。因此,23.5 位允许您表示从 0 到 11863282(11863283 个值)的数字。65670345 英尺除以 11863283 值等于 5.53 英尺(经度的精度值相同)。

底线:因此,如果你能同时承受 5.5 英尺的纬度和经度精度,你可以将这两个值打包到六个字节中。

*旁注:关于纬度和经度对于存储球体周围的位置信息是可怕的评论(因为在两极存储的信息较少)——好吧,这些评论经不起数学!让我们弄清楚。比方说,我们想要设计一个新的完美系统,它可以记录并在每平方英尺地球中心的地面上放置一根木桩。地球的表面积(R 为 3959 英里;球体表面积的公式)是 5490965469267303 SQ FT – 许多木桩需要 52.29 位来表示。现在,现有的经纬度系统使用矩形系统。矩形的宽度是地球的周长,矩形的高度是周长的 1/2)——这是 131340690 * 65670345(见上文),或 8625188424838050 SQ FT——这需要 52.94 位来表示(这个系统在两极周围的地面上放置了“太多”的木桩)。因此,令人震惊的答案是,无论是新的完美系统,还是旧的纬度/液化天然气系统,都需要 53 个实际位来存储地球上的单个位置,精度低至 1 英尺!

1赞 Hugo Walter 2/11/2020 #11

因为我需要它,所以这里是 Jerry Jongerius 答案的 python 代码,它表示 6 字节的 Lat/Lon 值,使用 23.5 位和 24.5 位在赤道附近精度约为 1.7m:

import struct

NBYTES=6
LATVALS=int(2**(NBYTES*4-0.5))
LONVALS=int(2**(NBYTES*4+0.5))

def serialize_gps(latlon):
    lat=(int(latlon[0]*LATVALS/180)+LATVALS//2)%LATVALS
    lon=(int(latlon[1]*LONVALS/360)+LONVALS//2)%LONVALS
    return struct.pack("!Q",lat*LONVALS+lon)[8-NBYTES:]

def deserialize_gps(b):
    if len(b)!=NBYTES:
        raise Exception("len(b)!=NBYTES")
    c=struct.unpack("!Q",(b"\x00"*6+b)[-8:])[0]
    lat=(c//LONVALS-LATVALS//2)*180/LATVALS
    lon=(c%LONVALS-LONVALS//2)*360/LONVALS
    return (lat,lon)

s=serialize_gps((47.55754133918577,10.74961245059967))
print(deserialize_gps(s))
#(47.557526866719776, 10.749611216389258)
1赞 jul-nc 9/15/2023 #12

将纬度和经度值乘以 10,000,000 (10^7) 是在该范围内实现更高精度的好方法。使用此比例因子,您可以用 7 位小数精度表示纬度和经度坐标。这是你如何做到的:int32_t

  1. 对于纬度:

    • 通过将纬度值乘以 10,000,000 (10^7) 来缩放纬度值。
    • 这将有效地将从 -90.0000000 到 +90.0000000 度的纬度范围转换为 -900,000,000 到 +900,000,000 作为范围内的整数。int32_t
  2. 对于经度:

    • 通过将经度值乘以 10,000,000 (10^7) 来缩放经度值。
    • 然后,移动缩放值以确保它们适合该范围。int32_t
    • 映射该范围内从 -180.0000000 到 +180.0000000 度到 -1,800,000,000 到 +1,800,000,000 的经度范围。int32_t

应该是正确的吧?

评论

0赞 Sam Mason 9/20/2023
转换时使用幻数(即)怎么样?会给你稍微更高的精度(但几乎没有区别),以及很好地包裹在支持范围之外的角度11930464.712**31/180
0赞 user3168890 11/16/2023 #13

我同意山姆·梅森(Sam Mason)的观点。只需使用所需的位数/字节即可获得所需的角度精度。纬度和经度是角度。取您的总位数并将它们解释为 2*pi(或 360 度)的有符号(或无符号)分数。它也会自动地绕地球一圈。
如果您使用 32 位(假设有符号),最低有效位代表 1,4629 纳弧度或 527 纳度,这将为您提供赤道 10 毫米范围内的最坏情况精度。180 度与 -180 度 (0x80000000) 相同,360 度与 0 度 (0x00000000) 相同,添加或细分时无需任何检查或转换。