PageRenderTime 272ms CodeModel.GetById 1ms RepoModel.GetById 0ms app.codeStats 0ms

/django/contrib/gis/db/backends/base.py

https://code.google.com/p/mango-py/
Python | 336 lines | 224 code | 41 blank | 71 comment | 45 complexity | 90b150e3540bb96a490a46c5e38b6202 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. """
  2. Base/mixin classes for the spatial backend database operations and the
  3. `SpatialRefSys` model the backend.
  4. """
  5. import re
  6. from django.conf import settings
  7. from django.contrib.gis import gdal
  8. class BaseSpatialOperations(object):
  9. """
  10. This module holds the base `BaseSpatialBackend` object, which is
  11. instantiated by each spatial database backend with the features
  12. it has.
  13. """
  14. distance_functions = {}
  15. geometry_functions = {}
  16. geometry_operators = {}
  17. geography_operators = {}
  18. geography_functions = {}
  19. gis_terms = {}
  20. truncate_params = {}
  21. # Quick booleans for the type of this spatial backend, and
  22. # an attribute for the spatial database version tuple (if applicable)
  23. postgis = False
  24. spatialite = False
  25. mysql = False
  26. oracle = False
  27. spatial_version = None
  28. # How the geometry column should be selected.
  29. select = None
  30. # Does the spatial database have a geography type?
  31. geography = False
  32. area = False
  33. centroid = False
  34. difference = False
  35. distance = False
  36. distance_sphere = False
  37. distance_spheroid = False
  38. envelope = False
  39. force_rhr = False
  40. mem_size = False
  41. bounding_circle = False
  42. num_geom = False
  43. num_points = False
  44. perimeter = False
  45. perimeter3d = False
  46. point_on_surface = False
  47. polygonize = False
  48. reverse = False
  49. scale = False
  50. snap_to_grid = False
  51. sym_difference = False
  52. transform = False
  53. translate = False
  54. union = False
  55. # Aggregates
  56. collect = False
  57. extent = False
  58. extent3d = False
  59. make_line = False
  60. unionagg = False
  61. # Serialization
  62. geohash = False
  63. geojson = False
  64. gml = False
  65. kml = False
  66. svg = False
  67. # Constructors
  68. from_text = False
  69. from_wkb = False
  70. # Default conversion functions for aggregates; will be overridden if implemented
  71. # for the spatial backend.
  72. def convert_extent(self, box):
  73. raise NotImplementedError('Aggregate extent not implemented for this spatial backend.')
  74. def convert_extent3d(self, box):
  75. raise NotImplementedError('Aggregate 3D extent not implemented for this spatial backend.')
  76. def convert_geom(self, geom_val, geom_field):
  77. raise NotImplementedError('Aggregate method not implemented for this spatial backend.')
  78. # For quoting column values, rather than columns.
  79. def geo_quote_name(self, name):
  80. if isinstance(name, unicode):
  81. name = name.encode('ascii')
  82. return "'%s'" % name
  83. # GeometryField operations
  84. def geo_db_type(self, f):
  85. """
  86. Returns the database column type for the geometry field on
  87. the spatial backend.
  88. """
  89. raise NotImplementedError
  90. def get_distance(self, f, value, lookup_type):
  91. """
  92. Returns the distance parameters for the given geometry field,
  93. lookup value, and lookup type.
  94. """
  95. raise NotImplementedError('Distance operations not available on this spatial backend.')
  96. def get_geom_placeholder(self, f, value):
  97. """
  98. Returns the placeholder for the given geometry field with the given
  99. value. Depending on the spatial backend, the placeholder may contain a
  100. stored procedure call to the transformation function of the spatial
  101. backend.
  102. """
  103. raise NotImplementedError
  104. # Spatial SQL Construction
  105. def spatial_aggregate_sql(self, agg):
  106. raise NotImplementedError('Aggregate support not implemented for this spatial backend.')
  107. def spatial_lookup_sql(self, lvalue, lookup_type, value, field):
  108. raise NotImplementedError
  109. # Routines for getting the OGC-compliant models.
  110. def geometry_columns(self):
  111. raise NotImplementedError
  112. def spatial_ref_sys(self):
  113. raise NotImplementedError
  114. class SpatialRefSysMixin(object):
  115. """
  116. The SpatialRefSysMixin is a class used by the database-dependent
  117. SpatialRefSys objects to reduce redundnant code.
  118. """
  119. # For pulling out the spheroid from the spatial reference string. This
  120. # regular expression is used only if the user does not have GDAL installed.
  121. # TODO: Flattening not used in all ellipsoids, could also be a minor axis,
  122. # or 'b' parameter.
  123. spheroid_regex = re.compile(r'.+SPHEROID\[\"(?P<name>.+)\",(?P<major>\d+(\.\d+)?),(?P<flattening>\d{3}\.\d+),')
  124. # For pulling out the units on platforms w/o GDAL installed.
  125. # TODO: Figure out how to pull out angular units of projected coordinate system and
  126. # fix for LOCAL_CS types. GDAL should be highly recommended for performing
  127. # distance queries.
  128. 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+)"\])?\]$')
  129. @property
  130. def srs(self):
  131. """
  132. Returns a GDAL SpatialReference object, if GDAL is installed.
  133. """
  134. if gdal.HAS_GDAL:
  135. # TODO: Is caching really necessary here? Is complexity worth it?
  136. if hasattr(self, '_srs'):
  137. # Returning a clone of the cached SpatialReference object.
  138. return self._srs.clone()
  139. else:
  140. # Attempting to cache a SpatialReference object.
  141. # Trying to get from WKT first.
  142. try:
  143. self._srs = gdal.SpatialReference(self.wkt)
  144. return self.srs
  145. except Exception, msg:
  146. pass
  147. try:
  148. self._srs = gdal.SpatialReference(self.proj4text)
  149. return self.srs
  150. except Exception, msg:
  151. pass
  152. raise Exception('Could not get OSR SpatialReference from WKT: %s\nError:\n%s' % (self.wkt, msg))
  153. else:
  154. raise Exception('GDAL is not installed.')
  155. @property
  156. def ellipsoid(self):
  157. """
  158. Returns a tuple of the ellipsoid parameters:
  159. (semimajor axis, semiminor axis, and inverse flattening).
  160. """
  161. if gdal.HAS_GDAL:
  162. return self.srs.ellipsoid
  163. else:
  164. m = self.spheroid_regex.match(self.wkt)
  165. if m: return (float(m.group('major')), float(m.group('flattening')))
  166. else: return None
  167. @property
  168. def name(self):
  169. "Returns the projection name."
  170. return self.srs.name
  171. @property
  172. def spheroid(self):
  173. "Returns the spheroid name for this spatial reference."
  174. return self.srs['spheroid']
  175. @property
  176. def datum(self):
  177. "Returns the datum for this spatial reference."
  178. return self.srs['datum']
  179. @property
  180. def projected(self):
  181. "Is this Spatial Reference projected?"
  182. if gdal.HAS_GDAL:
  183. return self.srs.projected
  184. else:
  185. return self.wkt.startswith('PROJCS')
  186. @property
  187. def local(self):
  188. "Is this Spatial Reference local?"
  189. if gdal.HAS_GDAL:
  190. return self.srs.local
  191. else:
  192. return self.wkt.startswith('LOCAL_CS')
  193. @property
  194. def geographic(self):
  195. "Is this Spatial Reference geographic?"
  196. if gdal.HAS_GDAL:
  197. return self.srs.geographic
  198. else:
  199. return self.wkt.startswith('GEOGCS')
  200. @property
  201. def linear_name(self):
  202. "Returns the linear units name."
  203. if gdal.HAS_GDAL:
  204. return self.srs.linear_name
  205. elif self.geographic:
  206. return None
  207. else:
  208. m = self.units_regex.match(self.wkt)
  209. return m.group('unit_name')
  210. @property
  211. def linear_units(self):
  212. "Returns the linear units."
  213. if gdal.HAS_GDAL:
  214. return self.srs.linear_units
  215. elif self.geographic:
  216. return None
  217. else:
  218. m = self.units_regex.match(self.wkt)
  219. return m.group('unit')
  220. @property
  221. def angular_name(self):
  222. "Returns the name of the angular units."
  223. if gdal.HAS_GDAL:
  224. return self.srs.angular_name
  225. elif self.projected:
  226. return None
  227. else:
  228. m = self.units_regex.match(self.wkt)
  229. return m.group('unit_name')
  230. @property
  231. def angular_units(self):
  232. "Returns the angular units."
  233. if gdal.HAS_GDAL:
  234. return self.srs.angular_units
  235. elif self.projected:
  236. return None
  237. else:
  238. m = self.units_regex.match(self.wkt)
  239. return m.group('unit')
  240. @property
  241. def units(self):
  242. "Returns a tuple of the units and the name."
  243. if self.projected or self.local:
  244. return (self.linear_units, self.linear_name)
  245. elif self.geographic:
  246. return (self.angular_units, self.angular_name)
  247. else:
  248. return (None, None)
  249. @classmethod
  250. def get_units(cls, wkt):
  251. """
  252. Class method used by GeometryField on initialization to
  253. retrive the units on the given WKT, without having to use
  254. any of the database fields.
  255. """
  256. if gdal.HAS_GDAL:
  257. return gdal.SpatialReference(wkt).units
  258. else:
  259. m = cls.units_regex.match(wkt)
  260. return m.group('unit'), m.group('unit_name')
  261. @classmethod
  262. def get_spheroid(cls, wkt, string=True):
  263. """
  264. Class method used by GeometryField on initialization to
  265. retrieve the `SPHEROID[..]` parameters from the given WKT.
  266. """
  267. if gdal.HAS_GDAL:
  268. srs = gdal.SpatialReference(wkt)
  269. sphere_params = srs.ellipsoid
  270. sphere_name = srs['spheroid']
  271. else:
  272. m = cls.spheroid_regex.match(wkt)
  273. if m:
  274. sphere_params = (float(m.group('major')), float(m.group('flattening')))
  275. sphere_name = m.group('name')
  276. else:
  277. return None
  278. if not string:
  279. return sphere_name, sphere_params
  280. else:
  281. # `string` parameter used to place in format acceptable by PostGIS
  282. if len(sphere_params) == 3:
  283. radius, flattening = sphere_params[0], sphere_params[2]
  284. else:
  285. radius, flattening = sphere_params
  286. return 'SPHEROID["%s",%s,%s]' % (sphere_name, radius, flattening)
  287. def __unicode__(self):
  288. """
  289. Returns the string representation. If GDAL is installed,
  290. it will be 'pretty' OGC WKT.
  291. """
  292. try:
  293. return unicode(self.srs)
  294. except:
  295. return unicode(self.wkt)