| """ |
| Imports the SpatialRefSys and GeometryColumns models dependent on the |
| spatial database backend. |
| """ |
| import re |
| from django.conf import settings |
| |
| # Checking for the presence of GDAL (needed for the SpatialReference object) |
| from django.contrib.gis.gdal import HAS_GDAL, PYTHON23 |
| if HAS_GDAL: |
| from django.contrib.gis.gdal import SpatialReference |
| |
| 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+)"\])?\]$') |
| |
| def srs(self): |
| """ |
| Returns a GDAL SpatialReference object, if GDAL is installed. |
| """ |
| if HAS_GDAL: |
| 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 = SpatialReference(self.wkt) |
| 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.') |
| srs = property(srs) |
| |
| def ellipsoid(self): |
| """ |
| Returns a tuple of the ellipsoid parameters: |
| (semimajor axis, semiminor axis, and inverse flattening). |
| """ |
| if 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 |
| ellipsoid = property(ellipsoid) |
| |
| def name(self): |
| "Returns the projection name." |
| return self.srs.name |
| name = property(name) |
| |
| def spheroid(self): |
| "Returns the spheroid name for this spatial reference." |
| return self.srs['spheroid'] |
| spheroid = property(spheroid) |
| |
| def datum(self): |
| "Returns the datum for this spatial reference." |
| return self.srs['datum'] |
| datum = property(datum) |
| |
| def projected(self): |
| "Is this Spatial Reference projected?" |
| if HAS_GDAL: |
| return self.srs.projected |
| else: |
| return self.wkt.startswith('PROJCS') |
| projected = property(projected) |
| |
| def local(self): |
| "Is this Spatial Reference local?" |
| if HAS_GDAL: |
| return self.srs.local |
| else: |
| return self.wkt.startswith('LOCAL_CS') |
| local = property(local) |
| |
| def geographic(self): |
| "Is this Spatial Reference geographic?" |
| if HAS_GDAL: |
| return self.srs.geographic |
| else: |
| return self.wkt.startswith('GEOGCS') |
| geographic = property(geographic) |
| |
| def linear_name(self): |
| "Returns the linear units name." |
| if 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') |
| linear_name = property(linear_name) |
| |
| def linear_units(self): |
| "Returns the linear units." |
| if HAS_GDAL: |
| return self.srs.linear_units |
| elif self.geographic: |
| return None |
| else: |
| m = self.units_regex.match(self.wkt) |
| return m.group('unit') |
| linear_units = property(linear_units) |
| |
| def angular_name(self): |
| "Returns the name of the angular units." |
| if 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') |
| angular_name = property(angular_name) |
| |
| def angular_units(self): |
| "Returns the angular units." |
| if HAS_GDAL: |
| return self.srs.angular_units |
| elif self.projected: |
| return None |
| else: |
| m = self.units_regex.match(self.wkt) |
| return m.group('unit') |
| angular_units = property(angular_units) |
| |
| 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) |
| units = property(units) |
| |
| 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 HAS_GDAL: |
| return SpatialReference(wkt).units |
| else: |
| m = cls.units_regex.match(wkt) |
| return m.group('unit'), m.group('unit_name') |
| get_units = classmethod(get_units) |
| |
| def get_spheroid(cls, wkt, string=True): |
| """ |
| Class method used by GeometryField on initialization to |
| retrieve the `SPHEROID[..]` parameters from the given WKT. |
| """ |
| if HAS_GDAL: |
| srs = 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) |
| get_spheroid = classmethod(get_spheroid) |
| |
| 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) |
| |
| # The SpatialRefSys and GeometryColumns models |
| _srid_info = True |
| if not PYTHON23 and settings.DATABASE_ENGINE == 'postgresql_psycopg2': |
| # Because the PostGIS version is checked when initializing the spatial |
| # backend a `ProgrammingError` will be raised if the PostGIS tables |
| # and functions are not installed. We catch here so it won't be raised when |
| # running the Django test suite. `ImportError` is also possible if no ctypes. |
| try: |
| from django.contrib.gis.db.backend.postgis.models import GeometryColumns, SpatialRefSys |
| except: |
| _srid_info = False |
| elif not PYTHON23 and settings.DATABASE_ENGINE == 'oracle': |
| # Same thing as above, except the GEOS library is attempted to be loaded for |
| # `BaseSpatialBackend`, and an exception will be raised during the |
| # Django test suite if it doesn't exist. |
| try: |
| from django.contrib.gis.db.backend.oracle.models import GeometryColumns, SpatialRefSys |
| except: |
| _srid_info = False |
| else: |
| _srid_info = False |
| |
| if _srid_info: |
| def get_srid_info(srid): |
| """ |
| Returns the units, unit name, and spheroid WKT associated with the |
| given SRID from the `spatial_ref_sys` (or equivalent) spatial database |
| table. We use a database cursor to execute the query because this |
| function is used when it is not possible to use the ORM (for example, |
| during field initialization). |
| """ |
| # SRID=-1 is a common convention for indicating the geometry has no |
| # spatial reference information associated with it. Thus, we will |
| # return all None values without raising an exception. |
| if srid == -1: return None, None, None |
| |
| # Getting the spatial reference WKT associated with the SRID from the |
| # `spatial_ref_sys` (or equivalent) spatial database table. This query |
| # cannot be executed using the ORM because this information is needed |
| # when the ORM cannot be used (e.g., during the initialization of |
| # `GeometryField`). |
| from django.db import connection |
| cur = connection.cursor() |
| qn = connection.ops.quote_name |
| stmt = 'SELECT %(table)s.%(wkt_col)s FROM %(table)s WHERE (%(table)s.%(srid_col)s = %(srid)s)' |
| stmt = stmt % {'table' : qn(SpatialRefSys._meta.db_table), |
| 'wkt_col' : qn(SpatialRefSys.wkt_col()), |
| 'srid_col' : qn('srid'), |
| 'srid' : srid, |
| } |
| cur.execute(stmt) |
| |
| # Fetching the WKT from the cursor; if the query failed raise an Exception. |
| fetched = cur.fetchone() |
| if not fetched: |
| raise ValueError('Failed to find spatial reference entry in "%s" corresponding to SRID=%s.' % |
| (SpatialRefSys._meta.db_table, srid)) |
| srs_wkt = fetched[0] |
| |
| # Getting metadata associated with the spatial reference system identifier. |
| # Specifically, getting the unit information and spheroid information |
| # (both required for distance queries). |
| unit, unit_name = SpatialRefSys.get_units(srs_wkt) |
| spheroid = SpatialRefSys.get_spheroid(srs_wkt) |
| return unit, unit_name, spheroid |
| else: |
| def get_srid_info(srid): |
| """ |
| Dummy routine for the backends that do not have the OGC required |
| spatial metadata tables (like MySQL). |
| """ |
| return None, None, None |
| |