void QgsGeometryDuplicateNodesCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { Q_UNUSED( messages ) QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap(); QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds, compatibleGeometryTypes(), feedback, mContext ); for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures ) { const QgsAbstractGeometry *geom = layerFeature.geometry().constGet(); 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++ ) { QgsPoint pi = geom->vertexAt( QgsVertexId( iPart, iRing, iVert ) ); QgsPoint pj = geom->vertexAt( QgsVertexId( iPart, iRing, jVert ) ); if ( QgsGeometryUtils::sqrDistance2D( pi, pj ) < mContext->tolerance ) { errors.append( new QgsGeometryCheckError( this, layerFeature, pj, QgsVertexId( iPart, iRing, jVert ) ) ); } } } } } }
void QgsGeometryFollowBoundariesCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const { if ( !mIndex || !mCheckLayer ) { return; } QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; featureIds.remove( mCheckLayer->id() ); // Don't check layer against itself QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter ); for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures ) { const QgsAbstractGeometry *geom = layerFeature.geometry(); // The geometry to crs of the check layer QgsCoordinateTransform crst = QgsCoordinateTransformCache::instance()->transform( layerFeature.layer().crs().authid(), mCheckLayer->crs().authid() ); QScopedPointer<QgsAbstractGeometry> geomt( geom->clone() ); geomt->transform( crst ); QSharedPointer<QgsGeometryEngine> geomEngine = QgsGeometryCheckerUtils::createGeomEngine( geomt.data(), mContext->tolerance ); // Get potential reference features QgsRectangle searchBounds = geomt->boundingBox(); searchBounds.grow( mContext->tolerance ); QgsFeatureIds refFeatureIds = mIndex->intersects( searchBounds ).toSet(); QgsFeatureRequest refFeatureRequest = QgsFeatureRequest().setFilterFids( refFeatureIds ).setSubsetOfAttributes( QgsAttributeList() ); QgsFeatureIterator refFeatureIt = mCheckLayer->getFeatures( refFeatureRequest ); if ( refFeatureIds.isEmpty() ) { // If no potential reference features are found, the geometry is definitely not following boundaries of reference layer features errors.append( new QgsGeometryCheckError( this, layerFeature, QgsPointXY( geom->centroid() ) ) ); } else { // All reference features must be either contained or disjoint from tested geometry QgsFeature refFeature; while ( refFeatureIt.nextFeature( refFeature ) ) { QgsAbstractGeometry *refGeom = refFeature.geometry().geometry(); QSharedPointer<QgsGeometryEngine> refgeomEngine = QgsGeometryCheckerUtils::createGeomEngine( refGeom, mContext->tolerance ); QScopedPointer<QgsAbstractGeometry> reducedRefGeom( refgeomEngine->buffer( -mContext->tolerance, 0 ) ); if ( !( geomEngine->contains( reducedRefGeom.data() ) || geomEngine->disjoint( reducedRefGeom.data() ) ) ) { errors.append( new QgsGeometryCheckError( this, layerFeature, QgsPointXY( geom->centroid() ) ) ); break; } } } } }
void QgsGeometryPointCoveredByLineCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { Q_UNUSED( messages ) QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap(); QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds, compatibleGeometryTypes(), feedback, mContext, true ); for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures ) { const QgsAbstractGeometry *geom = layerFeature.geometry().constGet(); for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart ) { const QgsPoint *point = dynamic_cast<const QgsPoint *>( QgsGeometryCheckerUtils::getGeomPart( geom, iPart ) ); if ( !point ) { // Should not happen continue; } // Check that point lies on a line bool touches = false; QgsRectangle rect( point->x() - mContext->tolerance, point->y() - mContext->tolerance, point->x() + mContext->tolerance, point->y() + mContext->tolerance ); QgsGeometryCheckerUtils::LayerFeatures checkFeatures( featurePools, featureIds.keys(), rect, {QgsWkbTypes::LineGeometry}, mContext ); for ( const QgsGeometryCheckerUtils::LayerFeature &checkFeature : checkFeatures ) { const QgsAbstractGeometry *testGeom = checkFeature.geometry().constGet(); for ( int jPart = 0, mParts = testGeom->partCount(); jPart < mParts; ++jPart ) { const QgsLineString *testLine = dynamic_cast<const QgsLineString *>( QgsGeometryCheckerUtils::getGeomPart( testGeom, jPart ) ); if ( !testLine ) { continue; } if ( QgsGeometryCheckerUtils::pointOnLine( *point, testLine, mContext->tolerance ) ) { touches = true; break; } } if ( touches ) { break; } } if ( touches ) { continue; } errors.append( new QgsGeometryCheckError( this, layerFeature, *point, QgsVertexId( iPart, 0, 0 ) ) ); } } }
void QgsGeometryLineLayerIntersectionCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const { QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; featureIds.remove( mCheckLayer ); // Don't check layer against itself QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter, mContext, true ); for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures ) { const QgsAbstractGeometry *geom = layerFeature.geometry().constGet(); for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart ) { const QgsLineString *line = dynamic_cast<const QgsLineString *>( QgsGeometryCheckerUtils::getGeomPart( geom, iPart ) ); if ( !line ) { // Should not happen continue; } // Check whether the line intersects with any other features of the specified layer QgsGeometryCheckerUtils::LayerFeatures checkFeatures( mContext->featurePools, QStringList() << mCheckLayer, line->boundingBox(), {QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry}, mContext ); for ( const QgsGeometryCheckerUtils::LayerFeature &checkFeature : checkFeatures ) { const QgsAbstractGeometry *testGeom = checkFeature.geometry().constGet(); for ( int jPart = 0, mParts = testGeom->partCount(); jPart < mParts; ++jPart ) { const QgsAbstractGeometry *part = QgsGeometryCheckerUtils::getGeomPart( testGeom, jPart ); if ( const QgsLineString *testLine = dynamic_cast<const QgsLineString *>( part ) ) { const QList< QgsPoint > intersections = QgsGeometryCheckerUtils::lineIntersections( line, testLine, mContext->tolerance ); for ( const QgsPoint &inter : intersections ) { errors.append( new QgsGeometryCheckError( this, layerFeature, inter, QgsVertexId( iPart ), checkFeature.id() ) ); } } else if ( const QgsPolygon *polygon = dynamic_cast<const QgsPolygon *>( part ) ) { QList< const QgsLineString * > rings = QgsGeometryCheckerUtils::polygonRings( polygon ); for ( const QgsLineString *ring : rings ) { const QList< QgsPoint > intersections = QgsGeometryCheckerUtils::lineIntersections( line, ring, mContext->tolerance ); for ( const QgsPoint &inter : intersections ) { errors.append( new QgsGeometryCheckError( this, layerFeature, inter, QgsVertexId( iPart ), checkFeature.id() ) ); } } } } } } } }
void QgsGeometryTypeCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const { QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter ); for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures ) { const QgsAbstractGeometry *geom = layerFeature.geometry(); QgsWkbTypes::Type type = QgsWkbTypes::flatType( geom->wkbType() ); if ( ( mAllowedTypes & ( 1 << type ) ) == 0 ) { errors.append( new QgsGeometryTypeCheckError( this, layerFeature, geom->centroid(), type ) ); } } }
void QgsGeometryAreaCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const { QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter ); for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures ) { double layerToMapUnits = layerFeature.layerToMapUnits(); const QgsAbstractGeometry *geom = layerFeature.geometry(); for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart ) { double value; const QgsAbstractGeometry *part = QgsGeometryCheckerUtils::getGeomPart( geom, iPart ); if ( checkThreshold( layerToMapUnits, part, value ) ) { errors.append( new QgsGeometryCheckError( this, layerFeature, part->centroid(), QgsVertexId( iPart ), value * layerToMapUnits * layerToMapUnits, QgsGeometryCheckError::ValueArea ) ); } } } }
void QgsGeometrySelfIntersectionCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const { QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter, mContext ); for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures ) { const QgsAbstractGeometry *geom = layerFeature.geometry().constGet(); for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart ) { for ( int iRing = 0, nRings = geom->ringCount( iPart ); iRing < nRings; ++iRing ) { for ( const QgsGeometryUtils::SelfIntersection &inter : QgsGeometryUtils::selfIntersections( geom, iPart, iRing, mContext->tolerance ) ) { errors.append( new QgsGeometrySelfIntersectionCheckError( this, layerFeature, inter.point, QgsVertexId( iPart, iRing ), inter ) ); } } } } }
void QgsGeometryDegeneratePolygonCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const { QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter ); for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures ) { const QgsAbstractGeometry *geom = layerFeature.geometry(); for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart ) { for ( int iRing = 0, nRings = geom->ringCount( iPart ); iRing < nRings; ++iRing ) { if ( QgsGeometryCheckerUtils::polyLineSize( geom, iPart, iRing ) < 3 ) { QgsVertexId vidx( iPart, iRing ); errors.append( new QgsGeometryCheckError( this, layerFeature, geom->vertexAt( vidx ), vidx ) ); } } } } }
void QgsGeometryHoleCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &/*messages*/, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const { QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter ); for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures ) { const QgsAbstractGeometry *geom = layerFeature.geometry(); for ( int iPart = 0, nParts = geom->partCount(); iPart < nParts; ++iPart ) { const QgsCurvePolygon *poly = dynamic_cast<const QgsCurvePolygon *>( QgsGeometryCheckerUtils::getGeomPart( geom, iPart ) ); if ( !poly ) { continue; } // Rings after the first one are interiors for ( int iRing = 1, nRings = poly->ringCount( iPart ); iRing < nRings; ++iRing ) { QgsPoint pos = poly->interiorRing( iRing - 1 )->centroid(); errors.append( new QgsGeometryCheckError( this, layerFeature, pos, QgsVertexId( iPart, iRing ) ) ); } } } }
void QgsGeometryGapCheck::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const { if ( progressCounter ) progressCounter->fetchAndAddRelaxed( 1 ); QVector<QgsAbstractGeometry *> geomList; QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds, mCompatibleGeometryTypes, 0, true ); for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures ) { geomList.append( layerFeature.geometry()->clone() ); } if ( geomList.isEmpty() ) { return; } std::unique_ptr< QgsGeometryEngine > geomEngine = QgsGeometryCheckerUtils::createGeomEngine( nullptr, mContext->tolerance ); // Create union of geometry QString errMsg; QgsAbstractGeometry *unionGeom = geomEngine->combine( geomList, &errMsg ); qDeleteAll( geomList ); if ( !unionGeom ) { messages.append( tr( "Gap check: %1" ).arg( errMsg ) ); return; } // Get envelope of union geomEngine = QgsGeometryCheckerUtils::createGeomEngine( unionGeom, mContext->tolerance ); QgsAbstractGeometry *envelope = geomEngine->envelope( &errMsg ); if ( !envelope ) { messages.append( tr( "Gap check: %1" ).arg( errMsg ) ); delete unionGeom; return; } // Buffer envelope geomEngine = QgsGeometryCheckerUtils::createGeomEngine( envelope, mContext->tolerance ); QgsAbstractGeometry *bufEnvelope = geomEngine->buffer( 2, 0, GEOSBUF_CAP_SQUARE, GEOSBUF_JOIN_MITRE, 4. ); //#spellok //#spellok delete envelope; envelope = bufEnvelope; // Compute difference between envelope and union to obtain gap polygons geomEngine = QgsGeometryCheckerUtils::createGeomEngine( envelope, mContext->tolerance ); QgsAbstractGeometry *diffGeom = geomEngine->difference( unionGeom, &errMsg ); if ( !diffGeom ) { messages.append( tr( "Gap check: %1" ).arg( errMsg ) ); delete unionGeom; delete diffGeom; return; } // 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 ) { QgsAbstractGeometry *gapGeom = QgsGeometryCheckerUtils::getGeomPart( diffGeom, iPart )->clone(); // Skip the gap between features and boundingbox if ( gapGeom->boundingBox() == envelope->boundingBox() ) { continue; } // Skip gaps above threshold if ( gapGeom->area() > mThresholdMapUnits || gapGeom->area() < mContext->reducedTolerance ) { continue; } QgsRectangle gapAreaBBox = gapGeom->boundingBox(); // Get neighboring polygons QMap<QString, QgsFeatureIds> neighboringIds; QgsGeometryCheckerUtils::LayerFeatures layerFeatures( mContext->featurePools, featureIds.keys(), gapAreaBBox, mCompatibleGeometryTypes ); for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures ) { if ( QgsGeometryCheckerUtils::sharedEdgeLength( gapGeom, layerFeature.geometry(), mContext->reducedTolerance ) > 0 ) { neighboringIds[layerFeature.layer().id()].insert( layerFeature.feature().id() ); gapAreaBBox.combineExtentWith( layerFeature.geometry()->boundingBox() ); } } if ( neighboringIds.isEmpty() ) { delete gapGeom; continue; } // Add error errors.append( new QgsGeometryGapCheckError( this, "", gapGeom, neighboringIds, gapGeom->area(), gapAreaBBox ) ); } delete unionGeom; delete envelope; delete diffGeom; }