type approprié / meilleur pour stocker la latitude et la longitude

Dans un langage de programmation au niveau système comme C, C ++ ou D, quel est le meilleur type / encodage pour stocker la latitude et la longitude?

Les options que je vois sont:

  • IEEE-754 FP en degrés ou en radians
  • degrés ou radians stockés en valeur de point fixe dans un int 32 ou 64 bits
  • mappage d’un nombre entier compris dans la plage de degrés: -> deg = (360/2^32)*val
  • degrés, minutes, secondes et fraction de seconde stockés sous forme de champs de bits dans un int
  • une structure quelconque.

La solution simple (FP) présente l’inconvénient majeur d’avoir une résolution hautement non uniforme (quelque part en Angleterre, elle peut mesurer en micromètres, au Japon, elle ne le peut pas). En outre, cela a toutes les questions de comparaison FP et autres joyeusetés. Les autres options nécessitent des efforts supplémentaires dans différentes parties du cycle de vie des données. (génération, présentation, calculs etc.)

Une option intéressante est un type de précision flottant qui, à mesure que l’augmentation de Latitude, reçoit plus de bits et que la longitude diminue (à mesure qu’ils se rapprochent vers les pôles).

Questions connexes qui ne couvrent pas tout à fait ceci:

  • Quel est le type de données idéal à utiliser lors du stockage de latitude / longitude dans une firebase database MySQL?
  • Travailler avec les valeurs de latitude / longitude en Java

BTW: 32 bits vous donne une résolution E / W à l’équateur d’environ 0,3 po. C’est proche de l’échelle à laquelle les configurations GPS de haute qualité peuvent fonctionner (dans certains modes, elles peuvent atteindre environ 0,5 po).

Si les 32 bits sont uniformément répartis sur la surface de la terre, vous pouvez indexer des carrés d’environ 344 m de côté, 5 octets, 21 m, 6 b-> 1,3 m et 8 b-> 5 mm.

Je n’ai pas d’utilisation spécifique en tête pour le moment, mais j’ai déjà travaillé avec ce genre de choses et je compte y revenir à un moment donné.

Le moyen le plus simple est de simplement le stocker en tant que float / double en degrés. Positif pour N et E, négatif pour S et W. N’oubliez pas que les minutes et les secondes sont sur 60 (donc 31 45’N est 31,75). Il est facile de comprendre quelles sont les valeurs en les regardant et, si nécessaire, la conversion en radians est sortingviale.

Les calculs sur les latitudes et les longitudes, tels que la distance du grand cercle entre deux coordonnées, dépendent fortement des fonctions sortinggonomésortingques, qui utilisent généralement des doubles. Tout autre format reposera au minimum sur une autre implémentation de sinus, cosinus, atan2 et racine carrée. Les nombres arbitraires de précision (par exemple BigDecimal en Java) ne fonctionneront pas pour cela. Quelque chose comme l’Int où 2 ^ 32 se répand uniformément aura des problèmes similaires.

Le point d’uniformité a été soulevé dans plusieurs commentaires. Je noterai simplement que la Terre, par rapport à la longitude, n’est pas uniforme. Une distance d’une seconde d’arc au cercle arctique est une distance plus courte qu’à l’équateur. Les flotteurs à double précision donnent une précision inférieure au millimètre sur la Terre. N’est-ce pas suffisant? Si non, pourquoi pas?

Il convient également de noter ce que vous voulez faire avec ces informations, car les types de calculs dont vous avez besoin auront un impact sur le format de stockage que vous utilisez.

Les longitudes et les latitudes ne sont généralement pas connues avec plus de précision qu’un flotteur de 32 bits. Donc, si vous êtes préoccupé par l’espace de stockage, vous pouvez utiliser des flottants. Mais en général, il est plus pratique de travailler avec des nombres en double.

Les radians sont plus pratiques pour les mathématiques théoriques. (Par exemple, la dérivée du sinus est un cosinus uniquement lorsque vous utilisez des radians.) Mais les diplômes sont généralement plus familiers et plus faciles à interpréter, de sorte que vous pourriez vouloir vous en tenir aux diplômes.

Une représentation décimale avec une précision de 8 devrait être plus que suffisante selon cet article de Wikipedia sur les degrés décimaux .

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

Les problèmes que vous avez mentionnés avec des valeurs à virgule flottante pourraient-ils devenir un problème? Si la réponse est non, je suggérerais d’utiliser simplement la valeur de radians en double précision – vous en aurez besoin si vous faites des calculs sortinggonomésortingques de toute façon.

Si vous rencontrez un problème de perte de précision lorsque vous utilisez des doubles ou que vous ne faites pas de sortinggonomésortinge, je vous suggère de mapper la solution à un nombre entier – cela vous donnera la meilleure résolution, peut être facilement convertie au format d’affichage votre locale utilisera et – après avoir choisi un méridien 0 approprié – peut être utilisé pour convertir en valeurs à virgule flottante de haute précision.

PS: Je me suis toujours demandé pourquoi il ne semble y avoir personne qui utilise des coordonnées sphériques géocensortingques – elles devraient être raisonnablement proches des coordonnées géographiques et ne nécessiteraient pas toutes ces maths sophistiquées sur les sphéroïdes pour faire des calculs; pour le plaisir, je voulais convertir les coordonnées de Gauss-Krüger-Koordinaten (utilisées par les Allemands Katasteramt) en coordonnées GPS – laissez-moi vous dire que c’était moche: on utilise l’ellipsoïde de Bessel, l’autre le WGS84 et le Gauss-Krüger la cartographie elle-même est assez folle à elle seule …

La résolution de 0,3 pouce est telle que les tremblements de terre de quelques années font la différence. Vous voudrez peut-être reconsidérer la raison pour laquelle vous croyez avoir besoin d’une résolution aussi fine dans le monde entier.

Certains des centres d’épandage de l’océan Pacifique changent de 15 cm / an .

Le meilleur encodage dépend vraiment de vos objectives / exigences.

Si vous effectuez des calculs arithmétiques, la latitude en virgule flottante, la longitude est souvent très pratique. D’autres fois, les coordonnées cartésiennes (c’est-à-dire x, y, z) peuvent être plus pratiques. Par exemple, si vous ne vous souciez que des points à la surface de la terre, vous pouvez utiliser un vecteur n .

En ce qui concerne le stockage à plus long terme, IEEE Floating Point gaspillera des bits pour les plages dont vous vous moquez (pour lat / lon) ou pour la précision dont vous ne vous souciez pas dans les coordonnées cartésiennes (sauf si vous voulez une très bonne précision à l’origine pour quelque raison que ce soit). Vous pouvez bien sûr mapper l’un ou l’autre type de coordonnées à la taille de votre choix, de sorte que toute la gamme de ces coordonnées correspond à la plage qui vous intéresse à la résolution qui vous intéresse.

Il y a bien sûr d’autres choses à considérer que de ne pas simplement gaspiller des bits dans l’encodage. Par exemple, (Geohashes) [https://en.wikipedia.org/wiki/Geohash] a la bonne propriété de trouver facilement d’autres géo-séches dans la même zone. (La plupart auront le même préfixe et vous pourrez calculer le préfixe que les autres auront.) Malheureusement, ils maintiennent la même précision en degrés de longitude près de l’équateur que près des pôles. J’utilise actuellement des geohashes 64 bits pour le stockage, ce qui donne une résolution d’environ 3 m à l’équateur.

Le système de localisation de Maidenhead présente des caractéristiques similaires, mais semble plus optimisé pour la communication d’emplacements entre humains plutôt que sur un ordinateur. (Le stockage de chaînes MLS gaspillerait beaucoup de bits pour une détection d’erreur plutôt sortingviale.)

Le système de référence du réseau militaire est le système que j’ai trouvé qui gère différemment les pôles, même s’il semble lui aussi davantage axé sur les communications humaines. (Et cela semble être une douleur à convertir de ou en lat / lon.)

Selon ce que vous voulez exactement, vous pouvez utiliser quelque chose de similaire au système de coordonnées séréographique polaire universel près des pôles, ainsi que quelque chose de plus informatif que l’ UTM pour le rest du monde, et utiliser au plus un bit pour indiquer lequel des deux systèmes vous utilisez Je dis tout au plus un peu, car il est peu probable que la plupart des points qui vous intéressent soient à proximité des pôles. Par exemple, vous pouvez utiliser “demi-bit” en indiquant que 11 indique l’utilisation du système polaire, tandis que 00, 01 et 10 indiquent l’utilisation de l’autre système et font partie de la représentation.

Désolé, c’est un peu long, mais je voulais sauver ce que j’avais appris récemment. Malheureusement, je n’ai trouvé aucun moyen standard, sain et efficace de représenter un point sur la terre avec une précision uniforme.

Edit: J’ai trouvé une autre approche qui ressemble beaucoup à ce que vous vouliez, car elle tire plus directement parti de la plus faible précision nécessaire pour que la longitude soit plus proche des pôles. Il s’avère qu’il y a beaucoup de recherches sur le stockage de vecteurs normaux. L’encodage de vecteurs normaux à l’aide de coordonnées sphériques optimisées décrit un tel système de codage de vecteurs normaux tout en maintenant un niveau de précision minimal, mais il pourrait tout aussi bien être utilisé pour des coordonnées géographiques.

http://www.esri.com/news/arcuser/0400/wdside.html
À l’équateur, une seconde d’arc de longitude équivaut à une seconde d’arc de latitude, soit 1/60 de mille marin (ou 101,27 pieds ou 30,87 mètres).

Flotteur 32 bits contient 23 bits de données explicites.
180 * 3600 nécessite log2 (648000) = 19.305634287546711769425914064259 bits de données. Notez que le bit de signe est stocké séparément et que, par conséquent, nous ne devons calculer que pour 180 degrés.
Après avoir soustrait de 23 les bits pour log2 (648000), nous avons encore 3.694365712453288230574085935741 bits supplémentaires pour des données de moins d’une seconde.
C’est-à-dire 2 ^ 3.694365712453288230574085935741 = 12.945382716049382716049382716053 parties par seconde.
Par conséquent, un type de données float peut avoir une précision de 30,87 / 12,945382716049382716049382716053 ~ = 2,38 mètres à l’équateur.

Si en “stockant” tu veux dire “en mémoire”, la vraie question est: que vas-tu en faire?

Je soupçonne qu’avant que ces coordonnées ne fassent quelque chose d’intéressant, elles auront été canalisées en radians à travers les fonctions de math.h. Sauf si vous envisagez d’implémenter un certain nombre de fonctions transcendantales fonctionnant en Deg / Min / Secs, regroupées dans un champ de bits.

Alors, pourquoi ne pas garder les choses simples et les stocker dans des degrés IEEE-754 ou des radians à la précision de vos exigences?

Un programme Java permettant de calculer l’erreur d’arrondi max en mètres entre les valeurs lat / long dans Float / Double:

 import java.util.*; import java.lang.*; import com.javadocmd.simplelatlng.*; import com.javadocmd.simplelatlng.util.*; public class MaxError { public static void main(Ssortingng[] 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); } } 

Sortie:

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

Vous pouvez utiliser decimal type de données decimal :

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

Le code suivant emballe les coordonnées sans perte WGS84 dans un long non signé (c.-à-d. En 8 octets):

 using System; using System.Collections.Generic; using System.Text; namespace Utils { ///  /// Lossless conversion of OSM coordinates to a simple long. ///  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 UnPackCoord(long coord) { var tmp = new CoordinateStore(coord); return new KeyValuePair(tmp.Lat,tmp.Lon); } } } 

Source: http://www.dupuis.me/node/35