Подтвердить что ты не робот

Правильный/лучший тип для хранения широты и долготы

На языке программирования на уровне системы, таком как C, С++ или D, каков наилучший тип/кодировка для хранения широты и долготы?

Параметры, которые я вижу, следующие:

  • IEEE-754 FP как градусы или радиан.
  • градусов или радиан, сохраненных как фиксированное значение точки в 32 или 64 бит int
  • отображение целочисленного диапазона в диапазон степени: → deg = (360/2^32)*val
  • градусы, минуты, секунды и дробные секунды, хранящиеся как битовые поля в int
  • некоторая структура.

Простое решение (FP) имеет основную нижнюю сторону, что оно имеет очень неравномерное разрешение (где-то в Англии он может измеряться в микронах, в Японии он не может). Также у этого есть все проблемы сравнения FP и еще чего-то. Другие варианты требуют дополнительных усилий в разных частях жизненного цикла данных. (генерация, презентация, расчеты и т.д.)

Один интересный вариант - это тип плавающей точности, в котором при увеличении широты он получает больше бит, а долгота становится меньше (по мере приближения к полюсам).

Связанные вопросы, которые не совсем охватывают это:


BTW: 32 бита дают вам разрешение E/W на экваторе около 0,3 дюйма. Это близко к масштабу, на котором могут работать высокоуровневые GPS-установки (IIRC они могут опускаться до примерно 0,5 дюйма в некоторых режимах).

OTOH, если 32 бита равномерно распределены по земной поверхности, вы можете индексировать квадраты около 344 м на стороне, 5 байтов дают 21 м, 6 Б- > 1,3 м и 8 Б- > 5 мм.

У меня сейчас нет конкретного использования, но я работал с этим раньше и ожидаю снова, в какой-то момент.

4b9b3361

Ответ 1

Самый простой способ - просто сохранить его как float/double в градусах. Положительный для N и E, отрицательный для S и W. Просто помните, что минуты и секунды из 60 (поэтому 31 45'N - 31,75). Его легко понять, каковы ценности, глядя на них и, при необходимости, преобразование в радианы тривиально.

Вычисления на широтах и ​​долготах, такие как расстояние Великий круг между двумя координатами, в значительной степени зависят от тригонометрических функций, которые обычно используют удвоения. Любой другой формат будет опираться на другую реализацию синуса, косинуса, atan2 и квадратного корня, как минимум. Произвольные числа точности (например, BigDecimal в Java) не будут работать для этого. То же, что и у int, где 2 ^ 32 распространяется равномерно, будет иметь похожие проблемы.

Точность единообразия возникла в нескольких комментариях. На этом я просто хочу отметить, что Земля относительно долготы не является однородной. Одна дуга-вторая долгота в Северном полярном круге - это более короткое расстояние, чем у экватора. Двумерные прецизионные поплавки дают точность в миллиметрах в любом месте Земли. Этого недостаточно? Если нет, почему бы и нет?

Также стоит отметить, что вы хотите сделать с этой информацией, так как типы требуемых вычислений будут влиять на используемый вами формат хранения.

Ответ 2

Долготы и широты, как правило, не известны с большей точностью, чем 32-битные поплавки. Поэтому, если вас беспокоит пространство для хранения, вы можете использовать float. Но в целом удобнее работать с числами как удваивается.

Радианы более удобны для теоретической математики. (Например, производная синуса является косинусом только тогда, когда вы используете радианы.) Но степени обычно более знакомы и легче для людей интерпретировать, поэтому вы можете захотеть придерживаться градусов.

Ответ 3

Десятичное представление с точностью 8 должно быть более чем достаточно в соответствии с этой статьей wikipedia на Десятичные градусы.

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

Ответ 4

Могут ли проблемы, о которых вы говорили, с плавающей точкой, становятся проблемой? Если ответ отрицательный, я бы предложил просто использовать значение радианов в двойной точности - вам это понадобится, если вы все равно будете выполнять тригонометрические вычисления.

Если может возникнуть проблема с потерей точности при использовании удвоений или вы не будете делать тригонометрию, я бы предложил ваше решение для сопоставления целочисленному диапазону - это даст вам наилучшее разрешение, которое можно легко преобразовать в любой формат отображения, который вы используете для локали, будет использоваться и - после выбора подходящего 0-меридиана - может использоваться для преобразования в значения с плавающей точкой высокой точности.

PS: Я всегда задавался вопросом, почему, похоже, никто не использует геоцентрические сферические координаты - они должны быть достаточно близки к географическим координатам и не потребуют всей этой фантастической математики сфероиды для выполнения вычислений; для удовольствия я хотел преобразовать Gauss-Krüger-Koordinaten (которые используются немецким Катастерамтом) в координаты GPS - позвольте мне сказать вам, что это было уродливо: один использует эллипсоид Бесселя, другой WGS84 и Gauss-Krüger само портретирование довольно сумасшедшее...

Ответ 5

0,3-дюймовое разрешение приближается к точке, где землетрясения за несколько лет имеют значение. Возможно, вам захочется пересмотреть, почему вы считаете, что вам нужно такое прекрасное разрешение во всем мире.

Некоторые из центров распространения в Тихом океане меняются на 15 см/год.

Ответ 6

Какая кодировка "наилучшая" действительно зависит от ваших целей/требований.

Если вы выполняете арифметику, широту с плавающей запятой, долгота часто бывает весьма удобной. В других случаях декартовы координаты (т.е. Х, у, г) могут быть более удобными. Например, если вы только заботитесь о точках на поверхности земли, вы можете использовать n-vector.

Что касается долгосрочного хранения, то плавающая точка IEEE будет отбрасывать биты для диапазонов, которые вам не нужны (для lat/lon), или для точности, которую вы можете не беспокоиться в случае декартовых координат (если вы не хотите очень хорошей точности по происхождению по какой-либо причине). Конечно, вы можете сопоставить любой тип координат с целями вашего предпочтительного размера, так что весь диапазон указанных ints охватывает диапазон, который вас интересует, по вашему желанию.

Есть, конечно, другие вещи, о которых стоит подумать, чем просто не растрачивать биты в кодировке. Например, (Geohash) [https://en.wikipedia.org/wiki/Geohash] имеют приятное свойство, что легко найти другие геохэши в той же области. (У большинства будет тот же префикс, и вы можете вычислить префикс, который остальные будут иметь.) К сожалению, они сохраняют ту же точность в градусах долготы около экватора, что и вблизи полюсов. В настоящее время я использую 64-битные geohash для хранения, что дает разрешение около 3 м на экваторе.

Maidenhead Locator System имеет некоторые аналогичные характеристики, но, по-видимому, более оптимизирована для связи мест между людьми, а не для хранения на компьютере. (Сохранение строк MLS будет тратить много бит для некоторого довольно тривиального обнаружения ошибок.)

Одна система, которую я нашел, которая по-разному обрабатывает полюсы, - это Военная справочная система Grid, хотя она тоже кажется более ориентированной на человеческую связь. (И кажется, что боль превращается из или в lat/lon.)

В зависимости от того, что вы хотите точно, вы можете использовать нечто похожее на Универсальную полярную сериграфическую систему координат рядом с полюсами наряду с чем-то более вычислительным разумным чем UTM для остального мира и используйте не более одного бита, чтобы указать, какую из двух систем вы используете. Я говорю не более одного раза, потому что маловероятно, что большинство вопросов, о которых вы заботитесь, будут рядом с полюсами. Например, вы можете использовать "половину бит", указав, что 11 означает использование полярной системы, а 00, 01 и 10 указывают на использование другой системы и являются частью представления.

Извините, это немного долго, но я хотел сохранить то, что я узнал недавно. К сожалению, я не нашел стандартного, нормального и эффективного способа представления точки на земле с равномерной точностью.

Edit: я нашел другой подход, который выглядит намного больше, чем то, что вы хотели, так как он более непосредственно использует более низкую точность, необходимую для долготы ближе к полюсам. Оказывается, существует много исследований по сохранению нормальных векторов. Кодирование нормальных векторов с использованием оптимизированных сферических координат описывает такую ​​систему кодирования нормальных векторов при сохранении минимального уровня точности, но ее можно было бы как можно лучше использовать для географических координат.

Ответ 7

http://www.esri.com/news/arcuser/0400/wdside.html
На экваторе дуга-секунда долготы приблизительно равна дуге-секунде широты, которая составляет 1/60 морской мили (или 101,27 фута или 30,87 метра).
32-битный float содержит 23 явных бита данных.
180 * 3600 требует log2 (648000) = 19.305634287546711769425914064259 бит данных. Обратите внимание, что бит знака хранится отдельно, поэтому мы должны составлять только 180 градусов.
После вычитания из 23 бит для log2 (648000) у нас осталось дополнительно 3,694365712453288230574085935741 бит для подсевых данных.
То есть 2 ^ 3.694365712453288230574085935741 = 12.945382716049382716049382716053 детали в секунду. Таким образом, тип данных с плавающей точкой может иметь точность 30,87/12,945382716049382716049382716053 ~ = 2,38 м на экваторе.

Ответ 8

Если "запоминание" означает "удерживание в памяти", реальный вопрос: что вы собираетесь с ними делать?

Я подозреваю, что до того, как эти координаты сделают что-нибудь интересное, они будут направляться как радианы через функции в математике. Если вы не планируете реализовать несколько трансцендентных функций, которые работают с Deg/Min/Secs, упакованными в поле бит.

Итак, почему бы не упростить и просто сохранить их в IEEE-754 градусах или радианах с точностью ваших требований?

Ответ 9

Программа Java для вычисления максимальной погрешности округления в метрах от литья lat/long значений в 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

Ответ 10

Вы можете использовать тип данных decimal:

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

Ответ 11

Следующий код объединяет координаты без потерь координат 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