Как отсортировать негеографические (X, Y, Z) расстояния с помощью GeoDjango / PostGIS?

В настоящее время я использую GeoDjango в базе данных "Поиск и сортировка по звездам", которая предоставляет информацию и будет имитировать информацию о звездных и планетных системах.

Я использую GeoDjango 1), потому что он мне нравится, и использую его в другом месте, и 2), потому что я в конечном итоге хочу использовать различные функции "гео" поиска, такие как преобразования расстояния / линий / полигонов, для сложных и классных объемных запросов, которые я не могу найти в другом месте.

У меня есть система, запущенная и работающая ( https://github.com/jaycrossler/procyon), и в настоящее время она использует звездные галактические координаты (уже преобразованные из прямого восхождения / склонения). В настоящее время в базе данных есть 150 тысяч звезд, и я собираюсь увеличить их до нескольких миллионов.

После того, как звезда находится в базе данных, я создаю новую таблицу с GeoDjango PointField, а затем заполняю ее координатами X, Y, Z Галактики (которые находятся в парсеках, и такие в основном находятся в диапазоне от -500 до 500)., Прямо сейчас я установил SRID на 900913 (так, чтобы у меня был хороший диапазон координат, который не перевернется вокруг мировой линии)... но когда я ищу по ближайшим звездам и упорядочиваю по расстоянию, Я получаю только возвраты, которые находятся на одной линии, а не на расстоянии, основанном на расстоянии X, Y, Z.

location = models.PointField(dim=3, blank=True, null=True, srid=900913)
objects = models.GeoManager()

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

Текущий поиск, который я использую в Django:

    origin = self.location

    distance = 1000
    close_by_stars = StarModel.objects.filter(location__distance_lte=(origin, D(m=distance))).distance(origin).order_by('distance')
    for s in close_by_stars[:200]:
      #export results

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

Итак, большой вопрос: 1) я должен использовать SRID, такой как 900913 (Spherical Mercator) или 2) есть ли SRID, который не отображается на поверхности планеты, чтобы я мог просто искать по X, Y, Расстояния Z без него, переворачивая -180 в +180 (или любой другой эквивалент, основанный на проекционной системе)? Я пытался использовать SRID=0, но geodjango, чем тошнит и не позволяет этого.

1 ответ

Решение

У меня есть исправление, которым я делюсь здесь, чтобы потенциально помочь другим.

Я думаю, что проблема в том, что функция location__distance_lte= преобразуется в POSTGIS в функцию гео: ST_DWithin

Посмотрев на pg_log/latest, я вижу команду SQL:

SELECT (ST_Distance("modelname"."location",
ST_GeomFromEWKB('\x01010000a031bf0d0021e527d53e2963409f3c2cd49ab2404081b22957787d5540'::bytea))) AS "distance",
"modelname"."info", "modelname"."location" 
FROM "modelname" WHERE ST_Distance("modelname"."location",
ST_GeomFromEWKB('\x01010000a031bf0d0021e527d53e2963409f3c2cd49ab2404081b22957787d5540'::bytea))
<= 10.0 ORDER BY "distance" ASC

Таким образом, при поиске по X, Y, Z и поиске ближайших точек, он ищет только в двухмерном пространстве - и ищет те, которые находятся на расстоянии X, Y... а не те, которые находятся внутри Z.

Существует ST_3DDWithin ( http://postgis.net/docs/ST_3DDWithin.html), но, к сожалению, Django не знает об этом: https://github.com/django/django/blob/master/django/contrib/gis/db/backends/postgis/operations.py.

Вместо переопределения исходного кода django я мог бы использовать сырой метод sql: https://docs.djangoproject.com/en/dev/topics/db/sql/. Но тогда орма потеряет большую часть своей выгоды.

Но вместо этого я решил пойти немного проще / сложнее. Я сохранил тот же поиск (который возвращает в основном "цилиндр" вместо сферы результатов). Затем в функции я перебираю результаты и разбираю те, которые не попадают в сферические результаты:

origin = item.location
origin_array = numpy.array((origin.x, origin.y, origin.z))

close_by_stars = star_model.objects.filter(location__distance_lte=(origin, D(m=distance))).distance(origin).order_by('distance')

star_list = []
for s in close_by_stars:
    location_array = numpy.array((s.location.x, s.location.y, s.location.z))

    dist = numpy.linalg.norm(origin_array - location_array)
    if dist > distance:
        continue

    star_handle = dict()
    star_handle['data'] = s.data ...
Другие вопросы по тегам