69 , mProfileCurve( request.profileCurve() ? request.profileCurve()->clone() : nullptr )
70 , mSourceCrs( layer->crs() )
71 , mTargetCrs( request.crs() )
72 , mTransformContext( request.transformContext() )
73 , mOffset( layer->elevationProperties()->zOffset() )
74 , mScale( layer->elevationProperties()->zScale() )
77 , mRasterUnitsPerPixelX( layer->rasterUnitsPerPixelX() )
78 , mRasterUnitsPerPixelY( layer->rasterUnitsPerPixelY() )
79 , mStepDistance( request.stepDistance() )
82 mRasterProvider->moveToThread(
nullptr );
109 if ( !mProfileCurve || mFeedback->isCanceled() )
117 std::unique_ptr< QgsCurve > trimmedCurve;
119 if ( startDistanceOffset > 0 || endDistance < mProfileCurve->length() )
121 trimmedCurve.reset( mProfileCurve->curveSubstring( startDistanceOffset, endDistance ) );
122 sourceCurve = trimmedCurve.get();
126 sourceCurve = mProfileCurve.get();
130 std::unique_ptr< QgsCurve > transformedCurve( sourceCurve->
clone() );
138 QgsDebugError( u
"Error transforming profile line to raster CRS"_s );
142 if ( mFeedback->isCanceled() )
145 const QgsRectangle profileCurveBoundingBox = transformedCurve->boundingBox();
146 if ( !profileCurveBoundingBox.
intersects( mRasterProvider->extent() ) )
149 if ( mFeedback->isCanceled() )
152 mResults = std::make_unique< QgsRasterLayerProfileResults >();
153 mResults->mLayer = mLayer;
155 mResults->copyPropertiesFromGenerator(
this );
158 curveEngine->prepareGeometry();
160 if ( mFeedback->isCanceled() )
163 double stepDistance = mStepDistance;
174 QSet< QgsPointXY > profilePoints;
175 if ( !std::isnan( stepDistance ) )
184 profilePoints.insert( *it );
188 if ( mFeedback->isCanceled() )
192 int subRegionWidth = 0;
193 int subRegionHeight = 0;
194 int subRegionLeft = 0;
195 int subRegionTop = 0;
197 = mRasterProvider->xSize() > 0 && mRasterProvider->ySize() > 0
198 ?
QgsRasterIterator::subRegion( mRasterProvider->extent(), mRasterProvider->xSize(), mRasterProvider->ySize(), transformedCurve->boundingBox(), subRegionWidth, subRegionHeight, subRegionLeft, subRegionTop )
199 : transformedCurve->boundingBox();
201 const bool zeroXYSize = mRasterProvider->xSize() == 0 || mRasterProvider->ySize() == 0;
205 const double conversionFactor = curveLengthInPixels / transformedCurve->length();
206 subRegionWidth = rasterSubRegion.
width() * conversionFactor;
207 subRegionHeight = rasterSubRegion.
height() * conversionFactor;
211 if ( subRegionWidth == 0 )
216 if ( subRegionHeight == 0 )
231 it.
startRasterRead( mBand, subRegionWidth, subRegionHeight, rasterSubRegion );
233 const double halfPixelSizeX = mRasterUnitsPerPixelX / 2.0;
234 const double halfPixelSizeY = mRasterUnitsPerPixelY / 2.0;
235 int blockColumns = 0;
237 int blockTopLeftColumn = 0;
238 int blockTopLeftRow = 0;
241 while ( it.
next( mBand, blockColumns, blockRows, blockTopLeftColumn, blockTopLeftRow, blockExtent ) )
243 if ( mFeedback->isCanceled() )
247 if ( !curveEngine->intersects( blockExtentGeom.
constGet() ) )
250 std::unique_ptr< QgsRasterBlock > block( mRasterProvider->block( mBand, blockExtent, blockColumns, blockRows, mFeedback.get() ) );
251 if ( mFeedback->isCanceled() )
257 bool isNoData =
false;
261 if ( !std::isnan( stepDistance ) )
263 auto it = profilePoints.begin();
264 while ( it != profilePoints.end() )
266 if ( mFeedback->isCanceled() )
275 row = std::clamp(
static_cast< int >( std::round( ( blockExtent.
yMaximum() - it->y() ) * blockRows / blockExtent.
height() ) ), 0, blockRows - 1 );
276 col = std::clamp(
static_cast< int >( std::round( ( it->x() - blockExtent.
xMinimum() ) * blockColumns / blockExtent.
width() ) ), 0, blockColumns - 1 );
280 row = std::clamp(
static_cast< int >( std::round( ( blockExtent.
yMaximum() - it->y() ) / mRasterUnitsPerPixelY ) ), 0, blockRows - 1 );
281 col = std::clamp(
static_cast< int >( std::round( ( it->x() - blockExtent.
xMinimum() ) / mRasterUnitsPerPixelX ) ), 0, blockColumns - 1 );
283 double val = block->valueAndNoData( row, col, isNoData );
286 val = val * mScale + mOffset;
290 val = std::numeric_limits<double>::quiet_NaN();
293 QgsPoint pixel( it->x(), it->y(), val );
296 pixel.
transform( rasterToTargetTransform );
302 mResults->mRawPoints.append( pixel );
304 it = profilePoints.erase( it );
312 if ( profilePoints.isEmpty() )
317 double currentY = blockExtent.
yMaximum() - 0.5 * mRasterUnitsPerPixelY;
318 for (
int row = 0; row < blockRows; ++row )
320 if ( mFeedback->isCanceled() )
323 double currentX = blockExtent.
xMinimum() + 0.5 * mRasterUnitsPerPixelX;
324 for (
int col = 0; col < blockColumns; ++col, currentX += mRasterUnitsPerPixelX )
326 const double val = block->valueAndNoData( row, col, isNoData );
330 if ( !curveEngine->intersects( pixelRectGeometry.
constGet() ) )
333 QgsPoint pixel( currentX, currentY, isNoData ? std::numeric_limits<double>::quiet_NaN() : val * mScale + mOffset );
336 pixel.
transform( rasterToTargetTransform );
342 mResults->mRawPoints.append( pixel );
344 currentY -= mRasterUnitsPerPixelY;
349 if ( mFeedback->isCanceled() )
353 QgsGeos originalCurveGeos( sourceCurve );
356 for (
const QgsPoint &pixel : std::as_const( mResults->mRawPoints ) )
358 if ( mFeedback->isCanceled() )
361 const double distance = originalCurveGeos.
lineLocatePoint( pixel, &lastError ) + startDistanceOffset;
363 if ( !std::isnan( pixel.z() ) )
365 mResults->minZ = std::min( pixel.z(), mResults->minZ );
366 mResults->maxZ = std::max( pixel.z(), mResults->maxZ );
368 mResults->mDistanceToHeightMap.insert( distance, pixel.z() );