void QgsGeometryOverlapCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const { QString errMsg; QgsGeometryOverlapCheckError *overlapError = static_cast<QgsGeometryOverlapCheckError *>( error ); QgsFeaturePool *featurePoolA = mContext->featurePools[ overlapError->layerId() ]; QgsFeaturePool *featurePoolB = mContext->featurePools[ overlapError->overlappedFeature().first ]; QgsFeature featureA; QgsFeature featureB; if ( !featurePoolA->get( overlapError->featureId(), featureA ) || !featurePoolB->get( overlapError->overlappedFeature().second, featureB ) ) { error->setObsolete(); return; } // Check if error still applies QgsGeometryCheckerUtils::LayerFeature layerFeatureA( featurePoolA, featureA, true ); QgsGeometryCheckerUtils::LayerFeature layerFeatureB( featurePoolB, featureB, true ); std::unique_ptr< QgsGeometryEngine > geomEngineA = QgsGeometryCheckerUtils::createGeomEngine( layerFeatureA.geometry(), mContext->reducedTolerance ); if ( !geomEngineA->overlaps( layerFeatureB.geometry() ) ) { error->setObsolete(); return; } std::unique_ptr< QgsAbstractGeometry > interGeom( geomEngineA->intersection( layerFeatureB.geometry(), &errMsg ) ); if ( !interGeom ) { error->setFixFailed( tr( "Failed to compute intersection between overlapping features: %1" ).arg( errMsg ) ); return; } // Search which overlap part this error parametrizes (using fuzzy-matching of the area and centroid...) QgsAbstractGeometry *interPart = nullptr; for ( int iPart = 0, nParts = interGeom->partCount(); iPart < nParts; ++iPart ) { QgsAbstractGeometry *part = QgsGeometryCheckerUtils::getGeomPart( interGeom.get(), iPart ); if ( std::fabs( part->area() - overlapError->value().toDouble() ) < mContext->reducedTolerance && QgsGeometryCheckerUtils::pointsFuzzyEqual( part->centroid(), overlapError->location(), mContext->reducedTolerance ) ) { interPart = part; break; } } if ( !interPart || interPart->isEmpty() ) { error->setObsolete(); return; } // Fix error if ( method == NoChange ) { error->setFixed( method ); } else if ( method == Subtract ) { std::unique_ptr< QgsAbstractGeometry > diff1( geomEngineA->difference( interPart, &errMsg ) ); if ( !diff1 || diff1->isEmpty() ) { diff1.reset(); } else { QgsGeometryCheckerUtils::filter1DTypes( diff1.get() ); } std::unique_ptr< QgsGeometryEngine > geomEngineB = QgsGeometryCheckerUtils::createGeomEngine( layerFeatureB.geometry(), mContext->reducedTolerance ); std::unique_ptr< QgsAbstractGeometry > diff2( geomEngineB->difference( interPart, &errMsg ) ); if ( !diff2 || diff2->isEmpty() ) { diff2.reset(); } else { QgsGeometryCheckerUtils::filter1DTypes( diff2.get() ); } double shared1 = diff1 ? QgsGeometryCheckerUtils::sharedEdgeLength( diff1.get(), interPart, mContext->reducedTolerance ) : 0; double shared2 = diff2 ? QgsGeometryCheckerUtils::sharedEdgeLength( diff2.get(), interPart, mContext->reducedTolerance ) : 0; if ( !diff1 || !diff2 || shared1 == 0. || shared2 == 0. ) { error->setFixFailed( tr( "Could not find shared edges between intersection and overlapping features" ) ); } else { if ( shared1 < shared2 ) { diff1->transform( featurePoolA->getLayerToMapTransform(), QgsCoordinateTransform::ReverseTransform ); featureA.setGeometry( QgsGeometry( std::move( diff1 ) ) ); changes[error->layerId()][featureA.id()].append( Change( ChangeFeature, ChangeChanged ) ); featurePoolA->updateFeature( featureA ); } else { diff2->transform( featurePoolB->getLayerToMapTransform(), QgsCoordinateTransform::ReverseTransform ); featureB.setGeometry( QgsGeometry( std::move( diff2 ) ) ); changes[overlapError->overlappedFeature().first][featureB.id()].append( Change( ChangeFeature, ChangeChanged ) ); featurePoolB->updateFeature( featureB ); } error->setFixed( method ); } } else { error->setFixFailed( tr( "Unknown method" ) ); } }
bool QgsGeometryGapCheck::mergeWithNeighbor( QgsGeometryGapCheckError *err, Changes &changes, QString &errMsg ) const { double maxVal = 0.; QString mergeLayerId; QgsFeature mergeFeature; int mergePartIdx = -1; const QgsAbstractGeometry *errGeometry = QgsGeometryCheckerUtils::getGeomPart( err->geometry(), 0 ); // Search for touching neighboring geometries for ( const QString &layerId : err->neighbors().keys() ) { QgsFeaturePool *featurePool = mContext->featurePools[ layerId ]; QgsAbstractGeometry *errLayerGeom = errGeometry->clone(); errLayerGeom->transform( featurePool->getLayerToMapTransform(), QgsCoordinateTransform::ReverseTransform ); for ( QgsFeatureId testId : err->neighbors()[layerId] ) { QgsFeature testFeature; if ( !featurePool->get( testId, testFeature ) ) { continue; } QgsGeometry featureGeom = testFeature.geometry(); const QgsAbstractGeometry *testGeom = featureGeom.constGet(); for ( int iPart = 0, nParts = testGeom->partCount(); iPart < nParts; ++iPart ) { double len = QgsGeometryCheckerUtils::sharedEdgeLength( errLayerGeom, QgsGeometryCheckerUtils::getGeomPart( testGeom, iPart ), mContext->reducedTolerance ); if ( len > maxVal ) { maxVal = len; mergeFeature = testFeature; mergePartIdx = iPart; mergeLayerId = layerId; } } } delete errLayerGeom; } if ( maxVal == 0. ) { return false; } // Merge geometries QgsFeaturePool *featurePool = mContext->featurePools[ mergeLayerId ]; QgsAbstractGeometry *errLayerGeom = errGeometry->clone(); errLayerGeom->transform( featurePool->getLayerToMapTransform(), QgsCoordinateTransform::ReverseTransform ); QgsGeometry mergeFeatureGeom = mergeFeature.geometry(); const QgsAbstractGeometry *mergeGeom = mergeFeatureGeom.constGet(); std::unique_ptr< QgsGeometryEngine > geomEngine = QgsGeometryCheckerUtils::createGeomEngine( errLayerGeom, mContext->reducedTolerance ); QgsAbstractGeometry *combinedGeom = geomEngine->combine( QgsGeometryCheckerUtils::getGeomPart( mergeGeom, mergePartIdx ), &errMsg ); delete errLayerGeom; if ( !combinedGeom || combinedGeom->isEmpty() || !QgsWkbTypes::isSingleType( combinedGeom->wkbType() ) ) { delete combinedGeom; return false; } // Add merged polygon to destination geometry replaceFeatureGeometryPart( mergeLayerId, mergeFeature, mergePartIdx, combinedGeom, changes ); return true; }