Как я могу запросить ближайшую запись в заданных координатах (широта и долгота строкового типа)? - PullRequest
13 голосов
/ 21 октября 2011

Я использую GeoDjango с PostGIS. Тогда у меня возникают проблемы с тем, как получить ближайшую запись по заданным координатам из моей таблицы postgres db.

Ответы [ 6 ]

19 голосов
/ 27 октября 2011

Это этот ответ используя GeoDjango с PostGIS

Координаты точки должны быть объектом GEOSGeometry. Чтобы сделать это используйте

from django.contrib.gis.geos import GEOSGeometry
point = GEOSGeometry('POINT(5 23)')

Тогда, давайте представим, что у вас есть модель "Ресторан" и координаты точки. Итак, для ближайшего ресторана просто используйте:

Restaurants.objects.distance(point).order_by('distance')[0] 
5 голосов
/ 30 октября 2011

Следующая версия PostGIS, 2.0, имеет суть KNN, чтобы получить ближайший сосед . Например:

SELECT ST_Distance(geom, 'SRID=26910;POINT(34.5 -23.2)'::geometry) AS d
FROM mypoints
ORDER BY geom <-> 'SRID=26910;POINT(34.5 -23.2)'::geometry LIMIT 1;

Функция в версии SVN, и вы можете протестировать / использовать ее сейчас.

3 голосов
/ 30 октября 2011

Я должен согласиться с ответом Делавена, но использование st_distance само по себе будет очень медленным . Таким образом, чтобы ускорить процесс, вам придется использовать индексы GIST (обратите внимание большинство функций PostGIS, включая st_distance НЕ использовать индексы см .: рекомендация по индексированию Postgis ).

Таким образом, вы сначала создали бы буфер вокруг точки, а затем отметили его ограничивающий прямоугольник, используя «&&» (это использует встроенный индекс GIST - так он будет работать намного лучше), а затем вы проверили бы расстояние с помощью «st_distance». ».

Например, чтобы получить ближайший «ресторан» из заданного расположения кухни (например, X = 1, Y = 1), вы должны написать:

select *,st_distance(the_geom_col,st_geomfromtext('POINT(1 1)',27700)) as distance 
from restaurants where st_buffer(st_geomfromtext('POINT(1 1)',27700),100) 
&& "the_geom_col"

Это было бы очень быстро по сравнению с "st_distance", но результаты могут содержать рестораны, которые находятся на расстоянии более 100 метров от заданного местоположения (особенно, когда геометрии сохраняются в линейных или полигональных форматах).

Чтобы получить более точные результаты, в ресторанах с точным радиусом действия 100 метров, вы добавляете , следуя приведенному выше запросу:

and st_distance(the_geom_col,st_geomfromtext('POINTFROMTEXT(1 1)',27700)) <= 100

Это все равно будет более эффективным и быстрым, чем само использование st_distance . Поскольку база данных будет работать только st_distance против записей, которые соответствуют первым критериям.

Так что, как правило, всякий раз, когда вам приходится выполнять дорогостоящие пространственные поиски, старайтесь:

  • Отфильтруйте как можно больше ложных результатов, используя специальные операторы (см. Специальные операции в официальных документах postgis .
  • Затем напишите фактическую функцию проверки пространственных отношений.
  • Всегда иметь индекс GIST в столбце "геометрия".
  • Добавьте ограничивающие рамки к вашим «геометриям», используя st_addbbox .
  • Регулярно переиндексируйте и анализируйте ваши таблицы.

Примечание. Размер буфера или фактическое расстояние должны соответствовать проекционной системе, которую вы используете, т. Е. Если вы используете EPSG: 4326 (широта / долгота), то вы должны указать эти расстояния в градусах. Например 1 метр в реальном мире = 0,00000899 градусов .. и 100 метров = сделать математику:)

2 голосов
/ 29 января 2016

Пользователи PostgreSQL / PostGIS должны использовать оператор «<->» в предложении ORDER BY, чтобы получить «K ближайших соседей» (KNN), как сказано Майк T в этого ответа .

Чтобы получить выгоду от улучшения производительности KNN-GiST в GeoDjango, вы можете написать следующее:

from django.contrib.gis.db.models.functions import GeomValue
from django.contrib.gis.geos import Point
from django.db.models.expressions import CombinedExpression, F

knn = 10
longitude = -96.876369
latitude = 29.905320
pnt = Point(longitude, latitude, srid=4326)
order_by_expression = CombinedExpression(F('geom'), '<->', GeomValue(pnt))
nearest_neighbors = Neighbor.objects.order_by(order_by_expression)[:knn]
2 голосов
/ 26 октября 2011

У меня нет опыта работы с GeoDjango, но в PostgreSQL / PostGIS у вас есть функция st_distance (..). Таким образом, вы можете упорядочить результаты по st_distance(geom_column, your_coordinates) asc и посмотреть, какие ближайшие строки.

Если у вас есть простые координаты (без постгисовой геометрии), вы можете преобразовать свои координаты в точку с помощью функции geometryFromText.

Это то, что вы искали? Если нет, попробуйте быть более явным.

0 голосов
/ 14 октября 2016

Использование PostGIS и GeoDjango для поиска ближайшего соседа

Рассмотрите эту модель:

from django.contrib.gis.geos import Point
from django.contrib.gis.db import models

    class Store(models.Model):
        name = models.CharField(max_length=100)
        location = models.PointField(geography=True, srid=4326)
        longitude = models.FloatField()
        latitude = models.FloatField()
        objects = models.GeoManager()
    def save(self, **kwargs):
        self.location = Point(self.longitude, self.latitude)
        super(Store, self).save(**kwargs)

В представлении получите все точки интереса в радиусе 100 миль от указанной долготы/ широта:

from django.contrib.gis.geos import Point
from django.contrib.gis.measure import D

point = Point(lng, lat)
points = Store.objects.filter(
    location__distance_lte=(point, D(mi=100))
).distance(point).order_by('distance')

Показать результаты в шаблоне:

<ul>
    {% for point in points %}
    <li>{{ point.name }} is {{ point.distance.mi|floatformat }} miles away</li>
    {% endfor %}
</ul>
...