diff --git a/python/PyQt6/core/auto_generated/vector/qgsvectorlayereditutils.sip.in b/python/PyQt6/core/auto_generated/vector/qgsvectorlayereditutils.sip.in index 7a158d17fd58..579235d7a486 100644 --- a/python/PyQt6/core/auto_generated/vector/qgsvectorlayereditutils.sip.in +++ b/python/PyQt6/core/auto_generated/vector/qgsvectorlayereditutils.sip.in @@ -332,6 +332,7 @@ Merge features into a single one. .. versionadded:: 3.30 %End + }; /************************************************************************ diff --git a/python/core/auto_generated/vector/qgsvectorlayereditutils.sip.in b/python/core/auto_generated/vector/qgsvectorlayereditutils.sip.in index 7a158d17fd58..579235d7a486 100644 --- a/python/core/auto_generated/vector/qgsvectorlayereditutils.sip.in +++ b/python/core/auto_generated/vector/qgsvectorlayereditutils.sip.in @@ -332,6 +332,7 @@ Merge features into a single one. .. versionadded:: 3.30 %End + }; /************************************************************************ diff --git a/src/app/qgsmaptooladdfeature.cpp b/src/app/qgsmaptooladdfeature.cpp index befcec9d2e99..3f9a89c776f6 100644 --- a/src/app/qgsmaptooladdfeature.cpp +++ b/src/app/qgsmaptooladdfeature.cpp @@ -26,6 +26,7 @@ #include "qgisapp.h" #include "qgsexpressioncontextutils.h" #include "qgsrubberband.h" +#include "qgsvectorlayereditutils.h" #include @@ -119,11 +120,16 @@ void QgsMapToolAddFeature::featureDigitized( const QgsFeature &feature ) } if ( topologicalEditing ) { + QgsFeatureRequest request = QgsFeatureRequest().setNoAttributes().setFlags( Qgis::FeatureRequestFlag::NoGeometry ).setLimit( 1 ); + const QgsRectangle bbox = feature.geometry().boundingBox(); const QList layers = canvas()->layers( true ); for ( QgsMapLayer *layer : layers ) { QgsVectorLayer *vectorLayer = qobject_cast( layer ); + QgsRectangle searchRect; + QgsFeature f; + QgsCoordinateTransform transform; if ( !vectorLayer || !vectorLayer->isEditable() ) continue; @@ -131,6 +137,23 @@ void QgsMapToolAddFeature::featureDigitized( const QgsFeature &feature ) if ( !( vectorLayer->geometryType() == Qgis::GeometryType::Polygon || vectorLayer->geometryType() == Qgis::GeometryType::Line ) ) continue; + if ( vectorLayer->crs() == vlayer->crs() ) + { + searchRect = QgsRectangle( bbox ); + } + else + { + transform = QgsCoordinateTransform( vlayer->crs(), vectorLayer->crs(), vectorLayer->transformContext() ); + searchRect = transform.transformBoundingBox( bbox ); + } + + searchRect.grow( QgsVectorLayerEditUtils::getTopologicalSearchRadius( vectorLayer ) ); + request.setFilterRect( searchRect ); + + // We check that there is actually at least one feature intersecting our geometry in the layer to avoid creating an empty edit command and calling costly addTopologicalPoint + if ( !vectorLayer->getFeatures( request ).nextFeature( f ) ) + continue; + vectorLayer->beginEditCommand( tr( "Topological points added by 'Add Feature'" ) ); int res = 2; @@ -140,7 +163,7 @@ void QgsMapToolAddFeature::featureDigitized( const QgsFeature &feature ) try { // transform digitized geometry from vlayer crs to vectorLayer crs and add topological points - transformedGeom.transform( QgsCoordinateTransform( vlayer->crs(), vectorLayer->crs(), vectorLayer->transformContext() ) ); + transformedGeom.transform( transform ); res = vectorLayer->addTopologicalPoints( transformedGeom ); } catch ( QgsCsException &cse ) diff --git a/src/app/vertextool/qgsvertextool.cpp b/src/app/vertextool/qgsvertextool.cpp index 03491256913f..c1ad8120bd86 100644 --- a/src/app/vertextool/qgsvertextool.cpp +++ b/src/app/vertextool/qgsvertextool.cpp @@ -43,6 +43,7 @@ #include "qgsexpressioncontextutils.h" #include "qgsmessagebar.h" #include "qgssettingsentryimpl.h" +#include "qgsvectorlayereditutils.h" #include @@ -2204,6 +2205,10 @@ void QgsVertexTool::moveVertex( const QgsPointXY &mapPoint, const QgsPointLocato { // topo editing: add vertex to existing segments when moving/adding a vertex to such segment. + QgsFeatureRequest request = QgsFeatureRequest().setNoAttributes().setFlags( Qgis::FeatureRequestFlag::NoGeometry ).setLimit( 1 ); + const QgsRectangle bbox = layerPoint.boundingBox(); + + const QList targetLayers = canvas()->layers( true ); for ( auto itLayerEdits = edits.begin(); itLayerEdits != edits.end(); ++itLayerEdits ) @@ -2211,6 +2216,8 @@ void QgsVertexTool::moveVertex( const QgsPointXY &mapPoint, const QgsPointLocato for ( QgsMapLayer *targetLayer : targetLayers ) { QgsVectorLayer *vectorLayer = qobject_cast( targetLayer ); + QgsRectangle searchRect; + QgsFeature f; if ( !vectorLayer || !vectorLayer->isEditable() ) continue; @@ -2222,6 +2229,14 @@ void QgsVertexTool::moveVertex( const QgsPointXY &mapPoint, const QgsPointLocato if ( vectorLayer->crs() != itLayerEdits.key()->crs() ) continue; + searchRect = QgsRectangle( bbox ); + searchRect.grow( QgsVectorLayerEditUtils::getTopologicalSearchRadius( vectorLayer ) ); + request.setFilterRect( searchRect ); + + // We check that there is actually at least one feature intersecting our geometry in the layer to avoid creating an empty edit command and calling costly addTopologicalPoint + if ( !vectorLayer->getFeatures( request ).nextFeature( f ) ) + continue; + vectorLayer->beginEditCommand( tr( "Topological points added by 'Vertex Tool'" ) ); bool topoPointsAdded = false; diff --git a/src/core/vector/qgsvectorlayereditutils.cpp b/src/core/vector/qgsvectorlayereditutils.cpp index 17df3e729fb3..0ab5a0260061 100644 --- a/src/core/vector/qgsvectorlayereditutils.cpp +++ b/src/core/vector/qgsvectorlayereditutils.cpp @@ -211,6 +211,28 @@ Qgis::GeometryOperationResult staticAddRing( QgsVectorLayer *layer, std::unique_ return success ? Qgis::GeometryOperationResult::Success : addRingReturnCode; } +///@cond PRIVATE +double QgsVectorLayerEditUtils::getTopologicalSearchRadius( const QgsVectorLayer *layer ) +{ + double threshold = layer->geometryOptions()->geometryPrecision(); + + if ( qgsDoubleNear( threshold, 0.0 ) ) + { + threshold = 1e-8; + + if ( layer->crs().mapUnits() == Qgis::DistanceUnit::Meters ) + { + threshold = 0.001; + } + else if ( layer->crs().mapUnits() == Qgis::DistanceUnit::Feet ) + { + threshold = 0.0001; + } + } + return threshold; +} +///@endcond + Qgis::GeometryOperationResult QgsVectorLayerEditUtils::addRing( const QVector &ring, const QgsFeatureIds &targetFeatureIds, QgsFeatureId *modifiedFeatureId ) { QgsPointSequence l; @@ -801,21 +823,7 @@ int QgsVectorLayerEditUtils::addTopologicalPoints( const QgsPoint &p ) double segmentSearchEpsilon = mLayer->crs().isGeographic() ? 1e-12 : 1e-8; //work with a tolerance because coordinate projection may introduce some rounding - double threshold = mLayer->geometryOptions()->geometryPrecision(); - - if ( qgsDoubleNear( threshold, 0.0 ) ) - { - threshold = 1e-8; - - if ( mLayer->crs().mapUnits() == Qgis::DistanceUnit::Meters ) - { - threshold = 0.001; - } - else if ( mLayer->crs().mapUnits() == Qgis::DistanceUnit::Feet ) - { - threshold = 0.0001; - } - } + double threshold = getTopologicalSearchRadius( mLayer ); QgsRectangle searchRect( p, p, false ); searchRect.grow( threshold ); diff --git a/src/core/vector/qgsvectorlayereditutils.h b/src/core/vector/qgsvectorlayereditutils.h index c2443c4d241e..7571042fae32 100644 --- a/src/core/vector/qgsvectorlayereditutils.h +++ b/src/core/vector/qgsvectorlayereditutils.h @@ -282,6 +282,10 @@ class CORE_EXPORT QgsVectorLayerEditUtils */ bool mergeFeatures( const QgsFeatureId &targetFeatureId, const QgsFeatureIds &mergeFeatureIds, const QgsAttributes &mergeAttributes, const QgsGeometry &unionGeometry, QString &errorMessage SIP_OUT ); + ///@cond PRIVATE + static double getTopologicalSearchRadius( const QgsVectorLayer *layer ) SIP_SKIP; + ///@endcond + private: /**