--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/lib/django/contrib/gis/db/models/fields.py Tue May 25 02:43:45 2010 +0200
@@ -0,0 +1,294 @@
+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.contrib.gis import forms
+from django.contrib.gis.db.models.proxy import GeometryProxy
+from django.contrib.gis.geometry.backend import Geometry, GeometryException
+
+# Local cache of the spatial_ref_sys table, which holds SRID data for each
+# spatial database alias. This cache exists so that the database isn't queried
+# for SRID info each time a distance query is constructed.
+_srid_cache = {}
+
+def get_srid_info(srid, connection):
+ """
+ Returns the units, unit name, and spheroid WKT associated with the
+ given SRID from the `spatial_ref_sys` (or equivalent) spatial database
+ table for the given database connection. These results are cached.
+ """
+ global _srid_cache
+
+ try:
+ # 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
+
+ if not connection.alias in _srid_cache:
+ # Initialize SRID dictionary for database if it doesn't exist.
+ _srid_cache[connection.alias] = {}
+
+ if not srid in _srid_cache[connection.alias]:
+ # Use `SpatialRefSys` model to query for spatial reference info.
+ sr = SpatialRefSys.objects.using(connection.alias).get(srid=srid)
+ units, units_name = sr.units
+ spheroid = SpatialRefSys.get_spheroid(sr.wkt)
+ _srid_cache[connection.alias][srid] = (units, units_name, spheroid)
+
+ return _srid_cache[connection.alias][srid]
+
+class GeometryField(Field):
+ "The base GIS field -- maps to the OpenGIS Specification Geometry type."
+
+ # The OpenGIS Geometry name.
+ geom_type = 'GEOMETRY'
+
+ # Geodetic units.
+ geodetic_units = ('Decimal Degree', 'degree')
+
+ description = _("The base GIS field -- maps to the OpenGIS Specification Geometry type.")
+
+ def __init__(self, verbose_name=None, srid=4326, spatial_index=True, dim=2,
+ geography=False, **kwargs):
+ """
+ The initialization function for geometry fields. Takes the following
+ as keyword arguments:
+
+ srid:
+ The spatial reference system identifier, an OGC standard.
+ Defaults to 4326 (WGS84).
+
+ spatial_index:
+ Indicates whether to create a spatial index. Defaults to True.
+ Set this instead of 'db_index' for geographic fields since index
+ creation is different for geometry columns.
+
+ dim:
+ The number of dimensions for this geometry. Defaults to 2.
+
+ extent:
+ Customize the extent, in a 4-tuple of WGS 84 coordinates, for the
+ geometry field entry in the `USER_SDO_GEOM_METADATA` table. Defaults
+ to (-180.0, -90.0, 180.0, 90.0).
+
+ tolerance:
+ Define the tolerance, in meters, to use for the geometry field
+ entry in the `USER_SDO_GEOM_METADATA` table. Defaults to 0.05.
+ """
+
+ # Setting the index flag with the value of the `spatial_index` keyword.
+ self.spatial_index = spatial_index
+
+ # Setting the SRID and getting the units. Unit information must be
+ # easily available in the field instance for distance queries.
+ self.srid = srid
+
+ # Setting the dimension of the geometry field.
+ self.dim = dim
+
+ # Setting the verbose_name keyword argument with the positional
+ # first parameter, so this works like normal fields.
+ kwargs['verbose_name'] = verbose_name
+
+ # Is this a geography rather than a geometry column?
+ self.geography = geography
+
+ # Oracle-specific private attributes for creating the entrie in
+ # `USER_SDO_GEOM_METADATA`
+ self._extent = kwargs.pop('extent', (-180.0, -90.0, 180.0, 90.0))
+ self._tolerance = kwargs.pop('tolerance', 0.05)
+
+ super(GeometryField, self).__init__(**kwargs)
+
+ # The following functions are used to get the units, their name, and
+ # the spheroid corresponding to the SRID of the GeometryField.
+ def _get_srid_info(self, connection):
+ # Get attributes from `get_srid_info`.
+ self._units, self._units_name, self._spheroid = get_srid_info(self.srid, connection)
+
+ def spheroid(self, connection):
+ if not hasattr(self, '_spheroid'):
+ self._get_srid_info(connection)
+ return self._spheroid
+
+ def units(self, connection):
+ if not hasattr(self, '_units'):
+ self._get_srid_info(connection)
+ return self._units
+
+ def units_name(self, connection):
+ if not hasattr(self, '_units_name'):
+ self._get_srid_info(connection)
+ return self._units_name
+
+ ### Routines specific to GeometryField ###
+ def geodetic(self, connection):
+ """
+ Returns true if this field's SRID corresponds with a coordinate
+ system that uses non-projected units (e.g., latitude/longitude).
+ """
+ return self.units_name(connection) in self.geodetic_units
+
+ def get_distance(self, value, lookup_type, connection):
+ """
+ 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.
+ """
+ return connection.ops.get_distance(self, value, lookup_type)
+
+ def get_prep_value(self, value):
+ """
+ Spatial lookup values are either a parameter that is (or may be
+ converted to) a geometry, or a sequence of lookup values that
+ begins with a geometry. This routine will setup the geometry
+ value properly, and preserve any other lookup parameters before
+ returning to the caller.
+ """
+ if isinstance(value, SQLEvaluator):
+ return value
+ elif isinstance(value, (tuple, list)):
+ geom = value[0]
+ seq_value = True
+ else:
+ geom = value
+ seq_value = False
+
+ # When the input is not a GEOS geometry, attempt to construct one
+ # from the given string input.
+ if isinstance(geom, Geometry):
+ pass
+ elif isinstance(geom, basestring) or hasattr(geom, '__geo_interface__'):
+ try:
+ geom = Geometry(geom)
+ except GeometryException:
+ raise ValueError('Could not create geometry from lookup value.')
+ else:
+ raise ValueError('Cannot use object with type %s for a geometry lookup parameter.' % type(geom).__name__)
+
+ # Assigning the SRID value.
+ geom.srid = self.get_srid(geom)
+
+ if seq_value:
+ lookup_val = [geom]
+ lookup_val.extend(value[1:])
+ return tuple(lookup_val)
+ else:
+ return geom
+
+ def get_srid(self, geom):
+ """
+ Returns the default SRID for the given geometry, taking into account
+ the SRID set for the field. For example, if the input geometry
+ has no SRID, then that of the field will be returned.
+ """
+ gsrid = geom.srid # SRID of given geometry.
+ if gsrid is None or self.srid == -1 or (gsrid == -1 and self.srid != -1):
+ return self.srid
+ else:
+ return gsrid
+
+ ### Routines overloaded from Field ###
+ def contribute_to_class(self, cls, name):
+ super(GeometryField, self).contribute_to_class(cls, name)
+
+ # Setup for lazy-instantiated Geometry object.
+ setattr(cls, self.attname, GeometryProxy(Geometry, self))
+
+ def db_type(self, connection):
+ return connection.ops.geo_db_type(self)
+
+ def formfield(self, **kwargs):
+ defaults = {'form_class' : forms.GeometryField,
+ 'null' : self.null,
+ 'geom_type' : self.geom_type,
+ 'srid' : self.srid,
+ }
+ defaults.update(kwargs)
+ return super(GeometryField, self).formfield(**defaults)
+
+ def get_db_prep_lookup(self, lookup_type, value, connection, prepared=False):
+ """
+ Prepare for the database lookup, and return any spatial parameters
+ necessary for the query. This includes wrapping any geometry
+ parameters with a backend-specific adapter and formatting any distance
+ parameters into the correct units for the coordinate system of the
+ field.
+ """
+ if lookup_type in connection.ops.gis_terms:
+ # special case for isnull lookup
+ if lookup_type == 'isnull':
+ return []
+
+ # Populating the parameters list, and wrapping the Geometry
+ # with the Adapter of the spatial backend.
+ if isinstance(value, (tuple, list)):
+ params = [connection.ops.Adapter(value[0])]
+ if lookup_type in connection.ops.distance_functions:
+ # Getting the distance parameter in the units of the field.
+ params += self.get_distance(value[1:], lookup_type, connection)
+ elif lookup_type in connection.ops.truncate_params:
+ # Lookup is one where SQL parameters aren't needed from the
+ # given lookup value.
+ pass
+ else:
+ params += value[1:]
+ elif isinstance(value, SQLEvaluator):
+ params = []
+ else:
+ params = [connection.ops.Adapter(value)]
+
+ return params
+ else:
+ raise ValueError('%s is not a valid spatial lookup for %s.' %
+ (lookup_type, self.__class__.__name__))
+
+ def get_prep_lookup(self, lookup_type, value):
+ if lookup_type == 'isnull':
+ return bool(value)
+ else:
+ return self.get_prep_value(value)
+
+ def get_db_prep_save(self, value, connection):
+ "Prepares the value for saving in the database."
+ if value is None:
+ return None
+ else:
+ return connection.ops.Adapter(self.get_prep_value(value))
+
+ def get_placeholder(self, value, connection):
+ """
+ Returns the placeholder for the geometry column for the
+ given value.
+ """
+ return connection.ops.get_geom_placeholder(self, value)
+
+# The OpenGIS Geometry Type Fields
+class PointField(GeometryField):
+ geom_type = 'POINT'
+ description = _("Point")
+
+class LineStringField(GeometryField):
+ geom_type = 'LINESTRING'
+ description = _("Line string")
+
+class PolygonField(GeometryField):
+ geom_type = 'POLYGON'
+ description = _("Polygon")
+
+class MultiPointField(GeometryField):
+ geom_type = 'MULTIPOINT'
+ description = _("Multi-point")
+
+class MultiLineStringField(GeometryField):
+ geom_type = 'MULTILINESTRING'
+ description = _("Multi-line string")
+
+class MultiPolygonField(GeometryField):
+ geom_type = 'MULTIPOLYGON'
+ description = _("Multi polygon")
+
+class GeometryCollectionField(GeometryField):
+ geom_type = 'GEOMETRYCOLLECTION'
+ description = _("Geometry collection")