在 geodjango 中加速 "Find points in polygon"-查询

Speed up "Find points in polygon"-query in geodjango

我正在开发一个 geodjango 应用程序,我想在其中找到多边形中的所有地理位置。这个想法是我将所有地理信息(城市、国家、POI 等)存储在 table 中,如果我想在一个(多)多边形内找到所有内容,我查询数据库以找到这些点。

我有以下型号:

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)

在我看来我有: class 位置详细视图(详细视图): 模型 = 位置

    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)

我怎样才能更有效地做到这一点?我当然可以加载所有对象并在 python 中做多边形部分的点,但我假设它可以直接在数据库中完成?

假设您使用的是 postgresql 和 postgis (?),您可以使用查询在数据库中执行此操作。 PostGIS 提供了许多空间函数来做到这一点:

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

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

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

为避免性能问题,请注意空间索引:

https://web.archive.org/web/20200222103516/http://revenant.ca/www/postgis/workshop/indexing.html

因为我不是 ORM 的忠实粉丝,所以我不确定如何在 django 模型中执行此操作,但我很确定这是可能的

感谢@Jedndrusk 他的回答,我在互联网上多看了一些,找到了更快的解决方案,并且在 geodjango 中:

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})