bool QgsGeometryEditUtils::deleteRing( QgsAbstractGeometry *geom, int ringNum, int partNum ) { if ( !geom || partNum < 0 ) { return false; } if ( ringNum < 1 ) //cannot remove exterior ring { return false; } QgsAbstractGeometry *g = geom; QgsGeometryCollection *c = qgsgeometry_cast<QgsGeometryCollection *>( geom ); if ( c ) { g = c->geometryN( partNum ); } else if ( partNum > 0 ) { //part num specified, but not a multi part geometry type return false; } QgsCurvePolygon *cpoly = qgsgeometry_cast<QgsCurvePolygon *>( g ); if ( !cpoly ) { return false; } return cpoly->removeInteriorRing( ringNum - 1 ); }
static bool lwcollection_make_geos_friendly( QgsGeometryCollection &g ) { for ( int i = 0; i < g.numGeometries(); i++ ) { if ( !lwgeom_make_geos_friendly( *g.geometryN( i ) ) ) return false; } return true; }
bool QgsGeometryEditUtils::deletePart( QgsAbstractGeometry *geom, int partNum ) { if ( !geom ) { return false; } QgsGeometryCollection *c = qgsgeometry_cast<QgsGeometryCollection *>( geom ); if ( !c ) { return false; } return c->removeGeometry( partNum ); }
QgsAbstractGeometry *QgsGeometryCollection::segmentize( double tolerance, SegmentationToleranceType toleranceType ) const { std::unique_ptr< QgsAbstractGeometry > geom( QgsGeometryFactory::geomFromWkbType( mWkbType ) ); QgsGeometryCollection *geomCollection = qgsgeometry_cast<QgsGeometryCollection *>( geom.get() ); if ( !geomCollection ) { return clone(); } QVector< QgsAbstractGeometry * >::const_iterator geomIt = mGeometries.constBegin(); for ( ; geomIt != mGeometries.constEnd(); ++geomIt ) { geomCollection->addGeometry( ( *geomIt )->segmentize( tolerance, toleranceType ) ); } return geom.release(); }
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" ) ); } }
QgsGeometry::OperationResult QgsGeometryEditUtils::addPart( QgsAbstractGeometry *geom, std::unique_ptr<QgsAbstractGeometry> part ) { if ( !geom ) { return QgsGeometry::InvalidBaseGeometry; } if ( !part ) { return QgsGeometry::InvalidInput; } //multitype? QgsGeometryCollection *geomCollection = qgsgeometry_cast<QgsGeometryCollection *>( geom ); if ( !geomCollection ) { return QgsGeometry::AddPartNotMultiGeometry; } bool added = false; if ( QgsWkbTypes::flatType( geom->wkbType() ) == QgsWkbTypes::MultiSurface || QgsWkbTypes::flatType( geom->wkbType() ) == QgsWkbTypes::MultiPolygon ) { QgsCurve *curve = qgsgeometry_cast<QgsCurve *>( part.get() ); if ( curve && curve->isClosed() && curve->numPoints() >= 4 ) { std::unique_ptr<QgsCurvePolygon> poly; if ( QgsWkbTypes::flatType( curve->wkbType() ) == QgsWkbTypes::LineString ) { poly = qgis::make_unique< QgsPolygonV2 >(); } else { poly = qgis::make_unique< QgsCurvePolygon >(); } // Ownership is still with part, curve points to the same object and is transferred // to poly here. part.release(); poly->setExteriorRing( curve ); added = geomCollection->addGeometry( poly.release() ); } else if ( QgsWkbTypes::flatType( part->wkbType() ) == QgsWkbTypes::Polygon ) { added = geomCollection->addGeometry( part.release() ); } else if ( QgsWkbTypes::flatType( part->wkbType() ) == QgsWkbTypes::MultiPolygon ) { std::unique_ptr<QgsGeometryCollection> parts( static_cast<QgsGeometryCollection *>( part.release() ) ); int i; int n = geomCollection->numGeometries(); for ( i = 0; i < parts->numGeometries() && geomCollection->addGeometry( parts->geometryN( i )->clone() ); i++ ) ; added = i == parts->numGeometries(); if ( !added ) { while ( geomCollection->numGeometries() > n ) geomCollection->removeGeometry( n ); return QgsGeometry::InvalidInput; } } else { return QgsGeometry::InvalidInput; } } else { added = geomCollection->addGeometry( part.release() ); } return added ? QgsGeometry::Success : QgsGeometry::InvalidInput; }
QgsGeometry::OperationResult QgsGeometryEditUtils::addRing( QgsAbstractGeometry *geom, std::unique_ptr<QgsCurve> ring ) { if ( !ring ) { return QgsGeometry::InvalidInput; } QList< QgsCurvePolygon * > polygonList; QgsCurvePolygon *curvePoly = qgsgeometry_cast< QgsCurvePolygon * >( geom ); QgsGeometryCollection *multiGeom = qgsgeometry_cast< QgsGeometryCollection * >( geom ); if ( curvePoly ) { polygonList.append( curvePoly ); } else if ( multiGeom ) { polygonList.reserve( multiGeom->numGeometries() ); for ( int i = 0; i < multiGeom->numGeometries(); ++i ) { polygonList.append( qgsgeometry_cast< QgsCurvePolygon * >( multiGeom->geometryN( i ) ) ); } } else { return QgsGeometry::InvalidInput; //not polygon / multipolygon; } //ring must be closed if ( !ring->isClosed() ) { return QgsGeometry::AddRingNotClosed; } else if ( !ring->isRing() ) { return QgsGeometry::AddRingNotValid; } std::unique_ptr<QgsGeometryEngine> ringGeom( QgsGeometry::createGeometryEngine( ring.get() ) ); ringGeom->prepareGeometry(); //for each polygon, test if inside outer ring and no intersection with other interior ring QList< QgsCurvePolygon * >::const_iterator polyIter = polygonList.constBegin(); for ( ; polyIter != polygonList.constEnd(); ++polyIter ) { if ( ringGeom->within( *polyIter ) ) { //check if disjoint with other interior rings int nInnerRings = ( *polyIter )->numInteriorRings(); for ( int i = 0; i < nInnerRings; ++i ) { if ( !ringGeom->disjoint( ( *polyIter )->interiorRing( i ) ) ) { return QgsGeometry::AddRingCrossesExistingRings; } } //make sure dimensionality of ring matches geometry if ( QgsWkbTypes::hasZ( geom->wkbType() ) ) ring->addZValue( 0 ); if ( QgsWkbTypes::hasM( geom->wkbType() ) ) ring->addMValue( 0 ); ( *polyIter )->addInteriorRing( ring.release() ); return QgsGeometry::Success; //success } } return QgsGeometry::AddRingNotInExistingFeature; //not contained in any outer ring }
std::unique_ptr< QgsAbstractGeometry > _qgis_lwgeom_make_valid( const QgsAbstractGeometry *lwgeom_in, QString &errorMessage ) { //bool is3d = FLAGS_GET_Z(lwgeom_in->flags); // try to convert to GEOS, if impossible, clean that up first // otherwise (adding only duplicates of existing points) GEOSContextHandle_t handle = QgsGeos::getGEOSHandler(); geos::unique_ptr geosgeom = QgsGeos::asGeos( lwgeom_in ); if ( !geosgeom ) { QgsDebugMsg( QStringLiteral( "Original geom can't be converted to GEOS - will try cleaning that up first" ) ); std::unique_ptr<QgsAbstractGeometry> lwgeom_in_clone( lwgeom_in->clone() ); if ( !lwgeom_make_geos_friendly( lwgeom_in_clone.get() ) ) { QgsDebugMsg( QStringLiteral( "Could not make a valid geometry out of input" ) ); } // try again as we did cleanup now // TODO: invoke LWGEOM2GEOS directly with autoclean ? geosgeom = QgsGeos::asGeos( lwgeom_in_clone.get() ); if ( ! geosgeom ) { errorMessage = QStringLiteral( "Could not convert QGIS geom to GEOS" ); return nullptr; } } else { QgsDebugMsgLevel( QStringLiteral( "original geom converted to GEOS" ), 4 ); } GEOSGeometry *geosout = LWGEOM_GEOS_makeValid( geosgeom.get(), errorMessage ); if ( !geosout ) return nullptr; std::unique_ptr< QgsAbstractGeometry > lwgeom_out = QgsGeos::fromGeos( geosout ); GEOSGeom_destroy_r( handle, geosout ); if ( !lwgeom_out ) return nullptr; // force multi-type if we had a multi-type before if ( QgsWkbTypes::isMultiType( lwgeom_in->wkbType() ) && !QgsWkbTypes::isMultiType( lwgeom_out->wkbType() ) ) { QgsGeometryCollection *collection = nullptr; switch ( QgsWkbTypes::multiType( lwgeom_out->wkbType() ) ) { case QgsWkbTypes::MultiPoint: collection = new QgsMultiPoint(); break; case QgsWkbTypes::MultiLineString: collection = new QgsMultiLineString(); break; case QgsWkbTypes::MultiPolygon: collection = new QgsMultiPolygon(); break; default: collection = new QgsGeometryCollection(); break; } collection->addGeometry( lwgeom_out.release() ); // takes ownership lwgeom_out.reset( collection ); } return lwgeom_out; }
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" ) ); } }