diff --git a/src/core/pointcloud/qgspointcloudlayerprofilegenerator.cpp b/src/core/pointcloud/qgspointcloudlayerprofilegenerator.cpp index 420a3eb6e917..9982c4f1d325 100644 --- a/src/core/pointcloud/qgspointcloudlayerprofilegenerator.cpp +++ b/src/core/pointcloud/qgspointcloudlayerprofilegenerator.cpp @@ -389,12 +389,23 @@ bool QgsPointCloudLayerProfileGenerator::generateProfile( const QgsProfileGenera // this is not AT ALL thread safe, but it's what QgsPointCloudLayerRenderer does ! // TODO: fix when QgsPointCloudLayerRenderer is made thread safe to use same approach - QgsPointCloudIndex *pc = mLayer->dataProvider()->index(); - if ( !pc || !pc->isValid() ) + QVector indexes; + QgsPointCloudIndex *mainIndex = mLayer->dataProvider()->index(); + if ( mainIndex && mainIndex->isValid() ) + indexes.append( mainIndex ); + + // Gather all relevant sub-indexes + const QgsRectangle profileCurveBbox = mProfileCurve->boundingBox(); + for ( const QgsPointCloudSubIndex &subidx : mLayer->dataProvider()->subIndexes() ) { - return false; + QgsPointCloudIndex *index = subidx.index(); + if ( index && index->isValid() && subidx.polygonBounds().intersects( profileCurveBbox ) ) + indexes.append( subidx.index() ); } + if ( indexes.empty() ) + return false; + const double startDistanceOffset = std::max( !context.distanceRange().isInfinite() ? context.distanceRange().lower() : 0, 0.0 ); const double endDistance = context.distanceRange().upper(); @@ -433,9 +444,13 @@ bool QgsPointCloudLayerProfileGenerator::generateProfile( const QgsProfileGenera mSearchGeometryInLayerCrsGeometryEngine->prepareGeometry(); mMaxSearchExtentInLayerCrs = mSearchGeometryInLayerCrs->boundingBox(); - const IndexedPointCloudNode root = pc->root(); - double maximumErrorPixels = context.convertDistanceToPixels( mMaximumScreenError, mMaximumScreenErrorUnit ); + if ( maximumErrorPixels < 0.0 ) + { + QgsDebugError( QStringLiteral( "Invalid maximum error in pixels" ) ); + return false; + } + const double toleranceInPixels = context.convertDistanceToPixels( mTolerance, Qgis::RenderUnit::MapUnits ); // ensure that the maximum error is compatible with the tolerance size -- otherwise if the tolerance size // is much smaller than the maximum error, we don't dig deep enough into the point cloud nodes to find @@ -444,42 +459,6 @@ bool QgsPointCloudLayerProfileGenerator::generateProfile( const QgsProfileGenera if ( toleranceInPixels / 4 < maximumErrorPixels ) maximumErrorPixels = toleranceInPixels / 4; - const QgsRectangle rootNodeExtentLayerCoords = pc->nodeMapExtent( root ); - QgsRectangle rootNodeExtentInCurveCrs; - try - { - QgsCoordinateTransform extentTransform = mLayerToTargetTransform; - extentTransform.setBallparkTransformsAreAppropriate( true ); - rootNodeExtentInCurveCrs = extentTransform.transformBoundingBox( rootNodeExtentLayerCoords ); - } - catch ( QgsCsException & ) - { - QgsDebugError( QStringLiteral( "Could not transform node extent to curve CRS" ) ); - rootNodeExtentInCurveCrs = rootNodeExtentLayerCoords; - } - - const double rootErrorInMapCoordinates = rootNodeExtentInCurveCrs.width() / pc->span(); // in curve coords - - const double mapUnitsPerPixel = context.mapUnitsPerDistancePixel(); - if ( ( rootErrorInMapCoordinates < 0.0 ) || ( mapUnitsPerPixel < 0.0 ) || ( maximumErrorPixels < 0.0 ) ) - { - QgsDebugError( QStringLiteral( "invalid screen error" ) ); - return false; - } - double rootErrorPixels = rootErrorInMapCoordinates / mapUnitsPerPixel; // in pixels - const QVector nodes = traverseTree( pc, pc->root(), maximumErrorPixels, rootErrorPixels, context.elevationRange() ); - if ( nodes.empty() ) - { - return false; - } - - const double rootErrorInLayerCoordinates = rootNodeExtentLayerCoords.width() / pc->span(); - const double maxErrorInMapCoordinates = maximumErrorPixels * mapUnitsPerPixel; - - mResults = std::make_unique< QgsPointCloudLayerProfileResults >(); - mResults->copyPropertiesFromGenerator( this ); - mResults->mMaxErrorInLayerCoordinates = maxErrorInMapCoordinates * rootErrorInLayerCoordinates / rootErrorInMapCoordinates; - QgsPointCloudRequest request; QgsPointCloudAttributeCollection attributes; attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "X" ), QgsPointCloudAttribute::Int32 ) ); @@ -515,22 +494,75 @@ bool QgsPointCloudLayerProfileGenerator::generateProfile( const QgsProfileGenera request.setAttributes( attributes ); - switch ( pc->accessType() ) + mResults = std::make_unique< QgsPointCloudLayerProfileResults >(); + mResults->copyPropertiesFromGenerator( this ); + mResults->mMaxErrorInLayerCoordinates = 0; + + QgsCoordinateTransform extentTransform = mLayerToTargetTransform; + extentTransform.setBallparkTransformsAreAppropriate( true ); + + const double mapUnitsPerPixel = context.mapUnitsPerDistancePixel(); + if ( mapUnitsPerPixel < 0.0 ) + { + QgsDebugError( QStringLiteral( "Invalid map units per pixel ratio" ) ); + return false; + } + + for ( QgsPointCloudIndex *pc : std::as_const( indexes ) ) { - case QgsPointCloudIndex::AccessType::Local: + const IndexedPointCloudNode root = pc->root(); + const QgsRectangle rootNodeExtentLayerCoords = pc->nodeMapExtent( root ); + QgsRectangle rootNodeExtentInCurveCrs; + try { - visitNodesSync( nodes, pc, request, context.elevationRange() ); - break; + rootNodeExtentInCurveCrs = extentTransform.transformBoundingBox( rootNodeExtentLayerCoords ); } - case QgsPointCloudIndex::AccessType::Remote: + catch ( QgsCsException & ) { - visitNodesAsync( nodes, pc, request, context.elevationRange() ); - break; + QgsDebugError( QStringLiteral( "Could not transform node extent to curve CRS" ) ); + rootNodeExtentInCurveCrs = rootNodeExtentLayerCoords; + } + + const double rootErrorInMapCoordinates = rootNodeExtentInCurveCrs.width() / pc->span(); // in curve coords + if ( rootErrorInMapCoordinates < 0.0 ) + { + QgsDebugError( QStringLiteral( "Invalid root node error" ) ); + return false; + } + + double rootErrorPixels = rootErrorInMapCoordinates / mapUnitsPerPixel; // in pixels + const QVector nodes = traverseTree( pc, pc->root(), maximumErrorPixels, rootErrorPixels, context.elevationRange() ); + + const double rootErrorInLayerCoordinates = rootNodeExtentLayerCoords.width() / pc->span(); + const double maxErrorInMapCoordinates = maximumErrorPixels * mapUnitsPerPixel; + + mResults->mMaxErrorInLayerCoordinates = std::max( + mResults->mMaxErrorInLayerCoordinates, + maxErrorInMapCoordinates * rootErrorInLayerCoordinates / rootErrorInMapCoordinates ); + + switch ( pc->accessType() ) + { + case QgsPointCloudIndex::AccessType::Local: + { + visitNodesSync( nodes, pc, request, context.elevationRange() ); + break; + } + case QgsPointCloudIndex::AccessType::Remote: + { + visitNodesAsync( nodes, pc, request, context.elevationRange() ); + break; + } } + + if ( mFeedback->isCanceled() ) + return false; } - if ( mFeedback->isCanceled() ) + if ( mGatheredPoints.empty() ) + { + mResults = nullptr; return false; + } // convert x/y values back to distance/height values