|
1 """ |
|
2 The Spatial Reference class, represensents OGR Spatial Reference objects. |
|
3 |
|
4 Example: |
|
5 >>> from django.contrib.gis.gdal import SpatialReference |
|
6 >>> srs = SpatialReference('WGS84') |
|
7 >>> print srs |
|
8 GEOGCS["WGS 84", |
|
9 DATUM["WGS_1984", |
|
10 SPHEROID["WGS 84",6378137,298.257223563, |
|
11 AUTHORITY["EPSG","7030"]], |
|
12 TOWGS84[0,0,0,0,0,0,0], |
|
13 AUTHORITY["EPSG","6326"]], |
|
14 PRIMEM["Greenwich",0, |
|
15 AUTHORITY["EPSG","8901"]], |
|
16 UNIT["degree",0.01745329251994328, |
|
17 AUTHORITY["EPSG","9122"]], |
|
18 AUTHORITY["EPSG","4326"]] |
|
19 >>> print srs.proj |
|
20 +proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs |
|
21 >>> print srs.ellipsoid |
|
22 (6378137.0, 6356752.3142451793, 298.25722356300003) |
|
23 >>> print srs.projected, srs.geographic |
|
24 False True |
|
25 >>> srs.import_epsg(32140) |
|
26 >>> print srs.name |
|
27 NAD83 / Texas South Central |
|
28 """ |
|
29 import re |
|
30 from types import UnicodeType, TupleType |
|
31 from ctypes import byref, c_char_p, c_int, c_void_p |
|
32 |
|
33 # Getting the error checking routine and exceptions |
|
34 from django.contrib.gis.gdal.error import OGRException, SRSException |
|
35 from django.contrib.gis.gdal.prototypes.srs import * |
|
36 |
|
37 #### Spatial Reference class. #### |
|
38 class SpatialReference(object): |
|
39 """ |
|
40 A wrapper for the OGRSpatialReference object. According to the GDAL website, |
|
41 the SpatialReference object "provide[s] services to represent coordinate |
|
42 systems (projections and datums) and to transform between them." |
|
43 """ |
|
44 |
|
45 # Well-Known Geographical Coordinate System Name |
|
46 _well_known = {'WGS84':4326, 'WGS72':4322, 'NAD27':4267, 'NAD83':4269} |
|
47 _epsg_regex = re.compile('^(EPSG:)?(?P<epsg>\d+)$', re.I) |
|
48 _proj_regex = re.compile(r'^\+proj') |
|
49 |
|
50 #### Python 'magic' routines #### |
|
51 def __init__(self, srs_input='', srs_type='wkt'): |
|
52 """ |
|
53 Creates a GDAL OSR Spatial Reference object from the given input. |
|
54 The input may be string of OGC Well Known Text (WKT), an integer |
|
55 EPSG code, a PROJ.4 string, and/or a projection "well known" shorthand |
|
56 string (one of 'WGS84', 'WGS72', 'NAD27', 'NAD83'). |
|
57 """ |
|
58 # Intializing pointer and string buffer. |
|
59 self._ptr = None |
|
60 buf = c_char_p('') |
|
61 |
|
62 if isinstance(srs_input, basestring): |
|
63 # Encoding to ASCII if unicode passed in. |
|
64 if isinstance(srs_input, UnicodeType): |
|
65 srs_input = srs_input.encode('ascii') |
|
66 |
|
67 epsg_m = self._epsg_regex.match(srs_input) |
|
68 proj_m = self._proj_regex.match(srs_input) |
|
69 if epsg_m: |
|
70 # Is this an EPSG well known name? |
|
71 srs_type = 'epsg' |
|
72 srs_input = int(epsg_m.group('epsg')) |
|
73 elif proj_m: |
|
74 # Is the string a PROJ.4 string? |
|
75 srs_type = 'proj' |
|
76 elif srs_input in self._well_known: |
|
77 # Is this a short-hand well known name? |
|
78 srs_type = 'epsg' |
|
79 srs_input = self._well_known[srs_input] |
|
80 elif srs_type == 'proj': |
|
81 pass |
|
82 else: |
|
83 # Setting the buffer with WKT, PROJ.4 string, etc. |
|
84 buf = c_char_p(srs_input) |
|
85 elif isinstance(srs_input, int): |
|
86 # EPSG integer code was input. |
|
87 if srs_type != 'epsg': srs_type = 'epsg' |
|
88 elif isinstance(srs_input, c_void_p): |
|
89 srs_type = 'ogr' |
|
90 else: |
|
91 raise TypeError('Invalid SRS type "%s"' % srs_type) |
|
92 |
|
93 if srs_type == 'ogr': |
|
94 # SRS input is OGR pointer |
|
95 srs = srs_input |
|
96 else: |
|
97 # Creating a new pointer, using the string buffer. |
|
98 srs = new_srs(buf) |
|
99 |
|
100 # If the pointer is NULL, throw an exception. |
|
101 if not srs: |
|
102 raise SRSException('Could not create spatial reference from: %s' % srs_input) |
|
103 else: |
|
104 self._ptr = srs |
|
105 |
|
106 # Post-processing if in PROJ.4 or EPSG formats. |
|
107 if srs_type == 'proj': self.import_proj(srs_input) |
|
108 elif srs_type == 'epsg': self.import_epsg(srs_input) |
|
109 |
|
110 def __del__(self): |
|
111 "Destroys this spatial reference." |
|
112 if self._ptr: release_srs(self._ptr) |
|
113 |
|
114 def __getitem__(self, target): |
|
115 """ |
|
116 Returns the value of the given string attribute node, None if the node |
|
117 doesn't exist. Can also take a tuple as a parameter, (target, child), |
|
118 where child is the index of the attribute in the WKT. For example: |
|
119 |
|
120 >>> wkt = 'GEOGCS["WGS 84", DATUM["WGS_1984, ... AUTHORITY["EPSG","4326"]]') |
|
121 >>> srs = SpatialReference(wkt) # could also use 'WGS84', or 4326 |
|
122 >>> print srs['GEOGCS'] |
|
123 WGS 84 |
|
124 >>> print srs['DATUM'] |
|
125 WGS_1984 |
|
126 >>> print srs['AUTHORITY'] |
|
127 EPSG |
|
128 >>> print srs['AUTHORITY', 1] # The authority value |
|
129 4326 |
|
130 >>> print srs['TOWGS84', 4] # the fourth value in this wkt |
|
131 0 |
|
132 >>> print srs['UNIT|AUTHORITY'] # For the units authority, have to use the pipe symbole. |
|
133 EPSG |
|
134 >>> print srs['UNIT|AUTHORITY', 1] # The authority value for the untis |
|
135 9122 |
|
136 """ |
|
137 if isinstance(target, TupleType): |
|
138 return self.attr_value(*target) |
|
139 else: |
|
140 return self.attr_value(target) |
|
141 |
|
142 def __str__(self): |
|
143 "The string representation uses 'pretty' WKT." |
|
144 return self.pretty_wkt |
|
145 |
|
146 #### SpatialReference Methods #### |
|
147 def attr_value(self, target, index=0): |
|
148 """ |
|
149 The attribute value for the given target node (e.g. 'PROJCS'). The index |
|
150 keyword specifies an index of the child node to return. |
|
151 """ |
|
152 if not isinstance(target, str) or not isinstance(index, int): |
|
153 raise TypeError |
|
154 return get_attr_value(self._ptr, target, index) |
|
155 |
|
156 def auth_name(self, target): |
|
157 "Returns the authority name for the given string target node." |
|
158 return get_auth_name(self._ptr, target) |
|
159 |
|
160 def auth_code(self, target): |
|
161 "Returns the authority code for the given string target node." |
|
162 return get_auth_code(self._ptr, target) |
|
163 |
|
164 def clone(self): |
|
165 "Returns a clone of this SpatialReference object." |
|
166 return SpatialReference(clone_srs(self._ptr)) |
|
167 |
|
168 def from_esri(self): |
|
169 "Morphs this SpatialReference from ESRI's format to EPSG." |
|
170 morph_from_esri(self._ptr) |
|
171 |
|
172 def identify_epsg(self): |
|
173 """ |
|
174 This method inspects the WKT of this SpatialReference, and will |
|
175 add EPSG authority nodes where an EPSG identifier is applicable. |
|
176 """ |
|
177 identify_epsg(self._ptr) |
|
178 |
|
179 def to_esri(self): |
|
180 "Morphs this SpatialReference to ESRI's format." |
|
181 morph_to_esri(self._ptr) |
|
182 |
|
183 def validate(self): |
|
184 "Checks to see if the given spatial reference is valid." |
|
185 srs_validate(self._ptr) |
|
186 |
|
187 #### Name & SRID properties #### |
|
188 @property |
|
189 def name(self): |
|
190 "Returns the name of this Spatial Reference." |
|
191 if self.projected: return self.attr_value('PROJCS') |
|
192 elif self.geographic: return self.attr_value('GEOGCS') |
|
193 elif self.local: return self.attr_value('LOCAL_CS') |
|
194 else: return None |
|
195 |
|
196 @property |
|
197 def srid(self): |
|
198 "Returns the SRID of top-level authority, or None if undefined." |
|
199 try: |
|
200 return int(self.attr_value('AUTHORITY', 1)) |
|
201 except (TypeError, ValueError): |
|
202 return None |
|
203 |
|
204 #### Unit Properties #### |
|
205 @property |
|
206 def linear_name(self): |
|
207 "Returns the name of the linear units." |
|
208 units, name = linear_units(self._ptr, byref(c_char_p())) |
|
209 return name |
|
210 |
|
211 @property |
|
212 def linear_units(self): |
|
213 "Returns the value of the linear units." |
|
214 units, name = linear_units(self._ptr, byref(c_char_p())) |
|
215 return units |
|
216 |
|
217 @property |
|
218 def angular_name(self): |
|
219 "Returns the name of the angular units." |
|
220 units, name = angular_units(self._ptr, byref(c_char_p())) |
|
221 return name |
|
222 |
|
223 @property |
|
224 def angular_units(self): |
|
225 "Returns the value of the angular units." |
|
226 units, name = angular_units(self._ptr, byref(c_char_p())) |
|
227 return units |
|
228 |
|
229 @property |
|
230 def units(self): |
|
231 """ |
|
232 Returns a 2-tuple of the units value and the units name, |
|
233 and will automatically determines whether to return the linear |
|
234 or angular units. |
|
235 """ |
|
236 if self.projected or self.local: |
|
237 return linear_units(self._ptr, byref(c_char_p())) |
|
238 elif self.geographic: |
|
239 return angular_units(self._ptr, byref(c_char_p())) |
|
240 else: |
|
241 return (None, None) |
|
242 |
|
243 #### Spheroid/Ellipsoid Properties #### |
|
244 @property |
|
245 def ellipsoid(self): |
|
246 """ |
|
247 Returns a tuple of the ellipsoid parameters: |
|
248 (semimajor axis, semiminor axis, and inverse flattening) |
|
249 """ |
|
250 return (self.semi_major, self.semi_minor, self.inverse_flattening) |
|
251 |
|
252 @property |
|
253 def semi_major(self): |
|
254 "Returns the Semi Major Axis for this Spatial Reference." |
|
255 return semi_major(self._ptr, byref(c_int())) |
|
256 |
|
257 @property |
|
258 def semi_minor(self): |
|
259 "Returns the Semi Minor Axis for this Spatial Reference." |
|
260 return semi_minor(self._ptr, byref(c_int())) |
|
261 |
|
262 @property |
|
263 def inverse_flattening(self): |
|
264 "Returns the Inverse Flattening for this Spatial Reference." |
|
265 return invflattening(self._ptr, byref(c_int())) |
|
266 |
|
267 #### Boolean Properties #### |
|
268 @property |
|
269 def geographic(self): |
|
270 """ |
|
271 Returns True if this SpatialReference is geographic |
|
272 (root node is GEOGCS). |
|
273 """ |
|
274 return bool(isgeographic(self._ptr)) |
|
275 |
|
276 @property |
|
277 def local(self): |
|
278 "Returns True if this SpatialReference is local (root node is LOCAL_CS)." |
|
279 return bool(islocal(self._ptr)) |
|
280 |
|
281 @property |
|
282 def projected(self): |
|
283 """ |
|
284 Returns True if this SpatialReference is a projected coordinate system |
|
285 (root node is PROJCS). |
|
286 """ |
|
287 return bool(isprojected(self._ptr)) |
|
288 |
|
289 #### Import Routines ##### |
|
290 def import_wkt(self, wkt): |
|
291 "Imports the Spatial Reference from OGC WKT (string)" |
|
292 from_wkt(self._ptr, byref(c_char_p(wkt))) |
|
293 |
|
294 def import_proj(self, proj): |
|
295 "Imports the Spatial Reference from a PROJ.4 string." |
|
296 from_proj(self._ptr, proj) |
|
297 |
|
298 def import_epsg(self, epsg): |
|
299 "Imports the Spatial Reference from the EPSG code (an integer)." |
|
300 from_epsg(self._ptr, epsg) |
|
301 |
|
302 def import_xml(self, xml): |
|
303 "Imports the Spatial Reference from an XML string." |
|
304 from_xml(self._ptr, xml) |
|
305 |
|
306 #### Export Properties #### |
|
307 @property |
|
308 def wkt(self): |
|
309 "Returns the WKT representation of this Spatial Reference." |
|
310 return to_wkt(self._ptr, byref(c_char_p())) |
|
311 |
|
312 @property |
|
313 def pretty_wkt(self, simplify=0): |
|
314 "Returns the 'pretty' representation of the WKT." |
|
315 return to_pretty_wkt(self._ptr, byref(c_char_p()), simplify) |
|
316 |
|
317 @property |
|
318 def proj(self): |
|
319 "Returns the PROJ.4 representation for this Spatial Reference." |
|
320 return to_proj(self._ptr, byref(c_char_p())) |
|
321 |
|
322 @property |
|
323 def proj4(self): |
|
324 "Alias for proj()." |
|
325 return self.proj |
|
326 |
|
327 @property |
|
328 def xml(self, dialect=''): |
|
329 "Returns the XML representation of this Spatial Reference." |
|
330 # FIXME: This leaks memory, have to figure out why. |
|
331 return to_xml(self._ptr, byref(c_char_p()), dialect) |
|
332 |
|
333 def to_esri(self): |
|
334 "Morphs this SpatialReference to ESRI's format." |
|
335 morph_to_esri(self._ptr) |
|
336 |
|
337 def from_esri(self): |
|
338 "Morphs this SpatialReference from ESRI's format to EPSG." |
|
339 morph_from_esri(self._ptr) |
|
340 |
|
341 class CoordTransform(object): |
|
342 "The coordinate system transformation object." |
|
343 |
|
344 def __init__(self, source, target): |
|
345 "Initializes on a source and target SpatialReference objects." |
|
346 self._ptr = None # Initially NULL |
|
347 if not isinstance(source, SpatialReference) or not isinstance(target, SpatialReference): |
|
348 raise SRSException('source and target must be of type SpatialReference') |
|
349 self._ptr = new_ct(source._ptr, target._ptr) |
|
350 if not self._ptr: |
|
351 raise SRSException('could not intialize CoordTransform object') |
|
352 self._srs1_name = source.name |
|
353 self._srs2_name = target.name |
|
354 |
|
355 def __del__(self): |
|
356 "Deletes this Coordinate Transformation object." |
|
357 if self._ptr: destroy_ct(self._ptr) |
|
358 |
|
359 def __str__(self): |
|
360 return 'Transform from "%s" to "%s"' % (self._srs1_name, self._srs2_name) |