Skip to content Skip to sidebar Skip to footer

How To Calculate Frechet Distance In Django?

This is basically a question about running custom PostGIS functions inside the Django code. There is a number of related answers on this site, most close to my case is this one. It

Solution 1:

In your SQLAlchemy example, you are doing something that you didn't do in the GeoDjango one and that is to cast the WKT string to Geometry. What happens here essentially is that you are trying to use a PostGIS function but instead of a Geometry, you are passing it a string.

Another problem that we would stumble upon after fixing the first one would be the following exception:

django.core.exceptions.FieldError: Cannot resolve expression type, unknown output_field

and that is why we need to create a custom database function based on GeoFunc. That poses some problems of its own though and we will need to consider the following:

  • Our DB Function will receive 2 Geometries as arguments.

    That is a bit convoluted, but if we look at the code of GeoFunc we will see that the class inherits a mixin called: GeoFuncMixin which has the attribute geom_param_pos = (0,) and specifies the positions of the function arguments that will be geometries. (Yeaahhh frameworks are fun :P)

  • Our function will output a FloatField.

Therefore our custom DB Function should look like this:

from django.contrib.gis.db.models.functionsimportGeoFuncfrom django.db.models.fieldsimportFloatFieldclassFrechetDistance(GeoFunc):
    function='ST_FrechetDistance'
    geom_param_pos = (0, 1,)
    output_field = FloatField()

Now we can use this function in our query to calculate the ST_FrechetDistance.We will also need to address the original issue of passing geometries to the function and not just WKT strings:

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   

Post a Comment for "How To Calculate Frechet Distance In Django?"