1 """ |
1 from django.db import connection |
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 |
2 |
8 # Checking for the presence of GDAL (needed for the SpatialReference object) |
3 if (hasattr(connection.ops, 'spatial_version') and |
9 from django.contrib.gis.gdal import HAS_GDAL, PYTHON23 |
4 not connection.ops.mysql): |
10 if HAS_GDAL: |
5 # Getting the `SpatialRefSys` and `GeometryColumns` |
11 from django.contrib.gis.gdal import SpatialReference |
6 # models for the default spatial backend. These |
12 |
7 # aliases are provided for backwards-compatibility. |
13 class SpatialRefSysMixin(object): |
8 SpatialRefSys = connection.ops.spatial_ref_sys() |
14 """ |
9 GeometryColumns = connection.ops.geometry_columns() |
15 The SpatialRefSysMixin is a class used by the database-dependent |
|
16 SpatialRefSys objects to reduce redundnant code. |
|
17 """ |
|
18 # For pulling out the spheroid from the spatial reference string. This |
|
19 # regular expression is used only if the user does not have GDAL installed. |
|
20 # TODO: Flattening not used in all ellipsoids, could also be a minor axis, |
|
21 # or 'b' parameter. |
|
22 spheroid_regex = re.compile(r'.+SPHEROID\[\"(?P<name>.+)\",(?P<major>\d+(\.\d+)?),(?P<flattening>\d{3}\.\d+),') |
|
23 |
|
24 # For pulling out the units on platforms w/o GDAL installed. |
|
25 # TODO: Figure out how to pull out angular units of projected coordinate system and |
|
26 # fix for LOCAL_CS types. GDAL should be highly recommended for performing |
|
27 # distance queries. |
|
28 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+)"\])?\]$') |
|
29 |
|
30 def srs(self): |
|
31 """ |
|
32 Returns a GDAL SpatialReference object, if GDAL is installed. |
|
33 """ |
|
34 if HAS_GDAL: |
|
35 # TODO: Is caching really necessary here? Is complexity worth it? |
|
36 if hasattr(self, '_srs'): |
|
37 # Returning a clone of the cached SpatialReference object. |
|
38 return self._srs.clone() |
|
39 else: |
|
40 # Attempting to cache a SpatialReference object. |
|
41 |
|
42 # Trying to get from WKT first. |
|
43 try: |
|
44 self._srs = SpatialReference(self.wkt) |
|
45 return self.srs |
|
46 except Exception, msg: |
|
47 pass |
|
48 |
|
49 try: |
|
50 self._srs = SpatialReference(self.proj4text) |
|
51 return self.srs |
|
52 except Exception, msg: |
|
53 pass |
|
54 |
|
55 raise Exception('Could not get OSR SpatialReference from WKT: %s\nError:\n%s' % (self.wkt, msg)) |
|
56 else: |
|
57 raise Exception('GDAL is not installed.') |
|
58 srs = property(srs) |
|
59 |
|
60 def ellipsoid(self): |
|
61 """ |
|
62 Returns a tuple of the ellipsoid parameters: |
|
63 (semimajor axis, semiminor axis, and inverse flattening). |
|
64 """ |
|
65 if HAS_GDAL: |
|
66 return self.srs.ellipsoid |
|
67 else: |
|
68 m = self.spheroid_regex.match(self.wkt) |
|
69 if m: return (float(m.group('major')), float(m.group('flattening'))) |
|
70 else: return None |
|
71 ellipsoid = property(ellipsoid) |
|
72 |
|
73 def name(self): |
|
74 "Returns the projection name." |
|
75 return self.srs.name |
|
76 name = property(name) |
|
77 |
|
78 def spheroid(self): |
|
79 "Returns the spheroid name for this spatial reference." |
|
80 return self.srs['spheroid'] |
|
81 spheroid = property(spheroid) |
|
82 |
|
83 def datum(self): |
|
84 "Returns the datum for this spatial reference." |
|
85 return self.srs['datum'] |
|
86 datum = property(datum) |
|
87 |
|
88 def projected(self): |
|
89 "Is this Spatial Reference projected?" |
|
90 if HAS_GDAL: |
|
91 return self.srs.projected |
|
92 else: |
|
93 return self.wkt.startswith('PROJCS') |
|
94 projected = property(projected) |
|
95 |
|
96 def local(self): |
|
97 "Is this Spatial Reference local?" |
|
98 if HAS_GDAL: |
|
99 return self.srs.local |
|
100 else: |
|
101 return self.wkt.startswith('LOCAL_CS') |
|
102 local = property(local) |
|
103 |
|
104 def geographic(self): |
|
105 "Is this Spatial Reference geographic?" |
|
106 if HAS_GDAL: |
|
107 return self.srs.geographic |
|
108 else: |
|
109 return self.wkt.startswith('GEOGCS') |
|
110 geographic = property(geographic) |
|
111 |
|
112 def linear_name(self): |
|
113 "Returns the linear units name." |
|
114 if HAS_GDAL: |
|
115 return self.srs.linear_name |
|
116 elif self.geographic: |
|
117 return None |
|
118 else: |
|
119 m = self.units_regex.match(self.wkt) |
|
120 return m.group('unit_name') |
|
121 linear_name = property(linear_name) |
|
122 |
|
123 def linear_units(self): |
|
124 "Returns the linear units." |
|
125 if HAS_GDAL: |
|
126 return self.srs.linear_units |
|
127 elif self.geographic: |
|
128 return None |
|
129 else: |
|
130 m = self.units_regex.match(self.wkt) |
|
131 return m.group('unit') |
|
132 linear_units = property(linear_units) |
|
133 |
|
134 def angular_name(self): |
|
135 "Returns the name of the angular units." |
|
136 if HAS_GDAL: |
|
137 return self.srs.angular_name |
|
138 elif self.projected: |
|
139 return None |
|
140 else: |
|
141 m = self.units_regex.match(self.wkt) |
|
142 return m.group('unit_name') |
|
143 angular_name = property(angular_name) |
|
144 |
|
145 def angular_units(self): |
|
146 "Returns the angular units." |
|
147 if HAS_GDAL: |
|
148 return self.srs.angular_units |
|
149 elif self.projected: |
|
150 return None |
|
151 else: |
|
152 m = self.units_regex.match(self.wkt) |
|
153 return m.group('unit') |
|
154 angular_units = property(angular_units) |
|
155 |
|
156 def units(self): |
|
157 "Returns a tuple of the units and the name." |
|
158 if self.projected or self.local: |
|
159 return (self.linear_units, self.linear_name) |
|
160 elif self.geographic: |
|
161 return (self.angular_units, self.angular_name) |
|
162 else: |
|
163 return (None, None) |
|
164 units = property(units) |
|
165 |
|
166 def get_units(cls, wkt): |
|
167 """ |
|
168 Class method used by GeometryField on initialization to |
|
169 retrive the units on the given WKT, without having to use |
|
170 any of the database fields. |
|
171 """ |
|
172 if HAS_GDAL: |
|
173 return SpatialReference(wkt).units |
|
174 else: |
|
175 m = cls.units_regex.match(wkt) |
|
176 return m.group('unit'), m.group('unit_name') |
|
177 get_units = classmethod(get_units) |
|
178 |
|
179 def get_spheroid(cls, wkt, string=True): |
|
180 """ |
|
181 Class method used by GeometryField on initialization to |
|
182 retrieve the `SPHEROID[..]` parameters from the given WKT. |
|
183 """ |
|
184 if HAS_GDAL: |
|
185 srs = SpatialReference(wkt) |
|
186 sphere_params = srs.ellipsoid |
|
187 sphere_name = srs['spheroid'] |
|
188 else: |
|
189 m = cls.spheroid_regex.match(wkt) |
|
190 if m: |
|
191 sphere_params = (float(m.group('major')), float(m.group('flattening'))) |
|
192 sphere_name = m.group('name') |
|
193 else: |
|
194 return None |
|
195 |
|
196 if not string: |
|
197 return sphere_name, sphere_params |
|
198 else: |
|
199 # `string` parameter used to place in format acceptable by PostGIS |
|
200 if len(sphere_params) == 3: |
|
201 radius, flattening = sphere_params[0], sphere_params[2] |
|
202 else: |
|
203 radius, flattening = sphere_params |
|
204 return 'SPHEROID["%s",%s,%s]' % (sphere_name, radius, flattening) |
|
205 get_spheroid = classmethod(get_spheroid) |
|
206 |
|
207 def __unicode__(self): |
|
208 """ |
|
209 Returns the string representation. If GDAL is installed, |
|
210 it will be 'pretty' OGC WKT. |
|
211 """ |
|
212 try: |
|
213 return unicode(self.srs) |
|
214 except: |
|
215 return unicode(self.wkt) |
|
216 |
|
217 # Django test suite on 2.3 platforms will choke on code inside this |
|
218 # conditional. |
|
219 if not PYTHON23: |
|
220 try: |
|
221 # try/except'ing the importation of SpatialBackend. Have to fail |
|
222 # silently because this module may be inadvertently invoked by |
|
223 # non-GeoDjango users (e.g., when the Django test suite executes |
|
224 # the models.py of all contrib apps). |
|
225 from django.contrib.gis.db.backend import SpatialBackend |
|
226 if SpatialBackend.mysql: raise Exception |
|
227 |
|
228 # Exposing the SpatialRefSys and GeometryColumns models. |
|
229 class SpatialRefSys(SpatialBackend.SpatialRefSys, SpatialRefSysMixin): |
|
230 pass |
|
231 GeometryColumns = SpatialBackend.GeometryColumns |
|
232 except: |
|
233 pass |
|