2017-05-09 2 views
3

Dies ist ein unerwartetes Verhalten, das auf dem Weg zur Lösung dieses Problems mit @Nargiza aufgetreten ist: 3d distance calculations with GeoDjango.Djangos Distance-Funktion gibt kein Distance-Objekt zurück

Im Anschluss an die Django-Dokumentation auf der DistanceFunktion:

Accepts two geographic fields or expressions and returns the distance between them, as a Distance object.

Und von einem DistanceObjekt wir den Abstand in jedem einzelnen der supported units bekommen kann.

Aber:

Let Modell sein:

class MyModel(models.Model): 
    ... 
    coordinates = models.PointField() 

dann folgendes:

p1 = MyModel.objects.get(id=1).coordinates 
p2 = MyModel.objects.get(id=2).coordinates 
d = Distance(p1, p2) # This is the function call, 
        # so d should be a Distance object 
print d.m 

sollte der Abstand zwischen p1 und p2 in Metern drucken.

Stattdessen haben wir die folgende Fehlermeldung:

AttributeError: 'Distance' object has no attribute 'm' 

Wir schließlich eine Abhilfe gefunden (d = Distance(m=p1.distance(p2))), aber die Frage bleibt:

Warum die DistanceFunktion nicht zurück ein DistanceObjekt?
Ist das ein Fehler, oder haben wir etwas übersehen?

Vielen Dank im Voraus für Ihre Zeit.

Antwort

2

Diese beiden sind nicht so eng miteinander verwandt. Dokumente sagen tatsächlich, dass es Distance-Objekt "zurückgibt", aber es gibt einen zusätzlichen Schritt:

django.contrib.gis. db.models .functions.Distance ist eine Datenbank Funktion, es dauert zwei Ausdrücke (die Datenbankfeldnamen enthalten können) und gibt ein Func Objekt zurück, das als Teil einer Abfrage verwendet werden kann.

Also einfach gesagt, es muss in einer Datenbank ausgeführt werden. Er berechnet die Entfernung unter Verwendung der Datenbankfunktion (z. B. postgis ST_Distance) und bringt sie dann als django.contrib.gis.measure.Distance Objekt zurück.

Also, es sei denn man zu Chaos mit SQL-Compiler und DB-Verbindungen will, einfachste Weg, Entfernung zwischen zwei Punkten zu erhalten, ist Distance(m=p1.distance(p2))

EDIT: Einige Code, um den Punkt zu illustrieren:

Sie können prüfen, Out-Code für die Entfernung (Messung) Klasse in django/contrib/gis/measure.py. Es ist ziemlich klein und leicht zu verstehen.Alles, was Sie eine bequeme Möglichkeit ist, was es tut zu tun Umwandlung, Vergleichs- und Rechenoperationen mit Entfernungen:

In [1]: from django.contrib.gis.measure import Distance 

In [2]: d1 = Distance(mi=10) 

In [3]: d2 = Distance(km=15) 

In [4]: d1 > d2 
Out[4]: True 

In [5]: d1 + d2 
Out[5]: Distance(mi=19.32056788356001) 

In [6]: _.km 
Out[6]: 31.09344 

Nun schauen wir uns die Entfernung Funktion einen Blick:

hinzufügen __str__ Methode zum Modell, so dass wir siehe Distanzwert kann, wenn es durch die queryset api und kurze db_table Namen zurückgegeben, so dass wir in den Abfragen aussehen:

class MyModel(models.Model): 
    coordinates = models.PointField() 

    class Meta: 
     db_table = 'mymodel' 

    def __str__(self): 
     return f"{self.coordinates} {getattr(self, 'distance', '')}" 

einige Objekte erstellen und machen eine einfache select * from Abfrage:

012.351.
In [7]: from gisexperiments.models import MyModel 

In [8]: from django.contrib.gis.geos import Point 

In [10]: some_places = MyModel.objects.bulk_create(
    ...:  MyModel(coordinates=Point(i, i, srid=4326)) for i in range(1, 5) 
    ...:) 

In [11]: MyModel.objects.all() 
Out[11]: <QuerySet [<MyModel: SRID=4326;POINT (1 1) >, <MyModel: SRID=4326;POINT (2 2) >, <MyModel: SRID=4326;POINT (3 3) >, <MyModel: SRID=4326;POINT (4 4) >]> 

In [12]: str(MyModel.objects.all().query) 
Out[12]: 'SELECT "mymodel"."id", "mymodel"."coordinates" FROM "mymodel"' 

Bohren. Lassen Sie sich Entfernung Funktion verwenden, um Abstandswert zum Ergebnis hinzu: berechne Abstand unter Verwendung von Werten aus "mymodel"."coordinates" und Byte-Darstellung unseres origin Punkt

In [14]: from django.contrib.gis.db.models.functions import Distance 

In [15]: from django.contrib.gis.measure import D # an alias 

In [16]: q = MyModel.objects.annotate(dist=Distance('coordinates', origin)) 

In [17]: list(q) 
Out[17]: 
[<MyModel: SRID=4326;POINT (1 1) 157249.597768505 m>, 
<MyModel: SRID=4326;POINT (2 2) 314475.238061007 m>, 
<MyModel: SRID=4326;POINT (3 3) 471652.937856715 m>, 
<MyModel: SRID=4326;POINT (4 4) 628758.663018087 m>] 

In [18]: str(q.query) 
Out[18]: 'SELECT "mymodel"."id", "mymodel"."coordinates", ST_distance_sphere("mymodel"."coordinates", ST_GeomFromEWKB(\'\\001\\001\\000\\000 \\346\\020\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\'::bytea)) AS "distance" FROM "mymodel"' 

Sie können sehen, es ST_distance_sphere SQL-Funktion verwendet.

wir sie jetzt für die Filterung und Ordnung und viele andere Dinge verwenden können, die alle in der Datenbank-Management-System (schnell):

In [19]: q = q.filter(distance__lt=D(km=400).m) 

In [20]: list(q) 
Out[20]: 
[<MyModel: SRID=4326;POINT (1 1) 157249.597768505 m>, 
<MyModel: SRID=4326;POINT (2 2) 314475.238061007 m>] 

Hinweis .m Sie müssen Schwimmer Nummer an den Filter passieren, es gewann Objekt "Entfernung" kann nicht erkannt werden.

+0

Lassen Sie mich wissen, wenn ich der Antwort einen Code hinzufügen muss. Ich wollte, aber kämpfte darum, etwas Minimales zu bekommen. Ich werde es später noch einmal versuchen – Igonato

+0

Vielen Dank für die Antwort @Igonato, wenn Sie ein Code-Beispiel zur Verfügung stellen können, das wäre schön! Ich werde das Kopfgeld bei anderen Antworten behalten :) –

+0

@JohnMoutafis hat etwas Code hinzugefügt. Ich bin ziemlich neu in GeoDjango, vielleicht gibt es einen besseren Weg, es zu erklären, aber das ist mein Bestes. Außerdem wurde ein Fehler behoben. Function Distance gibt Func zurück, kein Lookup-Objekt – Igonato

Verwandte Themen