summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--django/contrib/gis/db/backend/postgis/query.py2
-rw-r--r--django/contrib/gis/db/models/fields/__init__.py17
-rw-r--r--django/contrib/gis/tests/distapp/tests.py17
3 files changed, 27 insertions, 9 deletions
diff --git a/django/contrib/gis/db/backend/postgis/query.py b/django/contrib/gis/db/backend/postgis/query.py
index 1b8c3fa029..2610173673 100644
--- a/django/contrib/gis/db/backend/postgis/query.py
+++ b/django/contrib/gis/db/backend/postgis/query.py
@@ -228,7 +228,7 @@ def get_geo_where_clause(lookup_type, table_prefix, field, value):
if lookup_type == 'relate':
op = op(value[1])
elif lookup_type in DISTANCE_FUNCTIONS and lookup_type != 'dwithin':
- if field._unit_name == 'degree':
+ if field.geodetic:
# Geodetic distances are only availble from Points to PointFields.
if field._geom != 'POINT':
raise TypeError('PostGIS spherical operations are only valid on PointFields.')
diff --git a/django/contrib/gis/db/models/fields/__init__.py b/django/contrib/gis/db/models/fields/__init__.py
index 8948854b35..264ba40509 100644
--- a/django/contrib/gis/db/models/fields/__init__.py
+++ b/django/contrib/gis/db/models/fields/__init__.py
@@ -79,15 +79,22 @@ class GeometryField(SpatialBackend.Field):
super(GeometryField, self).__init__(**kwargs) # Calling the parent initializtion function
### Routines specific to GeometryField ###
- def get_distance(self, dist):
+ @property
+ def geodetic(self):
+ return self._unit_name in self.geodetic_units
+
+ def get_distance(self, dist, lookup_type):
"""
Returns a distance number in units of the field. For example, if
`D(km=1)` was passed in and the units of the field were in meters,
then 1000 would be returned.
"""
-
+ postgis = SpatialBackend.name == 'postgis'
if isinstance(dist, Distance):
- if self._unit_name in self.geodetic_units:
+ if self.geodetic:
+ # Won't allow Distance objects w/DWithin lookups on PostGIS.
+ if postgis and lookup_type == 'dwithin':
+ raise TypeError('Only numeric values of degree units are allowed on geographic DWithin queries.')
# Spherical distance calculation parameter should be in meters.
dist_param = dist.m
else:
@@ -97,7 +104,7 @@ class GeometryField(SpatialBackend.Field):
dist_param = dist
# Sphereical distance query; returning meters.
- if SpatialBackend.name == 'postgis' and self._unit_name in self.geodetic_units:
+ if postgis and self.geodetic and lookup_type != 'dwithin':
return [gqn(self._spheroid), dist_param]
else:
return [dist_param]
@@ -170,7 +177,7 @@ class GeometryField(SpatialBackend.Field):
if isinstance(value, (tuple, list)):
if lookup_type in SpatialBackend.distance_functions:
# Getting the distance parameter in the units of the field.
- where += self.get_distance(value[1])
+ where += self.get_distance(value[1], lookup_type)
elif lookup_type in SpatialBackend.limited_where:
pass
else:
diff --git a/django/contrib/gis/tests/distapp/tests.py b/django/contrib/gis/tests/distapp/tests.py
index 0f5b0349dd..3bedb19b4c 100644
--- a/django/contrib/gis/tests/distapp/tests.py
+++ b/django/contrib/gis/tests/distapp/tests.py
@@ -17,7 +17,9 @@ class DistanceTest(unittest.TestCase):
# the coordinate system of the field, EPSG:32140 (Texas South Central
# w/units in meters)
stx_pnt = GEOSGeometry('POINT (-95.370401017314293 29.704867409475465)', 4326)
-
+ # Another one for Australia
+ au_pnt = GEOSGeometry('POINT (150.791 -34.4919)', 4326)
+
def get_cities(self, qs):
cities = [c.name for c in qs]
cities.sort()
@@ -43,8 +45,17 @@ class DistanceTest(unittest.TestCase):
dists = [7000, D(km=7), D(mi=4.349)]
for dist in dists:
qs = SouthTexasCity.objects.filter(point__dwithin=(self.stx_pnt, dist))
- cities = self.get_cities(qs)
- self.assertEqual(cities, ['Downtown Houston', 'Southside Place'])
+ self.assertEqual(['Downtown Houston', 'Southside Place'], self.get_cities(qs))
+
+ if isinstance(dist, D):
+ # A TypeError should be raised when trying to pass Distance objects
+ # into a DWithin query using a geodetic field.
+ qs = AustraliaCity.objects.filter(point__dwithin=(self.au_pnt, dist))
+ self.assertRaises(TypeError, qs.count)
+ else:
+ # Actually using a distance value of 0.5 degrees.
+ qs = AustraliaCity.objects.filter(point__dwithin=(self.au_pnt, 0.5)).order_by('name')
+ self.assertEqual(['Mittagong', 'Shellharbour', 'Thirroul', 'Wollongong'], self.get_cities(qs))
def test03_distance_aggregate(self):
"Testing the `distance` GeoQuerySet method."