--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/lib/django/contrib/gis/db/backends/base.py Tue May 25 02:43:45 2010 +0200
@@ -0,0 +1,336 @@
+"""
+Base/mixin classes for the spatial backend database operations and the
+`SpatialRefSys` model the backend.
+"""
+import re
+from django.conf import settings
+from django.contrib.gis import gdal
+
+class BaseSpatialOperations(object):
+ """
+ This module holds the base `BaseSpatialBackend` object, which is
+ instantiated by each spatial database backend with the features
+ it has.
+ """
+ distance_functions = {}
+ geometry_functions = {}
+ geometry_operators = {}
+ geography_operators = {}
+ geography_functions = {}
+ gis_terms = {}
+ truncate_params = {}
+
+ # Quick booleans for the type of this spatial backend, and
+ # an attribute for the spatial database version tuple (if applicable)
+ postgis = False
+ spatialite = False
+ mysql = False
+ oracle = False
+ spatial_version = None
+
+ # How the geometry column should be selected.
+ select = None
+
+ # Does the spatial database have a geography type?
+ geography = False
+
+ area = False
+ centroid = False
+ difference = False
+ distance = False
+ distance_sphere = False
+ distance_spheroid = False
+ envelope = False
+ force_rhr = False
+ mem_size = False
+ bounding_circle = False
+ num_geom = False
+ num_points = False
+ perimeter = False
+ perimeter3d = False
+ point_on_surface = False
+ polygonize = False
+ reverse = False
+ scale = False
+ snap_to_grid = False
+ sym_difference = False
+ transform = False
+ translate = False
+ union = False
+
+ # Aggregates
+ collect = False
+ extent = False
+ extent3d = False
+ make_line = False
+ unionagg = False
+
+ # Serialization
+ geohash = False
+ geojson = False
+ gml = False
+ kml = False
+ svg = False
+
+ # Constructors
+ from_text = False
+ from_wkb = False
+
+ # Default conversion functions for aggregates; will be overridden if implemented
+ # for the spatial backend.
+ def convert_extent(self, box):
+ raise NotImplementedError('Aggregate extent not implemented for this spatial backend.')
+
+ def convert_extent3d(self, box):
+ raise NotImplementedError('Aggregate 3D extent not implemented for this spatial backend.')
+
+ def convert_geom(self, geom_val, geom_field):
+ raise NotImplementedError('Aggregate method not implemented for this spatial backend.')
+
+ # For quoting column values, rather than columns.
+ def geo_quote_name(self, name):
+ if isinstance(name, unicode):
+ name = name.encode('ascii')
+ return "'%s'" % name
+
+ # GeometryField operations
+ def geo_db_type(self, f):
+ """
+ Returns the database column type for the geometry field on
+ the spatial backend.
+ """
+ raise NotImplementedError
+
+ def get_distance(self, f, value, lookup_type):
+ """
+ Returns the distance parameters for the given geometry field,
+ lookup value, and lookup type.
+ """
+ raise NotImplementedError('Distance operations not available on this spatial backend.')
+
+ def get_geom_placeholder(self, f, value):
+ """
+ Returns the placeholder for the given geometry field with the given
+ value. Depending on the spatial backend, the placeholder may contain a
+ stored procedure call to the transformation function of the spatial
+ backend.
+ """
+ raise NotImplementedError
+
+ # Spatial SQL Construction
+ def spatial_aggregate_sql(self, agg):
+ raise NotImplementedError('Aggregate support not implemented for this spatial backend.')
+
+ def spatial_lookup_sql(self, lvalue, lookup_type, value, field):
+ 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):
+ """
+ The SpatialRefSysMixin is a class used by the database-dependent
+ SpatialRefSys objects to reduce redundnant code.
+ """
+ # For pulling out the spheroid from the spatial reference string. This
+ # regular expression is used only if the user does not have GDAL installed.
+ # TODO: Flattening not used in all ellipsoids, could also be a minor axis,
+ # or 'b' parameter.
+ spheroid_regex = re.compile(r'.+SPHEROID\[\"(?P<name>.+)\",(?P<major>\d+(\.\d+)?),(?P<flattening>\d{3}\.\d+),')
+
+ # For pulling out the units on platforms w/o GDAL installed.
+ # TODO: Figure out how to pull out angular units of projected coordinate system and
+ # fix for LOCAL_CS types. GDAL should be highly recommended for performing
+ # distance queries.
+ units_regex = re.compile(r'.+UNIT ?\["(?P<unit_name>[\w \'\(\)]+)", ?(?P<unit>[\d\.]+)(,AUTHORITY\["(?P<unit_auth_name>[\w \'\(\)]+)","(?P<unit_auth_val>\d+)"\])?\]([\w ]+)?(,AUTHORITY\["(?P<auth_name>[\w \'\(\)]+)","(?P<auth_val>\d+)"\])?\]$')
+
+ @property
+ def srs(self):
+ """
+ Returns a GDAL SpatialReference object, if GDAL is installed.
+ """
+ if gdal.HAS_GDAL:
+ # TODO: Is caching really necessary here? Is complexity worth it?
+ if hasattr(self, '_srs'):
+ # Returning a clone of the cached SpatialReference object.
+ return self._srs.clone()
+ else:
+ # Attempting to cache a SpatialReference object.
+
+ # Trying to get from WKT first.
+ try:
+ self._srs = gdal.SpatialReference(self.wkt)
+ return self.srs
+ except Exception, msg:
+ pass
+
+ try:
+ self._srs = gdal.SpatialReference(self.proj4text)
+ return self.srs
+ except Exception, msg:
+ pass
+
+ raise Exception('Could not get OSR SpatialReference from WKT: %s\nError:\n%s' % (self.wkt, msg))
+ else:
+ raise Exception('GDAL is not installed.')
+
+ @property
+ def ellipsoid(self):
+ """
+ Returns a tuple of the ellipsoid parameters:
+ (semimajor axis, semiminor axis, and inverse flattening).
+ """
+ if gdal.HAS_GDAL:
+ return self.srs.ellipsoid
+ else:
+ m = self.spheroid_regex.match(self.wkt)
+ if m: return (float(m.group('major')), float(m.group('flattening')))
+ else: return None
+
+ @property
+ def name(self):
+ "Returns the projection name."
+ return self.srs.name
+
+ @property
+ def spheroid(self):
+ "Returns the spheroid name for this spatial reference."
+ return self.srs['spheroid']
+
+ @property
+ def datum(self):
+ "Returns the datum for this spatial reference."
+ return self.srs['datum']
+
+ @property
+ def projected(self):
+ "Is this Spatial Reference projected?"
+ if gdal.HAS_GDAL:
+ return self.srs.projected
+ else:
+ return self.wkt.startswith('PROJCS')
+
+ @property
+ def local(self):
+ "Is this Spatial Reference local?"
+ if gdal.HAS_GDAL:
+ return self.srs.local
+ else:
+ return self.wkt.startswith('LOCAL_CS')
+
+ @property
+ def geographic(self):
+ "Is this Spatial Reference geographic?"
+ if gdal.HAS_GDAL:
+ return self.srs.geographic
+ else:
+ return self.wkt.startswith('GEOGCS')
+
+ @property
+ def linear_name(self):
+ "Returns the linear units name."
+ if gdal.HAS_GDAL:
+ return self.srs.linear_name
+ elif self.geographic:
+ return None
+ else:
+ m = self.units_regex.match(self.wkt)
+ return m.group('unit_name')
+
+ @property
+ def linear_units(self):
+ "Returns the linear units."
+ if gdal.HAS_GDAL:
+ return self.srs.linear_units
+ elif self.geographic:
+ return None
+ else:
+ m = self.units_regex.match(self.wkt)
+ return m.group('unit')
+
+ @property
+ def angular_name(self):
+ "Returns the name of the angular units."
+ if gdal.HAS_GDAL:
+ return self.srs.angular_name
+ elif self.projected:
+ return None
+ else:
+ m = self.units_regex.match(self.wkt)
+ return m.group('unit_name')
+
+ @property
+ def angular_units(self):
+ "Returns the angular units."
+ if gdal.HAS_GDAL:
+ return self.srs.angular_units
+ elif self.projected:
+ return None
+ else:
+ m = self.units_regex.match(self.wkt)
+ return m.group('unit')
+
+ @property
+ def units(self):
+ "Returns a tuple of the units and the name."
+ if self.projected or self.local:
+ return (self.linear_units, self.linear_name)
+ elif self.geographic:
+ return (self.angular_units, self.angular_name)
+ else:
+ return (None, None)
+
+ @classmethod
+ def get_units(cls, wkt):
+ """
+ Class method used by GeometryField on initialization to
+ retrive the units on the given WKT, without having to use
+ any of the database fields.
+ """
+ if gdal.HAS_GDAL:
+ return gdal.SpatialReference(wkt).units
+ else:
+ m = cls.units_regex.match(wkt)
+ return m.group('unit'), m.group('unit_name')
+
+ @classmethod
+ def get_spheroid(cls, wkt, string=True):
+ """
+ Class method used by GeometryField on initialization to
+ retrieve the `SPHEROID[..]` parameters from the given WKT.
+ """
+ if gdal.HAS_GDAL:
+ srs = gdal.SpatialReference(wkt)
+ sphere_params = srs.ellipsoid
+ sphere_name = srs['spheroid']
+ else:
+ m = cls.spheroid_regex.match(wkt)
+ if m:
+ sphere_params = (float(m.group('major')), float(m.group('flattening')))
+ sphere_name = m.group('name')
+ else:
+ return None
+
+ if not string:
+ return sphere_name, sphere_params
+ else:
+ # `string` parameter used to place in format acceptable by PostGIS
+ if len(sphere_params) == 3:
+ radius, flattening = sphere_params[0], sphere_params[2]
+ else:
+ radius, flattening = sphere_params
+ return 'SPHEROID["%s",%s,%s]' % (sphere_name, radius, flattening)
+
+ def __unicode__(self):
+ """
+ Returns the string representation. If GDAL is installed,
+ it will be 'pretty' OGC WKT.
+ """
+ try:
+ return unicode(self.srs)
+ except:
+ return unicode(self.wkt)