1
0
mirror of https://github.com/django/django.git synced 2025-07-04 01:39:20 +00:00

gis: Fixed bug where extra parameters were passed into SQL for dwithin lookups for geodetic fields; now degree values (no Distance objects) may be used with dwithin lookups.

git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@7456 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Justin Bronn 2008-04-24 23:01:04 +00:00
parent 1731135661
commit 115d4170ae
3 changed files with 27 additions and 9 deletions

View File

@ -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.')

View File

@ -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:

View File

@ -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."