用于存储经度和纬度的适当/最佳types

在像C,C ++或D这样的系统级编程语言中,什么是存储经度和纬度的最佳types/编码?

我看到的选项是:

  • IEEE-754 FP为度或弧度
  • 度或弧度存储为一个32位或64位int的定点值
  • 整数范围映射到程度范围: – > deg = (360/2^32)*val
  • 度,分,秒和分数秒作为位字段存储在一个int中
  • 某种结构。

简单的解决scheme(FP)具有非常不均匀的分辨率(在英国的某个地方可以以微米计量,而在日本则不能),这是主要的缺点。 这也有所有的FP比较和什么的问题。 其他选项在数据生命周期的不同部分需要额外的努力。 (生成,演示,计算等)

一个有趣的select是浮动精度types,随着纬度的增加,它得到更多的位,经度变得更小(当它们靠近极点时)。

相关的问题不完全包括这个:

  • 在MySQL中存储纬度/经度时使用的理想数据types是什么?
  • 在Java中使用纬度/经度值

顺便说一下:32位在赤道约0.3英寸处给出E / W分辨率,这与高等级GPS设置可以工作的规模(在某些模式下,它们可以降至约0.5英寸)相近。

OTOH如果32位均匀地分布在地球表面,则可以在一侧上标注344m的正方形,5字节为21m,6B-> 1.3m和8B-> 5mm。

我现在没有具体的用途,但是之前曾经和这种东西合作过,期待在某个时候再次。

最简单的方法就是把它作为浮点/双精度来存储。 N和E都是正数,S和W都是负数。只记得分数和秒数都在60以外(所以31 45'N是31.75)。 通过观察它们可以很容易地理解这些值,并且在必要的情况下,转换为弧度是微不足道的。

纬度和经度的计算,例如两个坐标之间的大圆距,很大程度上依赖于三angular函数,通常使用双精度函数。 任何其他格式都将依赖于另一个正弦,余弦,平方根和平方根的实现。 任意的精度数字(例如Java中的BigDecimal)将不适用于此。 类似于2 ^ 32均匀分布的int类似的问题也会有类似的问题。

统一点已经出现在几个意见。 在这一点上,我要简单地指出,地球在经度方面是不统一的。 北极圈上一秒的经度是比赤道更短的距离。 双精度浮标可以在地球上的任何地方提供亚毫米的精度。 这不够吗? 如果没有,为什么不呢?

由于您所需的计算types会影响您使用的存储格式,所以还需要注意您要如何处理这些信息。

一般来说,经度和纬度比32位浮点数的精度要高。 所以如果你关心存储空间,你可以使用浮点数。 但总的来说,使用数字作为双打更方便。

Radians在理论math上更为方便。 (例如,只有当你使用弧度时,正弦的导数才是余弦。)但是度数通常比较熟悉,容易让人解释,所以你可能要坚持度数。

精确度为8的十进制表示应该超过十进制度的维基百科文章。

 0 decimal places, 1.0 = 111 km ... 7 decimal places, 0.0000001 = 1.11 cm 8 decimal places, 0.00000001 = 1.11 mm 

您提到的浮点值问题可能会成为问题吗? 如果答案是否定的,我build议只使用双精度的弧度值 – 如果你要做三angular函数计算,你将需要它。

如果在使用双精度时可能会出现精度损失的问题,或者您不会做三angular函数,我build议您将解决scheme映射到整数范围 – 这将为您提供最佳的分辨率,可以轻松地将其转换为任何显示格式你的语言环境将会使用和 – 在select一个合适的0子午线后 – 可以用来转换为高精度的浮点值。

PS:我总是想知道为什么似乎没有人使用地心坐标 – 他们应该相当接近地理坐标,并且不需要所有这些在椭球体上的花式math计算; 为了好玩,我想把Gauss-Krüger-Koordinaten(被德国的Katasteramt使用)转换成GPS坐标 – 让我告诉你,这很丑陋:一个使用贝塞尔椭球,另一个使用WGS84,另一个使用Gauss-Krüger映射本身是非常疯狂的…

什么编码是“最好的”真的取决于你的目标/要求。

如果你正在执行算术,浮点纬度,经度往往相当方便。 其他时候,笛卡尔坐标(即x,y,z)可以更方便。 例如,如果你只关心地球表面上的点,你可以使用n向量 。

至于长期存储,IEEE浮点会浪费你不关心的范围(经纬度),或者在笛卡儿坐标的情况下你可能不关心的精度(除非你想在原点处有非常好的精度无论什么原因)。 您当然可以绘制任何types的坐标,以便input您的首选尺寸,从而使整个范围的整数覆盖您关心的分辨率所关注的范围。

当然还有其他的事情要考虑,而不是在编码中不浪费点数。 例如,(Geohashes)[https://en.wikipedia.org/wiki/Geohash]具有很好的属性,可以轻松地在同一区域find其他geohashes。 (大多数将有相同的前缀,并且可以计算其他人将会得到的前缀)。不幸的是,他们在赤道附近和极点附近维持相同的经度。 目前,我正在使用64位geohashes进行存储,在赤道上分辨率约为3米。

梅登黑德定位系统有一些相似的特征,但似乎更适合于人类之间的通信位置,而不是存储在计算机上。 (存储MLSstring会浪费很多比特来进行一些相当微不足道的错误检测。)

我发现的一个系统不同的处理极点是军事网格参考系统 ,虽然它也似乎更多的人性化通讯导向。 (看起来像是一个痛苦转换或经纬度。)

取决于你想要的东西,你可以在极点附近使用类似于通用极坐标系统的东西,以及在世界其他地方比UTM更具计算能力的东西,并且最多使用一位来指示两个系统中的哪一个你正在使用。 我至多说了一点,因为你所关心的大部分问题不大可能会在两极附近。 例如,可以使用“半个位”来表示11表示使用极坐标系统,而00,01和10表示使用另一个系统,并且是表示的一部分。

对不起,这有点长,但我想保存我最近学到的东西。 可悲的是,我还没有find任何标准,理智和有效的方式来expression一个统一的精度地球上的一个点。

编辑:我发现另一种看起来更像你想要的方法,因为它更直接地利用了更接近两极的经度所需的较低的精度。 原来有很多关于存储法线向量的研究。 使用优化的球形坐标对正常vector进行编码描述了用于编码法线vector的系统,同时保持最小精度水平,但是其也可以用于地理坐标。

0.3英寸的分辨率正在下降,几年来的地震有所不同。 您可能想重新考虑为什么您认为您需要在全球范围内提供这样的高分辨率。

太平洋的一些传播中心每年变化多达15厘米 。

http://www.esri.com/news/arcuser/0400/wdside.html
在赤道上,经度的第二个弧度约等于海里的1/60(或101.27英尺或30.87米)的纬度的第二个弧度。

32位浮点数包含23个明确的数据位。
180 * 3600需要log2(648000)= 19.305634287546711769425914064259位数据。 请注意,符号位是分开存储的,因此我们只需要计算180度。
在从23减去log2(648000)的位之后,对于亚秒数据,我们还剩下额外的3.694365712453288230574085935741位。
这是2 ^ 3.694365712453288230574085935741 = 12.945382716049382716049382716053部分每秒。
因此,一个浮点数据types在赤道上的精度可以达到30.87 / 12.945382716049382716049382716053〜= 2.38米。

如果“存储”的意思是“记住”,那么真正的问题是:你打算怎么办?

我怀疑,在这些坐标做任何有趣的事情之前,他们将被作为弧度通过math.h中的函数。 除非你计划实现相当多的以Deg / Min / Secs封装在一个位域的超越函数。

那么,为什么不把事情简单化并按照您的要求精确地存储在IEEE-754度或弧度范围内呢?

一个Java程序,用于计算最大舍入误差(以米为单位),从铸造纬度/长度值转换为Float / Double:

 import java.util.*; import java.lang.*; import com.javadocmd.simplelatlng.*; import com.javadocmd.simplelatlng.util.*; public class MaxError { public static void main(String[] args) { Float flng = 180f; Float flat = 0f; LatLng fpos = new LatLng(flat, flng); double flatprime = Float.intBitsToFloat(Float.floatToIntBits(flat) ^ 1); double flngprime = Float.intBitsToFloat(Float.floatToIntBits(flng) ^ 1); LatLng fposprime = new LatLng(flatprime, flngprime); double fdistanceM = LatLngTool.distance(fpos, fposprime, LengthUnit.METER); System.out.println("Float max error (meters): " + fdistanceM); Double dlng = 180d; Double dlat = 0d; LatLng dpos = new LatLng(dlat, dlng); double dlatprime = Double.longBitsToDouble(Double.doubleToLongBits(dlat) ^ 1); double dlngprime = Double.longBitsToDouble(Double.doubleToLongBits(dlng) ^ 1); LatLng dposprime = new LatLng(dlatprime, dlngprime); double ddistanceM = LatLngTool.distance(dpos, dposprime, LengthUnit.METER); System.out.println("Double max error (meters): " + ddistanceM); } } 

输出:

 Float max error (meters): 1.7791213425235692 Double max error (meters): 0.11119508289500799 

你可以使用decimal数据types:

 CREATE TABLE IF NOT EXISTS `map` ( `latitude` decimal(18,15) DEFAULT NULL, `longitude` decimal(18,15) DEFAULT NULL ); 

以下代码将WGS84坐标无损坐标打包为无符号长整型(即,分成8个字节):

 using System; using System.Collections.Generic; using System.Text; namespace Utils { /// <summary> /// Lossless conversion of OSM coordinates to a simple long. /// </summary> unsafe class CoordinateStore { private readonly double _lat, _lon; private readonly long _encoded; public CoordinateStore(double lon,double lat) { // Ensure valid lat/lon if (lon < -180.0) lon = 180.0+(lon+180.0); else if (lon > 180.0) lon = -180.0 + (lon-180.0); if (lat < -90.0) lat = 90.0 + (lat + 90.0); else if (lat > 90.0) lat = -90.0 + (lat - 90.0); _lon = lon; _lat = lat; // Move to 0..(180/90) var dlon = (decimal)lon + 180m; var dlat = (decimal)lat + 90m; // Calculate grid var grid = (((int)dlat) * 360) + ((int)dlon); // Get local offset var ilon = (uint)((dlon - (int)(dlon))*10000000m); var ilat = (uint)((dlat - (int)(dlat))*10000000m); var encoded = new byte[8]; fixed (byte* pEncoded = &encoded[0]) { ((ushort*)pEncoded)[0] = (ushort) grid; ((ushort*)pEncoded)[1] = (ushort)(ilon&0xFFFF); ((ushort*)pEncoded)[2] = (ushort)(ilat&0xFFFF); pEncoded[6] = (byte)((ilon >> 16)&0xFF); pEncoded[7] = (byte)((ilat >> 16)&0xFF); _encoded = ((long*) pEncoded)[0]; } } public CoordinateStore(long source) { // Extract grid and local offset int grid; decimal ilon, ilat; var encoded = new byte[8]; fixed(byte *pEncoded = &encoded[0]) { ((long*) pEncoded)[0] = source; grid = ((ushort*) pEncoded)[0]; ilon = ((ushort*)pEncoded)[1] + (((uint)pEncoded[6]) << 16); ilat = ((ushort*)pEncoded)[2] + (((uint)pEncoded[7]) << 16); } // Recalculate 0..(180/90) coordinates var dlon = (uint)(grid % 360) + (ilon / 10000000m); var dlat = (uint)(grid / 360) + (ilat / 10000000m); // Returns to WGS84 _lon = (double)(dlon - 180m); _lat = (double)(dlat - 90m); } public double Lon { get { return _lon; } } public double Lat { get { return _lat; } } public long Encoded { get { return _encoded; } } public static long PackCoord(double lon,double lat) { return (new CoordinateStore(lon, lat)).Encoded; } public static KeyValuePair<double, double> UnPackCoord(long coord) { var tmp = new CoordinateStore(coord); return new KeyValuePair<double, double>(tmp.Lat,tmp.Lon); } } } 

资料来源: http : //www.dupuis.me/node/35