Branch data Line data Source code
1 : : /***************************************************************************
2 : : memoryprovider.cpp - provider with storage in memory
3 : : ------------------
4 : : begin : June 2008
5 : : copyright : (C) 2008 by Martin Dobias
6 : : email : wonder dot sk at gmail dot com
7 : : ***************************************************************************
8 : : * *
9 : : * This program is free software; you can redistribute it and/or modify *
10 : : * it under the terms of the GNU General Public License as published by *
11 : : * the Free Software Foundation; either version 2 of the License, or *
12 : : * (at your option) any later version. *
13 : : * *
14 : : ***************************************************************************/
15 : :
16 : : #include "qgsmemoryprovider.h"
17 : : #include "qgsmemoryfeatureiterator.h"
18 : :
19 : : #include "qgsfeature.h"
20 : : #include "qgsfields.h"
21 : : #include "qgsgeometry.h"
22 : : #include "qgslogger.h"
23 : : #include "qgsspatialindex.h"
24 : : #include "qgscoordinatereferencesystem.h"
25 : :
26 : : #include <QUrl>
27 : : #include <QUrlQuery>
28 : : #include <QRegExp>
29 : :
30 : : ///@cond PRIVATE
31 : :
32 : : #define TEXT_PROVIDER_KEY QStringLiteral( "memory" )
33 : : #define TEXT_PROVIDER_DESCRIPTION QStringLiteral( "Memory provider" )
34 : :
35 : 14 : QgsMemoryProvider::QgsMemoryProvider( const QString &uri, const ProviderOptions &options, QgsDataProvider::ReadFlags flags )
36 : 14 : : QgsVectorDataProvider( uri, options, flags )
37 : 28 : {
38 : : // Initialize the geometry with the uri to support old style uri's
39 : : // (ie, just 'point', 'line', 'polygon')
40 : 14 : QUrl url = QUrl::fromEncoded( uri.toUtf8() );
41 : 14 : const QUrlQuery query( url );
42 : 14 : QString geometry;
43 : 28 : if ( query.hasQueryItem( QStringLiteral( "geometry" ) ) )
44 : : {
45 : 0 : geometry = query.queryItemValue( QStringLiteral( "geometry" ) );
46 : 0 : }
47 : : else
48 : : {
49 : 14 : geometry = url.path();
50 : : }
51 : :
52 : 14 : if ( geometry.compare( QLatin1String( "none" ), Qt::CaseInsensitive ) == 0 )
53 : : {
54 : 0 : mWkbType = QgsWkbTypes::NoGeometry;
55 : 0 : }
56 : : else
57 : : {
58 : 14 : mWkbType = QgsWkbTypes::parseType( geometry );
59 : : }
60 : :
61 : 28 : if ( query.hasQueryItem( QStringLiteral( "crs" ) ) )
62 : : {
63 : 2 : QString crsDef = query.queryItemValue( QStringLiteral( "crs" ) );
64 : 1 : mCrs.createFromString( crsDef );
65 : 1 : }
66 : : else
67 : : {
68 : : // TODO - remove in QGIS 4.0. Layers without an explicit CRS set SHOULD have an invalid CRS. But in order to maintain
69 : : // 3.x api, we have to be tolerant/shortsighted(?) here and fallback to EPSG:4326
70 : 26 : mCrs = QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) );
71 : : }
72 : :
73 : 14 : mNextFeatureId = 1;
74 : :
75 : 28 : setNativeTypes( QList< NativeType >()
76 : 28 : << QgsVectorDataProvider::NativeType( tr( "Whole number (integer)" ), QStringLiteral( "integer" ), QVariant::Int, 0, 10 )
77 : : // Decimal number from OGR/Shapefile/dbf may come with length up to 32 and
78 : : // precision up to length-2 = 30 (default, if width is not specified in dbf is length = 24 precision = 15)
79 : : // We know that double (QVariant::Double) has only 15-16 significant numbers,
80 : : // but setting that correct limits would disable the use of memory provider with
81 : : // data from Shapefiles. In any case, the data are handled as doubles.
82 : : // So the limits set here are not correct but enable use of data from Shapefiles.
83 : 28 : << QgsVectorDataProvider::NativeType( tr( "Decimal number (real)" ), QStringLiteral( "double" ), QVariant::Double, 0, 32, 0, 30 )
84 : 28 : << QgsVectorDataProvider::NativeType( tr( "Text (string)" ), QStringLiteral( "string" ), QVariant::String, 0, 255 )
85 : :
86 : : // date type
87 : 28 : << QgsVectorDataProvider::NativeType( tr( "Date" ), QStringLiteral( "date" ), QVariant::Date, -1, -1, -1, -1 )
88 : 28 : << QgsVectorDataProvider::NativeType( tr( "Time" ), QStringLiteral( "time" ), QVariant::Time, -1, -1, -1, -1 )
89 : 28 : << QgsVectorDataProvider::NativeType( tr( "Date & Time" ), QStringLiteral( "datetime" ), QVariant::DateTime, -1, -1, -1, -1 )
90 : :
91 : : // integer types
92 : 28 : << QgsVectorDataProvider::NativeType( tr( "Whole number (smallint - 16bit)" ), QStringLiteral( "int2" ), QVariant::Int, -1, -1, 0, 0 )
93 : 28 : << QgsVectorDataProvider::NativeType( tr( "Whole number (integer - 32bit)" ), QStringLiteral( "int4" ), QVariant::Int, -1, -1, 0, 0 )
94 : 28 : << QgsVectorDataProvider::NativeType( tr( "Whole number (integer - 64bit)" ), QStringLiteral( "int8" ), QVariant::LongLong, -1, -1, 0, 0 )
95 : 28 : << QgsVectorDataProvider::NativeType( tr( "Decimal number (numeric)" ), QStringLiteral( "numeric" ), QVariant::Double, 1, 20, 0, 20 )
96 : 28 : << QgsVectorDataProvider::NativeType( tr( "Decimal number (decimal)" ), QStringLiteral( "decimal" ), QVariant::Double, 1, 20, 0, 20 )
97 : :
98 : : // floating point
99 : 28 : << QgsVectorDataProvider::NativeType( tr( "Decimal number (real)" ), QStringLiteral( "real" ), QVariant::Double, -1, -1, -1, -1 )
100 : 42 : << QgsVectorDataProvider::NativeType( tr( "Decimal number (double)" ), QStringLiteral( "double precision" ), QVariant::Double, -1, -1, -1, -1 )
101 : :
102 : : // string types
103 : 28 : << QgsVectorDataProvider::NativeType( tr( "Text, unlimited length (text)" ), QStringLiteral( "text" ), QVariant::String, -1, -1, -1, -1 )
104 : :
105 : : // boolean
106 : 28 : << QgsVectorDataProvider::NativeType( tr( "Boolean" ), QStringLiteral( "bool" ), QVariant::Bool )
107 : :
108 : : // blob
109 : 28 : << QgsVectorDataProvider::NativeType( tr( "Binary object (BLOB)" ), QStringLiteral( "binary" ), QVariant::ByteArray )
110 : :
111 : : // list types
112 : 28 : << QgsVectorDataProvider::NativeType( tr( "String list" ), QStringLiteral( "stringlist" ), QVariant::StringList, 0, 0, 0, 0, QVariant::String )
113 : 28 : << QgsVectorDataProvider::NativeType( tr( "Integer list" ), QStringLiteral( "integerlist" ), QVariant::List, 0, 0, 0, 0, QVariant::Int )
114 : 28 : << QgsVectorDataProvider::NativeType( tr( "Decimal (real) list" ), QStringLiteral( "doublelist" ), QVariant::List, 0, 0, 0, 0, QVariant::Double )
115 : 28 : << QgsVectorDataProvider::NativeType( tr( "Integer (64bit) list" ), QStringLiteral( "doublelist" ), QVariant::List, 0, 0, 0, 0, QVariant::Double )
116 : :
117 : : );
118 : :
119 : 28 : if ( query.hasQueryItem( QStringLiteral( "field" ) ) )
120 : : {
121 : 0 : QList<QgsField> attributes;
122 : 0 : QRegExp reFieldDef( "\\:"
123 : : "(int|integer|long|int8|real|double|string|date|time|datetime|binary|bool|boolean)" // type
124 : : "(?:\\((\\-?\\d+)" // length
125 : : "(?:\\,(\\-?\\d+))?" // precision
126 : : "\\))?(\\[\\])?" // array
127 : : "$", Qt::CaseInsensitive );
128 : 0 : QStringList fields = query.allQueryItemValues( QStringLiteral( "field" ) );
129 : 0 : for ( int i = 0; i < fields.size(); i++ )
130 : : {
131 : 0 : QString name = QUrl::fromPercentEncoding( fields.at( i ).toUtf8() );
132 : 0 : QVariant::Type type = QVariant::String;
133 : 0 : QVariant::Type subType = QVariant::Invalid;
134 : 0 : QString typeName( QStringLiteral( "string" ) );
135 : 0 : int length = 255;
136 : 0 : int precision = 0;
137 : :
138 : 0 : int pos = reFieldDef.indexIn( name );
139 : 0 : if ( pos >= 0 )
140 : : {
141 : 0 : name = name.mid( 0, pos );
142 : 0 : typeName = reFieldDef.cap( 1 ).toLower();
143 : 0 : if ( typeName == QLatin1String( "int" ) || typeName == QLatin1String( "integer" ) )
144 : : {
145 : 0 : type = QVariant::Int;
146 : 0 : typeName = QStringLiteral( "integer" );
147 : 0 : length = -1;
148 : 0 : }
149 : 0 : else if ( typeName == QLatin1String( "int8" ) || typeName == QLatin1String( "long" ) )
150 : : {
151 : 0 : type = QVariant::LongLong;
152 : 0 : typeName = QStringLiteral( "int8" );
153 : 0 : length = -1;
154 : 0 : }
155 : 0 : else if ( typeName == QLatin1String( "real" ) || typeName == QLatin1String( "double" ) )
156 : : {
157 : 0 : type = QVariant::Double;
158 : 0 : typeName = QStringLiteral( "double" );
159 : 0 : length = 20;
160 : 0 : precision = 5;
161 : 0 : }
162 : 0 : else if ( typeName == QLatin1String( "date" ) )
163 : : {
164 : 0 : type = QVariant::Date;
165 : 0 : typeName = QStringLiteral( "date" );
166 : 0 : length = -1;
167 : 0 : }
168 : 0 : else if ( typeName == QLatin1String( "time" ) )
169 : : {
170 : 0 : type = QVariant::Time;
171 : 0 : typeName = QStringLiteral( "time" );
172 : 0 : length = -1;
173 : 0 : }
174 : 0 : else if ( typeName == QLatin1String( "datetime" ) )
175 : : {
176 : 0 : type = QVariant::DateTime;
177 : 0 : typeName = QStringLiteral( "datetime" );
178 : 0 : length = -1;
179 : 0 : }
180 : 0 : else if ( typeName == QLatin1String( "bool" ) || typeName == QLatin1String( "boolean" ) )
181 : : {
182 : 0 : type = QVariant::Bool;
183 : 0 : typeName = QStringLiteral( "boolean" );
184 : 0 : length = -1;
185 : 0 : }
186 : 0 : else if ( typeName == QLatin1String( "binary" ) )
187 : : {
188 : 0 : type = QVariant::ByteArray;
189 : 0 : typeName = QStringLiteral( "binary" );
190 : 0 : length = -1;
191 : 0 : }
192 : :
193 : 0 : if ( !reFieldDef.cap( 2 ).isEmpty() )
194 : : {
195 : 0 : length = reFieldDef.cap( 2 ).toInt();
196 : 0 : }
197 : 0 : if ( !reFieldDef.cap( 3 ).isEmpty() )
198 : : {
199 : 0 : precision = reFieldDef.cap( 3 ).toInt();
200 : 0 : }
201 : 0 : if ( !reFieldDef.cap( 4 ).isEmpty() )
202 : : {
203 : : //array
204 : 0 : subType = type;
205 : 0 : type = type == QVariant::String ? QVariant::StringList : QVariant::List;
206 : 0 : typeName += QStringLiteral( "list" );
207 : 0 : }
208 : 0 : }
209 : 0 : if ( !name.isEmpty() )
210 : 0 : attributes.append( QgsField( name, type, typeName, length, precision, QString(), subType ) );
211 : 0 : }
212 : 0 : addAttributes( attributes );
213 : 0 : }
214 : :
215 : 28 : if ( query.hasQueryItem( QStringLiteral( "index" ) ) && query.queryItemValue( QStringLiteral( "index" ) ) == QLatin1String( "yes" ) )
216 : : {
217 : 0 : createSpatialIndex();
218 : 0 : }
219 : :
220 : 14 : }
221 : :
222 : 2 : QgsMemoryProvider::~QgsMemoryProvider()
223 : 2 : {
224 : 1 : delete mSpatialIndex;
225 : 2 : }
226 : :
227 : 6 : QString QgsMemoryProvider::providerKey()
228 : : {
229 : 12 : return TEXT_PROVIDER_KEY;
230 : : }
231 : :
232 : 3 : QString QgsMemoryProvider::providerDescription()
233 : : {
234 : 6 : return TEXT_PROVIDER_DESCRIPTION;
235 : : }
236 : :
237 : 14 : QgsMemoryProvider *QgsMemoryProvider::createProvider( const QString &uri,
238 : : const ProviderOptions &options,
239 : : QgsDataProvider::ReadFlags flags )
240 : : {
241 : 14 : return new QgsMemoryProvider( uri, options, flags );
242 : 0 : }
243 : :
244 : 67 : QgsAbstractFeatureSource *QgsMemoryProvider::featureSource() const
245 : : {
246 : 67 : return new QgsMemoryFeatureSource( this );
247 : 0 : }
248 : :
249 : 0 : QString QgsMemoryProvider::dataSourceUri( bool expandAuthConfig ) const
250 : : {
251 : : Q_UNUSED( expandAuthConfig )
252 : :
253 : 0 : QUrl uri( QStringLiteral( "memory" ) );
254 : 0 : QUrlQuery query;
255 : 0 : QString geometry = QgsWkbTypes::displayString( mWkbType );
256 : 0 : query.addQueryItem( QStringLiteral( "geometry" ), geometry );
257 : :
258 : 0 : if ( mCrs.isValid() )
259 : : {
260 : 0 : QString crsDef;
261 : 0 : QString authid = mCrs.authid();
262 : 0 : if ( authid.startsWith( QLatin1String( "EPSG:" ) ) )
263 : : {
264 : 0 : crsDef = authid;
265 : 0 : }
266 : : else
267 : : {
268 : 0 : crsDef = QStringLiteral( "wkt:%1" ).arg( mCrs.toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED ) );
269 : : }
270 : 0 : query.addQueryItem( QStringLiteral( "crs" ), crsDef );
271 : 0 : }
272 : 0 : if ( mSpatialIndex )
273 : : {
274 : 0 : query.addQueryItem( QStringLiteral( "index" ), QStringLiteral( "yes" ) );
275 : 0 : }
276 : :
277 : 0 : QgsAttributeList attrs = const_cast<QgsMemoryProvider *>( this )->attributeIndexes();
278 : 0 : for ( int i = 0; i < attrs.size(); i++ )
279 : : {
280 : 0 : const QgsField field = mFields.at( attrs[i] );
281 : 0 : QString fieldDef = field.name();
282 : :
283 : 0 : QString typeName = field.typeName();
284 : 0 : bool isList = false;
285 : 0 : if ( field.type() == QVariant::List || field.type() == QVariant::StringList )
286 : : {
287 : 0 : switch ( field.subType() )
288 : : {
289 : : case QVariant::Int:
290 : 0 : typeName = QStringLiteral( "integer" );
291 : 0 : break;
292 : :
293 : : case QVariant::LongLong:
294 : 0 : typeName = QStringLiteral( "long" );
295 : 0 : break;
296 : :
297 : : case QVariant::Double:
298 : 0 : typeName = QStringLiteral( "double" );
299 : 0 : break;
300 : :
301 : : case QVariant::String:
302 : 0 : typeName = QStringLiteral( "string" );
303 : 0 : break;
304 : :
305 : : default:
306 : 0 : break;
307 : : }
308 : 0 : isList = true;
309 : 0 : }
310 : :
311 : 0 : fieldDef.append( QStringLiteral( ":%2(%3,%4)%5" ).arg( typeName ).arg( field.length() ).arg( field.precision() ).arg( isList ? QStringLiteral( "[]" ) : QString() ) );
312 : 0 : query.addQueryItem( QStringLiteral( "field" ), fieldDef );
313 : 0 : }
314 : 0 : uri.setQuery( query );
315 : :
316 : 0 : return QString( uri.toEncoded() );
317 : :
318 : 0 : }
319 : :
320 : 0 : QString QgsMemoryProvider::storageType() const
321 : : {
322 : 0 : return QStringLiteral( "Memory storage" );
323 : : }
324 : :
325 : 0 : QgsFeatureIterator QgsMemoryProvider::getFeatures( const QgsFeatureRequest &request ) const
326 : : {
327 : 0 : return QgsFeatureIterator( new QgsMemoryFeatureIterator( new QgsMemoryFeatureSource( this ), true, request ) );
328 : 0 : }
329 : :
330 : :
331 : 0 : QgsRectangle QgsMemoryProvider::extent() const
332 : : {
333 : 0 : if ( mExtent.isEmpty() && !mFeatures.isEmpty() )
334 : : {
335 : 0 : mExtent.setMinimal();
336 : 0 : if ( mSubsetString.isEmpty() )
337 : : {
338 : : // fast way - iterate through all features
339 : 0 : const auto constMFeatures = mFeatures;
340 : 0 : for ( const QgsFeature &feat : constMFeatures )
341 : : {
342 : 0 : if ( feat.hasGeometry() )
343 : 0 : mExtent.combineExtentWith( feat.geometry().boundingBox() );
344 : : }
345 : 0 : }
346 : : else
347 : : {
348 : 0 : QgsFeature f;
349 : 0 : QgsFeatureIterator fi = getFeatures( QgsFeatureRequest().setNoAttributes() );
350 : 0 : while ( fi.nextFeature( f ) )
351 : : {
352 : 0 : if ( f.hasGeometry() )
353 : 0 : mExtent.combineExtentWith( f.geometry().boundingBox() );
354 : : }
355 : 0 : }
356 : 0 : }
357 : 0 : else if ( mFeatures.isEmpty() )
358 : : {
359 : 0 : mExtent.setMinimal();
360 : 0 : }
361 : :
362 : 0 : return mExtent;
363 : 0 : }
364 : :
365 : 14 : QgsWkbTypes::Type QgsMemoryProvider::wkbType() const
366 : : {
367 : 14 : return mWkbType;
368 : : }
369 : :
370 : 0 : long QgsMemoryProvider::featureCount() const
371 : : {
372 : 0 : if ( mSubsetString.isEmpty() )
373 : 0 : return mFeatures.count();
374 : :
375 : : // subset string set, no alternative but testing each feature
376 : 0 : QgsFeatureIterator fit = QgsFeatureIterator( new QgsMemoryFeatureIterator( new QgsMemoryFeatureSource( this ), true, QgsFeatureRequest().setNoAttributes() ) );
377 : 0 : int count = 0;
378 : 0 : QgsFeature feature;
379 : 0 : while ( fit.nextFeature( feature ) )
380 : : {
381 : 0 : count++;
382 : : }
383 : 0 : return count;
384 : 0 : }
385 : :
386 : 15 : QgsFields QgsMemoryProvider::fields() const
387 : : {
388 : 15 : return mFields;
389 : : }
390 : :
391 : 14 : bool QgsMemoryProvider::isValid() const
392 : : {
393 : 14 : return ( mWkbType != QgsWkbTypes::Unknown );
394 : : }
395 : :
396 : 14 : QgsCoordinateReferenceSystem QgsMemoryProvider::crs() const
397 : : {
398 : : // TODO: make provider projection-aware
399 : 14 : return mCrs; // return default CRS
400 : : }
401 : :
402 : 0 : void QgsMemoryProvider::handlePostCloneOperations( QgsVectorDataProvider *source )
403 : : {
404 : 0 : if ( QgsMemoryProvider *other = qobject_cast< QgsMemoryProvider * >( source ) )
405 : : {
406 : : // these properties aren't copied when cloning a memory provider by uri, so we need to do it manually
407 : 0 : mFeatures = other->mFeatures;
408 : 0 : mNextFeatureId = other->mNextFeatureId;
409 : 0 : mExtent = other->mExtent;
410 : 0 : }
411 : 0 : }
412 : :
413 : : // returns TRUE if all features were added successfully, or FALSE if any feature could not be added
414 : 13 : bool QgsMemoryProvider::addFeatures( QgsFeatureList &flist, Flags flags )
415 : : {
416 : 13 : bool result = true;
417 : : // whether or not to update the layer extent on the fly as we add features
418 : 13 : bool updateExtent = mFeatures.isEmpty() || !mExtent.isEmpty();
419 : :
420 : 13 : int fieldCount = mFields.count();
421 : :
422 : : // For rollback
423 : 13 : const auto oldExtent { mExtent };
424 : 13 : const auto oldNextFeatureId { mNextFeatureId };
425 : 13 : QgsFeatureIds addedFids ;
426 : :
427 : 30 : for ( QgsFeatureList::iterator it = flist.begin(); it != flist.end() && result ; ++it )
428 : : {
429 : 17 : it->setId( mNextFeatureId );
430 : 17 : it->setValid( true );
431 : 17 : if ( it->attributes().count() < fieldCount )
432 : : {
433 : : // ensure features have the correct number of attributes by padding
434 : : // them with null attributes for missing values
435 : 0 : QgsAttributes attributes = it->attributes();
436 : 0 : for ( int i = it->attributes().count(); i < mFields.count(); ++i )
437 : : {
438 : 0 : attributes.append( QVariant( mFields.at( i ).type() ) );
439 : 0 : }
440 : 0 : it->setAttributes( attributes );
441 : 0 : }
442 : 17 : else if ( it->attributes().count() > fieldCount )
443 : : {
444 : : // too many attributes
445 : 0 : pushError( tr( "Feature has too many attributes (expecting %1, received %2)" ).arg( fieldCount ).arg( it->attributes().count() ) );
446 : 0 : QgsAttributes attributes = it->attributes();
447 : 0 : attributes.resize( mFields.count() );
448 : 0 : it->setAttributes( attributes );
449 : 0 : }
450 : :
451 : 17 : if ( it->hasGeometry() && mWkbType == QgsWkbTypes::NoGeometry )
452 : : {
453 : 0 : it->clearGeometry();
454 : 0 : }
455 : 34 : else if ( it->hasGeometry() && QgsWkbTypes::geometryType( it->geometry().wkbType() ) !=
456 : 17 : QgsWkbTypes::geometryType( mWkbType ) )
457 : : {
458 : 0 : pushError( tr( "Could not add feature with geometry type %1 to layer of type %2" ).arg( QgsWkbTypes::displayString( it->geometry().wkbType() ),
459 : 0 : QgsWkbTypes::displayString( mWkbType ) ) );
460 : 0 : result = false;
461 : 0 : continue;
462 : : }
463 : :
464 : : // Check attribute conversion
465 : 17 : bool conversionError { false };
466 : 17 : QString errorMessage;
467 : 17 : for ( int i = 0; i < mFields.count(); ++i )
468 : : {
469 : 0 : const QVariant originalValue = it->attribute( i );
470 : 0 : QVariant attrValue = originalValue;
471 : 0 : if ( ! attrValue.isNull() && ! mFields.at( i ).convertCompatible( attrValue, &errorMessage ) )
472 : : {
473 : : // Push first conversion error only
474 : 0 : if ( result )
475 : : {
476 : 0 : pushError( tr( "Could not store attribute \"%1\": %2" )
477 : 0 : .arg( mFields.at( i ).name(), errorMessage ) );
478 : 0 : }
479 : 0 : result = false;
480 : 0 : conversionError = true;
481 : 0 : continue;
482 : : }
483 : 0 : else if ( attrValue.type() != originalValue.type() )
484 : : {
485 : : // convertCompatible has resulted in a data type conversion
486 : 0 : it->setAttribute( i, attrValue );
487 : 0 : }
488 : 0 : }
489 : :
490 : : // Skip the feature if there is at least one conversion error
491 : 17 : if ( conversionError )
492 : : {
493 : 0 : if ( flags.testFlag( QgsFeatureSink::Flag::RollBackOnErrors ) )
494 : : {
495 : 0 : break;
496 : : }
497 : 0 : continue;
498 : : }
499 : :
500 : 17 : mFeatures.insert( mNextFeatureId, *it );
501 : 17 : addedFids.insert( mNextFeatureId );
502 : :
503 : 17 : if ( it->hasGeometry() )
504 : : {
505 : 17 : if ( updateExtent )
506 : 17 : mExtent.combineExtentWith( it->geometry().boundingBox() );
507 : :
508 : : // update spatial index
509 : 17 : if ( mSpatialIndex )
510 : 0 : mSpatialIndex->addFeature( *it );
511 : 17 : }
512 : :
513 : 17 : mNextFeatureId++;
514 : 17 : }
515 : :
516 : : // Roll back
517 : 13 : if ( ! result && flags.testFlag( QgsFeatureSink::Flag::RollBackOnErrors ) )
518 : : {
519 : 0 : for ( const QgsFeatureId &addedFid : addedFids )
520 : : {
521 : 0 : mFeatures.remove( addedFid );
522 : : }
523 : 0 : mExtent = oldExtent;
524 : 0 : mNextFeatureId = oldNextFeatureId;
525 : 0 : }
526 : : else
527 : : {
528 : 13 : clearMinMaxCache();
529 : : }
530 : :
531 : 13 : return result;
532 : 13 : }
533 : :
534 : 0 : bool QgsMemoryProvider::deleteFeatures( const QgsFeatureIds &id )
535 : : {
536 : 0 : for ( QgsFeatureIds::const_iterator it = id.begin(); it != id.end(); ++it )
537 : : {
538 : 0 : QgsFeatureMap::iterator fit = mFeatures.find( *it );
539 : :
540 : : // check whether such feature exists
541 : 0 : if ( fit == mFeatures.end() )
542 : 0 : continue;
543 : :
544 : : // update spatial index
545 : 0 : if ( mSpatialIndex )
546 : 0 : mSpatialIndex->deleteFeature( *fit );
547 : :
548 : 0 : mFeatures.erase( fit );
549 : 0 : }
550 : :
551 : 0 : updateExtents();
552 : 0 : clearMinMaxCache();
553 : :
554 : 0 : return true;
555 : : }
556 : :
557 : 0 : bool QgsMemoryProvider::addAttributes( const QList<QgsField> &attributes )
558 : : {
559 : 0 : for ( QList<QgsField>::const_iterator it = attributes.begin(); it != attributes.end(); ++it )
560 : : {
561 : 0 : switch ( it->type() )
562 : : {
563 : : case QVariant::Int:
564 : : case QVariant::Double:
565 : : case QVariant::String:
566 : : case QVariant::Date:
567 : : case QVariant::Time:
568 : : case QVariant::DateTime:
569 : : case QVariant::LongLong:
570 : : case QVariant::StringList:
571 : : case QVariant::List:
572 : : case QVariant::Bool:
573 : : case QVariant::ByteArray:
574 : 0 : break;
575 : : default:
576 : 0 : QgsDebugMsg( "Field type not supported: " + it->typeName() );
577 : 0 : continue;
578 : : }
579 : : // add new field as a last one
580 : 0 : mFields.append( *it );
581 : :
582 : 0 : for ( QgsFeatureMap::iterator fit = mFeatures.begin(); fit != mFeatures.end(); ++fit )
583 : : {
584 : 0 : QgsFeature &f = fit.value();
585 : 0 : QgsAttributes attr = f.attributes();
586 : 0 : attr.append( QVariant() );
587 : 0 : f.setAttributes( attr );
588 : 0 : }
589 : 0 : }
590 : 0 : return true;
591 : 0 : }
592 : :
593 : 0 : bool QgsMemoryProvider::renameAttributes( const QgsFieldNameMap &renamedAttributes )
594 : : {
595 : 0 : QgsFieldNameMap::const_iterator renameIt = renamedAttributes.constBegin();
596 : 0 : bool result = true;
597 : 0 : for ( ; renameIt != renamedAttributes.constEnd(); ++renameIt )
598 : : {
599 : 0 : int fieldIndex = renameIt.key();
600 : 0 : if ( fieldIndex < 0 || fieldIndex >= mFields.count() )
601 : : {
602 : 0 : result = false;
603 : 0 : continue;
604 : : }
605 : 0 : if ( mFields.indexFromName( renameIt.value() ) >= 0 )
606 : : {
607 : : //field name already in use
608 : 0 : result = false;
609 : 0 : continue;
610 : : }
611 : :
612 : 0 : mFields.rename( fieldIndex, renameIt.value() );
613 : 0 : }
614 : 0 : return result;
615 : : }
616 : :
617 : 0 : bool QgsMemoryProvider::deleteAttributes( const QgsAttributeIds &attributes )
618 : : {
619 : 0 : QList<int> attrIdx = qgis::setToList( attributes );
620 : 0 : std::sort( attrIdx.begin(), attrIdx.end(), std::greater<int>() );
621 : :
622 : : // delete attributes one-by-one with decreasing index
623 : 0 : for ( QList<int>::const_iterator it = attrIdx.constBegin(); it != attrIdx.constEnd(); ++it )
624 : : {
625 : 0 : int idx = *it;
626 : 0 : mFields.remove( idx );
627 : :
628 : 0 : for ( QgsFeatureMap::iterator fit = mFeatures.begin(); fit != mFeatures.end(); ++fit )
629 : : {
630 : 0 : QgsFeature &f = fit.value();
631 : 0 : QgsAttributes attr = f.attributes();
632 : 0 : attr.remove( idx );
633 : 0 : f.setAttributes( attr );
634 : 0 : }
635 : 0 : }
636 : 0 : clearMinMaxCache();
637 : : return true;
638 : 0 : }
639 : :
640 : 0 : bool QgsMemoryProvider::changeAttributeValues( const QgsChangedAttributesMap &attr_map )
641 : : {
642 : 0 : bool result { true };
643 : :
644 : 0 : QgsChangedAttributesMap rollBackMap;
645 : :
646 : 0 : QString errorMessage;
647 : 0 : for ( QgsChangedAttributesMap::const_iterator it = attr_map.begin(); it != attr_map.end(); ++it )
648 : : {
649 : 0 : QgsFeatureMap::iterator fit = mFeatures.find( it.key() );
650 : 0 : if ( fit == mFeatures.end() )
651 : 0 : continue;
652 : :
653 : 0 : const QgsAttributeMap &attrs = it.value();
654 : 0 : QgsAttributeMap rollBackAttrs;
655 : :
656 : : // Break on errors
657 : 0 : for ( QgsAttributeMap::const_iterator it2 = attrs.constBegin(); it2 != attrs.constEnd(); ++it2 )
658 : : {
659 : 0 : QVariant attrValue = it2.value();
660 : : // Check attribute conversion
661 : 0 : const bool conversionError { ! attrValue.isNull()
662 : 0 : && ! mFields.at( it2.key() ).convertCompatible( attrValue, &errorMessage ) };
663 : 0 : if ( conversionError )
664 : : {
665 : : // Push first conversion error only
666 : 0 : if ( result )
667 : : {
668 : 0 : pushError( tr( "Could not change attribute %1 having type %2 for feature %4: %3" )
669 : 0 : .arg( mFields.at( it2.key() ).name(), it2.value( ).typeName(),
670 : 0 : errorMessage ).arg( it.key() ) );
671 : 0 : }
672 : 0 : result = false;
673 : 0 : break;
674 : : }
675 : 0 : rollBackAttrs.insert( it2.key(), fit->attribute( it2.key() ) );
676 : 0 : fit->setAttribute( it2.key(), attrValue );
677 : 0 : }
678 : 0 : rollBackMap.insert( it.key(), rollBackAttrs );
679 : 0 : }
680 : :
681 : : // Roll back
682 : 0 : if ( ! result )
683 : : {
684 : 0 : changeAttributeValues( rollBackMap );
685 : 0 : }
686 : : else
687 : : {
688 : 0 : clearMinMaxCache();
689 : : }
690 : 0 : return result;
691 : 0 : }
692 : :
693 : 0 : bool QgsMemoryProvider::changeGeometryValues( const QgsGeometryMap &geometry_map )
694 : : {
695 : 0 : for ( QgsGeometryMap::const_iterator it = geometry_map.begin(); it != geometry_map.end(); ++it )
696 : : {
697 : 0 : QgsFeatureMap::iterator fit = mFeatures.find( it.key() );
698 : 0 : if ( fit == mFeatures.end() )
699 : 0 : continue;
700 : :
701 : : // update spatial index
702 : 0 : if ( mSpatialIndex )
703 : 0 : mSpatialIndex->deleteFeature( *fit );
704 : :
705 : 0 : fit->setGeometry( it.value() );
706 : :
707 : : // update spatial index
708 : 0 : if ( mSpatialIndex )
709 : 0 : mSpatialIndex->addFeature( *fit );
710 : 0 : }
711 : :
712 : 0 : updateExtents();
713 : :
714 : 0 : return true;
715 : : }
716 : :
717 : 0 : QString QgsMemoryProvider::subsetString() const
718 : : {
719 : 0 : return mSubsetString;
720 : : }
721 : :
722 : 0 : bool QgsMemoryProvider::setSubsetString( const QString &theSQL, bool updateFeatureCount )
723 : : {
724 : : Q_UNUSED( updateFeatureCount )
725 : :
726 : 0 : if ( !theSQL.isEmpty() )
727 : : {
728 : 0 : QgsExpression tempExpression( theSQL );
729 : 0 : if ( tempExpression.hasParserError() )
730 : 0 : return false;
731 : 0 : }
732 : :
733 : 0 : if ( theSQL == mSubsetString )
734 : 0 : return true;
735 : :
736 : 0 : mSubsetString = theSQL;
737 : 0 : clearMinMaxCache();
738 : 0 : mExtent.setMinimal();
739 : :
740 : 0 : emit dataChanged();
741 : 0 : return true;
742 : 0 : }
743 : :
744 : 0 : bool QgsMemoryProvider::createSpatialIndex()
745 : : {
746 : 0 : if ( !mSpatialIndex )
747 : : {
748 : 0 : mSpatialIndex = new QgsSpatialIndex();
749 : :
750 : : // add existing features to index
751 : 0 : for ( QgsFeatureMap::iterator it = mFeatures.begin(); it != mFeatures.end(); ++it )
752 : : {
753 : 0 : mSpatialIndex->addFeature( *it );
754 : 0 : }
755 : 0 : }
756 : 0 : return true;
757 : 0 : }
758 : :
759 : 0 : QgsFeatureSource::SpatialIndexPresence QgsMemoryProvider::hasSpatialIndex() const
760 : : {
761 : 0 : return mSpatialIndex ? SpatialIndexPresent : SpatialIndexNotPresent;
762 : : }
763 : :
764 : 60 : QgsVectorDataProvider::Capabilities QgsMemoryProvider::capabilities() const
765 : : {
766 : 120 : return AddFeatures | DeleteFeatures | ChangeGeometries |
767 : 60 : ChangeAttributeValues | AddAttributes | DeleteAttributes | RenameAttributes | CreateSpatialIndex |
768 : 60 : SelectAtId | CircularGeometries | FastTruncate;
769 : : }
770 : :
771 : 0 : bool QgsMemoryProvider::truncate()
772 : : {
773 : 0 : mFeatures.clear();
774 : 0 : clearMinMaxCache();
775 : 0 : mExtent.setMinimal();
776 : 0 : return true;
777 : : }
778 : :
779 : 0 : void QgsMemoryProvider::updateExtents()
780 : : {
781 : 0 : mExtent.setMinimal();
782 : 0 : }
783 : :
784 : 1 : QString QgsMemoryProvider::name() const
785 : : {
786 : 2 : return TEXT_PROVIDER_KEY;
787 : : }
788 : :
789 : 0 : QString QgsMemoryProvider::description() const
790 : : {
791 : 0 : return TEXT_PROVIDER_DESCRIPTION;
792 : : }
793 : :
794 : : ///@endcond
|