23
23
QgsFeatureRequest ,
24
24
QgsFeature ,
25
25
QgsGeometry ,
26
+ QgsProject ,
26
27
QgsWkbTypes ,
28
+ QgsExpression ,
29
+ QgsExpressionContext ,
30
+ QgsExpressionContextUtils ,
27
31
NULL ,
32
+ QgsCoordinateTransform ,
28
33
QgsMemoryProviderUtils ,
29
34
QgsCoordinateReferenceSystem ,
30
35
QgsRectangle ,
36
41
QgsApplication ,
37
42
QgsProviderRegistry ,
38
43
QgsProviderMetadata ,
44
+ QgsGeometryEngine ,
39
45
)
40
46
41
47
from qgis .PyQt .QtCore import QVariant
42
48
43
49
44
- class PyFeatureIterator (QgsFeatureIterator ):
50
+ class PyFeatureIterator (QgsAbstractFeatureIterator ):
45
51
46
52
def __init__ (self , source , request ):
47
- super (PyFeatureIterator , self ).__init__ ()
48
- self ._request = request
49
- self ._index = 0
53
+ super (PyFeatureIterator , self ).__init__ (request )
54
+ self ._request = request if request is not None else QgsFeatureRequest ()
50
55
self ._source = source
56
+ self ._index = 0
57
+ self ._transform = QgsCoordinateTransform ()
58
+ if self ._request .destinationCrs ().isValid () and self ._request .destinationCrs () != self ._source ._provider .crs ():
59
+ self ._transform = QgsCoordinateTransform (self ._source ._provider .crs (), self ._request .destinationCrs (), self ._request .transformContext ())
60
+ self ._filter_rect = self .filterRectToSourceCrs (self ._transform )
61
+ if not self ._filter_rect .isNull ():
62
+ self ._select_rect_geom = QgsGeometry .fromRect (self ._filter_rect )
63
+ self ._select_rect_engine = QgsGeometry .createGeometryEngine (self ._select_rect_geom .constGet ())
64
+ self ._select_rect_engine .prepareGeometry ()
65
+ else :
66
+ self ._select_rect_engine = None
67
+ self ._select_rect_geom = None
51
68
52
- def nextFeature (self , f ):
69
+ def fetchFeature (self , f ):
53
70
"""fetch next feature, return true on success"""
54
71
#virtual bool nextFeature( QgsFeature &f );
72
+ if self ._index < 0 :
73
+ f .setValid (False )
74
+ return False
55
75
try :
56
- _f = self ._source ._features [self ._index ]
57
- self ._index += 1
58
- f .setAttributes (_f .attributes ())
59
- f .setGeometry (_f .geometry ())
60
- f .setValid (_f .isValid ())
61
- return True
62
- except Exception as e :
76
+ found = False
77
+ while not found :
78
+ _f = self ._source ._features [list (self ._source ._features .keys ())[self ._index ]]
79
+ self ._index += 1
80
+ self ._source ._expression_context .setFeature (_f )
81
+ if self ._request .filterType () == QgsFeatureRequest .FilterExpression :
82
+ if not self ._request .filterExpression ().evaluate (self ._source ._expression_context ):
83
+ continue
84
+ if self ._source ._subset_expression :
85
+ if not self ._source ._subset_expression .evaluate (self ._source ._expression_context ):
86
+ continue
87
+ elif self ._request .filterType () == QgsFeatureRequest .FilterFids :
88
+ if not _f .id () in self ._request .filterFids ():
89
+ continue
90
+ elif self ._request .filterType () == QgsFeatureRequest .FilterFid :
91
+ if _f .id () != self ._request .filterFid ():
92
+ continue
93
+ if not self ._filter_rect .isNull ():
94
+ if not _f .hasGeometry ():
95
+ continue
96
+ if self ._request .flags () & QgsFeatureRequest .ExactIntersect :
97
+ # do exact check in case we're doing intersection
98
+ if not self ._select_rect_engine .intersects (_f .geometry ().constGet ()):
99
+ continue
100
+ else :
101
+ if not _f .geometry ().boundingBox ().intersects (self ._filter_rect ):
102
+ continue
103
+ f .setGeometry (_f .geometry ())
104
+ self .geometryToDestinationCrs (f , self ._transform )
105
+ f .setFields (_f .fields ())
106
+ f .setAttributes (_f .attributes ())
107
+ f .setValid (_f .isValid ())
108
+ f .setId (_f .id ())
109
+ return True
110
+ except IndexError as e :
63
111
f .setValid (False )
64
112
return False
65
113
66
114
def __iter__ (self ):
67
- 'Returns itself as an iterator object'
115
+ """Returns self as an iterator object"""
116
+ self ._index = 0
68
117
return self
69
118
70
119
def __next__ (self ):
71
- ' Returns the next value till current is lower than high'
120
+ """ Returns the next value till current is lower than high"""
72
121
f = QgsFeature ()
73
122
if not self .nextFeature (f ):
74
123
raise StopIteration
@@ -78,12 +127,15 @@ def __next__(self):
78
127
def rewind (self ):
79
128
"""reset the iterator to the starting position"""
80
129
#virtual bool rewind() = 0;
130
+ if self ._index < 0 :
131
+ return False
81
132
self ._index = 0
133
+ return True
82
134
83
135
def close (self ):
84
136
"""end of iterating: free the resources / lock"""
85
137
#virtual bool close() = 0;
86
- self ._index = 0
138
+ self ._index = - 1
87
139
return True
88
140
89
141
@@ -94,10 +146,20 @@ def __init__(self, provider):
94
146
self ._provider = provider
95
147
self ._features = provider ._features
96
148
149
+ self ._expression_context = QgsExpressionContext ()
150
+ self ._expression_context .appendScope (QgsExpressionContextUtils .globalScope ())
151
+ self ._expression_context .appendScope (QgsExpressionContextUtils .projectScope (QgsProject .instance ()))
152
+ self ._expression_context .setFields (self ._provider .fields ())
153
+ if self ._provider .subsetString ():
154
+ self ._subset_expression = QgsExpression (self ._provider .subsetString ())
155
+ self ._subset_expression .prepare (self ._expression_context )
156
+ else :
157
+ self ._subset_expression = None
158
+
97
159
def getFeatures (self , request ):
98
160
# QgsFeatureIterator getFeatures( const QgsFeatureRequest &request ) override;
99
161
# NOTE: this is the same as PyProvider.getFeatures
100
- return PyFeatureIterator (self , request )
162
+ return QgsFeatureIterator ( PyFeatureIterator (self , request ) )
101
163
102
164
103
165
class PyProvider (QgsVectorDataProvider ):
@@ -127,56 +189,81 @@ def __init__(self, uri=''):
127
189
super (PyProvider , self ).__init__ (uri )
128
190
# Use the memory layer to parse the uri
129
191
mlayer = QgsVectorLayer (uri , 'ml' , 'memory' )
192
+ self .setNativeTypes (mlayer .dataProvider ().nativeTypes ())
130
193
self ._uri = uri
131
194
self ._fields = mlayer .fields ()
132
195
self ._wkbType = mlayer .wkbType ()
133
196
self ._features = {}
134
197
self ._extent = QgsRectangle ()
135
198
self ._extent .setMinimal ()
136
199
self ._subset_string = ''
200
+ self ._crs = mlayer .crs ()
137
201
138
202
def featureSource (self ):
139
203
return PyFeatureSource (self )
140
204
141
205
def dataSourceUri (self , expandAuthConfig = True ):
142
- #QString dataSourceUri( bool expandAuthConfig = true ) const override;
143
206
return self ._uri
144
207
145
208
def storageType (self ):
146
- #QString storageType() const override;
147
209
return "Python test memory storage"
148
210
149
- def getFeatures (self , request = None ):
150
- #QgsFeatureIterator getFeatures( const QgsFeatureRequest &request ) const override;
151
- return PyFeatureIterator (PyFeatureSource (self ), request )
211
+ def getFeatures (self , request = QgsFeatureRequest ()):
212
+ return QgsFeatureIterator (PyFeatureIterator (PyFeatureSource (self ), request ))
213
+
214
+ def uniqueValues (self , fieldIndex , limit = 1 ):
215
+ results = set ()
216
+ if fieldIndex >= 0 and fieldIndex < self .fields ().count ():
217
+ req = QgsFeatureRequest ()
218
+ req .setFlags (QgsFeatureRequest .NoGeometry )
219
+ req .setSubsetOfAttributes ([fieldIndex ])
220
+ for f in self .getFeatures (req ):
221
+ results .add (f .attributes ()[fieldIndex ])
222
+ return results
152
223
153
224
def wkbType (self ):
154
- #QgsWkbTypes::Type wkbType() const override;
155
225
return self ._wkbType
156
226
157
227
def featureCount (self ):
158
- # TODO: get from source
159
- # long featureCount() const override;
160
- return len (self ._features )
228
+ if not self .subsetString ():
229
+ return len (self ._features )
230
+ else :
231
+ req = QgsFeatureRequest ()
232
+ req .setFlags (QgsFeatureRequest .NoGeometry )
233
+ req .setSubsetOfAttributes ([])
234
+ return len ([f for f in self .getFeatures (req )])
161
235
162
236
def fields (self ):
163
- #QgsFields fields() const override;
164
237
return self ._fields
165
238
166
239
def addFeatures (self , flist , flags = None ):
167
- # bool addFeatures( QgsFeatureList &flist, QgsFeatureSink::Flags flags = nullptr ) override;
168
240
added = False
241
+ f_added = []
242
+ for f in flist :
243
+ if f .hasGeometry () and (f .geometry ().wkbType () != self .wkbType ()):
244
+ return added , f_added
245
+
169
246
for f in flist :
170
- f .setId (self .next_feature_id )
171
- self ._features [self .next_feature_id ] = f
247
+ _f = QgsFeature (self .fields ())
248
+ _f .setGeometry (f .geometry ())
249
+ attrs = [None for i in range (_f .fields ().count ())]
250
+ for i in range (min (len (attrs ), len (f .attributes ()))):
251
+ attrs [i ] = f .attributes ()[i ]
252
+ _f .setAttributes (attrs )
253
+ _f .setId (self .next_feature_id )
254
+ self ._features [self .next_feature_id ] = _f
172
255
self .next_feature_id += 1
173
256
added = True
174
- if added :
257
+ f_added .append (_f )
258
+
259
+ if len (f_added ):
175
260
self .updateExtents ()
261
+
176
262
return added , flist
177
263
178
264
def deleteFeatures (self , ids ):
179
- #bool deleteFeatures( const QgsFeatureIds &id ) override;
265
+ if not ids :
266
+ return True
180
267
removed = False
181
268
for id in ids :
182
269
if id in self ._features :
@@ -187,21 +274,20 @@ def deleteFeatures(self, ids):
187
274
return removed
188
275
189
276
def addAttributes (self , attrs ):
190
- #bool addAttributes( const QList<QgsField> &attributes ) override;
191
277
try :
192
- self ._fields .append (attrs )
193
278
for new_f in attrs :
279
+ if new_f .type () not in (QVariant .Int , QVariant .Double , QVariant .String , QVariant .Date , QVariant .Time , QVariant .DateTime , QVariant .LongLong , QVariant .StringList , QVariant .List ):
280
+ continue
281
+ self ._fields .append (new_f )
194
282
for f in self ._features .values ():
195
283
old_attrs = f .attributes ()
196
- old_attrs .app
197
284
old_attrs .append (None )
198
285
f .setAttributes (old_attrs )
199
286
return True
200
- except :
287
+ except Exception :
201
288
return False
202
289
203
- def renameAttributes (self , renameAttributes ):
204
- #bool renameAttributes( const QgsFieldNameMap &renamedAttributes ) override;
290
+ def renameAttributes (self , renamedAttributes ):
205
291
result = True
206
292
for key , new_name in renamedAttributes :
207
293
fieldIndex = key
@@ -216,7 +302,6 @@ def renameAttributes(self, renameAttributes):
216
302
return True
217
303
218
304
def deleteAttributes (self , attributes ):
219
- #bool deleteAttributes( const QgsAttributeIds &attributes ) override;
220
305
attrIdx = sorted (attributes .toList (), reverse = True )
221
306
222
307
# delete attributes one-by-one with decreasing index
@@ -229,52 +314,55 @@ def deleteAttributes(self, attributes):
229
314
return True
230
315
231
316
def changeAttributeValues (self , attr_map ):
232
- #bool changeAttributeValues( const QgsChangedAttributesMap &attr_map ) override;
233
317
for feature_id , attrs in attr_map .items ():
234
318
try :
235
319
f = self ._features [feature_id ]
236
320
except KeyError :
237
321
continue
238
- for k , v in attrs :
322
+ for k , v in attrs . items () :
239
323
f .setAttribute (k , v )
240
324
return True
241
325
242
326
def changeGeometryValues (self , geometry_map ):
243
- #bool changeGeometryValues( const QgsGeometryMap &geometry_map ) override;
244
- #TODO
327
+ for feature_id , geometry in geometry_map .items ():
328
+ try :
329
+ f = self ._features [feature_id ]
330
+ f .setGeometry (geometry )
331
+ except KeyError :
332
+ continue
333
+ self .updateExtents ()
245
334
return True
246
335
336
+ def allFeatureIds (self ):
337
+ return list (self ._features .keys ())
338
+
247
339
def subsetString (self ):
248
- #QString subsetString() const override;
249
340
return self ._subset_string
250
341
251
342
def setSubsetString (self , subsetString ):
252
- #bool setSubsetString( const QString &theSQL, bool updateFeatureCount = true ) override;
253
343
self ._subset_string = subsetString
344
+ self .updateExtents ()
345
+ self .clearMinMaxCache ()
346
+ self .dataChanged .emit ()
254
347
255
348
def supportsSubsetString (self ):
256
- #bool supportsSubsetString() const override { return true; }
257
349
return True
258
350
259
351
def createSpatialIndex (self ):
260
- #bool createSpatialIndex() override;
261
352
return True
262
353
263
354
def capabilities (self ):
264
- #QgsVectorDataProvider::Capabilities capabilities() const override;
265
355
return QgsVectorDataProvider .AddFeatures | QgsVectorDataProvider .DeleteFeatures | QgsVectorDataProvider .ChangeGeometries | QgsVectorDataProvider .ChangeAttributeValues | QgsVectorDataProvider .AddAttributes | QgsVectorDataProvider .DeleteAttributes | QgsVectorDataProvider .RenameAttributes | QgsVectorDataProvider .SelectAtId | QgsVectorDataProvider . CircularGeometries
266
356
267
357
#/* Implementation of functions from QgsDataProvider */
268
358
269
359
def name (self ):
270
- #QString name() const override;
271
360
return self .providerKey ()
272
361
273
362
def extent (self ):
274
- #QgsRectangle extent() const override;
275
- if self ._extent .isEmpty () and not self ._features :
363
+ if self ._extent .isEmpty () and self ._features :
276
364
self ._extent .setMinimal ()
277
- if self ._subset_string . isEmpty () :
365
+ if not self ._subset_string :
278
366
# fast way - iterate through all features
279
367
for feat in self ._features .values ():
280
368
if feat .hasGeometry ():
@@ -284,18 +372,15 @@ def extent(self):
284
372
if f .hasGeometry ():
285
373
self ._extent .combineExtentWith (f .geometry ().boundingBox ())
286
374
287
- elif self ._features :
375
+ elif not self ._features :
288
376
self ._extent .setMinimal ()
289
- return self ._extent
377
+ return QgsRectangle ( self ._extent )
290
378
291
379
def updateExtents (self ):
292
- #void updateExtents() override;
293
380
self ._extent .setMinimal ()
294
381
295
382
def isValid (self ):
296
- #bool isValid() const override;
297
383
return True
298
384
299
385
def crs (self ):
300
- #QgsCoordinateReferenceSystem crs() const override;
301
- return QgsCoordinateReferenceSystem (4326 )
386
+ return self ._crs
0 commit comments