How To Calculate Frechet Distance In Django?
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 attributegeom_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?"