Fixed problems associated with the `GeometryField` SRID cache: it is now keyed by the connection alias rather than the spatial backend name (e.g., `spatial_ref_sys` tables may have different entries, even if backend is the same), explicitly pass in connection alias to `SpatialRefSys` query to ensure correct entry is retrieved, and removed unused code from `GeometryField.get_distance`. Thanks, Josh Livni, for the bug report.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@12258 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Justin Bronn 2010-01-18 21:49:13 +00:00
parent a1fbc0dc36
commit 6b659270d6
2 changed files with 27 additions and 48 deletions

View File

@ -122,6 +122,13 @@ class BaseSpatialOperations(object):
def spatial_lookup_sql(self, lvalue, lookup_type, value, field): def spatial_lookup_sql(self, lvalue, lookup_type, value, field):
raise NotImplmentedError raise NotImplmentedError
# Routines for getting the OGC-compliant models.
def geometry_columns(self):
raise NotImplementedError
def spatial_ref_sys(self):
raise NotImplementedError
class SpatialRefSysMixin(object): class SpatialRefSysMixin(object):
""" """
The SpatialRefSysMixin is a class used by the database-dependent The SpatialRefSysMixin is a class used by the database-dependent

View File

@ -1,45 +1,42 @@
from django.db.models.fields import Field from django.db.models.fields import Field
from django.db.models.sql.expressions import SQLEvaluator
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.contrib.gis import forms from django.contrib.gis import forms
from django.contrib.gis.db.models.proxy import GeometryProxy from django.contrib.gis.db.models.proxy import GeometryProxy
from django.contrib.gis.geometry.backend import Geometry, GeometryException from django.contrib.gis.geometry.backend import Geometry, GeometryException
from django.contrib.gis.measure import Distance
from django.db.models.sql.expressions import SQLEvaluator
# Local cache of the spatial_ref_sys table, which holds static data. # Local cache of the spatial_ref_sys table, which holds SRID data for each
# This exists so that we don't have to hit the database each time # spatial database alias. This cache exists so that the database isn't queried
# we construct a distance query. # for SRID info each time a distance query is constructed.
_srid_cache = {'postgis' : {}, _srid_cache = {}
'oracle' : {},
'spatialite' : {},
}
def get_srid_info(srid, connection): def get_srid_info(srid, connection):
""" """
Returns the units, unit name, and spheroid WKT associated with the Returns the units, unit name, and spheroid WKT associated with the
given SRID from the `spatial_ref_sys` (or equivalent) spatial database given SRID from the `spatial_ref_sys` (or equivalent) spatial database
table. These results are cached. table for the given database connection. These results are cached.
""" """
global _srid_cache global _srid_cache
# No `spatial_ref_sys` table in MySQL. try:
if connection.ops.mysql: # The SpatialRefSys model for the spatial backend.
SpatialRefSys = connection.ops.spatial_ref_sys()
except NotImplementedError:
# No `spatial_ref_sys` table in spatial backend (e.g., MySQL).
return None, None, None return None, None, None
name = connection.ops.name if not connection.alias in _srid_cache:
if not srid in _srid_cache[name]: # Initialize SRID dictionary for database if it doesn't exist.
if connection.ops.postgis: _srid_cache[connection.alias] = {}
from django.contrib.gis.db.backends.postgis.models import SpatialRefSys
elif connection.ops.oracle: if not srid in _srid_cache[connection.alias]:
from django.contrib.gis.db.backends.oracle.models import SpatialRefSys # Use `SpatialRefSys` model to query for spatial reference info.
elif connection.ops.spatialite: sr = SpatialRefSys.objects.using(connection.alias).get(srid=srid)
from django.contrib.gis.db.backends.spatialite.models import SpatialRefSys
sr = SpatialRefSys.objects.get(srid=srid)
units, units_name = sr.units units, units_name = sr.units
spheroid = SpatialRefSys.get_spheroid(sr.wkt) spheroid = SpatialRefSys.get_spheroid(sr.wkt)
_srid_cache[name][srid] = (units, units_name, spheroid) _srid_cache[connection.alias][srid] = (units, units_name, spheroid)
return _srid_cache[name][srid] return _srid_cache[connection.alias][srid]
class GeometryField(Field): class GeometryField(Field):
"The base GIS field -- maps to the OpenGIS Specification Geometry type." "The base GIS field -- maps to the OpenGIS Specification Geometry type."
@ -141,31 +138,6 @@ class GeometryField(Field):
""" """
return connection.ops.get_distance(self, value, lookup_type) return connection.ops.get_distance(self, value, lookup_type)
if isinstance(dist, Distance):
if self.geodetic(connection):
# Won't allow Distance objects w/DWithin lookups on PostGIS.
if connection.ops.postgis and lookup_type == 'dwithin':
raise ValueError('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:
dist_param = getattr(dist, Distance.unit_attname(self.units_name(connection)))
else:
# Assuming the distance is in the units of the field.
dist_param = dist
if connection.ops.oracle and lookup_type == 'dwithin':
dist_param = 'distance=%s' % dist_param
if connection.ops.postgis and self.geodetic(connection) and lookup_type != 'dwithin' and option == 'spheroid':
# On PostGIS, by default `ST_distance_sphere` is used; but if the
# accuracy of `ST_distance_spheroid` is needed than the spheroid
# needs to be passed to the SQL stored procedure.
return [self._spheroid, dist_param]
else:
return [dist_param]
def get_prep_value(self, value): def get_prep_value(self, value):
""" """
Spatial lookup values are either a parameter that is (or may be Spatial lookup values are either a parameter that is (or may be