mirror of
https://github.com/qgis/QGIS.git
synced 2025-03-23 00:05:43 -04:00
[Geometry checker] Initial multi-layer support
This commit is contained in:
parent
e3fc73f41e
commit
31cc65df49
@ -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
|
||||
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
@ -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" ); }
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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" ); }
|
||||
|
@ -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
|
||||
|
@ -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" ); }
|
||||
|
@ -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;
|
||||
|
@ -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" ); }
|
||||
|
@ -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
|
||||
|
@ -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" ); }
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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" ); }
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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" ); }
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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 )
|
||||
{
|
||||
|
@ -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" ); }
|
||||
|
@ -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
|
||||
|
@ -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" ); }
|
||||
|
@ -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
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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" ); }
|
||||
|
@ -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 );
|
||||
}
|
||||
|
@ -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 );
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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 );
|
||||
}
|
||||
|
@ -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();
|
||||
};
|
||||
|
@ -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 );
|
||||
|
@ -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 );
|
||||
|
@ -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 );
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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"/>
|
||||
|
@ -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() ;
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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>&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 &new layer</string>
|
||||
<string>Create &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>&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"/>
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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 )
|
||||
|
@ -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 );
|
||||
|
Loading…
x
Reference in New Issue
Block a user