You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

349 lines
12 KiB

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