Opened 4 years ago

Closed 3 weeks ago

#17635 closed New feature (fixed)

Missing ability to cast georaphy to geometry when using GeoDjango and PostgresSQL

Reported by: corentin.chary@… Owned by: nobody
Component: GIS Version: master
Severity: Normal Keywords:
Cc: Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by aaugustin)

Looking at you will see that a lot of functions only work on geometric fields (ie: geography=False).

That means something like Markers.objects.extent() won't work if the coordinate field of Marker have geography set to True because the underlying function (ST_Extent()) only work with geometric fields.

A workaround (if you don't really care about precision) is to cast geography to geometry:

SELECT ST_Extent(coordinates::geometry) FROM markers;

That will perfectly work. However, you can't really do that with django. The best you can do seems to be:

qs.extra(select={'extent': 'ST_Extent("coordinates"::geometry)'}).values('extent')

But that's really far from perfect.

Here are some propositions:

  • add a way to cast to geometry, that would allow to do: qs.extent(field_name=coordinates__geometry) (the trailing __geometry cast a GeometryField from geography to geometry)
  • another way to cast to geometry would be: qs.geometry('coordinates').extent()
  • automatically cast geography to geometry if a function only support geometry (hum...)

Change History (7)

comment:1 Changed 4 years ago by aaugustin

  • Description modified (diff)
  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset
  • Triage Stage changed from Unreviewed to Design decision needed

I've encountered the same problem (with point_on_surface and envelope) and I didn't even manage to resolve it with Django's public APIs.

Here's what I ended up with:

class ZoneManager(gis_models.GeoManager):

    def point_on_surface_and_envelope(self):
        # Use a private API to adapt point_on_surface and envelope for a
        # geographic field. See django.contrib.gis.db.models.query.GeoQuerySet.
        return self.all()._spatial_attribute('point_on_surface', {
            'select_field' : GeomField(),
            'procedure_fmt': '%(geo_col)s::geometry',   # added '::geometry'
        })._spatial_attribute('envelope', {
            'select_field' : GeomField(),
            'procedure_fmt': '%(geo_col)s::geometry',   # added '::geometry'

Maybe I should have started with geometry fields instead of geography fields, but I didn't expect to need these functions...

While I'm interested in a solution to this problem, I'm not convinced we should allow these kind of approximations.

Marking as DDN for now, the GIS maintainer may have a stronger opinion on this matter.

comment:2 Changed 3 years ago by aaugustin

  • Triage Stage changed from Design decision needed to Accepted

Oh well.

comment:3 Changed 7 weeks ago by claudep

Once #24932 is fixed, we should be able to leverage the Cast() database function to achieve this.

comment:4 Changed 3 weeks ago by claudep

  • Has patch set

To ensure the new Cast database function can be applied in this context, I added a test in this PR. Looks promising!

comment:5 Changed 3 weeks ago by timgraham

  • Triage Stage changed from Accepted to Ready for checkin

comment:6 Changed 3 weeks ago by Claude Paroz <claude@…>

In 1ee9c5b4:

Refs #17635 -- Tested the Cast function in a geography to geometry context

Thanks Tim Graham for the review.

comment:7 Changed 3 weeks ago by claudep

  • Resolution set to fixed
  • Status changed from new to closed
Note: See TracTickets for help on using tickets.
Back to Top