diff --git a/python/PyQt6/core/auto_generated/vector/qgsvectorlayercache.sip.in b/python/PyQt6/core/auto_generated/vector/qgsvectorlayercache.sip.in index de67128445e2..03dc2e84bd42 100644 --- a/python/PyQt6/core/auto_generated/vector/qgsvectorlayercache.sip.in +++ b/python/PyQt6/core/auto_generated/vector/qgsvectorlayercache.sip.in @@ -209,6 +209,25 @@ Considers the changed, added, deleted and permanent features .. seealso:: :py:func:`featureAtId` .. versionadded:: 3.32 +%End + + bool featureAtIdWithAllAttributesAndGeometry( QgsFeatureId featureId, QgsFeature &feature, bool skipCache = false ); +%Docstring +Gets the feature at the given feature id with all attributes and geometry, if the cached feature +already contains all attributes and geometry, calling this function has the same effect as calling +:py:func:`~QgsVectorLayerCache.featureAtId`. + +Considers the changed, added, deleted and permanent features + +:param featureId: The id of the feature to query +:param feature: The result of the operation will be written to this feature +:param skipCache: Will query the layer regardless if the feature is in the cache already + +:return: ``True`` in case of success + +.. seealso:: :py:func:`featureAtId` + +.. versionadded:: 3.42 %End bool removeCachedFeature( QgsFeatureId fid ); diff --git a/python/PyQt6/gui/auto_additions/qgsfeaturelistmodel.py b/python/PyQt6/gui/auto_additions/qgsfeaturelistmodel.py index a1be789fd7cd..ea83dfff9cd0 100644 --- a/python/PyQt6/gui/auto_additions/qgsfeaturelistmodel.py +++ b/python/PyQt6/gui/auto_additions/qgsfeaturelistmodel.py @@ -1,6 +1,7 @@ # The following has been generated automatically from src/gui/attributetable/qgsfeaturelistmodel.h QgsFeatureListModel.FeatureInfoRole = QgsFeatureListModel.Role.FeatureInfoRole QgsFeatureListModel.FeatureRole = QgsFeatureListModel.Role.FeatureRole +QgsFeatureListModel.FeatureWithGeometryRole = QgsFeatureListModel.Role.FeatureWithGeometryRole try: QgsFeatureListModel.FeatureInfo.__attribute_docs__ = {'isNew': 'True if feature is a newly added feature.', 'isEdited': 'True if feature has been edited.'} QgsFeatureListModel.FeatureInfo.__group__ = ['attributetable'] diff --git a/python/PyQt6/gui/auto_generated/attributetable/qgsfeaturelistmodel.sip.in b/python/PyQt6/gui/auto_generated/attributetable/qgsfeaturelistmodel.sip.in index daf64fdf4c39..9b9f8d126a3d 100644 --- a/python/PyQt6/gui/auto_generated/attributetable/qgsfeaturelistmodel.sip.in +++ b/python/PyQt6/gui/auto_generated/attributetable/qgsfeaturelistmodel.sip.in @@ -30,7 +30,8 @@ class QgsFeatureListModel : QSortFilterProxyModel, QgsFeatureModel enum Role /BaseType=IntEnum/ { FeatureInfoRole, - FeatureRole + FeatureRole, + FeatureWithGeometryRole, }; public: diff --git a/python/core/auto_generated/vector/qgsvectorlayercache.sip.in b/python/core/auto_generated/vector/qgsvectorlayercache.sip.in index 3213aab6111d..7026400c04dc 100644 --- a/python/core/auto_generated/vector/qgsvectorlayercache.sip.in +++ b/python/core/auto_generated/vector/qgsvectorlayercache.sip.in @@ -209,6 +209,25 @@ Considers the changed, added, deleted and permanent features .. seealso:: :py:func:`featureAtId` .. versionadded:: 3.32 +%End + + bool featureAtIdWithAllAttributesAndGeometry( QgsFeatureId featureId, QgsFeature &feature, bool skipCache = false ); +%Docstring +Gets the feature at the given feature id with all attributes and geometry, if the cached feature +already contains all attributes and geometry, calling this function has the same effect as calling +:py:func:`~QgsVectorLayerCache.featureAtId`. + +Considers the changed, added, deleted and permanent features + +:param featureId: The id of the feature to query +:param feature: The result of the operation will be written to this feature +:param skipCache: Will query the layer regardless if the feature is in the cache already + +:return: ``True`` in case of success + +.. seealso:: :py:func:`featureAtId` + +.. versionadded:: 3.42 %End bool removeCachedFeature( QgsFeatureId fid ); diff --git a/python/gui/auto_generated/attributetable/qgsfeaturelistmodel.sip.in b/python/gui/auto_generated/attributetable/qgsfeaturelistmodel.sip.in index d004de50d7ff..977c641fee83 100644 --- a/python/gui/auto_generated/attributetable/qgsfeaturelistmodel.sip.in +++ b/python/gui/auto_generated/attributetable/qgsfeaturelistmodel.sip.in @@ -30,7 +30,8 @@ class QgsFeatureListModel : QSortFilterProxyModel, QgsFeatureModel enum Role { FeatureInfoRole, - FeatureRole + FeatureRole, + FeatureWithGeometryRole, }; public: diff --git a/src/core/vector/qgsvectorlayercache.cpp b/src/core/vector/qgsvectorlayercache.cpp index e1e64daa8e11..49e507fc9f14 100644 --- a/src/core/vector/qgsvectorlayercache.cpp +++ b/src/core/vector/qgsvectorlayercache.cpp @@ -220,6 +220,33 @@ bool QgsVectorLayerCache::featureAtIdWithAllAttributes( QgsFeatureId featureId, return featureFound; } +bool QgsVectorLayerCache::featureAtIdWithAllAttributesAndGeometry( QgsFeatureId featureId, QgsFeature &feature, bool skipCache ) +{ + bool featureFound = false; + + QgsCachedFeature *cachedFeature = nullptr; + + if ( !skipCache ) + { + cachedFeature = mCache[ featureId ]; + } + + if ( cachedFeature && cachedFeature->allAttributesFetched() && cachedFeature->geometryFetched() ) + { + feature = QgsFeature( *cachedFeature->feature() ); + featureFound = true; + } + else if ( mLayer->getFeatures( QgsFeatureRequest() + .setFilterFid( featureId ) ) + .nextFeature( feature ) ) + { + cacheFeature( feature, true, true ); + featureFound = true; + } + + return featureFound; +} + bool QgsVectorLayerCache::removeCachedFeature( QgsFeatureId fid ) { bool removed = mCache.remove( fid ); @@ -561,3 +588,7 @@ bool QgsVectorLayerCache::QgsCachedFeature::allAttributesFetched() const return mAllAttributesFetched; } +bool QgsVectorLayerCache::QgsCachedFeature::geometryFetched() const +{ + return mGeometryFetched; +} diff --git a/src/core/vector/qgsvectorlayercache.h b/src/core/vector/qgsvectorlayercache.h index 7ae39df55e4a..f5cf92b3e4cf 100644 --- a/src/core/vector/qgsvectorlayercache.h +++ b/src/core/vector/qgsvectorlayercache.h @@ -64,10 +64,12 @@ class CORE_EXPORT QgsVectorLayerCache : public QObject * \param feat The feature to cache. A copy will be made. * \param vlCache The cache to inform when the feature has been removed from the cache. * \param allAttributesFetched TRUE if the feature was fetched with all attributes (and not a subset) + * \param geometryFetched TRUE if the feature was fetched with geometry, \since QGIS 3.42 */ - QgsCachedFeature( const QgsFeature &feat, QgsVectorLayerCache *vlCache, bool allAttributesFetched ) + QgsCachedFeature( const QgsFeature &feat, QgsVectorLayerCache *vlCache, bool allAttributesFetched, bool geometryFetched ) : mCache( vlCache ) , mAllAttributesFetched( allAttributesFetched ) + , mGeometryFetched( geometryFetched ) { mFeature = new QgsFeature( feat ); } @@ -84,10 +86,13 @@ class CORE_EXPORT QgsVectorLayerCache : public QObject bool allAttributesFetched() const; + bool geometryFetched() const; + private: QgsFeature *mFeature = nullptr; QgsVectorLayerCache *mCache = nullptr; bool mAllAttributesFetched = true; + bool mGeometryFetched = false; friend class QgsVectorLayerCache; Q_DISABLE_COPY( QgsCachedFeature ) @@ -270,6 +275,21 @@ class CORE_EXPORT QgsVectorLayerCache : public QObject */ bool featureAtIdWithAllAttributes( QgsFeatureId featureId, QgsFeature &feature, bool skipCache = false ); + /** + * Gets the feature at the given feature id with all attributes and geometry, if the cached feature + * already contains all attributes and geometry, calling this function has the same effect as calling + * featureAtId(). + * + * Considers the changed, added, deleted and permanent features + * \param featureId The id of the feature to query + * \param feature The result of the operation will be written to this feature + * \param skipCache Will query the layer regardless if the feature is in the cache already + * \returns TRUE in case of success + * \see featureAtId() + * \since QGIS 3.42 + */ + bool featureAtIdWithAllAttributesAndGeometry( QgsFeatureId featureId, QgsFeature &feature, bool skipCache = false ); + /** * Removes the feature identified by fid from the cache if present. * \param fid The id of the feature to delete @@ -417,9 +437,9 @@ class CORE_EXPORT QgsVectorLayerCache : public QObject void connectJoinedLayers() const; - inline void cacheFeature( QgsFeature &feat, bool allAttributesFetched ) + inline void cacheFeature( QgsFeature &feat, bool allAttributesFetched, bool geometryFetched = false ) { - QgsCachedFeature *cachedFeature = new QgsCachedFeature( feat, this, allAttributesFetched ); + QgsCachedFeature *cachedFeature = new QgsCachedFeature( feat, this, allAttributesFetched, geometryFetched ); mCache.insert( feat.id(), cachedFeature ); if ( mCacheUnorderedKeys.find( feat.id() ) == mCacheUnorderedKeys.end() ) { diff --git a/src/gui/attributetable/qgsfeaturelistmodel.cpp b/src/gui/attributetable/qgsfeaturelistmodel.cpp index ed55c6cb40c0..24da651d4093 100644 --- a/src/gui/attributetable/qgsfeaturelistmodel.cpp +++ b/src/gui/attributetable/qgsfeaturelistmodel.cpp @@ -115,6 +115,14 @@ QVariant QgsFeatureListModel::data( const QModelIndex &index, int role ) const return QVariant::fromValue( feat ); } + else if ( role == FeatureWithGeometryRole ) + { + QgsFeature feat; + + mFilterModel->layerCache()->featureAtIdWithAllAttributesAndGeometry( idxToFid( index ), feat ); + + return QVariant::fromValue( feat ); + } else if ( role == Qt::TextAlignmentRole ) { return static_cast( Qt::AlignLeft ); diff --git a/src/gui/attributetable/qgsfeaturelistmodel.h b/src/gui/attributetable/qgsfeaturelistmodel.h index a2e81a2dbbac..0377e7488cd5 100644 --- a/src/gui/attributetable/qgsfeaturelistmodel.h +++ b/src/gui/attributetable/qgsfeaturelistmodel.h @@ -56,7 +56,8 @@ class GUI_EXPORT QgsFeatureListModel : public QSortFilterProxyModel, public QgsF enum Role { FeatureInfoRole = 0x1000, // Make sure no collisions with roles on QgsAttributeTableModel - FeatureRole + FeatureRole, //!< Feature with all attributes and no geometry + FeatureWithGeometryRole, //!< Feature with all attributes and geometry, \since QGIS 3.42 }; public: diff --git a/src/gui/attributetable/qgsfeaturelistview.cpp b/src/gui/attributetable/qgsfeaturelistview.cpp index e3d3704950cf..30ed255cfc7c 100644 --- a/src/gui/attributetable/qgsfeaturelistview.cpp +++ b/src/gui/attributetable/qgsfeaturelistview.cpp @@ -389,7 +389,7 @@ void QgsFeatureListView::contextMenuEvent( QContextMenuEvent *event ) if ( index.isValid() ) { - const QgsFeature feature = mModel->data( index, QgsFeatureListModel::FeatureRole ).value(); + const QgsFeature feature = mModel->data( index, QgsFeatureListModel::FeatureWithGeometryRole ).value(); QgsActionMenu *menu = new QgsActionMenu( mModel->layerCache()->layer(), feature, QStringLiteral( "Feature" ), this );