在 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})
我正在开发一个 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})