70 , mProfileCurve( request.profileCurve() ? request.profileCurve()->clone() : nullptr )
71 , mSourceCrs( layer->crs() )
72 , mTargetCrs( request.crs() )
73 , mTransformContext( request.transformContext() )
74 , mOffset( layer->elevationProperties()->zOffset() )
75 , mScale( layer->elevationProperties()->zScale() )
78 , mRasterUnitsPerPixelX( layer->rasterUnitsPerPixelX() )
79 , mRasterUnitsPerPixelY( layer->rasterUnitsPerPixelY() )
80 , mStepDistance( request.stepDistance() )
83 mRasterProvider->moveToThread(
nullptr );
110 if ( !mProfileCurve || mFeedback->isCanceled() )
118 std::unique_ptr< QgsCurve > trimmedCurve;
120 if ( startDistanceOffset > 0 || endDistance < mProfileCurve->length() )
122 trimmedCurve.reset( mProfileCurve->curveSubstring( startDistanceOffset, endDistance ) );
123 sourceCurve = trimmedCurve.get();
127 sourceCurve = mProfileCurve.get();
131 std::unique_ptr< QgsCurve > transformedCurve( sourceCurve->
clone() );
139 QgsDebugError( u
"Error transforming profile line to raster CRS"_s );
143 if ( mFeedback->isCanceled() )
146 const QgsRectangle profileCurveBoundingBox = transformedCurve->boundingBox();
147 if ( !profileCurveBoundingBox.
intersects( mRasterProvider->extent() ) )
150 if ( mFeedback->isCanceled() )
153 mResults = std::make_unique< QgsRasterLayerProfileResults >();
154 mResults->mLayer = mLayer;
156 mResults->copyPropertiesFromGenerator(
this );
159 curveEngine->prepareGeometry();
161 if ( mFeedback->isCanceled() )
164 double stepDistance = mStepDistance;
175 QSet< QgsPointXY > profilePoints;
176 if ( !std::isnan( stepDistance ) )
185 profilePoints.insert( *it );
189 if ( mFeedback->isCanceled() )
193 int subRegionWidth = 0;
194 int subRegionHeight = 0;
195 int subRegionLeft = 0;
196 int subRegionTop = 0;
197 QgsRectangle rasterSubRegion = mRasterProvider->xSize() > 0 && mRasterProvider->ySize() > 0 ?
199 mRasterProvider->extent(),
200 mRasterProvider->xSize(),
201 mRasterProvider->ySize(),
202 transformedCurve->boundingBox(),
206 subRegionTop ) : transformedCurve->boundingBox();
208 const bool zeroXYSize = mRasterProvider->xSize() == 0 || mRasterProvider->ySize() == 0;
212 const double conversionFactor = curveLengthInPixels / transformedCurve->length();
213 subRegionWidth = rasterSubRegion.
width() * conversionFactor;
214 subRegionHeight = rasterSubRegion.
height() * conversionFactor;
218 if ( subRegionWidth == 0 )
223 if ( subRegionHeight == 0 )
238 it.
startRasterRead( mBand, subRegionWidth, subRegionHeight, rasterSubRegion );
240 const double halfPixelSizeX = mRasterUnitsPerPixelX / 2.0;
241 const double halfPixelSizeY = mRasterUnitsPerPixelY / 2.0;
242 int blockColumns = 0;
244 int blockTopLeftColumn = 0;
245 int blockTopLeftRow = 0;
248 while ( it.
next( mBand, blockColumns, blockRows, blockTopLeftColumn, blockTopLeftRow, blockExtent ) )
250 if ( mFeedback->isCanceled() )
254 if ( !curveEngine->intersects( blockExtentGeom.
constGet() ) )
257 std::unique_ptr< QgsRasterBlock > block( mRasterProvider->block( mBand, blockExtent, blockColumns, blockRows, mFeedback.get() ) );
258 if ( mFeedback->isCanceled() )
264 bool isNoData =
false;
268 if ( !std::isnan( stepDistance ) )
270 auto it = profilePoints.begin();
271 while ( it != profilePoints.end() )
273 if ( mFeedback->isCanceled() )
282 row = std::clamp(
static_cast< int >( std::round( ( blockExtent.
yMaximum() - it->y() ) * blockRows / blockExtent.
height() ) ), 0, blockRows - 1 );
283 col = std::clamp(
static_cast< int >( std::round( ( it->x() - blockExtent.
xMinimum() ) * blockColumns / blockExtent.
width() ) ), 0, blockColumns - 1 );
287 row = std::clamp(
static_cast< int >( std::round( ( blockExtent.
yMaximum() - it->y() ) / mRasterUnitsPerPixelY ) ), 0, blockRows - 1 );
288 col = std::clamp(
static_cast< int >( std::round( ( it->x() - blockExtent.
xMinimum() ) / mRasterUnitsPerPixelX ) ), 0, blockColumns - 1 );
290 double val = block->valueAndNoData( row, col, isNoData );
293 val = val * mScale + mOffset;
297 val = std::numeric_limits<double>::quiet_NaN();
300 QgsPoint pixel( it->x(), it->y(), val );
303 pixel.
transform( rasterToTargetTransform );
309 mResults->mRawPoints.append( pixel );
311 it = profilePoints.erase( it );
319 if ( profilePoints.isEmpty() )
324 double currentY = blockExtent.
yMaximum() - 0.5 * mRasterUnitsPerPixelY;
325 for (
int row = 0; row < blockRows; ++row )
327 if ( mFeedback->isCanceled() )
330 double currentX = blockExtent.
xMinimum() + 0.5 * mRasterUnitsPerPixelX;
331 for (
int col = 0; col < blockColumns; ++col, currentX += mRasterUnitsPerPixelX )
333 const double val = block->valueAndNoData( row, col, isNoData );
337 currentY - halfPixelSizeY,
338 currentX + halfPixelSizeX,
339 currentY + halfPixelSizeY ) );
340 if ( !curveEngine->intersects( pixelRectGeometry.
constGet() ) )
343 QgsPoint pixel( currentX, currentY, isNoData ? std::numeric_limits<double>::quiet_NaN() : val * mScale + mOffset );
346 pixel.
transform( rasterToTargetTransform );
352 mResults->mRawPoints.append( pixel );
354 currentY -= mRasterUnitsPerPixelY;
359 if ( mFeedback->isCanceled() )
363 QgsGeos originalCurveGeos( sourceCurve );
366 for (
const QgsPoint &pixel : std::as_const( mResults->mRawPoints ) )
368 if ( mFeedback->isCanceled() )
371 const double distance = originalCurveGeos.
lineLocatePoint( pixel, &lastError ) + startDistanceOffset;
373 if ( !std::isnan( pixel.z() ) )
375 mResults->minZ = std::min( pixel.z(), mResults->minZ );
376 mResults->maxZ = std::max( pixel.z(), mResults->maxZ );
378 mResults->mDistanceToHeightMap.insert( distance, pixel.z() );