layer.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. # Needed ctypes routines
  2. from ctypes import c_double, byref
  3. # Other GDAL imports.
  4. from django.contrib.gis.gdal.base import GDALBase
  5. from django.contrib.gis.gdal.envelope import Envelope, OGREnvelope
  6. from django.contrib.gis.gdal.error import OGRException, OGRIndexError, SRSException
  7. from django.contrib.gis.gdal.feature import Feature
  8. from django.contrib.gis.gdal.field import OGRFieldTypes
  9. from django.contrib.gis.gdal.geomtype import OGRGeomType
  10. from django.contrib.gis.gdal.geometries import OGRGeometry
  11. from django.contrib.gis.gdal.srs import SpatialReference
  12. # GDAL ctypes function prototypes.
  13. from django.contrib.gis.gdal.prototypes import ds as capi, geom as geom_api, srs as srs_api
  14. from django.utils.encoding import force_bytes, force_text
  15. from django.utils import six
  16. from django.utils.six.moves import xrange
  17. # For more information, see the OGR C API source code:
  18. # http://www.gdal.org/ogr/ogr__api_8h.html
  19. #
  20. # The OGR_L_* routines are relevant here.
  21. class Layer(GDALBase):
  22. "A class that wraps an OGR Layer, needs to be instantiated from a DataSource object."
  23. #### Python 'magic' routines ####
  24. def __init__(self, layer_ptr, ds):
  25. """
  26. Initializes on an OGR C pointer to the Layer and the `DataSource` object
  27. that owns this layer. The `DataSource` object is required so that a
  28. reference to it is kept with this Layer. This prevents garbage
  29. collection of the `DataSource` while this Layer is still active.
  30. """
  31. if not layer_ptr:
  32. raise OGRException('Cannot create Layer, invalid pointer given')
  33. self.ptr = layer_ptr
  34. self._ds = ds
  35. self._ldefn = capi.get_layer_defn(self._ptr)
  36. # Does the Layer support random reading?
  37. self._random_read = self.test_capability(b'RandomRead')
  38. def __getitem__(self, index):
  39. "Gets the Feature at the specified index."
  40. if isinstance(index, six.integer_types):
  41. # An integer index was given -- we cannot do a check based on the
  42. # number of features because the beginning and ending feature IDs
  43. # are not guaranteed to be 0 and len(layer)-1, respectively.
  44. if index < 0:
  45. raise OGRIndexError('Negative indices are not allowed on OGR Layers.')
  46. return self._make_feature(index)
  47. elif isinstance(index, slice):
  48. # A slice was given
  49. start, stop, stride = index.indices(self.num_feat)
  50. return [self._make_feature(fid) for fid in xrange(start, stop, stride)]
  51. else:
  52. raise TypeError('Integers and slices may only be used when indexing OGR Layers.')
  53. def __iter__(self):
  54. "Iterates over each Feature in the Layer."
  55. # ResetReading() must be called before iteration is to begin.
  56. capi.reset_reading(self._ptr)
  57. for i in xrange(self.num_feat):
  58. yield Feature(capi.get_next_feature(self._ptr), self)
  59. def __len__(self):
  60. "The length is the number of features."
  61. return self.num_feat
  62. def __str__(self):
  63. "The string name of the layer."
  64. return self.name
  65. def _make_feature(self, feat_id):
  66. """
  67. Helper routine for __getitem__ that constructs a Feature from the given
  68. Feature ID. If the OGR Layer does not support random-access reading,
  69. then each feature of the layer will be incremented through until the
  70. a Feature is found matching the given feature ID.
  71. """
  72. if self._random_read:
  73. # If the Layer supports random reading, return.
  74. try:
  75. return Feature(capi.get_feature(self.ptr, feat_id), self)
  76. except OGRException:
  77. pass
  78. else:
  79. # Random access isn't supported, have to increment through
  80. # each feature until the given feature ID is encountered.
  81. for feat in self:
  82. if feat.fid == feat_id:
  83. return feat
  84. # Should have returned a Feature, raise an OGRIndexError.
  85. raise OGRIndexError('Invalid feature id: %s.' % feat_id)
  86. #### Layer properties ####
  87. @property
  88. def extent(self):
  89. "Returns the extent (an Envelope) of this layer."
  90. env = OGREnvelope()
  91. capi.get_extent(self.ptr, byref(env), 1)
  92. return Envelope(env)
  93. @property
  94. def name(self):
  95. "Returns the name of this layer in the Data Source."
  96. name = capi.get_fd_name(self._ldefn)
  97. return force_text(name, self._ds.encoding, strings_only=True)
  98. @property
  99. def num_feat(self, force=1):
  100. "Returns the number of features in the Layer."
  101. return capi.get_feature_count(self.ptr, force)
  102. @property
  103. def num_fields(self):
  104. "Returns the number of fields in the Layer."
  105. return capi.get_field_count(self._ldefn)
  106. @property
  107. def geom_type(self):
  108. "Returns the geometry type (OGRGeomType) of the Layer."
  109. return OGRGeomType(capi.get_fd_geom_type(self._ldefn))
  110. @property
  111. def srs(self):
  112. "Returns the Spatial Reference used in this Layer."
  113. try:
  114. ptr = capi.get_layer_srs(self.ptr)
  115. return SpatialReference(srs_api.clone_srs(ptr))
  116. except SRSException:
  117. return None
  118. @property
  119. def fields(self):
  120. """
  121. Returns a list of string names corresponding to each of the Fields
  122. available in this Layer.
  123. """
  124. return [force_text(capi.get_field_name(capi.get_field_defn(self._ldefn, i)),
  125. self._ds.encoding, strings_only=True)
  126. for i in xrange(self.num_fields)]
  127. @property
  128. def field_types(self):
  129. """
  130. Returns a list of the types of fields in this Layer. For example,
  131. the list [OFTInteger, OFTReal, OFTString] would be returned for
  132. an OGR layer that had an integer, a floating-point, and string
  133. fields.
  134. """
  135. return [OGRFieldTypes[capi.get_field_type(capi.get_field_defn(self._ldefn, i))]
  136. for i in xrange(self.num_fields)]
  137. @property
  138. def field_widths(self):
  139. "Returns a list of the maximum field widths for the features."
  140. return [capi.get_field_width(capi.get_field_defn(self._ldefn, i))
  141. for i in xrange(self.num_fields)]
  142. @property
  143. def field_precisions(self):
  144. "Returns the field precisions for the features."
  145. return [capi.get_field_precision(capi.get_field_defn(self._ldefn, i))
  146. for i in xrange(self.num_fields)]
  147. def _get_spatial_filter(self):
  148. try:
  149. return OGRGeometry(geom_api.clone_geom(capi.get_spatial_filter(self.ptr)))
  150. except OGRException:
  151. return None
  152. def _set_spatial_filter(self, filter):
  153. if isinstance(filter, OGRGeometry):
  154. capi.set_spatial_filter(self.ptr, filter.ptr)
  155. elif isinstance(filter, (tuple, list)):
  156. if not len(filter) == 4:
  157. raise ValueError('Spatial filter list/tuple must have 4 elements.')
  158. # Map c_double onto params -- if a bad type is passed in it
  159. # will be caught here.
  160. xmin, ymin, xmax, ymax = map(c_double, filter)
  161. capi.set_spatial_filter_rect(self.ptr, xmin, ymin, xmax, ymax)
  162. elif filter is None:
  163. capi.set_spatial_filter(self.ptr, None)
  164. else:
  165. raise TypeError('Spatial filter must be either an OGRGeometry instance, a 4-tuple, or None.')
  166. spatial_filter = property(_get_spatial_filter, _set_spatial_filter)
  167. #### Layer Methods ####
  168. def get_fields(self, field_name):
  169. """
  170. Returns a list containing the given field name for every Feature
  171. in the Layer.
  172. """
  173. if field_name not in self.fields:
  174. raise OGRException('invalid field name: %s' % field_name)
  175. return [feat.get(field_name) for feat in self]
  176. def get_geoms(self, geos=False):
  177. """
  178. Returns a list containing the OGRGeometry for every Feature in
  179. the Layer.
  180. """
  181. if geos:
  182. from django.contrib.gis.geos import GEOSGeometry
  183. return [GEOSGeometry(feat.geom.wkb) for feat in self]
  184. else:
  185. return [feat.geom for feat in self]
  186. def test_capability(self, capability):
  187. """
  188. Returns a bool indicating whether the this Layer supports the given
  189. capability (a string). Valid capability strings include:
  190. 'RandomRead', 'SequentialWrite', 'RandomWrite', 'FastSpatialFilter',
  191. 'FastFeatureCount', 'FastGetExtent', 'CreateField', 'Transactions',
  192. 'DeleteFeature', and 'FastSetNextByIndex'.
  193. """
  194. return bool(capi.test_capability(self.ptr, force_bytes(capability)))