Django

Code

root/django/branches/gis/django/contrib/gis/models.py

Revision 8155, 10.1 kB (checked in by jbronn, 1 month ago)

gis: Now plays nice with the Django test suite if the spatial prerequisites aren't installed.

Line 
1 """
2  Imports the SpatialRefSys and GeometryColumns models dependent on the
3  spatial database backend.
4 """
5 import re
6 from django.conf import settings
7
8 # Checking for the presence of GDAL (needed for the SpatialReference object)
9 from django.contrib.gis.gdal import HAS_GDAL
10 if HAS_GDAL:
11     from django.contrib.gis.gdal import SpatialReference
12
13 class SpatialRefSysMixin(object):
14     """
15     The SpatialRefSysMixin is a class used by the database-dependent
16     SpatialRefSys objects to reduce redundnant code.
17     """
18
19     # For pulling out the spheroid from the spatial reference string. This
20     # regular expression is used only if the user does not have GDAL installed.
21     #  TODO: Flattening not used in all ellipsoids, could also be a minor axis, or 'b'
22     #        parameter.
23     spheroid_regex = re.compile(r'.+SPHEROID\[\"(?P<name>.+)\",(?P<major>\d+(\.\d+)?),(?P<flattening>\d{3}\.\d+),')
24
25     # For pulling out the units on platforms w/o GDAL installed.
26     # TODO: Figure out how to pull out angular units of projected coordinate system and
27     # fix for LOCAL_CS types.  GDAL should be highly recommended for performing
28     # distance queries.
29     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+)"\])?\]$')
30    
31     @property
32     def srs(self):
33         """
34         Returns a GDAL SpatialReference object, if GDAL is installed.
35         """
36         if HAS_GDAL:
37             if hasattr(self, '_srs'):
38                 # Returning a clone of the cached SpatialReference object.
39                 return self._srs.clone()
40             else:
41                 # Attempting to cache a SpatialReference object.
42
43                 # Trying to get from WKT first.
44                 try:
45                     self._srs = SpatialReference(self.wkt)
46                     return self.srs
47                 except Exception, msg:
48                     pass
49                
50                 raise Exception('Could not get OSR SpatialReference from WKT: %s\nError:\n%s' % (self.wkt, msg))
51         else:
52             raise Exception('GDAL is not installed.')
53
54     @property
55     def ellipsoid(self):
56         """
57         Returns a tuple of the ellipsoid parameters:
58         (semimajor axis, semiminor axis, and inverse flattening).
59         """
60         if HAS_GDAL:
61             return self.srs.ellipsoid
62         else:
63             m = self.spheroid_regex.match(self.wkt)
64             if m: return (float(m.group('major')), float(m.group('flattening')))
65             else: return None
66
67     @property
68     def name(self):
69         "Returns the projection name."
70         return self.srs.name
71
72     @property
73     def spheroid(self):
74         "Returns the spheroid name for this spatial reference."
75         return self.srs['spheroid']
76
77     @property
78     def datum(self):
79         "Returns the datum for this spatial reference."
80         return self.srs['datum']
81
82     @property
83     def projected(self):
84         "Is this Spatial Reference projected?"
85         if HAS_GDAL:
86             return self.srs.projected
87         else:
88             return self.wkt.startswith('PROJCS')
89
90     @property
91     def local(self):
92         "Is this Spatial Reference local?"
93         if HAS_GDAL:
94             return self.srs.local
95         else:
96             return self.wkt.startswith('LOCAL_CS')
97
98     @property
99     def geographic(self):
100         "Is this Spatial Reference geographic?"
101         if HAS_GDAL:
102             return self.srs.geographic
103         else:
104             return self.wkt.startswith('GEOGCS')
105
106     @property
107     def linear_name(self):
108         "Returns the linear units name."
109         if HAS_GDAL:
110             return self.srs.linear_name
111         elif self.geographic:
112             return None
113         else:
114             m = self.units_regex.match(self.wkt)
115             return m.group('unit_name')
116                
117     @property
118     def linear_units(self):
119         "Returns the linear units."
120         if HAS_GDAL:
121             return self.srs.linear_units
122         elif self.geographic:
123             return None
124         else:
125             m = self.units_regex.match(self.wkt)
126             return m.group('unit')
127
128     @property
129     def angular_name(self):
130         "Returns the name of the angular units."
131         if HAS_GDAL:
132             return self.srs.angular_name
133         elif self.projected:
134             return None
135         else:
136             m = self.units_regex.match(self.wkt)
137             return m.group('unit_name')
138
139     @property
140     def angular_units(self):
141         "Returns the angular units."
142         if HAS_GDAL:
143             return self.srs.angular_units
144         elif self.projected:
145             return None
146         else:
147             m = self.units_regex.match(self.wkt)
148             return m.group('unit')
149
150     @property
151     def units(self):
152         "Returns a tuple of the units and the name."
153         if self.projected or self.local:
154             return (self.linear_units, self.linear_name)
155         elif self.geographic:
156             return (self.angular_units, self.angular_name)
157         else:
158             return (None, None)
159
160     @classmethod
161     def get_units(cls, wkt):
162         """
163         Class method used by GeometryField on initialization to
164         retrive the units on the given WKT, without having to use
165         any of the database fields.
166         """
167         if HAS_GDAL:
168             return SpatialReference(wkt).units
169         else:
170             m = cls.units_regex.match(wkt)
171             return m.group('unit'), m.group('unit_name')
172
173     @classmethod
174     def get_spheroid(cls, wkt, string=True):
175         """
176         Class method used by GeometryField on initialization to
177         retrieve the `SPHEROID[..]` parameters from the given WKT.
178         """
179         if HAS_GDAL:
180             srs = SpatialReference(wkt)
181             sphere_params = srs.ellipsoid
182             sphere_name = srs['spheroid']
183         else:
184             m = cls.spheroid_regex.match(wkt)
185             if m:
186                 sphere_params = (float(m.group('major')), float(m.group('flattening')))
187                 sphere_name = m.group('name')
188             else:
189                 return None
190        
191         if not string:
192             return sphere_name, sphere_params
193         else:
194             # `string` parameter used to place in format acceptable by PostGIS
195             if len(sphere_params) == 3:
196                 radius, flattening = sphere_params[0], sphere_params[2]
197             else:
198                 radius, flattening = sphere_params
199             return 'SPHEROID["%s",%s,%s]' % (sphere_name, radius, flattening)
200
201     def __unicode__(self):
202         """
203         Returns the string representation.  If GDAL is installed,
204         it will be 'pretty' OGC WKT.
205         """
206         try:
207             return unicode(self.srs)
208         except:
209             return unicode(self.wkt)
210
211 # The SpatialRefSys and GeometryColumns models
212 _srid_info = True
213 if settings.DATABASE_ENGINE == 'postgresql_psycopg2':
214     # Because the PostGIS version is checked when initializing the spatial
215     # backend a `ProgrammingError` will be raised if the PostGIS tables
216     # and functions are not installed.  We catch here so it won't be raised when
217     # running the Django test suite.
218     from psycopg2 import ProgrammingError
219     try:
220         from django.contrib.gis.db.backend.postgis.models import GeometryColumns, SpatialRefSys
221     except ProgrammingError:
222         _srid_info = False
223 elif settings.DATABASE_ENGINE == 'oracle':
224     # Same thing as above, except the GEOS library is attempted to be loaded for
225     # `BaseSpatialBackend`, and an exception will be raised during the
226     # Django test suite if it doesn't exist.
227     try:
228         from django.contrib.gis.db.backend.oracle.models import GeometryColumns, SpatialRefSys
229     except:
230         _srid_info = False
231 else:
232     _srid_info = False
233
234 if _srid_info:
235     def get_srid_info(srid):
236         """
237         Returns the units, unit name, and spheroid WKT associated with the
238         given SRID from the `spatial_ref_sys` (or equivalent) spatial database
239         table.  We use a database cursor to execute the query because this
240         function is used when it is not possible to use the ORM (for example,
241         during field initialization).
242         """
243         # SRID=-1 is a common convention for indicating the geometry has no
244         # spatial reference information associated with it.  Thus, we will
245         # return all None values without raising an exception.
246         if srid == -1: return None, None, None
247
248         # Getting the spatial reference WKT associated with the SRID from the
249         # `spatial_ref_sys` (or equivalent) spatial database table. This query
250         # cannot be executed using the ORM because this information is needed
251         # when the ORM cannot be used (e.g., during the initialization of
252         # `GeometryField`).
253         from django.db import connection
254         cur = connection.cursor()
255         qn = connection.ops.quote_name
256         stmt = 'SELECT %(table)s.%(wkt_col)s FROM %(table)s WHERE (%(table)s.%(srid_col)s = %(srid)s)'
257         stmt = stmt % {'table' : qn(SpatialRefSys._meta.db_table),
258                        'wkt_col' : qn(SpatialRefSys.wkt_col()),
259                        'srid_col' : qn('srid'),
260                        'srid' : srid,
261                        }
262         cur.execute(stmt)
263        
264         # Fetching the WKT from the cursor; if the query failed raise an Exception.
265         fetched = cur.fetchone()
266         if not fetched:
267             raise ValueError('Failed to find spatial reference entry in "%s" corresponding to SRID=%s.' %
268                              (SpatialRefSys._meta.db_table, srid))
269         srs_wkt = fetched[0]
270
271         # Getting metadata associated with the spatial reference system identifier.
272         # Specifically, getting the unit information and spheroid information
273         # (both required for distance queries).
274         unit, unit_name = SpatialRefSys.get_units(srs_wkt)
275         spheroid = SpatialRefSys.get_spheroid(srs_wkt)
276         return unit, unit_name, spheroid
277 else:
278     def get_srid_info(srid):
279         """
280         Dummy routine for the backends that do not have the OGC required
281         spatial metadata tables (like MySQL).
282         """
283         return None, None, None
284    
Note: See TracBrowser for help on using the browser.