|
1 # Needed ctypes routines |
|
2 from ctypes import byref |
|
3 |
|
4 # Other GDAL imports. |
|
5 from django.contrib.gis.gdal.base import GDALBase |
|
6 from django.contrib.gis.gdal.envelope import Envelope, OGREnvelope |
|
7 from django.contrib.gis.gdal.error import OGRException, OGRIndexError, SRSException |
|
8 from django.contrib.gis.gdal.feature import Feature |
|
9 from django.contrib.gis.gdal.field import OGRFieldTypes |
|
10 from django.contrib.gis.gdal.geometries import OGRGeomType |
|
11 from django.contrib.gis.gdal.srs import SpatialReference |
|
12 |
|
13 # GDAL ctypes function prototypes. |
|
14 from django.contrib.gis.gdal.prototypes import ds as capi, srs as srs_api |
|
15 |
|
16 # For more information, see the OGR C API source code: |
|
17 # http://www.gdal.org/ogr/ogr__api_8h.html |
|
18 # |
|
19 # The OGR_L_* routines are relevant here. |
|
20 class Layer(GDALBase): |
|
21 "A class that wraps an OGR Layer, needs to be instantiated from a DataSource object." |
|
22 |
|
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('RandomRead') |
|
38 |
|
39 def __getitem__(self, index): |
|
40 "Gets the Feature at the specified index." |
|
41 if isinstance(index, (int, long)): |
|
42 # An integer index was given -- we cannot do a check based on the |
|
43 # number of features because the beginning and ending feature IDs |
|
44 # are not guaranteed to be 0 and len(layer)-1, respectively. |
|
45 if index < 0: 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 |
|
54 def __iter__(self): |
|
55 "Iterates over each Feature in the Layer." |
|
56 # ResetReading() must be called before iteration is to begin. |
|
57 capi.reset_reading(self._ptr) |
|
58 for i in xrange(self.num_feat): |
|
59 yield Feature(capi.get_next_feature(self._ptr), self._ldefn) |
|
60 |
|
61 def __len__(self): |
|
62 "The length is the number of features." |
|
63 return self.num_feat |
|
64 |
|
65 def __str__(self): |
|
66 "The string name of the layer." |
|
67 return self.name |
|
68 |
|
69 def _make_feature(self, feat_id): |
|
70 """ |
|
71 Helper routine for __getitem__ that constructs a Feature from the given |
|
72 Feature ID. If the OGR Layer does not support random-access reading, |
|
73 then each feature of the layer will be incremented through until the |
|
74 a Feature is found matching the given feature ID. |
|
75 """ |
|
76 if self._random_read: |
|
77 # If the Layer supports random reading, return. |
|
78 try: |
|
79 return Feature(capi.get_feature(self.ptr, feat_id), self._ldefn) |
|
80 except OGRException: |
|
81 pass |
|
82 else: |
|
83 # Random access isn't supported, have to increment through |
|
84 # each feature until the given feature ID is encountered. |
|
85 for feat in self: |
|
86 if feat.fid == feat_id: return feat |
|
87 # Should have returned a Feature, raise an OGRIndexError. |
|
88 raise OGRIndexError('Invalid feature id: %s.' % feat_id) |
|
89 |
|
90 #### Layer properties #### |
|
91 @property |
|
92 def extent(self): |
|
93 "Returns the extent (an Envelope) of this layer." |
|
94 env = OGREnvelope() |
|
95 capi.get_extent(self.ptr, byref(env), 1) |
|
96 return Envelope(env) |
|
97 |
|
98 @property |
|
99 def name(self): |
|
100 "Returns the name of this layer in the Data Source." |
|
101 return capi.get_fd_name(self._ldefn) |
|
102 |
|
103 @property |
|
104 def num_feat(self, force=1): |
|
105 "Returns the number of features in the Layer." |
|
106 return capi.get_feature_count(self.ptr, force) |
|
107 |
|
108 @property |
|
109 def num_fields(self): |
|
110 "Returns the number of fields in the Layer." |
|
111 return capi.get_field_count(self._ldefn) |
|
112 |
|
113 @property |
|
114 def geom_type(self): |
|
115 "Returns the geometry type (OGRGeomType) of the Layer." |
|
116 return OGRGeomType(capi.get_fd_geom_type(self._ldefn)) |
|
117 |
|
118 @property |
|
119 def srs(self): |
|
120 "Returns the Spatial Reference used in this Layer." |
|
121 try: |
|
122 ptr = capi.get_layer_srs(self.ptr) |
|
123 return SpatialReference(srs_api.clone_srs(ptr)) |
|
124 except SRSException: |
|
125 return None |
|
126 |
|
127 @property |
|
128 def fields(self): |
|
129 """ |
|
130 Returns a list of string names corresponding to each of the Fields |
|
131 available in this Layer. |
|
132 """ |
|
133 return [capi.get_field_name(capi.get_field_defn(self._ldefn, i)) |
|
134 for i in xrange(self.num_fields) ] |
|
135 |
|
136 @property |
|
137 def field_types(self): |
|
138 """ |
|
139 Returns a list of the types of fields in this Layer. For example, |
|
140 the list [OFTInteger, OFTReal, OFTString] would be returned for |
|
141 an OGR layer that had an integer, a floating-point, and string |
|
142 fields. |
|
143 """ |
|
144 return [OGRFieldTypes[capi.get_field_type(capi.get_field_defn(self._ldefn, i))] |
|
145 for i in xrange(self.num_fields)] |
|
146 |
|
147 @property |
|
148 def field_widths(self): |
|
149 "Returns a list of the maximum field widths for the features." |
|
150 return [capi.get_field_width(capi.get_field_defn(self._ldefn, i)) |
|
151 for i in xrange(self.num_fields)] |
|
152 |
|
153 @property |
|
154 def field_precisions(self): |
|
155 "Returns the field precisions for the features." |
|
156 return [capi.get_field_precision(capi.get_field_defn(self._ldefn, i)) |
|
157 for i in xrange(self.num_fields)] |
|
158 |
|
159 #### Layer Methods #### |
|
160 def get_fields(self, field_name): |
|
161 """ |
|
162 Returns a list containing the given field name for every Feature |
|
163 in the Layer. |
|
164 """ |
|
165 if not field_name in self.fields: |
|
166 raise OGRException('invalid field name: %s' % field_name) |
|
167 return [feat.get(field_name) for feat in self] |
|
168 |
|
169 def get_geoms(self, geos=False): |
|
170 """ |
|
171 Returns a list containing the OGRGeometry for every Feature in |
|
172 the Layer. |
|
173 """ |
|
174 if geos: |
|
175 from django.contrib.gis.geos import GEOSGeometry |
|
176 return [GEOSGeometry(feat.geom.wkb) for feat in self] |
|
177 else: |
|
178 return [feat.geom for feat in self] |
|
179 |
|
180 def test_capability(self, capability): |
|
181 """ |
|
182 Returns a bool indicating whether the this Layer supports the given |
|
183 capability (a string). Valid capability strings include: |
|
184 'RandomRead', 'SequentialWrite', 'RandomWrite', 'FastSpatialFilter', |
|
185 'FastFeatureCount', 'FastGetExtent', 'CreateField', 'Transactions', |
|
186 'DeleteFeature', and 'FastSetNextByIndex'. |
|
187 """ |
|
188 return bool(capi.test_capability(self.ptr, capability)) |