void QgsSpatialQuery::populateIndexResultDisjoint( QgsFeatureIds &qsetIndexResult, QgsFeatureId idTarget, const QgsGeometry& geomTarget, bool ( QgsGeometryEngine::* op )( const QgsAbstractGeometry&, QString* ) const ) { QgsFeatureIds listIdReference = mIndexReference.intersects( geomTarget.boundingBox() ).toSet(); if ( listIdReference.isEmpty() ) { qsetIndexResult.insert( idTarget ); return; } //prepare geometry QgsGeometryEngine* geomEngine = geomTarget.createGeometryEngine( geomTarget.geometry() ); geomEngine->prepareGeometry(); QgsFeature featureReference; QgsGeometry geomReference; QgsFeatureIterator listIt = mLayerReference->getFeatures( QgsFeatureRequest().setFilterFids( listIdReference ) ); bool addIndex = true; while ( listIt.nextFeature( featureReference ) ) { geomReference = featureReference.geometry(); if (( geomEngine->*op )( *geomReference.geometry(), 0 ) ) { addIndex = false; break; } } if ( addIndex ) { qsetIndexResult.insert( idTarget ); } delete geomEngine; } // void QgsSpatialQuery::populateIndexResultDisjoint( ...
QgsVectorLayer::EditResult QgsVectorLayerEditUtils::deleteVertexV2( QgsFeatureId featureId, int vertex ) { if ( !L->hasGeometryType() ) return QgsVectorLayer::InvalidLayer; QgsGeometry geometry; if ( !cache()->geometry( featureId, geometry ) ) { // it's not in cache: let's fetch it from layer QgsFeature f; if ( !L->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) || !f.hasGeometry() ) return QgsVectorLayer::FetchFeatureFailed; // geometry not found geometry = f.geometry(); } if ( !geometry.deleteVertex( vertex ) ) return QgsVectorLayer::EditFailed; if ( geometry.geometry() && geometry.geometry()->nCoordinates() == 0 ) { //last vertex deleted, set geometry to null geometry.setGeometry( nullptr ); } L->editBuffer()->changeGeometry( featureId, geometry ); return !geometry.isEmpty() ? QgsVectorLayer::Success : QgsVectorLayer::EmptyGeometry; }
bool QgsVectorLayerEditUtils::deleteVertex( QgsFeatureId atFeatureId, int atVertex ) { if ( !L->hasGeometryType() ) return false; QgsGeometry geometry; if ( !cache()->geometry( atFeatureId, geometry ) ) { // it's not in cache: let's fetch it from layer QgsFeature f; if ( !L->getFeatures( QgsFeatureRequest().setFilterFid( atFeatureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) || !f.constGeometry() ) return false; // geometry not found geometry = *f.constGeometry(); } if ( !geometry.deleteVertex( atVertex ) ) return false; if ( geometry.geometry() && geometry.geometry()->nCoordinates() == 0 ) { //last vertex deleted, set geometry to null geometry.setGeometry( 0 ); } L->editBuffer()->changeGeometry( atFeatureId, &geometry ); return true; }
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::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; } 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 ) { int nVerts = QgsGeometryCheckerUtils::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 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; } 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 ) { 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 ) ); } } } }
double QgsDistanceArea::measureArea( const QgsGeometry& geometry ) const { if ( geometry.isEmpty() ) return 0.0; const QgsAbstractGeometry* geomV2 = geometry.geometry(); return measure( geomV2, Area ); }
double QgsDistanceArea::measureLength( const QgsGeometry& geometry ) const { if ( geometry.isNull() ) return 0.0; const QgsAbstractGeometry* geomV2 = geometry.geometry(); return measure( geomV2, Length ); }
void QgsGeometryDuplicateNodesCheck::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( 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 ) { 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 ); mFeaturePool->updateFeature( feature ); error->setFixed( method ); changes[error->featureId()].append( Change( ChangeNode, ChangeRemoved, error->vidx() ) ); } } else { error->setFixFailed( tr( "Unknown method" ) ); } }
double QgsDistanceArea::measurePerimeter( const QgsGeometry& geometry ) const { if ( geometry.isNull() ) return 0.0; const QgsAbstractGeometry* geomV2 = geometry.geometry(); if ( !geomV2 || geomV2->dimension() < 2 ) { return 0.0; } if ( !mEllipsoidalMode || mEllipsoid == GEO_NONE ) { return geomV2->perimeter(); } //create list with (single) surfaces QList< const QgsSurface* > surfaces; const QgsSurface* surf = dynamic_cast<const QgsSurface*>( geomV2 ); if ( surf ) { surfaces.append( surf ); } const QgsMultiSurface* multiSurf = dynamic_cast<const QgsMultiSurface*>( geomV2 ); if ( multiSurf ) { surfaces.reserve(( surf ? 1 : 0 ) + multiSurf->numGeometries() ); for ( int i = 0; i < multiSurf->numGeometries(); ++i ) { surfaces.append( static_cast<const QgsSurface*>( multiSurf->geometryN( i ) ) ); } } double length = 0; QList<const QgsSurface*>::const_iterator surfaceIt = surfaces.constBegin(); for ( ; surfaceIt != surfaces.constEnd(); ++surfaceIt ) { if ( !*surfaceIt ) { continue; } QgsPolygonV2* poly = ( *surfaceIt )->surfaceToPolygon(); const QgsCurve* outerRing = poly->exteriorRing(); if ( outerRing ) { length += measure( outerRing ); } int nInnerRings = poly->numInteriorRings(); for ( int i = 0; i < nInnerRings; ++i ) { length += measure( poly->interiorRing( i ) ); } delete poly; } return length; }
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; } 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 ) { // >= : 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 ) ) { QgsAbstractGeometry *interGeom = geomEngine->intersection( *otherFeature.geometry().geometry() ); if ( interGeom && !interGeom->isEmpty() ) { QgsGeometryCheckerUtils::filter1DTypes( interGeom ); for ( int iPart = 0, nParts = interGeom->partCount(); iPart < nParts; ++iPart ) { double area = QgsGeometryCheckerUtils::getGeomPart( interGeom, iPart )->area(); if ( area > QgsGeometryCheckPrecision::reducedTolerance() && area < mThreshold ) { errors.append( new QgsGeometryOverlapCheckError( this, 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; } } delete geomEngine; } }
void QgsGeometryContainedCheck::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; } QgsGeometry featureGeom = feature.geometry(); QgsGeometryEngine* geomEngine = QgsGeometryCheckerUtils::createGeomEngine( featureGeom.geometry(), QgsGeometryCheckPrecision::tolerance() ); QgsFeatureIds ids = mFeaturePool->getIntersects( featureGeom.geometry()->boundingBox() ); Q_FOREACH ( QgsFeatureId otherid, ids ) { if ( otherid == featureid ) { continue; } QgsFeature otherFeature; if ( !mFeaturePool->get( otherid, otherFeature ) ) { continue; } QString errMsg; if ( geomEngine->within( *otherFeature.geometry().geometry(), &errMsg ) ) { 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 ) ); } } delete geomEngine; } }
void QgsGeometryAngleCheck::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; } 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 ) { int nVerts = QgsGeometryCheckerUtils::polyLineSize( geom, iPart, iRing ); // Less than three points, no angles to check if ( nVerts < 3 ) { 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 { 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, featureid, p2, QgsVertexId( iPart, iRing, iVert ), angle ) ); } } } } } }
//! Returns a simplified version the specified geometry (Removing duplicated points) when is applied the specified map2pixel context QgsGeometry QgsMapToPixelSimplifier::simplify( const QgsGeometry& geometry ) const { if ( geometry.isEmpty() ) { return QgsGeometry(); } if ( mSimplifyFlags == QgsMapToPixelSimplifier::NoFlags ) { return geometry; } // Check whether the geometry can be simplified using the map2pixel context const QgsWkbTypes::Type singleType = QgsWkbTypes::singleType( geometry.wkbType() ); const QgsWkbTypes::Type flatType = QgsWkbTypes::flatType( singleType ); if ( flatType == QgsWkbTypes::Point ) { return geometry; } const bool isaLinearRing = flatType == QgsWkbTypes::Polygon; const int numPoints = geometry.geometry()->nCoordinates(); if ( numPoints <= ( isaLinearRing ? 6 : 3 ) ) { // No simplify simple geometries return geometry; } const QgsRectangle envelope = geometry.boundingBox(); if ( qMax( envelope.width(), envelope.height() ) / numPoints > mTolerance * 2.0 ) { //points are in average too far apart to lead to any significant simplification return geometry; } return simplifyGeometry( mSimplifyFlags, mSimplifyAlgorithm, geometry.wkbType(), *geometry.geometry(), envelope, mTolerance, false ); }
void QgsSelectByFormDialog::zoomToFeatures( const QString &filter ) { QgsFeatureIds ids; QgsExpressionContext context( QgsExpressionContextUtils::globalProjectLayerScopes( mLayer ) ); QgsFeatureRequest request = QgsFeatureRequest().setFilterExpression( filter ) .setExpressionContext( context ) .setSubsetOfAttributes( QgsAttributeList() ); QgsFeatureIterator features = mLayer->getFeatures( request ); QgsRectangle bbox; bbox.setMinimal(); QgsFeature feat; int featureCount = 0; while ( features.nextFeature( feat ) ) { QgsGeometry geom = feat.geometry(); if ( geom.isNull() || geom.geometry()->isEmpty() ) continue; QgsRectangle r = mMapCanvas->mapSettings().layerExtentToOutputExtent( mLayer, geom.boundingBox() ); bbox.combineExtentWith( r ); featureCount++; } features.close(); QgsSettings settings; int timeout = settings.value( QStringLiteral( "qgis/messageTimeout" ), 5 ).toInt(); if ( featureCount > 0 ) { mMapCanvas->zoomToFeatureExtent( bbox ); if ( mMessageBar ) { mMessageBar->pushMessage( QString(), tr( "Zoomed to %n matching feature(s)", "number of matching features", featureCount ), QgsMessageBar::INFO, timeout ); } } else if ( mMessageBar ) { mMessageBar->pushMessage( QString(), tr( "No matching features found" ), QgsMessageBar::INFO, timeout ); } }
void QgsGeometryMultipartCheck::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(); // Check if error still applies if ( geom->partCount() > 1 || !QgsWkbTypes::isMultiType( geom->wkbType() ) ) { error->setObsolete(); return; } // Fix error if ( method == NoChange ) { error->setFixed( method ); } else if ( method == ConvertToSingle ) { feature.setGeometry( QgsGeometry( QgsGeometryCheckerUtils::getGeomPart( geom, 0 )->clone() ) ); mFeaturePool->updateFeature( feature ); error->setFixed( method ); changes[feature.id()].append( Change( ChangeFeature, ChangeChanged ) ); } else if ( method == RemoveObject ) { mFeaturePool->deleteFeature( feature ); error->setFixed( method ); changes[feature.id()].append( Change( ChangeFeature, ChangeRemoved ) ); } else { error->setFixFailed( tr( "Unknown method" ) ); } }
void QgsGeometryContainedCheck::fixError( QgsGeometryCheckError* error, int method, int /*mergeAttributeIndex*/, Changes &changes ) const { QgsGeometryContainedCheckError* coverError = static_cast<QgsGeometryContainedCheckError*>( error ); QgsFeature feature; QgsFeature otherFeature; if ( !mFeaturePool->get( error->featureId(), feature ) || !mFeaturePool->get( coverError->otherId(), otherFeature ) ) { error->setObsolete(); return; } // Check if error still applies QgsGeometry featureGeom = feature.geometry(); QgsGeometryEngine* geomEngine = QgsGeometryCheckerUtils::createGeomEngine( featureGeom.geometry(), QgsGeometryCheckPrecision::tolerance() ); if ( !geomEngine->within( *otherFeature.geometry().geometry() ) ) { delete geomEngine; error->setObsolete(); return; } delete geomEngine; // Fix error if ( method == NoChange ) { error->setFixed( method ); } else if ( method == Delete ) { changes[feature.id()].append( Change( ChangeFeature, ChangeRemoved ) ); mFeaturePool->deleteFeature( feature ); error->setFixed( method ); } else { error->setFixFailed( tr( "Unknown method" ) ); } }
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 QgsGeometryTypeCheck::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; } 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, featureid, geom->centroid(), type ) ); } } }
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; } 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 ) { int nVerts = QgsGeometryCheckerUtils::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 QgsGeometryHoleCheck::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; } 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, featureid, QgsGeometryCheckerUtils::getGeomPart( geom, iPart )->centroid(), QgsVertexId( iPart, iRing ) ) ); } } } }
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; } QgsGeometry featureGeom = feature.geometry(); QgsAbstractGeometry *geom = featureGeom.geometry(); QgsGeometryEngine *geomEngine = QgsGeometryCheckerUtils::createGeomEngine( geom, QgsGeometryCheckPrecision::tolerance() ); // Check if error still applies if ( !geomEngine->overlaps( *otherFeature.geometry().geometry() ) ) { delete geomEngine; error->setObsolete(); return; } QgsAbstractGeometry *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...) QgsAbstractGeometry *interPart = nullptr; for ( int iPart = 0, nParts = interGeom->partCount(); iPart < nParts; ++iPart ) { QgsAbstractGeometry *part = QgsGeometryCheckerUtils::getGeomPart( interGeom, iPart ); if ( qAbs( part->area() - overlapError->value().toDouble() ) < QgsGeometryCheckPrecision::reducedTolerance() && QgsGeometryCheckerUtils::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 = QgsGeometryCheckerUtils::createGeomEngine( geom, QgsGeometryCheckPrecision::reducedTolerance() ); QgsAbstractGeometry *diff1 = geomEngine->difference( *interPart, &errMsg ); delete geomEngine; if ( !diff1 || diff1->isEmpty() ) { delete diff1; diff1 = nullptr; } else { QgsGeometryCheckerUtils::filter1DTypes( diff1 ); } QgsGeometry otherFeatureGeom = otherFeature.geometry(); QgsGeometryEngine *otherGeomEngine = QgsGeometryCheckerUtils::createGeomEngine( otherFeatureGeom.geometry(), QgsGeometryCheckPrecision::reducedTolerance() ); QgsAbstractGeometry *diff2 = otherGeomEngine->difference( *interPart, &errMsg ); delete otherGeomEngine; if ( !diff2 || diff2->isEmpty() ) { delete diff2; diff2 = nullptr; } else { QgsGeometryCheckerUtils::filter1DTypes( diff2 ); } double shared1 = diff1 ? QgsGeometryCheckerUtils::sharedEdgeLength( diff1, interPart, QgsGeometryCheckPrecision::reducedPrecision() ) : 0; double shared2 = diff2 ? QgsGeometryCheckerUtils::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( QgsGeometry( diff1 ) ); changes[feature.id()].append( Change( ChangeFeature, ChangeChanged ) ); mFeaturePool->updateFeature( feature ); delete diff2; } else { otherFeature.setGeometry( 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; }
void QgsGeometryTypeCheck::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(); // 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( QgsGeometry( QgsGeometryCheckerUtils::getGeomPart( geom, iPart )->clone() ) ); mFeaturePool->addFeature( newFeature ); changes[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 ) ); } // Check if corresponding multi type is allowed else if ( QgsWkbTypes::isSingleType( type ) && (( 1 << QgsWkbTypes::multiType( type ) ) & mAllowedTypes ) != 0 ) { QgsGeometryCollection* geomCollection = nullptr; switch ( QgsWkbTypes::multiType( type ) ) { case QgsWkbTypes::MultiPoint: { geomCollection = new QgsMultiPointV2(); break; } case QgsWkbTypes::MultiLineString: { geomCollection = new QgsMultiLineString(); break; } case QgsWkbTypes::MultiPolygon: { geomCollection = new QgsMultiPolygonV2(); break; } case QgsWkbTypes::MultiCurve: { geomCollection = new QgsMultiCurve(); break; } case QgsWkbTypes::MultiSurface: { geomCollection = new QgsMultiSurface(); break; } default: break; } if ( !geomCollection ) { error->setFixFailed( tr( "Unknown geometry type" ) ); } else { geomCollection->addGeometry( geom->clone() ); feature.setGeometry( 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" ) ); } }
void QgsGeometryAngleCheck::fixError( QgsGeometryCheckError *error, int method, int /*mergeAttributeIndex*/, Changes &changes ) const { QgsFeature feature; if ( !mFeaturePool->get( error->featureId(), feature ) ) { error->setObsolete(); return; } QgsGeometry g = feature.geometry(); QgsAbstractGeometry *geometry = g.geometry(); QgsVertexId vidx = error->vidx(); // Check if point still exists if ( !vidx.isValid( geometry ) ) { error->setObsolete(); return; } // Check if error still applies int n = QgsGeometryCheckerUtils::polyLineSize( geometry, vidx.part, vidx.ring ); if ( n == 0 ) { error->setObsolete(); return; } const QgsPoint &p1 = geometry->vertexAt( QgsVertexId( vidx.part, vidx.ring, ( vidx.vertex - 1 + n ) % n ) ); const QgsPoint &p2 = geometry->vertexAt( vidx ); const QgsPoint &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() ).normalized(); v23 = QgsVector( p3.x() - p2.x(), p3.y() - p2.y() ).normalized(); } 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 ( !QgsGeometryCheckerUtils::canDeleteVertex( geometry, vidx.part, vidx.ring ) ) { error->setFixFailed( tr( "Resulting geometry is degenerate" ) ); } else if ( !geometry->deleteVertex( error->vidx() ) ) { error->setFixFailed( tr( "Failed to delete vertex" ) ); } else { changes[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 ) ) ); } feature.setGeometry( g ); mFeaturePool->updateFeature( feature ); error->setFixed( method ); } } else { error->setFixFailed( tr( "Unknown method" ) ); } }
void QgsMapToolAddPart::cadCanvasReleaseEvent( QgsMapMouseEvent * e ) { //check if we operate on a vector layer QgsVectorLayer *vlayer = currentVectorLayer(); if ( !vlayer ) { notifyNotVectorLayer(); return; } if ( !vlayer->isEditable() ) { notifyNotEditableLayer(); return; } bool isGeometryEmpty = false; if ( vlayer->selectedFeatures()[0].geometry().isEmpty() ) isGeometryEmpty = true; if ( !checkSelection() ) { stopCapturing(); return; } int errorCode = 0; switch ( mode() ) { case CapturePoint: { QgsPointV2 layerPoint; QgsPoint mapPoint = e->mapPoint(); if ( nextPoint( QgsPointV2( mapPoint ), layerPoint ) != 0 ) { QgsDebugMsg( "nextPoint failed" ); return; } vlayer->beginEditCommand( tr( "Part added" ) ); errorCode = vlayer->addPart( QgsPointSequence() << layerPoint ); } break; case CaptureLine: case CapturePolygon: { //add point to list and to rubber band if ( e->button() == Qt::LeftButton ) { int error = addVertex( e->mapPoint(), e->mapPointMatch() ); if ( error == 1 ) { QgsDebugMsg( "current layer is not a vector layer" ); return; } else if ( error == 2 ) { //problem with coordinate transformation emit messageEmitted( tr( "Coordinate transform error. Cannot transform the point to the layers coordinate system" ), QgsMessageBar::WARNING ); return; } startCapturing(); return; } else if ( e->button() != Qt::RightButton ) { deleteTempRubberBand(); return; } if ( !isCapturing() ) return; if ( mode() == CapturePolygon ) { closePolygon(); } //does compoundcurve contain circular strings? //does provider support circular strings? bool hasCurvedSegments = captureCurve()->hasCurvedSegments(); bool providerSupportsCurvedSegments = vlayer->dataProvider()->capabilities() & QgsVectorDataProvider::CircularGeometries; QgsCurve* curveToAdd = nullptr; if ( hasCurvedSegments && providerSupportsCurvedSegments ) { curveToAdd = captureCurve()->clone(); } else { curveToAdd = captureCurve()->curveToLine(); } vlayer->beginEditCommand( tr( "Part added" ) ); if ( mode() == CapturePolygon ) { //avoid intersections QgsCurvePolygon* cp = new QgsCurvePolygon(); cp->setExteriorRing( curveToAdd ); QgsGeometry* geom = new QgsGeometry( cp ); geom->avoidIntersections( QgsProject::instance()->avoidIntersectionsLayers() ); const QgsCurvePolygon* cpGeom = dynamic_cast<const QgsCurvePolygon*>( geom->geometry() ); if ( !cpGeom ) { stopCapturing(); delete geom; vlayer->destroyEditCommand(); return; } errorCode = vlayer->addPart( cpGeom->exteriorRing()->clone() ); delete geom; } else { errorCode = vlayer->addPart( curveToAdd ); } stopCapturing(); } break; default: Q_ASSERT( !"invalid capture mode" ); errorCode = 6; break; } QString errorMessage; switch ( errorCode ) { case 0: { // remove previous message emit messageDiscarded(); //add points to other features to keep topology up-to-date bool topologicalEditing = QgsProject::instance()->topologicalEditing(); if ( topologicalEditing ) { addTopologicalPoints( points() ); } vlayer->endEditCommand(); vlayer->triggerRepaint(); if (( !isGeometryEmpty ) && QgsWkbTypes::isSingleType( vlayer->wkbType() ) ) { emit messageEmitted( tr( "Add part: Feature geom is single part and you've added more than one" ), QgsMessageBar::WARNING ); } return; } case 1: errorMessage = tr( "Selected feature is not multi part." ); break; case 2: errorMessage = tr( "New part's geometry is not valid." ); break; case 3: errorMessage = tr( "New polygon ring not disjoint with existing polygons." ); break; case 4: errorMessage = tr( "No feature selected. Please select a feature with the selection tool or in the attribute table" ); break; case 5: errorMessage = tr( "Several features are selected. Please select only one feature to which an island should be added." ); break; case 6: errorMessage = tr( "Selected geometry could not be found" ); break; } emit messageEmitted( errorMessage, QgsMessageBar::WARNING ); vlayer->destroyEditCommand(); }