1

First off: 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 relevant model:

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

Does anyone know why this isn't working as expected? Is this perhaps a bug, or am I making a mistake I just don't see?

[edit]
I think this may be a Django bug. I went ahead and opened a ticket here. Once I figure out what the proper fix is, I'll add the answer here.

Geoff
  • 2,461
  • 2
  • 24
  • 42
  • 1
    I don't know anything about geodjango, but the underlying Postgis function, ST_DWithin has a signature of (geometry, geometry, double), although I agree with you re the documentation. Plus, the Postgis function accepts an int as the 3rd argument. – John Powell Jun 13 '14 at 04:55
  • Yeah, I saw the ST_DWithin documentation. I assume that `D`' job is to convert whatever units to the double that ST_DWithin is expecting (I believe meters). I think I'll submit a Django bug report and update this question as I get more info. – Geoff Jun 13 '14 at 16:21
  • 1
    Yes, most Postgis functions work with meters, unless you specify SRID as something that is in degrees, like 4326. Good luck with the bug filling. – John Powell Jun 13 '14 at 16:28
  • Thanks! I went ahead and filed the bug report: https://code.djangoproject.com/ticket/22830 – Geoff Jun 13 '14 at 17:22

1 Answers1

4

I got a response to the ticket I submitted (https://code.djangoproject.com/ticket/22830). Apparently, I found a seemingly undocumented (or at least not clearly documented) problem that dwithin queries have with Distance objects. A dev says this:

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!

It looks like they're going to clarify the documentation to make this clearer (great!).

Since what I want is 5km, I need to convert 5km to degrees. 1 degree is approximately 111.325km. Therefore, 1km = 1/111.325 degrees. 5km is therefore approximately 0.0449 or about 0.05 degrees. So I just need to change my call to this:

touching_locations = Location.objects.filter(geometry__dwithin=(location.geometry, 0.05))
Geoff
  • 2,461
  • 2
  • 24
  • 42
  • 1
    If you used `geography` types instead, you could use regular length units like km, but `geometry` uses the same unit as the SRID. Assuming 1km = 1/111.325 degrees only works near the equator. – Mike T Jun 15 '14 at 22:08
  • I considered this when I first defined my models, but this line in the [PostGIS documentation](http://postgis.net/docs/manual-2.1/using_postgis_dbmanagement.html#PostGIS_GeographyVSGeometry) turned me off: "The new GEOGRAPHY type allows you to store data in longitude/latitude coordinates, but at a cost: there are fewer functions defined on GEOGRAPHY than there are on GEOMETRY; those functions that are defined take more CPU time to execute." http://postgis.net/docs/manual-2.1/PostGIS_Special_Functions_Index.html#PostGIS_TypeFunctionMatrix shows that the available functions are limited. – Geoff Jun 16 '14 at 15:14
  • 1
    True, there are fewer functions, but only if you need them. Also, with SQL you could always cast `geog::geometry` to use the geometry functions with Cartesian assumptions. – Mike T Jun 16 '14 at 21:52