it-swarm.com.de

richtiger / bester Typ zum Speichern von Breiten- und Längengraden

Was ist in einer Programmiersprache auf Systemebene wie C, C++ oder D der beste Typ/die beste Codierung zum Speichern von Breiten- und Längengraden?

Die Optionen, die ich sehe, sind:

  • IEEE-754 FP als Grad oder Bogenmaß
  • grad oder Bogenmaß als fester Punktwert in einem 32- oder 64-Bit-Int
  • zuordnung eines ganzzahligen Bereichs zum Gradbereich: -> deg = (360/2^32)*val
  • grad, Minuten, Sekunden und Sekundenbruchteile, die als Bitfelder in einem int gespeichert sind
  • eine Art Struktur.

Die einfache Lösung (FP) hat den großen Nachteil, dass sie eine sehr ungleichmäßige Auflösung aufweist (irgendwo in England kann sie in Mikrometern gemessen werden, in Japan dagegen nicht). Auch dies hat alle Probleme des FP Vergleich und so weiter. Die anderen Optionen erfordern zusätzlichen Aufwand in verschiedenen Teilen des Lebenszyklus der Daten. (Generierung, Darstellung, Berechnungen usw.)

Eine interessante Option ist ein Typ mit schwebender Genauigkeit, bei dem mit zunehmender Breite mehr Bits und mit abnehmender Länge weniger Bits auftreten (je näher sie sich den Polen nähern).

Verwandte Fragen, die dies nicht ganz abdecken:


Übrigens: Mit 32 Bit erhalten Sie eine E/W-Auflösung am Äquator von ca. 0,3 Zoll. Dies entspricht in etwa der Skala, mit der hochwertige GPS-Setups arbeiten können (IIRC-Werte können in einigen Modi auf ca. 0,5 Zoll gesenkt werden).

OTOH Wenn die 32 Bits gleichmäßig über die Erdoberfläche verteilt sind, können Sie Quadrate von ca. 344 m auf einer Seite indizieren. 5 Bytes ergeben 21 m, 6 B -> 1,3 m und 8 B -> 5 mm.

Ich habe im Moment keine spezielle Verwendung im Sinn, habe aber schon früher mit so etwas gearbeitet und erwarte es irgendwann wieder.

63
BCS

Am einfachsten ist es, sie als Float/Double in Grad zu speichern. Positiv für N und E, negativ für S und W. Denken Sie daran, dass Minuten und Sekunden von 60 abweichen (also 31 45'N ist 31,75). Es ist leicht zu verstehen, was die Werte sind, wenn man sie ansieht, und wenn nötig, ist die Umrechnung in Bogenmaß trivial.

Berechnungen von Breiten- und Längengraden wie dem Großkreis Abstand zwischen zwei Koordinaten stützen sich in hohem Maße auf trigonometrische Funktionen, bei denen normalerweise Doppelfunktionen verwendet werden. Für jedes andere Format ist mindestens eine andere Implementierung von Sinus, Cosinus, Atan2 und Quadratwurzel erforderlich. Beliebig genaue Zahlen (zB BigDecimal in Java) funktionieren dafür nicht. So etwas wie das int, in dem 2 ^ 32 gleichmäßig verteilt ist, wird ähnliche Probleme haben.

Der Punkt der Einheitlichkeit wurde in mehreren Kommentaren angesprochen. Hierzu möchte ich lediglich bemerken, dass die Erde in Bezug auf die Länge nicht einheitlich ist. Eine Bogensekunde Länge am Polarkreis ist kürzer als am Äquator. Schwimmer mit doppelter Präzision bieten überall auf der Erde Präzision im Submillimeterbereich. Reicht das nicht aus? Wenn nein, warum nicht?

Es ist auch erwähnenswert, was Sie mit diesen Informationen tun möchten, da sich die Art der erforderlichen Berechnungen auf das von Ihnen verwendete Speicherformat auswirkt.

37
cletus

Längen- und Breitengrade sind im Allgemeinen nicht genauer bekannt als ein 32-Bit-Float. Wenn Sie sich also Gedanken über Speicherplatz machen, können Sie Floats verwenden. Im Allgemeinen ist es jedoch praktischer, mit doppelten Zahlen zu arbeiten.

Radianten sind für theoretische Mathematik geeigneter. (Die Ableitung von Sinus ist beispielsweise nur Cosinus, wenn Sie Bogenmaß verwenden.) Grad ist jedoch in der Regel vertrauter und leichter zu interpretieren. Daher möchten Sie möglicherweise bei Grad bleiben.

17
John D. Cook

Eine Dezimaldarstellung mit einer Genauigkeit von 8 sollte nach diesem Wikipedia-Artikel über Dezimalgrade mehr als ausreichend sein.

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

Könnten die Probleme, die Sie mit Gleitkommawerten angesprochen haben, zu einem Problem werden? Wenn die Antwort nein ist, würde ich vorschlagen, nur den Bogenmaßwert in doppelter Genauigkeit zu verwenden - Sie benötigen ihn, wenn Sie ohnehin trigonometrische Berechnungen durchführen.

Wenn bei der Verwendung von Doppelwerten ein Problem mit dem Genauigkeitsverlust auftreten könnte oder Sie keine Trigonometrie durchführen, würde ich Ihnen eine Lösung für die Zuordnung zu einem ganzzahligen Bereich vorschlagen. Auf diese Weise erhalten Sie die beste Auflösung und können problemlos in ein beliebiges Anzeigeformat konvertiert werden Ihr Gebietsschema verwendet und kann - nach Auswahl eines geeigneten 0-Meridians - verwendet werden, um Fließkommawerte mit hoher Genauigkeit zu konvertieren.

PS: Ich habe mich immer gefragt, warum es anscheinend niemanden gibt, der geozentrische Kugelkoordinaten verwendet - sie sollten einigermaßen nahe an den geografischen Koordinaten liegen und es wird nicht erforderlich sein, all diese ausgefallenen Berechnungen für Sphäroide durchzuführen Berechnungen; zum Spaß wollte ich Gauß-Krüger-Koordinaten (die vom deutschen Katasteramt verwendet werden) in GPS-Koordinaten umwandeln - lassen Sie mich sagen, das war hässlich: einer verwendet das Bessel-Ellipsoid, der andere WGS84 und der Gauß-Krüger Mapping selbst ist schon ziemlich verrückt ...

4
Christoph

Welche Kodierung "am besten" ist, hängt wirklich von Ihren Zielen/Anforderungen ab.

Wenn Sie einen arithmetischen Gleitkomma-Breitengrad ausführen, ist der Längengrad oft recht praktisch. Andere kartesische Koordinaten (dh x, y, z) können praktischer sein. Wenn Sie sich zum Beispiel nur um Punkte auf der Erdoberfläche kümmern, können Sie einen n-Vektor verwenden.

Was die Langzeitspeicherung betrifft, verschwendet IEEE-Gleitkomma Bits für Bereiche, die Sie nicht interessieren (für Lat/Lon) oder für die Genauigkeit, die Sie im Fall von kartesischen Koordinaten möglicherweise nicht interessieren (es sei denn, Sie möchten eine sehr gute Genauigkeit für den Ursprung warum auch immer). Sie können natürlich jede Art von Koordinaten auf Ints Ihrer bevorzugten Größe abbilden, sodass der gesamte Bereich dieser Ints den Bereich abdeckt, an dem Sie interessiert sind, und zwar mit der Auflösung, die Sie interessiert.

Es gibt natürlich noch andere Dinge zu beachten, als nur keine Bits in der Codierung zu verschwenden. Zum Beispiel haben (Geohashes) [https://en.wikipedia.org/wiki/Geohash] die Eigenschaft Nice, dass es einfach ist, andere Geohashes in derselben Region zu finden. (Die meisten haben das gleiche Präfix, und Sie können das Präfix berechnen, das die anderen haben.) Leider behalten sie in der Nähe des Äquators die gleiche Genauigkeit in Längengraden bei wie in der Nähe der Pole. Ich verwende derzeit 64-Bit-Geohashes für die Speicherung, was eine Auflösung von ca. 3 m am Äquator ergibt.

Das Maidenhead Locator System weist einige ähnliche Eigenschaften auf, scheint jedoch eher für die Kommunikation von Orten zwischen Menschen optimiert zu sein, als für die Speicherung auf einem Computer. (Das Speichern von MLS-Zeichenfolgen würde eine Menge Bits für eine eher triviale Fehlererkennung verschwenden.)

Das einzige System, das ich gefunden habe, das die Pole anders behandelt, ist das Military Grid Reference System , obwohl es auch mehr auf die menschliche Kommunikation ausgerichtet zu sein scheint. (Und es scheint ein Schmerz zu sein, von oder nach lat/lon zu konvertieren.)

Abhängig davon, was Sie genau wollen, können Sie etwas verwenden, das dem niverselles Polarkoordinatensystem in der Nähe der Pole ähnelt, zusammen mit etwas, das rechnerisch vernünftiger ist als UTM für den Rest der Welt, und geben Sie mit höchstens einem Bit an, welches der beiden Systeme Sie verwenden. Ich sage höchstens ein bisschen, weil es unwahrscheinlich ist, dass sich die meisten Punkte, die Sie interessieren, in der Nähe der Pole befinden. Zum Beispiel könnten Sie "ein halbes Bit" verwenden, indem Sie sagen, dass 11 die Verwendung des Polarsystems angibt, während 00, 01 und 10 die Verwendung des anderen Systems angeben und Teil der Darstellung sind.

Tut mir leid, das ist ein bisschen lang, aber ich wollte speichern, was ich kürzlich gelernt hatte. Leider habe ich keine einheitliche, vernünftige und effiziente Möglichkeit gefunden, einen Punkt auf der Erde mit einheitlicher Präzision darzustellen.

Bearbeiten: Ich habe einen anderen Ansatz gefunden, der viel ähnlicher aussieht, als Sie es wollten, da er direkter die geringere Präzision nutzt, die für Längen näher an den Polen benötigt wird. Es stellt sich heraus, dass die Speicherung normaler Vektoren viel Forschung erfordert. Kodieren normaler Vektoren mit optimierten sphärischen Koordinaten beschreibt ein solches System zum Kodieren normaler Vektoren unter Beibehaltung eines Mindestmaßes an Genauigkeit, es könnte jedoch genauso gut für geografische Koordinaten verwendet werden.

3
aij

Ein Java Programm zur Berechnung des maximalen Rundungsfehlers in Metern aus dem Umsetzen von Lat/Long-Werten in 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);
  }
}

Ausgabe:

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

Die Auflösung von 0,3 Zoll erreicht den Punkt, an dem Erdbeben über ein paar Jahre einen Unterschied machen. Vielleicht möchten Sie noch einmal überlegen, warum Sie der Meinung sind, dass Sie weltweit eine so gute Auflösung benötigen.

Einige der Ausbreitungszentren im Pazifischen Ozean verändern sich um bis zu 15 cm/Jahr .

3
Greg Hewgill

http://www.esri.com/news/arcuser/0400/wdside.html
Am Äquator entspricht eine Bogensekunde der Länge ungefähr einer Bogensekunde der Breite, die 1/60 einer Seemeile (oder 101,27 Fuß oder 30,87 Meter) beträgt.

32-Bit-Float enthält 23 explizite Datenbits.
180 * 3600 erfordert log2 (648000) = 19.305634287546711769425914064259 Datenbits. Beachten Sie, dass das Vorzeichenbit separat gespeichert wird und daher nur 180 Grad betragen muss.
Nach dem Subtrahieren der Bits für log2 (648000) von 23 verbleiben zusätzliche 3.694365712453288230574085935741 Bits für Subsekundendaten.
Das sind 2 ^ 3.694365712453288230574085935741 = 12.945382716049382716049382716053 Teile pro Sekunde.
Daher kann ein Float-Datentyp eine Genauigkeit von 30,87/12,945382716049382716049382716053 ~ = 2,38 Metern am Äquator haben.

3
Roland Pihlakas

Gute Frage!

Ich weiß, dass diese Frage jetzt 9 Jahre alt ist, und ich weiß nur einen Teil der Antwort, die Sie gesucht haben, aber ich bin gerade mit einer ähnlichen Frage hierher gekommen, und viele Dinge haben sich geändert, seitdem diese Frage gestellt wurde, wie Hardware und GPS-Geräte . Ich arbeite mit diesem Thema häufig in Firmware, die sich mit verschiedenen Arten von GPS in verschiedenen Arten von Anwendungen befasst, und habe die Anzahl der Stunden (und Tage) verloren, die ich damit verbracht habe, "das beste Design" für verschiedene Anwendungen zu erarbeiten, mit denen ich gearbeitet habe oder entwickelt.

Wie immer bieten unterschiedliche Lösungen Vorteile und Kosten, und letztendlich ist ein "bestes Design" immer eine "beste Übereinstimmung" der Vorteile und Kosten mit den Systemanforderungen. Hier sind einige Dinge, die ich beachten muss, wenn ich die gleiche Frage stelle:

CPU Time Cost

Wenn in der CPU kein Gleitkomma-Coprozessor integriert ist (wie dies bei vielen Mikrocontrollern der Fall ist), kann der Umgang mit "Float", "Double" und "Long Double" extrem kostspielig sein. Zum Beispiel mit einem 16-Bit-Mikrocontroller, mit dem ich regelmäßig arbeite, kostet eine Multiplikation mit 'doppelten' Werten 326 CPU-Taktzyklen und eine Division 1193 Taktzyklen. Sehr teuer!

Genauigkeits-Kompromiss

Am Äquator ein Gleitkommawert (IEEE-754 32-Bit-Gleitkommawert), der einen vorzeichenbehafteten Gradwert darstellen muss, vorausgesetzt, es können 7 "saubere" signifikante Dezimalstellen dargestellt werden, die Änderung einer niedrigstwertigen Dezimalstelle (zB von 179.9999 bis 180.0000) wird eine Entfernung von ca. 11,12 Metern darstellen. Dies kann die Anforderungen an die Genauigkeit des Systems erfüllen oder nicht. Während ein "Doppel" (mit 15 "sauberen" signifikanten Dezimalstellen, also einer Änderung von 179.999999999999 auf 180.000000000000) ungefähr 0.00011 mm darstellt.

Eingabegenauigkeitsbeschränkungen

Wenn Sie es mit Eingaben von einem GPS zu tun haben, wie viele Stellen mit echter Genauigkeit erhalten Sie und wie viele müssen Sie beibehalten?

Entwicklungszeitkosten

Ein 64-Bit-Wert mit doppelter Genauigkeit ('double') und ein 32-Bit-Wert mit einfacher Genauigkeit ('float') gemäß IEEE-754 sind in der C-Sprache SEHR praktisch, da Mathematikbibliotheken für beide mit praktisch jedem C-Compiler geliefert werden und sind in der Regel sehr zuverlässig. Wenn Ihre CPU mit einem Hardware-Gleitkommaprozessor ausgestattet ist, ist dies eine einfache Wahl.

RAM und Speicherkosten

Wenn Sie eine große Anzahl dieser Werte in RAM (oder im Speicher, z. B. MYSQL) speichern müssen, kann sich verfügbares RAM (und verfügbarer Speicherplatz) auf die Funktionsfähigkeit der Lösung auswirken.

Verfügbare Daten vs Erforderliche Daten

Ein Beispiel, mit dem ich mich in diesem Artikel beschäftige (der Grund, warum ich hier auf diese Frage gekommen bin), ist, dass ich mich mit einem u-blox M8-GPS befasse, das mir binäre GPS-Informationen geben kann (wodurch der CPU-Aufwand für die Übersetzung gespart wird ASCII NMEA-Sätze). In diesem Binärformat (als "UBX-Protokoll" bezeichnet) werden Breite und Länge als vorzeichenbehaftete 32-Bit-Ganzzahlen dargestellt, wobei diese Darstellung eine Genauigkeit (am Äquator) von bis zu etwa 1,11 cm darstellen kann. Beispielsweise wird der Längengrad -105,0269805 als -1050269805 (unter Verwendung aller 32 Bits) dargestellt, und eine LSb-Änderung entspricht einer Änderung des Breitengrads von etwa 1,11 cm und des Längengrads von 1,11 cm am Äquator (und weniger bei höheren Breitengraden im Verhältnis zum Kosinus) des Breitengrads). Die Anwendung, in der sich dieses GPS befindet, erledigt Navigationsaufgaben, für die (bereits vorhandener und erprobter Code) "doppelte" Datentypen erforderlich sind. Leider kann das Konvertieren dieser Ganzzahl in ein 64-Bit-Doppel nach IEEE-754 nicht einfach durchgeführt werden, indem die Basis-2-Bits der Ganzzahl in die internen Repräsentationsbits des Doppelwerts verschoben werden, da die durchzuführende Dezimalverschiebung a ist Basis-10-Dezimalverschiebung. Wäre es stattdessen eine Basis-2-Dezimalverschiebung, dann könnten die Basis-2-Bits der Ganzzahl mit sehr geringem Übersetzungsaufwand in die Bitfelder des 'Double' verschoben werden. Aber leider ist dies bei der vorzeichenbehafteten Ganzzahl, die ich habe, nicht der Fall. Es kostet mich also eine Multiplikation auf einer CPU ohne Hardware-Fließkommaprozessor: 326 CPU-Taktzyklen.

double   ldLatitude;
int32_t  li32LatFromGps;
ldLatitude = (double)li32LatFromGps * 0.0000001;

Beachten Sie, dass diese Multiplikation darüber gewählt wurde:

ldLatitude = (double)li32LatFromGps / 10000000.0;

weil die 'doppelte' Multiplikation ungefähr 3,6-mal schneller ist als die 'doppelte' Division auf der CPU, mit der ich es zu tun habe. So ist das Leben in der Welt der Mikrocontroller. :-)

Was BRILLIANT gewesen wäre (und vielleicht in Zukunft sein wird, wenn ich am Wochenende die Zeit sparen könnte), wäre, wenn die Navigationsaufgaben direkt mit der 32-Bit-Ganzzahl mit Vorzeichen ausgeführt werden könnten! Dann wäre keine Konvertierung erforderlich ... Aber würde es mehr kosten, die Navigationsaufgaben mit einer solchen Ganzzahl auszuführen? CPU-Kosten, wahrscheinlich viel effizienter. Entwicklungszeit kostet? Dies ist eine weitere Frage, insbesondere bei einem bereits getesteten System, das IEEE-754-64-Bit-Doppelwerte verwendet. Außerdem gibt es bereits eine Software, die Kartendaten bereitstellt (unter Verwendung von "doppelten" Gradwerten). Diese Software müsste konvertiert werden, um auch die vorzeichenbehaftete Ganzzahl zu verwenden - keine Aufgabe über Nacht!

Eine SEHR interessante Option besteht darin, Schnittpunkte zwischen Approximationen von "Rechtecken" (eigentlich Trapezoiden, die an den Polen zu Dreiecken werden) direkt (ohne Übersetzung) unter Verwendung der rohen Ganzzahlen für Breite und Länge darzustellen. Am Äquator hätten diese Rechtecke Abmessungen von ungefähr 1,11 cm Ost-West mal 1,11 cm Nord-Süd, während in einem Breitengrad von beispielsweise London, England, die Abmessungen ungefähr 0,69 cm Ost-West mal 1,11 cm Nord-Süd wären. Das kann leicht zu handhaben sein oder auch nicht, je nachdem, was die Anwendung benötigt.

Wie auch immer, ich hoffe, diese Gedanken und Diskussionen helfen anderen, die dieses Thema nach "dem besten Design" für ihr System suchen.

Herzliche Grüße, Vic

1
V. Wheeler

Der folgende Code packt die WGS84-Koordinaten verlustfrei in eine vorzeichenlose Länge (d. H. In 8 Bytes):

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);
        }
    }
}

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

1
Augustin

Wenn mit "Speichern" "In Erinnerung bleiben" gemeint ist, lautet die eigentliche Frage: Was werden Sie mit ihnen tun?

Ich vermute, bevor diese Koordinaten etwas Interessantes tun, werden sie durch die Funktionen in math.h als Bogenmaß übertragen. Es sei denn, Sie planen, einige transzendentale Funktionen zu implementieren, die mit in ein Bitfeld gepackten Deg/Min/Secs arbeiten.

Warum also nicht die Dinge einfach halten und sie einfach in IEEE-754-Graden oder Radianten mit der Präzision Ihrer Anforderungen speichern?

1
natevw

Sie können den Datentyp decimal verwenden:

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