Ich habe Daten mit Längen- und Breitengrad in meiner SQLite-Datenbank gespeichert und möchte die nächstgelegenen Speicherorte zu den von mir eingegebenen Parametern ermitteln (z. B. meinen aktuellen Standort - lat / lng usw.).
Ich weiß, dass dies in MySQL möglich ist, und ich habe einige Untersuchungen durchgeführt, wonach SQLite eine benutzerdefinierte externe Funktion für die Haversine-Formel (Berechnung der Entfernung auf einer Kugel) benötigt, aber ich habe nichts gefunden, was in Java geschrieben ist und funktioniert .
Wenn ich benutzerdefinierte Funktionen hinzufügen möchte, benötige ich außerdem die org.sqlite
.jar (für org.sqlite.Function
), wodurch die App unnötig vergrößert wird.
Die andere Seite davon ist, dass ich die Funktion "Sortieren nach" aus SQL benötige, da das Anzeigen der Entfernung allein kein so großes Problem darstellt - ich habe es bereits in meinem benutzerdefinierten SimpleCursorAdapter getan, aber ich kann die Daten nicht sortieren, weil ich Ich habe die Entfernungsspalte nicht in meiner Datenbank. Das würde bedeuten, die Datenbank jedes Mal zu aktualisieren, wenn sich der Standort ändert, und das ist eine Verschwendung von Batterie und Leistung. Wenn also jemand eine Idee hat, den Cursor nach einer Spalte zu sortieren, die nicht in der Datenbank enthalten ist, wäre ich auch dankbar!
Ich weiß, dass es Unmengen von Android-Apps gibt, die diese Funktion verwenden, aber kann jemand bitte die Magie erklären.
Übrigens habe ich diese Alternative gefunden: Abfrage zum Abrufen von Datensätzen basierend auf Radius in SQLite?
Es wird vorgeschlagen, 4 neue Spalten für die cos- und sin-Werte von lat und lng zu erstellen. Gibt es jedoch einen anderen, nicht so redundanten Weg?
quelle
Antworten:
1) Filtern Sie zuerst Ihre SQLite-Daten mit einer guten Annäherung und verringern Sie die Datenmenge, die Sie in Ihrem Java-Code auswerten müssen. Verwenden Sie zu diesem Zweck das folgende Verfahren:
Um einen deterministischen Schwellenwert und einen genaueren Filter für Daten zu erhalten, ist es besser, 4 Positionen in
radius
Metern des Nordens, Westens, Ostens und Südens Ihres Mittelpunkts in Ihrem Java-Code zu berechnen und dann einfach um weniger als und mehr als zu überprüfen SQL-Operatoren (>, <), um festzustellen, ob sich Ihre Punkte in der Datenbank in diesem Rechteck befinden oder nicht.Die Methode
calculateDerivedPosition(...)
berechnet diese Punkte für Sie (p1, p2, p3, p4 im Bild).Und jetzt erstellen Sie Ihre Abfrage:
COL_X
ist der Name der Spalte in der Datenbank, in der Breitengradwerte undCOL_Y
Längengrade gespeichert sind .Sie haben also einige Daten, die sich mit einer guten Annäherung in der Nähe Ihres Mittelpunkts befinden.
2) Jetzt können Sie diese gefilterten Daten in einer Schleife bearbeiten und mithilfe der folgenden Methoden feststellen, ob sie sich wirklich in der Nähe Ihres Punkts (im Kreis) befinden oder nicht:
Genießen!
Ich habe diese Referenz verwendet, angepasst und vervollständigt.
quelle
Chris 'Antwort ist wirklich nützlich (danke!), Funktioniert aber nur, wenn Sie geradlinige Koordinaten verwenden (z. B. UTM- oder OS-Gitterreferenzen). Wenn Sie Grad für lat / lng verwenden (z. B. WGS84), funktioniert das Obige nur am Äquator. In anderen Breiten müssen Sie den Einfluss der Länge auf die Sortierreihenfolge verringern. (Stellen Sie sich vor, Sie befinden sich in der Nähe des Nordpols ... ein Breitengrad ist immer noch derselbe wie überall, aber ein Längengrad kann nur wenige Fuß betragen. Dies bedeutet, dass die Sortierreihenfolge falsch ist.)
Wenn Sie nicht am Äquator sind, berechnen Sie den Fudge-Faktor anhand Ihres aktuellen Breitengrads vor:
Dann bestellen Sie bei:
((<lat> - LAT_COLUMN) * (<lat> - LAT_COLUMN) + (<lng> - LNG_COLUMN) * (<lng> - LNG_COLUMN) * <fudge>)
Es ist immer noch nur eine Annäherung, aber viel besser als die erste, so dass Ungenauigkeiten in der Sortierreihenfolge viel seltener sind.
quelle
((<lat> - LAT_COLUMN) * (<lat> - LAT_COLUMN) + (<lng> - LNG_COLUMN) * (<lng> - LNG_COLUMN) * <fudge>)
kleiner sein alsdistance
oderdistance^2
?Ich weiß, dass dies beantwortet und akzeptiert wurde, dachte aber, ich würde meine Erfahrungen und Lösungen hinzufügen.
Während ich gerne eine Haversine-Funktion auf dem Gerät ausführte, um die genaue Entfernung zwischen der aktuellen Position des Benutzers und einem bestimmten Zielort zu berechnen, musste die Abfrageergebnisse in der Reihenfolge der Entfernung sortiert und begrenzt werden.
Die weniger als zufriedenstellende Lösung besteht darin, das Los zurückzugeben und nachträglich zu sortieren und zu filtern. Dies würde jedoch dazu führen, dass ein zweiter Cursor und viele unnötige Ergebnisse zurückgegeben und verworfen werden.
Meine bevorzugte Lösung bestand darin, in einer Sortierreihenfolge der quadratischen Deltawerte von Long und Lats zu übergeben:
Es ist nicht erforderlich, den vollständigen Haversine nur für eine Sortierreihenfolge auszuführen, und es ist nicht erforderlich, die Ergebnisse zu quadrieren, sodass SQLite die Berechnung durchführen kann.
BEARBEITEN:
Diese Antwort empfängt immer noch Liebe. In den meisten Fällen funktioniert es einwandfrei. Wenn Sie jedoch etwas mehr Genauigkeit benötigen, lesen Sie bitte die Antwort von @Teasel unten, in der ein "Fudge" -Faktor hinzugefügt wird, der Ungenauigkeiten behebt, die zunehmen, wenn sich der Breitengrad 90 nähert.
quelle
cos(latitude)
, dass Breiten- und Längengrad ungefähr gleich sind. Siehe en.wikipedia.org/wiki/…Um die Leistung so weit wie möglich zu steigern, schlage ich vor, die Idee von @Chris Simpson mit der folgenden
ORDER BY
Klausel zu verbessern :In diesem Fall sollten Sie die folgenden Werte aus dem Code übergeben:
Und Sie sollten auch
LAT_LON_SQ_SUM = LAT_COL^2 + LON_COL^2
als zusätzliche Spalte in der Datenbank speichern. Füllen Sie es aus und fügen Sie Ihre Entitäten in die Datenbank ein. Dies verbessert die Leistung geringfügig, während große Datenmengen extrahiert werden.quelle
Versuchen Sie so etwas:
Danach enthält id das gewünschte Element aus der Datenbank, damit Sie es abrufen können:
Hoffentlich hilft das!
quelle