Opened 5 years ago

Last modified 5 years ago

#25446 new New feature

Can't use PostGIS function ST_OrderingEquals with GeoDjango

Reported by: Evan Heidtmann Owned by: nobody
Component: GIS Version: master
Severity: Normal Keywords:
Cc: Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by Evan Heidtmann)

Unless I'm missing something, Django 1.8 provides no facility in the ORM to use the ST_OrderingEquals() function to compare two geometries. I would like to use this function in a query.

Am I correct? If so, I may attempt a patch or at least a workaround.



P.S. It's another question entirely whether the 'exact' lookup should use this function. For comparison, two LineString objects with opposite vertex ordering are not considered equal, so why should they be considered equal at the DB level?

Change History (4)

comment:1 Changed 5 years ago by Evan Heidtmann

Description: modified (diff)

For other users who may find this, here's my workaround that allows me to find the geometry that matches an input geometry, both in vertex values and in vertex ordering. A Django wizard may be able to improve this and I would welcome any criticism.

class SegmentModel(models.Model):

    def get_or_create_for_line(cls, line):
        from django.contrib.gis.db.backends.postgis.adapter import PostGISAdapter

        # We have to run a query outside of Manager.raw() because Manager.raw()
        # always supplies a params argument to cursor.execute(), and sometimes
        # the binary form of the line contains a percent sign.
        from django.db import connection
        with connection.cursor() as cursor:
            sql = 'SELECT id FROM ride_segment WHERE line ~= {line_serialized} AND ST_OrderingEquals(line, {line_serialized})'.format(
            ids = cursor.fetchmany()

        if len(ids) > 1:
            raise cls.MultipleObjectsReturned('Multiple segments for line {}'.format(line.ewkt))
        elif len(ids) == 0:
            return (cls.objects.create(line=line), True)
            return (cls.objects.get(id=ids[0][0]), False)

Last edited 5 years ago by Evan Heidtmann (previous) (diff)

comment:2 Changed 5 years ago by Claude Paroz

Triage Stage: UnreviewedAccepted
Type: BugNew feature
Version: 1.8master

You are right in that GeoDjango doesn't support this lookup. In Django 1.9, GIS lookups/functions have seen many refactorings which should allow you to more easily add your own lookups.

While studying your use case, I've seen a limitation in current lookup implementation (#25448). After fixing this one, you should be able to add your custom lookup like this (not portable way):

from django.contrib.gis.db.backends.postgis.operations import PostGISOperator
from django.contrib.gis.db.models import GeometryField
from django.contrib.gis.db.models.lookups import GisLookup

class OrderingEqualsLookup(GISLookup):
    def get_operator(connection):
        return PostGISOperator(func='ST_OrderingEquals')

For Django 1.8, keep your workaround...

We should at least document that.

Last edited 5 years ago by Claude Paroz (previous) (diff)

comment:3 Changed 5 years ago by Evan Heidtmann

Thank you! I updated the workaround to fix a couple edge cases. And I will look forward to 1.9.

While we're on the topic, what do you think about changing the "exact" lookup to use ST_OrderingEquals? This would match the behavior of GEOS LineString objects but I am not sure of the broader implications of that change. Perhaps SpatiaLite does not offer an equivalent, so we cannot bake it into Django?


comment:4 Changed 5 years ago by Claude Paroz

It may make sense. I do recognize that using the bounding box comparison for exact (~= PostGIS operator) is at least misleading... A cross-backends summary would be nice. But as usual, the backwards compatibility is important, we cannot suddenly change the operator used by a comparison.
Read also

Note: See TracTickets for help on using tickets.
Back to Top