Skip to content
Permalink
Browse files

[API] Introduce optional QgsVectorDataProvider::ChangeFeatures

capability and changeFeaturss method to allows joint geometry and
attribute updates.

Currently only implemented in the postgres provider
  • Loading branch information
jef-n committed Jan 15, 2016
1 parent c063838 commit ad1fd2a922777ad048f2726341e9c09bc39128b9
@@ -39,6 +39,7 @@ class QgsVectorDataProvider : QgsDataProvider
RandomSelectGeometryAtId,
/** DEPRECATED - do not use */
SequentialSelectGeometryAtId,
/** DEPRECATED - do not use */

This comment has been minimized.

Copy link
@nyalldawson

nyalldawson Jul 25, 2016

Collaborator

@jef-n what was the motivation behind deprecating CreateAttributeIndex? It's still used + useful.

CreateAttributeIndex,
/** Allows user to select encoding */
SelectEncoding,
@@ -47,7 +48,13 @@ class QgsVectorDataProvider : QgsDataProvider
/** Supports topological simplification of geometries on provider side according to a distance tolerance */
SimplifyGeometriesWithTopologicalValidation,
/** Supports transactions*/
TransactionSupport
TransactionSupport,
/** Supports circular geometry types (circularstring, compoundcurve, curvepolygon)*/
CircularGeometries,
/** Supports joint updates for attributes and geometry
* Providers supporting this should still define ChangeGeometries | ChangeAttributeValues
*/
ChangeFeatures
};

/** Bitmask of all provider's editing capabilities */
@@ -189,6 +196,17 @@ class QgsVectorDataProvider : QgsDataProvider
*/
virtual bool changeAttributeValues( const QMap<qint64, QMap<int, QVariant> > &attr_map );

/**
* Changes attribute values and geometries of existing features.
* @param attr_map a map containing changed attributes
* @param geometry_map A QgsGeometryMap whose index contains the feature IDs
* that will have their geometries changed.
* The second map parameter being the new geometries themselves
* @return true in case of success and false in case of failure
*/
virtual bool changeFeatures( const QMap<qint64, QMap<int, QVariant> > &attr_map,
const QMap<qint64, QgsGeometry> &geometry_map );

/**
* Returns the default value for field specified by @c fieldId
*/
@@ -201,7 +219,7 @@ class QgsVectorDataProvider : QgsDataProvider
* The second map parameter being the new geometries themselves
* @return True in case of success and false in case of failure
*/
virtual bool changeGeometryValues( QMap<qint64, QgsGeometry> & geometry_map );
virtual bool changeGeometryValues( const QMap<qint64, QgsGeometry> &geometry_map );

/**
* Creates a spatial index on the datasource (if supported by the provider type).
@@ -320,6 +338,13 @@ class QgsVectorDataProvider : QgsDataProvider
*/
virtual QgsTransaction* transaction() const;

/**
* Forces a reload of the underlying datasource if the provider implements this
* method.
* In particular on the OGR provider, a pooled connection will be invalidated.
* This forces QGIS to reopen a file or connection.
* This can be required if the underlying file is replaced.
*/
virtual void forceReload();

/**
@@ -87,12 +87,20 @@ QVariant QgsVectorDataProvider::defaultValue( int fieldId )
return QVariant();
}

bool QgsVectorDataProvider::changeGeometryValues( QgsGeometryMap &geometry_map )
bool QgsVectorDataProvider::changeGeometryValues( const QgsGeometryMap &geometry_map )
{
Q_UNUSED( geometry_map );
return false;
}

bool QgsVectorDataProvider::changeFeatures( const QgsChangedAttributesMap &attr_map,
const QgsGeometryMap &geometry_map )
{
Q_UNUSED( attr_map );
Q_UNUSED( geometry_map );
return false;
}

bool QgsVectorDataProvider::createSpatialIndex()
{
return false;
@@ -206,8 +214,13 @@ QString QgsVectorDataProvider::capabilitiesString() const
QgsDebugMsg( "Capability: Simplify Geometries before fetch the feature ensuring that the result is a valid geometry" );
}

return abilitiesList.join( ", " );
if ( abilities & QgsVectorDataProvider::ChangeFeatures )
{
abilitiesList += tr( "Change Geometries and Attributes at once" );
QgsDebugMsg( "Capability: change attributes and geometries at once" );
}

return abilitiesList.join( ", " );
}


@@ -61,42 +61,47 @@ class CORE_EXPORT QgsVectorDataProvider : public QgsDataProvider
enum Capability
{
/** Provider has no capabilities */
NoCapabilities = 0,
NoCapabilities = 0,
/** Allows adding features */
AddFeatures = 1,
AddFeatures = 1,
/** Allows deletion of features */
DeleteFeatures = 1 << 1,
DeleteFeatures = 1 << 1,
/** Allows modification of attribute values */
ChangeAttributeValues = 1 << 2,
ChangeAttributeValues = 1 << 2,
/** Allows addition of new attributes (fields) */
AddAttributes = 1 << 3,
AddAttributes = 1 << 3,
/** Allows deletion of attributes (fields) */
DeleteAttributes = 1 << 4,
DeleteAttributes = 1 << 4,
/** DEPRECATED - do not use */
SaveAsShapefile = 1 << 5,
SaveAsShapefile = 1 << 5,
/** Allows creation of spatial index */
CreateSpatialIndex = 1 << 6,
CreateSpatialIndex = 1 << 6,
/** Fast access to features using their ID */
SelectAtId = 1 << 7,
SelectAtId = 1 << 7,
/** Allows modifications of geometries */
ChangeGeometries = 1 << 8,
ChangeGeometries = 1 << 8,
/** DEPRECATED - do not use */
SelectGeometryAtId = 1 << 9,
SelectGeometryAtId = 1 << 9,
/** DEPRECATED - do not use */
RandomSelectGeometryAtId = 1 << 10,
RandomSelectGeometryAtId = 1 << 10,
/** DEPRECATED - do not use */
SequentialSelectGeometryAtId = 1 << 11,
CreateAttributeIndex = 1 << 12,
SequentialSelectGeometryAtId = 1 << 11,
/** DEPRECATED - do not use */
CreateAttributeIndex = 1 << 12,
/** Allows user to select encoding */
SelectEncoding = 1 << 13,
SelectEncoding = 1 << 13,
/** Supports simplification of geometries on provider side according to a distance tolerance */
SimplifyGeometries = 1 << 14,
SimplifyGeometries = 1 << 14,
/** Supports topological simplification of geometries on provider side according to a distance tolerance */
SimplifyGeometriesWithTopologicalValidation = 1 << 15,
/** Supports transactions*/
TransactionSupport = 1 << 16,
TransactionSupport = 1 << 16,
/** Supports circular geometry types (circularstring, compoundcurve, curvepolygon)*/
CircularGeometries = 1 << 17
CircularGeometries = 1 << 17,
/** Supports joint updates for attributes and geometry
* Providers supporting this should still define ChangeGeometries | ChangeAttributeValues
*/
ChangeFeatures = 1 << 18
};

/** Bitmask of all provider's editing capabilities */
@@ -239,6 +244,16 @@ class CORE_EXPORT QgsVectorDataProvider : public QgsDataProvider
*/
virtual bool changeAttributeValues( const QgsChangedAttributesMap &attr_map );

/**
* Changes attribute values and geometries of existing features.
* @param attr_map a map containing changed attributes
* @param geometry_map A QgsGeometryMap whose index contains the feature IDs
* that will have their geometries changed.
* The second map parameter being the new geometries themselves
* @return true in case of success and false in case of failure
*/
virtual bool changeFeatures( const QgsChangedAttributesMap &attr_map, const QgsGeometryMap &geometry_map );

/**
* Returns the default value for field specified by @c fieldId
*/
@@ -251,7 +266,7 @@ class CORE_EXPORT QgsVectorDataProvider : public QgsDataProvider
* The second map parameter being the new geometries themselves
* @return True in case of success and false in case of failure
*/
virtual bool changeGeometryValues( QgsGeometryMap & geometry_map );
virtual bool changeGeometryValues( const QgsGeometryMap &geometry_map );

/**
* Creates a spatial index on the datasource (if supported by the provider type).
@@ -271,17 +271,21 @@ bool QgsVectorLayerEditBuffer::commitChanges( QStringList& commitErrors )
int cap = provider->capabilities();
bool success = true;

// geometry updates attribute updates
// yes no => changeGeometryValues
// no yes => changeAttributeValues
// yes yes => changeFeatures

//
// update geometries
//
if ( !mChangedGeometries.isEmpty() )
if ( !mChangedGeometries.isEmpty() && (( cap & QgsVectorDataProvider::ChangeFeatures ) == 0 || mChangedAttributeValues.isEmpty() ) )
{
if (( cap & QgsVectorDataProvider::ChangeGeometries ) && provider->changeGeometryValues( mChangedGeometries ) )
if ( provider->changeGeometryValues( mChangedGeometries ) )
{
commitErrors << tr( "SUCCESS: %n geometries were changed.", "changed geometries count", mChangedGeometries.size() );

emit committedGeometriesChanges( L->id(), mChangedGeometries );

mChangedGeometries.clear();
}
else
@@ -398,36 +402,56 @@ bool QgsVectorLayerEditBuffer::commitChanges( QStringList& commitErrors )

if ( attributeChangesOk )
{
//
// change attributes
//
if ( !mChangedAttributeValues.isEmpty() )
if ( cap & QgsVectorDataProvider::ChangeFeatures && !mChangedGeometries.isEmpty() && !mChangedAttributeValues.isEmpty() )
{
if (( cap & QgsVectorDataProvider::ChangeAttributeValues ) && provider->changeAttributeValues( mChangedAttributeValues ) )
{
commitErrors << tr( "SUCCESS: %n attribute value(s) changed.", "changed attribute values count", mChangedAttributeValues.size() );
Q_ASSERT(( cap & ( QgsVectorDataProvider::ChangeAttributeValues | QgsVectorDataProvider::ChangeGeometries ) ) == ( QgsVectorDataProvider::ChangeAttributeValues | QgsVectorDataProvider::ChangeGeometries ) );

if ( provider->changeFeatures( mChangedAttributeValues, mChangedGeometries ) )
{
commitErrors << tr( "SUCCESS: %1 attribute value(s) and %2 geometries changed." ).arg( mChangedAttributeValues.size(), mChangedGeometries.size() );
emit committedAttributeValuesChanges( L->id(), mChangedAttributeValues );

mChangedAttributeValues.clear();

emit committedGeometriesChanges( L->id(), mChangedGeometries );
mChangedGeometries.clear();
}
else
{
commitErrors << tr( "ERROR: %n attribute value change(s) not applied.", "not changed attribute values count", mChangedAttributeValues.size() );
#if 0
QString list = "ERROR: pending changes:";
Q_FOREACH ( QgsFeatureId id, mChangedAttributeValues.keys() )
success = false;
}
}
else
{
//
// change attributes
//
if ( !mChangedAttributeValues.isEmpty() && (( cap & QgsVectorDataProvider::ChangeFeatures ) == 0 || mChangedGeometries.isEmpty() ) )
{
if (( cap & QgsVectorDataProvider::ChangeAttributeValues ) && provider->changeAttributeValues( mChangedAttributeValues ) )
{
commitErrors << tr( "SUCCESS: %n attribute value(s) changed.", "changed attribute values count", mChangedAttributeValues.size() );

emit committedAttributeValuesChanges( L->id(), mChangedAttributeValues );
mChangedAttributeValues.clear();
}
else
{
list.append( "\n " + FID_TO_STRING( id ) + '[' );
Q_FOREACH ( int idx, mChangedAttributeValues[ id ].keys() )
commitErrors << tr( "ERROR: %n attribute value change(s) not applied.", "not changed attribute values count", mChangedAttributeValues.size() );
#if 0
QString list = "ERROR: pending changes:";
Q_FOREACH ( QgsFeatureId id, mChangedAttributeValues.keys() )
{
list.append( QString( " %1:%2" ).arg( L->pendingFields().at( idx ).name() ).arg( mChangedAttributeValues[id][idx].toString() ) );
list.append( "\n " + FID_TO_STRING( id ) + '[' );
Q_FOREACH ( int idx, mChangedAttributeValues[ id ].keys() )
{
list.append( QString( " %1:%2" ).arg( L->pendingFields().at( idx ).name() ).arg( mChangedAttributeValues[id][idx].toString() ) );
}
list.append( " ]" );
}
list.append( " ]" );
}
commitErrors << list;
commitErrors << list;
#endif
success = false;
success = false;
}
}
}

@@ -133,7 +133,7 @@ class GRASS_LIB_EXPORT QgsGrassProvider : public QgsVectorDataProvider
virtual bool addAttributes( const QList<QgsField> &attributes ) override;
virtual bool deleteAttributes( const QgsAttributeIds &attributes ) override;
virtual bool changeAttributeValues( const QgsChangedAttributesMap & attr_map ) override { Q_UNUSED( attr_map ); return true; }
virtual bool changeGeometryValues( QgsGeometryMap & geometry_map ) override { Q_UNUSED( geometry_map ); return true; }
virtual bool changeGeometryValues( const QgsGeometryMap &geometry_map ) override { Q_UNUSED( geometry_map ); return true; }


//----------------------------------------------------------------------------
@@ -392,7 +392,7 @@ bool QgsMemoryProvider::deleteAttributes( const QgsAttributeIds& attributes )
return true;
}

bool QgsMemoryProvider::changeAttributeValues( const QgsChangedAttributesMap & attr_map )
bool QgsMemoryProvider::changeAttributeValues( const QgsChangedAttributesMap &attr_map )
{
for ( QgsChangedAttributesMap::const_iterator it = attr_map.begin(); it != attr_map.end(); ++it )
{
@@ -407,7 +407,7 @@ bool QgsMemoryProvider::changeAttributeValues( const QgsChangedAttributesMap & a
return true;
}

bool QgsMemoryProvider::changeGeometryValues( QgsGeometryMap & geometry_map )
bool QgsMemoryProvider::changeGeometryValues( const QgsGeometryMap &geometry_map )
{
for ( QgsGeometryMap::const_iterator it = geometry_map.begin(); it != geometry_map.end(); ++it )
{
@@ -109,7 +109,7 @@ class QgsMemoryProvider : public QgsVectorDataProvider
* the second map parameter being the new geometries themselves
* @return true in case of success and false in case of failure
*/
virtual bool changeGeometryValues( QgsGeometryMap & geometry_map ) override;
virtual bool changeGeometryValues( const QgsGeometryMap & geometry_map ) override;

/** Accessor for sql where clause used to limit dataset */
QString subsetString() override;
@@ -1167,15 +1167,15 @@ bool QgsMssqlProvider::changeAttributeValues( const QgsChangedAttributesMap &att
return true;
}

bool QgsMssqlProvider::changeGeometryValues( QgsGeometryMap & geometry_map )
bool QgsMssqlProvider::changeGeometryValues( const QgsGeometryMap &geometry_map )
{
if ( geometry_map.isEmpty() )
return true;

if ( mFidColName.isEmpty() )
return false;

for ( QgsGeometryMap::iterator it = geometry_map.begin(); it != geometry_map.end(); ++it )
for ( QgsGeometryMap::const_iterator it = geometry_map.constBegin(); it != geometry_map.constEnd(); ++it )
{
QgsFeatureId fid = it.key();
// skip added features
@@ -207,10 +207,10 @@ class QgsMssqlProvider : public QgsVectorDataProvider
virtual bool deleteAttributes( const QgsAttributeIds &attributes ) override;

/** Changes attribute values of existing features */
virtual bool changeAttributeValues( const QgsChangedAttributesMap & attr_map ) override;
virtual bool changeAttributeValues( const QgsChangedAttributesMap &attr_map ) override;

/** Changes existing geometries*/
virtual bool changeGeometryValues( QgsGeometryMap & geometry_map ) override;
virtual bool changeGeometryValues( const QgsGeometryMap &geometry_map ) override;

/**
* Create a spatial index for the current layer
@@ -1261,14 +1261,14 @@ bool QgsOgrProvider::changeAttributeValues( const QgsChangedAttributesMap &attr_
return true;
}

bool QgsOgrProvider::changeGeometryValues( QgsGeometryMap & geometry_map )
bool QgsOgrProvider::changeGeometryValues( const QgsGeometryMap &geometry_map )
{
OGRFeatureH theOGRFeature = nullptr;
OGRGeometryH theNewGeometry = nullptr;

setRelevantFields( ogrLayer, true, attributeIndexes() );

for ( QgsGeometryMap::iterator it = geometry_map.begin(); it != geometry_map.end(); ++it )
for ( QgsGeometryMap::const_iterator it = geometry_map.constBegin(); it != geometry_map.constEnd(); ++it )
{
if ( FID_TO_NUMBER( it.key() ) > std::numeric_limits<long>::max() )
{
@@ -152,10 +152,10 @@ class QgsOgrProvider : public QgsVectorDataProvider
virtual bool deleteAttributes( const QgsAttributeIds &attributes ) override;

/** Changes attribute values of existing features */
virtual bool changeAttributeValues( const QgsChangedAttributesMap & attr_map ) override;
virtual bool changeAttributeValues( const QgsChangedAttributesMap &attr_map ) override;

/** Changes existing geometries*/
virtual bool changeGeometryValues( QgsGeometryMap & geometry_map ) override;
virtual bool changeGeometryValues( const QgsGeometryMap &geometry_map ) override;

/** Tries to create a .qix index file for faster access if only a subset of the features is required
@return true in case of success*/

2 comments on commit ad1fd2a

@3nids

This comment has been minimized.

Copy link
Member

@3nids 3nids replied Jan 15, 2016

so cool, will test!!!!
thanks Jürgen!

@3nids

This comment has been minimized.

Copy link
Member

@3nids 3nids replied Jan 15, 2016

works like a charm, thanks!

Please sign in to comment.
You can’t perform that action at this time.