mirror of
https://github.com/qgis/QGIS.git
synced 2025-03-28 00:04:04 -04:00
[Geometry checker] Add multi-layer support to gap check
This commit is contained in:
parent
23affe4fae
commit
dd12b132c5
@ -24,6 +24,8 @@ void QgsGeometryGapCheck::collectErrors( QList<QgsGeometryCheckError *> &errors,
|
|||||||
{
|
{
|
||||||
if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 );
|
if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 );
|
||||||
|
|
||||||
|
QList<QgsAbstractGeometry *> geomList;
|
||||||
|
|
||||||
QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids;
|
QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids;
|
||||||
// Collect geometries, build spatial index
|
// Collect geometries, build spatial index
|
||||||
for ( const QString &layerId : featureIds.keys() )
|
for ( const QString &layerId : featureIds.keys() )
|
||||||
@ -34,15 +36,15 @@ void QgsGeometryGapCheck::collectErrors( QList<QgsGeometryCheckError *> &errors,
|
|||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
double mapToLayerUnits = featurePool->getMapToLayerUnits();
|
|
||||||
double gapAreaThreshold = mThresholdMapUnits * mapToLayerUnits * mapToLayerUnits;
|
|
||||||
QList<QgsAbstractGeometry *> geomList;
|
|
||||||
for ( QgsFeatureId id : featureIds[layerId] )
|
for ( QgsFeatureId id : featureIds[layerId] )
|
||||||
{
|
{
|
||||||
QgsFeature feature;
|
QgsFeature feature;
|
||||||
if ( featurePool->get( id, feature ) )
|
if ( featurePool->get( id, feature ) )
|
||||||
{
|
{
|
||||||
geomList.append( feature.geometry().geometry()->clone() );
|
QgsAbstractGeometry *geometry = feature.geometry().geometry()->clone();
|
||||||
|
geometry->transform( t );
|
||||||
|
geomList.append( geometry );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,23 +99,32 @@ void QgsGeometryGapCheck::collectErrors( QList<QgsGeometryCheckError *> &errors,
|
|||||||
// For each gap polygon which does not lie on the boundary, get neighboring polygons and add error
|
// 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 )
|
for ( int iPart = 0, nParts = diffGeom->partCount(); iPart < nParts; ++iPart )
|
||||||
{
|
{
|
||||||
QgsAbstractGeometry *geom = QgsGeometryCheckerUtils::getGeomPart( diffGeom, iPart )->clone();
|
QgsAbstractGeometry *gapGeom = QgsGeometryCheckerUtils::getGeomPart( diffGeom, iPart )->clone();
|
||||||
// Skip the gap between features and boundingbox
|
// Skip the gap between features and boundingbox
|
||||||
if ( geom->boundingBox() == envelope->boundingBox() )
|
if ( gapGeom->boundingBox() == envelope->boundingBox() )
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip gaps above threshold
|
// Skip gaps above threshold
|
||||||
if ( geom->area() > gapAreaThreshold || geom->area() < mContext->reducedTolerance )
|
if ( gapGeom->area() > mThresholdMapUnits || gapGeom->area() < mContext->reducedTolerance )
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QgsRectangle gapAreaBBox = gapGeom->boundingBox();
|
||||||
|
|
||||||
// Get neighboring polygons
|
// Get neighboring polygons
|
||||||
QgsFeatureIds neighboringIds;
|
QMap<QString, QgsFeatureIds> neighboringIds;
|
||||||
QgsRectangle gapAreaBBox = t.transform( geom->boundingBox() );
|
for ( const QString &layerId : featureIds.keys() )
|
||||||
QgsFeatureIds intersectIds = featurePool->getIntersects( geom->boundingBox() );
|
{
|
||||||
|
QgsFeaturePool *featurePool = mContext->featurePools[ layerId ];
|
||||||
|
QgsCoordinateTransform t = QgsCoordinateTransformCache::instance()->transform( mContext->mapCrs, featurePool->getLayer()->crs().authid() );
|
||||||
|
QgsRectangle gapAreaLayerBBox = t.transform( gapGeom->boundingBox() ); // Don't use gapAreaBBox since it is updated below
|
||||||
|
|
||||||
|
QgsFeatureIds intersectIds = featurePool->getIntersects( gapAreaLayerBBox );
|
||||||
|
QgsAbstractGeometry *gapLayerGeom = gapGeom->clone();
|
||||||
|
gapLayerGeom->transform( t );
|
||||||
|
|
||||||
for ( QgsFeatureId id : intersectIds )
|
for ( QgsFeatureId id : intersectIds )
|
||||||
{
|
{
|
||||||
@ -122,23 +133,24 @@ void QgsGeometryGapCheck::collectErrors( QList<QgsGeometryCheckError *> &errors,
|
|||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
QgsGeometry featureGeom = feature.geometry();
|
QgsAbstractGeometry *featureGeom = feature.geometry().geometry();
|
||||||
QgsAbstractGeometry *geom2 = featureGeom.geometry();
|
if ( QgsGeometryCheckerUtils::sharedEdgeLength( gapLayerGeom, featureGeom, mContext->reducedTolerance ) > 0 )
|
||||||
if ( QgsGeometryCheckerUtils::sharedEdgeLength( geom, geom2, mContext->reducedTolerance ) > 0 )
|
|
||||||
{
|
{
|
||||||
neighboringIds.insert( feature.id() );
|
neighboringIds[layerId].insert( feature.id() );
|
||||||
gapAreaBBox.unionRect( t.transform( geom2->boundingBox() ) );
|
gapAreaLayerBBox.unionRect( featureGeom->boundingBox() );
|
||||||
}
|
}
|
||||||
|
gapAreaBBox.unionRect( t.transform( gapAreaLayerBBox, QgsCoordinateTransform::ReverseTransform ) );
|
||||||
|
}
|
||||||
|
delete gapLayerGeom;
|
||||||
}
|
}
|
||||||
if ( neighboringIds.isEmpty() )
|
if ( neighboringIds.isEmpty() )
|
||||||
{
|
{
|
||||||
delete geom;
|
delete gapGeom;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add error
|
// Add error
|
||||||
errors.append( new QgsGeometryGapCheckError( this, layerId, geom, neighboringIds, geom->area(), gapAreaBBox ) );
|
errors.append( new QgsGeometryGapCheckError( this, "", gapGeom, neighboringIds, gapGeom->area(), gapAreaBBox ) );
|
||||||
}
|
|
||||||
|
|
||||||
delete unionGeom;
|
delete unionGeom;
|
||||||
delete envelope;
|
delete envelope;
|
||||||
@ -172,15 +184,22 @@ void QgsGeometryGapCheck::fixError( QgsGeometryCheckError *error, int method, co
|
|||||||
|
|
||||||
bool QgsGeometryGapCheck::mergeWithNeighbor( QgsGeometryGapCheckError *err, Changes &changes, QString &errMsg ) const
|
bool QgsGeometryGapCheck::mergeWithNeighbor( QgsGeometryGapCheckError *err, Changes &changes, QString &errMsg ) const
|
||||||
{
|
{
|
||||||
QgsFeaturePool *featurePool = mContext->featurePools[ err->layerId() ];
|
|
||||||
double maxVal = 0.;
|
double maxVal = 0.;
|
||||||
|
QString mergeLayerId;
|
||||||
QgsFeature mergeFeature;
|
QgsFeature mergeFeature;
|
||||||
int mergePartIdx = -1;
|
int mergePartIdx = -1;
|
||||||
|
|
||||||
QgsAbstractGeometry *errGeometry = QgsGeometryCheckerUtils::getGeomPart( err->geometry(), 0 );
|
QgsAbstractGeometry *errGeometry = QgsGeometryCheckerUtils::getGeomPart( err->geometry(), 0 );
|
||||||
|
|
||||||
// Search for touching neighboring geometries
|
// Search for touching neighboring geometries
|
||||||
for ( QgsFeatureId testId : err->neighbors() )
|
for ( const QString &layerId : err->neighbors().keys() )
|
||||||
|
{
|
||||||
|
QgsFeaturePool *featurePool = mContext->featurePools[ err->layerId() ];
|
||||||
|
QgsCoordinateTransform t = QgsCoordinateTransformCache::instance()->transform( mContext->mapCrs, featurePool->getLayer()->crs().authid() );
|
||||||
|
QgsAbstractGeometry *errLayerGeom = errGeometry->clone();
|
||||||
|
errLayerGeom->transform( t );
|
||||||
|
|
||||||
|
for ( QgsFeatureId testId : err->neighbors()[layerId] )
|
||||||
{
|
{
|
||||||
QgsFeature testFeature;
|
QgsFeature testFeature;
|
||||||
if ( !featurePool->get( testId, testFeature ) )
|
if ( !featurePool->get( testId, testFeature ) )
|
||||||
@ -191,15 +210,18 @@ bool QgsGeometryGapCheck::mergeWithNeighbor( QgsGeometryGapCheckError *err, Chan
|
|||||||
QgsAbstractGeometry *testGeom = featureGeom.geometry();
|
QgsAbstractGeometry *testGeom = featureGeom.geometry();
|
||||||
for ( int iPart = 0, nParts = testGeom->partCount(); iPart < nParts; ++iPart )
|
for ( int iPart = 0, nParts = testGeom->partCount(); iPart < nParts; ++iPart )
|
||||||
{
|
{
|
||||||
double len = QgsGeometryCheckerUtils::sharedEdgeLength( errGeometry, QgsGeometryCheckerUtils::getGeomPart( testGeom, iPart ), mContext->reducedTolerance );
|
double len = QgsGeometryCheckerUtils::sharedEdgeLength( errLayerGeom, QgsGeometryCheckerUtils::getGeomPart( testGeom, iPart ), mContext->reducedTolerance );
|
||||||
if ( len > maxVal )
|
if ( len > maxVal )
|
||||||
{
|
{
|
||||||
maxVal = len;
|
maxVal = len;
|
||||||
mergeFeature = testFeature;
|
mergeFeature = testFeature;
|
||||||
mergePartIdx = iPart;
|
mergePartIdx = iPart;
|
||||||
|
mergeLayerId = layerId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
delete errLayerGeom;
|
||||||
|
}
|
||||||
|
|
||||||
if ( maxVal == 0. )
|
if ( maxVal == 0. )
|
||||||
{
|
{
|
||||||
@ -207,11 +229,16 @@ bool QgsGeometryGapCheck::mergeWithNeighbor( QgsGeometryGapCheckError *err, Chan
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Merge geometries
|
// Merge geometries
|
||||||
|
QgsFeaturePool *featurePool = mContext->featurePools[ mergeLayerId ];
|
||||||
|
QgsCoordinateTransform t = QgsCoordinateTransformCache::instance()->transform( mContext->mapCrs, featurePool->getLayer()->crs().authid() );
|
||||||
|
QgsAbstractGeometry *errLayerGeom = errGeometry->clone();
|
||||||
|
errLayerGeom->transform( t );
|
||||||
QgsGeometry mergeFeatureGeom = mergeFeature.geometry();
|
QgsGeometry mergeFeatureGeom = mergeFeature.geometry();
|
||||||
QgsAbstractGeometry *mergeGeom = mergeFeatureGeom.geometry();
|
QgsAbstractGeometry *mergeGeom = mergeFeatureGeom.geometry();
|
||||||
QgsGeometryEngine *geomEngine = QgsGeometryCheckerUtils::createGeomEngine( errGeometry, mContext->tolerance );
|
QgsGeometryEngine *geomEngine = QgsGeometryCheckerUtils::createGeomEngine( errLayerGeom, mContext->tolerance );
|
||||||
QgsAbstractGeometry *combinedGeom = geomEngine->combine( *QgsGeometryCheckerUtils::getGeomPart( mergeGeom, mergePartIdx ), &errMsg );
|
QgsAbstractGeometry *combinedGeom = geomEngine->combine( *QgsGeometryCheckerUtils::getGeomPart( mergeGeom, mergePartIdx ), &errMsg );
|
||||||
delete geomEngine;
|
delete geomEngine;
|
||||||
|
delete errLayerGeom;
|
||||||
if ( !combinedGeom || combinedGeom->isEmpty() )
|
if ( !combinedGeom || combinedGeom->isEmpty() )
|
||||||
{
|
{
|
||||||
delete combinedGeom;
|
delete combinedGeom;
|
||||||
|
@ -25,7 +25,7 @@ class QgsGeometryGapCheckError : public QgsGeometryCheckError
|
|||||||
QgsGeometryGapCheckError( const QgsGeometryCheck *check,
|
QgsGeometryGapCheckError( const QgsGeometryCheck *check,
|
||||||
const QString &layerId,
|
const QString &layerId,
|
||||||
QgsAbstractGeometry *geometry,
|
QgsAbstractGeometry *geometry,
|
||||||
const QgsFeatureIds &neighbors,
|
const QMap<QString, QgsFeatureIds> &neighbors,
|
||||||
double area,
|
double area,
|
||||||
const QgsRectangle &gapAreaBBox )
|
const QgsRectangle &gapAreaBBox )
|
||||||
: QgsGeometryCheckError( check, layerId, FEATUREID_NULL, geometry->centroid(), QgsVertexId(), area, ValueArea )
|
: QgsGeometryCheckError( check, layerId, FEATUREID_NULL, geometry->centroid(), QgsVertexId(), area, ValueArea )
|
||||||
@ -40,7 +40,7 @@ class QgsGeometryGapCheckError : public QgsGeometryCheckError
|
|||||||
}
|
}
|
||||||
|
|
||||||
QgsAbstractGeometry *geometry() const override { return mGeometry->clone(); }
|
QgsAbstractGeometry *geometry() const override { return mGeometry->clone(); }
|
||||||
const QgsFeatureIds &neighbors() const { return mNeighbors; }
|
const QMap<QString, QgsFeatureIds> &neighbors() const { return mNeighbors; }
|
||||||
|
|
||||||
bool isEqual( QgsGeometryCheckError *other ) const override
|
bool isEqual( QgsGeometryCheckError *other ) const override
|
||||||
{
|
{
|
||||||
@ -76,7 +76,7 @@ class QgsGeometryGapCheckError : public QgsGeometryCheckError
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QgsFeatureIds mNeighbors;
|
QMap<QString, QgsFeatureIds> mNeighbors;
|
||||||
QgsRectangle mGapAreaBBox;
|
QgsRectangle mGapAreaBBox;
|
||||||
QgsAbstractGeometry *mGeometry = nullptr;
|
QgsAbstractGeometry *mGeometry = nullptr;
|
||||||
};
|
};
|
||||||
|
@ -324,7 +324,7 @@ void QgsGeometryCheckerResultTab::highlightErrors( bool current )
|
|||||||
for ( QTableWidgetItem *item : items )
|
for ( QTableWidgetItem *item : items )
|
||||||
{
|
{
|
||||||
QgsGeometryCheckError *error = ui.tableWidgetErrors->item( item->row(), 0 )->data( Qt::UserRole ).value<QgsGeometryCheckError *>();
|
QgsGeometryCheckError *error = ui.tableWidgetErrors->item( item->row(), 0 )->data( Qt::UserRole ).value<QgsGeometryCheckError *>();
|
||||||
QgsVectorLayer *layer = mChecker->getContext()->featurePools[error->layerId()]->getLayer();
|
QgsVectorLayer *layer = !error->layerId().isEmpty() ? mChecker->getContext()->featurePools[error->layerId()]->getLayer() : nullptr;
|
||||||
|
|
||||||
QgsAbstractGeometry *geometry = error->geometry();
|
QgsAbstractGeometry *geometry = error->geometry();
|
||||||
if ( ui.checkBoxHighlight->isChecked() && geometry )
|
if ( ui.checkBoxHighlight->isChecked() && geometry )
|
||||||
|
Loading…
x
Reference in New Issue
Block a user