Ускорьте запрос "Найти точки в многоугольнике" в геоджанго - PullRequest
0 голосов
/ 11 мая 2018

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

У меня есть следующие модели:

class Location(models.Model):
    name = models.CharField(max_length=250, unique=True)
    geometry = models.GeometryField(null=True, blank=True)


class Project(models.Model):
    name = models.CharField(max_length=2000, blank=False, null=False, unique=True)
    location = models.ManyToManyField(Location, related_name='projects', blank=True)

На мой взгляд, у меня есть: Класс LocationDetailView (DetailView): модель = расположение

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        location_geometry = self.get_object().geometry

        location_projects = []
        if 'Polygon' in location_geometry.geom_type:
            # location is a Polygon or MultiPolygon
            for loc in Location.objects.prefetch_related('projects').all():
                if location_geometry.contains(loc.geometry):
                    for location_project in loc.projects.all():
                        location_projects.append({'name': location_project.name, 'pk': location_project.pk})

        # Add in a QuerySet of all the projects
        context['projects'] = location_projects
        return context

Это довольно медленно (и у меня нет только нескольких сотен мест в БД). Как вы можете видеть, я пытался ускорить это путем предварительной загрузки проектов, но когда я смотрю на SQL на панели инструментов отладки, я вижу, что (хотя это быстрый запрос) один запрос дублируется 259 раз:

SELECT
    "app_location"."id",
    "app_location"."name",
    "app_location"."geometry"::bytea,
FROM
    "app_location" WHERE "app_location"."id" = 979

Duplicated 259 times.

И этот запрос вызывается один раз, но довольно медленно:

SELECT
    ("app_project_location"."location_id") AS "_prefetch_related_val_location_id",
    "app_project"."id",
    "app_project"."name"
FROM
    "app_project" INNER JOIN "app_project_location" ON
    ("app_project"."id" = "app_project_location"."project_id")
WHERE "app_project_location"."location_id" IN (780, ..., 1018, 1019, 1020, 1021, 1022, 1023)

Как бы я мог сделать это более эффективно? Я мог бы, конечно, загрузить все объекты и сделать точку в poly part в python, но я предполагаю, что это можно сделать напрямую в DB?

Ответы [ 2 ]

0 голосов
/ 11 мая 2018

Благодаря @Jedndrusk его ответу, я еще немного посмотрел в интернете и нашел решение, которое намного быстрее и в геоджанго:

projects = []
if 'Polygon' in self.get_object().geometry.geom_type:
# location is a polygon
    for loc in Location.objects.filter(geometry__intersects=self.get_object().geometry).prefetch_related('projects').all():
        for project in loc.projects.all():
            projects.append({'name': project.name, 'pk': project.pk})
0 голосов
/ 11 мая 2018

Предполагая, что вы используете postgresql и postgis (?), Вы можете сделать это в базе данных с помощью запроса.PostGIS предоставляет множество пространственных функций для этого:

http://postgis.org/docs/ST_Intersects.html

https://postgis.net/docs/ST_Covers.html

https://postgis.net/docs/ST_Contains.html

Чтобы избежать проблем с производительностью, позаботьтесь о пространственноминдексы:

http://revenant.ca/www/postgis/workshop/indexing.html

Так как я не большой поклонник ORM, я не уверен, как это сделать в модели django, но я совершенно уверен, что это возможно

...