PageRenderTime 56ms CodeModel.GetById 29ms app.highlight 22ms RepoModel.GetById 1ms app.codeStats 0ms

/django/contrib/gis/db/backends/base.py

https://code.google.com/p/mango-py/
Python | 336 lines | 224 code | 41 blank | 71 comment | 20 complexity | 90b150e3540bb96a490a46c5e38b6202 MD5 | raw file
  1"""
  2Base/mixin classes for the spatial backend database operations and the
  3`SpatialRefSys` model the backend.
  4"""
  5import re
  6from django.conf import settings
  7from django.contrib.gis import gdal
  8
  9class BaseSpatialOperations(object):
 10    """
 11    This module holds the base `BaseSpatialBackend` object, which is
 12    instantiated by each spatial database backend with the features
 13    it has.
 14    """
 15    distance_functions = {}
 16    geometry_functions = {}
 17    geometry_operators = {}
 18    geography_operators = {}
 19    geography_functions = {}
 20    gis_terms = {}
 21    truncate_params = {}
 22
 23    # Quick booleans for the type of this spatial backend, and
 24    # an attribute for the spatial database version tuple (if applicable)
 25    postgis = False
 26    spatialite = False
 27    mysql = False
 28    oracle = False
 29    spatial_version = None
 30
 31    # How the geometry column should be selected.
 32    select = None
 33
 34    # Does the spatial database have a geography type?
 35    geography = False
 36
 37    area = False
 38    centroid = False
 39    difference = False
 40    distance = False
 41    distance_sphere = False
 42    distance_spheroid = False
 43    envelope = False
 44    force_rhr = False
 45    mem_size = False
 46    bounding_circle = False
 47    num_geom = False
 48    num_points = False
 49    perimeter = False
 50    perimeter3d = False
 51    point_on_surface = False
 52    polygonize = False
 53    reverse = False
 54    scale = False
 55    snap_to_grid = False
 56    sym_difference = False
 57    transform = False
 58    translate = False
 59    union = False
 60
 61    # Aggregates
 62    collect = False
 63    extent = False
 64    extent3d = False
 65    make_line = False
 66    unionagg = False
 67
 68    # Serialization
 69    geohash = False
 70    geojson = False
 71    gml = False
 72    kml = False
 73    svg = False
 74
 75    # Constructors
 76    from_text = False
 77    from_wkb = False
 78
 79    # Default conversion functions for aggregates; will be overridden if implemented
 80    # for the spatial backend.
 81    def convert_extent(self, box):
 82        raise NotImplementedError('Aggregate extent not implemented for this spatial backend.')
 83
 84    def convert_extent3d(self, box):
 85        raise NotImplementedError('Aggregate 3D extent not implemented for this spatial backend.')
 86
 87    def convert_geom(self, geom_val, geom_field):
 88        raise NotImplementedError('Aggregate method not implemented for this spatial backend.')
 89
 90    # For quoting column values, rather than columns.
 91    def geo_quote_name(self, name):
 92        if isinstance(name, unicode):
 93            name = name.encode('ascii')
 94        return "'%s'" % name
 95
 96    # GeometryField operations
 97    def geo_db_type(self, f):
 98        """
 99        Returns the database column type for the geometry field on
100        the spatial backend.
101        """
102        raise NotImplementedError
103
104    def get_distance(self, f, value, lookup_type):
105        """
106        Returns the distance parameters for the given geometry field,
107        lookup value, and lookup type.
108        """
109        raise NotImplementedError('Distance operations not available on this spatial backend.')
110
111    def get_geom_placeholder(self, f, value):
112        """
113        Returns the placeholder for the given geometry field with the given
114        value.  Depending on the spatial backend, the placeholder may contain a
115        stored procedure call to the transformation function of the spatial
116        backend.
117        """
118        raise NotImplementedError
119
120    # Spatial SQL Construction
121    def spatial_aggregate_sql(self, agg):
122        raise NotImplementedError('Aggregate support not implemented for this spatial backend.')
123
124    def spatial_lookup_sql(self, lvalue, lookup_type, value, field):
125        raise NotImplementedError
126
127    # Routines for getting the OGC-compliant models.
128    def geometry_columns(self):
129        raise NotImplementedError
130
131    def spatial_ref_sys(self):
132        raise NotImplementedError
133
134class SpatialRefSysMixin(object):
135    """
136    The SpatialRefSysMixin is a class used by the database-dependent
137    SpatialRefSys objects to reduce redundnant code.
138    """
139    # For pulling out the spheroid from the spatial reference string. This
140    # regular expression is used only if the user does not have GDAL installed.
141    # TODO: Flattening not used in all ellipsoids, could also be a minor axis,
142    # or 'b' parameter.
143    spheroid_regex = re.compile(r'.+SPHEROID\[\"(?P<name>.+)\",(?P<major>\d+(\.\d+)?),(?P<flattening>\d{3}\.\d+),')
144
145    # For pulling out the units on platforms w/o GDAL installed.
146    # TODO: Figure out how to pull out angular units of projected coordinate system and
147    # fix for LOCAL_CS types.  GDAL should be highly recommended for performing
148    # distance queries.
149    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+)"\])?\]$')
150
151    @property
152    def srs(self):
153        """
154        Returns a GDAL SpatialReference object, if GDAL is installed.
155        """
156        if gdal.HAS_GDAL:
157            # TODO: Is caching really necessary here?  Is complexity worth it?
158            if hasattr(self, '_srs'):
159                # Returning a clone of the cached SpatialReference object.
160                return self._srs.clone()
161            else:
162                # Attempting to cache a SpatialReference object.
163
164                # Trying to get from WKT first.
165                try:
166                    self._srs = gdal.SpatialReference(self.wkt)
167                    return self.srs
168                except Exception, msg:
169                    pass
170
171                try:
172                    self._srs = gdal.SpatialReference(self.proj4text)
173                    return self.srs
174                except Exception, msg:
175                    pass
176
177                raise Exception('Could not get OSR SpatialReference from WKT: %s\nError:\n%s' % (self.wkt, msg))
178        else:
179            raise Exception('GDAL is not installed.')
180
181    @property
182    def ellipsoid(self):
183        """
184        Returns a tuple of the ellipsoid parameters:
185        (semimajor axis, semiminor axis, and inverse flattening).
186        """
187        if gdal.HAS_GDAL:
188            return self.srs.ellipsoid
189        else:
190            m = self.spheroid_regex.match(self.wkt)
191            if m: return (float(m.group('major')), float(m.group('flattening')))
192            else: return None
193
194    @property
195    def name(self):
196        "Returns the projection name."
197        return self.srs.name
198
199    @property
200    def spheroid(self):
201        "Returns the spheroid name for this spatial reference."
202        return self.srs['spheroid']
203
204    @property
205    def datum(self):
206        "Returns the datum for this spatial reference."
207        return self.srs['datum']
208
209    @property
210    def projected(self):
211        "Is this Spatial Reference projected?"
212        if gdal.HAS_GDAL:
213            return self.srs.projected
214        else:
215            return self.wkt.startswith('PROJCS')
216
217    @property
218    def local(self):
219        "Is this Spatial Reference local?"
220        if gdal.HAS_GDAL:
221            return self.srs.local
222        else:
223            return self.wkt.startswith('LOCAL_CS')
224
225    @property
226    def geographic(self):
227        "Is this Spatial Reference geographic?"
228        if gdal.HAS_GDAL:
229            return self.srs.geographic
230        else:
231            return self.wkt.startswith('GEOGCS')
232
233    @property
234    def linear_name(self):
235        "Returns the linear units name."
236        if gdal.HAS_GDAL:
237            return self.srs.linear_name
238        elif self.geographic:
239            return None
240        else:
241            m = self.units_regex.match(self.wkt)
242            return m.group('unit_name')
243
244    @property
245    def linear_units(self):
246        "Returns the linear units."
247        if gdal.HAS_GDAL:
248            return self.srs.linear_units
249        elif self.geographic:
250            return None
251        else:
252            m = self.units_regex.match(self.wkt)
253            return m.group('unit')
254
255    @property
256    def angular_name(self):
257        "Returns the name of the angular units."
258        if gdal.HAS_GDAL:
259            return self.srs.angular_name
260        elif self.projected:
261            return None
262        else:
263            m = self.units_regex.match(self.wkt)
264            return m.group('unit_name')
265
266    @property
267    def angular_units(self):
268        "Returns the angular units."
269        if gdal.HAS_GDAL:
270            return self.srs.angular_units
271        elif self.projected:
272            return None
273        else:
274            m = self.units_regex.match(self.wkt)
275            return m.group('unit')
276
277    @property
278    def units(self):
279        "Returns a tuple of the units and the name."
280        if self.projected or self.local:
281            return (self.linear_units, self.linear_name)
282        elif self.geographic:
283            return (self.angular_units, self.angular_name)
284        else:
285            return (None, None)
286
287    @classmethod
288    def get_units(cls, wkt):
289        """
290        Class method used by GeometryField on initialization to
291        retrive the units on the given WKT, without having to use
292        any of the database fields.
293        """
294        if gdal.HAS_GDAL:
295            return gdal.SpatialReference(wkt).units
296        else:
297            m = cls.units_regex.match(wkt)
298            return m.group('unit'), m.group('unit_name')
299
300    @classmethod
301    def get_spheroid(cls, wkt, string=True):
302        """
303        Class method used by GeometryField on initialization to
304        retrieve the `SPHEROID[..]` parameters from the given WKT.
305        """
306        if gdal.HAS_GDAL:
307            srs = gdal.SpatialReference(wkt)
308            sphere_params = srs.ellipsoid
309            sphere_name = srs['spheroid']
310        else:
311            m = cls.spheroid_regex.match(wkt)
312            if m:
313                sphere_params = (float(m.group('major')), float(m.group('flattening')))
314                sphere_name = m.group('name')
315            else:
316                return None
317
318        if not string:
319            return sphere_name, sphere_params
320        else:
321            # `string` parameter used to place in format acceptable by PostGIS
322            if len(sphere_params) == 3:
323                radius, flattening = sphere_params[0], sphere_params[2]
324            else:
325                radius, flattening = sphere_params
326            return 'SPHEROID["%s",%s,%s]' % (sphere_name, radius, flattening)
327
328    def __unicode__(self):
329        """
330        Returns the string representation.  If GDAL is installed,
331        it will be 'pretty' OGC WKT.
332        """
333        try:
334            return unicode(self.srs)
335        except:
336            return unicode(self.wkt)