do not create spatial index if preserving attributes is not needed

This commit is contained in:
Alexander Bruy 2023-08-09 13:58:09 +03:00 committed by Martin Dobias
parent 514e8b5b24
commit ea6a706c01
2 changed files with 106 additions and 39 deletions

View File

@ -66,46 +66,58 @@ void QgsVoronoiPolygonsAlgorithm::initAlgorithm( const QVariantMap & )
addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Voronoi polygons" ), QgsProcessing::TypeVectorPolygon ) ); addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Voronoi polygons" ), QgsProcessing::TypeVectorPolygon ) );
} }
QVariantMap QgsVoronoiPolygonsAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) bool QgsVoronoiPolygonsAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{ {
std::unique_ptr< QgsProcessingFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) ); Q_UNUSED( feedback );
if ( !source )
mSource.reset( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
if ( !mSource )
throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) ); throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
if ( source->featureCount() < 3 ) if ( mSource->featureCount() < 3 )
throw QgsProcessingException( QObject::tr( "Input layer should contain at least 3 points." ) ); throw QgsProcessingException( QObject::tr( "Input layer should contain at least 3 points." ) );
const double buffer = parameterAsDouble( parameters, QStringLiteral( "BUFFER" ), context ); mBuffer = parameterAsDouble( parameters, QStringLiteral( "BUFFER" ), context );
const double tolerance = parameterAsDouble( parameters, QStringLiteral( "TOLERANCE" ), context ); mTolerance = parameterAsDouble( parameters, QStringLiteral( "TOLERANCE" ), context );
const bool copyAttributes = parameterAsBool( parameters, QStringLiteral( "COPY_ATTRIBUTES" ), context ); mCopyAttributes = parameterAsBool( parameters, QStringLiteral( "COPY_ATTRIBUTES" ), context );
QgsFields fields; return true;
if ( copyAttributes ) }
QVariantMap QgsVoronoiPolygonsAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
QString dest;
if ( mCopyAttributes )
{ {
fields = source->fields(); dest = voronoiWithAttributes( parameters, context, feedback );
} }
else else
{ {
fields.append( QgsField( QStringLiteral( "id" ), QVariant::LongLong ) ); dest = voronoiWithoutAttributes( parameters, context, feedback );
} }
QVariantMap outputs;
outputs.insert( QStringLiteral( "OUTPUT" ), dest );
return outputs;
}
QString QgsVoronoiPolygonsAlgorithm::voronoiWithAttributes( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
QgsFields fields = mSource->fields();
QString dest; QString dest;
std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, fields, Qgis::WkbType::Polygon, source->sourceCrs() ) ); std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, fields, Qgis::WkbType::Polygon, mSource->sourceCrs() ) );
if ( !sink ) if ( !sink )
throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) ); throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
QgsFeatureRequest request; QgsFeatureRequest request;
if ( !copyAttributes ) QgsFeatureIterator it = mSource->getFeatures( request, QgsProcessingFeatureSource::FlagSkipGeometryValidityChecks );
{
request.setSubsetOfAttributes( QList< int >() );
}
QgsFeatureIterator it = source->getFeatures( request, QgsProcessingFeatureSource::FlagSkipGeometryValidityChecks );
QgsGeometry allPoints; QgsGeometry allPoints;
QHash< QgsFeatureId, QgsAttributes > attributeCache; QHash< QgsFeatureId, QgsAttributes > attributeCache;
long i = 0; long i = 0;
double step = source->featureCount() > 0 ? 50.0 / source->featureCount() : 1; double step = mSource->featureCount() > 0 ? 50.0 / mSource->featureCount() : 1;
const QgsSpatialIndex index( it, [&]( const QgsFeature & f )->bool const QgsSpatialIndex index( it, [&]( const QgsFeature & f )->bool
{ {
@ -137,16 +149,16 @@ QVariantMap QgsVoronoiPolygonsAlgorithm::processAlgorithm( const QVariantMap &pa
return true; return true;
}, QgsSpatialIndex::FlagStoreFeatureGeometries ); }, QgsSpatialIndex::FlagStoreFeatureGeometries );
QgsRectangle extent = source->sourceExtent(); QgsRectangle extent = mSource->sourceExtent();
double delta = extent.width() * buffer / 100.0; double delta = extent.width() * mBuffer / 100.0;
extent.setXMinimum( extent.xMinimum() - delta ); extent.setXMinimum( extent.xMinimum() - delta );
extent.setXMaximum( extent.xMaximum() + delta ); extent.setXMaximum( extent.xMaximum() + delta );
delta = extent.height() * buffer / 100.0; delta = extent.height() * mBuffer / 100.0;
extent.setYMinimum( extent.yMinimum() - delta ); extent.setYMinimum( extent.yMinimum() - delta );
extent.setYMaximum( extent.yMaximum() + delta ); extent.setYMaximum( extent.yMaximum() + delta );
QgsGeometry clippingGeom = QgsGeometry::fromRect( extent ); QgsGeometry clippingGeom = QgsGeometry::fromRect( extent );
QgsGeometry voronoiDiagram = allPoints.voronoiDiagram( clippingGeom, tolerance ); QgsGeometry voronoiDiagram = allPoints.voronoiDiagram( clippingGeom, mTolerance );
if ( !voronoiDiagram.isEmpty() ) if ( !voronoiDiagram.isEmpty() )
{ {
@ -162,33 +174,78 @@ QVariantMap QgsVoronoiPolygonsAlgorithm::processAlgorithm( const QVariantMap &pa
QgsFeature f; QgsFeature f;
f.setFields( fields ); f.setFields( fields );
f.setGeometry( QgsGeometry( extentEngine->intersection( collection[i].constGet() ) ) ); f.setGeometry( QgsGeometry( extentEngine->intersection( collection[i].constGet() ) ) );
if ( copyAttributes ) const QList< QgsFeatureId > intersected = index.intersects( collection[i].boundingBox() );
engine.reset( QgsGeometry::createGeometryEngine( collection[i].constGet() ) );
engine->prepareGeometry();
for ( const QgsFeatureId id : intersected )
{ {
const QList< QgsFeatureId > intersected = index.intersects( collection[i].boundingBox() ); if ( engine->intersects( index.geometry( id ).constGet() ) )
engine.reset( QgsGeometry::createGeometryEngine( collection[i].constGet() ) );
engine->prepareGeometry();
for ( const QgsFeatureId id : intersected )
{ {
if ( engine->intersects( index.geometry( id ).constGet() ) ) f.setAttributes( attributeCache.value( id ) );
{ break;
f.setAttributes( attributeCache.value( id ) );
break;
}
} }
} }
else
{
f.setAttributes( QgsAttributes() << i );
}
if ( !sink->addFeature( f, QgsFeatureSink::FastInsert ) ) if ( !sink->addFeature( f, QgsFeatureSink::FastInsert ) )
throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral( "OUTPUT" ) ) ); throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
feedback->setProgress( 50 + i * step ); feedback->setProgress( 50 + i * step );
} }
} }
QVariantMap outputs; return dest;
outputs.insert( QStringLiteral( "OUTPUT" ), dest ); }
return outputs;
QString QgsVoronoiPolygonsAlgorithm::voronoiWithoutAttributes( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
QgsFields fields;
fields.append( QgsField( QStringLiteral( "id" ), QVariant::LongLong ) );
QString dest;
std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, fields, Qgis::WkbType::Polygon, mSource->sourceCrs() ) );
if ( !sink )
throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
QgsFeatureRequest request;
request.setSubsetOfAttributes( QList< int >() );
QgsFeatureIterator it = mSource->getFeatures( request, QgsProcessingFeatureSource::FlagSkipGeometryValidityChecks );
QgsGeometry allPoints;
QHash< QgsFeatureId, QgsAttributes > attributeCache;
double step = mSource->featureCount() > 0 ? 100.0 / mSource->featureCount() : 1;
QgsRectangle extent = mSource->sourceExtent();
double delta = extent.width() * mBuffer / 100.0;
extent.setXMinimum( extent.xMinimum() - delta );
extent.setXMaximum( extent.xMaximum() + delta );
delta = extent.height() * mBuffer / 100.0;
extent.setYMinimum( extent.yMinimum() - delta );
extent.setYMaximum( extent.yMaximum() + delta );
QgsGeometry clippingGeom = QgsGeometry::fromRect( extent );
QgsGeometry voronoiDiagram = allPoints.voronoiDiagram( clippingGeom, mTolerance );
if ( !voronoiDiagram.isEmpty() )
{
std::unique_ptr< QgsGeometryEngine > engine;
std::unique_ptr< QgsGeometryEngine > extentEngine( QgsGeometry::createGeometryEngine( clippingGeom.constGet() ) );
QVector< QgsGeometry > collection = voronoiDiagram.asGeometryCollection();
for ( int i = 0; i < collection.length(); i++ )
{
if ( feedback->isCanceled() )
{
break;
}
QgsFeature f;
f.setFields( fields );
f.setGeometry( QgsGeometry( extentEngine->intersection( collection[i].constGet() ) ) );
f.setAttributes( QgsAttributes() << i );
if ( !sink->addFeature( f, QgsFeatureSink::FastInsert ) )
throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
feedback->setProgress( i * step );
}
}
return dest;
} }
///@endcond ///@endcond

View File

@ -47,8 +47,18 @@ class QgsVoronoiPolygonsAlgorithm : public QgsProcessingAlgorithm
protected: protected:
bool prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
QVariantMap processAlgorithm( const QVariantMap &parameters, QVariantMap processAlgorithm( const QVariantMap &parameters,
QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override; QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
private:
QString voronoiWithAttributes( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback );
QString voronoiWithoutAttributes( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback );
std::unique_ptr< QgsProcessingFeatureSource > mSource;
double mBuffer;
double mTolerance;
bool mCopyAttributes;
}; };
///@endcond PRIVATE ///@endcond PRIVATE