【发布时间】:2019-10-21 20:54:44
【问题描述】:
这基本上是关于在 Django 代码中运行自定义 PostGIS 函数的问题。这个网站上有很多相关的答案,最接近我的情况是this one。建议使用Func() 甚至GeoFunc() 类,但那里没有地理空间功能的示例。后者('GeoFunc')甚至对我抛出 st_geofunc does not exist 异常(Django 2.1.5)都不起作用。
我必须完成的任务是根据 LineStrings 到给定几何的 Frechet 距离来过滤它们。 Frechet 距离应该使用 PostGIS 提供的ST_FrechetDistance 函数计算。
在另一个基于 SQLAlchemy 的项目中,我使用以下函数完成了完全相同的任务(它正在工作):
from geoalchemy2 import Geography, Geometry
from sqlalchemy import func, cast
def get_matched_segments(wkt: str, freche_threshold: float = 0.002):
matched_segments = db_session.query(RoadElement).filter(
func.ST_Dwithin(
RoadElement.geom,
cast(wkt, Geography),
10
)
).filter(
(func.ST_FrechetDistance(
cast(RoadElement.geom, Geometry),
cast(wkt, Geometry),
0.1
) < freche_threshold) |
# Frechet Distance is sensitive to geometry direction
(func.ST_FrechetDistance(
cast(RoadElement.geom, Geometry),
func.ST_Reverse(cast(wkt, Geometry)),
0.1
) < freche_threshold)
)
return matched_segments
正如我所说,上面的函数正在运行,我想在 Django 中重新实现它。我不得不添加额外的几何 SRS 转换,因为在基于 SQLite 的项目中 LineStrings 位于 EPSG:4326 中,而在 Django 中它们最初位于 EPSG:3857 中。这是我想出的:
from django.db.models import Func, Value, Q, QuerySet, F
from django.contrib.gis.geos import GEOSGeometry
class HighwayOnlyMotor(models.Model):
geom = LineStringField(srid=3857)
def get_matched_segments(wkt: str, freche_threshold: float = 0.002) -> QuerySet:
linestring = GEOSGeometry(wkt, srid=4326)
transform_ls = linestring.transform(3857, clone=True)
linestring.reverse()
frechet_annotation = HighwayOnlyMotor.objects.filter(
geom__dwithin=(transform_ls, D(m=20))
).annotate(
fre_forward=Func(
Func(F('geom'), Value(4326), function='ST_Transform'),
Value(wkt),
Value(0.1),
function='ST_FrechetDistance'
),
fre_backward=Func(
Func(F('geom'), Value(4326), function='ST_Transform'),
Value(linestring.wkt),
Value(0.1),
function='ST_FrechetDistance'
)
)
matched_segments = frechet_annotation.filter(
Q(fre_forward__lte=freche_threshold) |
Q(fre_backward__lte=freche_threshold)
)
return matched_segments
它不起作用,因为frechet_annotation QuerySet 抛出异常:
django.db.utils.ProgrammingError: cannot cast type double precision to bytea
LINE 1: ...548 55.717805109,36.825235998 55.717761246)', 0.1)::bytea AS...
^
似乎我错误地定义了“ST_FrechetDistance”计算。我该如何解决?
更新
查看了 Django 编写的 SQL。它总体上是正确的,但尝试将FrecheDistance 的结果转换为bytea 会破坏它ST_FrechetDistance(...)::bytea。当我在没有bytea 演员的情况下手动运行查询时,SQL 工作。所以问题是如何避免这种转换为bytea?
【问题讨论】:
-
我会使用 shell 尝试逐步分解问题:首先仅使用
Func(F('geom'), Value(4326), function='ST_Transform')进行注释以查看输出,然后仅使用Value(wkt)进行注释并检查每次结果是否是你所期望的。不知道为什么它需要向 bytea 投射一些东西
标签: python django postgresql postgis geodjango