int QgsGeometrySnapperSingleSource::run( const QgsFeatureSource &source, QgsFeatureSink &sink, double thresh, QgsFeedback *feedback ) { // the logic here comes from GRASS implementation of Vect_snap_lines_list() int count = 0; int totalCount = source.featureCount() * 2; // step 1: record all point locations in a spatial index + extra data structure to keep // reference to which other point they have been snapped to (in the next phase). QgsSpatialIndex index; QVector<AnchorPoint> pnts; QgsFeatureRequest request; request.setSubsetOfAttributes( QgsAttributeList() ); QgsFeatureIterator fi = source.getFeatures( request ); buildSnapIndex( fi, index, pnts, feedback, count, totalCount ); if ( feedback->isCanceled() ) return 0; // step 2: go through all registered points and if not yet marked mark it as anchor and // assign this anchor to all not yet marked points in threshold assignAnchors( index, pnts, thresh ); // step 3: alignment of vertices and segments to the anchors // Go through all lines and: // 1) for all vertices: if not anchor snap it to its anchor // 2) for all segments: snap it to all anchors in threshold (except anchors of vertices of course) int modified = 0; QgsFeature f; fi = source.getFeatures(); while ( fi.nextFeature( f ) ) { if ( feedback->isCanceled() ) break; QgsGeometry geom = f.geometry(); if ( snapGeometry( geom.get(), index, pnts, thresh ) ) { f.setGeometry( geom ); ++modified; } sink.addFeature( f, QgsFeatureSink::FastInsert ); ++count; feedback->setProgress( 100. * count / totalCount ); } return modified; }
void QgsOverlayUtils::difference( const QgsFeatureSource &sourceA, const QgsFeatureSource &sourceB, QgsFeatureSink &sink, QgsProcessingContext &context, QgsProcessingFeedback *feedback, int &count, int totalCount, QgsOverlayUtils::DifferenceOutput outputAttrs ) { QgsFeatureRequest requestB; requestB.setSubsetOfAttributes( QgsAttributeList() ); if ( outputAttrs != OutputBA ) requestB.setDestinationCrs( sourceA.sourceCrs(), context.transformContext() ); QgsSpatialIndex indexB( sourceB.getFeatures( requestB ), feedback ); int fieldsCountA = sourceA.fields().count(); int fieldsCountB = sourceB.fields().count(); QgsAttributes attrs; attrs.resize( outputAttrs == OutputA ? fieldsCountA : ( fieldsCountA + fieldsCountB ) ); if ( totalCount == 0 ) totalCount = 1; // avoid division by zero QgsFeature featA; QgsFeatureRequest requestA; if ( outputAttrs == OutputBA ) requestA.setDestinationCrs( sourceB.sourceCrs(), context.transformContext() ); QgsFeatureIterator fitA = sourceA.getFeatures( requestA ); while ( fitA.nextFeature( featA ) ) { if ( feedback->isCanceled() ) break; if ( featA.hasGeometry() ) { QgsGeometry geom( featA.geometry() ); QgsFeatureIds intersects = indexB.intersects( geom.boundingBox() ).toSet(); QgsFeatureRequest request; request.setFilterFids( intersects ); request.setSubsetOfAttributes( QgsAttributeList() ); if ( outputAttrs != OutputBA ) request.setDestinationCrs( sourceA.sourceCrs(), context.transformContext() ); std::unique_ptr< QgsGeometryEngine > engine; if ( !intersects.isEmpty() ) { // use prepared geometries for faster intersection tests engine.reset( QgsGeometry::createGeometryEngine( geom.constGet() ) ); engine->prepareGeometry(); } QVector<QgsGeometry> geometriesB; QgsFeature featB; QgsFeatureIterator fitB = sourceB.getFeatures( request ); while ( fitB.nextFeature( featB ) ) { if ( feedback->isCanceled() ) break; if ( engine->intersects( featB.geometry().constGet() ) ) geometriesB << featB.geometry(); } if ( !geometriesB.isEmpty() ) { QgsGeometry geomB = QgsGeometry::unaryUnion( geometriesB ); geom = geom.difference( geomB ); } if ( !sanitizeDifferenceResult( geom ) ) continue; const QgsAttributes attrsA( featA.attributes() ); switch ( outputAttrs ) { case OutputA: attrs = attrsA; break; case OutputAB: for ( int i = 0; i < fieldsCountA; ++i ) attrs[i] = attrsA[i]; break; case OutputBA: for ( int i = 0; i < fieldsCountA; ++i ) attrs[i + fieldsCountB] = attrsA[i]; break; } QgsFeature outFeat; outFeat.setGeometry( geom ); outFeat.setAttributes( attrs ); sink.addFeature( outFeat, QgsFeatureSink::FastInsert ); } else { // TODO: should we write out features that do not have geometry? sink.addFeature( featA, QgsFeatureSink::FastInsert ); } ++count; feedback->setProgress( count / ( double ) totalCount * 100. ); } }
void QgsOverlayUtils::resolveOverlaps( const QgsFeatureSource &source, QgsFeatureSink &sink, QgsProcessingFeedback *feedback ) { int count = 0; int totalCount = source.featureCount(); if ( totalCount == 0 ) return; // nothing to do here QgsFeatureId newFid = -1; QgsWkbTypes::GeometryType geometryType = QgsWkbTypes::geometryType( QgsWkbTypes::multiType( source.wkbType() ) ); QgsFeatureRequest requestOnlyGeoms; requestOnlyGeoms.setSubsetOfAttributes( QgsAttributeList() ); QgsFeatureRequest requestOnlyAttrs; requestOnlyAttrs.setFlags( QgsFeatureRequest::NoGeometry ); QgsFeatureRequest requestOnlyIds; requestOnlyIds.setFlags( QgsFeatureRequest::NoGeometry ); requestOnlyIds.setSubsetOfAttributes( QgsAttributeList() ); // make a set of used feature IDs so that we do not try to reuse them for newly added features QgsFeature f; QSet<QgsFeatureId> fids; QgsFeatureIterator it = source.getFeatures( requestOnlyIds ); while ( it.nextFeature( f ) ) { if ( feedback->isCanceled() ) return; fids.insert( f.id() ); } QHash<QgsFeatureId, QgsGeometry> geometries; QgsSpatialIndex index; QHash<QgsFeatureId, QList<QgsFeatureId> > intersectingIds; // which features overlap a particular area // resolve intersections it = source.getFeatures( requestOnlyGeoms ); while ( it.nextFeature( f ) ) { if ( feedback->isCanceled() ) return; QgsFeatureId fid1 = f.id(); QgsGeometry g1 = f.geometry(); std::unique_ptr< QgsGeometryEngine > g1engine; geometries.insert( fid1, g1 ); index.insertFeature( f ); QgsRectangle bbox( f.geometry().boundingBox() ); const QList<QgsFeatureId> ids = index.intersects( bbox ); for ( QgsFeatureId fid2 : ids ) { if ( fid1 == fid2 ) continue; if ( !g1engine ) { // use prepared geometries for faster intersection tests g1engine.reset( QgsGeometry::createGeometryEngine( g1.constGet() ) ); g1engine->prepareGeometry(); } QgsGeometry g2 = geometries.value( fid2 ); if ( !g1engine->intersects( g2.constGet() ) ) continue; QgsGeometry geomIntersection = g1.intersection( g2 ); if ( !sanitizeIntersectionResult( geomIntersection, geometryType ) ) continue; // // add intersection geometry // // figure out new fid while ( fids.contains( newFid ) ) --newFid; fids.insert( newFid ); geometries.insert( newFid, geomIntersection ); QgsFeature fx( newFid ); fx.setGeometry( geomIntersection ); index.insertFeature( fx ); // figure out which feature IDs belong to this intersection. Some of the IDs can be of the newly // created geometries - in such case we need to retrieve original IDs QList<QgsFeatureId> lst; if ( intersectingIds.contains( fid1 ) ) lst << intersectingIds.value( fid1 ); else lst << fid1; if ( intersectingIds.contains( fid2 ) ) lst << intersectingIds.value( fid2 ); else lst << fid2; intersectingIds.insert( newFid, lst ); // // update f1 // QgsGeometry g12 = g1.difference( g2 ); index.deleteFeature( f ); geometries.remove( fid1 ); if ( sanitizeDifferenceResult( g12 ) ) { geometries.insert( fid1, g12 ); QgsFeature f1x( fid1 ); f1x.setGeometry( g12 ); index.insertFeature( f1x ); } // // update f2 // QgsGeometry g21 = g2.difference( g1 ); QgsFeature f2old( fid2 ); f2old.setGeometry( g2 ); index.deleteFeature( f2old ); geometries.remove( fid2 ); if ( sanitizeDifferenceResult( g21 ) ) { geometries.insert( fid2, g21 ); QgsFeature f2x( fid2 ); f2x.setGeometry( g21 ); index.insertFeature( f2x ); } // update our temporary copy of the geometry to what is left from it g1 = g12; g1engine.reset(); } ++count; feedback->setProgress( count / ( double ) totalCount * 100. ); } // release some memory of structures we don't need anymore fids.clear(); index = QgsSpatialIndex(); // load attributes QHash<QgsFeatureId, QgsAttributes> attributesHash; it = source.getFeatures( requestOnlyAttrs ); while ( it.nextFeature( f ) ) { if ( feedback->isCanceled() ) return; attributesHash.insert( f.id(), f.attributes() ); } // store stuff in the sink for ( auto i = geometries.constBegin(); i != geometries.constEnd(); ++i ) { if ( feedback->isCanceled() ) return; QgsFeature outFeature( i.key() ); outFeature.setGeometry( i.value() ); if ( intersectingIds.contains( i.key() ) ) { const QList<QgsFeatureId> ids = intersectingIds.value( i.key() ); for ( QgsFeatureId id : ids ) { outFeature.setAttributes( attributesHash.value( id ) ); sink.addFeature( outFeature, QgsFeatureSink::FastInsert ); } } else { outFeature.setAttributes( attributesHash.value( i.key() ) ); sink.addFeature( outFeature, QgsFeatureSink::FastInsert ); } } }
void QgsOverlayUtils::intersection( const QgsFeatureSource &sourceA, const QgsFeatureSource &sourceB, QgsFeatureSink &sink, QgsProcessingContext &context, QgsProcessingFeedback *feedback, int &count, int totalCount, const QList<int> &fieldIndicesA, const QList<int> &fieldIndicesB ) { QgsWkbTypes::GeometryType geometryType = QgsWkbTypes::geometryType( QgsWkbTypes::multiType( sourceA.wkbType() ) ); int attrCount = fieldIndicesA.count() + fieldIndicesB.count(); QgsFeatureRequest request; request.setSubsetOfAttributes( QgsAttributeList() ); request.setDestinationCrs( sourceA.sourceCrs(), context.transformContext() ); QgsFeature outFeat; QgsSpatialIndex indexB( sourceB.getFeatures( request ), feedback ); if ( totalCount == 0 ) totalCount = 1; // avoid division by zero QgsFeature featA; QgsFeatureIterator fitA = sourceA.getFeatures( QgsFeatureRequest().setSubsetOfAttributes( fieldIndicesA ) ); while ( fitA.nextFeature( featA ) ) { if ( feedback->isCanceled() ) break; if ( !featA.hasGeometry() ) continue; QgsGeometry geom( featA.geometry() ); QgsFeatureIds intersects = indexB.intersects( geom.boundingBox() ).toSet(); QgsFeatureRequest request; request.setFilterFids( intersects ); request.setDestinationCrs( sourceA.sourceCrs(), context.transformContext() ); request.setSubsetOfAttributes( fieldIndicesB ); std::unique_ptr< QgsGeometryEngine > engine; if ( !intersects.isEmpty() ) { // use prepared geometries for faster intersection tests engine.reset( QgsGeometry::createGeometryEngine( geom.constGet() ) ); engine->prepareGeometry(); } QgsAttributes outAttributes( attrCount ); const QgsAttributes attrsA( featA.attributes() ); for ( int i = 0; i < fieldIndicesA.count(); ++i ) outAttributes[i] = attrsA[fieldIndicesA[i]]; QgsFeature featB; QgsFeatureIterator fitB = sourceB.getFeatures( request ); while ( fitB.nextFeature( featB ) ) { if ( feedback->isCanceled() ) break; QgsGeometry tmpGeom( featB.geometry() ); if ( !engine->intersects( tmpGeom.constGet() ) ) continue; QgsGeometry intGeom = geom.intersection( tmpGeom ); if ( !sanitizeIntersectionResult( intGeom, geometryType ) ) continue; const QgsAttributes attrsB( featB.attributes() ); for ( int i = 0; i < fieldIndicesB.count(); ++i ) outAttributes[fieldIndicesA.count() + i] = attrsB[fieldIndicesB[i]]; outFeat.setGeometry( intGeom ); outFeat.setAttributes( outAttributes ); sink.addFeature( outFeat, QgsFeatureSink::FastInsert ); } ++count; feedback->setProgress( count / ( double ) totalCount * 100. ); } }
QgsSpatialIndex::QgsSpatialIndex( const QgsFeatureSource &source, QgsFeedback *feedback ) { d = new QgsSpatialIndexData( source.getFeatures( QgsFeatureRequest().setNoAttributes() ), feedback ); }
bool QgsProcessingAlgorithm::validateInputCrs( const QVariantMap ¶meters, QgsProcessingContext &context ) const { if ( !( flags() & FlagRequiresMatchingCrs ) ) { // I'm a well behaved algorithm - I take work AWAY from users! return true; } bool foundCrs = false; QgsCoordinateReferenceSystem crs; Q_FOREACH ( const QgsProcessingParameterDefinition *def, mParameters ) { if ( def->type() == QStringLiteral( "layer" ) || def->type() == QStringLiteral( "raster" ) ) { QgsMapLayer *layer = QgsProcessingParameters::parameterAsLayer( def, parameters, context ); if ( layer ) { if ( foundCrs && layer->crs().isValid() && crs != layer->crs() ) { return false; } else if ( !foundCrs && layer->crs().isValid() ) { foundCrs = true; crs = layer->crs(); } } } else if ( def->type() == QStringLiteral( "source" ) ) { QgsFeatureSource *source = QgsProcessingParameters::parameterAsSource( def, parameters, context ); if ( source ) { if ( foundCrs && source->sourceCrs().isValid() && crs != source->sourceCrs() ) { return false; } else if ( !foundCrs && source->sourceCrs().isValid() ) { foundCrs = true; crs = source->sourceCrs(); } } } else if ( def->type() == QStringLiteral( "multilayer" ) ) { QList< QgsMapLayer *> layers = QgsProcessingParameters::parameterAsLayerList( def, parameters, context ); Q_FOREACH ( QgsMapLayer *layer, layers ) { if ( !layer ) continue; if ( foundCrs && layer->crs().isValid() && crs != layer->crs() ) { return false; } else if ( !foundCrs && layer->crs().isValid() ) { foundCrs = true; crs = layer->crs(); } } } }