Opened 13 months ago

Closed 13 months ago

Last modified 13 months ago

#22830 closed Bug (invalid)

GeoDjango dwithin errors when using django.contrib.gis.measure.D

Reported by: django@… Owned by: nobody
Component: GIS Version: 1.6
Severity: Normal Keywords:
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

First off, I started a StackOverflow thread here but haven't come up with an answer yet. I think there may be a bug in Django somewhere.

Versions: Python 2.7.6, Django 1.6.5, Postgres 9.3.4, PostGIS 2.1.3, psycopg2 2.5.3 on RHEL 6.5

Here's the model I'm using:

class Location(models.Model):
    name = models.CharField(max_length=255)
    geometry = models.MultiPolygonField(blank=True, default=None, null=True)
    objects = models.GeoManager()  # override the default manager with a GeoManager instance
    parent = models.ForeignKey('self', blank=True, default=None, null=True)

    def __unicode__(self):
        return self.name

This query should work according to the docs:

touching_locations = Location.objects.filter(geometry__dwithin=(location.geometry, D(km=5)))
logging.debug(type(touching_locations))
logging.debug(len(touching_locations))

But it doesn't. The first debug call works, but the second throws a ValueError:

<class 'django.contrib.gis.db.models.query.GeoQuerySet'>
ValueError: Only numeric values of degree units are allowed on geographic DWithin queries.

If I make a small change by changing D(km=5) to 5:

touching_locations = Location.objects.filter(geometry__dwithin=(location.geometry, 5))
logging.debug(type(touching_locations))
logging.debug(len(touching_locations))

All of a sudden it works. The output I get is this:

<class 'django.contrib.gis.db.models.query.GeoQuerySet'>
54

Is there a bug somewhere in Django? Am I misunderstanding how to use D? Are the Django docs incorrect? Is something else going on here? I appreciate any help I can get. Thanks!

Change History (6)

comment:1 follow-up: Changed 13 months ago by claudep

  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset
  • Resolution set to invalid
  • Status changed from new to closed

The error message is pretty clear. As your objects are in geographic coordinates (geometry fields default to WGS84), you have to provide the distance as degree units. This is for example matching the PostGIS definition:

boolean ST_DWithin(geometry g1, geometry g2, double precision distance_of_srid);

distance_of_srid being degrees for WGS84. So the 5 that works in your example means 5 degrees, not 5 km!

You could argue that GeoDjango could automatically cast geographic fields to geometry types when distance is a Distance object. It is unfortunately not as smart currently. See #17635 for a similar feature request (but in the opposite direction).

comment:2 in reply to: ↑ 1 Changed 13 months ago by django@…

Replying to claudep:

The error message is pretty clear. As your objects are in geographic coordinates (geometry fields default to WGS84), you have to provide the distance as degree units. This is for example matching the PostGIS definition:

boolean ST_DWithin(geometry g1, geometry g2, double precision distance_of_srid);

distance_of_srid being degrees for WGS84. So the 5 that works in your example means 5 degrees, not 5 km!

You could argue that GeoDjango could automatically cast geographic fields to geometry types when distance is a Distance object. It is unfortunately not as smart currently. See #17635 for a similar feature request (but in the opposite direction).

The error may be clear, but the cause isn't (which is why I had to submit this bug report). Either GeoDjango should correctly handle Distance objects in all cases, or the documentation should be edited to make it clear that Distance objects don't work in all cases. Since #17635 has been open for 2 years without any progress, I'd opt for the latter. I don't see anywhere in the documentation that even hints that Distance objects can't be used for the dwithin query in certain cases.

comment:3 follow-up: Changed 13 months ago by claudep

Would something like that be clear enough for you?

diff --git a/docs/ref/contrib/gis/geoquerysets.txt b/docs/ref/contrib/gis/geoquerysets.txt
index 957a049..dd2dfe8 100644
--- a/docs/ref/contrib/gis/geoquerysets.txt
+++ b/docs/ref/contrib/gis/geoquerysets.txt
@@ -613,8 +613,11 @@ SpatiaLite  ``Distance(poly, geom) <= 5``
 dwithin
 -------
 
-Returns models where the distance to the geometry field from the
-lookup geometry are within the given distance from one another.
+Returns models where the distance to the geometry field from the lookup
+geometry are within the given distance from one another. Note that you can only
+provide :class:`~django.contrib.gis.measure.Distance` objects if the targeted
+geometries are in a projected system. For geographic geometries, you should use
+units of the geometry field (e.g. degrees for ``WGS84``) .
 
 Example::

comment:4 in reply to: ↑ 3 Changed 13 months ago by django@…

Replying to claudep:

Would something like that be clear enough for you?

diff --git a/docs/ref/contrib/gis/geoquerysets.txt b/docs/ref/contrib/gis/geoquerysets.txt
index 957a049..dd2dfe8 100644
--- a/docs/ref/contrib/gis/geoquerysets.txt
+++ b/docs/ref/contrib/gis/geoquerysets.txt
@@ -613,8 +613,11 @@ SpatiaLite  ``Distance(poly, geom) <= 5``
 dwithin
 -------
 
-Returns models where the distance to the geometry field from the
-lookup geometry are within the given distance from one another.
+Returns models where the distance to the geometry field from the lookup
+geometry are within the given distance from one another. Note that you can only
+provide :class:`~django.contrib.gis.measure.Distance` objects if the targeted
+geometries are in a projected system. For geographic geometries, you should use
+units of the geometry field (e.g. degrees for ``WGS84``) .
 
 Example::

Yes, that would be excellent!

comment:5 Changed 13 months ago by Claude Paroz <claude@…>

In c281831a5cb20a31cb080ff7b4af4bcfc83fcf3d:

Complemented dwithin docs about using geographic geometries

Refs #22830. Thanks django@… for the suggestion.

comment:6 Changed 13 months ago by Claude Paroz <claude@…>

In ec2f0417364050aed3e8c2aa1948915d08c39514:

[1.7.x] Complemented dwithin docs about using geographic geometries

Refs #22830. Thanks django@… for the suggestion.
Backport of c281831a5c from master.

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