void QgsGeometryDuplicateNodesCheck::collectErrors( QList<QgsGeometryCheckError*>& errors, QStringList &/*messages*/, QAtomicInt* progressCounter , const QgsFeatureIds &ids ) const { const QgsFeatureIds& featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids; Q_FOREACH ( QgsFeatureId featureid, featureIds ) { if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 ); QgsFeature feature; if ( !mFeaturePool->get( featureid, feature ) ) { continue; } QgsAbstractGeometryV2* geom = feature.geometry()->geometry(); for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart ) { for ( int iRing = 0, nRings = geom->ringCount( iPart ); iRing < nRings; ++iRing ) { int nVerts = QgsGeomUtils::polyLineSize( geom, iPart, iRing ); if ( nVerts < 2 ) continue; for ( int iVert = nVerts - 1, jVert = 0; jVert < nVerts; iVert = jVert++ ) { QgsPointV2 pi = geom->vertexAt( QgsVertexId( iPart, iRing, iVert ) ); QgsPointV2 pj = geom->vertexAt( QgsVertexId( iPart, iRing, jVert ) ); if ( QgsGeometryUtils::sqrDistance2D( pi, pj ) < QgsGeometryCheckPrecision::tolerance() * QgsGeometryCheckPrecision::tolerance() ) { errors.append( new QgsGeometryCheckError( this, featureid, pj, QgsVertexId( iPart, iRing, jVert ) ) ); } } } } } }
void QgsGeometryOverlapCheck::collectErrors( QList<QgsGeometryCheckError*>& errors, QStringList &messages, QAtomicInt* progressCounter , const QgsFeatureIds &ids ) const { const QgsFeatureIds& featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids; Q_FOREACH ( QgsFeatureId featureid, featureIds ) { if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 ); QgsFeature feature; if ( !mFeaturePool->get( featureid, feature ) ) { continue; } QgsAbstractGeometryV2* geom = feature.geometry()->geometry(); QgsGeometryEngine* geomEngine = QgsGeomUtils::createGeomEngine( geom, QgsGeometryCheckPrecision::tolerance() ); QgsFeatureIds ids = mFeaturePool->getIntersects( feature.geometry()->boundingBox() ); Q_FOREACH ( QgsFeatureId otherid, ids ) { // >= : only report overlaps once if ( otherid >= featureid ) { continue; } QgsFeature otherFeature; if ( !mFeaturePool->get( otherid, otherFeature ) ) { continue; } QString errMsg; if ( geomEngine->overlaps( *otherFeature.geometry()->geometry(), &errMsg ) ) { QgsAbstractGeometryV2* interGeom = geomEngine->intersection( *otherFeature.geometry()->geometry() ); if ( interGeom && !interGeom->isEmpty() ) { QgsGeomUtils::filter1DTypes( interGeom ); for ( int iPart = 0, nParts = interGeom->partCount(); iPart < nParts; ++iPart ) { double area = QgsGeomUtils::getGeomPart( interGeom, iPart )->area(); if ( area > QgsGeometryCheckPrecision::reducedTolerance() && area < mThreshold ) { errors.append( new QgsGeometryOverlapCheckError( this, featureid, QgsGeomUtils::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; } } delete geomEngine; } }
void QgsGeometrySelfIntersectionCheck::collectErrors( QList<QgsGeometryCheckError*>& errors, QStringList &/*messages*/, QAtomicInt* progressCounter , const QgsFeatureIds &ids ) const { const QgsFeatureIds& featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids; Q_FOREACH ( QgsFeatureId featureid, featureIds ) { if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 ); QgsFeature feature; if ( !mFeaturePool->get( featureid, feature ) ) { continue; } QgsAbstractGeometryV2* geom = feature.geometry()->geometry(); for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart ) { for ( int iRing = 0, nRings = geom->ringCount( iPart ); iRing < nRings; ++iRing ) { Q_FOREACH ( const QgsGeometryUtils::SelfIntersection& inter, QgsGeometryUtils::getSelfIntersections( geom, iPart, iRing, QgsGeometryCheckPrecision::tolerance() ) ) { errors.append( new QgsGeometrySelfIntersectionCheckError( this, featureid, inter.point, QgsVertexId( iPart, iRing ), inter ) ); } } } }
void QgsGeometrySegmentLengthCheck::collectErrors( QList<QgsGeometryCheckError*>& errors, QStringList &/*messages*/, QAtomicInt* progressCounter , const QgsFeatureIds &ids ) const { const QgsFeatureIds& featureIds = ids.isEmpty() ? mFeaturePool->getFeatureIds() : ids; Q_FOREACH ( QgsFeatureId featureid, featureIds ) { if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 ); QgsFeature feature; if ( !mFeaturePool->get( featureid, feature ) ) { continue; } QgsAbstractGeometryV2* geom = feature.geometry()->geometry(); for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart ) { for ( int iRing = 0, nRings = geom->ringCount( iPart ); iRing < nRings; ++iRing ) { int nVerts = QgsGeomUtils::polyLineSize( geom, iPart, iRing ); if ( nVerts < 2 ) { continue; } for ( int iVert = 0, jVert = nVerts - 1; iVert < nVerts; jVert = iVert++ ) { QgsPointV2 pi = geom->vertexAt( QgsVertexId( iPart, iRing, iVert ) ); QgsPointV2 pj = geom->vertexAt( QgsVertexId( iPart, iRing, jVert ) ); double dist = qSqrt( QgsGeometryUtils::sqrDistance2D( pi, pj ) ); if ( dist < mMinLength ) { errors.append( new QgsGeometryCheckError( this, featureid, QgsPointV2( 0.5 * ( pi.x() + pj.x() ), 0.5 * ( pi.y() + pj.y() ) ), QgsVertexId( iPart, iRing, iVert ), dist, QgsGeometryCheckError::ValueLength ) ); } } } } } }
void QgsGeometryTypeCheck::fixError( QgsGeometryCheckError* error, int method, int /*mergeAttributeIndex*/, Changes &changes ) const { QgsFeature feature; if ( !mFeaturePool->get( error->featureId(), feature ) ) { error->setObsolete(); return; } QgsAbstractGeometryV2* geom = feature.geometry()->geometry(); // Check if error still applies QgsWKBTypes::Type type = QgsWKBTypes::flatType( geom->wkbType() ); if (( mAllowedTypes & ( 1 << type ) ) != 0 ) { error->setObsolete(); return; } // Fix with selected method if ( method == NoChange ) { error->setFixed( method ); } else if ( method == Convert ) { // Check if corresponding single type is allowed if ( QgsWKBTypes::isMultiType( type ) && (( 1 << QgsWKBTypes::singleType( type ) ) & mAllowedTypes ) != 0 ) { // Explode multi-type feature into single-type features for ( int iPart = 1, nParts = geom->partCount(); iPart < nParts; ++iPart ) { QgsFeature newFeature; newFeature.setAttributes( feature.attributes() ); newFeature.setGeometry( new QgsGeometry( QgsGeomUtils::getGeomPart( geom, iPart )->clone() ) ); mFeaturePool->addFeature( newFeature ); changes[newFeature.id()].append( Change( ChangeFeature, ChangeAdded ) ); } // Recycle feature for part 0 feature.setGeometry( new QgsGeometry( QgsGeomUtils::getGeomPart( geom, 0 ) ) ); changes[feature.id()].append( Change( ChangeFeature, ChangeChanged ) ); } // Check if corresponding multi type is allowed else if ( QgsWKBTypes::isSingleType( type ) && (( 1 << QgsWKBTypes::multiType( type ) ) & mAllowedTypes ) != 0 ) { QgsGeometryCollectionV2* geomCollection = nullptr; switch ( QgsWKBTypes::multiType( type ) ) { case QgsWKBTypes::MultiPoint: { geomCollection = new QgsMultiPointV2(); break; } case QgsWKBTypes::MultiLineString: { geomCollection = new QgsMultiLineStringV2(); break; } case QgsWKBTypes::MultiPolygon: { geomCollection = new QgsMultiPolygonV2(); break; } case QgsWKBTypes::MultiCurve: { geomCollection = new QgsMultiCurveV2(); break; } case QgsWKBTypes::MultiSurface: { geomCollection = new QgsMultiSurfaceV2(); break; } default: break; } if ( !geomCollection ) { error->setFixFailed( tr( "Unknown geometry type" ) ); } else { geomCollection->addGeometry( geom->clone() ); feature.setGeometry( new QgsGeometry( geomCollection ) ); mFeaturePool->updateFeature( feature ); changes[feature.id()].append( Change( ChangeFeature, ChangeChanged ) ); } } // Delete feature else { mFeaturePool->deleteFeature( feature ); changes[error->featureId()].append( Change( ChangeFeature, ChangeRemoved ) ); } error->setFixed( method ); } else if ( method == Delete ) { mFeaturePool->deleteFeature( feature ); error->setFixed( method ); changes[error->featureId()].append( Change( ChangeFeature, ChangeRemoved ) ); } else { error->setFixFailed( tr( "Unknown method" ) ); } }
bool QgsGeometryAreaCheck::mergeWithNeighbor( QgsFeature& feature, int partIdx, int method, int mergeAttributeIndex, Changes &changes, QString& errMsg ) const { double maxVal = 0.; QgsFeature mergeFeature; int mergePartIdx = -1; bool matchFound = false; QgsAbstractGeometryV2* geom = feature.geometry()->geometry(); // Search for touching neighboring geometries foreach ( const QgsFeatureId& testId, mFeaturePool->getIntersects( feature.geometry()->boundingBox() ) ) { QgsFeature testFeature; if ( !mFeaturePool->get( testId, testFeature ) ) { continue; } QgsAbstractGeometryV2* testGeom = testFeature.geometry()->geometry(); for ( int testPartIdx = 0, nTestParts = testGeom->partCount(); testPartIdx < nTestParts; ++testPartIdx ) { if ( testId == feature.id() && testPartIdx == partIdx ) { continue; } double len = QgsGeomUtils::sharedEdgeLength( QgsGeomUtils::getGeomPart( geom, partIdx ), QgsGeomUtils::getGeomPart( testGeom, testPartIdx ), QgsGeometryCheckPrecision::reducedTolerance() ); if ( len > 0. ) { if ( method == MergeLongestEdge || method == MergeLargestArea ) { double val; if ( method == MergeLongestEdge ) { val = len; } else { if ( dynamic_cast<QgsGeometryCollectionV2*>( testGeom ) ) val = static_cast<QgsGeometryCollectionV2*>( testGeom )->geometryN( testPartIdx )->area(); else val = testGeom->area(); } if ( val > maxVal ) { maxVal = val; mergeFeature = testFeature; mergePartIdx = testPartIdx; } } else if ( method == MergeIdenticalAttribute ) { if ( testFeature.attribute( mergeAttributeIndex ) == feature.attribute( mergeAttributeIndex ) ) { mergeFeature = testFeature; mergePartIdx = testPartIdx; matchFound = true; break; } } } } if ( matchFound ) { break; } } if ( !matchFound && maxVal == 0. ) { return method == MergeIdenticalAttribute ? true : false; } // Remove polygon from source geometry deleteFeatureGeometryPart( feature, partIdx, changes ); if ( mergeFeature.id() == feature.id() && mergePartIdx > partIdx ) { --mergePartIdx; } // Merge geometries QgsAbstractGeometryV2* mergeGeom = mergeFeature.geometry()->geometry(); QgsGeometryEngine* geomEngine = QgsGeomUtils::createGeomEngine( QgsGeomUtils::getGeomPart( mergeGeom, mergePartIdx ), QgsGeometryCheckPrecision::tolerance() ); QgsAbstractGeometryV2* combinedGeom = geomEngine->combine( *QgsGeomUtils::getGeomPart( geom, partIdx ), &errMsg ); delete geomEngine; if ( !combinedGeom || combinedGeom->isEmpty() ) { return false; } replaceFeatureGeometryPart( mergeFeature, mergePartIdx, combinedGeom, changes ); return true; }
void QgsGeometryOverlapCheck::fixError( QgsGeometryCheckError* error, int method, int /*mergeAttributeIndex*/, 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 ) ) { error->setObsolete(); return; } QgsAbstractGeometryV2* geom = feature.geometry()->geometry(); QgsGeometryEngine* geomEngine = QgsGeomUtils::createGeomEngine( geom, QgsGeometryCheckPrecision::tolerance() ); // Check if error still applies if ( !geomEngine->overlaps( *otherFeature.geometry()->geometry() ) ) { delete geomEngine; error->setObsolete(); return; } QgsAbstractGeometryV2* interGeom = geomEngine->intersection( *otherFeature.geometry()->geometry(), &errMsg ); delete geomEngine; 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...) QgsAbstractGeometryV2* interPart = nullptr; for ( int iPart = 0, nParts = interGeom->partCount(); iPart < nParts; ++iPart ) { QgsAbstractGeometryV2* part = QgsGeomUtils::getGeomPart( interGeom, iPart ); if ( qAbs( part->area() - overlapError->value().toDouble() ) < QgsGeometryCheckPrecision::reducedTolerance() && QgsGeomUtils::pointsFuzzyEqual( part->centroid(), overlapError->location(), QgsGeometryCheckPrecision::reducedTolerance() ) ) { interPart = part; break; } } if ( !interPart || interPart->isEmpty() ) { delete interGeom; error->setObsolete(); return; } // Fix error if ( method == NoChange ) { error->setFixed( method ); } else if ( method == Subtract ) { geomEngine = QgsGeomUtils::createGeomEngine( geom, QgsGeometryCheckPrecision::reducedTolerance() ); QgsAbstractGeometryV2* diff1 = geomEngine->difference( *interPart, &errMsg ); delete geomEngine; if ( !diff1 || diff1->isEmpty() ) { delete diff1; diff1 = nullptr; } else { QgsGeomUtils::filter1DTypes( diff1 ); } QgsGeometryEngine* otherGeomEngine = QgsGeomUtils::createGeomEngine( otherFeature.geometry()->geometry(), QgsGeometryCheckPrecision::reducedTolerance() ); QgsAbstractGeometryV2* diff2 = otherGeomEngine->difference( *interPart, &errMsg ); delete otherGeomEngine; if ( !diff2 || diff2->isEmpty() ) { delete diff2; diff2 = nullptr; } else { QgsGeomUtils::filter1DTypes( diff2 ); } double shared1 = diff1 ? QgsGeomUtils::sharedEdgeLength( diff1, interPart, QgsGeometryCheckPrecision::reducedPrecision() ) : 0; double shared2 = diff2 ? QgsGeomUtils::sharedEdgeLength( diff2, interPart, QgsGeometryCheckPrecision::reducedPrecision() ) : 0; if ( shared1 == 0. || shared2 == 0. ) { error->setFixFailed( tr( "Could not find shared edges between intersection and overlapping features" ) ); } else { if ( shared1 < shared2 ) { feature.setGeometry( new QgsGeometry( diff1 ) ); changes[feature.id()].append( Change( ChangeFeature, ChangeChanged ) ); mFeaturePool->updateFeature( feature ); delete diff2; } else { otherFeature.setGeometry( new QgsGeometry( diff2 ) ); changes[otherFeature.id()].append( Change( ChangeFeature, ChangeChanged ) ); mFeaturePool->updateFeature( otherFeature ); delete diff1; } error->setFixed( method ); } } else { error->setFixFailed( tr( "Unknown method" ) ); } delete interGeom; }