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