bool QgsGeometryCollection::deleteVertex( QgsVertexId position ) { if ( position.part < 0 || position.part >= mGeometries.size() ) { return false; } QgsAbstractGeometry *geom = mGeometries.at( position.part ); if ( !geom ) { return false; } bool success = geom->deleteVertex( position ); //remove geometry if no vertices left if ( geom->isEmpty() ) { removeGeometry( position.part ); } if ( success ) { clearCache(); //set bounding box invalid } return success; }
//! Generalize the WKB-geometry using the BBOX of the original geometry static QgsGeometry generalizeWkbGeometryByBoundingBox( QgsWkbTypes::Type wkbType, const QgsAbstractGeometry& geometry, const QgsRectangle &envelope ) { unsigned int geometryType = QgsWkbTypes::singleType( QgsWkbTypes::flatType( wkbType ) ); // If the geometry is already minimal skip the generalization int minimumSize = geometryType == QgsWkbTypes::LineString ? 2 : 5; if ( geometry.nCoordinates() <= minimumSize ) { return QgsGeometry( geometry.clone() ); } const double x1 = envelope.xMinimum(); const double y1 = envelope.yMinimum(); const double x2 = envelope.xMaximum(); const double y2 = envelope.yMaximum(); // Write the generalized geometry if ( geometryType == QgsWkbTypes::LineString ) { QgsLineString* lineString = new QgsLineString(); lineString->addVertex( QgsPointV2( x1, y1 ) ); lineString->addVertex( QgsPointV2( x2, y2 ) ); return QgsGeometry( lineString ); } else { return QgsGeometry::fromRect( envelope ); } }
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 ) ); } } } }
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 QgsMapToolIdentify::closestVertexAttributes( const QgsAbstractGeometry& geometry, QgsVertexId vId, QgsMapLayer *layer, QMap< QString, QString >& derivedAttributes ) { QString str = QLocale::system().toString( vId.vertex + 1 ); derivedAttributes.insert( tr( "Closest vertex number" ), str ); QgsPointV2 closestPoint = geometry.vertexAt( vId ); QgsPoint closestPointMapCoords = mCanvas->mapSettings().layerToMapCoordinates( layer, QgsPoint( closestPoint.x(), closestPoint.y() ) ); derivedAttributes.insert( "Closest vertex X", formatXCoordinate( closestPointMapCoords ) ); derivedAttributes.insert( "Closest vertex Y", formatYCoordinate( closestPointMapCoords ) ); if ( closestPoint.is3D() ) { str = QLocale::system().toString( closestPoint.z(), 'g', 10 ); derivedAttributes.insert( "Closest vertex Z", str ); } if ( closestPoint.isMeasure() ) { str = QLocale::system().toString( closestPoint.m(), 'g', 10 ); derivedAttributes.insert( "Closest vertex M", str ); } if ( vId.type == QgsVertexId::CurveVertex ) { double radius, centerX, centerY; QgsVertexId vIdBefore = vId; --vIdBefore.vertex; QgsVertexId vIdAfter = vId; ++vIdAfter.vertex; QgsGeometryUtils::circleCenterRadius( geometry.vertexAt( vIdBefore ), geometry.vertexAt( vId ), geometry.vertexAt( vIdAfter ), radius, centerX, centerY ); derivedAttributes.insert( "Closest vertex radius", QLocale::system().toString( radius ) ); } }
bool QgsGeometryCollection::nextVertex( QgsVertexId &id, QgsPoint &vertex ) const { if ( id.part < 0 ) { id.part = 0; id.ring = -1; id.vertex = -1; } if ( mGeometries.isEmpty() ) { return false; } if ( id.part >= mGeometries.count() ) return false; QgsAbstractGeometry *geom = mGeometries.at( id.part ); if ( geom->nextVertex( id, vertex ) ) { return true; } if ( ( id.part + 1 ) >= numGeometries() ) { return false; } ++id.part; id.ring = -1; id.vertex = -1; return mGeometries.at( id.part )->nextVertex( id, vertex ); }
// Ensure the geometry is "structurally" valid (enough for GEOS to accept it) static bool lwgeom_make_geos_friendly( QgsAbstractGeometry &geom ) { QgsDebugMsg( QString( "lwgeom_make_geos_friendly enter (type %1)" ).arg( geom.wkbType() ) ); switch ( QgsWkbTypes::flatType( geom.wkbType() ) ) { case QgsWkbTypes::Point: case QgsWkbTypes::MultiPoint: // a point is always valid return true; break; case QgsWkbTypes::LineString: // lines need at least 2 points return lwline_make_geos_friendly( dynamic_cast<QgsLineString &>( geom ) ); break; case QgsWkbTypes::Polygon: // polygons need all rings closed and with npoints > 3 return lwpoly_make_geos_friendly( dynamic_cast<QgsPolygonV2 &>( geom ) ); break; case QgsWkbTypes::MultiLineString: case QgsWkbTypes::MultiPolygon: case QgsWkbTypes::GeometryCollection: return lwcollection_make_geos_friendly( dynamic_cast<QgsGeometryCollection &>( geom ) ); break; default: QgsDebugMsg( QString( "lwgeom_make_geos_friendly: unsupported input geometry type: %1" ).arg( geom.wkbType() ) ); break; } return false; }
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" ) ); } }
Qt3DRender::QGeometryRenderer *QgsLine3DSymbolEntityNode::renderer( const Qgs3DMapSettings &map, const QgsLine3DSymbol &symbol, const QgsVectorLayer *layer, const QgsFeatureRequest &request ) { QgsPointXY origin( map.origin().x(), map.origin().y() ); // TODO: configurable int nSegments = 4; QgsGeometry::EndCapStyle endCapStyle = QgsGeometry::CapRound; QgsGeometry::JoinStyle joinStyle = QgsGeometry::JoinStyleRound; double mitreLimit = 0; QList<QgsPolygon *> polygons; QgsFeature f; QgsFeatureIterator fi = layer->getFeatures( request ); while ( fi.nextFeature( f ) ) { if ( f.geometry().isNull() ) continue; QgsGeometry geom = f.geometry(); // segmentize curved geometries if necessary if ( QgsWkbTypes::isCurvedType( geom.constGet()->wkbType() ) ) geom = QgsGeometry( geom.constGet()->segmentize() ); const QgsAbstractGeometry *g = geom.constGet(); QgsGeos engine( g ); QgsAbstractGeometry *buffered = engine.buffer( symbol.width() / 2., nSegments, endCapStyle, joinStyle, mitreLimit ); // factory if ( QgsWkbTypes::flatType( buffered->wkbType() ) == QgsWkbTypes::Polygon ) { QgsPolygon *polyBuffered = static_cast<QgsPolygon *>( buffered ); Qgs3DUtils::clampAltitudes( polyBuffered, symbol.altitudeClamping(), symbol.altitudeBinding(), symbol.height(), map ); polygons.append( polyBuffered ); } else if ( QgsWkbTypes::flatType( buffered->wkbType() ) == QgsWkbTypes::MultiPolygon ) { QgsMultiPolygon *mpolyBuffered = static_cast<QgsMultiPolygon *>( buffered ); for ( int i = 0; i < mpolyBuffered->numGeometries(); ++i ) { QgsAbstractGeometry *partBuffered = mpolyBuffered->geometryN( i ); Q_ASSERT( QgsWkbTypes::flatType( partBuffered->wkbType() ) == QgsWkbTypes::Polygon ); QgsPolygon *polyBuffered = static_cast<QgsPolygon *>( partBuffered )->clone(); // need to clone individual geometry parts Qgs3DUtils::clampAltitudes( polyBuffered, symbol.altitudeClamping(), symbol.altitudeBinding(), symbol.height(), map ); polygons.append( polyBuffered ); } delete buffered; } } mGeometry = new QgsTessellatedPolygonGeometry; mGeometry->setPolygons( polygons, origin, symbol.extrusionHeight() ); Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer; renderer->setGeometry( mGeometry ); return renderer; }
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 QgsMapToolNodeTool::createTopologyRubberBands() { QgsVectorLayer* vlayer = mSelectedFeature->vlayer(); Q_FOREACH ( const QgsVertexEntry* vertexEntry, mSelectedFeature->vertexMap() ) { if ( !vertexEntry->isSelected() ) { continue; } // Snap vertex QMultiMap<double, QgsSnappingResult> snapResults; vlayer->snapWithContext( vertexEntry->pointV1(), ZERO_TOLERANCE, snapResults, QgsSnapper::SnapToVertex ); Q_FOREACH ( const QgsSnappingResult& snapResult, snapResults ) { // Get geometry of snapped feature QgsFeatureId snapFeatureId = snapResult.snappedAtGeometry; QgsFeature feature; if ( !vlayer->getFeatures( QgsFeatureRequest( snapFeatureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( feature ) ) { continue; } // Get VertexId of snapped vertex QgsVertexId vid; if ( !feature.geometry().vertexIdFromVertexNr( snapResult.snappedVertexNr, vid ) ) { continue; } // Add rubberband if not already added if ( !mMoveRubberBands.contains( snapFeatureId ) ) { QgsGeometryRubberBand* rb = new QgsGeometryRubberBand( mCanvas, feature.geometry().type() ); QSettings settings; QColor color( settings.value( QStringLiteral( "/qgis/digitizing/line_color_red" ), 255 ).toInt(), settings.value( QStringLiteral( "/qgis/digitizing/line_color_green" ), 0 ).toInt(), settings.value( QStringLiteral( "/qgis/digitizing/line_color_blue" ), 0 ).toInt() ); double myAlpha = settings.value( QStringLiteral( "/qgis/digitizing/line_color_alpha" ), 30 ).toInt() / 255.0 ; color.setAlphaF( myAlpha ); rb->setOutlineColor( color ); rb->setBrushStyle( Qt::NoBrush ); rb->setOutlineWidth( settings.value( QStringLiteral( "/qgis/digitizing/line_width" ), 1 ).toInt() ); QgsAbstractGeometry* rbGeom = feature.geometry().geometry()->clone(); if ( mCanvas->mapSettings().layerTransform( vlayer ).isValid() ) rbGeom->transform( mCanvas->mapSettings().layerTransform( vlayer ) ); rb->setGeometry( rbGeom ); mMoveRubberBands.insert( snapFeatureId, rb ); } // Add to list of vertices to be moved mMoveVertices[snapFeatureId].append( qMakePair( vid, toMapCoordinates( vlayer, feature.geometry().geometry()->vertexAt( vid ) ) ) ); } }
double QgsGeometryCollection::vertexAngle( QgsVertexId vertex ) const { if ( vertex.part < 0 || vertex.part >= mGeometries.size() ) { return 0.0; } QgsAbstractGeometry *geom = mGeometries[vertex.part]; if ( !geom ) { return 0.0; } return geom->vertexAngle( vertex ); }
void QgsBufferedLine3DSymbolHandler::processFeature( QgsFeature &f, const Qgs3DRenderContext &context ) { if ( f.geometry().isNull() ) return; LineData &out = mSelectedIds.contains( f.id() ) ? outSelected : outNormal; QgsGeometry geom = f.geometry(); // segmentize curved geometries if necessary if ( QgsWkbTypes::isCurvedType( geom.constGet()->wkbType() ) ) geom = QgsGeometry( geom.constGet()->segmentize() ); const QgsAbstractGeometry *g = geom.constGet(); // TODO: configurable const int nSegments = 4; const QgsGeometry::EndCapStyle endCapStyle = QgsGeometry::CapRound; const QgsGeometry::JoinStyle joinStyle = QgsGeometry::JoinStyleRound; const double mitreLimit = 0; QgsGeos engine( g ); QgsAbstractGeometry *buffered = engine.buffer( mSymbol.width() / 2., nSegments, endCapStyle, joinStyle, mitreLimit ); // factory if ( QgsWkbTypes::flatType( buffered->wkbType() ) == QgsWkbTypes::Polygon ) { QgsPolygon *polyBuffered = static_cast<QgsPolygon *>( buffered ); Qgs3DUtils::clampAltitudes( polyBuffered, mSymbol.altitudeClamping(), mSymbol.altitudeBinding(), mSymbol.height(), context.map() ); out.polygons.append( polyBuffered ); out.fids.append( f.id() ); } else if ( QgsWkbTypes::flatType( buffered->wkbType() ) == QgsWkbTypes::MultiPolygon ) { QgsMultiPolygon *mpolyBuffered = static_cast<QgsMultiPolygon *>( buffered ); for ( int i = 0; i < mpolyBuffered->numGeometries(); ++i ) { QgsAbstractGeometry *partBuffered = mpolyBuffered->geometryN( i ); Q_ASSERT( QgsWkbTypes::flatType( partBuffered->wkbType() ) == QgsWkbTypes::Polygon ); QgsPolygon *polyBuffered = static_cast<QgsPolygon *>( partBuffered )->clone(); // need to clone individual geometry parts Qgs3DUtils::clampAltitudes( polyBuffered, mSymbol.altitudeClamping(), mSymbol.altitudeBinding(), mSymbol.height(), context.map() ); out.polygons.append( polyBuffered ); out.fids.append( f.id() ); } delete buffered; } }
void QgsMapToolIdentify::closestPointAttributes( const QgsAbstractGeometry &geometry, QgsMapLayer *layer, const QgsPointXY &layerPoint, QMap< QString, QString > &derivedAttributes ) { Q_UNUSED( layer ); // measure if ( QgsWkbTypes::hasM( geometry.wkbType() ) ) { QgsPoint closestPoint = QgsGeometryUtils::closestPoint( geometry, QgsPoint( layerPoint.x(), layerPoint.y() ) ); QString str = QLocale::system().toString( closestPoint.m(), 'g', 10 ); derivedAttributes.insert( QStringLiteral( "Closest point M" ), str ); } }
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 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 QgsGeometryRubberBand::paint( QPainter *painter ) { if ( !mGeometry || !painter ) { return; } painter->save(); painter->translate( -pos() ); if ( mGeometryType == QgsWkbTypes::PolygonGeometry ) { painter->setBrush( mBrush ); } else { painter->setBrush( Qt::NoBrush ); } painter->setPen( mPen ); QgsAbstractGeometry *paintGeom = mGeometry->clone(); paintGeom->transform( mMapCanvas->getCoordinateTransform()->transform() ); paintGeom->draw( *painter ); //draw vertices QgsVertexId vertexId; QgsPoint vertex; while ( paintGeom->nextVertex( vertexId, vertex ) ) { drawVertex( painter, vertex.x(), vertex.y() ); } delete paintGeom; painter->restore(); }
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::collectErrors( QList<QgsGeometryCheckError *> &errors, QStringList &messages, QAtomicInt *progressCounter, const QMap<QString, QgsFeatureIds> &ids ) const { double overlapThreshold = mThresholdMapUnits; QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds() : ids; QgsGeometryCheckerUtils::LayerFeatures layerFeaturesA( mContext->featurePools, featureIds, mCompatibleGeometryTypes, progressCounter, true ); QList<QString> layerIds = featureIds.keys(); for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeatureA : layerFeaturesA ) { // Ensure each pair of layers only gets compared once: remove the current layer from the layerIds, but add it to the layerList for layerFeaturesB layerIds.removeOne( layerFeatureA.layer().id() ); QgsRectangle bboxA = layerFeatureA.geometry()->boundingBox(); std::unique_ptr< QgsGeometryEngine > geomEngineA = QgsGeometryCheckerUtils::createGeomEngine( layerFeatureA.geometry(), mContext->tolerance ); if ( !geomEngineA->isValid() ) { messages.append( tr( "Overlap check failed for (%1): the geometry is invalid" ).arg( layerFeatureA.id() ) ); continue; } QgsGeometryCheckerUtils::LayerFeatures layerFeaturesB( mContext->featurePools, QList<QString>() << layerFeatureA.layer().id() << layerIds, bboxA, mCompatibleGeometryTypes ); for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeatureB : layerFeaturesB ) { // > : only report overlaps within same layer once if ( layerFeatureA.layer().id() == layerFeatureB.layer().id() && layerFeatureB.feature().id() >= layerFeatureA.feature().id() ) { continue; } QString errMsg; if ( geomEngineA->overlaps( layerFeatureB.geometry(), &errMsg ) ) { QgsAbstractGeometry *interGeom = geomEngineA->intersection( layerFeatureB.geometry() ); if ( interGeom && !interGeom->isEmpty() ) { QgsGeometryCheckerUtils::filter1DTypes( interGeom ); for ( int iPart = 0, nParts = interGeom->partCount(); iPart < nParts; ++iPart ) { QgsAbstractGeometry *interPart = QgsGeometryCheckerUtils::getGeomPart( interGeom, iPart ); double area = interPart->area(); if ( area > mContext->reducedTolerance && area < overlapThreshold ) { errors.append( new QgsGeometryOverlapCheckError( this, layerFeatureA, interPart->clone(), interPart->centroid(), area, layerFeatureB ) ); } } } else if ( !errMsg.isEmpty() ) { messages.append( tr( "Overlap check between features %1 and %2 %3" ).arg( layerFeatureA.id() ).arg( layerFeatureB.id() ).arg( errMsg ) ); } delete interGeom; } } } }
bool QgsGeometryAreaCheck::mergeWithNeighbor( const QString &layerId, QgsFeature &feature, int partIdx, int method, int mergeAttributeIndex, Changes &changes, QString &errMsg ) const { QgsFeaturePool *featurePool = mContext->featurePools[ layerId ]; double maxVal = 0.; QgsFeature mergeFeature; int mergePartIdx = -1; bool matchFound = false; QgsGeometry featureGeometry = feature.geometry(); const QgsAbstractGeometry *geom = featureGeometry.constGet(); // Search for touching neighboring geometries const QgsFeatureIds intersects = featurePool->getIntersects( featureGeometry.boundingBox() ); for ( QgsFeatureId testId : intersects ) { QgsFeature testFeature; if ( !featurePool->get( testId, testFeature ) ) { continue; } QgsGeometry testFeatureGeom = testFeature.geometry(); const QgsAbstractGeometry *testGeom = testFeatureGeom.constGet(); for ( int testPartIdx = 0, nTestParts = testGeom->partCount(); testPartIdx < nTestParts; ++testPartIdx ) { if ( testId == feature.id() && testPartIdx == partIdx ) { continue; } double len = QgsGeometryCheckerUtils::sharedEdgeLength( QgsGeometryCheckerUtils::getGeomPart( geom, partIdx ), QgsGeometryCheckerUtils::getGeomPart( testGeom, testPartIdx ), mContext->reducedTolerance ); if ( len > 0. ) { if ( method == MergeLongestEdge || method == MergeLargestArea ) { double val; if ( method == MergeLongestEdge ) { val = len; } else { if ( dynamic_cast<const QgsGeometryCollection *>( testGeom ) ) val = static_cast<const QgsGeometryCollection *>( 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; } // Merge geometries QgsGeometry mergeFeatureGeom = mergeFeature.geometry(); const QgsAbstractGeometry *mergeGeom = mergeFeatureGeom.constGet(); std::unique_ptr<QgsGeometryEngine> geomEngine( QgsGeometryCheckerUtils::createGeomEngine( QgsGeometryCheckerUtils::getGeomPart( mergeGeom, mergePartIdx ), mContext->reducedTolerance ) ); QgsAbstractGeometry *combinedGeom = geomEngine->combine( QgsGeometryCheckerUtils::getGeomPart( geom, partIdx ), &errMsg ); if ( !combinedGeom || combinedGeom->isEmpty() || !QgsWkbTypes::isSingleType( combinedGeom->wkbType() ) ) { return false; } // Replace polygon in merge geometry if ( mergeFeature.id() == feature.id() && mergePartIdx > partIdx ) { --mergePartIdx; } replaceFeatureGeometryPart( layerId, mergeFeature, mergePartIdx, combinedGeom, changes ); // Remove polygon from source geometry deleteFeatureGeometryPart( layerId, feature, partIdx, changes ); return true; }
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" ) ); } }
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; }
//! Simplify the WKB-geometry using the specified tolerance QgsGeometry QgsMapToPixelSimplifier::simplifyGeometry( int simplifyFlags, SimplifyAlgorithm simplifyAlgorithm, QgsWkbTypes::Type wkbType, const QgsAbstractGeometry& geometry, const QgsRectangle &envelope, double map2pixelTol, bool isaLinearRing ) { bool isGeneralizable = true; // Can replace the geometry by its BBOX ? if (( simplifyFlags & QgsMapToPixelSimplifier::SimplifyEnvelope ) && isGeneralizableByMapBoundingBox( envelope, map2pixelTol ) ) { return generalizeWkbGeometryByBoundingBox( wkbType, geometry, envelope ); } if ( !( simplifyFlags & QgsMapToPixelSimplifier::SimplifyGeometry ) ) isGeneralizable = false; const QgsWkbTypes::Type flatType = QgsWkbTypes::flatType( wkbType ); // Write the geometry if ( flatType == QgsWkbTypes::LineString || flatType == QgsWkbTypes::CircularString ) { const QgsCurve& srcCurve = dynamic_cast<const QgsCurve&>( geometry ); QScopedPointer<QgsCurve> output( createEmptySameTypeGeom( srcCurve ) ); double x = 0.0, y = 0.0, lastX = 0.0, lastY = 0.0; QgsRectangle r; r.setMinimal(); const int numPoints = srcCurve.numPoints(); if ( numPoints <= ( isaLinearRing ? 4 : 2 ) ) isGeneralizable = false; bool isLongSegment; bool hasLongSegments = false; //-> To avoid replace the simplified geometry by its BBOX when there are 'long' segments. // Check whether the LinearRing is really closed. if ( isaLinearRing ) { isaLinearRing = qgsDoubleNear( srcCurve.xAt( 0 ), srcCurve.xAt( numPoints - 1 ) ) && qgsDoubleNear( srcCurve.yAt( 0 ), srcCurve.yAt( numPoints - 1 ) ); } // Process each vertex... switch ( simplifyAlgorithm ) { case SnapToGrid: { double gridOriginX = envelope.xMinimum(); double gridOriginY = envelope.yMinimum(); // Use a factor for the maximum displacement distance for simplification, similar as GeoServer does float gridInverseSizeXY = map2pixelTol != 0 ? ( float )( 1.0f / ( 0.8 * map2pixelTol ) ) : 0.0f; for ( int i = 0; i < numPoints; ++i ) { x = srcCurve.xAt( i ); y = srcCurve.yAt( i ); if ( i == 0 || !isGeneralizable || !equalSnapToGrid( x, y, lastX, lastY, gridOriginX, gridOriginY, gridInverseSizeXY ) || ( !isaLinearRing && ( i == 1 || i >= numPoints - 2 ) ) ) { output->insertVertex( QgsVertexId( 0, 0, output->numPoints() ), QgsPointV2( x, y ) ); lastX = x; lastY = y; } r.combineExtentWith( x, y ); } break; } case Visvalingam: { map2pixelTol *= map2pixelTol; //-> Use mappixelTol for 'Area' calculations. EFFECTIVE_AREAS ea( srcCurve ); int set_area = 0; ptarray_calc_areas( &ea, isaLinearRing ? 4 : 2, set_area, map2pixelTol ); for ( int i = 0; i < numPoints; ++i ) { if ( ea.res_arealist[ i ] > map2pixelTol ) { output->insertVertex( QgsVertexId( 0, 0, output->numPoints() ), ea.inpts.at( i ) ); } } break; } case Distance: { map2pixelTol *= map2pixelTol; //-> Use mappixelTol for 'LengthSquare' calculations. for ( int i = 0; i < numPoints; ++i ) { x = srcCurve.xAt( i ); y = srcCurve.yAt( i ); isLongSegment = false; if ( i == 0 || !isGeneralizable || ( isLongSegment = ( calculateLengthSquared2D( x, y, lastX, lastY ) > map2pixelTol ) ) || ( !isaLinearRing && ( i == 1 || i >= numPoints - 2 ) ) ) { output->insertVertex( QgsVertexId( 0, 0, output->numPoints() ), QgsPointV2( x, y ) ); lastX = x; lastY = y; hasLongSegments |= isLongSegment; } r.combineExtentWith( x, y ); } } } if ( output->numPoints() < ( isaLinearRing ? 4 : 2 ) ) { // we simplified the geometry too much! if ( !hasLongSegments ) { // approximate the geometry's shape by its bounding box // (rect for linear ring / one segment for line string) return generalizeWkbGeometryByBoundingBox( wkbType, geometry, r ); } else { // Bad luck! The simplified geometry is invalid and approximation by bounding box // would create artifacts due to long segments. // We will return the original geometry return QgsGeometry( geometry.clone() ); } } if ( isaLinearRing ) { // make sure we keep the linear ring closed if ( !qgsDoubleNear( lastX, output->xAt( 0 ) ) || !qgsDoubleNear( lastY, output->yAt( 0 ) ) ) { output->insertVertex( QgsVertexId( 0, 0, output->numPoints() ), QgsPointV2( output->xAt( 0 ), output->yAt( 0 ) ) ); } } return QgsGeometry( output.take() ); } else if ( flatType == QgsWkbTypes::Polygon ) { const QgsPolygonV2& srcPolygon = dynamic_cast<const QgsPolygonV2&>( geometry ); QScopedPointer<QgsPolygonV2> polygon( new QgsPolygonV2() ); polygon->setExteriorRing( dynamic_cast<QgsCurve*>( simplifyGeometry( simplifyFlags, simplifyAlgorithm, srcPolygon.exteriorRing()->wkbType(), *srcPolygon.exteriorRing(), envelope, map2pixelTol, true ).geometry()->clone() ) ); for ( int i = 0; i < srcPolygon.numInteriorRings(); ++i ) { const QgsCurve* sub = srcPolygon.interiorRing( i ); polygon->addInteriorRing( dynamic_cast<QgsCurve*>( simplifyGeometry( simplifyFlags, simplifyAlgorithm, sub->wkbType(), *sub, envelope, map2pixelTol, true ).geometry()->clone() ) ); } return QgsGeometry( polygon.take() ); } else if ( QgsWkbTypes::isMultiType( flatType ) ) { const QgsGeometryCollection& srcCollection = dynamic_cast<const QgsGeometryCollection&>( geometry ); QScopedPointer<QgsGeometryCollection> collection( createEmptySameTypeGeom( srcCollection ) ); const int numGeoms = srcCollection.numGeometries(); for ( int i = 0; i < numGeoms; ++i ) { const QgsAbstractGeometry* sub = srcCollection.geometryN( i ); collection->addGeometry( simplifyGeometry( simplifyFlags, simplifyAlgorithm, sub->wkbType(), *sub, envelope, map2pixelTol, false ).geometry()->clone() ); } return QgsGeometry( collection.take() ); } return QgsGeometry( geometry.clone() ); }
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; }
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" ) ); } }
std::unique_ptr<QgsAbstractGeometry> QgsGeometryEditUtils::avoidIntersections( const QgsAbstractGeometry &geom, const QList<QgsVectorLayer *> &avoidIntersectionsLayers, const QHash<QgsVectorLayer *, QSet<QgsFeatureId> > &ignoreFeatures ) { std::unique_ptr<QgsGeometryEngine> geomEngine( QgsGeometry::createGeometryEngine( &geom ) ); if ( !geomEngine ) { return nullptr; } QgsWkbTypes::Type geomTypeBeforeModification = geom.wkbType(); //check if g has polygon type if ( QgsWkbTypes::geometryType( geomTypeBeforeModification ) != QgsWkbTypes::PolygonGeometry ) { return nullptr; } if ( avoidIntersectionsLayers.isEmpty() ) return nullptr; //no intersections stored in project does not mean error QList< QgsGeometry > nearGeometries; //go through list, convert each layer to vector layer and call QgsVectorLayer::removePolygonIntersections for each for ( QgsVectorLayer *currentLayer : avoidIntersectionsLayers ) { QgsFeatureIds ignoreIds; QHash<QgsVectorLayer *, QSet<qint64> >::const_iterator ignoreIt = ignoreFeatures.constFind( currentLayer ); if ( ignoreIt != ignoreFeatures.constEnd() ) ignoreIds = ignoreIt.value(); QgsFeatureIterator fi = currentLayer->getFeatures( QgsFeatureRequest( geom.boundingBox() ) .setFlags( QgsFeatureRequest::ExactIntersect ) .setSubsetOfAttributes( QgsAttributeList() ) ); QgsFeature f; while ( fi.nextFeature( f ) ) { if ( ignoreIds.contains( f.id() ) ) continue; if ( !f.hasGeometry() ) continue; nearGeometries << f.geometry(); } } if ( nearGeometries.isEmpty() ) { return nullptr; } std::unique_ptr< QgsAbstractGeometry > combinedGeometries( geomEngine->combine( nearGeometries ) ); if ( !combinedGeometries ) { return nullptr; } std::unique_ptr< QgsAbstractGeometry > diffGeom( geomEngine->difference( combinedGeometries.get() ) ); return diffGeom; }
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; }