Python 如何计算Django中的Frechet距离?

Python 如何计算Django中的Frechet距离?,python,django,postgresql,postgis,geodjango,Python,Django,Postgresql,Postgis,Geodjango,这基本上是关于在Django代码中运行自定义PostGIS函数的问题。在这个网站上有很多相关的答案,最接近我的情况是。建议使用Func()或甚至GeoFunc()类,但这里没有地理空间函数的示例。后者('GeoFunc')甚至不适用于我,抛出st_GeoFunc不存在exception(Django 2.1.5) 我必须完成的任务是根据线串到给定几何体的Frechet距离过滤线串。应使用PostGIS提供的函数计算Frechet距离 在另一个基于SQLAlchemy的项目中,我使用以下功能完成了

这基本上是关于在Django代码中运行自定义PostGIS函数的问题。在这个网站上有很多相关的答案,最接近我的情况是。建议使用
Func()
或甚至
GeoFunc()
类,但这里没有地理空间函数的示例。后者('GeoFunc')甚至不适用于我,抛出
st_GeoFunc不存在
exception(Django 2.1.5)

我必须完成的任务是根据线串到给定几何体的Frechet距离过滤线串。应使用PostGIS提供的函数计算Frechet距离

在另一个基于SQLAlchemy的项目中,我使用以下功能完成了完全相同的任务(它正在工作):

它不起作用,因为
frechet\u注释
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...
                                                             ^
django.core.exceptions.FieldError: Cannot resolve expression type, unknown output_field
似乎我错误地定义了“ST_FrechetDistance”计算。我怎么修理它


更新


签出了Django编写的SQL。它总体上是正确的,但试图将
FrecheDistance
的结果投射到
bytea
会破坏它
ST_FrechetDistance(…)::bytea
。当我手动运行查询而不使用
bytea
cast时,SQL会工作。因此,问题是如何避免将
转换为bytea

在SQLAlchemy示例中,您正在做一些在GeoDjango one中没有做的事情,即将
WKT
字符串转换为
几何体
这里发生的事情本质上是,您试图使用
PostGIS
函数,但不是几何体,而是传递一个字符串

在解决第一个问题后,我们会遇到的另一个问题是以下例外:

django.db.utils.ProgrammingError: cannot cast type double precision to bytea
LINE 1: ...548 55.717805109,36.825235998 55.717761246)', 0.1)::bytea AS...
                                                             ^
django.core.exceptions.FieldError: Cannot resolve expression type, unknown output_field
这就是为什么我们需要基于
GeoFunc
创建自定义数据库函数的原因。这本身就带来了一些问题,我们需要考虑以下几点:

  • 我们的DB函数将接收2个几何图形作为参数

    这有点复杂,但如果我们查看的代码,我们将看到该类继承了一个名为:的mixin,它具有属性
    geom_param_pos=(0,)
    ,并指定将成为几何体的函数参数的位置。(是啊,框架很有趣:P)

  • 我们的函数将输出一个
    浮动字段
因此,我们的自定义DB函数应如下所示:

from django.contrib.gis.db.models.functions import GeoFunc
from django.db.models.fields import FloatField

class FrechetDistance(GeoFunc):
    function='ST_FrechetDistance'
    geom_param_pos = (0, 1,)
    output_field = FloatField()
现在我们可以在查询中使用此函数来计算。我们还需要解决将几何图形传递给函数的原始问题,而不仅仅是
WKT
字符串:

def get_matched_segments(wkt: str, freche_threshold: float = 0.002) -> QuerySet:
    forward_linestring = GEOSGeometry(wkt, srid=4326)
    backward_linestring = GEOSGeometry(wkt, srid=4326)
    backward_linestring.reverse()
    backward_linestring.srid = 4326  # On Django 2.1.5 `srid` is lost after `reverse()`
    transform_ls = linestring.transform(3857, clone=True)

    frechet_annotation = HighwayOnlyMotor.objects.filter(
        geom__dwithin=(transform_ls, D(m=20))  
    ).annotate(
        fre_forward=FrechetDistance(
            Func(F('geom'), Value(4326), function='ST_Transform'),
            Value(forward_linestring),
            Value(0.1)
        ),
        fre_backward=FrechetDistance(
            Func(F('geom'), Value(4326), function='ST_Transform'),
            Value(backward_linestring),
            Value(0.1)
        )
    )
    matched_segments = frechet_annotation.filter(
        Q(fre_forward__lte=freche_threshold) |
        Q(fre_backward__lte=freche_threshold)
    )
    return matched_segments   

我将使用shell尝试一步一步地分解问题:首先只使用
Func(F('geom')、Value(4326)、function='ST_Transform')
进行注释以查看输出,然后使用
Value(wkt)
进行注释,并每次检查结果是否符合预期。不确定为什么需要将某些内容强制转换为Bytea感谢您对
GeoFunc
用法的解释!我在你的回答中编辑了代码-调用
reverse()
方法后,出现了
srid
数据丢失的错误。@SS\u Rebelious是的,捕捉得很好@SS_Rebelious事实上,这是一个非常好的用例,我已经将它添加到链接的QA中(感谢您在问题btw中提到它)。