diff --git a/src/plugins/geometry_checker/CMakeLists.txt b/src/plugins/geometry_checker/CMakeLists.txt index 8db68efbb4f..982421dedee 100644 --- a/src/plugins/geometry_checker/CMakeLists.txt +++ b/src/plugins/geometry_checker/CMakeLists.txt @@ -20,6 +20,7 @@ SET (geometrychecker_SRCS checks/qgsgeometrysegmentlengthcheck.cpp checks/qgsgeometryselfcontactcheck.cpp checks/qgsgeometryselfintersectioncheck.cpp + checks/qgsgeometrysliverpolygoncheck.cpp checks/qgsgeometrytypecheck.cpp ui/qgsgeometrycheckerdialog.cpp ui/qgsgeometrycheckersetuptab.cpp diff --git a/src/plugins/geometry_checker/checks/qgsgeometryanglecheck.cpp b/src/plugins/geometry_checker/checks/qgsgeometryanglecheck.cpp index c7c08c54048..b21cb00c267 100644 --- a/src/plugins/geometry_checker/checks/qgsgeometryanglecheck.cpp +++ b/src/plugins/geometry_checker/checks/qgsgeometryanglecheck.cpp @@ -17,50 +17,57 @@ #include "qgsgeometryutils.h" #include "../utils/qgsfeaturepool.h" -void QgsGeometryAngleCheck::collectErrors( QList &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QgsFeatureIds &ids ) const +void QgsGeometryAngleCheck::collectErrors( QList &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap &ids ) const { - const QgsFeatureIds &featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids; - Q_FOREACH ( QgsFeatureId featureid, featureIds ) + QMap featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; + for ( const QString &layerId : featureIds.keys() ) { - if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 ); - QgsFeature feature; - if ( !mFeaturePool->get( featureid, feature ) ) + if ( !getCompatibility( getFeaturePool( layerId )->getLayer()->geometryType() ) ) { continue; } - QgsGeometry g = feature.geometry(); - const QgsAbstractGeometry *geom = g.geometry(); - for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart ) + for ( QgsFeatureId featureid : featureIds[layerId] ) { - for ( int iRing = 0, nRings = geom->ringCount( iPart ); iRing < nRings; ++iRing ) + if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 ); + QgsFeature feature; + if ( !getFeaturePool( layerId )->get( featureid, feature ) ) { - int nVerts = QgsGeometryCheckerUtils::polyLineSize( geom, iPart, iRing ); - // Less than three points, no angles to check - if ( nVerts < 3 ) + continue; + } + QgsGeometry g = feature.geometry(); + const QgsAbstractGeometry *geom = g.geometry(); + for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart ) + { + for ( int iRing = 0, nRings = geom->ringCount( iPart ); iRing < nRings; ++iRing ) { - continue; - } - for ( int iVert = 0; iVert < nVerts; ++iVert ) - { - const QgsPoint &p1 = geom->vertexAt( QgsVertexId( iPart, iRing, ( iVert - 1 + nVerts ) % nVerts ) ); - const QgsPoint &p2 = geom->vertexAt( QgsVertexId( iPart, iRing, iVert ) ); - const QgsPoint &p3 = geom->vertexAt( QgsVertexId( iPart, iRing, ( iVert + 1 ) % nVerts ) ); - QgsVector v21, v23; - try + int nVerts = QgsGeometryCheckerUtils::polyLineSize( geom, iPart, iRing ); + // Less than three points, no angles to check + if ( nVerts < 3 ) { - v21 = QgsVector( p1.x() - p2.x(), p1.y() - p2.y() ).normalized(); - v23 = QgsVector( p3.x() - p2.x(), p3.y() - p2.y() ).normalized(); - } - catch ( const QgsException & ) - { - // Zero length vectors continue; } - - double angle = std::acos( v21 * v23 ) / M_PI * 180.0; - if ( angle < mMinAngle ) + for ( int iVert = 0; iVert < nVerts; ++iVert ) { - errors.append( new QgsGeometryCheckError( this, featureid, p2, QgsVertexId( iPart, iRing, iVert ), angle ) ); + const QgsPoint &p1 = geom->vertexAt( QgsVertexId( iPart, iRing, ( iVert - 1 + nVerts ) % nVerts ) ); + const QgsPoint &p2 = geom->vertexAt( QgsVertexId( iPart, iRing, iVert ) ); + const QgsPoint &p3 = geom->vertexAt( QgsVertexId( iPart, iRing, ( iVert + 1 ) % nVerts ) ); + QgsVector v21, v23; + try + { + v21 = QgsVector( p1.x() - p2.x(), p1.y() - p2.y() ).normalized(); + v23 = QgsVector( p3.x() - p2.x(), p3.y() - p2.y() ).normalized(); + } + catch ( const QgsException & ) + { + // Zero length vectors + continue; + } + + double angle = std::acos( v21 * v23 ) / M_PI * 180.0; + if ( angle < mMinAngle ) + { + errors.append( new QgsGeometryCheckError( this, layerId, featureid, p2, QgsVertexId( iPart, iRing, iVert ), angle ) ); + } } } } @@ -68,10 +75,10 @@ void QgsGeometryAngleCheck::collectErrors( QList &error } } -void QgsGeometryAngleCheck::fixError( QgsGeometryCheckError *error, int method, int /*mergeAttributeIndex*/, Changes &changes ) const +void QgsGeometryAngleCheck::fixError( QgsGeometryCheckError *error, int method, const QMap & /*mergeAttributeIndices*/, Changes &changes ) const { QgsFeature feature; - if ( !mFeaturePool->get( error->featureId(), feature ) ) + if ( !getFeaturePool( error->layerId() )->get( error->featureId(), feature ) ) { error->setObsolete(); return; @@ -132,15 +139,15 @@ void QgsGeometryAngleCheck::fixError( QgsGeometryCheckError *error, int method, } else { - changes[error->featureId()].append( Change( ChangeNode, ChangeRemoved, vidx ) ); + changes[error->layerId()][error->featureId()].append( Change( ChangeNode, ChangeRemoved, vidx ) ); if ( QgsGeometryUtils::sqrDistance2D( p1, p3 ) < QgsGeometryCheckPrecision::tolerance() * QgsGeometryCheckPrecision::tolerance() && QgsGeometryCheckerUtils::canDeleteVertex( geometry, vidx.part, vidx.ring ) && geometry->deleteVertex( error->vidx() ) ) // error->vidx points to p3 after removing p2 { - changes[error->featureId()].append( Change( ChangeNode, ChangeRemoved, QgsVertexId( vidx.part, vidx.ring, ( vidx.vertex + 1 ) % n ) ) ); + changes[error->layerId()][error->featureId()].append( Change( ChangeNode, ChangeRemoved, QgsVertexId( vidx.part, vidx.ring, ( vidx.vertex + 1 ) % n ) ) ); } feature.setGeometry( g ); - mFeaturePool->updateFeature( feature ); + getFeaturePool( error->layerId() )->updateFeature( feature ); error->setFixed( method ); } } diff --git a/src/plugins/geometry_checker/checks/qgsgeometryanglecheck.h b/src/plugins/geometry_checker/checks/qgsgeometryanglecheck.h index 79b9b47033e..b7e6cd5e5a4 100644 --- a/src/plugins/geometry_checker/checks/qgsgeometryanglecheck.h +++ b/src/plugins/geometry_checker/checks/qgsgeometryanglecheck.h @@ -23,12 +23,12 @@ class QgsGeometryAngleCheck : public QgsGeometryCheck Q_OBJECT public: - QgsGeometryAngleCheck( QgsFeaturePool *featurePool, double minAngle ) - : QgsGeometryCheck( FeatureNodeCheck, featurePool ) - , mMinAngle( minAngle ) + QgsGeometryAngleCheck( const QMap &featurePools, double minAngle ) + : QgsGeometryCheck( FeatureNodeCheck, {QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}, featurePools ) + , mMinAngle( minAngle ) {} - void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QgsFeatureIds &ids = QgsFeatureIds() ) const override; - void fixError( QgsGeometryCheckError *error, int method, int mergeAttributeIndex, Changes &changes ) const override; + void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap &ids = QMap() ) const override; + void fixError( QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QStringList getResolutionMethods() const override; QString errorDescription() const override { return tr( "Minimal angle" ); } QString errorName() const override { return QStringLiteral( "QgsGeometryAngleCheck" ); } diff --git a/src/plugins/geometry_checker/checks/qgsgeometryareacheck.cpp b/src/plugins/geometry_checker/checks/qgsgeometryareacheck.cpp index 128f40d5ca2..5b46535474f 100644 --- a/src/plugins/geometry_checker/checks/qgsgeometryareacheck.cpp +++ b/src/plugins/geometry_checker/checks/qgsgeometryareacheck.cpp @@ -18,47 +18,54 @@ #include "qgsgeometryareacheck.h" #include "../utils/qgsfeaturepool.h" -void QgsGeometryAreaCheck::collectErrors( QList &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QgsFeatureIds &ids ) const +void QgsGeometryAreaCheck::collectErrors( QList &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap &ids ) const { - const QgsFeatureIds &featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids; - Q_FOREACH ( QgsFeatureId featureid, featureIds ) + QMap featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; + for ( const QString &layerId : featureIds.keys() ) { - if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 ); - QgsFeature feature; - if ( !mFeaturePool->get( featureid, feature ) ) + if ( !getCompatibility( getFeaturePool( layerId )->getLayer()->geometryType() ) ) { continue; } - - QgsGeometry g = feature.geometry(); - QgsAbstractGeometry *geom = g.geometry(); - if ( dynamic_cast( geom ) ) + for ( QgsFeatureId featureid : featureIds[layerId] ) { - QgsGeometryCollection *multiGeom = static_cast( geom ); - for ( int i = 0, n = multiGeom->numGeometries(); i < n; ++i ) + if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 ); + QgsFeature feature; + if ( !getFeaturePool( layerId )->get( featureid, feature ) ) { - double value; - if ( checkThreshold( multiGeom->geometryN( i ), value ) ) + continue; + } + + QgsGeometry g = feature.geometry(); + QgsAbstractGeometry *geom = g.geometry(); + if ( dynamic_cast( geom ) ) + { + QgsGeometryCollection *multiGeom = static_cast( geom ); + for ( int i = 0, n = multiGeom->numGeometries(); i < n; ++i ) { - errors.append( new QgsGeometryCheckError( this, featureid, multiGeom->geometryN( i )->centroid(), QgsVertexId( i ), value, QgsGeometryCheckError::ValueArea ) ); + double value; + if ( checkThreshold( layerId, multiGeom->geometryN( i ), value ) ) + { + errors.append( new QgsGeometryCheckError( this, layerId, featureid, multiGeom->geometryN( i )->centroid(), QgsVertexId( i ), value, QgsGeometryCheckError::ValueArea ) ); + } } } - } - else - { - double value; - if ( checkThreshold( geom, value ) ) + else { - errors.append( new QgsGeometryCheckError( this, featureid, geom->centroid(), QgsVertexId( 0 ), value, QgsGeometryCheckError::ValueArea ) ); + double value; + if ( checkThreshold( layerId, geom, value ) ) + { + errors.append( new QgsGeometryCheckError( this, layerId, featureid, geom->centroid(), QgsVertexId( 0 ), value, QgsGeometryCheckError::ValueArea ) ); + } } } } } -void QgsGeometryAreaCheck::fixError( QgsGeometryCheckError *error, int method, int mergeAttributeIndex, Changes &changes ) const +void QgsGeometryAreaCheck::fixError( QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const { QgsFeature feature; - if ( !mFeaturePool->get( error->featureId(), feature ) ) + if ( !getFeaturePool( error->layerId() )->get( error->featureId(), feature ) ) { error->setObsolete(); return; @@ -78,7 +85,7 @@ void QgsGeometryAreaCheck::fixError( QgsGeometryCheckError *error, int method, i if ( dynamic_cast( geom ) ) { double value; - if ( !checkThreshold( static_cast( geom )->geometryN( vidx.part ), value ) ) + if ( !checkThreshold( error->layerId(), static_cast( geom )->geometryN( vidx.part ), value ) ) { error->setObsolete(); return; @@ -87,7 +94,7 @@ void QgsGeometryAreaCheck::fixError( QgsGeometryCheckError *error, int method, i else { double value; - if ( !checkThreshold( geom, value ) ) + if ( !checkThreshold( error->layerId(), geom, value ) ) { error->setObsolete(); return; @@ -101,13 +108,13 @@ void QgsGeometryAreaCheck::fixError( QgsGeometryCheckError *error, int method, i } else if ( method == Delete ) { - deleteFeatureGeometryPart( feature, vidx.part, changes ); + deleteFeatureGeometryPart( error->layerId(), feature, vidx.part, changes ); error->setFixed( method ); } else if ( method == MergeLongestEdge || method == MergeLargestArea || method == MergeIdenticalAttribute ) { QString errMsg; - if ( mergeWithNeighbor( feature, vidx.part, method, mergeAttributeIndex, changes, errMsg ) ) + if ( mergeWithNeighbor( error->layerId(), feature, vidx.part, method, mergeAttributeIndices[error->layerId()], changes, errMsg ) ) { error->setFixed( method ); } @@ -122,20 +129,28 @@ void QgsGeometryAreaCheck::fixError( QgsGeometryCheckError *error, int method, i } } -bool QgsGeometryAreaCheck::mergeWithNeighbor( QgsFeature &feature, int partIdx, int method, int mergeAttributeIndex, Changes &changes, QString &errMsg ) const +bool QgsGeometryAreaCheck::checkThreshold( const QString &layerId, const QgsAbstractGeometry *geom, double &value ) const +{ + value = geom->area(); + double mapToLayerUnits = getFeaturePool( layerId )->getMapToLayerUnits(); + double threshold = mThresholdMapUnits * mapToLayerUnits * mapToLayerUnits; + return value < threshold; +} + +bool QgsGeometryAreaCheck::mergeWithNeighbor( const QString &layerId, QgsFeature &feature, int partIdx, int method, int mergeAttributeIndex, Changes &changes, QString &errMsg ) const { double maxVal = 0.; QgsFeature mergeFeature; int mergePartIdx = -1; bool matchFound = false; - QgsGeometry g = feature.geometry();; + QgsGeometry g = feature.geometry(); QgsAbstractGeometry *geom = g.geometry(); // Search for touching neighboring geometries - Q_FOREACH ( QgsFeatureId testId, mFeaturePool->getIntersects( g.boundingBox() ) ) + for ( QgsFeatureId testId : getFeaturePool( layerId )->getIntersects( g.boundingBox() ) ) { QgsFeature testFeature; - if ( !mFeaturePool->get( testId, testFeature ) ) + if ( !getFeaturePool( layerId )->get( testId, testFeature ) ) { continue; } @@ -210,9 +225,9 @@ bool QgsGeometryAreaCheck::mergeWithNeighbor( QgsFeature &feature, int partIdx, { --mergePartIdx; } - replaceFeatureGeometryPart( mergeFeature, mergePartIdx, combinedGeom, changes ); + replaceFeatureGeometryPart( layerId, mergeFeature, mergePartIdx, combinedGeom, changes ); // Remove polygon from source geometry - deleteFeatureGeometryPart( feature, partIdx, changes ); + deleteFeatureGeometryPart( layerId, feature, partIdx, changes ); return true; } diff --git a/src/plugins/geometry_checker/checks/qgsgeometryareacheck.h b/src/plugins/geometry_checker/checks/qgsgeometryareacheck.h index ce42ff6feba..b7d50aa0f40 100644 --- a/src/plugins/geometry_checker/checks/qgsgeometryareacheck.h +++ b/src/plugins/geometry_checker/checks/qgsgeometryareacheck.h @@ -25,23 +25,23 @@ class QgsGeometryAreaCheck : public QgsGeometryCheck Q_OBJECT public: - QgsGeometryAreaCheck( QgsFeaturePool *featurePool, double threshold ) - : QgsGeometryCheck( FeatureCheck, featurePool ) - , mThreshold( threshold ) + QgsGeometryAreaCheck( const QMap &featurePools, double thresholdMapUnits ) + : QgsGeometryCheck( FeatureCheck, {QgsWkbTypes::PolygonGeometry}, featurePools ) + , mThresholdMapUnits( thresholdMapUnits ) {} - void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QgsFeatureIds &ids = QgsFeatureIds() ) const override; - void fixError( QgsGeometryCheckError *error, int method, int mergeAttributeIndex, Changes &changes ) const override; + void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap &ids = QMap() ) const override; + void fixError( QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QStringList getResolutionMethods() const override; QString errorDescription() const override { return tr( "Minimal area" ); } QString errorName() const override { return QStringLiteral( "QgsGeometryAreaCheck" ); } private: enum ResolutionMethod { MergeLongestEdge, MergeLargestArea, MergeIdenticalAttribute, Delete, NoChange }; - bool mergeWithNeighbor( QgsFeature &feature, int partIdx, int method, int mergeAttributeIndex, Changes &changes, QString &errMsg ) const; - virtual bool checkThreshold( const QgsAbstractGeometry *geom, double &value ) const { value = geom->area(); return value < mThreshold; } + virtual bool checkThreshold( const QString &layerId, const QgsAbstractGeometry *geom, double &value ) const; + bool mergeWithNeighbor( const QString &layerId, QgsFeature &feature, int partIdx, int method, int mergeAttributeIndex, Changes &changes, QString &errMsg ) const; protected: - double mThreshold; + double mThresholdMapUnits; }; #endif // QGS_GEOMETRY_AREA_CHECK_H diff --git a/src/plugins/geometry_checker/checks/qgsgeometrycheck.cpp b/src/plugins/geometry_checker/checks/qgsgeometrycheck.cpp index 75558644805..21bb9bc28cc 100644 --- a/src/plugins/geometry_checker/checks/qgsgeometrycheck.cpp +++ b/src/plugins/geometry_checker/checks/qgsgeometrycheck.cpp @@ -56,12 +56,13 @@ double QgsGeometryCheckPrecision::reducedTolerance() return std::pow( 10, -get()->mReducedPrecision ); } -QgsGeometryCheckError::QgsGeometryCheckError( const QgsGeometryCheck *check, +QgsGeometryCheckError::QgsGeometryCheckError( const QgsGeometryCheck *check, const QString &layerId, QgsFeatureId featureId, const QgsPoint &errorLocation, QgsVertexId vidx, const QVariant &value, ValueType valueType ) : mCheck( check ) + , mLayerId( layerId ) , mFeatureId( featureId ) , mErrorLocation( errorLocation ) , mVidx( vidx ) @@ -73,7 +74,7 @@ QgsGeometryCheckError::QgsGeometryCheckError( const QgsGeometryCheck *check, QgsAbstractGeometry *QgsGeometryCheckError::geometry() { QgsFeature f; - if ( mCheck->getFeaturePool()->get( featureId(), f ) && f.hasGeometry() ) + if ( mCheck->getFeaturePool( layerId() )->get( featureId(), f ) && f.hasGeometry() ) { QgsGeometry featureGeom = f.geometry(); QgsAbstractGeometry *geom = featureGeom.geometry(); @@ -89,7 +90,7 @@ bool QgsGeometryCheckError::handleChanges( const QgsGeometryCheck::Changes &chan return false; } - Q_FOREACH ( const QgsGeometryCheck::Change &change, changes.value( featureId() ) ) + for ( const QgsGeometryCheck::Change &change : changes.value( layerId() ).value( featureId() ) ) { if ( change.what == QgsGeometryCheck::ChangeFeature ) { @@ -148,7 +149,17 @@ bool QgsGeometryCheckError::handleChanges( const QgsGeometryCheck::Changes &chan return true; } -void QgsGeometryCheck::replaceFeatureGeometryPart( QgsFeature &feature, int partIdx, QgsAbstractGeometry *newPartGeom, Changes &changes ) const +QMap QgsGeometryCheck::allLayerFeatureIds() const +{ + QMap featureIds; + for ( QgsFeaturePool *pool : mFeaturePools ) + { + featureIds.insert( pool->getLayer()->id(), pool->getFeatureIds() ); + } + return featureIds; +} + +void QgsGeometryCheck::replaceFeatureGeometryPart( const QString &layerId, QgsFeature &feature, int partIdx, QgsAbstractGeometry *newPartGeom, Changes &changes ) const { QgsGeometry featureGeom = feature.geometry(); QgsAbstractGeometry *geom = featureGeom.geometry(); @@ -157,19 +168,19 @@ void QgsGeometryCheck::replaceFeatureGeometryPart( QgsFeature &feature, int part QgsGeometryCollection *GeomCollection = static_cast( geom ); GeomCollection->removeGeometry( partIdx ); GeomCollection->addGeometry( newPartGeom ); - changes[feature.id()].append( Change( ChangeFeature, ChangeRemoved, QgsVertexId( partIdx ) ) ); - changes[feature.id()].append( Change( ChangeFeature, ChangeAdded, QgsVertexId( GeomCollection->partCount() - 1 ) ) ); + changes[layerId][feature.id()].append( Change( ChangeFeature, ChangeRemoved, QgsVertexId( partIdx ) ) ); + changes[layerId][feature.id()].append( Change( ChangeFeature, ChangeAdded, QgsVertexId( GeomCollection->partCount() - 1 ) ) ); feature.setGeometry( featureGeom ); } else { feature.setGeometry( QgsGeometry( newPartGeom ) ); - changes[feature.id()].append( Change( ChangeFeature, ChangeChanged ) ); + changes[layerId][feature.id()].append( Change( ChangeFeature, ChangeChanged ) ); } - mFeaturePool->updateFeature( feature ); + mFeaturePools[layerId]->updateFeature( feature ); } -void QgsGeometryCheck::deleteFeatureGeometryPart( QgsFeature &feature, int partIdx, Changes &changes ) const +void QgsGeometryCheck::deleteFeatureGeometryPart( const QString &layerId, QgsFeature &feature, int partIdx, Changes &changes ) const { QgsGeometry featureGeom = feature.geometry(); QgsAbstractGeometry *geom = featureGeom.geometry(); @@ -178,24 +189,24 @@ void QgsGeometryCheck::deleteFeatureGeometryPart( QgsFeature &feature, int partI static_cast( geom )->removeGeometry( partIdx ); if ( static_cast( geom )->numGeometries() == 0 ) { - mFeaturePool->deleteFeature( feature ); - changes[feature.id()].append( Change( ChangeFeature, ChangeRemoved ) ); + mFeaturePools[layerId]->deleteFeature( feature ); + changes[layerId][feature.id()].append( Change( ChangeFeature, ChangeRemoved ) ); } else { feature.setGeometry( featureGeom ); - mFeaturePool->updateFeature( feature ); - changes[feature.id()].append( Change( ChangePart, ChangeRemoved, QgsVertexId( partIdx ) ) ); + mFeaturePools[layerId]->updateFeature( feature ); + changes[layerId][feature.id()].append( Change( ChangePart, ChangeRemoved, QgsVertexId( partIdx ) ) ); } } else { - mFeaturePool->deleteFeature( feature ); - changes[feature.id()].append( Change( ChangeFeature, ChangeRemoved ) ); + mFeaturePools[layerId]->deleteFeature( feature ); + changes[layerId][feature.id()].append( Change( ChangeFeature, ChangeRemoved ) ); } } -void QgsGeometryCheck::deleteFeatureGeometryRing( QgsFeature &feature, int partIdx, int ringIdx, Changes &changes ) const +void QgsGeometryCheck::deleteFeatureGeometryRing( const QString &layerId, QgsFeature &feature, int partIdx, int ringIdx, Changes &changes ) const { QgsGeometry featureGeom = feature.geometry(); QgsAbstractGeometry *partGeom = QgsGeometryCheckerUtils::getGeomPart( featureGeom.geometry(), partIdx ); @@ -204,19 +215,19 @@ void QgsGeometryCheck::deleteFeatureGeometryRing( QgsFeature &feature, int partI // If we delete the exterior ring of a polygon, it makes no sense to keep the interiors if ( ringIdx == 0 ) { - deleteFeatureGeometryPart( feature, partIdx, changes ); + deleteFeatureGeometryPart( layerId, feature, partIdx, changes ); } else { static_cast( partGeom )->removeInteriorRing( ringIdx - 1 ); feature.setGeometry( featureGeom ); - mFeaturePool->updateFeature( feature ); - changes[feature.id()].append( Change( ChangeRing, ChangeRemoved, QgsVertexId( partIdx, ringIdx ) ) ); + mFeaturePools[layerId]->updateFeature( feature ); + changes[layerId][feature.id()].append( Change( ChangeRing, ChangeRemoved, QgsVertexId( partIdx, ringIdx ) ) ); } } // Other geometry types do not have rings, remove the entire part else { - deleteFeatureGeometryPart( feature, partIdx, changes ); + deleteFeatureGeometryPart( layerId, feature, partIdx, changes ); } } diff --git a/src/plugins/geometry_checker/checks/qgsgeometrycheck.h b/src/plugins/geometry_checker/checks/qgsgeometrycheck.h index 5053e1262a7..964acf2602e 100644 --- a/src/plugins/geometry_checker/checks/qgsgeometrycheck.h +++ b/src/plugins/geometry_checker/checks/qgsgeometrycheck.h @@ -69,27 +69,33 @@ class QgsGeometryCheck : public QObject QgsVertexId vidx; }; - typedef QMap > Changes; + typedef QMap>> Changes; - QgsGeometryCheck( CheckType checkType, QgsFeaturePool *featurePool ) + QgsGeometryCheck( CheckType checkType, const QList &compatibleGeometryTypes, const QMap &featurePools ) : mCheckType( checkType ) - , mFeaturePool( featurePool ) + , mCompatibleGeometryTypes( compatibleGeometryTypes ) + , mFeaturePools( featurePools ) {} - virtual void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QgsFeatureIds &ids = QgsFeatureIds() ) const = 0; - virtual void fixError( QgsGeometryCheckError *error, int method, int mergeAttributeIndex, Changes &changes ) const = 0; + virtual void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap &ids = QMap() ) const = 0; + virtual void fixError( QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const = 0; virtual QStringList getResolutionMethods() const = 0; virtual QString errorDescription() const = 0; virtual QString errorName() const = 0; CheckType getCheckType() const { return mCheckType; } - QgsFeaturePool *getFeaturePool() const { return mFeaturePool; } + bool getCompatibility( QgsWkbTypes::GeometryType type ) const { return mCompatibleGeometryTypes.contains( type ); } + const QMap &getFeaturePools() const { return mFeaturePools; } + QgsFeaturePool *getFeaturePool( const QString &layerId ) const { return mFeaturePools.value( layerId, nullptr ); } protected: - const CheckType mCheckType; - QgsFeaturePool *mFeaturePool = nullptr; + QMap allLayerFeatureIds() const; + void replaceFeatureGeometryPart( const QString &layerId, QgsFeature &feature, int partIdx, QgsAbstractGeometry *newPartGeom, Changes &changes ) const; + void deleteFeatureGeometryPart( const QString &layerId, QgsFeature &feature, int partIdx, Changes &changes ) const; + void deleteFeatureGeometryRing( const QString &layerId, QgsFeature &feature, int partIdx, int ringIdx, Changes &changes ) const; - void replaceFeatureGeometryPart( QgsFeature &feature, int partIdx, QgsAbstractGeometry *newPartGeom, Changes &changes ) const; - void deleteFeatureGeometryPart( QgsFeature &feature, int partIdx, Changes &changes ) const; - void deleteFeatureGeometryRing( QgsFeature &feature, int partIdx, int ringIdx, Changes &changes ) const; + private: + const CheckType mCheckType; + QList mCompatibleGeometryTypes; + QMap mFeaturePools; }; @@ -100,6 +106,7 @@ class QgsGeometryCheckError enum ValueType { ValueLength, ValueArea, ValueOther }; QgsGeometryCheckError( const QgsGeometryCheck *check, + const QString &layerId, QgsFeatureId featureId, const QgsPoint &errorLocation, QgsVertexId vidx = QgsVertexId(), @@ -110,6 +117,7 @@ class QgsGeometryCheckError const QgsGeometryCheckError &operator=( const QgsGeometryCheckError & ) = delete; const QgsGeometryCheck *check() const { return mCheck; } + const QString &layerId() const { return mLayerId; } QgsFeatureId featureId() const { return mFeatureId; } virtual QgsAbstractGeometry *geometry(); virtual QgsRectangle affectedAreaBBox() { return geometry() ? geometry()->boundingBox() : QgsRectangle(); } @@ -134,6 +142,7 @@ class QgsGeometryCheckError virtual bool isEqual( QgsGeometryCheckError *other ) const { return other->check() == check() && + other->layerId() == layerId() && other->featureId() == featureId() && other->vidx() == vidx(); } @@ -144,6 +153,7 @@ class QgsGeometryCheckError virtual void update( const QgsGeometryCheckError *other ) { Q_ASSERT( mCheck == other->mCheck ); + Q_ASSERT( mLayerId == other->mLayerId ); Q_ASSERT( mFeatureId == other->mFeatureId ); mErrorLocation = other->mErrorLocation; mVidx = other->mVidx; @@ -154,6 +164,7 @@ class QgsGeometryCheckError protected: const QgsGeometryCheck *mCheck = nullptr; + QString mLayerId; QgsFeatureId mFeatureId; QgsPoint mErrorLocation; QgsVertexId mVidx; diff --git a/src/plugins/geometry_checker/checks/qgsgeometrycontainedcheck.cpp b/src/plugins/geometry_checker/checks/qgsgeometrycontainedcheck.cpp index 551c3d2bf12..ef72359620f 100644 --- a/src/plugins/geometry_checker/checks/qgsgeometrycontainedcheck.cpp +++ b/src/plugins/geometry_checker/checks/qgsgeometrycontainedcheck.cpp @@ -17,56 +17,63 @@ #include "qgsgeometrycontainedcheck.h" #include "../utils/qgsfeaturepool.h" -void QgsGeometryContainedCheck::collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter, const QgsFeatureIds &ids ) const +void QgsGeometryContainedCheck::collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter, const QMap &ids ) const { - const QgsFeatureIds &featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids; - Q_FOREACH ( QgsFeatureId featureid, featureIds ) + QMap featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; + for ( const QString &layerId : featureIds.keys() ) { - if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 ); - QgsFeature feature; - if ( !mFeaturePool->get( featureid, feature ) ) + if ( !getCompatibility( getFeaturePool( layerId )->getLayer()->geometryType() ) ) { continue; } - - QgsGeometry featureGeom = feature.geometry(); - QgsGeometryEngine *geomEngine = QgsGeometryCheckerUtils::createGeomEngine( featureGeom.geometry(), QgsGeometryCheckPrecision::tolerance() ); - - QgsFeatureIds ids = mFeaturePool->getIntersects( featureGeom.geometry()->boundingBox() ); - Q_FOREACH ( QgsFeatureId otherid, ids ) + for ( QgsFeatureId featureid : featureIds[layerId] ) { - if ( otherid == featureid ) - { - continue; - } - QgsFeature otherFeature; - if ( !mFeaturePool->get( otherid, otherFeature ) ) + if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 ); + QgsFeature feature; + if ( !getFeaturePool( layerId )->get( featureid, feature ) ) { continue; } - QString errMsg; - if ( geomEngine->within( otherFeature.geometry().geometry(), &errMsg ) ) + QgsGeometry featureGeom = feature.geometry(); + QgsGeometryEngine *geomEngine = QgsGeometryCheckerUtils::createGeomEngine( featureGeom.geometry(), QgsGeometryCheckPrecision::tolerance() ); + + QgsFeatureIds ids = getFeaturePool( layerId )->getIntersects( featureGeom.geometry()->boundingBox() ); + for ( QgsFeatureId otherid : ids ) { - errors.append( new QgsGeometryContainedCheckError( this, featureid, feature.geometry().geometry()->centroid(), otherid ) ); - } - else if ( !errMsg.isEmpty() ) - { - messages.append( tr( "Feature %1 within feature %2: %3" ).arg( feature.id() ).arg( otherFeature.id() ).arg( errMsg ) ); + if ( otherid == featureid ) + { + continue; + } + QgsFeature otherFeature; + if ( !getFeaturePool( layerId )->get( otherid, otherFeature ) ) + { + continue; + } + + QString errMsg; + if ( geomEngine->within( *otherFeature.geometry().geometry(), &errMsg ) ) + { + errors.append( new QgsGeometryContainedCheckError( this, layerId, featureid, feature.geometry().geometry()->centroid(), otherid ) ); + } + else if ( !errMsg.isEmpty() ) + { + messages.append( tr( "Feature %1 within feature %2: %3" ).arg( feature.id() ).arg( otherFeature.id() ).arg( errMsg ) ); + } } + delete geomEngine; } - delete geomEngine; } } -void QgsGeometryContainedCheck::fixError( QgsGeometryCheckError *error, int method, int /*mergeAttributeIndex*/, Changes &changes ) const +void QgsGeometryContainedCheck::fixError( QgsGeometryCheckError *error, int method, const QMap & /*mergeAttributeIndices*/, Changes &changes ) const { QgsGeometryContainedCheckError *coverError = static_cast( error ); QgsFeature feature; QgsFeature otherFeature; - if ( !mFeaturePool->get( error->featureId(), feature ) || - !mFeaturePool->get( coverError->otherId(), otherFeature ) ) + if ( !getFeaturePool( error->layerId() )->get( error->featureId(), feature ) || + !getFeaturePool( error->layerId() )->get( coverError->otherId(), otherFeature ) ) { error->setObsolete(); return; @@ -91,8 +98,8 @@ void QgsGeometryContainedCheck::fixError( QgsGeometryCheckError *error, int meth } else if ( method == Delete ) { - changes[feature.id()].append( Change( ChangeFeature, ChangeRemoved ) ); - mFeaturePool->deleteFeature( feature ); + changes[error->layerId()][feature.id()].append( Change( ChangeFeature, ChangeRemoved ) ); + getFeaturePool( error->layerId() )->deleteFeature( feature ); error->setFixed( method ); } else diff --git a/src/plugins/geometry_checker/checks/qgsgeometrycontainedcheck.h b/src/plugins/geometry_checker/checks/qgsgeometrycontainedcheck.h index 209810104df..a55922fc391 100644 --- a/src/plugins/geometry_checker/checks/qgsgeometrycontainedcheck.h +++ b/src/plugins/geometry_checker/checks/qgsgeometrycontainedcheck.h @@ -22,11 +22,12 @@ class QgsGeometryContainedCheckError : public QgsGeometryCheckError { public: QgsGeometryContainedCheckError( const QgsGeometryCheck *check, + const QString &layerId, QgsFeatureId featureId, const QgsPoint &errorLocation, QgsFeatureId otherId ) - : QgsGeometryCheckError( check, featureId, errorLocation, QgsVertexId(), otherId, ValueOther ) + : QgsGeometryCheckError( check, layerId, featureId, errorLocation, QgsVertexId(), otherId, ValueOther ) , mOtherId( otherId ) { } QgsFeatureId otherId() const { return mOtherId; } @@ -49,9 +50,10 @@ class QgsGeometryContainedCheck : public QgsGeometryCheck Q_OBJECT public: - explicit QgsGeometryContainedCheck( QgsFeaturePool *featurePool ) : QgsGeometryCheck( FeatureCheck, featurePool ) {} - void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QgsFeatureIds &ids = QgsFeatureIds() ) const override; - void fixError( QgsGeometryCheckError *error, int method, int mergeAttributeIndex, Changes &changes ) const override; + explicit QgsGeometryContainedCheck( const QMap &featurePools ) + : QgsGeometryCheck( FeatureCheck, {QgsWkbTypes::PolygonGeometry}, featurePools ) {} + void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap &ids = QMap() ) const override; + void fixError( QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QStringList getResolutionMethods() const override; QString errorDescription() const override { return tr( "Within" ); } QString errorName() const override { return QStringLiteral( "QgsGeometryContainedCheck" ); } diff --git a/src/plugins/geometry_checker/checks/qgsgeometrydegeneratepolygoncheck.cpp b/src/plugins/geometry_checker/checks/qgsgeometrydegeneratepolygoncheck.cpp index bd942a8031e..9bb39841d69 100644 --- a/src/plugins/geometry_checker/checks/qgsgeometrydegeneratepolygoncheck.cpp +++ b/src/plugins/geometry_checker/checks/qgsgeometrydegeneratepolygoncheck.cpp @@ -16,36 +16,43 @@ #include "qgsgeometrydegeneratepolygoncheck.h" #include "../utils/qgsfeaturepool.h" -void QgsGeometryDegeneratePolygonCheck::collectErrors( QList &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QgsFeatureIds &ids ) const +void QgsGeometryDegeneratePolygonCheck::collectErrors( QList &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap &ids ) const { - const QgsFeatureIds &featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids; - Q_FOREACH ( QgsFeatureId featureid, featureIds ) + QMap featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; + for ( const QString &layerId : featureIds.keys() ) { - if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 ); - QgsFeature feature; - if ( !mFeaturePool->get( featureid, feature ) ) + if ( !getCompatibility( getFeaturePool( layerId )->getLayer()->geometryType() ) ) { continue; } - QgsGeometry featureGeom = feature.geometry(); - QgsAbstractGeometry *geom = featureGeom.geometry(); - for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart ) + for ( QgsFeatureId featureid : featureIds[layerId] ) { - for ( int iRing = 0, nRings = geom->ringCount( iPart ); iRing < nRings; ++iRing ) + if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 ); + QgsFeature feature; + if ( !getFeaturePool( layerId )->get( featureid, feature ) ) { - if ( QgsGeometryCheckerUtils::polyLineSize( geom, iPart, iRing ) < 3 ) + continue; + } + QgsGeometry featureGeom = feature.geometry(); + QgsAbstractGeometry *geom = featureGeom.geometry(); + for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart ) + { + for ( int iRing = 0, nRings = geom->ringCount( iPart ); iRing < nRings; ++iRing ) { - errors.append( new QgsGeometryCheckError( this, featureid, geom->vertexAt( QgsVertexId( iPart, iRing, 0 ) ), QgsVertexId( iPart, iRing ) ) ); + if ( QgsGeometryCheckerUtils::polyLineSize( geom, iPart, iRing ) < 3 ) + { + errors.append( new QgsGeometryCheckError( this, layerId, featureid, geom->vertexAt( QgsVertexId( iPart, iRing, 0 ) ), QgsVertexId( iPart, iRing ) ) ); + } } } } } } -void QgsGeometryDegeneratePolygonCheck::fixError( QgsGeometryCheckError *error, int method, int /*mergeAttributeIndex*/, Changes &changes ) const +void QgsGeometryDegeneratePolygonCheck::fixError( QgsGeometryCheckError *error, int method, const QMap & /*mergeAttributeIndices*/, Changes &changes ) const { QgsFeature feature; - if ( !mFeaturePool->get( error->featureId(), feature ) ) + if ( !getFeaturePool( error->layerId() )->get( error->featureId(), feature ) ) { error->setObsolete(); return; @@ -75,7 +82,7 @@ void QgsGeometryDegeneratePolygonCheck::fixError( QgsGeometryCheckError *error, } else if ( method == DeleteRing ) { - deleteFeatureGeometryRing( feature, vidx.part, vidx.ring, changes ); + deleteFeatureGeometryRing( error->layerId(), feature, vidx.part, vidx.ring, changes ); error->setFixed( method ); } else diff --git a/src/plugins/geometry_checker/checks/qgsgeometrydegeneratepolygoncheck.h b/src/plugins/geometry_checker/checks/qgsgeometrydegeneratepolygoncheck.h index ac274744bff..d9f20d908c6 100644 --- a/src/plugins/geometry_checker/checks/qgsgeometrydegeneratepolygoncheck.h +++ b/src/plugins/geometry_checker/checks/qgsgeometrydegeneratepolygoncheck.h @@ -23,10 +23,10 @@ class QgsGeometryDegeneratePolygonCheck : public QgsGeometryCheck Q_OBJECT public: - explicit QgsGeometryDegeneratePolygonCheck( QgsFeaturePool *featurePool ) - : QgsGeometryCheck( FeatureNodeCheck, featurePool ) {} - void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QgsFeatureIds &ids = QgsFeatureIds() ) const override; - void fixError( QgsGeometryCheckError *error, int method, int mergeAttributeIndex, Changes &changes ) const override; + explicit QgsGeometryDegeneratePolygonCheck( const QMap &featurePools ) + : QgsGeometryCheck( FeatureNodeCheck, {QgsWkbTypes::PolygonGeometry}, featurePools ) {} + void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap &ids = QMap() ) const override; + void fixError( QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QStringList getResolutionMethods() const override; QString errorDescription() const override { return tr( "Polygon with less than three nodes" ); } QString errorName() const override { return QStringLiteral( "QgsGeometryDegeneratePolygonCheck" ); } diff --git a/src/plugins/geometry_checker/checks/qgsgeometryduplicatecheck.cpp b/src/plugins/geometry_checker/checks/qgsgeometryduplicatecheck.cpp index a32eb9cf555..82c06afcb39 100644 --- a/src/plugins/geometry_checker/checks/qgsgeometryduplicatecheck.cpp +++ b/src/plugins/geometry_checker/checks/qgsgeometryduplicatecheck.cpp @@ -19,59 +19,66 @@ #include "qgsgeometry.h" #include "../utils/qgsfeaturepool.h" -void QgsGeometryDuplicateCheck::collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter, const QgsFeatureIds &ids ) const +void QgsGeometryDuplicateCheck::collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter, const QMap &ids ) const { - const QgsFeatureIds &featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids; - Q_FOREACH ( QgsFeatureId featureid, featureIds ) + QMap featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; + for ( const QString &layerId : featureIds.keys() ) { - if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 ); - QgsFeature feature; - if ( !mFeaturePool->get( featureid, feature ) ) + if ( !getCompatibility( getFeaturePool( layerId )->getLayer()->geometryType() ) ) { continue; } - QgsGeometry featureGeom = feature.geometry(); - QgsGeometryEngine *geomEngine = QgsGeometryCheckerUtils::createGeomEngine( featureGeom.geometry(), QgsGeometryCheckPrecision::tolerance() ); + for ( QgsFeatureId featureid : featureIds[layerId] ) + { + if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 ); + QgsFeature feature; + if ( !getFeaturePool( layerId )->get( featureid, feature ) ) + { + continue; + } + QgsGeometry featureGeom = feature.geometry(); + QgsGeometryEngine *geomEngine = QgsGeometryCheckerUtils::createGeomEngine( featureGeom.geometry(), QgsGeometryCheckPrecision::tolerance() ); - QList duplicates; - QgsFeatureIds ids = mFeaturePool->getIntersects( featureGeom.geometry()->boundingBox() ); - Q_FOREACH ( QgsFeatureId id, ids ) - { - // > : only report overlaps once - if ( id >= featureid ) + QList duplicates; + QgsFeatureIds ids = getFeaturePool( layerId )->getIntersects( featureGeom.geometry()->boundingBox() ); + for ( QgsFeatureId id : ids ) { - continue; + // > : only report overlaps once + if ( id >= featureid ) + { + continue; + } + QgsFeature testFeature; + if ( !getFeaturePool( layerId )->get( id, testFeature ) ) + { + continue; + } + QString errMsg; + QgsAbstractGeometry *diffGeom = geomEngine->symDifference( *testFeature.geometry().geometry(), &errMsg ); + if ( diffGeom && diffGeom->area() < QgsGeometryCheckPrecision::tolerance() ) + { + duplicates.append( id ); + } + else if ( !diffGeom ) + { + messages.append( tr( "Duplicate check between features %1 and %2: %3" ).arg( feature.id() ).arg( testFeature.id() ).arg( errMsg ) ); + } + delete diffGeom; } - QgsFeature testFeature; - if ( !mFeaturePool->get( id, testFeature ) ) + if ( !duplicates.isEmpty() ) { - continue; + std::sort( duplicates.begin(), duplicates.end() ); + errors.append( new QgsGeometryDuplicateCheckError( this, layerId, featureid, feature.geometry().geometry()->centroid(), duplicates ) ); } - QString errMsg; - QgsAbstractGeometry *diffGeom = geomEngine->symDifference( testFeature.geometry().geometry(), &errMsg ); - if ( diffGeom && diffGeom->area() < QgsGeometryCheckPrecision::tolerance() ) - { - duplicates.append( id ); - } - else if ( !diffGeom ) - { - messages.append( tr( "Duplicate check between features %1 and %2: %3" ).arg( feature.id() ).arg( testFeature.id() ).arg( errMsg ) ); - } - delete diffGeom; + delete geomEngine; } - if ( !duplicates.isEmpty() ) - { - std::sort( duplicates.begin(), duplicates.end() ); - errors.append( new QgsGeometryDuplicateCheckError( this, featureid, feature.geometry().geometry()->centroid(), duplicates ) ); - } - delete geomEngine; } } -void QgsGeometryDuplicateCheck::fixError( QgsGeometryCheckError *error, int method, int /*mergeAttributeIndex*/, Changes &changes ) const +void QgsGeometryDuplicateCheck::fixError( QgsGeometryCheckError *error, int method, const QMap & /*mergeAttributeIndices*/, Changes &changes ) const { QgsFeature feature; - if ( !mFeaturePool->get( error->featureId(), feature ) ) + if ( !getFeaturePool( error->layerId() )->get( error->featureId(), feature ) ) { error->setObsolete(); return; @@ -87,18 +94,18 @@ void QgsGeometryDuplicateCheck::fixError( QgsGeometryCheckError *error, int meth QgsGeometryEngine *geomEngine = QgsGeometryCheckerUtils::createGeomEngine( featureGeom.geometry(), QgsGeometryCheckPrecision::tolerance() ); QgsGeometryDuplicateCheckError *duplicateError = static_cast( error ); - Q_FOREACH ( QgsFeatureId id, duplicateError->duplicates() ) + for ( QgsFeatureId id : duplicateError->duplicates() ) { QgsFeature testFeature; - if ( !mFeaturePool->get( id, testFeature ) ) + if ( !getFeaturePool( error->layerId() )->get( id, testFeature ) ) { continue; } QgsAbstractGeometry *diffGeom = geomEngine->symDifference( testFeature.geometry().geometry() ); if ( diffGeom && diffGeom->area() < QgsGeometryCheckPrecision::tolerance() ) { - mFeaturePool->deleteFeature( testFeature ); - changes[id].append( Change( ChangeFeature, ChangeRemoved ) ); + getFeaturePool( error->layerId() )->deleteFeature( testFeature ); + changes[error->layerId()][id].append( Change( ChangeFeature, ChangeRemoved ) ); } delete diffGeom; diff --git a/src/plugins/geometry_checker/checks/qgsgeometryduplicatecheck.h b/src/plugins/geometry_checker/checks/qgsgeometryduplicatecheck.h index c3fb0fd864a..0f1ffd6f3c0 100644 --- a/src/plugins/geometry_checker/checks/qgsgeometryduplicatecheck.h +++ b/src/plugins/geometry_checker/checks/qgsgeometryduplicatecheck.h @@ -22,10 +22,11 @@ class QgsGeometryDuplicateCheckError : public QgsGeometryCheckError { public: QgsGeometryDuplicateCheckError( const QgsGeometryCheck *check, + const QString &layerId, QgsFeatureId featureId, const QgsPoint &errorLocation, const QList &duplicates ) - : QgsGeometryCheckError( check, featureId, errorLocation, QgsVertexId(), duplicatesString( duplicates ) ) + : QgsGeometryCheckError( check, layerId, featureId, errorLocation, QgsVertexId(), duplicatesString( duplicates ) ) , mDuplicates( duplicates ) { } QList duplicates() const { return mDuplicates; } @@ -33,6 +34,7 @@ class QgsGeometryDuplicateCheckError : public QgsGeometryCheckError bool isEqual( QgsGeometryCheckError *other ) const override { return other->check() == check() && + other->layerId() == layerId() && other->featureId() == featureId() && // static_cast: since other->checker() == checker is only true if the types are actually the same static_cast( other )->duplicates() == duplicates(); @@ -44,7 +46,7 @@ class QgsGeometryDuplicateCheckError : public QgsGeometryCheckError static inline QString duplicatesString( const QList &duplicates ) { QStringList str; - Q_FOREACH ( QgsFeatureId id, duplicates ) + for ( QgsFeatureId id : duplicates ) { str.append( QString::number( id ) ); } @@ -57,10 +59,10 @@ class QgsGeometryDuplicateCheck : public QgsGeometryCheck Q_OBJECT public: - explicit QgsGeometryDuplicateCheck( QgsFeaturePool *featurePool ) - : QgsGeometryCheck( FeatureCheck, featurePool ) {} - void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QgsFeatureIds &ids = QgsFeatureIds() ) const override; - void fixError( QgsGeometryCheckError *error, int method, int mergeAttributeIndex, Changes &changes ) const override; + explicit QgsGeometryDuplicateCheck( const QMap &featurePools ) + : QgsGeometryCheck( FeatureCheck, {QgsWkbTypes::PolygonGeometry}, featurePools ) {} + void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap &ids = QMap() ) const override; + void fixError( QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QStringList getResolutionMethods() const override; QString errorDescription() const override { return tr( "Duplicate" ); } QString errorName() const override { return QStringLiteral( "QgsGeometryDuplicateCheck" ); } diff --git a/src/plugins/geometry_checker/checks/qgsgeometryduplicatenodescheck.cpp b/src/plugins/geometry_checker/checks/qgsgeometryduplicatenodescheck.cpp index 69ae426ab0d..9750853a8e3 100644 --- a/src/plugins/geometry_checker/checks/qgsgeometryduplicatenodescheck.cpp +++ b/src/plugins/geometry_checker/checks/qgsgeometryduplicatenodescheck.cpp @@ -17,34 +17,41 @@ #include "qgsgeometryutils.h" #include "../utils/qgsfeaturepool.h" -void QgsGeometryDuplicateNodesCheck::collectErrors( QList &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QgsFeatureIds &ids ) const +void QgsGeometryDuplicateNodesCheck::collectErrors( QList &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap &ids ) const { - const QgsFeatureIds &featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids; - Q_FOREACH ( QgsFeatureId featureid, featureIds ) + QMap featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; + for ( const QString &layerId : featureIds.keys() ) { - if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 ); - QgsFeature feature; - if ( !mFeaturePool->get( featureid, feature ) ) + if ( !getCompatibility( getFeaturePool( layerId )->getLayer()->geometryType() ) ) { continue; } - - QgsGeometry featureGeom = feature.geometry(); - QgsAbstractGeometry *geom = featureGeom.geometry(); - for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart ) + for ( QgsFeatureId featureid : featureIds[layerId] ) { - for ( int iRing = 0, nRings = geom->ringCount( iPart ); iRing < nRings; ++iRing ) + if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 ); + QgsFeature feature; + if ( !getFeaturePool( layerId )->get( featureid, feature ) ) { - int nVerts = QgsGeometryCheckerUtils::polyLineSize( geom, iPart, iRing ); - if ( nVerts < 2 ) - continue; - for ( int iVert = nVerts - 1, jVert = 0; jVert < nVerts; iVert = jVert++ ) + continue; + } + + QgsGeometry featureGeom = feature.geometry(); + QgsAbstractGeometry *geom = featureGeom.geometry(); + for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart ) + { + for ( int iRing = 0, nRings = geom->ringCount( iPart ); iRing < nRings; ++iRing ) { - QgsPoint pi = geom->vertexAt( QgsVertexId( iPart, iRing, iVert ) ); - QgsPoint pj = geom->vertexAt( QgsVertexId( iPart, iRing, jVert ) ); - if ( QgsGeometryUtils::sqrDistance2D( pi, pj ) < QgsGeometryCheckPrecision::tolerance() * QgsGeometryCheckPrecision::tolerance() ) + int nVerts = QgsGeometryCheckerUtils::polyLineSize( geom, iPart, iRing ); + if ( nVerts < 2 ) + continue; + for ( int iVert = nVerts - 1, jVert = 0; jVert < nVerts; iVert = jVert++ ) { - errors.append( new QgsGeometryCheckError( this, featureid, pj, QgsVertexId( iPart, iRing, jVert ) ) ); + QgsPoint pi = geom->vertexAt( QgsVertexId( iPart, iRing, iVert ) ); + QgsPoint pj = geom->vertexAt( QgsVertexId( iPart, iRing, jVert ) ); + if ( QgsGeometryUtils::sqrDistance2D( pi, pj ) < QgsGeometryCheckPrecision::tolerance() * QgsGeometryCheckPrecision::tolerance() ) + { + errors.append( new QgsGeometryCheckError( this, layerId, featureid, pj, QgsVertexId( iPart, iRing, jVert ) ) ); + } } } } @@ -52,10 +59,10 @@ void QgsGeometryDuplicateNodesCheck::collectErrors( QList & /*mergeAttributeIndices*/, Changes &changes ) const { QgsFeature feature; - if ( !mFeaturePool->get( error->featureId(), feature ) ) + if ( !getFeaturePool( error->layerId() )->get( error->featureId(), feature ) ) { error->setObsolete(); return; @@ -104,9 +111,9 @@ void QgsGeometryDuplicateNodesCheck::fixError( QgsGeometryCheckError *error, int else { feature.setGeometry( featureGeom ); - mFeaturePool->updateFeature( feature ); + getFeaturePool( error->layerId() )->updateFeature( feature ); error->setFixed( method ); - changes[error->featureId()].append( Change( ChangeNode, ChangeRemoved, error->vidx() ) ); + changes[error->layerId()][error->featureId()].append( Change( ChangeNode, ChangeRemoved, error->vidx() ) ); } } else diff --git a/src/plugins/geometry_checker/checks/qgsgeometryduplicatenodescheck.h b/src/plugins/geometry_checker/checks/qgsgeometryduplicatenodescheck.h index 6247263049d..e118121290c 100644 --- a/src/plugins/geometry_checker/checks/qgsgeometryduplicatenodescheck.h +++ b/src/plugins/geometry_checker/checks/qgsgeometryduplicatenodescheck.h @@ -23,10 +23,10 @@ class QgsGeometryDuplicateNodesCheck : public QgsGeometryCheck Q_OBJECT public: - explicit QgsGeometryDuplicateNodesCheck( QgsFeaturePool *featurePool ) - : QgsGeometryCheck( FeatureNodeCheck, featurePool ) {} - void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QgsFeatureIds &ids = QgsFeatureIds() ) const override; - void fixError( QgsGeometryCheckError *error, int method, int mergeAttributeIndex, Changes &changes ) const override; + explicit QgsGeometryDuplicateNodesCheck( const QMap &featurePools ) + : QgsGeometryCheck( FeatureNodeCheck, {QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}, featurePools ) {} + void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap &ids = QMap() ) const override; + void fixError( QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QStringList getResolutionMethods() const override; QString errorDescription() const override { return tr( "Duplicate node" ); } QString errorName() const override { return QStringLiteral( "QgsGeometryDuplicateNodesCheck" ); } diff --git a/src/plugins/geometry_checker/checks/qgsgeometrygapcheck.cpp b/src/plugins/geometry_checker/checks/qgsgeometrygapcheck.cpp index 0afcd4f2314..69c20425243 100644 --- a/src/plugins/geometry_checker/checks/qgsgeometrygapcheck.cpp +++ b/src/plugins/geometry_checker/checks/qgsgeometrygapcheck.cpp @@ -19,122 +19,131 @@ #include "../utils/qgsfeaturepool.h" -void QgsGeometryGapCheck::collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter, const QgsFeatureIds &ids ) const +void QgsGeometryGapCheck::collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter, const QMap &ids ) const { - Q_ASSERT( mFeaturePool->getLayer()->geometryType() == QgsWkbTypes::PolygonGeometry ); if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 ); + QMap featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; // Collect geometries, build spatial index - QList geomList; - const QgsFeatureIds &featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids; - Q_FOREACH ( QgsFeatureId id, featureIds ) + for ( const QString &layerId : featureIds.keys() ) { - QgsFeature feature; - if ( mFeaturePool->get( id, feature ) ) - { - geomList.append( feature.geometry() ); - } - } - - if ( geomList.isEmpty() ) - { - return; - } - - QgsGeometryEngine *geomEngine = QgsGeometryCheckerUtils::createGeomEngine( nullptr, QgsGeometryCheckPrecision::tolerance() ); - - // Create union of geometry - QString errMsg; - QgsAbstractGeometry *unionGeom = geomEngine->combine( geomList, &errMsg ); - delete geomEngine; - if ( !unionGeom ) - { - messages.append( tr( "Gap check: %1" ).arg( errMsg ) ); - return; - } - - // Get envelope of union - geomEngine = QgsGeometryCheckerUtils::createGeomEngine( unionGeom, QgsGeometryCheckPrecision::tolerance() ); - QgsAbstractGeometry *envelope = geomEngine->envelope( &errMsg ); - delete geomEngine; - if ( !envelope ) - { - messages.append( tr( "Gap check: %1" ).arg( errMsg ) ); - delete unionGeom; - return; - } - - // Buffer envelope - geomEngine = QgsGeometryCheckerUtils::createGeomEngine( envelope, QgsGeometryCheckPrecision::tolerance() ); - QgsAbstractGeometry *bufEnvelope = geomEngine->buffer( 2, 0, GEOSBUF_CAP_SQUARE, GEOSBUF_JOIN_MITRE, 4. ); //#spellok //#spellok - delete geomEngine; - delete envelope; - envelope = bufEnvelope; - - // Compute difference between envelope and union to obtain gap polygons - geomEngine = QgsGeometryCheckerUtils::createGeomEngine( envelope, QgsGeometryCheckPrecision::tolerance() ); - QgsAbstractGeometry *diffGeom = geomEngine->difference( unionGeom, &errMsg ); - delete geomEngine; - if ( !diffGeom ) - { - messages.append( tr( "Gap check: %1" ).arg( errMsg ) ); - delete unionGeom; - delete diffGeom; - return; - } - - // For each gap polygon which does not lie on the boundary, get neighboring polygons and add error - for ( int iPart = 0, nParts = diffGeom->partCount(); iPart < nParts; ++iPart ) - { - QgsAbstractGeometry *geom = QgsGeometryCheckerUtils::getGeomPart( diffGeom, iPart )->clone(); - // Skip the gap between features and boundingbox - if ( geom->boundingBox() == envelope->boundingBox() ) + if ( !getCompatibility( getFeaturePool( layerId )->getLayer()->geometryType() ) ) { continue; } - - // Skip gaps above threshold - if ( geom->area() > mThreshold || geom->area() < QgsGeometryCheckPrecision::reducedTolerance() ) - { - continue; - } - - // Get neighboring polygons - QgsFeatureIds neighboringIds; - QgsRectangle gapAreaBBox = geom->boundingBox(); - QgsFeatureIds intersectIds = mFeaturePool->getIntersects( geom->boundingBox() ); - - Q_FOREACH ( QgsFeatureId id, intersectIds ) + double mapToLayerUnits = getFeaturePool( layerId )->getMapToLayerUnits(); + double gapAreaThreshold = mThresholdMapUnits * mapToLayerUnits * mapToLayerUnits; + QList geomList; + for ( QgsFeatureId id : featureIds[layerId] ) { QgsFeature feature; - if ( !mFeaturePool->get( id, feature ) ) + if ( getFeaturePool( layerId )->get( id, feature ) ) + { + geomList.append( feature.geometry().geometry()->clone() ); + } + } + + if ( geomList.isEmpty() ) + { + return; + } + + QgsGeometryEngine *geomEngine = QgsGeometryCheckerUtils::createGeomEngine( nullptr, QgsGeometryCheckPrecision::tolerance() ); + + // Create union of geometry + QString errMsg; + QgsAbstractGeometry *unionGeom = geomEngine->combine( geomList, &errMsg ); + qDeleteAll( geomList ); + delete geomEngine; + if ( !unionGeom ) + { + messages.append( tr( "Gap check: %1" ).arg( errMsg ) ); + return; + } + + // Get envelope of union + geomEngine = QgsGeometryCheckerUtils::createGeomEngine( unionGeom, QgsGeometryCheckPrecision::tolerance() ); + QgsAbstractGeometry *envelope = geomEngine->envelope( &errMsg ); + delete geomEngine; + if ( !envelope ) + { + messages.append( tr( "Gap check: %1" ).arg( errMsg ) ); + delete unionGeom; + return; + } + + // Buffer envelope + geomEngine = QgsGeometryCheckerUtils::createGeomEngine( envelope, QgsGeometryCheckPrecision::tolerance() ); + QgsAbstractGeometry *bufEnvelope = geomEngine->buffer( 2, 0, GEOSBUF_CAP_SQUARE, GEOSBUF_JOIN_MITRE, 4. ); + delete geomEngine; + delete envelope; + envelope = bufEnvelope; + + // Compute difference between envelope and union to obtain gap polygons + geomEngine = QgsGeometryCheckerUtils::createGeomEngine( envelope, QgsGeometryCheckPrecision::tolerance() ); + QgsAbstractGeometry *diffGeom = geomEngine->difference( *unionGeom, &errMsg ); + delete geomEngine; + if ( !diffGeom ) + { + messages.append( tr( "Gap check: %1" ).arg( errMsg ) ); + delete unionGeom; + delete diffGeom; + return; + } + + // For each gap polygon which does not lie on the boundary, get neighboring polygons and add error + for ( int iPart = 0, nParts = diffGeom->partCount(); iPart < nParts; ++iPart ) + { + QgsAbstractGeometry *geom = QgsGeometryCheckerUtils::getGeomPart( diffGeom, iPart )->clone(); + // Skip the gap between features and boundingbox + if ( geom->boundingBox() == envelope->boundingBox() ) { continue; } - QgsGeometry featureGeom = feature.geometry(); - QgsAbstractGeometry *geom2 = featureGeom.geometry(); - if ( QgsGeometryCheckerUtils::sharedEdgeLength( geom, geom2, QgsGeometryCheckPrecision::reducedTolerance() ) > 0 ) + + // Skip gaps above threshold + if ( geom->area() > gapAreaThreshold || geom->area() < QgsGeometryCheckPrecision::reducedTolerance() ) { - neighboringIds.insert( feature.id() ); - gapAreaBBox.combineExtentWith( geom2->boundingBox() ); + continue; } - } - if ( neighboringIds.isEmpty() ) - { - delete geom; - continue; + + // Get neighboring polygons + QgsFeatureIds neighboringIds; + QgsRectangle gapAreaBBox = geom->boundingBox(); + QgsFeatureIds intersectIds = getFeaturePool( layerId )->getIntersects( geom->boundingBox() ); + + for ( QgsFeatureId id : intersectIds ) + { + QgsFeature feature; + if ( !getFeaturePool( layerId )->get( id, feature ) ) + { + continue; + } + QgsGeometry featureGeom = feature.geometry(); + QgsAbstractGeometry *geom2 = featureGeom.geometry(); + if ( QgsGeometryCheckerUtils::sharedEdgeLength( geom, geom2, QgsGeometryCheckPrecision::reducedTolerance() ) > 0 ) + { + neighboringIds.insert( feature.id() ); + gapAreaBBox.unionRect( geom2->boundingBox() ); + } + } + if ( neighboringIds.isEmpty() ) + { + delete geom; + continue; + } + + // Add error + errors.append( new QgsGeometryGapCheckError( this, layerId, geom, neighboringIds, geom->area(), gapAreaBBox ) ); } - // Add error - errors.append( new QgsGeometryGapCheckError( this, geom, neighboringIds, geom->area(), gapAreaBBox ) ); + delete unionGeom; + delete envelope; + delete diffGeom; } - - delete unionGeom; - delete envelope; - delete diffGeom; } -void QgsGeometryGapCheck::fixError( QgsGeometryCheckError *error, int method, int /*mergeAttributeIndex*/, Changes &changes ) const +void QgsGeometryGapCheck::fixError( QgsGeometryCheckError *error, int method, const QMap & /*mergeAttributeIndices*/, Changes &changes ) const { if ( method == NoChange ) { @@ -167,10 +176,10 @@ bool QgsGeometryGapCheck::mergeWithNeighbor( QgsGeometryGapCheckError *err, Chan QgsAbstractGeometry *errGeometry = QgsGeometryCheckerUtils::getGeomPart( err->geometry(), 0 ); // Search for touching neighboring geometries - Q_FOREACH ( QgsFeatureId testId, err->neighbors() ) + for ( QgsFeatureId testId : err->neighbors() ) { QgsFeature testFeature; - if ( !mFeaturePool->get( testId, testFeature ) ) + if ( !getFeaturePool( err->layerId() )->get( testId, testFeature ) ) { continue; } @@ -206,7 +215,7 @@ bool QgsGeometryGapCheck::mergeWithNeighbor( QgsGeometryGapCheckError *err, Chan } // Add merged polygon to destination geometry - replaceFeatureGeometryPart( mergeFeature, mergePartIdx, combinedGeom, changes ); + replaceFeatureGeometryPart( err->layerId(), mergeFeature, mergePartIdx, combinedGeom, changes ); return true; } diff --git a/src/plugins/geometry_checker/checks/qgsgeometrygapcheck.h b/src/plugins/geometry_checker/checks/qgsgeometrygapcheck.h index 88a838bf6d3..fbe39372b84 100644 --- a/src/plugins/geometry_checker/checks/qgsgeometrygapcheck.h +++ b/src/plugins/geometry_checker/checks/qgsgeometrygapcheck.h @@ -23,11 +23,12 @@ class QgsGeometryGapCheckError : public QgsGeometryCheckError { public: QgsGeometryGapCheckError( const QgsGeometryCheck *check, + const QString &layerId, QgsAbstractGeometry *geometry, const QgsFeatureIds &neighbors, double area, const QgsRectangle &gapAreaBBox ) - : QgsGeometryCheckError( check, FEATUREID_NULL, geometry->centroid(), QgsVertexId(), area, ValueArea ) + : QgsGeometryCheckError( check, layerId, FEATUREID_NULL, geometry->centroid(), QgsVertexId(), area, ValueArea ) , mNeighbors( neighbors ) , mGapAreaBBox( gapAreaBBox ) { @@ -50,7 +51,7 @@ class QgsGeometryGapCheckError : public QgsGeometryCheckError bool closeMatch( QgsGeometryCheckError *other ) const override { QgsGeometryGapCheckError *err = dynamic_cast( other ); - return err && err->neighbors() == neighbors(); + return err && err->layerId() == layerId() && err->neighbors() == neighbors(); } void update( const QgsGeometryCheckError *other ) override @@ -85,12 +86,12 @@ class QgsGeometryGapCheck : public QgsGeometryCheck Q_OBJECT public: - QgsGeometryGapCheck( QgsFeaturePool *featurePool, double threshold ) - : QgsGeometryCheck( LayerCheck, featurePool ) - , mThreshold( threshold ) + QgsGeometryGapCheck( const QMap &featurePools, double thresholdMapUnits ) + : QgsGeometryCheck( LayerCheck, {QgsWkbTypes::PolygonGeometry}, featurePools ) + , mThresholdMapUnits( thresholdMapUnits ) {} - void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QgsFeatureIds &ids = QgsFeatureIds() ) const override; - void fixError( QgsGeometryCheckError *error, int method, int mergeAttributeIndex, Changes &changes ) const override; + void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap &ids = QMap() ) const override; + void fixError( QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QStringList getResolutionMethods() const override; QString errorDescription() const override { return tr( "Gap" ); } QString errorName() const override { return QStringLiteral( "QgsGeometryGapCheck" ); } @@ -98,7 +99,7 @@ class QgsGeometryGapCheck : public QgsGeometryCheck private: enum ResolutionMethod { MergeLongestEdge, NoChange }; - double mThreshold; + double mThresholdMapUnits; bool mergeWithNeighbor( QgsGeometryGapCheckError *err, Changes &changes, QString &errMsg ) const; }; diff --git a/src/plugins/geometry_checker/checks/qgsgeometryholecheck.cpp b/src/plugins/geometry_checker/checks/qgsgeometryholecheck.cpp index e9d55a577e9..19801f58e66 100644 --- a/src/plugins/geometry_checker/checks/qgsgeometryholecheck.cpp +++ b/src/plugins/geometry_checker/checks/qgsgeometryholecheck.cpp @@ -16,35 +16,42 @@ #include "qgsgeometryholecheck.h" #include "../utils/qgsfeaturepool.h" -void QgsGeometryHoleCheck::collectErrors( QList &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QgsFeatureIds &ids ) const +void QgsGeometryHoleCheck::collectErrors( QList &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap &ids ) const { - const QgsFeatureIds &featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids; - Q_FOREACH ( QgsFeatureId featureid, featureIds ) + QMap featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; + for ( const QString &layerId : featureIds.keys() ) { - if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 ); - QgsFeature feature; - if ( !mFeaturePool->get( featureid, feature ) ) + if ( !getCompatibility( getFeaturePool( layerId )->getLayer()->geometryType() ) ) { continue; } - - QgsGeometry featureGeom = feature.geometry(); - QgsAbstractGeometry *geom = featureGeom.geometry(); - for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart ) + for ( QgsFeatureId featureid : featureIds[layerId] ) { - // Rings after the first one are interiors - for ( int iRing = 1, nRings = geom->ringCount( iPart ); iRing < nRings; ++iRing ) + if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 ); + QgsFeature feature; + if ( !getFeaturePool( layerId )->get( featureid, feature ) ) { - errors.append( new QgsGeometryCheckError( this, featureid, QgsGeometryCheckerUtils::getGeomPart( geom, iPart )->centroid(), QgsVertexId( iPart, iRing ) ) ); + continue; + } + + QgsGeometry featureGeom = feature.geometry(); + QgsAbstractGeometry *geom = featureGeom.geometry(); + for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart ) + { + // Rings after the first one are interiors + for ( int iRing = 1, nRings = geom->ringCount( iPart ); iRing < nRings; ++iRing ) + { + errors.append( new QgsGeometryCheckError( this, layerId, featureid, QgsGeometryCheckerUtils::getGeomPart( geom, iPart )->centroid(), QgsVertexId( iPart, iRing ) ) ); + } } } } } -void QgsGeometryHoleCheck::fixError( QgsGeometryCheckError *error, int method, int /*mergeAttributeIndex*/, Changes &changes ) const +void QgsGeometryHoleCheck::fixError( QgsGeometryCheckError *error, int method, const QMap & /*mergeAttributeIndices*/, Changes &changes ) const { QgsFeature feature; - if ( !mFeaturePool->get( error->featureId(), feature ) ) + if ( !getFeaturePool( error->layerId() )->get( error->featureId(), feature ) ) { error->setObsolete(); return; @@ -67,7 +74,7 @@ void QgsGeometryHoleCheck::fixError( QgsGeometryCheckError *error, int method, i } else if ( method == RemoveHoles ) { - deleteFeatureGeometryRing( feature, vidx.part, vidx.ring, changes ); + deleteFeatureGeometryRing( error->layerId(), feature, vidx.part, vidx.ring, changes ); error->setFixed( method ); } else diff --git a/src/plugins/geometry_checker/checks/qgsgeometryholecheck.h b/src/plugins/geometry_checker/checks/qgsgeometryholecheck.h index 82b9df4c775..9f2aea0b9b6 100644 --- a/src/plugins/geometry_checker/checks/qgsgeometryholecheck.h +++ b/src/plugins/geometry_checker/checks/qgsgeometryholecheck.h @@ -23,10 +23,10 @@ class QgsGeometryHoleCheck : public QgsGeometryCheck Q_OBJECT public: - explicit QgsGeometryHoleCheck( QgsFeaturePool *featurePool ) - : QgsGeometryCheck( FeatureCheck, featurePool ) {} - void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QgsFeatureIds &ids = QgsFeatureIds() ) const override; - void fixError( QgsGeometryCheckError *error, int method, int mergeAttributeIndex, Changes &changes ) const override; + explicit QgsGeometryHoleCheck( const QMap &featurePools ) + : QgsGeometryCheck( FeatureCheck, {QgsWkbTypes::PolygonGeometry}, featurePools ) {} + void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap &ids = QMap() ) const override; + void fixError( QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QStringList getResolutionMethods() const override; QString errorDescription() const override { return tr( "Polygon with hole" ); } QString errorName() const override { return QStringLiteral( "QgsGeometryHoleCheck" ); } diff --git a/src/plugins/geometry_checker/checks/qgsgeometrymultipartcheck.cpp b/src/plugins/geometry_checker/checks/qgsgeometrymultipartcheck.cpp index c2d4f68b3e8..99871aa3f24 100644 --- a/src/plugins/geometry_checker/checks/qgsgeometrymultipartcheck.cpp +++ b/src/plugins/geometry_checker/checks/qgsgeometrymultipartcheck.cpp @@ -16,32 +16,39 @@ #include "qgsgeometrymultipartcheck.h" #include "../utils/qgsfeaturepool.h" -void QgsGeometryMultipartCheck::collectErrors( QList &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QgsFeatureIds &ids ) const +void QgsGeometryMultipartCheck::collectErrors( QList &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap &ids ) const { - const QgsFeatureIds &featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids; - Q_FOREACH ( QgsFeatureId featureid, featureIds ) + QMap featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; + for ( const QString &layerId : featureIds.keys() ) { - if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 ); - QgsFeature feature; - if ( !mFeaturePool->get( featureid, feature ) ) + if ( !getCompatibility( getFeaturePool( layerId )->getLayer()->geometryType() ) ) { continue; } - QgsGeometry featureGeom = feature.geometry(); - QgsAbstractGeometry *geom = featureGeom.geometry(); - - QgsWkbTypes::Type type = geom->wkbType(); - if ( geom->partCount() == 1 && QgsWkbTypes::isMultiType( type ) ) + for ( QgsFeatureId featureid : featureIds[layerId] ) { - errors.append( new QgsGeometryCheckError( this, featureid, geom->centroid() ) ); + if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 ); + QgsFeature feature; + if ( !getFeaturePool( layerId )->get( featureid, feature ) ) + { + continue; + } + QgsGeometry featureGeom = feature.geometry(); + QgsAbstractGeometry *geom = featureGeom.geometry(); + + QgsWkbTypes::Type type = geom->wkbType(); + if ( geom->partCount() == 1 && QgsWkbTypes::isMultiType( type ) ) + { + errors.append( new QgsGeometryCheckError( this, layerId, featureid, geom->centroid() ) ); + } } } } -void QgsGeometryMultipartCheck::fixError( QgsGeometryCheckError *error, int method, int /*mergeAttributeIndex*/, Changes &changes ) const +void QgsGeometryMultipartCheck::fixError( QgsGeometryCheckError *error, int method, const QMap & /*mergeAttributeIndices*/, Changes &changes ) const { QgsFeature feature; - if ( !mFeaturePool->get( error->featureId(), feature ) ) + if ( !getFeaturePool( error->layerId() )->get( error->featureId(), feature ) ) { error->setObsolete(); return; @@ -64,15 +71,15 @@ void QgsGeometryMultipartCheck::fixError( QgsGeometryCheckError *error, int meth else if ( method == ConvertToSingle ) { feature.setGeometry( QgsGeometry( QgsGeometryCheckerUtils::getGeomPart( geom, 0 )->clone() ) ); - mFeaturePool->updateFeature( feature ); + getFeaturePool( error->layerId() )->updateFeature( feature ); error->setFixed( method ); - changes[feature.id()].append( Change( ChangeFeature, ChangeChanged ) ); + changes[error->layerId()][feature.id()].append( Change( ChangeFeature, ChangeChanged ) ); } else if ( method == RemoveObject ) { - mFeaturePool->deleteFeature( feature ); + getFeaturePool( error->layerId() )->deleteFeature( feature ); error->setFixed( method ); - changes[feature.id()].append( Change( ChangeFeature, ChangeRemoved ) ); + changes[error->layerId()][feature.id()].append( Change( ChangeFeature, ChangeRemoved ) ); } else { diff --git a/src/plugins/geometry_checker/checks/qgsgeometrymultipartcheck.h b/src/plugins/geometry_checker/checks/qgsgeometrymultipartcheck.h index 91a48c29e2d..c58f373b70e 100644 --- a/src/plugins/geometry_checker/checks/qgsgeometrymultipartcheck.h +++ b/src/plugins/geometry_checker/checks/qgsgeometrymultipartcheck.h @@ -23,10 +23,10 @@ class QgsGeometryMultipartCheck : public QgsGeometryCheck Q_OBJECT public: - explicit QgsGeometryMultipartCheck( QgsFeaturePool *featurePool ) - : QgsGeometryCheck( FeatureCheck, featurePool ) {} - void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QgsFeatureIds &ids = QgsFeatureIds() ) const override; - void fixError( QgsGeometryCheckError *error, int method, int mergeAttributeIndex, Changes &changes ) const override; + explicit QgsGeometryMultipartCheck( const QMap &featurePools ) + : QgsGeometryCheck( FeatureCheck, {QgsWkbTypes::PointGeometry, QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}, featurePools ) {} + void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap &ids = QMap() ) const override; + void fixError( QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QStringList getResolutionMethods() const override; QString errorDescription() const override { return tr( "Multipart object with only one feature" ); } QString errorName() const override { return QStringLiteral( "QgsGeometryMultipartCheck" ); } diff --git a/src/plugins/geometry_checker/checks/qgsgeometryoverlapcheck.cpp b/src/plugins/geometry_checker/checks/qgsgeometryoverlapcheck.cpp index 0ac17d81f07..48e5d5c17fb 100644 --- a/src/plugins/geometry_checker/checks/qgsgeometryoverlapcheck.cpp +++ b/src/plugins/geometry_checker/checks/qgsgeometryoverlapcheck.cpp @@ -17,72 +17,81 @@ #include "qgsgeometryoverlapcheck.h" #include "../utils/qgsfeaturepool.h" -void QgsGeometryOverlapCheck::collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter, const QgsFeatureIds &ids ) const +void QgsGeometryOverlapCheck::collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter, const QMap &ids ) const { - const QgsFeatureIds &featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids; - Q_FOREACH ( QgsFeatureId featureid, featureIds ) + QMap featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; + for ( const QString &layerId : featureIds.keys() ) { - if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 ); - QgsFeature feature; - if ( !mFeaturePool->get( featureid, feature ) ) + if ( !getCompatibility( getFeaturePool( layerId )->getLayer()->geometryType() ) ) { continue; } - QgsGeometry featureGeom = feature.geometry(); - QgsAbstractGeometry *geom = featureGeom.geometry(); - QgsGeometryEngine *geomEngine = QgsGeometryCheckerUtils::createGeomEngine( geom, QgsGeometryCheckPrecision::tolerance() ); - - QgsFeatureIds ids = mFeaturePool->getIntersects( feature.geometry().boundingBox() ); - Q_FOREACH ( QgsFeatureId otherid, ids ) + double mapToLayerUnits = getFeaturePool( layerId )->getMapToLayerUnits(); + double overlapThreshold = mThresholdMapUnits * mapToLayerUnits * mapToLayerUnits; + for ( QgsFeatureId featureid : featureIds[layerId] ) { - // >= : only report overlaps once - if ( otherid >= featureid ) + if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 ); + QgsFeature feature; + if ( !getFeaturePool( layerId )->get( featureid, feature ) ) { continue; } + QgsGeometry featureGeom = feature.geometry(); + QgsAbstractGeometry *geom = featureGeom.geometry(); + QgsGeometryEngine *geomEngine = QgsGeometryCheckerUtils::createGeomEngine( geom, QgsGeometryCheckPrecision::tolerance() ); - QgsFeature otherFeature; - if ( !mFeaturePool->get( otherid, otherFeature ) ) + QgsFeatureIds ids = getFeaturePool( layerId )->getIntersects( feature.geometry().boundingBox() ); + for ( QgsFeatureId otherid : ids ) { - continue; - } - - QString errMsg; - if ( geomEngine->overlaps( otherFeature.geometry().geometry(), &errMsg ) ) - { - QgsAbstractGeometry *interGeom = geomEngine->intersection( otherFeature.geometry().geometry() ); - if ( interGeom && !interGeom->isEmpty() ) + // >= : only report overlaps once + if ( otherid >= featureid ) { - QgsGeometryCheckerUtils::filter1DTypes( interGeom ); - for ( int iPart = 0, nParts = interGeom->partCount(); iPart < nParts; ++iPart ) + continue; + } + + QgsFeature otherFeature; + if ( !getFeaturePool( layerId )->get( otherid, otherFeature ) ) + { + continue; + } + + QString errMsg; + if ( geomEngine->overlaps( *otherFeature.geometry().geometry(), &errMsg ) ) + { + QgsAbstractGeometry *interGeom = geomEngine->intersection( *otherFeature.geometry().geometry() ); + if ( interGeom && !interGeom->isEmpty() ) { - double area = QgsGeometryCheckerUtils::getGeomPart( interGeom, iPart )->area(); - if ( area > QgsGeometryCheckPrecision::reducedTolerance() && area < mThreshold ) + QgsGeometryCheckerUtils::filter1DTypes( interGeom ); + for ( int iPart = 0, nParts = interGeom->partCount(); iPart < nParts; ++iPart ) { - errors.append( new QgsGeometryOverlapCheckError( this, featureid, QgsGeometryCheckerUtils::getGeomPart( interGeom, iPart )->centroid(), area, otherid ) ); + double area = QgsGeometryCheckerUtils::getGeomPart( interGeom, iPart )->area(); + if ( area > QgsGeometryCheckPrecision::reducedTolerance() && area < overlapThreshold ) + { + errors.append( new QgsGeometryOverlapCheckError( this, layerId, featureid, QgsGeometryCheckerUtils::getGeomPart( interGeom, iPart )->centroid(), area, otherid ) ); + } } } + else if ( !errMsg.isEmpty() ) + { + messages.append( tr( "Overlap check between features %1 and %2: %3" ).arg( feature.id() ).arg( otherFeature.id() ).arg( errMsg ) ); + } + delete interGeom; } - else if ( !errMsg.isEmpty() ) - { - messages.append( tr( "Overlap check between features %1 and %2: %3" ).arg( feature.id() ).arg( otherFeature.id() ).arg( errMsg ) ); - } - delete interGeom; } + delete geomEngine; } - delete geomEngine; } } -void QgsGeometryOverlapCheck::fixError( QgsGeometryCheckError *error, int method, int /*mergeAttributeIndex*/, Changes &changes ) const +void QgsGeometryOverlapCheck::fixError( QgsGeometryCheckError *error, int method, const QMap & /*mergeAttributeIndices*/, Changes &changes ) const { QString errMsg; QgsGeometryOverlapCheckError *overlapError = static_cast( error ); QgsFeature feature; QgsFeature otherFeature; - if ( !mFeaturePool->get( error->featureId(), feature ) || - !mFeaturePool->get( overlapError->otherId(), otherFeature ) ) + if ( !getFeaturePool( error->layerId() )->get( error->featureId(), feature ) || + !getFeaturePool( error->layerId() )->get( overlapError->otherId(), otherFeature ) ) { error->setObsolete(); return; @@ -169,8 +178,8 @@ void QgsGeometryOverlapCheck::fixError( QgsGeometryCheckError *error, int method { feature.setGeometry( QgsGeometry( diff1 ) ); - changes[feature.id()].append( Change( ChangeFeature, ChangeChanged ) ); - mFeaturePool->updateFeature( feature ); + changes[error->layerId()][feature.id()].append( Change( ChangeFeature, ChangeChanged ) ); + getFeaturePool( error->layerId() )->updateFeature( feature ); delete diff2; } @@ -178,8 +187,8 @@ void QgsGeometryOverlapCheck::fixError( QgsGeometryCheckError *error, int method { otherFeature.setGeometry( QgsGeometry( diff2 ) ); - changes[otherFeature.id()].append( Change( ChangeFeature, ChangeChanged ) ); - mFeaturePool->updateFeature( otherFeature ); + changes[error->layerId()][otherFeature.id()].append( Change( ChangeFeature, ChangeChanged ) ); + getFeaturePool( error->layerId() )->updateFeature( otherFeature ); delete diff1; } diff --git a/src/plugins/geometry_checker/checks/qgsgeometryoverlapcheck.h b/src/plugins/geometry_checker/checks/qgsgeometryoverlapcheck.h index 79508a35043..4c74d2e3503 100644 --- a/src/plugins/geometry_checker/checks/qgsgeometryoverlapcheck.h +++ b/src/plugins/geometry_checker/checks/qgsgeometryoverlapcheck.h @@ -22,11 +22,12 @@ class QgsGeometryOverlapCheckError : public QgsGeometryCheckError { public: QgsGeometryOverlapCheckError( const QgsGeometryCheck *check, + const QString &layerId, QgsFeatureId featureId, const QgsPoint &errorLocation, const QVariant &value, QgsFeatureId otherId ) - : QgsGeometryCheckError( check, featureId, errorLocation, QgsVertexId(), value, ValueArea ) + : QgsGeometryCheckError( check, layerId, featureId, errorLocation, QgsVertexId(), value, ValueArea ) , mOtherId( otherId ) { } QgsFeatureId otherId() const { return mOtherId; } @@ -35,6 +36,7 @@ class QgsGeometryOverlapCheckError : public QgsGeometryCheckError { QgsGeometryOverlapCheckError *err = dynamic_cast( other ); return err && + other->layerId() == layerId() && other->featureId() == featureId() && err->otherId() == otherId() && QgsGeometryCheckerUtils::pointsFuzzyEqual( location(), other->location(), QgsGeometryCheckPrecision::reducedTolerance() ) && @@ -44,7 +46,7 @@ class QgsGeometryOverlapCheckError : public QgsGeometryCheckError bool closeMatch( QgsGeometryCheckError *other ) const override { QgsGeometryOverlapCheckError *err = dynamic_cast( other ); - return err && other->featureId() == featureId() && err->otherId() == otherId(); + return err && other->layerId() == layerId() && other->featureId() == featureId() && err->otherId() == otherId(); } virtual QString description() const override { return QApplication::translate( "QgsGeometryTypeCheckError", "Overlap with %1" ).arg( otherId() ); } @@ -58,18 +60,18 @@ class QgsGeometryOverlapCheck : public QgsGeometryCheck Q_OBJECT public: - QgsGeometryOverlapCheck( QgsFeaturePool *featurePool, double threshold ) - : QgsGeometryCheck( FeatureCheck, featurePool ) - , mThreshold( threshold ) + QgsGeometryOverlapCheck( const QMap &featurePools, double thresholdMapUnits ) + : QgsGeometryCheck( FeatureCheck, {QgsWkbTypes::PolygonGeometry}, featurePools ) + , mThresholdMapUnits( thresholdMapUnits ) {} - void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QgsFeatureIds &ids = QgsFeatureIds() ) const override; - void fixError( QgsGeometryCheckError *error, int method, int mergeAttributeIndex, Changes &changes ) const override; + void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap &ids = QMap() ) const override; + void fixError( QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QStringList getResolutionMethods() const override; QString errorDescription() const override { return tr( "Overlap" ); } QString errorName() const override { return QStringLiteral( "QgsGeometryOverlapCheck" ); } private: enum ResolutionMethod { Subtract, NoChange }; - double mThreshold; + double mThresholdMapUnits; }; #endif // QGS_GEOMETRY_OVERLAP_CHECK_H diff --git a/src/plugins/geometry_checker/checks/qgsgeometrysegmentlengthcheck.cpp b/src/plugins/geometry_checker/checks/qgsgeometrysegmentlengthcheck.cpp index 1f86f0e7199..177d68c06cd 100644 --- a/src/plugins/geometry_checker/checks/qgsgeometrysegmentlengthcheck.cpp +++ b/src/plugins/geometry_checker/checks/qgsgeometrysegmentlengthcheck.cpp @@ -17,37 +17,46 @@ #include "qgsgeometryutils.h" #include "../utils/qgsfeaturepool.h" -void QgsGeometrySegmentLengthCheck::collectErrors( QList &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QgsFeatureIds &ids ) const +void QgsGeometrySegmentLengthCheck::collectErrors( QList &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap &ids ) const { - const QgsFeatureIds &featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids; - Q_FOREACH ( QgsFeatureId featureid, featureIds ) + QMap featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; + for ( const QString &layerId : featureIds.keys() ) { - if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 ); - QgsFeature feature; - if ( !mFeaturePool->get( featureid, feature ) ) + if ( !getCompatibility( getFeaturePool( layerId )->getLayer()->geometryType() ) ) { continue; } - QgsGeometry featureGeom = feature.geometry(); - QgsAbstractGeometry *geom = featureGeom.geometry(); - - for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart ) + double mapToLayerUnits = getFeaturePool( layerId )->getMapToLayerUnits(); + double minLength = mMinLengthMapUnits * mapToLayerUnits; + for ( QgsFeatureId featureid : featureIds[layerId] ) { - for ( int iRing = 0, nRings = geom->ringCount( iPart ); iRing < nRings; ++iRing ) + if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 ); + QgsFeature feature; + if ( !getFeaturePool( layerId )->get( featureid, feature ) ) { - int nVerts = QgsGeometryCheckerUtils::polyLineSize( geom, iPart, iRing ); - if ( nVerts < 2 ) + continue; + } + QgsGeometry featureGeom = feature.geometry(); + QgsAbstractGeometry *geom = featureGeom.geometry(); + + for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart ) + { + for ( int iRing = 0, nRings = geom->ringCount( iPart ); iRing < nRings; ++iRing ) { - continue; - } - for ( int iVert = 0, jVert = nVerts - 1; iVert < nVerts; jVert = iVert++ ) - { - QgsPoint pi = geom->vertexAt( QgsVertexId( iPart, iRing, iVert ) ); - QgsPoint pj = geom->vertexAt( QgsVertexId( iPart, iRing, jVert ) ); - double dist = std::sqrt( QgsGeometryUtils::sqrDistance2D( pi, pj ) ); - if ( dist < mMinLength ) + int nVerts = QgsGeometryCheckerUtils::polyLineSize( geom, iPart, iRing ); + if ( nVerts < 2 ) { - errors.append( new QgsGeometryCheckError( this, featureid, QgsPoint( 0.5 * ( pi.x() + pj.x() ), 0.5 * ( pi.y() + pj.y() ) ), QgsVertexId( iPart, iRing, iVert ), dist, QgsGeometryCheckError::ValueLength ) ); + continue; + } + for ( int iVert = 0, jVert = nVerts - 1; iVert < nVerts; jVert = iVert++ ) + { + QgsPoint pi = geom->vertexAt( QgsVertexId( iPart, iRing, iVert ) ); + QgsPoint pj = geom->vertexAt( QgsVertexId( iPart, iRing, jVert ) ); + double dist = qSqrt( QgsGeometryUtils::sqrDistance2D( pi, pj ) ); + if ( dist < minLength ) + { + errors.append( new QgsGeometryCheckError( this, layerId, featureid, QgsPoint( 0.5 * ( pi.x() + pj.x() ), 0.5 * ( pi.y() + pj.y() ) ), QgsVertexId( iPart, iRing, iVert ), dist, QgsGeometryCheckError::ValueLength ) ); + } } } } @@ -55,10 +64,10 @@ void QgsGeometrySegmentLengthCheck::collectErrors( QList & /*mergeAttributeIndices*/, Changes &/*changes*/ ) const { QgsFeature feature; - if ( !mFeaturePool->get( error->featureId(), feature ) ) + if ( !getFeaturePool( error->layerId() )->get( error->featureId(), feature ) ) { error->setObsolete(); return; @@ -85,8 +94,10 @@ void QgsGeometrySegmentLengthCheck::fixError( QgsGeometryCheckError *error, int QgsPoint pi = geom->vertexAt( error->vidx() ); QgsPoint pj = geom->vertexAt( QgsVertexId( vidx.part, vidx.ring, ( vidx.vertex - 1 + nVerts ) % nVerts ) ); - double dist = std::sqrt( QgsGeometryUtils::sqrDistance2D( pi, pj ) ); - if ( dist >= mMinLength ) + double dist = qSqrt( QgsGeometryUtils::sqrDistance2D( pi, pj ) ); + double mapToLayerUnits = getFeaturePool( error->layerId() )->getMapToLayerUnits(); + double minLength = mMinLengthMapUnits * mapToLayerUnits; + if ( dist >= minLength ) { error->setObsolete(); return; diff --git a/src/plugins/geometry_checker/checks/qgsgeometrysegmentlengthcheck.h b/src/plugins/geometry_checker/checks/qgsgeometrysegmentlengthcheck.h index aa8f6ad4cfa..aa5fc9e05e4 100644 --- a/src/plugins/geometry_checker/checks/qgsgeometrysegmentlengthcheck.h +++ b/src/plugins/geometry_checker/checks/qgsgeometrysegmentlengthcheck.h @@ -23,18 +23,18 @@ class QgsGeometrySegmentLengthCheck : public QgsGeometryCheck Q_OBJECT public: - QgsGeometrySegmentLengthCheck( QgsFeaturePool *featurePool, double minLength ) - : QgsGeometryCheck( FeatureNodeCheck, featurePool ) - , mMinLength( minLength ) + QgsGeometrySegmentLengthCheck( const QMap &featurePools, double minLengthMapUnits ) + : QgsGeometryCheck( FeatureNodeCheck, {QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}, featurePools ) + , mMinLengthMapUnits( minLengthMapUnits ) {} - void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QgsFeatureIds &ids = QgsFeatureIds() ) const override; - void fixError( QgsGeometryCheckError *error, int method, int mergeAttributeIndex, Changes &changes ) const override; + void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap &ids = QMap() ) const override; + void fixError( QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QStringList getResolutionMethods() const override; QString errorDescription() const override { return tr( "Minimal segment length" ); } QString errorName() const override { return QStringLiteral( "QgsGeometrySegmentLengthCheck" ); } private: enum ResolutionMethod { NoChange }; - double mMinLength; + double mMinLengthMapUnits; }; #endif // QGS_GEOMETRY_SEGMENTLENGTH_CHECK_H diff --git a/src/plugins/geometry_checker/checks/qgsgeometryselfcontactcheck.cpp b/src/plugins/geometry_checker/checks/qgsgeometryselfcontactcheck.cpp index 1551f5284c5..e387fdcb917 100644 --- a/src/plugins/geometry_checker/checks/qgsgeometryselfcontactcheck.cpp +++ b/src/plugins/geometry_checker/checks/qgsgeometryselfcontactcheck.cpp @@ -9,71 +9,78 @@ #include "qgsgeometryutils.h" #include "../utils/qgsfeaturepool.h" -void QgsGeometrySelfContactCheck::collectErrors( QList &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QgsFeatureIds &ids ) const +void QgsGeometrySelfContactCheck::collectErrors( QList &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap &ids ) const { - const QgsFeatureIds &featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids; + QMap featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; double tolerance = QgsGeometryCheckPrecision::tolerance(); - foreach ( const QgsFeatureId &featureid, featureIds ) + for ( const QString &layerId : featureIds.keys() ) { - if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 ); - QgsFeature feature; - if ( !mFeaturePool->get( featureid, feature ) ) + if ( !getCompatibility( getFeaturePool( layerId )->getLayer()->geometryType() ) ) { continue; } - QgsAbstractGeometry *geom = feature.geometry().geometry(); - - for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart ) + for ( QgsFeatureId featureid : featureIds[layerId] ) { - for ( int iRing = 0, nRings = geom->ringCount( iPart ); iRing < nRings; ++iRing ) + if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 ); + QgsFeature feature; + if ( !getFeaturePool( layerId )->get( featureid, feature ) ) { - // Test for self-contacts - int n = geom->vertexCount( iPart, iRing ); - bool isClosed = geom->vertexAt( QgsVertexId( iPart, iRing, 0 ) ) == geom->vertexAt( QgsVertexId( iPart, iRing, n - 1 ) ); + continue; + } + QgsAbstractGeometry *geom = feature.geometry().geometry(); - // Geometry ring without duplicate nodes - QVector vtxMap; - QVector ring; - vtxMap.append( 0 ); - ring.append( geom->vertexAt( QgsVertexId( iPart, iRing, 0 ) ) ); - for ( int i = 1; i < n; ++i ) + for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart ) + { + for ( int iRing = 0, nRings = geom->ringCount( iPart ); iRing < nRings; ++iRing ) { - QgsPoint p = geom->vertexAt( QgsVertexId( iPart, iRing, i ) ); - if ( QgsGeometryUtils::sqrDistance2D( p, ring.last() ) > tolerance * tolerance ) - { - vtxMap.append( i ); - ring.append( p ); - } - } - while ( QgsGeometryUtils::sqrDistance2D( ring.front(), ring.back() ) < tolerance * tolerance ) - { - vtxMap.pop_back(); - ring.pop_back(); - } - if ( isClosed ) - { - vtxMap.append( n - 1 ); - ring.append( ring.front() ); - } - n = ring.size(); + // Test for self-contacts + int n = geom->vertexCount( iPart, iRing ); + bool isClosed = geom->vertexAt( QgsVertexId( iPart, iRing, 0 ) ) == geom->vertexAt( QgsVertexId( iPart, iRing, n - 1 ) ); - // For each vertex, check whether it lies on a segment - for ( int iVert = 0, nVerts = n - isClosed; iVert < nVerts; ++iVert ) - { - const QgsPoint &p = ring[iVert]; - for ( int i = 0, j = 1; j < n; i = j++ ) + // Geometry ring without duplicate nodes + QVector vtxMap; + QVector ring; + vtxMap.append( 0 ); + ring.append( geom->vertexAt( QgsVertexId( iPart, iRing, 0 ) ) ); + for ( int i = 1; i < n; ++i ) { - if ( iVert == i || iVert == j || ( isClosed && iVert == 0 && j == n - 1 ) ) + QgsPoint p = geom->vertexAt( QgsVertexId( iPart, iRing, i ) ); + if ( QgsGeometryUtils::sqrDistance2D( p, ring.last() ) > tolerance * tolerance ) { - continue; + vtxMap.append( i ); + ring.append( p ); } - const QgsPoint &si = ring[i]; - const QgsPoint &sj = ring[j]; - QgsPoint q = QgsGeometryUtils::projPointOnSegment( p, si, sj ); - if ( QgsGeometryUtils::sqrDistance2D( p, q ) < tolerance * tolerance ) + } + while ( QgsGeometryUtils::sqrDistance2D( ring.front(), ring.back() ) < tolerance * tolerance ) + { + vtxMap.pop_back(); + ring.pop_back(); + } + if ( isClosed ) + { + vtxMap.append( n - 1 ); + ring.append( ring.front() ); + } + n = ring.size(); + + // For each vertex, check whether it lies on a segment + for ( int iVert = 0, nVerts = n - isClosed; iVert < nVerts; ++iVert ) + { + const QgsPoint &p = ring[iVert]; + for ( int i = 0, j = 1; j < n; i = j++ ) { - errors.append( new QgsGeometryCheckError( this, featureid, p, QgsVertexId( iPart, iRing, vtxMap[iVert] ) ) ); - break; // No need to report same contact on different segments multiple times + if ( iVert == i || iVert == j || ( isClosed && iVert == 0 && j == n - 1 ) ) + { + continue; + } + const QgsPoint &si = ring[i]; + const QgsPoint &sj = ring[j]; + QgsPoint q = QgsGeometryUtils::projPointOnSegment( p, si, sj ); + if ( QgsGeometryUtils::sqrDistance2D( p, q ) < tolerance * tolerance ) + { + errors.append( new QgsGeometryCheckError( this, layerId, featureid, p, QgsVertexId( iPart, iRing, vtxMap[iVert] ) ) ); + break; // No need to report same contact on different segments multiple times + } } } } @@ -82,7 +89,7 @@ void QgsGeometrySelfContactCheck::collectErrors( QList } } -void QgsGeometrySelfContactCheck::fixError( QgsGeometryCheckError *error, int method, int /*mergeAttributeIndex*/, Changes & /*changes*/ ) const +void QgsGeometrySelfContactCheck::fixError( QgsGeometryCheckError *error, int method, const QMap & /*mergeAttributeIndices*/, Changes & /*changes*/ ) const { if ( method == NoChange ) { diff --git a/src/plugins/geometry_checker/checks/qgsgeometryselfcontactcheck.h b/src/plugins/geometry_checker/checks/qgsgeometryselfcontactcheck.h index 82535a69b3f..15f3369b438 100644 --- a/src/plugins/geometry_checker/checks/qgsgeometryselfcontactcheck.h +++ b/src/plugins/geometry_checker/checks/qgsgeometryselfcontactcheck.h @@ -15,10 +15,10 @@ class QgsGeometrySelfContactCheck : public QgsGeometryCheck Q_OBJECT public: - QgsGeometrySelfContactCheck( QgsFeaturePool *featurePool ) - : QgsGeometryCheck( FeatureNodeCheck, featurePool ) {} - void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = 0, const QgsFeatureIds &ids = QgsFeatureIds() ) const; - void fixError( QgsGeometryCheckError *error, int method, int, Changes & ) const; + QgsGeometrySelfContactCheck( const QMap &featurePools ) + : QgsGeometryCheck( FeatureNodeCheck, {QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}, featurePools ) {} + void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = 0, const QMap &ids = QMap() ) const; + void fixError( QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes & ) const; QStringList getResolutionMethods() const; QString errorDescription() const { return tr( "Self contact" ); } QString errorName() const { return QStringLiteral( "QgsGeometrySelfContactCheck" ); } diff --git a/src/plugins/geometry_checker/checks/qgsgeometryselfintersectioncheck.cpp b/src/plugins/geometry_checker/checks/qgsgeometryselfintersectioncheck.cpp index 8e222f21254..b738d3d0394 100644 --- a/src/plugins/geometry_checker/checks/qgsgeometryselfintersectioncheck.cpp +++ b/src/plugins/geometry_checker/checks/qgsgeometryselfintersectioncheck.cpp @@ -35,7 +35,7 @@ bool QgsGeometrySelfIntersectionCheckError::handleChanges( const QgsGeometryChec { return false; } - Q_FOREACH ( const QgsGeometryCheck::Change &change, changes.value( featureId() ) ) + for ( const QgsGeometryCheck::Change &change : changes[layerId()].value( featureId() ) ) { if ( change.vidx.vertex == mInter.segment1 || change.vidx.vertex == mInter.segment1 + 1 || @@ -60,37 +60,44 @@ bool QgsGeometrySelfIntersectionCheckError::handleChanges( const QgsGeometryChec } -void QgsGeometrySelfIntersectionCheck::collectErrors( QList &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QgsFeatureIds &ids ) const +void QgsGeometrySelfIntersectionCheck::collectErrors( QList &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap &ids ) const { - const QgsFeatureIds &featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids; - Q_FOREACH ( QgsFeatureId featureid, featureIds ) + QMap featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; + for ( const QString &layerId : featureIds.keys() ) { - if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 ); - QgsFeature feature; - if ( !mFeaturePool->get( featureid, feature ) ) + if ( !getCompatibility( getFeaturePool( layerId )->getLayer()->geometryType() ) ) { continue; } - QgsGeometry featureGeom = feature.geometry(); - QgsAbstractGeometry *geom = featureGeom.geometry(); - - for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart ) + for ( QgsFeatureId featureid : featureIds[layerId] ) { - for ( int iRing = 0, nRings = geom->ringCount( iPart ); iRing < nRings; ++iRing ) + if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 ); + QgsFeature feature; + if ( !getFeaturePool( layerId )->get( featureid, feature ) ) { - Q_FOREACH ( const QgsGeometryUtils::SelfIntersection &inter, QgsGeometryUtils::getSelfIntersections( geom, iPart, iRing, QgsGeometryCheckPrecision::tolerance() ) ) + continue; + } + QgsGeometry featureGeom = feature.geometry(); + QgsAbstractGeometry *geom = featureGeom.geometry(); + + for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart ) + { + for ( int iRing = 0, nRings = geom->ringCount( iPart ); iRing < nRings; ++iRing ) { - errors.append( new QgsGeometrySelfIntersectionCheckError( this, featureid, inter.point, QgsVertexId( iPart, iRing ), inter ) ); + for ( const QgsGeometryUtils::SelfIntersection &inter : QgsGeometryUtils::getSelfIntersections( geom, iPart, iRing, QgsGeometryCheckPrecision::tolerance() ) ) + { + errors.append( new QgsGeometrySelfIntersectionCheckError( this, layerId, featureid, inter.point, QgsVertexId( iPart, iRing ), inter ) ); + } } } } } } -void QgsGeometrySelfIntersectionCheck::fixError( QgsGeometryCheckError *error, int method, int /*mergeAttributeIndex*/, Changes &changes ) const +void QgsGeometrySelfIntersectionCheck::fixError( QgsGeometryCheckError *error, int method, const QMap & /*mergeAttributeIndices*/, Changes &changes ) const { QgsFeature feature; - if ( !mFeaturePool->get( error->featureId(), feature ) ) + if ( !getFeaturePool( error->layerId() )->get( error->featureId(), feature ) ) { error->setObsolete(); return; @@ -191,11 +198,11 @@ void QgsGeometrySelfIntersectionCheck::fixError( QgsGeometryCheckError *error, i poly->removeInteriorRing( vidx.ring ); poly->addInteriorRing( ringGeom1 ); poly->addInteriorRing( ringGeom2 ); - changes[feature.id()].append( Change( ChangeRing, ChangeRemoved, vidx ) ); - changes[feature.id()].append( Change( ChangeRing, ChangeAdded, QgsVertexId( vidx.part, poly->ringCount() - 2 ) ) ); - changes[feature.id()].append( Change( ChangeRing, ChangeAdded, QgsVertexId( vidx.part, poly->ringCount() - 1 ) ) ); + changes[error->layerId()][feature.id()].append( Change( ChangeRing, ChangeRemoved, vidx ) ); + changes[error->layerId()][feature.id()].append( Change( ChangeRing, ChangeAdded, QgsVertexId( vidx.part, poly->ringCount() - 2 ) ) ); + changes[error->layerId()][feature.id()].append( Change( ChangeRing, ChangeAdded, QgsVertexId( vidx.part, poly->ringCount() - 1 ) ) ); feature.setGeometry( featureGeom ); - mFeaturePool->updateFeature( feature ); + getFeaturePool( error->layerId() )->updateFeature( feature ); } else { @@ -219,7 +226,7 @@ void QgsGeometrySelfIntersectionCheck::fixError( QgsGeometryCheckError *error, i // No point in adding ChangeAdded changes, since the entire poly2 is added anyways later on } poly->removeInteriorRing( i ); - changes[feature.id()].append( Change( ChangeRing, ChangeRemoved, QgsVertexId( vidx.part, i ) ) ); + changes[error->layerId()][feature.id()].append( Change( ChangeRing, ChangeRemoved, QgsVertexId( vidx.part, i ) ) ); } } delete geomEnginePoly1; @@ -231,10 +238,10 @@ void QgsGeometrySelfIntersectionCheck::fixError( QgsGeometryCheckError *error, i if ( dynamic_cast( geom ) ) { static_cast( geom )->addGeometry( poly2 ); - changes[feature.id()].append( Change( ChangeRing, ChangeChanged, QgsVertexId( vidx.part, vidx.ring ) ) ); - changes[feature.id()].append( Change( ChangePart, ChangeAdded, QgsVertexId( geom->partCount() - 1 ) ) ); + changes[error->layerId()][feature.id()].append( Change( ChangeRing, ChangeChanged, QgsVertexId( vidx.part, vidx.ring ) ) ); + changes[error->layerId()][feature.id()].append( Change( ChangePart, ChangeAdded, QgsVertexId( geom->partCount() - 1 ) ) ); feature.setGeometry( featureGeom ); - mFeaturePool->updateFeature( feature ); + getFeaturePool( error->layerId() )->updateFeature( feature ); } // Otherwise, create multipolygon else @@ -243,8 +250,8 @@ void QgsGeometrySelfIntersectionCheck::fixError( QgsGeometryCheckError *error, i multiPoly->addGeometry( poly->clone() ); multiPoly->addGeometry( poly2 ); feature.setGeometry( QgsGeometry( multiPoly ) ); - mFeaturePool->updateFeature( feature ); - changes[feature.id()].append( Change( ChangeFeature, ChangeChanged ) ); + getFeaturePool( error->layerId() )->updateFeature( feature ); + changes[error->layerId()][feature.id()].append( Change( ChangeFeature, ChangeChanged ) ); } } else // if ( method == ToSingleObjects ) @@ -252,10 +259,10 @@ void QgsGeometrySelfIntersectionCheck::fixError( QgsGeometryCheckError *error, i QgsFeature newFeature; newFeature.setAttributes( feature.attributes() ); newFeature.setGeometry( QgsGeometry( poly2 ) ); - mFeaturePool->updateFeature( feature ); - mFeaturePool->addFeature( newFeature ); - changes[feature.id()].append( Change( ChangeRing, ChangeChanged, QgsVertexId( vidx.part, vidx.ring ) ) ); - changes[newFeature.id()].append( Change( ChangeFeature, ChangeAdded ) ); + getFeaturePool( error->layerId() )->updateFeature( feature ); + getFeaturePool( error->layerId() )->addFeature( newFeature ); + changes[error->layerId()][feature.id()].append( Change( ChangeRing, ChangeChanged, QgsVertexId( vidx.part, vidx.ring ) ) ); + changes[error->layerId()][newFeature.id()].append( Change( ChangeFeature, ChangeAdded ) ); } } } @@ -269,10 +276,10 @@ void QgsGeometrySelfIntersectionCheck::fixError( QgsGeometryCheckError *error, i geomCollection->removeGeometry( vidx.part ); geomCollection->addGeometry( ringGeom1 ); geomCollection->addGeometry( ringGeom2 ); - mFeaturePool->updateFeature( feature ); - changes[feature.id()].append( Change( ChangePart, ChangeRemoved, QgsVertexId( vidx.part ) ) ); - changes[feature.id()].append( Change( ChangePart, ChangeAdded, QgsVertexId( geomCollection->partCount() - 2 ) ) ); - changes[feature.id()].append( Change( ChangePart, ChangeAdded, QgsVertexId( geomCollection->partCount() - 1 ) ) ); + getFeaturePool( error->layerId() )->updateFeature( feature ); + changes[error->layerId()][feature.id()].append( Change( ChangePart, ChangeRemoved, QgsVertexId( vidx.part ) ) ); + changes[error->layerId()][feature.id()].append( Change( ChangePart, ChangeAdded, QgsVertexId( geomCollection->partCount() - 2 ) ) ); + changes[error->layerId()][feature.id()].append( Change( ChangePart, ChangeAdded, QgsVertexId( geomCollection->partCount() - 1 ) ) ); } else { @@ -280,8 +287,8 @@ void QgsGeometrySelfIntersectionCheck::fixError( QgsGeometryCheckError *error, i geomCollection->addGeometry( ringGeom1 ); geomCollection->addGeometry( ringGeom2 ); feature.setGeometry( QgsGeometry( geomCollection ) ); - mFeaturePool->updateFeature( feature ); - changes[feature.id()].append( Change( ChangeFeature, ChangeChanged ) ); + getFeaturePool( error->layerId() )->updateFeature( feature ); + changes[error->layerId()][feature.id()].append( Change( ChangeFeature, ChangeChanged ) ); } } else // if(method == ToSingleObjects) @@ -292,21 +299,21 @@ void QgsGeometrySelfIntersectionCheck::fixError( QgsGeometryCheckError *error, i geomCollection->removeGeometry( vidx.part ); geomCollection->addGeometry( ringGeom1 ); feature.setGeometry( featureGeom ); - mFeaturePool->updateFeature( feature ); - changes[feature.id()].append( Change( ChangePart, ChangeRemoved, QgsVertexId( vidx.part ) ) ); - changes[feature.id()].append( Change( ChangePart, ChangeAdded, QgsVertexId( geomCollection->partCount() - 1 ) ) ); + getFeaturePool( error->layerId() )->updateFeature( feature ); + changes[error->layerId()][feature.id()].append( Change( ChangePart, ChangeRemoved, QgsVertexId( vidx.part ) ) ); + changes[error->layerId()][feature.id()].append( Change( ChangePart, ChangeAdded, QgsVertexId( geomCollection->partCount() - 1 ) ) ); } else { feature.setGeometry( QgsGeometry( ringGeom1 ) ); - mFeaturePool->updateFeature( feature ); - changes[feature.id()].append( Change( ChangeFeature, ChangeChanged, QgsVertexId( vidx.part ) ) ); + getFeaturePool( error->layerId() )->updateFeature( feature ); + changes[error->layerId()][feature.id()].append( Change( ChangeFeature, ChangeChanged, QgsVertexId( vidx.part ) ) ); } QgsFeature newFeature; newFeature.setAttributes( feature.attributes() ); newFeature.setGeometry( QgsGeometry( ringGeom2 ) ); - mFeaturePool->addFeature( newFeature ); - changes[newFeature.id()].append( Change( ChangeFeature, ChangeAdded ) ); + getFeaturePool( error->layerId() )->addFeature( newFeature ); + changes[error->layerId()][newFeature.id()].append( Change( ChangeFeature, ChangeAdded ) ); } } else diff --git a/src/plugins/geometry_checker/checks/qgsgeometryselfintersectioncheck.h b/src/plugins/geometry_checker/checks/qgsgeometryselfintersectioncheck.h index 35e273e9997..75ec64295b0 100644 --- a/src/plugins/geometry_checker/checks/qgsgeometryselfintersectioncheck.h +++ b/src/plugins/geometry_checker/checks/qgsgeometryselfintersectioncheck.h @@ -23,11 +23,12 @@ class QgsGeometrySelfIntersectionCheckError : public QgsGeometryCheckError { public: QgsGeometrySelfIntersectionCheckError( const QgsGeometryCheck *check, + const QString &layerId, QgsFeatureId featureId, const QgsPoint &errorLocation, QgsVertexId vidx, const QgsGeometryUtils::SelfIntersection &inter ) - : QgsGeometryCheckError( check, featureId, errorLocation, vidx ) + : QgsGeometryCheckError( check, layerId, featureId, errorLocation, vidx ) , mInter( inter ) { } const QgsGeometryUtils::SelfIntersection &intersection() const { return mInter; } @@ -50,10 +51,10 @@ class QgsGeometrySelfIntersectionCheck : public QgsGeometryCheck Q_OBJECT public: - explicit QgsGeometrySelfIntersectionCheck( QgsFeaturePool *featurePool ) - : QgsGeometryCheck( FeatureNodeCheck, featurePool ) {} - void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QgsFeatureIds &ids = QgsFeatureIds() ) const override; - void fixError( QgsGeometryCheckError *error, int method, int mergeAttributeIndex, Changes &changes ) const override; + explicit QgsGeometrySelfIntersectionCheck( const QMap &featurePools ) + : QgsGeometryCheck( FeatureNodeCheck, {QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}, featurePools ) {} + void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap &ids = QMap() ) const override; + void fixError( QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QStringList getResolutionMethods() const override; QString errorDescription() const override { return tr( "Self intersection" ); } QString errorName() const override { return QStringLiteral( "QgsGeometrySelfIntersectionCheck" ); } diff --git a/src/plugins/geometry_checker/checks/qgsgeometrysliverpolygoncheck.cpp b/src/plugins/geometry_checker/checks/qgsgeometrysliverpolygoncheck.cpp new file mode 100644 index 00000000000..bef3dfa16e4 --- /dev/null +++ b/src/plugins/geometry_checker/checks/qgsgeometrysliverpolygoncheck.cpp @@ -0,0 +1,32 @@ +/*************************************************************************** + qgsgeometryareacheck.cpp + --------------------- + begin : September 2015 + copyright : (C) 2014 by Sandro Mani / Sourcepole AG + email : smani at sourcepole dot ch + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsgeometrysliverpolygoncheck.h" +#include "../utils/qgsfeaturepool.h" + +bool QgsGeometrySliverPolygonCheck::checkThreshold( const QString &layerId, const QgsAbstractGeometry *geom, double &value ) const +{ + double mapToLayerUnits = getFeaturePool( layerId )->getMapToLayerUnits(); + double maxArea = mMaxAreaMapUnits * mapToLayerUnits * mapToLayerUnits; + QgsRectangle bb = geom->boundingBox(); + double maxDim = qMax( bb.width(), bb.height() ); + double area = geom->area(); + value = ( maxDim * maxDim ) / area; + if ( maxArea > 0. && area > maxArea ) + { + return false; + } + return value > mThresholdMapUnits; // the sliver threshold is actually a map unit independent number, just abusing QgsGeometryAreaCheck::mThresholdMapUnits to store it +} diff --git a/src/plugins/geometry_checker/checks/qgsgeometrysliverpolygoncheck.h b/src/plugins/geometry_checker/checks/qgsgeometrysliverpolygoncheck.h index 51a91a6a176..331666c9de1 100644 --- a/src/plugins/geometry_checker/checks/qgsgeometrysliverpolygoncheck.h +++ b/src/plugins/geometry_checker/checks/qgsgeometrysliverpolygoncheck.h @@ -23,26 +23,17 @@ class QgsGeometrySliverPolygonCheck : public QgsGeometryAreaCheck Q_OBJECT public: - QgsGeometrySliverPolygonCheck( QgsFeaturePool *featurePool, double threshold, double maxArea = 0. ) - : QgsGeometryAreaCheck( featurePool, threshold ), mMaxArea( maxArea ) {} + QgsGeometrySliverPolygonCheck( const QMap &featurePools, double threshold, double maxAreaMapUnits ) + : QgsGeometryAreaCheck( featurePools, threshold ) + , mMaxAreaMapUnits( maxAreaMapUnits ) + {} QString errorDescription() const override { return tr( "Sliver polygon" ); } QString errorName() const override { return QStringLiteral( "QgsGeometrySliverPolygonCheck" ); } private: - double mMaxArea; + double mMaxAreaMapUnits; - bool checkThreshold( const QgsAbstractGeometry *geom, double &value ) const override - { - QgsRectangle bb = geom->boundingBox(); - double maxDim = std::max( bb.width(), bb.height() ); - double area = geom->area(); - value = ( maxDim * maxDim ) / area; - if ( mMaxArea > 0. && area > mMaxArea ) - { - return false; - } - return value > mThreshold; - } + bool checkThreshold( const QString &layerId, const QgsAbstractGeometry *geom, double &value ) const override; }; #endif // QGS_GEOMETRY_SLIVERPOLYGON_CHECK_H diff --git a/src/plugins/geometry_checker/checks/qgsgeometrytypecheck.cpp b/src/plugins/geometry_checker/checks/qgsgeometrytypecheck.cpp index 0f5ee4b1acf..3aa91c120be 100644 --- a/src/plugins/geometry_checker/checks/qgsgeometrytypecheck.cpp +++ b/src/plugins/geometry_checker/checks/qgsgeometrytypecheck.cpp @@ -23,32 +23,39 @@ #include "../utils/qgsfeaturepool.h" -void QgsGeometryTypeCheck::collectErrors( QList &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QgsFeatureIds &ids ) const +void QgsGeometryTypeCheck::collectErrors( QList &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap &ids ) const { - const QgsFeatureIds &featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids; - Q_FOREACH ( QgsFeatureId featureid, featureIds ) + QMap featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; + for ( const QString &layerId : featureIds.keys() ) { - if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 ); - QgsFeature feature; - if ( !mFeaturePool->get( featureid, feature ) ) + if ( !getCompatibility( getFeaturePool( layerId )->getLayer()->geometryType() ) ) { continue; } - QgsGeometry featureGeom = feature.geometry(); - QgsAbstractGeometry *geom = featureGeom.geometry(); - - QgsWkbTypes::Type type = QgsWkbTypes::flatType( geom->wkbType() ); - if ( ( mAllowedTypes & ( 1 << type ) ) == 0 ) + for ( QgsFeatureId featureid : featureIds[layerId] ) { - errors.append( new QgsGeometryTypeCheckError( this, featureid, geom->centroid(), type ) ); + if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 ); + QgsFeature feature; + if ( !getFeaturePool( layerId )->get( featureid, feature ) ) + { + continue; + } + QgsGeometry featureGeom = feature.geometry(); + QgsAbstractGeometry *geom = featureGeom.geometry(); + + QgsWkbTypes::Type type = QgsWkbTypes::flatType( geom->wkbType() ); + if ( ( mAllowedTypes & ( 1 << type ) ) == 0 ) + { + errors.append( new QgsGeometryTypeCheckError( this, layerId, featureid, geom->centroid(), type ) ); + } } } } -void QgsGeometryTypeCheck::fixError( QgsGeometryCheckError *error, int method, int /*mergeAttributeIndex*/, Changes &changes ) const +void QgsGeometryTypeCheck::fixError( QgsGeometryCheckError *error, int method, const QMap & /*mergeAttributeIndices*/, Changes &changes ) const { QgsFeature feature; - if ( !mFeaturePool->get( error->featureId(), feature ) ) + if ( !getFeaturePool( error->layerId() )->get( error->featureId(), feature ) ) { error->setObsolete(); return; @@ -80,13 +87,13 @@ void QgsGeometryTypeCheck::fixError( QgsGeometryCheckError *error, int method, i QgsFeature newFeature; newFeature.setAttributes( feature.attributes() ); newFeature.setGeometry( QgsGeometry( QgsGeometryCheckerUtils::getGeomPart( geom, iPart )->clone() ) ); - mFeaturePool->addFeature( newFeature ); - changes[newFeature.id()].append( Change( ChangeFeature, ChangeAdded ) ); + getFeaturePool( error->layerId() )->addFeature( newFeature ); + changes[error->layerId()][newFeature.id()].append( Change( ChangeFeature, ChangeAdded ) ); } // Recycle feature for part 0 feature.setGeometry( QgsGeometry( QgsGeometryCheckerUtils::getGeomPart( geom, 0 )->clone() ) ); - mFeaturePool->updateFeature( feature ); - changes[feature.id()].append( Change( ChangeFeature, ChangeChanged ) ); + getFeaturePool( error->layerId() )->updateFeature( feature ); + changes[error->layerId()][feature.id()].append( Change( ChangeFeature, ChangeChanged ) ); } // Check if corresponding multi type is allowed else if ( QgsWkbTypes::isSingleType( type ) && ( ( 1 << QgsWkbTypes::multiType( type ) ) & mAllowedTypes ) != 0 ) @@ -131,23 +138,23 @@ void QgsGeometryTypeCheck::fixError( QgsGeometryCheckError *error, int method, i geomCollection->addGeometry( geom->clone() ); feature.setGeometry( QgsGeometry( geomCollection ) ); - mFeaturePool->updateFeature( feature ); - changes[feature.id()].append( Change( ChangeFeature, ChangeChanged ) ); + getFeaturePool( error->layerId() )->updateFeature( feature ); + changes[error->layerId()][feature.id()].append( Change( ChangeFeature, ChangeChanged ) ); } } // Delete feature else { - mFeaturePool->deleteFeature( feature ); - changes[error->featureId()].append( Change( ChangeFeature, ChangeRemoved ) ); + getFeaturePool( error->layerId() )->deleteFeature( feature ); + changes[error->layerId()][error->featureId()].append( Change( ChangeFeature, ChangeRemoved ) ); } error->setFixed( method ); } else if ( method == Delete ) { - mFeaturePool->deleteFeature( feature ); + getFeaturePool( error->layerId() )->deleteFeature( feature ); error->setFixed( method ); - changes[error->featureId()].append( Change( ChangeFeature, ChangeRemoved ) ); + changes[error->layerId()][error->featureId()].append( Change( ChangeFeature, ChangeRemoved ) ); } else { diff --git a/src/plugins/geometry_checker/checks/qgsgeometrytypecheck.h b/src/plugins/geometry_checker/checks/qgsgeometrytypecheck.h index 9308e7c3d46..36049598cce 100644 --- a/src/plugins/geometry_checker/checks/qgsgeometrytypecheck.h +++ b/src/plugins/geometry_checker/checks/qgsgeometrytypecheck.h @@ -22,10 +22,11 @@ class QgsGeometryTypeCheckError : public QgsGeometryCheckError { public: QgsGeometryTypeCheckError( const QgsGeometryCheck *check, + const QString &layerId, QgsFeatureId featureId, const QgsPoint &errorLocation, QgsWkbTypes::Type flatType ) - : QgsGeometryCheckError( check, featureId, errorLocation ) + : QgsGeometryCheckError( check, layerId, featureId, errorLocation ) { mTypeName = QgsWkbTypes::displayString( flatType ); } @@ -47,12 +48,12 @@ class QgsGeometryTypeCheck : public QgsGeometryCheck Q_OBJECT public: - QgsGeometryTypeCheck( QgsFeaturePool *featurePool, int allowedTypes ) - : QgsGeometryCheck( FeatureCheck, featurePool ) - , mAllowedTypes( allowedTypes ) + QgsGeometryTypeCheck( const QMap &featurePools, int allowedTypes ) + : QgsGeometryCheck( FeatureCheck, {QgsWkbTypes::PointGeometry, QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}, featurePools ) + , mAllowedTypes( allowedTypes ) {} - void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QgsFeatureIds &ids = QgsFeatureIds() ) const override; - void fixError( QgsGeometryCheckError *error, int method, int mergeAttributeIndex, Changes &changes ) const override; + void collectErrors( QList &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap &ids = QMap() ) const override; + void fixError( QgsGeometryCheckError *error, int method, const QMap &mergeAttributeIndices, Changes &changes ) const override; QStringList getResolutionMethods() const override; QString errorDescription() const override { return tr( "Geometry type" ); } QString errorName() const override { return QStringLiteral( "QgsGeometryTypeCheck" ); } diff --git a/src/plugins/geometry_checker/qgsgeometrychecker.cpp b/src/plugins/geometry_checker/qgsgeometrychecker.cpp index 9355e04c260..260b62985ad 100644 --- a/src/plugins/geometry_checker/qgsgeometrychecker.cpp +++ b/src/plugins/geometry_checker/qgsgeometrychecker.cpp @@ -24,10 +24,9 @@ #include -QgsGeometryChecker::QgsGeometryChecker( const QList &checks, QgsFeaturePool *featurePool ) +QgsGeometryChecker::QgsGeometryChecker( const QList &checks, const QMap &featurePools ) : mChecks( checks ) - , mFeaturePool( featurePool ) - , mMergeAttributeIndex( -1 ) + , mFeaturePools( featurePools ) { } @@ -42,16 +41,18 @@ QFuture QgsGeometryChecker::execute( int *totalSteps ) if ( totalSteps ) { *totalSteps = 0; - int nCheckFeatures = mFeaturePool->getFeatureIds().size(); - Q_FOREACH ( QgsGeometryCheck *check, mChecks ) + for ( QgsGeometryCheck *check : mChecks ) { - if ( check->getCheckType() <= QgsGeometryCheck::FeatureCheck ) + for ( const QgsFeaturePool *featurePool : mFeaturePools.values() ) { - *totalSteps += nCheckFeatures; - } - else - { - *totalSteps += 1; + if ( check->getCheckType() <= QgsGeometryCheck::FeatureCheck ) + { + *totalSteps += check->getCompatibility( featurePool->getLayer()->geometryType() ) ? featurePool->getFeatureIds().size() : 0; + } + else + { + *totalSteps += 1; + } } } } @@ -74,7 +75,7 @@ void QgsGeometryChecker::emitProgressValue() emit progressValue( mProgressCounter ); } -bool QgsGeometryChecker::fixError( QgsGeometryCheckError *error, int method ) +bool QgsGeometryChecker::fixError( QgsGeometryCheckError *error, int method, bool triggerRepaint ) { mMessages.clear(); if ( error->status() >= QgsGeometryCheckError::StatusFixed ) @@ -85,7 +86,7 @@ bool QgsGeometryChecker::fixError( QgsGeometryCheckError *error, int method ) QgsGeometryCheck::Changes changes; QgsRectangle recheckArea = error->affectedAreaBBox(); - error->check()->fixError( error, method, mMergeAttributeIndex, changes ); + error->check()->fixError( error, method, mMergeAttributeIndices, changes ); emit errorUpdated( error, true ); if ( error->status() != QgsGeometryCheckError::StatusFixed ) { @@ -99,30 +100,34 @@ bool QgsGeometryChecker::fixError( QgsGeometryCheckError *error, int method ) // Determine what to recheck // - Collect all features which were changed, get affected area - QgsFeatureIds recheckFeatures; - Q_FOREACH ( QgsFeatureId id, changes.keys() ) + QMap> recheckFeatures; + for ( const QString &layerId : changes.keys() ) { - bool removed = false; - Q_FOREACH ( const QgsGeometryCheck::Change &change, changes.value( id ) ) + const QMap> &layerChanges = changes[layerId]; + for ( QgsFeatureId id : layerChanges.keys() ) { - if ( change.what == QgsGeometryCheck::ChangeFeature && change.type == QgsGeometryCheck::ChangeRemoved ) + bool removed = false; + for ( const QgsGeometryCheck::Change &change : layerChanges.value( id ) ) { - removed = true; - break; + if ( change.what == QgsGeometryCheck::ChangeFeature && change.type == QgsGeometryCheck::ChangeRemoved ) + { + removed = true; + break; + } } - } - if ( !removed ) - { - QgsFeature f; - if ( mFeaturePool->get( id, f ) ) + if ( !removed ) { - recheckFeatures.insert( id ); - recheckArea.combineExtentWith( f.geometry().boundingBox() ); + QgsFeature f; + if ( mFeaturePools[layerId]->get( id, f ) ) + { + recheckFeatures[layerId].insert( id ); + recheckArea.combineExtentWith( f.geometry().boundingBox() ); + } } } } // - Determine extent to recheck for gaps - Q_FOREACH ( QgsGeometryCheckError *err, mCheckErrors ) + for ( QgsGeometryCheckError *err : mCheckErrors ) { if ( err->check()->getCheckType() == QgsGeometryCheck::LayerCheck ) { @@ -133,17 +138,20 @@ bool QgsGeometryChecker::fixError( QgsGeometryCheckError *error, int method ) } } recheckArea.grow( 10 * QgsGeometryCheckPrecision::tolerance() ); - QgsFeatureIds recheckAreaFeatures = mFeaturePool->getIntersects( recheckArea ); - - // If only selected features were checked, confine the recheck areas to the selected features - if ( mFeaturePool->getSelectedOnly() ) + QMap recheckAreaFeatures; + for ( const QString &layerId : mFeaturePools.keys() ) { - recheckAreaFeatures = recheckAreaFeatures.intersect( mFeaturePool->getLayer()->selectedFeatureIds() ); + recheckAreaFeatures[layerId] = mFeaturePools[layerId]->getIntersects( recheckArea ); + // If only selected features were checked, confine the recheck areas to the selected features + if ( mFeaturePools[layerId]->getSelectedOnly() ) + { + recheckAreaFeatures[layerId] = recheckAreaFeatures[layerId].intersect( mFeaturePools[layerId]->getLayer()->selectedFeatureIds() ); + } } // Recheck feature / changed area to detect new errors QList recheckErrors; - Q_FOREACH ( const QgsGeometryCheck *check, mChecks ) + for ( const QgsGeometryCheck *check : mChecks ) { if ( check->getCheckType() == QgsGeometryCheck::LayerCheck ) { @@ -158,7 +166,7 @@ bool QgsGeometryChecker::fixError( QgsGeometryCheckError *error, int method ) // Remove just-fixed error from newly-found errors if no changes occurred (needed in case error was fixed with "no change") if ( changes.isEmpty() ) { - Q_FOREACH ( QgsGeometryCheckError *recheckErr, recheckErrors ) + for ( QgsGeometryCheckError *recheckErr : recheckErrors ) { if ( recheckErr->isEqual( error ) ) { @@ -170,7 +178,7 @@ bool QgsGeometryChecker::fixError( QgsGeometryCheckError *error, int method ) } // Go through error list, update other errors of the checked feature - Q_FOREACH ( QgsGeometryCheckError *err, mCheckErrors ) + for ( QgsGeometryCheckError *err : mCheckErrors ) { if ( err == error || err->status() == QgsGeometryCheckError::StatusObsolete ) { @@ -190,7 +198,7 @@ bool QgsGeometryChecker::fixError( QgsGeometryCheckError *error, int method ) // Check if this error now matches one found when rechecking the feature/area QgsGeometryCheckError *matchErr = nullptr; int nMatch = 0; - Q_FOREACH ( QgsGeometryCheckError *recheckErr, recheckErrors ) + for ( QgsGeometryCheckError *recheckErr : recheckErrors ) { if ( recheckErr->isEqual( err ) ) { @@ -219,7 +227,7 @@ bool QgsGeometryChecker::fixError( QgsGeometryCheckError *error, int method ) if ( err->status() < QgsGeometryCheckError::StatusFixed && ( // it is a FeatureNodeCheck or FeatureCheck error whose feature was rechecked - ( err->check()->getCheckType() <= QgsGeometryCheck::FeatureCheck && recheckFeatures.contains( err->featureId() ) ) || + ( err->check()->getCheckType() <= QgsGeometryCheck::FeatureCheck && recheckFeatures[err->layerId()].contains( err->featureId() ) ) || // or if it is a LayerCheck error within the rechecked area ( err->check()->getCheckType() == QgsGeometryCheck::LayerCheck && recheckArea.contains( err->affectedAreaBBox() ) ) ) @@ -231,18 +239,21 @@ bool QgsGeometryChecker::fixError( QgsGeometryCheckError *error, int method ) } // Add new errors - Q_FOREACH ( QgsGeometryCheckError *recheckErr, recheckErrors ) + for ( QgsGeometryCheckError *recheckErr : recheckErrors ) { emit errorAdded( recheckErr ); mCheckErrors.append( recheckErr ); } - return true; -} + if ( triggerRepaint ) + { + for ( const QString &layerId : changes.keys() ) + { + mFeaturePools[layerId]->getLayer()->triggerRepaint(); + } + } -QgsMapLayer *QgsGeometryChecker::getLayer() const -{ - return mFeaturePool->getLayer(); + return true; } void QgsGeometryChecker::runCheck( const QgsGeometryCheck *check ) @@ -255,7 +266,7 @@ void QgsGeometryChecker::runCheck( const QgsGeometryCheck *check ) mCheckErrors.append( errors ); mMessages.append( messages ); mErrorListMutex.unlock(); - Q_FOREACH ( QgsGeometryCheckError *error, errors ) + for ( QgsGeometryCheckError *error : errors ) { emit errorAdded( error ); } diff --git a/src/plugins/geometry_checker/qgsgeometrychecker.h b/src/plugins/geometry_checker/qgsgeometrychecker.h index 38dc4ca3912..15fe446f4d2 100644 --- a/src/plugins/geometry_checker/qgsgeometrychecker.h +++ b/src/plugins/geometry_checker/qgsgeometrychecker.h @@ -35,16 +35,13 @@ class QgsGeometryChecker : public QObject { Q_OBJECT public: - QgsGeometryChecker( const QList &checks, QgsFeaturePool *featurePool ); + QgsGeometryChecker( const QList &checks, const QMap &featurePools ); ~QgsGeometryChecker(); QFuture execute( int *totalSteps = nullptr ); - bool fixError( QgsGeometryCheckError *error, int method ); - QgsMapLayer *getLayer() const; + bool fixError( QgsGeometryCheckError *error, int method, bool triggerRepaint = false ); const QList getChecks() const { return mChecks; } QStringList getMessages() const { return mMessages; } - - public slots: - void setMergeAttributeIndex( int mergeAttributeIndex ) { mMergeAttributeIndex = mergeAttributeIndex; } + void setMergeAttributeIndices( const QMap &mergeAttributeIndices ) { mMergeAttributeIndices = mergeAttributeIndices; } signals: void errorAdded( QgsGeometryCheckError *error ); @@ -62,11 +59,11 @@ class QgsGeometryChecker : public QObject }; QList mChecks; - QgsFeaturePool *mFeaturePool = nullptr; + QMap mFeaturePools; QList mCheckErrors; QStringList mMessages; QMutex mErrorListMutex; - int mMergeAttributeIndex; + QMap mMergeAttributeIndices; QAtomicInt mProgressCounter; void runCheck( const QgsGeometryCheck *check ); diff --git a/src/plugins/geometry_checker/qgsgeometrycheckfactory.cpp b/src/plugins/geometry_checker/qgsgeometrycheckfactory.cpp index 36a55dc1b25..9b6a74713b6 100644 --- a/src/plugins/geometry_checker/qgsgeometrycheckfactory.cpp +++ b/src/plugins/geometry_checker/qgsgeometrycheckfactory.cpp @@ -44,19 +44,19 @@ template<> void QgsGeometryCheckFactoryT::restorePrevious ui.doubleSpinBoxAngle->setValue( QgsSettings().value( sSettingsGroup + "minimalAngle" ).toDouble() ); } -template<> bool QgsGeometryCheckFactoryT::checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, QgsWkbTypes::GeometryType geomType ) const +template<> bool QgsGeometryCheckFactoryT::checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, int /*nPoint*/, int nLineString, int nPolygon ) const { - ui.checkBoxAngle->setEnabled( geomType == QgsWkbTypes::PolygonGeometry || geomType == QgsWkbTypes::LineGeometry ); + ui.checkBoxAngle->setEnabled( nPolygon > 0 || nLineString > 0 ); return ui.checkBoxAngle->isEnabled(); } -template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsFeaturePool *featurePool, const Ui::QgsGeometryCheckerSetupTab &ui, double /*mapToLayerUnits*/ ) const +template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( const QMap &featurePools, const Ui::QgsGeometryCheckerSetupTab &ui ) const { QgsSettings().setValue( sSettingsGroup + "checkAngle", ui.checkBoxAngle->isChecked() ); QgsSettings().setValue( sSettingsGroup + "minimalAngle", ui.doubleSpinBoxAngle->value() ); if ( ui.checkBoxAngle->isEnabled() && ui.checkBoxAngle->isChecked() ) { - return new QgsGeometryAngleCheck( featurePool, ui.doubleSpinBoxAngle->value() ); + return new QgsGeometryAngleCheck( featurePools, ui.doubleSpinBoxAngle->value() ); } else { @@ -74,19 +74,19 @@ template<> void QgsGeometryCheckFactoryT::restorePrevious( ui.doubleSpinBoxArea->setValue( QgsSettings().value( sSettingsGroup + "minimalArea" ).toDouble() ); } -template<> bool QgsGeometryCheckFactoryT::checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, QgsWkbTypes::GeometryType geomType ) const +template<> bool QgsGeometryCheckFactoryT::checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, int /*nPoint*/, int /*nLineString*/, int nPolygon ) const { - ui.checkBoxArea->setEnabled( geomType == QgsWkbTypes::PolygonGeometry ); + ui.checkBoxArea->setEnabled( nPolygon > 0 ); return ui.checkBoxArea->isEnabled(); } -template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsFeaturePool *featurePool, const Ui::QgsGeometryCheckerSetupTab &ui, double mapToLayerUnits ) const +template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( const QMap &featurePools, const Ui::QgsGeometryCheckerSetupTab &ui ) const { QgsSettings().setValue( sSettingsGroup + "checkArea", ui.checkBoxArea->isChecked() ); QgsSettings().setValue( sSettingsGroup + "minimalArea", ui.doubleSpinBoxArea->value() ); if ( ui.checkBoxArea->isEnabled() && ui.checkBoxArea->isChecked() ) { - return new QgsGeometryAreaCheck( featurePool, ui.doubleSpinBoxArea->value() * mapToLayerUnits * mapToLayerUnits ); + return new QgsGeometryAreaCheck( featurePools, ui.doubleSpinBoxArea->value() ); } else { @@ -103,18 +103,18 @@ template<> void QgsGeometryCheckFactoryT::restorePrev ui.checkBoxCovered->setChecked( QgsSettings().value( sSettingsGroup + "checkCovers" ).toBool() ); } -template<> bool QgsGeometryCheckFactoryT::checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, QgsWkbTypes::GeometryType geomType ) const +template<> bool QgsGeometryCheckFactoryT::checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, int /*nPoint*/, int /*nLineString*/, int nPolygon ) const { - ui.checkBoxCovered->setEnabled( geomType == QgsWkbTypes::PolygonGeometry ); + ui.checkBoxCovered->setEnabled( nPolygon > 0 ); return ui.checkBoxCovered->isEnabled(); } -template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsFeaturePool *featurePool, const Ui::QgsGeometryCheckerSetupTab &ui, double /*mapToLayerUnits*/ ) const +template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( const QMap &featurePools, const Ui::QgsGeometryCheckerSetupTab &ui ) const { QgsSettings().setValue( sSettingsGroup + "checkCovers", ui.checkBoxCovered->isChecked() ); if ( ui.checkBoxCovered->isEnabled() && ui.checkBoxCovered->isChecked() ) { - return new QgsGeometryContainedCheck( featurePool ); + return new QgsGeometryContainedCheck( featurePools ); } else { @@ -131,18 +131,18 @@ template<> void QgsGeometryCheckFactoryT::res ui.checkBoxDegeneratePolygon->setChecked( QgsSettings().value( sSettingsGroup + "checkDegeneratePolygon" ).toBool() ); } -template<> bool QgsGeometryCheckFactoryT::checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, QgsWkbTypes::GeometryType geomType ) const +template<> bool QgsGeometryCheckFactoryT::checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, int /*nPoint*/, int /*nLineString*/, int nPolygon ) const { - ui.checkBoxDegeneratePolygon->setEnabled( geomType == QgsWkbTypes::PolygonGeometry ); + ui.checkBoxDegeneratePolygon->setEnabled( nPolygon > 0 ); return ui.checkBoxDegeneratePolygon->isEnabled(); } -template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsFeaturePool *featurePool, const Ui::QgsGeometryCheckerSetupTab &ui, double /*mapToLayerUnits*/ ) const +template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( const QMap &featurePools, const Ui::QgsGeometryCheckerSetupTab &ui ) const { QgsSettings().setValue( sSettingsGroup + "checkDegeneratePolygon", ui.checkBoxDegeneratePolygon->isChecked() ); if ( ui.checkBoxDegeneratePolygon->isEnabled() && ui.checkBoxDegeneratePolygon->isChecked() ) { - return new QgsGeometryDegeneratePolygonCheck( featurePool ); + return new QgsGeometryDegeneratePolygonCheck( featurePools ); } else { @@ -159,18 +159,18 @@ template<> void QgsGeometryCheckFactoryT::restorePrev ui.checkBoxDuplicates->setChecked( QgsSettings().value( sSettingsGroup + "checkDuplicates" ).toBool() ); } -template<> bool QgsGeometryCheckFactoryT::checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, QgsWkbTypes::GeometryType geomType ) const +template<> bool QgsGeometryCheckFactoryT::checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, int /*nPoint*/, int /*nLineString*/, int nPolygon ) const { - ui.checkBoxDuplicates->setEnabled( geomType == QgsWkbTypes::PolygonGeometry ); + ui.checkBoxDuplicates->setEnabled( nPolygon > 0 ); return ui.checkBoxDuplicates->isEnabled(); } -template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsFeaturePool *featurePool, const Ui::QgsGeometryCheckerSetupTab &ui, double /*mapToLayerUnits*/ ) const +template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( const QMap &featurePools, const Ui::QgsGeometryCheckerSetupTab &ui ) const { QgsSettings().setValue( sSettingsGroup + "checkDuplicates", ui.checkBoxDuplicates->isChecked() ); if ( ui.checkBoxDuplicates->isEnabled() && ui.checkBoxDuplicates->isChecked() ) { - return new QgsGeometryDuplicateCheck( featurePool ); + return new QgsGeometryDuplicateCheck( featurePools ); } else { @@ -187,18 +187,18 @@ template<> void QgsGeometryCheckFactoryT::restor ui.checkBoxDuplicateNodes->setChecked( QgsSettings().value( sSettingsGroup + "checkDuplicateNodes" ).toBool() ); } -template<> bool QgsGeometryCheckFactoryT::checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, QgsWkbTypes::GeometryType geomType ) const +template<> bool QgsGeometryCheckFactoryT::checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, int /*nPoint*/, int nLineString, int nPolygon ) const { - ui.checkBoxDuplicateNodes->setEnabled( geomType == QgsWkbTypes::PolygonGeometry || geomType == QgsWkbTypes::LineGeometry ); + ui.checkBoxDuplicateNodes->setEnabled( nPolygon > 0 || nLineString > 0 ); return ui.checkBoxDuplicateNodes->isEnabled(); } -template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsFeaturePool *featurePool, const Ui::QgsGeometryCheckerSetupTab &ui, double /*mapToLayerUnits*/ ) const +template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( const QMap &featurePools, const Ui::QgsGeometryCheckerSetupTab &ui ) const { QgsSettings().setValue( sSettingsGroup + "checkDuplicateNodes", ui.checkBoxDuplicateNodes->isChecked() ); if ( ui.checkBoxDuplicateNodes->isEnabled() && ui.checkBoxDuplicateNodes->isChecked() ) { - return new QgsGeometryDuplicateNodesCheck( featurePool ); + return new QgsGeometryDuplicateNodesCheck( featurePools ); } else { @@ -216,20 +216,20 @@ template<> void QgsGeometryCheckFactoryT::restorePrevious( ui.doubleSpinBoxGapArea->setValue( QgsSettings().value( sSettingsGroup + "maxGapArea" ).toDouble() ); } -template<> bool QgsGeometryCheckFactoryT::checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, QgsWkbTypes::GeometryType geomType ) const +template<> bool QgsGeometryCheckFactoryT::checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, int /*nPoint*/, int /*nLineString*/, int nPolygon ) const { - ui.checkBoxGaps->setEnabled( geomType == QgsWkbTypes::PolygonGeometry ); + ui.checkBoxGaps->setEnabled( nPolygon > 0 ); ui.doubleSpinBoxGapArea->setEnabled( ui.checkBoxGaps->isEnabled() ); return ui.checkBoxGaps->isEnabled(); } -template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsFeaturePool *featurePool, const Ui::QgsGeometryCheckerSetupTab &ui, double mapToLayerUnits ) const +template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( const QMap &featurePools, const Ui::QgsGeometryCheckerSetupTab &ui ) const { QgsSettings().setValue( sSettingsGroup + "checkGaps", ui.checkBoxGaps->isChecked() ); QgsSettings().setValue( sSettingsGroup + "maxGapArea", ui.doubleSpinBoxGapArea->value() ); if ( ui.checkBoxGaps->isEnabled() && ui.checkBoxGaps->isChecked() ) { - return new QgsGeometryGapCheck( featurePool, ui.doubleSpinBoxGapArea->value() * mapToLayerUnits * mapToLayerUnits ); + return new QgsGeometryGapCheck( featurePools, ui.doubleSpinBoxGapArea->value() ); } else { @@ -246,18 +246,18 @@ template<> void QgsGeometryCheckFactoryT::restorePrevious( ui.checkBoxNoHoles->setChecked( QgsSettings().value( sSettingsGroup + "checkHoles" ).toBool() ); } -template<> bool QgsGeometryCheckFactoryT::checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, QgsWkbTypes::GeometryType geomType ) const +template<> bool QgsGeometryCheckFactoryT::checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, int /*nPoint*/, int /*nLineString*/, int nPolygon ) const { - ui.checkBoxNoHoles->setEnabled( geomType == QgsWkbTypes::PolygonGeometry ); + ui.checkBoxNoHoles->setEnabled( nPolygon > 0 ); return ui.checkBoxNoHoles->isEnabled(); } -template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsFeaturePool *featurePool, const Ui::QgsGeometryCheckerSetupTab &ui, double /*mapToLayerUnits*/ ) const +template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( const QMap &featurePools, const Ui::QgsGeometryCheckerSetupTab &ui ) const { QgsSettings().setValue( sSettingsGroup + "checkHoles", ui.checkBoxNoHoles->isChecked() ); if ( ui.checkBoxNoHoles->isEnabled() && ui.checkBoxNoHoles->isChecked() ) { - return new QgsGeometryHoleCheck( featurePool ); + return new QgsGeometryHoleCheck( featurePools ); } else { @@ -274,17 +274,17 @@ template<> void QgsGeometryCheckFactoryT::restorePrev ui.checkBoxMultipart->setChecked( QgsSettings().value( sSettingsGroup + "checkMultipart" ).toBool() ); } -template<> bool QgsGeometryCheckFactoryT::checkApplicability( Ui::QgsGeometryCheckerSetupTab & /*ui*/, QgsWkbTypes::GeometryType /*geomType*/ ) const +template<> bool QgsGeometryCheckFactoryT::checkApplicability( Ui::QgsGeometryCheckerSetupTab & /*ui*/, int /*nPoint*/, int /*nLineString*/, int /*nPolygon*/ ) const { return true; } -template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsFeaturePool *featurePool, const Ui::QgsGeometryCheckerSetupTab &ui, double /*mapToLayerUnits*/ ) const +template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( const QMap &featurePools, const Ui::QgsGeometryCheckerSetupTab &ui ) const { QgsSettings().setValue( sSettingsGroup + "checkMultipart", ui.checkBoxMultipart->isChecked() ); if ( ui.checkBoxMultipart->isEnabled() && ui.checkBoxMultipart->isChecked() ) { - return new QgsGeometryMultipartCheck( featurePool ); + return new QgsGeometryMultipartCheck( featurePools ); } else { @@ -302,20 +302,20 @@ template<> void QgsGeometryCheckFactoryT::restorePrevio ui.doubleSpinBoxOverlapArea->setValue( QgsSettings().value( sSettingsGroup + "maxOverlapArea" ).toDouble() ); } -template<> bool QgsGeometryCheckFactoryT::checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, QgsWkbTypes::GeometryType geomType ) const +template<> bool QgsGeometryCheckFactoryT::checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, int /*nPoint*/, int /*nLineString*/, int nPolygon ) const { - ui.checkBoxOverlaps->setEnabled( geomType == QgsWkbTypes::PolygonGeometry ); + ui.checkBoxOverlaps->setEnabled( nPolygon > 0 ); ui.doubleSpinBoxOverlapArea->setEnabled( ui.checkBoxOverlaps->isEnabled() ); return ui.checkBoxOverlaps->isEnabled(); } -template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsFeaturePool *featurePool, const Ui::QgsGeometryCheckerSetupTab &ui, double mapToLayerUnits ) const +template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( const QMap &featurePools, const Ui::QgsGeometryCheckerSetupTab &ui ) const { QgsSettings().setValue( sSettingsGroup + "checkOverlaps", ui.checkBoxOverlaps->isChecked() ); QgsSettings().setValue( sSettingsGroup + "maxOverlapArea", ui.doubleSpinBoxOverlapArea->value() ); if ( ui.checkBoxOverlaps->isEnabled() && ui.checkBoxOverlaps->isChecked() ) { - return new QgsGeometryOverlapCheck( featurePool, ui.doubleSpinBoxOverlapArea->value() * mapToLayerUnits * mapToLayerUnits ); + return new QgsGeometryOverlapCheck( featurePools, ui.doubleSpinBoxOverlapArea->value() ); } else { @@ -333,20 +333,20 @@ template<> void QgsGeometryCheckFactoryT::restore ui.doubleSpinBoxSegmentLength->setValue( QgsSettings().value( sSettingsGroup + "minSegmentLength" ).toDouble() ); } -template<> bool QgsGeometryCheckFactoryT::checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, QgsWkbTypes::GeometryType geomType ) const +template<> bool QgsGeometryCheckFactoryT::checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, int /*nPoint*/, int nLineString, int nPolygon ) const { - ui.checkBoxSegmentLength->setEnabled( geomType == QgsWkbTypes::PolygonGeometry || geomType == QgsWkbTypes::LineGeometry ); + ui.checkBoxSegmentLength->setEnabled( nPolygon > 0 || nLineString > 0 ); ui.doubleSpinBoxSegmentLength->setEnabled( ui.checkBoxSegmentLength->isEnabled() ); return ui.checkBoxSegmentLength->isEnabled(); } -template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsFeaturePool *featurePool, const Ui::QgsGeometryCheckerSetupTab &ui, double mapToLayerUnits ) const +template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( const QMap &featurePools, const Ui::QgsGeometryCheckerSetupTab &ui ) const { QgsSettings().setValue( sSettingsGroup + "checkSegmentLength", ui.checkBoxSegmentLength->isChecked() ); QgsSettings().setValue( sSettingsGroup + "minSegmentLength", ui.doubleSpinBoxSegmentLength->value() ); if ( ui.checkBoxSegmentLength->isEnabled() && ui.checkBoxSegmentLength->isChecked() ) { - return new QgsGeometrySegmentLengthCheck( featurePool, ui.doubleSpinBoxSegmentLength->value() * mapToLayerUnits ); + return new QgsGeometrySegmentLengthCheck( featurePools, ui.doubleSpinBoxSegmentLength->value() ); } else { @@ -363,18 +363,18 @@ template<> void QgsGeometryCheckFactoryT::restorePr ui.checkBoxSelfContacts->setChecked( QgsSettings().value( sSettingsGroup + "checkSelfContacts" ).toBool() ); } -template<> bool QgsGeometryCheckFactoryT::checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, QgsWkbTypes::GeometryType geomType ) const +template<> bool QgsGeometryCheckFactoryT::checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, int /*nPoint*/, int nLineString, int nPolygon ) const { - ui.checkBoxSelfContacts->setEnabled( geomType == QgsWkbTypes::PolygonGeometry || geomType == QgsWkbTypes::LineGeometry ); + ui.checkBoxSelfContacts->setEnabled( nPolygon > 0 || nLineString > 0 ); return ui.checkBoxSelfContacts->isEnabled(); } -template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsFeaturePool *featurePool, const Ui::QgsGeometryCheckerSetupTab &ui, double /*mapToLayerUnits*/ ) const +template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( const QMap &featurePools, const Ui::QgsGeometryCheckerSetupTab &ui ) const { QgsSettings().setValue( sSettingsGroup + "checkSelfContacts", ui.checkBoxSelfContacts->isChecked() ); if ( ui.checkBoxSelfContacts->isEnabled() && ui.checkBoxSelfContacts->isChecked() ) { - return new QgsGeometrySelfContactCheck( featurePool ); + return new QgsGeometrySelfContactCheck( featurePools ); } else { @@ -391,18 +391,18 @@ template<> void QgsGeometryCheckFactoryT::rest ui.checkBoxSelfIntersections->setChecked( QgsSettings().value( sSettingsGroup + "checkSelfIntersections" ).toBool() ); } -template<> bool QgsGeometryCheckFactoryT::checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, QgsWkbTypes::GeometryType geomType ) const +template<> bool QgsGeometryCheckFactoryT::checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, int /*nPoint*/, int nLineString, int nPolygon ) const { - ui.checkBoxSelfIntersections->setEnabled( geomType == QgsWkbTypes::PolygonGeometry || geomType == QgsWkbTypes::LineGeometry ); + ui.checkBoxSelfIntersections->setEnabled( nPolygon > 0 || nLineString > 0 ); return ui.checkBoxSelfIntersections->isEnabled(); } -template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsFeaturePool *featurePool, const Ui::QgsGeometryCheckerSetupTab &ui, double /*mapToLayerUnits*/ ) const +template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( const QMap &featurePools, const Ui::QgsGeometryCheckerSetupTab &ui ) const { QgsSettings().setValue( sSettingsGroup + "checkSelfIntersections", ui.checkBoxSelfIntersections->isChecked() ); if ( ui.checkBoxSelfIntersections->isEnabled() && ui.checkBoxSelfIntersections->isChecked() ) { - return new QgsGeometrySelfIntersectionCheck( featurePool ); + return new QgsGeometrySelfIntersectionCheck( featurePools ); } else { @@ -423,13 +423,13 @@ template<> void QgsGeometryCheckFactoryT::restore ui.checkBoxSliverPolygons->setChecked( QgsSettings().value( sSettingsGroup + "checkSliverPolygons" ).toBool() ); } -template<> bool QgsGeometryCheckFactoryT::checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, QgsWkbTypes::GeometryType geomType ) const +template<> bool QgsGeometryCheckFactoryT::checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, int /*nPoint*/, int /*nLineString*/, int nPolygon ) const { - ui.checkBoxSliverPolygons->setEnabled( geomType == QgsWkbTypes::PolygonGeometry ); + ui.checkBoxSliverPolygons->setEnabled( nPolygon > 0 ); return ui.checkBoxSliverPolygons->isEnabled(); } -template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsFeaturePool *featurePool, const Ui::QgsGeometryCheckerSetupTab &ui, double mapToLayerUnits ) const +template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( const QMap &featurePools, const Ui::QgsGeometryCheckerSetupTab &ui ) const { double threshold = ui.doubleSpinBoxSliverThinness->value(); double maxArea = ui.checkBoxSliverArea->isChecked() ? ui.doubleSpinBoxSliverArea->value() : 0.; @@ -439,7 +439,7 @@ template<> QgsGeometryCheck *QgsGeometryCheckFactoryTisChecked() ); if ( ui.checkBoxSliverPolygons->isEnabled() && ui.checkBoxSliverPolygons->isChecked() ) { - return new QgsGeometrySliverPolygonCheck( featurePool, threshold, maxArea * mapToLayerUnits * mapToLayerUnits ); + return new QgsGeometrySliverPolygonCheck( featurePools, threshold, maxArea ); } else { @@ -461,12 +461,12 @@ template<> void QgsGeometryCheckFactoryT::restorePrevious( ui.checkBoxMultipolygon->setChecked( QgsSettings().value( sSettingsGroup + "checkTypeMultipolygon" ).toBool() ); } -template<> bool QgsGeometryCheckFactoryT::checkApplicability( Ui::QgsGeometryCheckerSetupTab & /*ui*/, QgsWkbTypes::GeometryType /*geomType*/ ) const +template<> bool QgsGeometryCheckFactoryT::checkApplicability( Ui::QgsGeometryCheckerSetupTab & /*ui*/, int /*nPoint*/, int /*nLineString*/, int /*nPolygon*/ ) const { return true; } -template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( QgsFeaturePool *featurePool, const Ui::QgsGeometryCheckerSetupTab &ui, double /*mapToLayerUnits*/ ) const +template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::createInstance( const QMap &featurePools, const Ui::QgsGeometryCheckerSetupTab &ui ) const { QgsSettings().setValue( sSettingsGroup + "checkTypePoint", ui.checkBoxPoint->isChecked() ); QgsSettings().setValue( sSettingsGroup + "checkTypeMultipoint", ui.checkBoxMultipoint->isChecked() ); @@ -502,7 +502,7 @@ template<> QgsGeometryCheck *QgsGeometryCheckFactoryT::cre } if ( allowedTypes != 0 ) { - return new QgsGeometryTypeCheck( featurePool, allowedTypes ); + return new QgsGeometryTypeCheck( featurePools, allowedTypes ); } else { diff --git a/src/plugins/geometry_checker/qgsgeometrycheckfactory.h b/src/plugins/geometry_checker/qgsgeometrycheckfactory.h index 7c03c9f56c9..e3e02048e1f 100644 --- a/src/plugins/geometry_checker/qgsgeometrycheckfactory.h +++ b/src/plugins/geometry_checker/qgsgeometrycheckfactory.h @@ -20,15 +20,14 @@ class QgsFeaturePool; class QgsGeometryCheck; -class QgsMapSettings; class QgsGeometryCheckFactory { public: virtual ~QgsGeometryCheckFactory() = default; virtual void restorePrevious( Ui::QgsGeometryCheckerSetupTab & /*ui*/ ) const = 0; - virtual bool checkApplicability( Ui::QgsGeometryCheckerSetupTab & /*ui*/, QgsWkbTypes::GeometryType /*geomType*/ ) const = 0; - virtual QgsGeometryCheck *createInstance( QgsFeaturePool *featurePool, const Ui::QgsGeometryCheckerSetupTab &ui, double mapToLayerUnits ) const = 0; + virtual bool checkApplicability( Ui::QgsGeometryCheckerSetupTab & /*ui*/, int /*nPoint*/, int /*nLineString*/, int /*nPolygon*/ ) const = 0; + virtual QgsGeometryCheck *createInstance( const QMap &featurePools, const Ui::QgsGeometryCheckerSetupTab &ui ) const = 0; protected: static QString sSettingsGroup; @@ -38,8 +37,8 @@ template class QgsGeometryCheckFactoryT : public QgsGeometryCheckFactory { void restorePrevious( Ui::QgsGeometryCheckerSetupTab & /*ui*/ ) const override; - bool checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, QgsWkbTypes::GeometryType geomType ) const override; - QgsGeometryCheck *createInstance( QgsFeaturePool *featurePool, const Ui::QgsGeometryCheckerSetupTab &ui, double mapToLayerUnits ) const override; + bool checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, int nPoint, int nLineString, int nPolygon ) const override; + QgsGeometryCheck *createInstance( const QMap &featurePools, const Ui::QgsGeometryCheckerSetupTab &ui ) const override; }; class QgsGeometryCheckFactoryRegistry diff --git a/src/plugins/geometry_checker/ui/qgsgeometrycheckerdialog.cpp b/src/plugins/geometry_checker/ui/qgsgeometrycheckerdialog.cpp index 37be5c7ede5..8b91a999a0d 100644 --- a/src/plugins/geometry_checker/ui/qgsgeometrycheckerdialog.cpp +++ b/src/plugins/geometry_checker/ui/qgsgeometrycheckerdialog.cpp @@ -57,11 +57,11 @@ QgsGeometryCheckerDialog::~QgsGeometryCheckerDialog() s.setValue( QStringLiteral( "/Plugin-GeometryChecker/Window/geometry" ), saveGeometry() ); } -void QgsGeometryCheckerDialog::onCheckerStarted( QgsGeometryChecker *checker, QgsFeaturePool *featurePool ) +void QgsGeometryCheckerDialog::onCheckerStarted( QgsGeometryChecker *checker, QMap featurePools ) { delete mTabWidget->widget( 1 ); mTabWidget->removeTab( 1 ); - mTabWidget->addTab( new QgsGeometryCheckerResultTab( mIface, checker, featurePool, mTabWidget ), tr( "Result" ) ); + mTabWidget->addTab( new QgsGeometryCheckerResultTab( mIface, checker, featurePools, mTabWidget ), tr( "Result" ) ); mTabWidget->setTabEnabled( 1, false ); mButtonBox->button( QDialogButtonBox::Close )->setEnabled( false ); } diff --git a/src/plugins/geometry_checker/ui/qgsgeometrycheckerdialog.h b/src/plugins/geometry_checker/ui/qgsgeometrycheckerdialog.h index b39b87bac52..d97cc3becf0 100644 --- a/src/plugins/geometry_checker/ui/qgsgeometrycheckerdialog.h +++ b/src/plugins/geometry_checker/ui/qgsgeometrycheckerdialog.h @@ -21,6 +21,7 @@ #include #include +#include #include class QgisInterface; @@ -44,7 +45,7 @@ class QgsGeometryCheckerDialog : public QDialog void closeEvent( QCloseEvent *ev ) override; private slots: - void onCheckerStarted( QgsGeometryChecker *checker, QgsFeaturePool *featurePool ); + void onCheckerStarted( QgsGeometryChecker *checker, QMap featurePools ); void onCheckerFinished( bool successful ); void showHelp(); }; diff --git a/src/plugins/geometry_checker/ui/qgsgeometrycheckerfixsummarydialog.cpp b/src/plugins/geometry_checker/ui/qgsgeometrycheckerfixsummarydialog.cpp index 3ca612b9d34..b5c4698ee45 100644 --- a/src/plugins/geometry_checker/ui/qgsgeometrycheckerfixsummarydialog.cpp +++ b/src/plugins/geometry_checker/ui/qgsgeometrycheckerfixsummarydialog.cpp @@ -16,13 +16,13 @@ #include "qgsgeometrycheckerfixsummarydialog.h" #include "../checks/qgsgeometrycheck.h" +#include "../utils/qgsfeaturepool.h" #include "qgisinterface.h" #include "qgsmapcanvas.h" -QgsGeometryCheckerFixSummaryDialog::QgsGeometryCheckerFixSummaryDialog( QgisInterface *iface, QgsVectorLayer *layer, const Statistics &stats, const QStringList &messages, QWidget *parent ) +QgsGeometryCheckerFixSummaryDialog::QgsGeometryCheckerFixSummaryDialog( const QMap &featurePools, const Statistics &stats, const QStringList &messages, QWidget *parent ) : QDialog( parent ) - , mIface( iface ) - , mLayer( layer ) + , mFeaturePools( featurePools ) { ui.setupUi( this ); @@ -31,19 +31,19 @@ QgsGeometryCheckerFixSummaryDialog::QgsGeometryCheckerFixSummaryDialog( QgisInte ui.groupBoxNotFixed->setTitle( tr( "%1 errors were not fixed" ).arg( stats.failedErrors.count() ) ); ui.groupBoxObsoleteErrors->setTitle( tr( "%1 errors are obsolete" ).arg( stats.obsoleteErrors.count() ) ); - Q_FOREACH ( QgsGeometryCheckError *error, stats.fixedErrors ) + for ( QgsGeometryCheckError *error : stats.fixedErrors ) { addError( ui.tableWidgetFixedErrors, error ); } - Q_FOREACH ( QgsGeometryCheckError *error, stats.newErrors ) + for ( QgsGeometryCheckError *error : stats.newErrors ) { addError( ui.tableWidgetNewErrors, error ); } - Q_FOREACH ( QgsGeometryCheckError *error, stats.failedErrors ) + for ( QgsGeometryCheckError *error : stats.failedErrors ) { addError( ui.tableWidgetNotFixed, error ); } - Q_FOREACH ( QgsGeometryCheckError *error, stats.obsoleteErrors ) + for ( QgsGeometryCheckError *error : stats.obsoleteErrors ) { addError( ui.tableWidgetObsoleteErrors, error ); } @@ -66,7 +66,7 @@ void QgsGeometryCheckerFixSummaryDialog::addError( QTableWidget *table, QgsGeome { int prec = 7 - std::floor( std::max( 0., std::log10( std::max( error->location().x(), error->location().y() ) ) ) ); QString posStr = QStringLiteral( "%1, %2" ).arg( error->location().x(), 0, 'f', prec ).arg( error->location().y(), 0, 'f', prec ); - double layerToMap = mIface->mapCanvas()->mapSettings().layerToMapUnits( mLayer ); + double layerToMap = 1. / mFeaturePools[error->layerId()]->getMapToLayerUnits(); QVariant value; if ( error->valueType() == QgsGeometryCheckError::ValueLength ) { @@ -116,7 +116,7 @@ void QgsGeometryCheckerFixSummaryDialog::onTableSelectionChanged( const QItemSel { const QAbstractItemModel *model = qobject_cast( QObject::sender() )->model(); - Q_FOREACH ( QTableWidget *table, QList() << ui.tableWidgetFixedErrors << ui.tableWidgetNewErrors << ui.tableWidgetNotFixed << ui.tableWidgetObsoleteErrors ) + for ( QTableWidget *table : QList() << ui.tableWidgetFixedErrors << ui.tableWidgetNewErrors << ui.tableWidgetNotFixed << ui.tableWidgetObsoleteErrors ) if ( table->model() != model ) { table->selectionModel()->blockSignals( true ); diff --git a/src/plugins/geometry_checker/ui/qgsgeometrycheckerfixsummarydialog.h b/src/plugins/geometry_checker/ui/qgsgeometrycheckerfixsummarydialog.h index 92efb60ff35..3561766113a 100644 --- a/src/plugins/geometry_checker/ui/qgsgeometrycheckerfixsummarydialog.h +++ b/src/plugins/geometry_checker/ui/qgsgeometrycheckerfixsummarydialog.h @@ -23,7 +23,7 @@ class QgisInterface; class QgsGeometryCheckError; -class QgsVectorLayer; +class QgsFeaturePool; class QgsGeometryCheckerFixSummaryDialog : public QDialog { @@ -41,15 +41,14 @@ class QgsGeometryCheckerFixSummaryDialog : public QDialog } }; - QgsGeometryCheckerFixSummaryDialog( QgisInterface *iface, QgsVectorLayer *layer, const Statistics &stats, const QStringList &messages, QWidget *parent = nullptr ); + QgsGeometryCheckerFixSummaryDialog( const QMap &featurePools, const Statistics &stats, const QStringList &messages, QWidget *parent = nullptr ); signals: void errorSelected( QgsGeometryCheckError *error ); private: Ui::QgsGeometryCheckerFixSummaryDialog ui; - QgisInterface *mIface = nullptr; - QgsVectorLayer *mLayer = nullptr; + QMap mFeaturePools; void addError( QTableWidget *table, QgsGeometryCheckError *error ); void setupTable( QTableWidget *table ); diff --git a/src/plugins/geometry_checker/ui/qgsgeometrycheckerresulttab.cpp b/src/plugins/geometry_checker/ui/qgsgeometrycheckerresulttab.cpp index 08ff218a0b1..60d6e250b48 100644 --- a/src/plugins/geometry_checker/ui/qgsgeometrycheckerresulttab.cpp +++ b/src/plugins/geometry_checker/ui/qgsgeometrycheckerresulttab.cpp @@ -14,6 +14,7 @@ * * ***************************************************************************/ +#include #include #include #include @@ -27,6 +28,7 @@ #include "../checks/qgsgeometrycheck.h" #include "../utils/qgsfeaturepool.h" +#include "qgscrscache.h" #include "qgsgeometry.h" #include "qgisinterface.h" #include "qgsmapcanvas.h" @@ -41,27 +43,35 @@ QString QgsGeometryCheckerResultTab::sSettingsGroup = QStringLiteral( "/geometry_checker/default_fix_methods/" ); -QgsGeometryCheckerResultTab::QgsGeometryCheckerResultTab( QgisInterface *iface, QgsGeometryChecker *checker, QgsFeaturePool *featurePool, QTabWidget *tabWidget, QWidget *parent ) +QgsGeometryCheckerResultTab::QgsGeometryCheckerResultTab( QgisInterface *iface, QgsGeometryChecker *checker, const QMap &featurePools, QTabWidget *tabWidget, QWidget *parent ) : QWidget( parent ) , mTabWidget( tabWidget ) , mIface( iface ) , mChecker( checker ) - , mFeaturePool( featurePool ) + , mFeaturePools( featurePools ) { ui.setupUi( this ); mErrorCount = 0; mFixedCount = 0; mCloseable = true; - mAttribTableDialog = nullptr; - for ( int i = 0, n = mFeaturePool->getLayer()->fields().count(); i < n; ++i ) + for ( const QString &layerId : mFeaturePools.keys() ) { - ui.comboBoxMergeAttribute->addItem( mFeaturePool->getLayer()->fields().at( i ).name() ); + QTreeWidgetItem *item = new QTreeWidgetItem( ui.treeWidgetMergeAttribute, QStringList() << layerId << "" ); + QComboBox *attribCombo = new QComboBox(); + QgsVectorLayer *layer = mFeaturePools[layerId]->getLayer(); + for ( int i = 0, n = layer->fields().count(); i < n; ++i ) + { + attribCombo->addItem( layer->fields().at( i ).name() ); + } + attribCombo->setCurrentIndex( 0 ); + connect( attribCombo, SIGNAL( currentIndexChanged( int ) ), this, SLOT( updateMergeAttributeIndices() ) ); + ui.treeWidgetMergeAttribute->setItemWidget( item, 1, attribCombo ); } + updateMergeAttributeIndices(); connect( checker, &QgsGeometryChecker::errorAdded, this, &QgsGeometryCheckerResultTab::addError ); connect( checker, &QgsGeometryChecker::errorUpdated, this, &QgsGeometryCheckerResultTab::updateError ); - connect( ui.comboBoxMergeAttribute, static_cast( &QComboBox::currentIndexChanged ), checker, &QgsGeometryChecker::setMergeAttributeIndex ); connect( ui.tableWidgetErrors->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsGeometryCheckerResultTab::onSelectionChanged ); connect( ui.buttonGroupSelectAction, static_cast( &QButtonGroup::buttonClicked ), this, &QgsGeometryCheckerResultTab::highlightErrors ); connect( ui.pushButtonOpenAttributeTable, &QAbstractButton::clicked, this, &QgsGeometryCheckerResultTab::openAttributeTable ); @@ -72,7 +82,16 @@ QgsGeometryCheckerResultTab::QgsGeometryCheckerResultTab( QgisInterface *iface, connect( QgsProject::instance(), static_cast( &QgsProject::layersWillBeRemoved ), this, &QgsGeometryCheckerResultTab::checkRemovedLayer ); connect( ui.pushButtonExport, &QAbstractButton::clicked, this, &QgsGeometryCheckerResultTab::exportErrors ); - if ( ( mFeaturePool->getLayer()->dataProvider()->capabilities() & QgsVectorDataProvider::ChangeGeometries ) == 0 ) + bool allLayersEditable = true; + for ( const QgsFeaturePool *featurePool : mFeaturePools.values() ) + { + if ( ( featurePool->getLayer()->dataProvider()->capabilities() & QgsVectorDataProvider::ChangeGeometries ) == 0 ) + { + allLayersEditable = false; + break; + } + } + if ( !allLayersEditable ) { ui.pushButtonFixWithDefault->setEnabled( false ); ui.pushButtonFixWithPrompt->setEnabled( false ); @@ -87,10 +106,13 @@ QgsGeometryCheckerResultTab::QgsGeometryCheckerResultTab( QgisInterface *iface, QgsGeometryCheckerResultTab::~QgsGeometryCheckerResultTab() { - if ( mFeaturePool->getLayer() ) - mFeaturePool->getLayer()->setReadOnly( false ); + for ( const QgsFeaturePool *featurePool : mFeaturePools.values() ) + { + if ( featurePool->getLayer() ) + featurePool->getLayer()->setReadOnly( false ); + delete featurePool; + } delete mChecker; - delete mFeaturePool; qDeleteAll( mCurrentRubberBands ); } @@ -121,7 +143,7 @@ void QgsGeometryCheckerResultTab::addError( QgsGeometryCheckError *error ) int row = ui.tableWidgetErrors->rowCount(); int prec = 7 - std::floor( std::max( 0., std::log10( std::max( error->location().x(), error->location().y() ) ) ) ); QString posStr = QStringLiteral( "%1, %2" ).arg( error->location().x(), 0, 'f', prec ).arg( error->location().y(), 0, 'f', prec ); - double layerToMap = mIface->mapCanvas()->mapSettings().layerToMapUnits( mFeaturePool->getLayer() ); + double layerToMap = 1. / mFeaturePools[error->layerId()]->getMapToLayerUnits(); QVariant value; if ( error->valueType() == QgsGeometryCheckError::ValueLength ) { @@ -169,7 +191,7 @@ void QgsGeometryCheckerResultTab::updateError( QgsGeometryCheckError *error, boo int row = mErrorMap.value( error ).row(); int prec = 7 - std::floor( std::max( 0., std::log10( std::max( error->location().x(), error->location().y() ) ) ) ); QString posStr = QStringLiteral( "%1, %2" ).arg( error->location().x(), 0, 'f', prec ).arg( error->location().y(), 0, 'f', prec ); - double layerToMap = mIface->mapCanvas()->mapSettings().layerToMapUnits( mFeaturePool->getLayer() ); + double layerToMap = 1. / mFeaturePools[error->layerId()]->getMapToLayerUnits(); QVariant value; if ( error->valueType() == QgsGeometryCheckError::ValueLength ) { @@ -221,7 +243,7 @@ void QgsGeometryCheckerResultTab::updateError( QgsGeometryCheckError *error, boo void QgsGeometryCheckerResultTab::exportErrors() { QString initialdir; - QDir dir = QFileInfo( mFeaturePool->getLayer()->dataProvider()->dataSourceUri() ).dir(); + QDir dir = QFileInfo( mFeaturePools.first()->getLayer()->dataProvider()->dataSourceUri() ).dir(); if ( dir.exists() ) { initialdir = dir.absolutePath(); @@ -241,6 +263,7 @@ void QgsGeometryCheckerResultTab::exportErrors() bool QgsGeometryCheckerResultTab::exportErrorsDo( const QString &file ) { QList< QPair > attributes; + attributes.append( qMakePair( QStringLiteral( "Layer" ), QStringLiteral( "String;30;" ) ) ); attributes.append( qMakePair( QStringLiteral( "FeatureID" ), QStringLiteral( "String;10;" ) ) ); attributes.append( qMakePair( QStringLiteral( "ErrorDesc" ), QStringLiteral( "String;80;" ) ) ); @@ -255,7 +278,7 @@ bool QgsGeometryCheckerResultTab::exportErrorsDo( const QString &file ) { return false; } - if ( !createEmptyDataSource( file, QStringLiteral( "ESRI Shapefile" ), mFeaturePool->getLayer()->dataProvider()->encoding(), QgsWkbTypes::Point, attributes, mFeaturePool->getLayer()->crs() ) ) + if ( !createEmptyDataSource( file, QStringLiteral( "ESRI Shapefile" ), "UTF-8", QgsWkbTypes::Point, attributes, mIface->mapCanvas()->mapSettings().destinationCrs() ) ) { return false; } @@ -266,22 +289,26 @@ bool QgsGeometryCheckerResultTab::exportErrorsDo( const QString &file ) return false; } + int fieldLayer = layer->fields().lookupField( QStringLiteral( "Layer" ) ); int fieldFeatureId = layer->fields().lookupField( QStringLiteral( "FeatureID" ) ); int fieldErrDesc = layer->fields().lookupField( QStringLiteral( "ErrorDesc" ) ); for ( int row = 0, nRows = ui.tableWidgetErrors->rowCount(); row < nRows; ++row ) { QgsGeometryCheckError *error = ui.tableWidgetErrors->item( row, 0 )->data( Qt::UserRole ).value(); - + QgsVectorLayer *srcLayer = mFeaturePools[error->layerId()]->getLayer(); QgsFeature f( layer->fields() ); + f.setAttribute( fieldLayer, srcLayer->name() ); f.setAttribute( fieldFeatureId, error->featureId() ); f.setAttribute( fieldErrDesc, error->description() ); - f.setGeometry( QgsGeometry( error->location().clone() ) ); + QgsGeometry geom( error->location().clone() ); + geom.transform( QgsCoordinateTransformCache::instance()->transform( srcLayer->crs().authid(), layer->crs().authid() ) ); + f.setGeometry( geom ); layer->dataProvider()->addFeatures( QgsFeatureList() << f ); } // Remove existing layer with same uri QStringList toRemove; - Q_FOREACH ( QgsMapLayer *maplayer, QgsProject::instance()->mapLayers() ) + for ( QgsMapLayer *maplayer : QgsProject::instance()->mapLayers() ) { if ( dynamic_cast( maplayer ) && static_cast( maplayer )->dataProvider()->dataSourceUri() == layer->dataProvider()->dataSourceUri() ) @@ -326,16 +353,17 @@ void QgsGeometryCheckerResultTab::highlightErrors( bool current ) { items.append( ui.tableWidgetErrors->selectedItems() ); } - Q_FOREACH ( QTableWidgetItem *item, items ) + for ( QTableWidgetItem *item : items ) { QgsGeometryCheckError *error = ui.tableWidgetErrors->item( item->row(), 0 )->data( Qt::UserRole ).value(); + QgsVectorLayer *layer = mFeaturePools[error->layerId()]->getLayer(); QgsAbstractGeometry *geometry = error->geometry(); if ( ui.checkBoxHighlight->isChecked() && geometry ) { QgsRubberBand *featureRubberBand = new QgsRubberBand( mIface->mapCanvas() ); QgsGeometry geom( geometry->clone() ); - featureRubberBand->addGeometry( geom, mFeaturePool->getLayer() ); + featureRubberBand->addGeometry( geom, layer ); featureRubberBand->setWidth( 5 ); featureRubberBand->setColor( Qt::yellow ); mCurrentRubberBands.append( featureRubberBand ); @@ -350,7 +378,7 @@ void QgsGeometryCheckerResultTab::highlightErrors( bool current ) if ( ui.radioButtonError->isChecked() || current || error->status() == QgsGeometryCheckError::StatusFixed ) { QgsRubberBand *pointRubberBand = new QgsRubberBand( mIface->mapCanvas(), QgsWkbTypes::PointGeometry ); - QgsPointXY pos = mIface->mapCanvas()->mapSettings().layerToMapCoordinates( mFeaturePool->getLayer(), QgsPointXY( error->location().x(), error->location().y() ) ); + QgsPoint pos = mIface->mapCanvas()->mapSettings().layerToMapCoordinates( layer, QgsPointXY( error->location().x(), error->location().y() ) ); pointRubberBand->addPoint( pos ); pointRubberBand->setWidth( 20 ); pointRubberBand->setColor( Qt::red ); @@ -359,7 +387,7 @@ void QgsGeometryCheckerResultTab::highlightErrors( bool current ) } else if ( ui.radioButtonFeature->isChecked() && geometry ) { - QgsRectangle geomextent = mIface->mapCanvas()->mapSettings().layerExtentToOutputExtent( mFeaturePool->getLayer(), geometry->boundingBox() ); + QgsRectangle geomextent = mIface->mapCanvas()->mapSettings().layerExtentToOutputExtent( layer, geometry->boundingBox() ); if ( totextent.isEmpty() ) { totextent = geomextent; @@ -422,31 +450,33 @@ void QgsGeometryCheckerResultTab::onSelectionChanged( const QItemSelection &newS void QgsGeometryCheckerResultTab::openAttributeTable() { - QSet ids; - Q_FOREACH ( QModelIndex idx, ui.tableWidgetErrors->selectionModel()->selectedRows() ) + QMap> ids; + for ( QModelIndex idx : ui.tableWidgetErrors->selectionModel()->selectedRows() ) { - QgsFeatureId id = ui.tableWidgetErrors->item( idx.row(), 0 )->data( Qt::UserRole ).value()->featureId(); + QgsGeometryCheckError *error = ui.tableWidgetErrors->item( idx.row(), 0 )->data( Qt::UserRole ).value(); + QgsFeatureId id = error->featureId(); if ( id >= 0 ) { - ids.insert( id ); + ids[error->layerId()].insert( id ); } } if ( ids.isEmpty() ) { return; } - QStringList expr; - Q_FOREACH ( int id, ids ) + for ( const QString &layerId : ids.keys() ) { - expr.append( QStringLiteral( "$id = %1 " ).arg( id ) ); + QStringList expr; + for ( QgsFeatureId id : ids[layerId] ) + { + expr.append( QStringLiteral( "$id = %1 " ).arg( id ) ); + } + if ( mAttribTableDialogs[layerId] ) + { + mAttribTableDialogs[layerId]->close(); + } + mAttribTableDialogs[layerId] = mIface->showAttributeTable( mFeaturePools[layerId]->getLayer(), expr.join( QStringLiteral( " or " ) ) ); } - if ( mAttribTableDialog ) - { - disconnect( mAttribTableDialog, &QObject::destroyed, this, &QgsGeometryCheckerResultTab::clearAttribTableDialog ); - mAttribTableDialog->close(); - } - mAttribTableDialog = mIface->showAttributeTable( mFeaturePool->getLayer(), expr.join( QStringLiteral( " or " ) ) ); - connect( mAttribTableDialog, &QObject::destroyed, this, &QgsGeometryCheckerResultTab::clearAttribTableDialog ); } void QgsGeometryCheckerResultTab::fixErrors( bool prompt ) @@ -460,7 +490,7 @@ void QgsGeometryCheckerResultTab::fixErrors( bool prompt ) rows = ui.tableWidgetErrors->selectionModel()->selectedRows(); } QList errors; - Q_FOREACH ( const QModelIndex &index, rows ) + for ( const QModelIndex &index : rows ) { QgsGeometryCheckError *error = ui.tableWidgetErrors->item( index.row(), 0 )->data( Qt::UserRole ).value(); if ( error->status() < QgsGeometryCheckError::StatusFixed ) @@ -487,7 +517,7 @@ void QgsGeometryCheckerResultTab::fixErrors( bool prompt ) mCloseable = false; if ( prompt ) { - QgsGeometryCheckerFixDialog fixdialog( mChecker, errors, mIface, mIface->mainWindow() ); + QgsGeometryCheckerFixDialog fixdialog( mChecker, errors, mIface->mainWindow() ); QEventLoop loop; connect( &fixdialog, &QgsGeometryCheckerFixDialog::currentErrorChanged, this, &QgsGeometryCheckerResultTab::highlightError ); connect( &fixdialog, &QDialog::finished, &loop, &QEventLoop::quit ); @@ -502,7 +532,7 @@ void QgsGeometryCheckerResultTab::fixErrors( bool prompt ) ui.progressBarFixErrors->setVisible( true ); ui.progressBarFixErrors->setRange( 0, errors.size() ); - Q_FOREACH ( QgsGeometryCheckError *error, errors ) + for ( QgsGeometryCheckError *error : errors ) { int fixMethod = QgsSettings().value( sSettingsGroup + error->check()->errorName(), QVariant::fromValue( 0 ) ).toInt(); mChecker->fixError( error, fixMethod ); @@ -513,11 +543,14 @@ void QgsGeometryCheckerResultTab::fixErrors( bool prompt ) ui.progressBarFixErrors->setVisible( false ); unsetCursor(); } - mChecker->getLayer()->triggerRepaint(); + for ( const QString &layerId : mFeaturePools.keys() ) + { + mFeaturePools[layerId]->getLayer()->triggerRepaint(); + } if ( mStatistics.itemCount() > 0 ) { - QgsGeometryCheckerFixSummaryDialog summarydialog( mIface, mFeaturePool->getLayer(), mStatistics, mChecker->getMessages(), mIface->mainWindow() ); + QgsGeometryCheckerFixSummaryDialog summarydialog( mFeaturePools, mStatistics, mChecker->getMessages(), mIface->mainWindow() ); QEventLoop loop; connect( &summarydialog, &QgsGeometryCheckerFixSummaryDialog::errorSelected, this, &QgsGeometryCheckerResultTab::highlightError ); connect( &summarydialog, &QDialog::finished, &loop, &QEventLoop::quit ); @@ -559,7 +592,7 @@ void QgsGeometryCheckerResultTab::setDefaultResolutionMethods() QWidget *scrollAreaContents = new QWidget( scrollArea ); QVBoxLayout *scrollAreaLayout = new QVBoxLayout( scrollAreaContents ); - Q_FOREACH ( const QgsGeometryCheck *check, mChecker->getChecks() ) + for ( const QgsGeometryCheck *check : mChecker->getChecks() ) { QGroupBox *groupBox = new QGroupBox( scrollAreaContents ); groupBox->setTitle( check->errorDescription() ); @@ -571,7 +604,7 @@ void QgsGeometryCheckerResultTab::setDefaultResolutionMethods() radioGroup->setProperty( "errorType", check->errorName() ); int id = 0; int checkedId = QgsSettings().value( sSettingsGroup + check->errorName(), QVariant::fromValue( 0 ) ).toInt(); - Q_FOREACH ( const QString &method, check->getResolutionMethods() ) + for ( const QString &method : check->getResolutionMethods() ) { QRadioButton *radio = new QRadioButton( method, groupBox ); radio->setChecked( id == checkedId ); @@ -598,15 +631,37 @@ void QgsGeometryCheckerResultTab::storeDefaultResolutionMethod( int id ) const void QgsGeometryCheckerResultTab::checkRemovedLayer( const QStringList &ids ) { - if ( mFeaturePool->getLayer() && ids.contains( mFeaturePool->getLayer()->id() ) && isEnabled() ) + bool requiredLayersRemoved = false; + for ( const QString &id : mFeaturePools.keys() ) + { + if ( ids.contains( id ) && isEnabled() ) + { + mFeaturePools[id]->clearLayer(); + requiredLayersRemoved = true; + } + } + if ( requiredLayersRemoved ) { if ( mTabWidget->currentWidget() == this ) { - QMessageBox::critical( this, tr( "Layer removed" ), tr( "The layer has been removed." ) ); + QMessageBox::critical( this, tr( "Layer removed" ), tr( "One or more layers have been removed." ) ); } setEnabled( false ); - mFeaturePool->clearLayer(); qDeleteAll( mCurrentRubberBands ); mCurrentRubberBands.clear(); } } + +void QgsGeometryCheckerResultTab::updateMergeAttributeIndices() +{ + QMap mergeAttribIndices; + QTreeWidgetItemIterator it( ui.treeWidgetMergeAttribute ); + while ( *it ) + { + QTreeWidgetItem *item = *it; + QComboBox *combo = qobject_cast( ui.treeWidgetMergeAttribute->itemWidget( item, 1 ) ); + mergeAttribIndices.insert( item->text( 0 ), combo->currentIndex() ); + ++it; + } + mChecker->setMergeAttributeIndices( mergeAttribIndices ); +} diff --git a/src/plugins/geometry_checker/ui/qgsgeometrycheckerresulttab.h b/src/plugins/geometry_checker/ui/qgsgeometrycheckerresulttab.h index 5c0f11775b6..5223afca426 100644 --- a/src/plugins/geometry_checker/ui/qgsgeometrycheckerresulttab.h +++ b/src/plugins/geometry_checker/ui/qgsgeometrycheckerresulttab.h @@ -32,7 +32,7 @@ class QgsGeometryCheckerResultTab : public QWidget { Q_OBJECT public: - QgsGeometryCheckerResultTab( QgisInterface *iface, QgsGeometryChecker *checker, QgsFeaturePool *featurePool, QTabWidget *tabWidget, QWidget *parent = nullptr ); + QgsGeometryCheckerResultTab( QgisInterface *iface, QgsGeometryChecker *checker, const QMap &featurePools, QTabWidget *tabWidget, QWidget *parent = nullptr ); ~QgsGeometryCheckerResultTab(); void finalize(); bool isCloseable() const { return mCloseable; } @@ -44,10 +44,10 @@ class QgsGeometryCheckerResultTab : public QWidget Ui::QgsGeometryCheckerResultTab ui; QgisInterface *mIface = nullptr; QgsGeometryChecker *mChecker = nullptr; - QgsFeaturePool *mFeaturePool = nullptr; + QMap mFeaturePools; QList mCurrentRubberBands; QMap mErrorMap; - QDialog *mAttribTableDialog = nullptr; + QMap> mAttribTableDialogs; int mErrorCount; int mFixedCount; bool mCloseable; @@ -71,7 +71,7 @@ class QgsGeometryCheckerResultTab : public QWidget void setDefaultResolutionMethods(); void storeDefaultResolutionMethod( int ) const; void checkRemovedLayer( const QStringList &ids ); - void clearAttribTableDialog() { mAttribTableDialog = nullptr; } + void updateMergeAttributeIndices(); }; #endif // QGS_GEOMETRY_CHECKER_RESULT_TAB_H diff --git a/src/plugins/geometry_checker/ui/qgsgeometrycheckerresulttab.ui b/src/plugins/geometry_checker/ui/qgsgeometrycheckerresulttab.ui index 75abb5ca805..a79398a360f 100644 --- a/src/plugins/geometry_checker/ui/qgsgeometrycheckerresulttab.ui +++ b/src/plugins/geometry_checker/ui/qgsgeometrycheckerresulttab.ui @@ -7,7 +7,7 @@ 0 0 587 - 394 + 548 @@ -39,8 +39,8 @@ 0 0 - 571 - 395 + 587 + 548 @@ -51,107 +51,6 @@ - - - - QAbstractItemView::NoEditTriggers - - - QAbstractItemView::ExtendedSelection - - - QAbstractItemView::SelectRows - - - true - - - true - - - true - - - false - - - - Object ID - - - - - Error - - - - - Coordinates - - - - - Value - - - - - Resolution - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Export - - - - - - - Qt::Horizontal - - - - 280 - 20 - - - - - - - - Total errors: 0 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - @@ -225,27 +124,6 @@ - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - @@ -389,8 +267,141 @@ + + + + + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectRows + + + true + + + true + + + true + + + false + + + + Object ID + + + + + Error + + + + + Coordinates + + + + + Value + + + + + Resolution + + + + + + + + + Layer + + + + + Attribute + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + - + + + Export + + + + + + + Qt::Horizontal + + + + 280 + 20 + + + + + + + + Total errors: 0 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + @@ -421,7 +432,6 @@ pushButtonFixWithDefault pushButtonFixWithPrompt pushButtonErrorResolutionSettings - comboBoxMergeAttribute diff --git a/src/plugins/geometry_checker/ui/qgsgeometrycheckersetuptab.cpp b/src/plugins/geometry_checker/ui/qgsgeometrycheckersetuptab.cpp index 3c2854093d9..8ea93cb1fa9 100644 --- a/src/plugins/geometry_checker/ui/qgsgeometrycheckersetuptab.cpp +++ b/src/plugins/geometry_checker/ui/qgsgeometrycheckersetuptab.cpp @@ -38,6 +38,7 @@ #include #include +static const int LayerIdRole = Qt::UserRole + 1; QgsGeometryCheckerSetupTab::QgsGeometryCheckerSetupTab( QgisInterface *iface, QWidget *parent ) : QWidget( parent ) @@ -47,17 +48,26 @@ QgsGeometryCheckerSetupTab::QgsGeometryCheckerSetupTab( QgisInterface *iface, QW ui.setupUi( this ); ui.progressBar->hide(); ui.labelStatus->hide(); - ui.comboBoxInputLayer->setFilters( QgsMapLayerProxyModel::HasGeometry ); mRunButton = ui.buttonBox->addButton( tr( "Run" ), QDialogButtonBox::ActionRole ); mAbortButton = new QPushButton( tr( "Abort" ) ); mRunButton->setEnabled( false ); + QMap filterFormatMap = QgsVectorFileWriter::supportedFiltersAndFormats(); + for ( const QString &filter : filterFormatMap.keys() ) + { + QString driverName = filterFormatMap.value( filter ); + ui.comboBoxOutputFormat->addItem( driverName ); + if ( driverName == QLatin1String( "ESRI Shapefile" ) ) + { + ui.comboBoxOutputFormat->setCurrentIndex( ui.comboBoxOutputFormat->count() - 1 ); + } + } + connect( mRunButton, &QAbstractButton::clicked, this, &QgsGeometryCheckerSetupTab::runChecks ); - connect( ui.comboBoxInputLayer, static_cast( &QComboBox::currentIndexChanged ), this, &QgsGeometryCheckerSetupTab::validateInput ); + connect( ui.listWidgetInputLayers, &QListWidgetItem::itemChanged, this, &QgsGeometryCheckerSetupTab::validateInput() ); connect( QgsProject::instance(), &QgsProject::layersAdded, this, &QgsGeometryCheckerSetupTab::updateLayers ); connect( QgsProject::instance(), static_cast( &QgsProject::layersWillBeRemoved ), this, &QgsGeometryCheckerSetupTab::updateLayers ); - connect( ui.radioButtonOutputNew, &QAbstractButton::toggled, ui.lineEditOutput, &QWidget::setEnabled ); - connect( ui.radioButtonOutputNew, &QAbstractButton::toggled, ui.pushButtonOutputBrowse, &QWidget::setEnabled ); + connect( ui.radioButtonOutputNew, &QAbstractButton::toggled, ui.frameOutput, &QWidget::setEnabled ); connect( ui.buttonGroupOutput, static_cast( &QButtonGroup::buttonClicked ), this, &QgsGeometryCheckerSetupTab::validateInput ); connect( ui.pushButtonOutputBrowse, &QAbstractButton::clicked, this, &QgsGeometryCheckerSetupTab::selectOutputFile ); connect( ui.lineEditOutput, &QLineEdit::textChanged, this, &QgsGeometryCheckerSetupTab::validateInput ); @@ -66,7 +76,7 @@ QgsGeometryCheckerSetupTab::QgsGeometryCheckerSetupTab( QgisInterface *iface, QW updateLayers(); - Q_FOREACH ( const QgsGeometryCheckFactory *factory, QgsGeometryCheckFactoryRegistry::getCheckFactories() ) + for ( const QgsGeometryCheckFactory *factory : QgsGeometryCheckFactoryRegistry::getCheckFactories() ) { factory->restorePrevious( ui ); } @@ -79,60 +89,107 @@ QgsGeometryCheckerSetupTab::~QgsGeometryCheckerSetupTab() void QgsGeometryCheckerSetupTab::updateLayers() { - QString prevLayer = ui.comboBoxInputLayer->currentText(); - ui.comboBoxInputLayer->clear(); + QStringList prevLayers; + for ( int row = 0, nRows = ui.listWidgetInputLayers->count(); row < nRows; ++row ) + { + QListWidgetItem *item = ui.listWidgetInputLayers->item( row ); + if ( item->checkState() == Qt::Checked ) + { + prevLayers.append( item->data( LayerIdRole ).toString() ); + } + } + ui.listWidgetInputLayers->clear(); // Collect layers - // Don't switch current layer if dialog is visible to avoid confusing the user - QgsMapLayer *currentLayer = isVisible() ? 0 : mIface->mapCanvas()->currentLayer(); - int currIdx = -1; - int idx = 0; - Q_FOREACH ( QgsVectorLayer *layer, QgsProject::instance()->layers() ) + for ( QgsVectorLayer *layer : QgsProject::instance()->layers() ) { - ui.comboBoxInputLayer->addItem( layer->name(), layer->id() ); - if ( layer->name() == prevLayer ) + QListWidgetItem *item = new QListWidgetItem( layer->name() ); + bool supportedGeometryType = true; + if ( layer->geometryType() == QgsWkbTypes::PointGeometry ) { - currIdx = idx; + item->setIcon( QgsApplication::getThemeIcon( "/mIconPointLayer.svg" ) ); } - else if ( currIdx == -1 && layer == currentLayer ) + else if ( layer->geometryType() == QgsWkbTypes::LineGeometry ) { - currIdx = idx; + item->setIcon( QgsApplication::getThemeIcon( "/mIconLineLayer.svg" ) ); } - ++idx; + else if ( layer->geometryType() == QgsWkbTypes::PolygonGeometry ) + { + item->setIcon( QgsApplication::getThemeIcon( "/mIconPolygonLayer.svg" ) ); + } + else + { + supportedGeometryType = false; + } + item->setData( LayerIdRole, layer->id() ); + if ( supportedGeometryType ) + { + item->setCheckState( prevLayers.contains( layer->id() ) ? Qt::Checked : Qt::Unchecked ); + } + else + { + item->setCheckState( Qt::Unchecked ); + item->setFlags( item->flags() & ~( Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsSelectable ) ); + } + ui.listWidgetInputLayers->addItem( item ); } - ui.comboBoxInputLayer->setCurrentIndex( std::max( 0, currIdx ) ); } -QgsVectorLayer *QgsGeometryCheckerSetupTab::getSelectedLayer() +QList QgsGeometryCheckerSetupTab::getSelectedLayers() { - int inputIdx = ui.comboBoxInputLayer->currentIndex(); - if ( inputIdx < 0 ) - return nullptr; - - QgsVectorLayer *layer = dynamic_cast( ui.comboBoxInputLayer->currentLayer() ); - return layer; + QList layers; + for ( int row = 0, nRows = ui.listWidgetInputLayers->count(); row < nRows; ++row ) + { + QListWidgetItem *item = ui.listWidgetInputLayers->item( row ); + QString layerId = item->data( LayerIdRole ).toString(); + QgsVectorLayer *layer = qobject_cast( QgsProject::instance()->mapLayer( layerId ) ); + if ( layer ) + { + layers.append( layer ); + } + } + return layers; } void QgsGeometryCheckerSetupTab::validateInput() { - QgsVectorLayer *layer = getSelectedLayer(); + QList layers = getSelectedLayers(); int nApplicable = 0; - if ( layer ) + if ( !layers.isEmpty() ) { - Q_FOREACH ( const QgsGeometryCheckFactory *factory, QgsGeometryCheckFactoryRegistry::getCheckFactories() ) + int nPoint = 0; + int nLineString = 0; + int nPolygon = 0; + for ( QgsVectorLayer *layer : layers ) { - nApplicable += factory->checkApplicability( ui, layer->geometryType() ); + QgsWkbTypes::GeometryType geomType = layer->geometryType(); + if ( geomType == QgsWkbTypes::PointGeometry ) + { + ++nPoint; + } + else if ( geomType == QgsWkbTypes::LineGeometry ) + { + ++nLineString; + } + else if ( geomType == QgsWkbTypes::PolygonGeometry ) + { + ++nPolygon; + } + } + for ( const QgsGeometryCheckFactory *factory : QgsGeometryCheckFactoryRegistry::getCheckFactories() ) + { + nApplicable += factory->checkApplicability( ui, nPoint, nLineString, nPolygon ); } } - bool outputOk = ui.radioButtonOutputModifyInput->isChecked() || !ui.lineEditOutput->text().isEmpty(); - mRunButton->setEnabled( layer && nApplicable > 0 && outputOk ); + bool outputOk = ui.radioButtonOutputModifyInput->isChecked() || !ui.lineEditOutputDirectory->text().isEmpty(); + mRunButton->setEnabled( !layers.isEmpty() && nApplicable > 0 && outputOk ); } void QgsGeometryCheckerSetupTab::selectOutputFile() { QString filterString = QgsVectorFileWriter::filterForDriver( QStringLiteral( "GPKG" ) ); QMap filterFormatMap = QgsVectorFileWriter::supportedFiltersAndFormats(); - Q_FOREACH ( const QString &filter, filterFormatMap.keys() ) + for ( const QString &filter : filterFormatMap.keys() ) { QString driverName = filterFormatMap.value( filter ); if ( driverName != QLatin1String( "ESRI Shapefile" ) ) // Default entry, first in list (see above) @@ -140,166 +197,236 @@ void QgsGeometryCheckerSetupTab::selectOutputFile() filterString += ";;" + filter; } } - QString initialdir; - QgsVectorLayer *layer = getSelectedLayer(); - if ( layer ) + QString initialdir = ui.lineEditOutputDirectory->text(); + if ( !QDir( initialdir ).exists() ) { - QDir dir = QFileInfo( layer->dataProvider()->dataSourceUri() ).dir(); - if ( dir.exists() ) + QList layers = getSelectedLayers(); + if ( !layers.isEmpty() ) { - initialdir = dir.absolutePath(); - } - } - QString selectedFilter; - QString filename = QFileDialog::getSaveFileName( this, tr( "Select Output File" ), initialdir, filterString, &selectedFilter ); - if ( !filename.isEmpty() ) - { - mOutputDriverName = filterFormatMap.value( selectedFilter ); - QgsVectorFileWriter::MetaData mdata; - if ( QgsVectorFileWriter::driverMetadata( mOutputDriverName, mdata ) ) - { - if ( !filename.endsWith( QStringLiteral( ".%1" ).arg( mdata.ext ), Qt::CaseInsensitive ) ) + QDir dir = QFileInfo( layers.front()->dataProvider()->dataSourceUri() ).dir(); + if ( dir.exists() ) { - filename += QStringLiteral( ".%1" ).arg( mdata.ext ); + initialdir = dir.absoluteFilePath( tr( "geometry_checker_result" ) ); } } - ui.lineEditOutput->setText( filename ); + else + { + initialdir = QDir::home().absoluteFilePath( tr( "geometry_checker_result" ) ); + } + } + QString dir = QFileDialog::getExistingDirectory( this, tr( "Select Output Directory" ), initialdir ); + if ( !dir.isEmpty() ) + { + ui.lineEditOutputDirectory->setText( dir ); } } void QgsGeometryCheckerSetupTab::runChecks() { - //! Get selected layer * - QgsVectorLayer *layer = getSelectedLayer(); - if ( !layer ) + // Get selected layer + QList layers = getSelectedLayers(); + if ( layers.isEmpty() ) return; - if ( ui.radioButtonOutputNew->isChecked() && - layer->dataProvider()->dataSourceUri().startsWith( ui.lineEditOutput->text() ) ) + if ( ui.radioButtonOutputNew->isChecked() ) { - QMessageBox::critical( this, tr( "Invalid Output Layer" ), tr( "The chosen output layer is the same as the input layer." ) ); - return; + for ( QgsVectorLayer *layer : layers ) + { + if ( layer->dataProvider()->dataSourceUri().startsWith( ui.lineEditOutputDirectory->text() ) ) + { + QMessageBox::critical( this, tr( "Invalid Output Layer" ), tr( "The chosen output directory contains one or more input layers." ) ); + return; + } + } } - if ( layer->isEditable() ) + for ( QgsVectorLayer *layer : layers ) { - QMessageBox::critical( this, tr( "Editable Input Layer" ), tr( "The input layer is not allowed to be in editing mode." ) ); - return; + if ( layer->isEditable() ) + { + QMessageBox::critical( this, tr( "Editable Input Layer" ), tr( "Input layer are not allowed to be in editing mode." ) ); + return; + } } bool selectedOnly = ui.checkBoxInputSelectedOnly->isChecked(); - //! Set window busy * + // Set window busy setCursor( Qt::WaitCursor ); mRunButton->setEnabled( false ); ui.labelStatus->setText( tr( "Preparing output..." ) ); ui.labelStatus->show(); QApplication::processEvents( QEventLoop::ExcludeUserInputEvents ); - //! Duplicate if necessary * + QList processLayers; if ( ui.radioButtonOutputNew->isChecked() ) { - // Write selected feature ids to new layer - QString filename = ui.lineEditOutput->text(); - - // Remove existing layer with same uri - QStringList toRemove; - Q_FOREACH ( QgsMapLayer *maplayer, QgsProject::instance()->mapLayers() ) + // Get output directory and file extension + QDir outputDir = QDir( ui.lineEditOutputDirectory->text() ); + QString outputDriverName = ui.comboBoxOutputFormat->currentText(); + QgsVectorFileWriter::MetaData metadata; + if ( !QgsVectorFileWriter::driverMetadata( outputDriverName, metadata ) ) { - if ( dynamic_cast( maplayer ) && - static_cast( maplayer )->dataProvider()->dataSourceUri().startsWith( filename ) ) - { - toRemove.append( maplayer->id() ); - } - } - if ( !toRemove.isEmpty() ) - { - QgsProject::instance()->removeMapLayers( toRemove ); - } - - QString errMsg; - QgsVectorFileWriter::WriterError err = QgsVectorFileWriter::writeAsVectorFormat( layer, filename, layer->dataProvider()->encoding(), layer->crs(), mOutputDriverName, selectedOnly, &errMsg ); - if ( err != QgsVectorFileWriter::NoError ) - { - QMessageBox::critical( this, tr( "Layer Creation Failed" ), tr( "Failed to create the output layer: %1" ).arg( errMsg ) ); + QMessageBox::critical( this, tr( "Unknown Output Format" ), tr( "The specified output format cannot be recognized." ) ); mRunButton->setEnabled( true ); ui.labelStatus->hide(); unsetCursor(); return; } - QgsVectorLayer *newlayer = new QgsVectorLayer( filename, QFileInfo( filename ).completeBaseName(), QStringLiteral( "ogr" ) ); + QString outputExtension = metadata.ext; - if ( selectedOnly ) + // List over input layers, check which existing project layers need to be removed and create output layers + QStringList toRemove; + QStringList createErrors; + for ( QgsVectorLayer *layer : layers ) { - QgsFeature feature; + QString outputPath = outputDir.absoluteFilePath( layer->name() + "." + outputExtension ); - // Get features to select (only selected features were written up to this point) - QgsFeatureIds selectedFeatures = newlayer->allFeatureIds(); - - // Write non-selected feature ids - QgsFeatureList features; - QgsFeatureIterator it = layer->getFeatures(); - while ( it.nextFeature( feature ) ) + // Remove existing layer with same uri from project + for ( QgsVectorLayer *projectLayer : QgsProject::instance()->layers() ) { - if ( !layer->selectedFeatureIds().contains( feature.id() ) ) + if ( projectLayer->dataProvider()->dataSourceUri().startsWith( outputPath ) ) { - features.append( feature ); + toRemove.append( projectLayer->id() ); } } - newlayer->dataProvider()->addFeatures( features ); - // Set selected features - newlayer->selectByIds( selectedFeatures ); + // Create output layer + QString errMsg; + QgsVectorFileWriter::WriterError err = QgsVectorFileWriter::writeAsVectorFormat( layer, outputPath, layer->dataProvider()->encoding(), layer->crs(), outputDriverName, selectedOnly, &errMsg ); + if ( err != QgsVectorFileWriter::NoError ) + { + createErrors.append( errMsg ); + continue; + } + + QgsVectorLayer *newlayer = new QgsVectorLayer( outputPath, QFileInfo( outputPath ).completeBaseName(), QStringLiteral( "ogr" ) ); + if ( selectedOnly ) + { + QgsFeature feature; + + // Get features to select (only selected features were written up to this point) + QgsFeatureIds selectedFeatures = newlayer->allFeatureIds(); + + // Write non-selected feature ids + QgsFeatureList features; + QgsFeatureIterator it = layer->getFeatures(); + while ( it.nextFeature( feature ) ) + { + if ( !layer->selectedFeatureIds().contains( feature.id() ) ) + { + features.append( feature ); + } + } + newlayer->dataProvider()->addFeatures( features ); + + // Set selected features + newlayer->selectByIds( selectedFeatures ); + } + processLayers.append( newlayer ); + } + + // Remove layers from project + if ( !toRemove.isEmpty() ) + { + QgsProject::instance()->removeMapLayers( toRemove ); + } + + // Error if an output layer could not be created + if ( !createErrors.isEmpty() ) + { + QMessageBox::critical( this, tr( "Layer Creation Failed" ), tr( "Failed to create one or moure output layers:\n%1" ).arg( createErrors.join( "\n" ) ) ); + mRunButton->setEnabled( true ); + ui.labelStatus->hide(); + unsetCursor(); + return; } - layer = newlayer; } - if ( ( layer->dataProvider()->capabilities() & QgsVectorDataProvider::ChangeGeometries ) == 0 ) + else { - if ( QMessageBox::Yes != QMessageBox::question( this, tr( "Non-editable Output Format" ), tr( "The output file format does not support editing features. The geometry check can be performed, but it will not be possible to fix any errors. Do you want to continue?" ), QMessageBox::Yes, QMessageBox::No ) ) + processLayers = layers; + } + + // Check if output layers are editable + QList nonEditableLayers; + for ( QgsVectorLayer *layer : processLayers ) + { + if ( ( layer->dataProvider()->capabilities() & QgsVectorDataProvider::ChangeGeometries ) == 0 ) + { + nonEditableLayers.append( layer ); + } + } + if ( !nonEditableLayers.isEmpty() ) + { + QStringList nonEditableLayerNames; + for ( QgsVectorLayer *layer : nonEditableLayers ) + { + nonEditableLayerNames.append( layer->name() ); + } + if ( QMessageBox::Yes != QMessageBox::question( this, tr( "Non-editable Output Layers" ), tr( "The following output layers are ina format that does not support editing features:\n%1\n\nThe geometry check can be performed, but it will not be possible to fix any errors. Do you want to continue?" ).arg( nonEditableLayerNames.join( "\n" ) ), QMessageBox::Yes, QMessageBox::No ) ) { if ( ui.radioButtonOutputNew->isChecked() ) { - QString outputFileName = ui.lineEditOutput->text(); - QFile( outputFileName ).remove(); - if ( mOutputDriverName == QLatin1String( "ESRI Shapefile" ) ) + for ( QgsVectorLayer *layer : processLayers ) { - QFile( QString( outputFileName ).replace( QRegExp( "shp$" ), QStringLiteral( "dbf" ) ) ).remove(); - QFile( QString( outputFileName ).replace( QRegExp( "shp$" ), QStringLiteral( "prj" ) ) ).remove(); - QFile( QString( outputFileName ).replace( QRegExp( "shp$" ), QStringLiteral( "qpj" ) ) ).remove(); - QFile( QString( outputFileName ).replace( QRegExp( "shp$" ), QStringLiteral( "shx" ) ) ).remove(); + QString layerPath = layer->dataProvider()->dataSourceUri(); + delete layer; + if ( ui.comboBoxOutputFormat->currentText() == QLatin1String( "ESRI Shapefile" ) ) + { + QgsVectorFileWriter::deleteShapeFile( layerPath ); + } + else + { + QFile( layerPath ).remove(); + } } + mRunButton->setEnabled( true ); + ui.labelStatus->hide(); + unsetCursor(); } return; } } - //! Setup checker * + // Setup checker ui.labelStatus->setText( tr( "Building spatial index..." ) ); QApplication::processEvents( QEventLoop::ExcludeUserInputEvents ); - QgsFeaturePool *featurePool = new QgsFeaturePool( layer, selectedOnly ); + QMap featurePools; + for ( QgsVectorLayer *layer : processLayers ) + { + double mapToLayerUnits = 1. / mIface->mapCanvas()->mapSettings().layerToMapUnits( layer ); + featurePools.insert( layer->id(), new QgsFeaturePool( layer, mapToLayerUnits, selectedOnly ) ); + } QList checks; - double mapToLayer = 1. / mIface->mapCanvas()->mapSettings().layerToMapUnits( layer ); - Q_FOREACH ( const QgsGeometryCheckFactory *factory, QgsGeometryCheckFactoryRegistry::getCheckFactories() ) + for ( const QgsGeometryCheckFactory *factory : QgsGeometryCheckFactoryRegistry::getCheckFactories() ) { - QgsGeometryCheck *check = factory->createInstance( featurePool, ui, mapToLayer ); + QgsGeometryCheck *check = factory->createInstance( featurePools, ui ); if ( check ) { checks.append( check ); } } QgsGeometryCheckPrecision::setPrecision( ui.spinBoxTolerance->value() ); - QgsGeometryChecker *checker = new QgsGeometryChecker( checks, featurePool ); + QgsGeometryChecker *checker = new QgsGeometryChecker( checks, featurePools ); - emit checkerStarted( checker, featurePool ); + emit checkerStarted( checker, featurePools ); - //! Add result layer (do this after checkerStarted, otherwise warning about removing of result layer may appear) * - layer->setReadOnly( true ); + // Add result layer (do this after checkerStarted, otherwise warning about removing of result layer may appear) + for ( QgsVectorLayer *layer : processLayers ) + { + layer->setReadOnly( true ); + } if ( ui.radioButtonOutputNew->isChecked() ) { - QgsProject::instance()->addMapLayers( QList() << layer ); + QList addLayers; + for ( QgsVectorLayer *layer : processLayers ) + { + addLayers.append( layer ); + } + QgsProject::instance()->addMapLayers( addLayers ); } - //! Run * + // Run ui.buttonBox->addButton( mAbortButton, QDialogButtonBox::ActionRole ); mRunButton->hide(); ui.progressBar->setRange( 0, 0 ); @@ -318,7 +445,7 @@ void QgsGeometryCheckerSetupTab::runChecks() ui.progressBar->setRange( 0, maxSteps ); evLoop.exec(); - //! Restore window * + // Restore window unsetCursor(); mAbortButton->setEnabled( true ); ui.buttonBox->removeButton( mAbortButton ); @@ -328,7 +455,7 @@ void QgsGeometryCheckerSetupTab::runChecks() ui.labelStatus->hide(); ui.widgetInputs->setEnabled( true ); - //! Show result * + // Show result emit checkerFinished( !futureWatcher.isCanceled() ); } @@ -337,5 +464,5 @@ void QgsGeometryCheckerSetupTab::showCancelFeedback() mAbortButton->setEnabled( false ); ui.labelStatus->setText( tr( "Waiting for running checks to finish..." ) ); ui.labelStatus->show(); - ui.progressBar->hide(); + ui.progressBar->hide() ; } diff --git a/src/plugins/geometry_checker/ui/qgsgeometrycheckersetuptab.h b/src/plugins/geometry_checker/ui/qgsgeometrycheckersetuptab.h index 7e1af1e5637..58282c5abe9 100644 --- a/src/plugins/geometry_checker/ui/qgsgeometrycheckersetuptab.h +++ b/src/plugins/geometry_checker/ui/qgsgeometrycheckersetuptab.h @@ -35,7 +35,7 @@ class QgsGeometryCheckerSetupTab : public QWidget ~QgsGeometryCheckerSetupTab(); signals: - void checkerStarted( QgsGeometryChecker *checker, QgsFeaturePool *featurePool ); + void checkerStarted( QgsGeometryChecker *checker, QMap featurePools ); void checkerFinished( bool ); private: @@ -44,9 +44,8 @@ class QgsGeometryCheckerSetupTab : public QWidget QPushButton *mRunButton = nullptr; QPushButton *mAbortButton = nullptr; QMutex m_errorListMutex; - QString mOutputDriverName; - QgsVectorLayer *getSelectedLayer(); + QList getSelectedLayers(); private slots: void runChecks(); diff --git a/src/plugins/geometry_checker/ui/qgsgeometrycheckersetuptab.ui b/src/plugins/geometry_checker/ui/qgsgeometrycheckersetuptab.ui index cfe62d36f65..5bf58c6e897 100644 --- a/src/plugins/geometry_checker/ui/qgsgeometrycheckersetuptab.ui +++ b/src/plugins/geometry_checker/ui/qgsgeometrycheckersetuptab.ui @@ -7,7 +7,7 @@ 0 0 640 - 491 + 726 @@ -41,9 +41,9 @@ 0 - 0 + -46 626 - 726 + 804 @@ -66,9 +66,9 @@ 2 - + - Input vector layer + Input vector layers true @@ -90,7 +90,7 @@ 2 - + @@ -598,9 +598,9 @@ - + - Output vector layer + Output vector layers true @@ -621,20 +621,10 @@ 2 - - - - &Modify input layer - - - buttonGroupOutput - - - - Create &new layer + Create &new layers true @@ -644,32 +634,86 @@ - - - - true + + + + &Modify input layers + + buttonGroupOutput + - - - - Browse + + + + QFrame::StyledPanel + + QFrame::Raised + + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + Format: + + + + + + + Output directory: + + + + + + + true + + + + + + + Browse + + + + + + + - groupBoxInputLayer + groupBoxInputLayers groupBox_2 groupBoxGeometryTypes groupBoxGeometryConditions line groupBoxGeometryProperties groupBoxTopology - groupBoxOutputLayer + groupBoxOutputLayers line_2 line_3 @@ -729,15 +773,9 @@
qgsscrollarea.h
1 - - QgsMapLayerComboBox - QComboBox -
qgsmaplayercombobox.h
-
scrollArea - comboBoxInputLayer checkBoxInputSelectedOnly checkBoxSelfIntersections checkBoxDuplicateNodes @@ -768,8 +806,6 @@ spinBoxTolerance radioButtonOutputModifyInput radioButtonOutputNew - lineEditOutput - pushButtonOutputBrowse diff --git a/src/plugins/geometry_checker/ui/qgsgeometrycheckfixdialog.cpp b/src/plugins/geometry_checker/ui/qgsgeometrycheckfixdialog.cpp index 1dae0ea9ce7..dfeefc76d21 100644 --- a/src/plugins/geometry_checker/ui/qgsgeometrycheckfixdialog.cpp +++ b/src/plugins/geometry_checker/ui/qgsgeometrycheckfixdialog.cpp @@ -33,12 +33,10 @@ #include QgsGeometryCheckerFixDialog::QgsGeometryCheckerFixDialog( QgsGeometryChecker *checker, - const QList &errors, - QgisInterface *iface, QWidget *parent ) + const QList &errors, QWidget *parent ) : QDialog( parent ) , mChecker( checker ) , mErrors( errors ) - , mIface( iface ) { setWindowTitle( tr( "Fix Errors" ) ); @@ -107,7 +105,7 @@ void QgsGeometryCheckerFixDialog::setupNextError() int id = 0; int checkedid = QgsSettings().value( QgsGeometryCheckerResultTab::sSettingsGroup + error->check()->errorName(), QVariant::fromValue( 0 ) ).toInt(); - Q_FOREACH ( const QString &method, error->check()->getResolutionMethods() ) + for ( const QString &method : error->check()->getResolutionMethods() ) { QRadioButton *radio = new QRadioButton( method ); radio->setChecked( checkedid == id ); @@ -126,8 +124,7 @@ void QgsGeometryCheckerFixDialog::fixError() setCursor( Qt::WaitCursor ); QgsGeometryCheckError *error = mErrors.at( 0 ); - mChecker->fixError( error, mRadioGroup->checkedId() ); - mChecker->getLayer()->triggerRepaint(); + mChecker->fixError( error, mRadioGroup->checkedId(), true ); unsetCursor(); diff --git a/src/plugins/geometry_checker/ui/qgsgeometrycheckfixdialog.h b/src/plugins/geometry_checker/ui/qgsgeometrycheckfixdialog.h index e762e025323..78670c5f860 100644 --- a/src/plugins/geometry_checker/ui/qgsgeometrycheckfixdialog.h +++ b/src/plugins/geometry_checker/ui/qgsgeometrycheckfixdialog.h @@ -32,7 +32,7 @@ class QgsGeometryCheckerFixDialog : public QDialog { Q_OBJECT public: - QgsGeometryCheckerFixDialog( QgsGeometryChecker *checker, const QList &errors, QgisInterface *iface, QWidget *parent = nullptr ); + QgsGeometryCheckerFixDialog( QgsGeometryChecker *checker, const QList &errors, QWidget *parent = nullptr ); signals: void currentErrorChanged( QgsGeometryCheckError *error ); @@ -40,7 +40,6 @@ class QgsGeometryCheckerFixDialog : public QDialog private: QgsGeometryChecker *mChecker = nullptr; QList mErrors; - QgisInterface *mIface = nullptr; QGroupBox *mResolutionsBox = nullptr; QDialogButtonBox *mButtonBox = nullptr; QLabel *mStatusLabel = nullptr; diff --git a/src/plugins/geometry_checker/utils/qgsfeaturepool.cpp b/src/plugins/geometry_checker/utils/qgsfeaturepool.cpp index a37ef0d956b..4f59235d808 100644 --- a/src/plugins/geometry_checker/utils/qgsfeaturepool.cpp +++ b/src/plugins/geometry_checker/utils/qgsfeaturepool.cpp @@ -25,9 +25,10 @@ #include #include -QgsFeaturePool::QgsFeaturePool( QgsVectorLayer *layer, bool selectedOnly ) +QgsFeaturePool::QgsFeaturePool( QgsVectorLayer *layer, double mapToLayerUnits, bool selectedOnly ) : mFeatureCache( CACHE_SIZE ) , mLayer( layer ) + , mMapToLayerUnits( mapToLayerUnits ) , mSelectedOnly( selectedOnly ) { if ( selectedOnly ) diff --git a/src/plugins/geometry_checker/utils/qgsfeaturepool.h b/src/plugins/geometry_checker/utils/qgsfeaturepool.h index 67b4c7cd6fd..bf2cdb65b2f 100644 --- a/src/plugins/geometry_checker/utils/qgsfeaturepool.h +++ b/src/plugins/geometry_checker/utils/qgsfeaturepool.h @@ -30,7 +30,7 @@ class QgsVectorLayer; class QgsFeaturePool { public: - QgsFeaturePool( QgsVectorLayer *layer, bool selectedOnly = false ); + QgsFeaturePool( QgsVectorLayer *layer, double mapToLayerUnits, bool selectedOnly = false ); bool get( QgsFeatureId id, QgsFeature &feature ); void addFeature( QgsFeature &feature ); void updateFeature( QgsFeature &feature ); @@ -38,6 +38,7 @@ class QgsFeaturePool QgsFeatureIds getIntersects( const QgsRectangle &rect ); QgsVectorLayer *getLayer() const { return mLayer; } const QgsFeatureIds &getFeatureIds() const { return mFeatureIds; } + double getMapToLayerUnits() const { return mMapToLayerUnits;} bool getSelectedOnly() const { return mSelectedOnly; } void clearLayer() { mLayer = nullptr; } @@ -60,6 +61,7 @@ class QgsFeaturePool QMutex mLayerMutex; QMutex mIndexMutex; QgsSpatialIndex mIndex; + double mMapToLayerUnits; bool mSelectedOnly; bool getTouchingWithSharedEdge( QgsFeature &feature, QgsFeatureId &touchingId, const double & ( *comparator )( const double &, const double & ), double init );