67 , mProfileCurve( request.profileCurve() ? request.profileCurve()->clone() : nullptr )
68 , mSourceCrs( layer->crs() )
69 , mTargetCrs( request.crs() )
70 , mTransformContext( request.transformContext() )
71 , mOffset( layer->elevationProperties()->zOffset() )
72 , mScale( layer->elevationProperties()->zScale() )
75 , mRasterUnitsPerPixelX( layer->rasterUnitsPerPixelX() )
76 , mRasterUnitsPerPixelY( layer->rasterUnitsPerPixelY() )
77 , mStepDistance( request.stepDistance() )
80 mRasterProvider->moveToThread(
nullptr );
102 if ( !mProfileCurve || mFeedback->isCanceled() )
110 std::unique_ptr< QgsCurve > trimmedCurve;
112 if ( startDistanceOffset > 0 || endDistance < mProfileCurve->length() )
114 trimmedCurve.reset( mProfileCurve->curveSubstring( startDistanceOffset, endDistance ) );
115 sourceCurve = trimmedCurve.get();
119 sourceCurve = mProfileCurve.get();
123 std::unique_ptr< QgsCurve > transformedCurve( sourceCurve->
clone() );
131 QgsDebugError( QStringLiteral(
"Error transforming profile line to raster CRS" ) );
135 if ( mFeedback->isCanceled() )
138 const QgsRectangle profileCurveBoundingBox = transformedCurve->boundingBox();
139 if ( !profileCurveBoundingBox.
intersects( mRasterProvider->extent() ) )
142 if ( mFeedback->isCanceled() )
145 mResults = std::make_unique< QgsRasterLayerProfileResults >();
146 mResults->mLayer = mLayer;
148 mResults->copyPropertiesFromGenerator(
this );
151 curveEngine->prepareGeometry();
153 if ( mFeedback->isCanceled() )
156 double stepDistance = mStepDistance;
167 QSet< QgsPointXY > profilePoints;
168 if ( !std::isnan( stepDistance ) )
177 profilePoints.insert( *it );
181 if ( mFeedback->isCanceled() )
185 int subRegionWidth = 0;
186 int subRegionHeight = 0;
187 int subRegionLeft = 0;
188 int subRegionTop = 0;
189 QgsRectangle rasterSubRegion = mRasterProvider->xSize() > 0 && mRasterProvider->ySize() > 0 ?
191 mRasterProvider->extent(),
192 mRasterProvider->xSize(),
193 mRasterProvider->ySize(),
194 transformedCurve->boundingBox(),
198 subRegionTop ) : transformedCurve->boundingBox();
200 const bool zeroXYSize = mRasterProvider->xSize() == 0 || mRasterProvider->ySize() == 0;
204 const double conversionFactor = curveLengthInPixels / transformedCurve->length();
205 subRegionWidth = rasterSubRegion.
width() * conversionFactor;
206 subRegionHeight = rasterSubRegion.
height() * conversionFactor;
210 if ( subRegionWidth == 0 )
215 if ( subRegionHeight == 0 )
230 it.
startRasterRead( mBand, subRegionWidth, subRegionHeight, rasterSubRegion );
232 const double halfPixelSizeX = mRasterUnitsPerPixelX / 2.0;
233 const double halfPixelSizeY = mRasterUnitsPerPixelY / 2.0;
234 int blockColumns = 0;
236 int blockTopLeftColumn = 0;
237 int blockTopLeftRow = 0;
240 while ( it.
next( mBand, blockColumns, blockRows, blockTopLeftColumn, blockTopLeftRow, blockExtent ) )
242 if ( mFeedback->isCanceled() )
246 if ( !curveEngine->intersects( blockExtentGeom.
constGet() ) )
249 std::unique_ptr< QgsRasterBlock > block( mRasterProvider->block( mBand, blockExtent, blockColumns, blockRows, mFeedback.get() ) );
250 if ( mFeedback->isCanceled() )
256 bool isNoData =
false;
260 if ( !std::isnan( stepDistance ) )
262 auto it = profilePoints.begin();
263 while ( it != profilePoints.end() )
265 if ( mFeedback->isCanceled() )
274 row = std::clamp(
static_cast< int >( std::round( ( blockExtent.
yMaximum() - it->y() ) * blockRows / blockExtent.
height() ) ), 0, blockRows - 1 );
275 col = std::clamp(
static_cast< int >( std::round( ( it->x() - blockExtent.
xMinimum() ) * blockColumns / blockExtent.
width() ) ), 0, blockColumns - 1 );
279 row = std::clamp(
static_cast< int >( std::round( ( blockExtent.
yMaximum() - it->y() ) / mRasterUnitsPerPixelY ) ), 0, blockRows - 1 );
280 col = std::clamp(
static_cast< int >( std::round( ( it->x() - blockExtent.
xMinimum() ) / mRasterUnitsPerPixelX ) ), 0, blockColumns - 1 );
282 double val = block->valueAndNoData( row, col, isNoData );
285 val = val * mScale + mOffset;
289 val = std::numeric_limits<double>::quiet_NaN();
292 QgsPoint pixel( it->x(), it->y(), val );
295 pixel.
transform( rasterToTargetTransform );
301 mResults->mRawPoints.append( pixel );
303 it = profilePoints.erase( it );
311 if ( profilePoints.isEmpty() )
316 double currentY = blockExtent.
yMaximum() - 0.5 * mRasterUnitsPerPixelY;
317 for (
int row = 0; row < blockRows; ++row )
319 if ( mFeedback->isCanceled() )
322 double currentX = blockExtent.
xMinimum() + 0.5 * mRasterUnitsPerPixelX;
323 for (
int col = 0; col < blockColumns; ++col, currentX += mRasterUnitsPerPixelX )
325 const double val = block->valueAndNoData( row, col, isNoData );
329 currentY - halfPixelSizeY,
330 currentX + halfPixelSizeX,
331 currentY + halfPixelSizeY ) );
332 if ( !curveEngine->intersects( pixelRectGeometry.
constGet() ) )
335 QgsPoint pixel( currentX, currentY, isNoData ? std::numeric_limits<double>::quiet_NaN() : val * mScale + mOffset );
338 pixel.
transform( rasterToTargetTransform );
344 mResults->mRawPoints.append( pixel );
346 currentY -= mRasterUnitsPerPixelY;
351 if ( mFeedback->isCanceled() )
355 QgsGeos originalCurveGeos( sourceCurve );
358 for (
const QgsPoint &pixel : std::as_const( mResults->mRawPoints ) )
360 if ( mFeedback->isCanceled() )
363 const double distance = originalCurveGeos.
lineLocatePoint( pixel, &lastError ) + startDistanceOffset;
365 if ( !std::isnan( pixel.z() ) )
367 mResults->minZ = std::min( pixel.z(), mResults->minZ );
368 mResults->maxZ = std::max( pixel.z(), mResults->maxZ );
370 mResults->mDistanceToHeightMap.insert( distance, pixel.z() );