[Geometry checker] Initial multi-layer support

This commit is contained in:
Sandro Mani 2017-03-30 15:52:01 +02:00
parent e3fc73f41e
commit 31cc65df49
51 changed files with 1506 additions and 1104 deletions

View File

@ -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

View File

@ -17,50 +17,57 @@
#include "qgsgeometryutils.h"
#include "../utils/qgsfeaturepool.h"
void QgsGeometryAngleCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QgsFeatureIds &ids ) const
void QgsGeometryAngleCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
{
const QgsFeatureIds &featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids;
Q_FOREACH ( QgsFeatureId featureid, featureIds )
QMap<QString, QgsFeatureIds> 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<QgsGeometryCheckError *> &error
}
}
void QgsGeometryAngleCheck::fixError( QgsGeometryCheckError *error, int method, int /*mergeAttributeIndex*/, Changes &changes ) const
void QgsGeometryAngleCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*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 );
}
}

View File

@ -23,12 +23,12 @@ class QgsGeometryAngleCheck : public QgsGeometryCheck
Q_OBJECT
public:
QgsGeometryAngleCheck( QgsFeaturePool *featurePool, double minAngle )
: QgsGeometryCheck( FeatureNodeCheck, featurePool )
, mMinAngle( minAngle )
QgsGeometryAngleCheck( const QMap<QString, QgsFeaturePool *> &featurePools, double minAngle )
: QgsGeometryCheck( FeatureNodeCheck, {QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}, featurePools )
, mMinAngle( minAngle )
{}
void collectErrors( QList<QgsGeometryCheckError *> &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<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap<QString, QgsFeatureIds> &ids = QMap<QString, QgsFeatureIds>() ) const override;
void fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const override;
QStringList getResolutionMethods() const override;
QString errorDescription() const override { return tr( "Minimal angle" ); }
QString errorName() const override { return QStringLiteral( "QgsGeometryAngleCheck" ); }

View File

@ -18,47 +18,54 @@
#include "qgsgeometryareacheck.h"
#include "../utils/qgsfeaturepool.h"
void QgsGeometryAreaCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QgsFeatureIds &ids ) const
void QgsGeometryAreaCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
{
const QgsFeatureIds &featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids;
Q_FOREACH ( QgsFeatureId featureid, featureIds )
QMap<QString, QgsFeatureIds> 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<QgsGeometryCollection *>( geom ) )
for ( QgsFeatureId featureid : featureIds[layerId] )
{
QgsGeometryCollection *multiGeom = static_cast<QgsGeometryCollection *>( 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<QgsGeometryCollection *>( geom ) )
{
QgsGeometryCollection *multiGeom = static_cast<QgsGeometryCollection *>( 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<QString, int> &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<QgsGeometryCollection *>( geom ) )
{
double value;
if ( !checkThreshold( static_cast<QgsGeometryCollection *>( geom )->geometryN( vidx.part ), value ) )
if ( !checkThreshold( error->layerId(), static_cast<QgsGeometryCollection *>( 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;
}

View File

@ -25,23 +25,23 @@ class QgsGeometryAreaCheck : public QgsGeometryCheck
Q_OBJECT
public:
QgsGeometryAreaCheck( QgsFeaturePool *featurePool, double threshold )
: QgsGeometryCheck( FeatureCheck, featurePool )
, mThreshold( threshold )
QgsGeometryAreaCheck( const QMap<QString, QgsFeaturePool *> &featurePools, double thresholdMapUnits )
: QgsGeometryCheck( FeatureCheck, {QgsWkbTypes::PolygonGeometry}, featurePools )
, mThresholdMapUnits( thresholdMapUnits )
{}
void collectErrors( QList<QgsGeometryCheckError *> &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<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap<QString, QgsFeatureIds> &ids = QMap<QString, QgsFeatureIds>() ) const override;
void fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &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

View File

@ -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<QString, QgsFeatureIds> QgsGeometryCheck::allLayerFeatureIds() const
{
QMap<QString, QgsFeatureIds> 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<QgsGeometryCollection *>( 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<QgsGeometryCollection *>( geom )->removeGeometry( partIdx );
if ( static_cast<QgsGeometryCollection *>( 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<QgsCurvePolygon *>( 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 );
}
}

View File

@ -69,27 +69,33 @@ class QgsGeometryCheck : public QObject
QgsVertexId vidx;
};
typedef QMap<QgsFeatureId, QList<Change> > Changes;
typedef QMap<QString, QMap<QgsFeatureId, QList<Change>>> Changes;
QgsGeometryCheck( CheckType checkType, QgsFeaturePool *featurePool )
QgsGeometryCheck( CheckType checkType, const QList<QgsWkbTypes::GeometryType> &compatibleGeometryTypes, const QMap<QString, QgsFeaturePool *> &featurePools )
: mCheckType( checkType )
, mFeaturePool( featurePool )
, mCompatibleGeometryTypes( compatibleGeometryTypes )
, mFeaturePools( featurePools )
{}
virtual void collectErrors( QList<QgsGeometryCheckError *> &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<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap<QString, QgsFeatureIds> &ids = QMap<QString, QgsFeatureIds>() ) const = 0;
virtual void fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &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<QString, QgsFeaturePool *> &getFeaturePools() const { return mFeaturePools; }
QgsFeaturePool *getFeaturePool( const QString &layerId ) const { return mFeaturePools.value( layerId, nullptr ); }
protected:
const CheckType mCheckType;
QgsFeaturePool *mFeaturePool = nullptr;
QMap<QString, QgsFeatureIds> 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<QgsWkbTypes::GeometryType> mCompatibleGeometryTypes;
QMap<QString, QgsFeaturePool *> 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;

View File

@ -17,56 +17,63 @@
#include "qgsgeometrycontainedcheck.h"
#include "../utils/qgsfeaturepool.h"
void QgsGeometryContainedCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter, const QgsFeatureIds &ids ) const
void QgsGeometryContainedCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
{
const QgsFeatureIds &featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids;
Q_FOREACH ( QgsFeatureId featureid, featureIds )
QMap<QString, QgsFeatureIds> 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<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const
{
QgsGeometryContainedCheckError *coverError = static_cast<QgsGeometryContainedCheckError *>( 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

View File

@ -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<QgsGeometryCheckError *> &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<QString, QgsFeaturePool *> &featurePools )
: QgsGeometryCheck( FeatureCheck, {QgsWkbTypes::PolygonGeometry}, featurePools ) {}
void collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap<QString, QgsFeatureIds> &ids = QMap<QString, QgsFeatureIds>() ) const override;
void fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const override;
QStringList getResolutionMethods() const override;
QString errorDescription() const override { return tr( "Within" ); }
QString errorName() const override { return QStringLiteral( "QgsGeometryContainedCheck" ); }

View File

@ -16,36 +16,43 @@
#include "qgsgeometrydegeneratepolygoncheck.h"
#include "../utils/qgsfeaturepool.h"
void QgsGeometryDegeneratePolygonCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QgsFeatureIds &ids ) const
void QgsGeometryDegeneratePolygonCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
{
const QgsFeatureIds &featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids;
Q_FOREACH ( QgsFeatureId featureid, featureIds )
QMap<QString, QgsFeatureIds> 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<QString, int> & /*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

View File

@ -23,10 +23,10 @@ class QgsGeometryDegeneratePolygonCheck : public QgsGeometryCheck
Q_OBJECT
public:
explicit QgsGeometryDegeneratePolygonCheck( QgsFeaturePool *featurePool )
: QgsGeometryCheck( FeatureNodeCheck, featurePool ) {}
void collectErrors( QList<QgsGeometryCheckError *> &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<QString, QgsFeaturePool *> &featurePools )
: QgsGeometryCheck( FeatureNodeCheck, {QgsWkbTypes::PolygonGeometry}, featurePools ) {}
void collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap<QString, QgsFeatureIds> &ids = QMap<QString, QgsFeatureIds>() ) const override;
void fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &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" ); }

View File

@ -19,59 +19,66 @@
#include "qgsgeometry.h"
#include "../utils/qgsfeaturepool.h"
void QgsGeometryDuplicateCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter, const QgsFeatureIds &ids ) const
void QgsGeometryDuplicateCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
{
const QgsFeatureIds &featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids;
Q_FOREACH ( QgsFeatureId featureid, featureIds )
QMap<QString, QgsFeatureIds> 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<QgsFeatureId> duplicates;
QgsFeatureIds ids = mFeaturePool->getIntersects( featureGeom.geometry()->boundingBox() );
Q_FOREACH ( QgsFeatureId id, ids )
{
// > : only report overlaps once
if ( id >= featureid )
QList<QgsFeatureId> 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<QString, int> & /*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<QgsGeometryDuplicateCheckError *>( 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;

View File

@ -22,10 +22,11 @@ class QgsGeometryDuplicateCheckError : public QgsGeometryCheckError
{
public:
QgsGeometryDuplicateCheckError( const QgsGeometryCheck *check,
const QString &layerId,
QgsFeatureId featureId,
const QgsPoint &errorLocation,
const QList<QgsFeatureId> &duplicates )
: QgsGeometryCheckError( check, featureId, errorLocation, QgsVertexId(), duplicatesString( duplicates ) )
: QgsGeometryCheckError( check, layerId, featureId, errorLocation, QgsVertexId(), duplicatesString( duplicates ) )
, mDuplicates( duplicates )
{ }
QList<QgsFeatureId> 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<QgsGeometryDuplicateCheckError *>( other )->duplicates() == duplicates();
@ -44,7 +46,7 @@ class QgsGeometryDuplicateCheckError : public QgsGeometryCheckError
static inline QString duplicatesString( const QList<QgsFeatureId> &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<QgsGeometryCheckError *> &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<QString, QgsFeaturePool *> &featurePools )
: QgsGeometryCheck( FeatureCheck, {QgsWkbTypes::PolygonGeometry}, featurePools ) {}
void collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap<QString, QgsFeatureIds> &ids = QMap<QString, QgsFeatureIds>() ) const override;
void fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const override;
QStringList getResolutionMethods() const override;
QString errorDescription() const override { return tr( "Duplicate" ); }
QString errorName() const override { return QStringLiteral( "QgsGeometryDuplicateCheck" ); }

View File

@ -17,34 +17,41 @@
#include "qgsgeometryutils.h"
#include "../utils/qgsfeaturepool.h"
void QgsGeometryDuplicateNodesCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QgsFeatureIds &ids ) const
void QgsGeometryDuplicateNodesCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
{
const QgsFeatureIds &featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids;
Q_FOREACH ( QgsFeatureId featureid, featureIds )
QMap<QString, QgsFeatureIds> 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<QgsGeometryCheckError
}
}
void QgsGeometryDuplicateNodesCheck::fixError( QgsGeometryCheckError *error, int method, int /*mergeAttributeIndex*/, Changes &changes ) const
void QgsGeometryDuplicateNodesCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*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

View File

@ -23,10 +23,10 @@ class QgsGeometryDuplicateNodesCheck : public QgsGeometryCheck
Q_OBJECT
public:
explicit QgsGeometryDuplicateNodesCheck( QgsFeaturePool *featurePool )
: QgsGeometryCheck( FeatureNodeCheck, featurePool ) {}
void collectErrors( QList<QgsGeometryCheckError *> &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<QString, QgsFeaturePool *> &featurePools )
: QgsGeometryCheck( FeatureNodeCheck, {QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}, featurePools ) {}
void collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap<QString, QgsFeatureIds> &ids = QMap<QString, QgsFeatureIds>() ) const override;
void fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const override;
QStringList getResolutionMethods() const override;
QString errorDescription() const override { return tr( "Duplicate node" ); }
QString errorName() const override { return QStringLiteral( "QgsGeometryDuplicateNodesCheck" ); }

View File

@ -19,122 +19,131 @@
#include "../utils/qgsfeaturepool.h"
void QgsGeometryGapCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter, const QgsFeatureIds &ids ) const
void QgsGeometryGapCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
{
Q_ASSERT( mFeaturePool->getLayer()->geometryType() == QgsWkbTypes::PolygonGeometry );
if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 );
QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids;
// Collect geometries, build spatial index
QList<QgsGeometry > 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<QgsAbstractGeometry *> 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<QString, int> & /*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;
}

View File

@ -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<QgsGeometryGapCheckError *>( 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<QString, QgsFeaturePool *> &featurePools, double thresholdMapUnits )
: QgsGeometryCheck( LayerCheck, {QgsWkbTypes::PolygonGeometry}, featurePools )
, mThresholdMapUnits( thresholdMapUnits )
{}
void collectErrors( QList<QgsGeometryCheckError *> &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<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap<QString, QgsFeatureIds> &ids = QMap<QString, QgsFeatureIds>() ) const override;
void fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &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;
};

View File

@ -16,35 +16,42 @@
#include "qgsgeometryholecheck.h"
#include "../utils/qgsfeaturepool.h"
void QgsGeometryHoleCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QgsFeatureIds &ids ) const
void QgsGeometryHoleCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
{
const QgsFeatureIds &featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids;
Q_FOREACH ( QgsFeatureId featureid, featureIds )
QMap<QString, QgsFeatureIds> 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<QString, int> & /*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

View File

@ -23,10 +23,10 @@ class QgsGeometryHoleCheck : public QgsGeometryCheck
Q_OBJECT
public:
explicit QgsGeometryHoleCheck( QgsFeaturePool *featurePool )
: QgsGeometryCheck( FeatureCheck, featurePool ) {}
void collectErrors( QList<QgsGeometryCheckError *> &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<QString, QgsFeaturePool *> &featurePools )
: QgsGeometryCheck( FeatureCheck, {QgsWkbTypes::PolygonGeometry}, featurePools ) {}
void collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap<QString, QgsFeatureIds> &ids = QMap<QString, QgsFeatureIds>() ) const override;
void fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &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" ); }

View File

@ -16,32 +16,39 @@
#include "qgsgeometrymultipartcheck.h"
#include "../utils/qgsfeaturepool.h"
void QgsGeometryMultipartCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QgsFeatureIds &ids ) const
void QgsGeometryMultipartCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
{
const QgsFeatureIds &featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids;
Q_FOREACH ( QgsFeatureId featureid, featureIds )
QMap<QString, QgsFeatureIds> 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<QString, int> & /*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
{

View File

@ -23,10 +23,10 @@ class QgsGeometryMultipartCheck : public QgsGeometryCheck
Q_OBJECT
public:
explicit QgsGeometryMultipartCheck( QgsFeaturePool *featurePool )
: QgsGeometryCheck( FeatureCheck, featurePool ) {}
void collectErrors( QList<QgsGeometryCheckError *> &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<QString, QgsFeaturePool *> &featurePools )
: QgsGeometryCheck( FeatureCheck, {QgsWkbTypes::PointGeometry, QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}, featurePools ) {}
void collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap<QString, QgsFeatureIds> &ids = QMap<QString, QgsFeatureIds>() ) const override;
void fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &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" ); }

View File

@ -17,72 +17,81 @@
#include "qgsgeometryoverlapcheck.h"
#include "../utils/qgsfeaturepool.h"
void QgsGeometryOverlapCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter, const QgsFeatureIds &ids ) const
void QgsGeometryOverlapCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
{
const QgsFeatureIds &featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids;
Q_FOREACH ( QgsFeatureId featureid, featureIds )
QMap<QString, QgsFeatureIds> 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<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const
{
QString errMsg;
QgsGeometryOverlapCheckError *overlapError = static_cast<QgsGeometryOverlapCheckError *>( 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;
}

View File

@ -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<QgsGeometryOverlapCheckError *>( 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<QgsGeometryOverlapCheckError *>( 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<QString, QgsFeaturePool *> &featurePools, double thresholdMapUnits )
: QgsGeometryCheck( FeatureCheck, {QgsWkbTypes::PolygonGeometry}, featurePools )
, mThresholdMapUnits( thresholdMapUnits )
{}
void collectErrors( QList<QgsGeometryCheckError *> &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<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap<QString, QgsFeatureIds> &ids = QMap<QString, QgsFeatureIds>() ) const override;
void fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &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

View File

@ -17,37 +17,46 @@
#include "qgsgeometryutils.h"
#include "../utils/qgsfeaturepool.h"
void QgsGeometrySegmentLengthCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QgsFeatureIds &ids ) const
void QgsGeometrySegmentLengthCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
{
const QgsFeatureIds &featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids;
Q_FOREACH ( QgsFeatureId featureid, featureIds )
QMap<QString, QgsFeatureIds> 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<QgsGeometryCheckError *
}
}
void QgsGeometrySegmentLengthCheck::fixError( QgsGeometryCheckError *error, int method, int /*mergeAttributeIndex*/, Changes &/*changes*/ ) const
void QgsGeometrySegmentLengthCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*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;

View File

@ -23,18 +23,18 @@ class QgsGeometrySegmentLengthCheck : public QgsGeometryCheck
Q_OBJECT
public:
QgsGeometrySegmentLengthCheck( QgsFeaturePool *featurePool, double minLength )
: QgsGeometryCheck( FeatureNodeCheck, featurePool )
, mMinLength( minLength )
QgsGeometrySegmentLengthCheck( const QMap<QString, QgsFeaturePool *> &featurePools, double minLengthMapUnits )
: QgsGeometryCheck( FeatureNodeCheck, {QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}, featurePools )
, mMinLengthMapUnits( minLengthMapUnits )
{}
void collectErrors( QList<QgsGeometryCheckError *> &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<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap<QString, QgsFeatureIds> &ids = QMap<QString, QgsFeatureIds>() ) const override;
void fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &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

View File

@ -9,71 +9,78 @@
#include "qgsgeometryutils.h"
#include "../utils/qgsfeaturepool.h"
void QgsGeometrySelfContactCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QgsFeatureIds &ids ) const
void QgsGeometrySelfContactCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
{
const QgsFeatureIds &featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids;
QMap<QString, QgsFeatureIds> 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<int> vtxMap;
QVector<QgsPoint> 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<int> vtxMap;
QVector<QgsPoint> 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<QgsGeometryCheckError *>
}
}
void QgsGeometrySelfContactCheck::fixError( QgsGeometryCheckError *error, int method, int /*mergeAttributeIndex*/, Changes & /*changes*/ ) const
void QgsGeometrySelfContactCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes & /*changes*/ ) const
{
if ( method == NoChange )
{

View File

@ -15,10 +15,10 @@ class QgsGeometrySelfContactCheck : public QgsGeometryCheck
Q_OBJECT
public:
QgsGeometrySelfContactCheck( QgsFeaturePool *featurePool )
: QgsGeometryCheck( FeatureNodeCheck, featurePool ) {}
void collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = 0, const QgsFeatureIds &ids = QgsFeatureIds() ) const;
void fixError( QgsGeometryCheckError *error, int method, int, Changes & ) const;
QgsGeometrySelfContactCheck( const QMap<QString, QgsFeaturePool *> &featurePools )
: QgsGeometryCheck( FeatureNodeCheck, {QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}, featurePools ) {}
void collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = 0, const QMap<QString, QgsFeatureIds> &ids = QMap<QString, QgsFeatureIds>() ) const;
void fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes & ) const;
QStringList getResolutionMethods() const;
QString errorDescription() const { return tr( "Self contact" ); }
QString errorName() const { return QStringLiteral( "QgsGeometrySelfContactCheck" ); }

View File

@ -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<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QgsFeatureIds &ids ) const
void QgsGeometrySelfIntersectionCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
{
const QgsFeatureIds &featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids;
Q_FOREACH ( QgsFeatureId featureid, featureIds )
QMap<QString, QgsFeatureIds> 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<QString, int> & /*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<QgsGeometryCollection *>( geom ) )
{
static_cast<QgsGeometryCollection *>( 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

View File

@ -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<QgsGeometryCheckError *> &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<QString, QgsFeaturePool *> &featurePools )
: QgsGeometryCheck( FeatureNodeCheck, {QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}, featurePools ) {}
void collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap<QString, QgsFeatureIds> &ids = QMap<QString, QgsFeatureIds>() ) const override;
void fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const override;
QStringList getResolutionMethods() const override;
QString errorDescription() const override { return tr( "Self intersection" ); }
QString errorName() const override { return QStringLiteral( "QgsGeometrySelfIntersectionCheck" ); }

View File

@ -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
}

View File

@ -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<QString, QgsFeaturePool *> &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

View File

@ -23,32 +23,39 @@
#include "../utils/qgsfeaturepool.h"
void QgsGeometryTypeCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QgsFeatureIds &ids ) const
void QgsGeometryTypeCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const
{
const QgsFeatureIds &featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids;
Q_FOREACH ( QgsFeatureId featureid, featureIds )
QMap<QString, QgsFeatureIds> 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<QString, int> & /*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
{

View File

@ -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<QString, QgsFeaturePool *> &featurePools, int allowedTypes )
: QgsGeometryCheck( FeatureCheck, {QgsWkbTypes::PointGeometry, QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}, featurePools )
, mAllowedTypes( allowedTypes )
{}
void collectErrors( QList<QgsGeometryCheckError *> &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<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter = nullptr, const QMap<QString, QgsFeatureIds> &ids = QMap<QString, QgsFeatureIds>() ) const override;
void fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const override;
QStringList getResolutionMethods() const override;
QString errorDescription() const override { return tr( "Geometry type" ); }
QString errorName() const override { return QStringLiteral( "QgsGeometryTypeCheck" ); }

View File

@ -24,10 +24,9 @@
#include <QTimer>
QgsGeometryChecker::QgsGeometryChecker( const QList<QgsGeometryCheck *> &checks, QgsFeaturePool *featurePool )
QgsGeometryChecker::QgsGeometryChecker( const QList<QgsGeometryCheck *> &checks, const QMap<QString, QgsFeaturePool *> &featurePools )
: mChecks( checks )
, mFeaturePool( featurePool )
, mMergeAttributeIndex( -1 )
, mFeaturePools( featurePools )
{
}
@ -42,16 +41,18 @@ QFuture<void> 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<QString, QSet<QgsFeatureId>> recheckFeatures;
for ( const QString &layerId : changes.keys() )
{
bool removed = false;
Q_FOREACH ( const QgsGeometryCheck::Change &change, changes.value( id ) )
const QMap<QgsFeatureId, QList<QgsGeometryCheck::Change>> &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<QString, QgsFeatureIds> 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<QgsGeometryCheckError *> 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 );
}

View File

@ -35,16 +35,13 @@ class QgsGeometryChecker : public QObject
{
Q_OBJECT
public:
QgsGeometryChecker( const QList<QgsGeometryCheck *> &checks, QgsFeaturePool *featurePool );
QgsGeometryChecker( const QList<QgsGeometryCheck *> &checks, const QMap<QString, QgsFeaturePool *> &featurePools );
~QgsGeometryChecker();
QFuture<void> execute( int *totalSteps = nullptr );
bool fixError( QgsGeometryCheckError *error, int method );
QgsMapLayer *getLayer() const;
bool fixError( QgsGeometryCheckError *error, int method, bool triggerRepaint = false );
const QList<QgsGeometryCheck *> getChecks() const { return mChecks; }
QStringList getMessages() const { return mMessages; }
public slots:
void setMergeAttributeIndex( int mergeAttributeIndex ) { mMergeAttributeIndex = mergeAttributeIndex; }
void setMergeAttributeIndices( const QMap<QString, int> &mergeAttributeIndices ) { mMergeAttributeIndices = mergeAttributeIndices; }
signals:
void errorAdded( QgsGeometryCheckError *error );
@ -62,11 +59,11 @@ class QgsGeometryChecker : public QObject
};
QList<QgsGeometryCheck *> mChecks;
QgsFeaturePool *mFeaturePool = nullptr;
QMap<QString, QgsFeaturePool *> mFeaturePools;
QList<QgsGeometryCheckError *> mCheckErrors;
QStringList mMessages;
QMutex mErrorListMutex;
int mMergeAttributeIndex;
QMap<QString, int> mMergeAttributeIndices;
QAtomicInt mProgressCounter;
void runCheck( const QgsGeometryCheck *check );

View File

@ -44,19 +44,19 @@ template<> void QgsGeometryCheckFactoryT<QgsGeometryAngleCheck>::restorePrevious
ui.doubleSpinBoxAngle->setValue( QgsSettings().value( sSettingsGroup + "minimalAngle" ).toDouble() );
}
template<> bool QgsGeometryCheckFactoryT<QgsGeometryAngleCheck>::checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, QgsWkbTypes::GeometryType geomType ) const
template<> bool QgsGeometryCheckFactoryT<QgsGeometryAngleCheck>::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<QgsGeometryAngleCheck>::createInstance( QgsFeaturePool *featurePool, const Ui::QgsGeometryCheckerSetupTab &ui, double /*mapToLayerUnits*/ ) const
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryAngleCheck>::createInstance( const QMap<QString, QgsFeaturePool *> &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<QgsGeometryAreaCheck>::restorePrevious(
ui.doubleSpinBoxArea->setValue( QgsSettings().value( sSettingsGroup + "minimalArea" ).toDouble() );
}
template<> bool QgsGeometryCheckFactoryT<QgsGeometryAreaCheck>::checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, QgsWkbTypes::GeometryType geomType ) const
template<> bool QgsGeometryCheckFactoryT<QgsGeometryAreaCheck>::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<QgsGeometryAreaCheck>::createInstance( QgsFeaturePool *featurePool, const Ui::QgsGeometryCheckerSetupTab &ui, double mapToLayerUnits ) const
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryAreaCheck>::createInstance( const QMap<QString, QgsFeaturePool *> &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<QgsGeometryContainedCheck>::restorePrev
ui.checkBoxCovered->setChecked( QgsSettings().value( sSettingsGroup + "checkCovers" ).toBool() );
}
template<> bool QgsGeometryCheckFactoryT<QgsGeometryContainedCheck>::checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, QgsWkbTypes::GeometryType geomType ) const
template<> bool QgsGeometryCheckFactoryT<QgsGeometryContainedCheck>::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<QgsGeometryContainedCheck>::createInstance( QgsFeaturePool *featurePool, const Ui::QgsGeometryCheckerSetupTab &ui, double /*mapToLayerUnits*/ ) const
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryContainedCheck>::createInstance( const QMap<QString, QgsFeaturePool *> &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<QgsGeometryDegeneratePolygonCheck>::res
ui.checkBoxDegeneratePolygon->setChecked( QgsSettings().value( sSettingsGroup + "checkDegeneratePolygon" ).toBool() );
}
template<> bool QgsGeometryCheckFactoryT<QgsGeometryDegeneratePolygonCheck>::checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, QgsWkbTypes::GeometryType geomType ) const
template<> bool QgsGeometryCheckFactoryT<QgsGeometryDegeneratePolygonCheck>::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<QgsGeometryDegeneratePolygonCheck>::createInstance( QgsFeaturePool *featurePool, const Ui::QgsGeometryCheckerSetupTab &ui, double /*mapToLayerUnits*/ ) const
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryDegeneratePolygonCheck>::createInstance( const QMap<QString, QgsFeaturePool *> &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<QgsGeometryDuplicateCheck>::restorePrev
ui.checkBoxDuplicates->setChecked( QgsSettings().value( sSettingsGroup + "checkDuplicates" ).toBool() );
}
template<> bool QgsGeometryCheckFactoryT<QgsGeometryDuplicateCheck>::checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, QgsWkbTypes::GeometryType geomType ) const
template<> bool QgsGeometryCheckFactoryT<QgsGeometryDuplicateCheck>::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<QgsGeometryDuplicateCheck>::createInstance( QgsFeaturePool *featurePool, const Ui::QgsGeometryCheckerSetupTab &ui, double /*mapToLayerUnits*/ ) const
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryDuplicateCheck>::createInstance( const QMap<QString, QgsFeaturePool *> &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<QgsGeometryDuplicateNodesCheck>::restor
ui.checkBoxDuplicateNodes->setChecked( QgsSettings().value( sSettingsGroup + "checkDuplicateNodes" ).toBool() );
}
template<> bool QgsGeometryCheckFactoryT<QgsGeometryDuplicateNodesCheck>::checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, QgsWkbTypes::GeometryType geomType ) const
template<> bool QgsGeometryCheckFactoryT<QgsGeometryDuplicateNodesCheck>::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<QgsGeometryDuplicateNodesCheck>::createInstance( QgsFeaturePool *featurePool, const Ui::QgsGeometryCheckerSetupTab &ui, double /*mapToLayerUnits*/ ) const
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryDuplicateNodesCheck>::createInstance( const QMap<QString, QgsFeaturePool *> &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<QgsGeometryGapCheck>::restorePrevious(
ui.doubleSpinBoxGapArea->setValue( QgsSettings().value( sSettingsGroup + "maxGapArea" ).toDouble() );
}
template<> bool QgsGeometryCheckFactoryT<QgsGeometryGapCheck>::checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, QgsWkbTypes::GeometryType geomType ) const
template<> bool QgsGeometryCheckFactoryT<QgsGeometryGapCheck>::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<QgsGeometryGapCheck>::createInstance( QgsFeaturePool *featurePool, const Ui::QgsGeometryCheckerSetupTab &ui, double mapToLayerUnits ) const
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryGapCheck>::createInstance( const QMap<QString, QgsFeaturePool *> &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<QgsGeometryHoleCheck>::restorePrevious(
ui.checkBoxNoHoles->setChecked( QgsSettings().value( sSettingsGroup + "checkHoles" ).toBool() );
}
template<> bool QgsGeometryCheckFactoryT<QgsGeometryHoleCheck>::checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, QgsWkbTypes::GeometryType geomType ) const
template<> bool QgsGeometryCheckFactoryT<QgsGeometryHoleCheck>::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<QgsGeometryHoleCheck>::createInstance( QgsFeaturePool *featurePool, const Ui::QgsGeometryCheckerSetupTab &ui, double /*mapToLayerUnits*/ ) const
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryHoleCheck>::createInstance( const QMap<QString, QgsFeaturePool *> &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<QgsGeometryMultipartCheck>::restorePrev
ui.checkBoxMultipart->setChecked( QgsSettings().value( sSettingsGroup + "checkMultipart" ).toBool() );
}
template<> bool QgsGeometryCheckFactoryT<QgsGeometryMultipartCheck>::checkApplicability( Ui::QgsGeometryCheckerSetupTab & /*ui*/, QgsWkbTypes::GeometryType /*geomType*/ ) const
template<> bool QgsGeometryCheckFactoryT<QgsGeometryMultipartCheck>::checkApplicability( Ui::QgsGeometryCheckerSetupTab & /*ui*/, int /*nPoint*/, int /*nLineString*/, int /*nPolygon*/ ) const
{
return true;
}
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryMultipartCheck>::createInstance( QgsFeaturePool *featurePool, const Ui::QgsGeometryCheckerSetupTab &ui, double /*mapToLayerUnits*/ ) const
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryMultipartCheck>::createInstance( const QMap<QString, QgsFeaturePool *> &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<QgsGeometryOverlapCheck>::restorePrevio
ui.doubleSpinBoxOverlapArea->setValue( QgsSettings().value( sSettingsGroup + "maxOverlapArea" ).toDouble() );
}
template<> bool QgsGeometryCheckFactoryT<QgsGeometryOverlapCheck>::checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, QgsWkbTypes::GeometryType geomType ) const
template<> bool QgsGeometryCheckFactoryT<QgsGeometryOverlapCheck>::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<QgsGeometryOverlapCheck>::createInstance( QgsFeaturePool *featurePool, const Ui::QgsGeometryCheckerSetupTab &ui, double mapToLayerUnits ) const
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryOverlapCheck>::createInstance( const QMap<QString, QgsFeaturePool *> &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<QgsGeometrySegmentLengthCheck>::restore
ui.doubleSpinBoxSegmentLength->setValue( QgsSettings().value( sSettingsGroup + "minSegmentLength" ).toDouble() );
}
template<> bool QgsGeometryCheckFactoryT<QgsGeometrySegmentLengthCheck>::checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, QgsWkbTypes::GeometryType geomType ) const
template<> bool QgsGeometryCheckFactoryT<QgsGeometrySegmentLengthCheck>::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<QgsGeometrySegmentLengthCheck>::createInstance( QgsFeaturePool *featurePool, const Ui::QgsGeometryCheckerSetupTab &ui, double mapToLayerUnits ) const
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometrySegmentLengthCheck>::createInstance( const QMap<QString, QgsFeaturePool *> &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<QgsGeometrySelfContactCheck>::restorePr
ui.checkBoxSelfContacts->setChecked( QgsSettings().value( sSettingsGroup + "checkSelfContacts" ).toBool() );
}
template<> bool QgsGeometryCheckFactoryT<QgsGeometrySelfContactCheck>::checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, QgsWkbTypes::GeometryType geomType ) const
template<> bool QgsGeometryCheckFactoryT<QgsGeometrySelfContactCheck>::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<QgsGeometrySelfContactCheck>::createInstance( QgsFeaturePool *featurePool, const Ui::QgsGeometryCheckerSetupTab &ui, double /*mapToLayerUnits*/ ) const
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometrySelfContactCheck>::createInstance( const QMap<QString, QgsFeaturePool *> &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<QgsGeometrySelfIntersectionCheck>::rest
ui.checkBoxSelfIntersections->setChecked( QgsSettings().value( sSettingsGroup + "checkSelfIntersections" ).toBool() );
}
template<> bool QgsGeometryCheckFactoryT<QgsGeometrySelfIntersectionCheck>::checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, QgsWkbTypes::GeometryType geomType ) const
template<> bool QgsGeometryCheckFactoryT<QgsGeometrySelfIntersectionCheck>::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<QgsGeometrySelfIntersectionCheck>::createInstance( QgsFeaturePool *featurePool, const Ui::QgsGeometryCheckerSetupTab &ui, double /*mapToLayerUnits*/ ) const
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometrySelfIntersectionCheck>::createInstance( const QMap<QString, QgsFeaturePool *> &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<QgsGeometrySliverPolygonCheck>::restore
ui.checkBoxSliverPolygons->setChecked( QgsSettings().value( sSettingsGroup + "checkSliverPolygons" ).toBool() );
}
template<> bool QgsGeometryCheckFactoryT<QgsGeometrySliverPolygonCheck>::checkApplicability( Ui::QgsGeometryCheckerSetupTab &ui, QgsWkbTypes::GeometryType geomType ) const
template<> bool QgsGeometryCheckFactoryT<QgsGeometrySliverPolygonCheck>::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<QgsGeometrySliverPolygonCheck>::createInstance( QgsFeaturePool *featurePool, const Ui::QgsGeometryCheckerSetupTab &ui, double mapToLayerUnits ) const
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometrySliverPolygonCheck>::createInstance( const QMap<QString, QgsFeaturePool *> &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 *QgsGeometryCheckFactoryT<QgsGeometrySliverPolygonCh
QgsSettings().setValue( sSettingsGroup + "checkSliverPolygons", ui.checkBoxSliverPolygons->isChecked() );
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<QgsGeometryTypeCheck>::restorePrevious(
ui.checkBoxMultipolygon->setChecked( QgsSettings().value( sSettingsGroup + "checkTypeMultipolygon" ).toBool() );
}
template<> bool QgsGeometryCheckFactoryT<QgsGeometryTypeCheck>::checkApplicability( Ui::QgsGeometryCheckerSetupTab & /*ui*/, QgsWkbTypes::GeometryType /*geomType*/ ) const
template<> bool QgsGeometryCheckFactoryT<QgsGeometryTypeCheck>::checkApplicability( Ui::QgsGeometryCheckerSetupTab & /*ui*/, int /*nPoint*/, int /*nLineString*/, int /*nPolygon*/ ) const
{
return true;
}
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryTypeCheck>::createInstance( QgsFeaturePool *featurePool, const Ui::QgsGeometryCheckerSetupTab &ui, double /*mapToLayerUnits*/ ) const
template<> QgsGeometryCheck *QgsGeometryCheckFactoryT<QgsGeometryTypeCheck>::createInstance( const QMap<QString, QgsFeaturePool *> &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<QgsGeometryTypeCheck>::cre
}
if ( allowedTypes != 0 )
{
return new QgsGeometryTypeCheck( featurePool, allowedTypes );
return new QgsGeometryTypeCheck( featurePools, allowedTypes );
}
else
{

View File

@ -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<QString, QgsFeaturePool *> &featurePools, const Ui::QgsGeometryCheckerSetupTab &ui ) const = 0;
protected:
static QString sSettingsGroup;
@ -38,8 +37,8 @@ template<class T>
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<QString, QgsFeaturePool *> &featurePools, const Ui::QgsGeometryCheckerSetupTab &ui ) const override;
};
class QgsGeometryCheckFactoryRegistry

View File

@ -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<QString, QgsFeaturePool *> 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 );
}

View File

@ -21,6 +21,7 @@
#include <QDialog>
#include <QDialogButtonBox>
#include <QMap>
#include <QTabWidget>
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<QString, QgsFeaturePool *> featurePools );
void onCheckerFinished( bool successful );
void showHelp();
};

View File

@ -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<QString, QgsFeaturePool *> &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<QItemSelectionModel *>( QObject::sender() )->model();
Q_FOREACH ( QTableWidget *table, QList<QTableWidget *>() << ui.tableWidgetFixedErrors << ui.tableWidgetNewErrors << ui.tableWidgetNotFixed << ui.tableWidgetObsoleteErrors )
for ( QTableWidget *table : QList<QTableWidget *>() << ui.tableWidgetFixedErrors << ui.tableWidgetNewErrors << ui.tableWidgetNotFixed << ui.tableWidgetObsoleteErrors )
if ( table->model() != model )
{
table->selectionModel()->blockSignals( true );

View File

@ -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<QString, QgsFeaturePool *> &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<QString, QgsFeaturePool *> mFeaturePools;
void addError( QTableWidget *table, QgsGeometryCheckError *error );
void setupTable( QTableWidget *table );

View File

@ -14,6 +14,7 @@
* *
***************************************************************************/
#include <QComboBox>
#include <QFileDialog>
#include <QGroupBox>
#include <QMessageBox>
@ -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<QString, QgsFeaturePool *> &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<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), checker, &QgsGeometryChecker::setMergeAttributeIndex );
connect( ui.tableWidgetErrors->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsGeometryCheckerResultTab::onSelectionChanged );
connect( ui.buttonGroupSelectAction, static_cast<void ( QButtonGroup::* )( int )>( &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<void ( QgsProject::* )( const QStringList & )>( &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<QString, QString> > 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<QgsGeometryCheckError *>();
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<QgsVectorLayer *>( maplayer ) &&
static_cast<QgsVectorLayer *>( 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<QgsGeometryCheckError *>();
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<int> ids;
Q_FOREACH ( QModelIndex idx, ui.tableWidgetErrors->selectionModel()->selectedRows() )
QMap<QString, QSet<QgsFeatureId>> ids;
for ( QModelIndex idx : ui.tableWidgetErrors->selectionModel()->selectedRows() )
{
QgsFeatureId id = ui.tableWidgetErrors->item( idx.row(), 0 )->data( Qt::UserRole ).value<QgsGeometryCheckError *>()->featureId();
QgsGeometryCheckError *error = ui.tableWidgetErrors->item( idx.row(), 0 )->data( Qt::UserRole ).value<QgsGeometryCheckError *>();
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<QgsGeometryCheckError *> errors;
Q_FOREACH ( const QModelIndex &index, rows )
for ( const QModelIndex &index : rows )
{
QgsGeometryCheckError *error = ui.tableWidgetErrors->item( index.row(), 0 )->data( Qt::UserRole ).value<QgsGeometryCheckError *>();
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<int>( 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<int>( 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<QString, int> mergeAttribIndices;
QTreeWidgetItemIterator it( ui.treeWidgetMergeAttribute );
while ( *it )
{
QTreeWidgetItem *item = *it;
QComboBox *combo = qobject_cast<QComboBox *>( ui.treeWidgetMergeAttribute->itemWidget( item, 1 ) );
mergeAttribIndices.insert( item->text( 0 ), combo->currentIndex() );
++it;
}
mChecker->setMergeAttributeIndices( mergeAttribIndices );
}

View File

@ -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<QString, QgsFeaturePool *> &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<QString, QgsFeaturePool *> mFeaturePools;
QList<QgsRubberBand *> mCurrentRubberBands;
QMap<QgsGeometryCheckError *, QPersistentModelIndex> mErrorMap;
QDialog *mAttribTableDialog = nullptr;
QMap<QString, QPointer<QDialog>> 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

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>587</width>
<height>394</height>
<height>548</height>
</rect>
</property>
<property name="windowTitle">
@ -39,8 +39,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>571</width>
<height>395</height>
<width>587</width>
<height>548</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout_3">
@ -51,107 +51,6 @@
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QTableWidget" name="tableWidgetErrors">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<attribute name="horizontalHeaderShowSortIndicator" stdset="0">
<bool>true</bool>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string>Object ID</string>
</property>
</column>
<column>
<property name="text">
<string>Error</string>
</property>
</column>
<column>
<property name="text">
<string>Coordinates</string>
</property>
</column>
<column>
<property name="text">
<string>Value</string>
</property>
</column>
<column>
<property name="text">
<string>Resolution</string>
</property>
</column>
</widget>
</item>
<item row="2" column="0">
<widget class="QWidget" name="widget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="pushButtonExport">
<property name="text">
<string>Export</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>280</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="labelErrorCount">
<property name="text">
<string>Total errors: 0</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="3" column="0">
<widget class="QGroupBox" name="groupBoxRowSelectionBehavior">
<property name="title">
@ -225,27 +124,6 @@
</layout>
</widget>
</item>
<item row="4" column="0">
<widget class="QWidget" name="widgetRowSelectionBehavior" native="true">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
</layout>
</widget>
</item>
<item row="5" column="0">
<widget class="QWidget" name="widgetFix" native="true">
<property name="enabled">
@ -389,8 +267,141 @@
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="0">
<widget class="QTableWidget" name="tableWidgetErrors">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<attribute name="horizontalHeaderShowSortIndicator" stdset="0">
<bool>true</bool>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string>Object ID</string>
</property>
</column>
<column>
<property name="text">
<string>Error</string>
</property>
</column>
<column>
<property name="text">
<string>Coordinates</string>
</property>
</column>
<column>
<property name="text">
<string>Value</string>
</property>
</column>
<column>
<property name="text">
<string>Resolution</string>
</property>
</column>
</widget>
</item>
<item row="7" column="0">
<widget class="QTreeWidget" name="treeWidgetMergeAttribute">
<column>
<property name="text">
<string>Layer</string>
</property>
</column>
<column>
<property name="text">
<string>Attribute</string>
</property>
</column>
</widget>
</item>
<item row="4" column="0">
<widget class="QWidget" name="widgetRowSelectionBehavior" native="true">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
</layout>
</widget>
</item>
<item row="2" column="0">
<widget class="QWidget" name="widget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QComboBox" name="comboBoxMergeAttribute"/>
<widget class="QPushButton" name="pushButtonExport">
<property name="text">
<string>Export</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>280</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="labelErrorCount">
<property name="text">
<string>Total errors: 0</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
@ -421,7 +432,6 @@
<tabstop>pushButtonFixWithDefault</tabstop>
<tabstop>pushButtonFixWithPrompt</tabstop>
<tabstop>pushButtonErrorResolutionSettings</tabstop>
<tabstop>comboBoxMergeAttribute</tabstop>
</tabstops>
<resources>
<include location="../../../../images/images.qrc"/>

View File

@ -38,6 +38,7 @@
#include <QPushButton>
#include <QtConcurrentMap>
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<QString, QString> 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<void ( QComboBox::* )( int )>( &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<void ( QgsProject::* )( const QStringList & )>( &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<void ( QButtonGroup::* )( int )>( &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<QgsVectorLayer *>() )
for ( QgsVectorLayer *layer : QgsProject::instance()->layers<QgsVectorLayer *>() )
{
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<QgsVectorLayer *> QgsGeometryCheckerSetupTab::getSelectedLayers()
{
int inputIdx = ui.comboBoxInputLayer->currentIndex();
if ( inputIdx < 0 )
return nullptr;
QgsVectorLayer *layer = dynamic_cast<QgsVectorLayer *>( ui.comboBoxInputLayer->currentLayer() );
return layer;
QList<QgsVectorLayer *> 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<QgsVectorLayer *>( QgsProject::instance()->mapLayer( layerId ) );
if ( layer )
{
layers.append( layer );
}
}
return layers;
}
void QgsGeometryCheckerSetupTab::validateInput()
{
QgsVectorLayer *layer = getSelectedLayer();
QList<QgsVectorLayer *> 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<QString, QString> 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<QgsVectorLayer *> 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<QgsVectorLayer *> 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( "<b>Preparing output...</b>" ) );
ui.labelStatus->show();
QApplication::processEvents( QEventLoop::ExcludeUserInputEvents );
//! Duplicate if necessary *
QList<QgsVectorLayer *> 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<QgsVectorLayer *>( maplayer ) &&
static_cast<QgsVectorLayer *>( 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<QgsVectorLayer *>() )
{
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<QgsVectorLayer *> 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( "<b>Building spatial index...</b>" ) );
QApplication::processEvents( QEventLoop::ExcludeUserInputEvents );
QgsFeaturePool *featurePool = new QgsFeaturePool( layer, selectedOnly );
QMap<QString, QgsFeaturePool *> featurePools;
for ( QgsVectorLayer *layer : processLayers )
{
double mapToLayerUnits = 1. / mIface->mapCanvas()->mapSettings().layerToMapUnits( layer );
featurePools.insert( layer->id(), new QgsFeaturePool( layer, mapToLayerUnits, selectedOnly ) );
}
QList<QgsGeometryCheck *> 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<QgsMapLayer *>() << layer );
QList<QgsMapLayer *> 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( "<b>Waiting for running checks to finish...</b>" ) );
ui.labelStatus->show();
ui.progressBar->hide();
ui.progressBar->hide() ;
}

View File

@ -35,7 +35,7 @@ class QgsGeometryCheckerSetupTab : public QWidget
~QgsGeometryCheckerSetupTab();
signals:
void checkerStarted( QgsGeometryChecker *checker, QgsFeaturePool *featurePool );
void checkerStarted( QgsGeometryChecker *checker, QMap<QString, QgsFeaturePool *> 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<QgsVectorLayer *> getSelectedLayers();
private slots:
void runChecks();

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>640</width>
<height>491</height>
<height>726</height>
</rect>
</property>
<property name="windowTitle">
@ -41,9 +41,9 @@
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<y>-46</y>
<width>626</width>
<height>726</height>
<height>804</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout_4">
@ -66,9 +66,9 @@
<number>2</number>
</property>
<item>
<widget class="QGroupBox" name="groupBoxInputLayer">
<widget class="QGroupBox" name="groupBoxInputLayers">
<property name="title">
<string>Input vector layer</string>
<string>Input vector layers</string>
</property>
<property name="flat">
<bool>true</bool>
@ -90,7 +90,7 @@
<number>2</number>
</property>
<item>
<widget class="QgsMapLayerComboBox" name="comboBoxInputLayer"/>
<widget class="QListWidget" name="listWidgetInputLayers"/>
</item>
<item>
<widget class="QCheckBox" name="checkBoxInputSelectedOnly">
@ -598,9 +598,9 @@
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBoxOutputLayer">
<widget class="QGroupBox" name="groupBoxOutputLayers">
<property name="title">
<string>Output vector layer</string>
<string>Output vector layers</string>
</property>
<property name="flat">
<bool>true</bool>
@ -621,20 +621,10 @@
<property name="spacing">
<number>2</number>
</property>
<item row="0" column="0">
<widget class="QRadioButton" name="radioButtonOutputModifyInput">
<property name="text">
<string>&amp;Modify input layer</string>
</property>
<attribute name="buttonGroup">
<string notr="true">buttonGroupOutput</string>
</attribute>
</widget>
</item>
<item row="1" column="0">
<widget class="QRadioButton" name="radioButtonOutputNew">
<property name="text">
<string>Create &amp;new layer</string>
<string>Create &amp;new layers</string>
</property>
<property name="checked">
<bool>true</bool>
@ -644,32 +634,86 @@
</attribute>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="lineEditOutput">
<property name="readOnly">
<bool>true</bool>
<item row="0" column="0" colspan="3">
<widget class="QRadioButton" name="radioButtonOutputModifyInput">
<property name="text">
<string>&amp;Modify input layers</string>
</property>
<attribute name="buttonGroup">
<string notr="true">buttonGroupOutput</string>
</attribute>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="pushButtonOutputBrowse">
<property name="text">
<string>Browse</string>
<item row="1" column="1">
<widget class="QFrame" name="frameOutput">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QGridLayout" name="gridLayout_9">
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<property name="spacing">
<number>2</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="labelOutputFormat">
<property name="text">
<string>Format:</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="labelOutputDirectory">
<property name="text">
<string>Output directory:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="lineEditOutputDirectory">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="pushButtonOutputDirectory">
<property name="text">
<string>Browse</string>
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QComboBox" name="comboBoxOutputFormat"/>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
<zorder>groupBoxInputLayer</zorder>
<zorder>groupBoxInputLayers</zorder>
<zorder>groupBox_2</zorder>
<zorder>groupBoxGeometryTypes</zorder>
<zorder>groupBoxGeometryConditions</zorder>
<zorder>line</zorder>
<zorder>groupBoxGeometryProperties</zorder>
<zorder>groupBoxTopology</zorder>
<zorder>groupBoxOutputLayer</zorder>
<zorder>groupBoxOutputLayers</zorder>
<zorder>line_2</zorder>
<zorder>line_3</zorder>
</widget>
@ -729,15 +773,9 @@
<header>qgsscrollarea.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QgsMapLayerComboBox</class>
<extends>QComboBox</extends>
<header location="global">qgsmaplayercombobox.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>scrollArea</tabstop>
<tabstop>comboBoxInputLayer</tabstop>
<tabstop>checkBoxInputSelectedOnly</tabstop>
<tabstop>checkBoxSelfIntersections</tabstop>
<tabstop>checkBoxDuplicateNodes</tabstop>
@ -768,8 +806,6 @@
<tabstop>spinBoxTolerance</tabstop>
<tabstop>radioButtonOutputModifyInput</tabstop>
<tabstop>radioButtonOutputNew</tabstop>
<tabstop>lineEditOutput</tabstop>
<tabstop>pushButtonOutputBrowse</tabstop>
</tabstops>
<resources>
<include location="../pluginres.qrc"/>

View File

@ -33,12 +33,10 @@
#include <QGridLayout>
QgsGeometryCheckerFixDialog::QgsGeometryCheckerFixDialog( QgsGeometryChecker *checker,
const QList<QgsGeometryCheckError *> &errors,
QgisInterface *iface, QWidget *parent )
const QList<QgsGeometryCheckError *> &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<int>( 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();

View File

@ -32,7 +32,7 @@ class QgsGeometryCheckerFixDialog : public QDialog
{
Q_OBJECT
public:
QgsGeometryCheckerFixDialog( QgsGeometryChecker *checker, const QList<QgsGeometryCheckError *> &errors, QgisInterface *iface, QWidget *parent = nullptr );
QgsGeometryCheckerFixDialog( QgsGeometryChecker *checker, const QList<QgsGeometryCheckError *> &errors, QWidget *parent = nullptr );
signals:
void currentErrorChanged( QgsGeometryCheckError *error );
@ -40,7 +40,6 @@ class QgsGeometryCheckerFixDialog : public QDialog
private:
QgsGeometryChecker *mChecker = nullptr;
QList<QgsGeometryCheckError *> mErrors;
QgisInterface *mIface = nullptr;
QGroupBox *mResolutionsBox = nullptr;
QDialogButtonBox *mButtonBox = nullptr;
QLabel *mStatusLabel = nullptr;

View File

@ -25,9 +25,10 @@
#include <QMutexLocker>
#include <limits>
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 )

View File

@ -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 );