void QgsGeometryHoleCheck::fixError( QgsGeometryCheckError* error, int method, int /*mergeAttributeIndex*/, Changes &changes ) const { QgsFeature feature; if ( !mFeaturePool->get( error->featureId(), feature ) ) { error->setObsolete(); return; } QgsGeometry featureGeom = feature.geometry(); QgsAbstractGeometry* geom = featureGeom.geometry(); QgsVertexId vidx = error->vidx(); // Check if ring still exists if ( !vidx.isValid( geom ) ) { error->setObsolete(); return; } // Fix error if ( method == NoChange ) { error->setFixed( method ); } else if ( method == RemoveHoles ) { deleteFeatureGeometryRing( feature, vidx.part, vidx.ring, changes ); error->setFixed( method ); } else { error->setFixFailed( tr( "Unknown method" ) ); } }
void QgsGeometryDuplicateNodesCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const { QgsFeaturePool *featurePool = featurePools[ error->layerId() ]; QgsFeature feature; if ( !featurePool->getFeature( error->featureId(), feature ) ) { error->setObsolete(); return; } QgsGeometry featureGeom = feature.geometry(); QgsAbstractGeometry *geom = featureGeom.get(); QgsVertexId vidx = error->vidx(); // Check if point still exists if ( !vidx.isValid( geom ) ) { error->setObsolete(); return; } // Check if error still applies int nVerts = QgsGeometryCheckerUtils::polyLineSize( geom, vidx.part, vidx.ring ); QgsPoint pi = geom->vertexAt( QgsVertexId( vidx.part, vidx.ring, ( vidx.vertex + nVerts - 1 ) % nVerts ) ); QgsPoint pj = geom->vertexAt( error->vidx() ); if ( QgsGeometryUtils::sqrDistance2D( pi, pj ) >= mContext->tolerance ) { error->setObsolete(); return; } // Fix error if ( method == NoChange ) { error->setFixed( method ); } else if ( method == RemoveDuplicates ) { if ( !QgsGeometryCheckerUtils::canDeleteVertex( geom, vidx.part, vidx.ring ) ) { error->setFixFailed( tr( "Resulting geometry is degenerate" ) ); } else if ( !geom->deleteVertex( error->vidx() ) ) { error->setFixFailed( tr( "Failed to delete vertex" ) ); } else { feature.setGeometry( featureGeom ); featurePool->updateFeature( feature ); error->setFixed( method ); changes[error->layerId()][error->featureId()].append( Change( ChangeNode, ChangeRemoved, error->vidx() ) ); } } else { error->setFixFailed( tr( "Unknown method" ) ); } }
void QgsGeometryAreaCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const { QgsFeaturePool *featurePool = mContext->featurePools[ error->layerId() ]; QgsFeature feature; if ( !featurePool->get( error->featureId(), feature ) ) { error->setObsolete(); return; } double layerToMapUnits = featurePool->getLayerToMapUnits(); QgsGeometry g = feature.geometry(); const QgsAbstractGeometry *geom = g.constGet(); QgsVertexId vidx = error->vidx(); // Check if polygon still exists if ( !vidx.isValid( geom ) ) { error->setObsolete(); return; } // Check if error still applies double value; if ( !checkThreshold( layerToMapUnits, QgsGeometryCheckerUtils::getGeomPart( geom, vidx.part ), value ) ) { error->setObsolete(); return; } // Fix with selected method if ( method == NoChange ) { error->setFixed( method ); } else if ( method == Delete ) { deleteFeatureGeometryPart( error->layerId(), feature, vidx.part, changes ); error->setFixed( method ); } else if ( method == MergeLongestEdge || method == MergeLargestArea || method == MergeIdenticalAttribute ) { QString errMsg; if ( mergeWithNeighbor( error->layerId(), feature, vidx.part, method, mergeAttributeIndices[error->layerId()], changes, errMsg ) ) { error->setFixed( method ); } else { error->setFixFailed( tr( "Failed to merge with neighbor: %1" ).arg( errMsg ) ); } } else { error->setFixFailed( tr( "Unknown method" ) ); } }
void QgsGeometryDuplicateNodesCheck::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(); QgsVertexId vidx = error->vidx(); // Check if point still exists if ( !vidx.isValid( geom ) ) { error->setObsolete(); return; } // Check if error still applies int nVerts = QgsGeomUtils::polyLineSize( geom, vidx.part, vidx.ring ); QgsPointV2 pi = geom->vertexAt( QgsVertexId( vidx.part, vidx.ring, ( vidx.vertex + nVerts - 1 ) % nVerts ) ); QgsPointV2 pj = geom->vertexAt( error->vidx() ); if ( QgsGeometryUtils::sqrDistance2D( pi, pj ) >= QgsGeometryCheckPrecision::tolerance() * QgsGeometryCheckPrecision::tolerance() ) { error->setObsolete(); return; } // Fix error if ( method == NoChange ) { error->setFixed( method ); } else if ( method == RemoveDuplicates ) { geom->deleteVertex( error->vidx() ); if ( QgsGeomUtils::polyLineSize( geom, vidx.part, vidx.ring ) < 3 ) { error->setFixFailed( tr( "Resulting geometry is degenerate" ) ); } else { mFeaturePool->updateFeature( feature ); error->setFixed( method ); changes[error->featureId()].append( Change( ChangeNode, ChangeRemoved, error->vidx() ) ); } } else { error->setFixFailed( tr( "Unknown method" ) ); } }
void QgsGeometryDegeneratePolygonCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const { QgsFeaturePool *featurePool = mContext->featurePools[ error->layerId() ]; QgsFeature feature; if ( !featurePool->get( error->featureId(), feature ) ) { error->setObsolete(); return; } QgsGeometry featureGeometry = feature.geometry(); const QgsAbstractGeometry *geom = featureGeometry.constGet(); QgsVertexId vidx = error->vidx(); // Check if ring still exists if ( !vidx.isValid( geom ) ) { error->setObsolete(); return; } // Check if error still applies if ( QgsGeometryCheckerUtils::polyLineSize( geom, vidx.part, vidx.ring ) >= 3 ) { error->setObsolete(); return; } // Fix error if ( method == NoChange ) { error->setFixed( method ); } else if ( method == DeleteRing ) { deleteFeatureGeometryRing( error->layerId(), feature, vidx.part, vidx.ring, changes ); error->setFixed( method ); } else { error->setFixFailed( tr( "Unknown method" ) ); } }
void TestQgsGeometryUtils::testAdjacentVertices() { // test polygon - should wrap around! QgsLineString* closedRing1 = new QgsLineString(); closedRing1->setPoints( QList<QgsPointV2>() << QgsPointV2( 1, 1 ) << QgsPointV2( 1, 2 ) << QgsPointV2( 2, 2 ) << QgsPointV2( 2, 1 ) << QgsPointV2( 1, 1 ) ); QgsPolygonV2 polygon1; polygon1.setExteriorRing( closedRing1 ); QgsVertexId previous; QgsVertexId next; QgsGeometryUtils::adjacentVertices( polygon1, QgsVertexId( 0, 0, 0 ), previous, next ); QCOMPARE( previous, QgsVertexId( 0, 0, 3 ) ); QCOMPARE( next, QgsVertexId( 0, 0, 1 ) ); // test point - both vertices should be invalid QgsPointV2 point( 1, 2 ); QgsGeometryUtils::adjacentVertices( point, QgsVertexId( 0, 0, 0 ), previous, next ); QVERIFY( !previous.isValid() ); QVERIFY( !next.isValid() ); }
void QgsGeometrySegmentLengthCheck::fixError( QgsGeometryCheckError* error, int method, int /*mergeAttributeIndex*/, Changes &/*changes*/ ) const { QgsFeature feature; if ( !mFeaturePool->get( error->featureId(), feature ) ) { error->setObsolete(); return; } QgsGeometry featureGeom = feature.geometry(); QgsAbstractGeometry* geom = featureGeom.geometry(); QgsVertexId vidx = error->vidx(); // Check if point still exists if ( !vidx.isValid( geom ) ) { error->setObsolete(); return; } // Check if error still applies int nVerts = QgsGeometryCheckerUtils::polyLineSize( geom, vidx.part, vidx.ring ); QgsPointV2 pi = geom->vertexAt( error->vidx() ); QgsPointV2 pj = geom->vertexAt( QgsVertexId( vidx.part, vidx.ring, ( vidx.vertex - 1 + nVerts ) % nVerts ) ); double dist = qSqrt( QgsGeometryUtils::sqrDistance2D( pi, pj ) ); if ( dist >= mMinLength ) { error->setObsolete(); return; } // Fix error if ( method == NoChange ) { error->setFixed( method ); } else { error->setFixFailed( tr( "Unknown method" ) ); } }
void QgsGeometryAngleCheck::fixError( QgsGeometryCheckError* error, int method, int /*mergeAttributeIndex*/, Changes &changes ) const { QgsFeature feature; if ( !mFeaturePool->get( error->featureId(), feature ) ) { error->setObsolete(); return; } QgsAbstractGeometryV2* geometry = feature.geometry()->geometry(); QgsVertexId vidx = error->vidx(); // Check if point still exists if ( !vidx.isValid( geometry ) ) { error->setObsolete(); return; } // Check if error still applies int n = QgsGeomUtils::polyLineSize( geometry, vidx.part, vidx.ring ); const QgsPointV2& p1 = geometry->vertexAt( QgsVertexId( vidx.part, vidx.ring, ( vidx.vertex - 1 + n ) % n ) ); const QgsPointV2& p2 = geometry->vertexAt( vidx ); const QgsPointV2& p3 = geometry->vertexAt( QgsVertexId( vidx.part, vidx.ring, ( vidx.vertex + 1 ) % n ) ); QgsVector v21, v23; try { v21 = QgsVector( p1.x() - p2.x(), p1.y() - p2.y() ).normal(); v23 = QgsVector( p3.x() - p2.x(), p3.y() - p2.y() ).normal(); } catch ( const QgsException& ) { error->setObsolete(); return; } double angle = std::acos( v21 * v23 ) / M_PI * 180.0; if ( angle >= mMinAngle ) { error->setObsolete(); return; } // Fix error if ( method == NoChange ) { error->setFixed( method ); } else if ( method == DeleteNode ) { if ( n <= 3 ) { error->setFixFailed( tr( "Resulting geometry is degenerate" ) ); } else { geometry->deleteVertex( vidx ); mFeaturePool->updateFeature( feature ); error->setFixed( method ); changes[error->featureId()].append( Change( ChangeNode, ChangeRemoved, vidx ) ); } } else { error->setFixFailed( tr( "Unknown method" ) ); } }
void QgsGeometrySelfIntersectionCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const { QgsFeaturePool *featurePool = mContext->featurePools[ error->layerId() ]; QgsFeature feature; if ( !featurePool->getFeature( error->featureId(), feature ) ) { error->setObsolete(); return; } QgsGeometry featureGeom = feature.geometry(); QgsAbstractGeometry *geom = featureGeom.get(); QgsVertexId vidx = error->vidx(); // Check if ring still exists if ( !vidx.isValid( geom ) ) { error->setObsolete(); return; } const QgsGeometryUtils::SelfIntersection &inter = static_cast<QgsGeometrySelfIntersectionCheckError *>( error )->intersection(); // Check if error still applies bool ringIsClosed = false; int nVerts = QgsGeometryCheckerUtils::polyLineSize( geom, vidx.part, vidx.ring, &ringIsClosed ); if ( nVerts == 0 || inter.segment1 >= nVerts || inter.segment2 >= nVerts ) { error->setObsolete(); return; } QgsPoint p1 = geom->vertexAt( QgsVertexId( vidx.part, vidx.ring, inter.segment1 ) ); QgsPoint q1 = geom->vertexAt( QgsVertexId( vidx.part, vidx.ring, inter.segment2 ) ); QgsPoint p2 = geom->vertexAt( QgsVertexId( vidx.part, vidx.ring, ( inter.segment1 + 1 ) % nVerts ) ); QgsPoint q2 = geom->vertexAt( QgsVertexId( vidx.part, vidx.ring, ( inter.segment2 + 1 ) % nVerts ) ); QgsPoint s; bool intersection = false; if ( !QgsGeometryUtils::segmentIntersection( p1, p2, q1, q2, s, intersection, mContext->tolerance ) ) { error->setObsolete(); return; } // Fix with selected method if ( method == NoChange ) { error->setFixed( method ); } else if ( method == ToMultiObject || method == ToSingleObjects ) { // Extract rings QgsPointSequence ring1, ring2; bool ring1EndsWithS = false; bool ring2EndsWithS = false; for ( int i = 0; i < nVerts; ++i ) { if ( i <= inter.segment1 || i >= inter.segment2 + 1 ) { ring1.append( geom->vertexAt( QgsVertexId( vidx.part, vidx.ring, i ) ) ); ring1EndsWithS = false; if ( i == inter.segment2 + 1 ) { ring2.append( s ); ring2EndsWithS = true; } } else { ring2.append( geom->vertexAt( QgsVertexId( vidx.part, vidx.ring, i ) ) ); ring2EndsWithS = true; if ( i == inter.segment1 + 1 ) { ring1.append( s ); ring1EndsWithS = false; } } } if ( nVerts == inter.segment2 + 1 ) { ring2.append( s ); ring2EndsWithS = true; } if ( ringIsClosed || ring1EndsWithS ) ring1.append( ring1.front() ); // Ensure ring is closed if ( ringIsClosed || ring2EndsWithS ) ring2.append( ring2.front() ); // Ensure ring is closed if ( ring1.size() < 3 + ( ringIsClosed || ring1EndsWithS ) || ring2.size() < 3 + ( ringIsClosed || ring2EndsWithS ) ) { error->setFixFailed( tr( "Resulting geometry is degenerate" ) ); return; } QgsLineString *ringGeom1 = new QgsLineString(); ringGeom1->setPoints( ring1 ); QgsLineString *ringGeom2 = new QgsLineString(); ringGeom2->setPoints( ring2 ); QgsAbstractGeometry *part = QgsGeometryCheckerUtils::getGeomPart( geom, vidx.part ); // If is a polygon... if ( dynamic_cast<QgsCurvePolygon *>( part ) ) { QgsCurvePolygon *poly = static_cast<QgsCurvePolygon *>( part ); // If self-intersecting ring is an interior ring, create separate holes if ( vidx.ring > 0 ) { poly->removeInteriorRing( vidx.ring ); poly->addInteriorRing( ringGeom1 ); poly->addInteriorRing( ringGeom2 ); 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 ); featurePool->updateFeature( feature ); } else { // If ring is exterior, build two polygons, and reassign interiors as necessary poly->setExteriorRing( ringGeom1 ); // If original feature was a linear polygon, also create the new part as a linear polygon QgsCurvePolygon *poly2 = dynamic_cast<QgsPolygon *>( part ) ? new QgsPolygon() : new QgsCurvePolygon(); poly2->setExteriorRing( ringGeom2 ); // Reassing interiors as necessary std::unique_ptr< QgsGeometryEngine > geomEnginePoly1 = QgsGeometryCheckerUtils::createGeomEngine( poly, mContext->tolerance ); std::unique_ptr< QgsGeometryEngine > geomEnginePoly2 = QgsGeometryCheckerUtils::createGeomEngine( poly2, mContext->tolerance ); for ( int n = poly->numInteriorRings(), i = n - 1; i >= 0; --i ) { if ( !geomEnginePoly1->contains( poly->interiorRing( i ) ) ) { if ( geomEnginePoly2->contains( poly->interiorRing( i ) ) ) { poly2->addInteriorRing( static_cast<QgsCurve *>( poly->interiorRing( i )->clone() ) ); // No point in adding ChangeAdded changes, since the entire poly2 is added anyways later on } poly->removeInteriorRing( i ); changes[error->layerId()][feature.id()].append( Change( ChangeRing, ChangeRemoved, QgsVertexId( vidx.part, 1 + i ) ) ); } } if ( method == ToMultiObject ) { // If is already a geometry collection, just add the new polygon. if ( dynamic_cast<QgsGeometryCollection *>( geom ) ) { static_cast<QgsGeometryCollection *>( geom )->addGeometry( poly2 ); 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 ); featurePool->updateFeature( feature ); } // Otherwise, create multipolygon else { QgsMultiPolygon *multiPoly = new QgsMultiPolygon(); multiPoly->addGeometry( poly->clone() ); multiPoly->addGeometry( poly2 ); feature.setGeometry( QgsGeometry( multiPoly ) ); featurePool->updateFeature( feature ); changes[error->layerId()][feature.id()].append( Change( ChangeFeature, ChangeChanged ) ); } } else // if ( method == ToSingleObjects ) { QgsFeature newFeature; newFeature.setAttributes( feature.attributes() ); newFeature.setGeometry( QgsGeometry( poly2 ) ); feature.setGeometry( featureGeom ); featurePool->updateFeature( feature ); featurePool->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 ) ); } } } else if ( dynamic_cast<QgsCurve *>( part ) ) { if ( method == ToMultiObject ) { if ( dynamic_cast<QgsGeometryCollection *>( geom ) ) { QgsGeometryCollection *geomCollection = static_cast<QgsGeometryCollection *>( geom ); geomCollection->removeGeometry( vidx.part ); geomCollection->addGeometry( ringGeom1 ); geomCollection->addGeometry( ringGeom2 ); feature.setGeometry( featureGeom ); featurePool->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 { QgsMultiCurve *geomCollection = new QgsMultiLineString(); geomCollection->addGeometry( ringGeom1 ); geomCollection->addGeometry( ringGeom2 ); feature.setGeometry( QgsGeometry( geomCollection ) ); featurePool->updateFeature( feature ); changes[error->layerId()][feature.id()].append( Change( ChangeFeature, ChangeChanged ) ); } } else // if(method == ToSingleObjects) { if ( dynamic_cast<QgsGeometryCollection *>( geom ) ) { QgsGeometryCollection *geomCollection = static_cast<QgsGeometryCollection *>( geom ); geomCollection->removeGeometry( vidx.part ); geomCollection->addGeometry( ringGeom1 ); feature.setGeometry( featureGeom ); featurePool->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 ) ); featurePool->updateFeature( feature ); changes[error->layerId()][feature.id()].append( Change( ChangeFeature, ChangeChanged, QgsVertexId( vidx.part ) ) ); } QgsFeature newFeature; newFeature.setAttributes( feature.attributes() ); newFeature.setGeometry( QgsGeometry( ringGeom2 ) ); featurePool->addFeature( newFeature ); changes[error->layerId()][newFeature.id()].append( Change( ChangeFeature, ChangeAdded ) ); } } else { delete ringGeom1; delete ringGeom2; } error->setFixed( method ); } else { error->setFixFailed( tr( "Unknown method" ) ); } }