QGIS API Documentation 3.30.0-'s-Hertogenbosch (f186b8efe0)
qgsrasterlayerrenderer.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsrasterlayerrenderer.cpp
3 --------------------------------------
4 Date : December 2013
5 Copyright : (C) 2013 by Martin Dobias
6 Email : wonder dot sk at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
17
18#include "qgsmessagelog.h"
20#include "qgsrasterdrawer.h"
21#include "qgsrasteriterator.h"
22#include "qgsrasterlayer.h"
23#include "qgsrasterprojector.h"
24#include "qgsrendercontext.h"
25#include "qgsrasterrenderer.h"
26#include "qgsexception.h"
28#include "qgsmapclippingutils.h"
29#include "qgsrasterpipe.h"
30#include "qgselevationmap.h"
31#include "qgsgdalutils.h"
34
35#include <QElapsedTimer>
36#include <QPointer>
37#include <QThread>
38
40
41QgsRasterLayerRendererFeedback::QgsRasterLayerRendererFeedback( QgsRasterLayerRenderer *r )
42 : mR( r )
43 , mMinimalPreviewInterval( 250 )
44{
46}
47
48void QgsRasterLayerRendererFeedback::onNewData()
49{
50 if ( !renderPartialOutput() )
51 return; // we were not asked for partial renders and we may not have a temporary image for overwriting...
52
53 // update only once upon a time
54 // (preview itself takes some time)
55 if ( mLastPreview.isValid() && mLastPreview.msecsTo( QTime::currentTime() ) < mMinimalPreviewInterval )
56 return;
57
58 // TODO: update only the area that got new data
59
60 QgsDebugMsgLevel( QStringLiteral( "new raster preview! %1" ).arg( mLastPreview.msecsTo( QTime::currentTime() ) ), 3 );
61 QElapsedTimer t;
62 t.start();
64 feedback.setPreviewOnly( true );
65 feedback.setRenderPartialOutput( true );
66 QgsRasterIterator iterator( mR->mPipe->last() );
67 QgsRasterDrawer drawer( &iterator );
68 drawer.draw( *( mR->renderContext() ), mR->mRasterViewPort, &feedback );
69 mR->mReadyToCompose = true;
70 QgsDebugMsgLevel( QStringLiteral( "total raster preview time: %1 ms" ).arg( t.elapsed() ), 3 );
71 mLastPreview = QTime::currentTime();
72}
73
77 : QgsMapLayerRenderer( layer->id(), &rendererContext )
78 , mLayerOpacity( layer->opacity() )
79 , mProviderCapabilities( static_cast<QgsRasterDataProvider::Capability>( layer->dataProvider()->capabilities() ) )
80 , mFeedback( new QgsRasterLayerRendererFeedback( this ) )
81{
82 mReadyToCompose = false;
83 QgsMapToPixel mapToPixel = rendererContext.mapToPixel();
84 if ( rendererContext.mapToPixel().mapRotation() )
85 {
86 // unset rotation for the sake of local computations.
87 // Rotation will be handled by QPainter later
88 // TODO: provide a method of QgsMapToPixel to fetch map center
89 // in geographical units
90 const QgsPointXY center = mapToPixel.toMapCoordinates(
91 static_cast<int>( mapToPixel.mapWidth() / 2.0 ),
92 static_cast<int>( mapToPixel.mapHeight() / 2.0 )
93 );
94 mapToPixel.setMapRotation( 0, center.x(), center.y() );
95 }
96
97 QgsRectangle myProjectedViewExtent;
98 QgsRectangle myProjectedLayerExtent;
99
100 if ( rendererContext.coordinateTransform().isValid() )
101 {
102 QgsDebugMsgLevel( QStringLiteral( "coordinateTransform set -> project extents." ), 4 );
103 if ( rendererContext.extent().xMinimum() == std::numeric_limits<double>::lowest() &&
104 rendererContext.extent().yMinimum() == std::numeric_limits<double>::lowest() &&
105 rendererContext.extent().xMaximum() == std::numeric_limits<double>::max() &&
106 rendererContext.extent().yMaximum() == std::numeric_limits<double>::max() )
107 {
108 // We get in this situation if the view CRS is geographical and the
109 // extent goes beyond -180,-90,180,90. To avoid reprojection issues to the
110 // layer CRS, then this dummy extent is returned by QgsMapRendererJob::reprojectToLayerExtent()
111 // Don't try to reproject it now to view extent as this would return
112 // a null rectangle.
113 myProjectedViewExtent = rendererContext.extent();
114 }
115 else
116 {
117 try
118 {
119 QgsCoordinateTransform ct = rendererContext.coordinateTransform();
121 myProjectedViewExtent = ct.transformBoundingBox( rendererContext.extent() );
122 }
123 catch ( QgsCsException &cs )
124 {
125 QgsMessageLog::logMessage( QObject::tr( "Could not reproject view extent: %1" ).arg( cs.what() ), QObject::tr( "Raster" ) );
126 myProjectedViewExtent.setMinimal();
127 }
128 }
129
130 try
131 {
132 QgsCoordinateTransform ct = rendererContext.coordinateTransform();
134 myProjectedLayerExtent = ct.transformBoundingBox( layer->extent() );
135 }
136 catch ( QgsCsException &cs )
137 {
138 QgsMessageLog::logMessage( QObject::tr( "Could not reproject layer extent: %1" ).arg( cs.what() ), QObject::tr( "Raster" ) );
139 myProjectedLayerExtent.setMinimal();
140 }
141 }
142 else
143 {
144 QgsDebugMsgLevel( QStringLiteral( "coordinateTransform not set" ), 4 );
145 myProjectedViewExtent = rendererContext.extent();
146 myProjectedLayerExtent = layer->extent();
147 }
148
149 // clip raster extent to view extent
150 QgsRectangle myRasterExtent = layer->ignoreExtents() ? myProjectedViewExtent : myProjectedViewExtent.intersect( myProjectedLayerExtent );
151 if ( myRasterExtent.isEmpty() )
152 {
153 QgsDebugMsgLevel( QStringLiteral( "draw request outside view extent." ), 2 );
154 // nothing to do
155 return;
156 }
157
158 QgsDebugMsgLevel( "theViewExtent is " + rendererContext.extent().toString(), 4 );
159 QgsDebugMsgLevel( "myProjectedViewExtent is " + myProjectedViewExtent.toString(), 4 );
160 QgsDebugMsgLevel( "myProjectedLayerExtent is " + myProjectedLayerExtent.toString(), 4 );
161 QgsDebugMsgLevel( "myRasterExtent is " + myRasterExtent.toString(), 4 );
162
163 //
164 // The first thing we do is set up the QgsRasterViewPort. This struct stores all the settings
165 // relating to the size (in pixels and coordinate system units) of the raster part that is
166 // in view in the map window. It also stores the origin.
167 //
168 //this is not a class level member because every time the user pans or zooms
169 //the contents of the rasterViewPort will change
170 mRasterViewPort = new QgsRasterViewPort();
171
172 mRasterViewPort->mDrawnExtent = myRasterExtent;
173 if ( rendererContext.coordinateTransform().isValid() )
174 {
175 mRasterViewPort->mSrcCRS = layer->crs();
176 mRasterViewPort->mDestCRS = rendererContext.coordinateTransform().destinationCrs();
177 mRasterViewPort->mTransformContext = rendererContext.transformContext();
178 }
179 else
180 {
181 mRasterViewPort->mSrcCRS = QgsCoordinateReferenceSystem(); // will be invalid
182 mRasterViewPort->mDestCRS = QgsCoordinateReferenceSystem(); // will be invalid
183 }
184
185 // get dimensions of clipped raster image in device coordinate space (this is the size of the viewport)
186 mRasterViewPort->mTopLeftPoint = mapToPixel.transform( myRasterExtent.xMinimum(), myRasterExtent.yMaximum() );
187 mRasterViewPort->mBottomRightPoint = mapToPixel.transform( myRasterExtent.xMaximum(), myRasterExtent.yMinimum() );
188
189 // align to output device grid, i.e. std::floor/ceil to integers
190 // TODO: this should only be done if paint device is raster - screen, image
191 // for other devices (pdf) it can have floating point origin
192 // we could use floating point for raster devices as well, but respecting the
193 // output device grid should make it more effective as the resampling is done in
194 // the provider anyway
195 mRasterViewPort->mTopLeftPoint.setX( std::floor( mRasterViewPort->mTopLeftPoint.x() ) );
196 mRasterViewPort->mTopLeftPoint.setY( std::floor( mRasterViewPort->mTopLeftPoint.y() ) );
197 mRasterViewPort->mBottomRightPoint.setX( std::ceil( mRasterViewPort->mBottomRightPoint.x() ) );
198 mRasterViewPort->mBottomRightPoint.setY( std::ceil( mRasterViewPort->mBottomRightPoint.y() ) );
199 // recalc myRasterExtent to aligned values
200 myRasterExtent.set(
201 mapToPixel.toMapCoordinates( mRasterViewPort->mTopLeftPoint.x(),
202 mRasterViewPort->mBottomRightPoint.y() ),
203 mapToPixel.toMapCoordinates( mRasterViewPort->mBottomRightPoint.x(),
204 mRasterViewPort->mTopLeftPoint.y() )
205 );
206
207 //raster viewport top left / bottom right are already rounded to int
208 mRasterViewPort->mWidth = static_cast<qgssize>( std::abs( mRasterViewPort->mBottomRightPoint.x() - mRasterViewPort->mTopLeftPoint.x() ) );
209 mRasterViewPort->mHeight = static_cast<qgssize>( std::abs( mRasterViewPort->mBottomRightPoint.y() - mRasterViewPort->mTopLeftPoint.y() ) );
210
211 const double dpi = 25.4 * rendererContext.scaleFactor();
212 if ( mProviderCapabilities & QgsRasterDataProvider::DpiDependentData
213 && rendererContext.dpiTarget() >= 0.0 )
214 {
215 const double dpiScaleFactor = rendererContext.dpiTarget() / dpi;
216 mRasterViewPort->mWidth *= dpiScaleFactor;
217 mRasterViewPort->mHeight *= dpiScaleFactor;
218 }
219 else
220 {
221 rendererContext.setDpiTarget( -1.0 );
222 }
223
224 //the drawable area can start to get very very large when you get down displaying 2x2 or smaller, this is because
225 //mapToPixel.mapUnitsPerPixel() is less then 1,
226 //so we will just get the pixel data and then render these special cases differently in paintImageToCanvas()
227
228 QgsDebugMsgLevel( QStringLiteral( "mapUnitsPerPixel = %1" ).arg( mapToPixel.mapUnitsPerPixel() ), 3 );
229 QgsDebugMsgLevel( QStringLiteral( "mWidth = %1" ).arg( layer->width() ), 3 );
230 QgsDebugMsgLevel( QStringLiteral( "mHeight = %1" ).arg( layer->height() ), 3 );
231 QgsDebugMsgLevel( QStringLiteral( "myRasterExtent.xMinimum() = %1" ).arg( myRasterExtent.xMinimum() ), 3 );
232 QgsDebugMsgLevel( QStringLiteral( "myRasterExtent.xMaximum() = %1" ).arg( myRasterExtent.xMaximum() ), 3 );
233 QgsDebugMsgLevel( QStringLiteral( "myRasterExtent.yMinimum() = %1" ).arg( myRasterExtent.yMinimum() ), 3 );
234 QgsDebugMsgLevel( QStringLiteral( "myRasterExtent.yMaximum() = %1" ).arg( myRasterExtent.yMaximum() ), 3 );
235
236 QgsDebugMsgLevel( QStringLiteral( "mTopLeftPoint.x() = %1" ).arg( mRasterViewPort->mTopLeftPoint.x() ), 3 );
237 QgsDebugMsgLevel( QStringLiteral( "mBottomRightPoint.x() = %1" ).arg( mRasterViewPort->mBottomRightPoint.x() ), 3 );
238 QgsDebugMsgLevel( QStringLiteral( "mTopLeftPoint.y() = %1" ).arg( mRasterViewPort->mTopLeftPoint.y() ), 3 );
239 QgsDebugMsgLevel( QStringLiteral( "mBottomRightPoint.y() = %1" ).arg( mRasterViewPort->mBottomRightPoint.y() ), 3 );
240
241 QgsDebugMsgLevel( QStringLiteral( "mWidth = %1" ).arg( mRasterViewPort->mWidth ), 3 );
242 QgsDebugMsgLevel( QStringLiteral( "mHeight = %1" ).arg( mRasterViewPort->mHeight ), 3 );
243
244 // /\/\/\ - added to handle zoomed-in rasters
245
246 // TODO R->mLastViewPort = *mRasterViewPort;
247
248 // TODO: is it necessary? Probably WMS only?
249 layer->dataProvider()->setDpi( dpi );
250
251 // copy the whole raster pipe!
252 mPipe.reset( new QgsRasterPipe( *layer->pipe() ) );
253
254 QObject::connect( mPipe->provider(), &QgsRasterDataProvider::statusChanged, layer, &QgsRasterLayer::statusChanged );
255 QgsRasterRenderer *rasterRenderer = mPipe->renderer();
256 if ( rasterRenderer
257 && !( rendererContext.flags() & Qgis::RenderContextFlag::RenderPreviewJob )
258 && !( rendererContext.flags() & Qgis::RenderContextFlag::Render3DMap ) )
259 {
260 layer->refreshRendererIfNeeded( rasterRenderer, rendererContext.extent() );
261 }
262
263 mPipe->evaluateDataDefinedProperties( rendererContext.expressionContext() );
264
265 const QgsRasterLayerTemporalProperties *temporalProperties = qobject_cast< const QgsRasterLayerTemporalProperties * >( layer->temporalProperties() );
266 if ( temporalProperties->isActive() && renderContext()->isTemporal() )
267 {
268 switch ( temporalProperties->mode() )
269 {
270 case Qgis::RasterTemporalMode::FixedTemporalRange:
271 case Qgis::RasterTemporalMode::RedrawLayerOnly:
272 break;
273
274 case Qgis::RasterTemporalMode::TemporalRangeFromDataProvider:
275 // in this mode we need to pass on the desired render temporal range to the data provider
276 if ( mPipe->provider()->temporalCapabilities() )
277 {
278 mPipe->provider()->temporalCapabilities()->setRequestedTemporalRange( rendererContext.temporalRange() );
279 mPipe->provider()->temporalCapabilities()->setIntervalHandlingMethod( temporalProperties->intervalHandlingMethod() );
280 }
281 break;
282 }
283 }
284 else if ( mPipe->provider()->temporalCapabilities() )
285 {
286 mPipe->provider()->temporalCapabilities()->setRequestedTemporalRange( QgsDateTimeRange() );
287 mPipe->provider()->temporalCapabilities()->setIntervalHandlingMethod( temporalProperties->intervalHandlingMethod() );
288 }
289
291
292 if ( layer->elevationProperties() && layer->elevationProperties()->hasElevation() )
293 {
295 = static_cast<QgsRasterLayerElevationProperties *>( layer->elevationProperties() );
296 mDrawElevationMap = true;
297 mElevationScale = elevProp->zScale();
298 mElevationOffset = elevProp->zOffset();
299 mElevationBand = elevProp->bandNumber();
300 }
301
302 mFeedback->setRenderContext( rendererContext );
303
304 mPipe->moveToThread( nullptr );
305}
306
308{
309 delete mFeedback;
310
311 delete mRasterViewPort;
312}
313
315{
316 // Skip rendering of out of view tiles (xyz)
317 if ( !mRasterViewPort || ( renderContext()->testFlag( Qgis::RenderContextFlag::RenderPreviewJob ) &&
318 !( mProviderCapabilities &
319 QgsRasterInterface::Capability::Prefetch ) ) )
320 return true;
321
322 mPipe->moveToThread( QThread::currentThread() );
323
324 QElapsedTimer time;
325 time.start();
326 //
327 //
328 // The goal here is to make as many decisions as possible early on (outside of the rendering loop)
329 // so that we can maximise performance of the rendering process. So now we check which drawing
330 // procedure to use :
331 //
332
333 const QgsScopedQPainterState painterSate( renderContext()->painter() );
334 if ( !mClippingRegions.empty() )
335 {
336 bool needsPainterClipPath = false;
337 const QPainterPath path = QgsMapClippingUtils::calculatePainterClipRegion( mClippingRegions, *renderContext(), Qgis::LayerType::Raster, needsPainterClipPath );
338 if ( needsPainterClipPath )
339 renderContext()->painter()->setClipPath( path, Qt::IntersectClip );
340 }
341
342 QgsRasterProjector *projector = mPipe->projector();
343 bool restoreOldResamplingStage = false;
344 const Qgis::RasterResamplingStage oldResamplingState = mPipe->resamplingStage();
345
346 // TODO add a method to interface to get provider and get provider
347 // params in QgsRasterProjector
348 if ( projector )
349 {
350 // Force provider resampling if reprojection is needed
351 if ( ( mPipe->provider()->providerCapabilities() & QgsRasterDataProvider::ProviderHintCanPerformProviderResampling ) &&
352 mRasterViewPort->mSrcCRS != mRasterViewPort->mDestCRS &&
353 oldResamplingState != Qgis::RasterResamplingStage::Provider )
354 {
355 restoreOldResamplingStage = true;
356 mPipe->setResamplingStage( Qgis::RasterResamplingStage::Provider );
357 }
358 projector->setCrs( mRasterViewPort->mSrcCRS, mRasterViewPort->mDestCRS, mRasterViewPort->mTransformContext );
359 }
360
361 // important -- disable SmoothPixmapTransform for raster layer renders. We want individual pixels to be clearly defined!
362 renderContext()->painter()->setRenderHint( QPainter::SmoothPixmapTransform, false );
363
364 // Drawer to pipe?
365 QgsRasterIterator iterator( mPipe->last() );
366 QgsRasterDrawer drawer( &iterator );
367 drawer.draw( *( renderContext() ), mRasterViewPort, mFeedback );
368
369 if ( mDrawElevationMap )
370 drawElevationMap();
371
372 if ( restoreOldResamplingStage )
373 {
374 mPipe->setResamplingStage( oldResamplingState );
375 }
376
377 const QStringList errors = mFeedback->errors();
378 for ( const QString &error : errors )
379 {
380 mErrors.append( error );
381 }
382
383 QgsDebugMsgLevel( QStringLiteral( "total raster draw time (ms): %1" ).arg( time.elapsed(), 5 ), 4 );
384 mReadyToCompose = true;
385
386 mPipe->moveToThread( nullptr );
387
388 return !mFeedback->isCanceled();
389}
390
392{
393 return mFeedback;
394}
395
397{
398 if ( !mRasterViewPort || !mPipe )
399 return false; // this layer is not going to get rendered
400
401 // preview of intermediate raster rendering results requires a temporary output image
403 return true;
404
405 if ( QgsRasterRenderer *renderer = mPipe->renderer() )
406 {
408 && renderContext()->testFlag( Qgis::RenderContextFlag::UseAdvancedEffects ) && ( !qgsDoubleNear( mLayerOpacity, 1.0 ) ) )
409 return true;
410 }
411
412 return false;
413}
414
415void QgsRasterLayerRenderer::drawElevationMap()
416{
417 QgsRasterDataProvider *dataProvider = mPipe->provider();
418 if ( renderContext()->elevationMap() && dataProvider )
419 {
420 double dpiScalefactor;
421
422 if ( renderContext()->dpiTarget() >= 0.0 )
423 dpiScalefactor = renderContext()->dpiTarget() / ( renderContext()->scaleFactor() * 25.4 );
424 else
425 dpiScalefactor = 1.0;
426
427 int outputWidth = static_cast<int>( static_cast<double>( mRasterViewPort->mWidth ) / dpiScalefactor ) ;
428 int outputHeight = static_cast<int>( static_cast<double>( mRasterViewPort->mHeight ) / dpiScalefactor );
429
430 QSize viewSize = renderContext()->deviceOutputSize();
431 int viewWidth = static_cast<int>( viewSize.width() / dpiScalefactor );
432 int viewHeight = static_cast<int>( viewSize.height() / dpiScalefactor );
433
434 bool canRenderElevation = false;
435 std::unique_ptr<QgsRasterBlock> elevationBlock;
436 if ( mRasterViewPort->mSrcCRS == mRasterViewPort->mDestCRS )
437 {
438 elevationBlock.reset(
439 dataProvider->block(
440 mElevationBand,
441 mRasterViewPort->mDrawnExtent,
442 outputWidth,
443 outputHeight,
444 mFeedback ) );
445 canRenderElevation = true;
446 }
447 else
448 {
449 // Destinaton CRS is different from the source CRS.
450 // Using the raster projector lead to have big artifacts when rendering the elevation map.
451 // To get a smoother elevation map, we use GDAL resampling with coordinates transform
452 QgsRectangle viewExtentInLayerCoordinate = renderContext()->extent();
453
454 // If view extent is infinite, we use the data provider extent
455 if ( viewExtentInLayerCoordinate.xMinimum() == std::numeric_limits<double>::lowest() &&
456 viewExtentInLayerCoordinate.yMinimum() == std::numeric_limits<double>::lowest() &&
457 viewExtentInLayerCoordinate.xMaximum() == std::numeric_limits<double>::max() &&
458 viewExtentInLayerCoordinate.yMaximum() == std::numeric_limits<double>::max() )
459 {
460 viewExtentInLayerCoordinate = dataProvider->extent();
461 }
462
463 double xLayerResol = viewExtentInLayerCoordinate.width() / static_cast<double>( viewWidth );
464 double yLayerResol = viewExtentInLayerCoordinate.height() / static_cast<double>( viewHeight );
465
466 double overSampling = 1;
467 if ( mPipe->resampleFilter() )
468 overSampling = mPipe->resampleFilter()->maxOversampling();
469
470 if ( dataProvider->capabilities() & QgsRasterDataProvider::Size )
471 {
472 // If the dataprovider has size capability, we calculate the requested resolution to provider
473 double providerXResol = dataProvider->extent().width() / dataProvider->xSize();
474 double providerYResol = dataProvider->extent().height() / dataProvider->ySize();
475 overSampling = ( xLayerResol / providerXResol + yLayerResol / providerYResol ) / 2;
476 }
477
478 GDALResampleAlg alg;
479 if ( overSampling > 1 )
481 else
483
484 Qgis::DataType dataType = dataProvider->dataType( mElevationBand );
485
486 // we need extra pixels on border to avoid effect border with resampling (at least 2 pixels band for cubic alg)
487 int sourceWidth = viewWidth + 4;
488 int sourceHeight = viewHeight + 4;
489 viewExtentInLayerCoordinate = QgsRectangle(
490 viewExtentInLayerCoordinate.xMinimum() - xLayerResol * 2,
491 viewExtentInLayerCoordinate.yMinimum() - yLayerResol * 2,
492 viewExtentInLayerCoordinate.xMaximum() + xLayerResol * 2,
493 viewExtentInLayerCoordinate.yMaximum() + yLayerResol * 2 );
494
495 // Now we can do the resampling
496 std::unique_ptr<QgsRasterBlock> sourcedata( dataProvider->block( mElevationBand, viewExtentInLayerCoordinate, sourceWidth, sourceHeight, mFeedback ) );
497 gdal::dataset_unique_ptr gdalDsInput =
498 QgsGdalUtils::blockToSingleBandMemoryDataset( viewExtentInLayerCoordinate, sourcedata.get() );
499
500
501 elevationBlock.reset( new QgsRasterBlock( dataType,
502 outputWidth,
503 outputHeight ) );
504
505 elevationBlock->setNoDataValue( dataProvider->sourceNoDataValue( mElevationBand ) );
506
507 gdal::dataset_unique_ptr gdalDsOutput =
508 QgsGdalUtils::blockToSingleBandMemoryDataset( mRasterViewPort->mDrawnExtent, elevationBlock.get() );
509
510 // For coordinate transformation, we try to obtain a coordinate operation string from the transform context.
511 // Depending of the CRS, if we can't we use GDAL transformation directly from the source and destination CRS
512 QString coordinateOperation;
513 const QgsCoordinateTransformContext &transformContext = renderContext()->transformContext();
514 if ( transformContext.mustReverseCoordinateOperation( mRasterViewPort->mDestCRS, mRasterViewPort->mSrcCRS ) )
515 coordinateOperation = transformContext.calculateCoordinateOperation( mRasterViewPort->mSrcCRS, mRasterViewPort->mDestCRS );
516 else
517 coordinateOperation = transformContext.calculateCoordinateOperation( mRasterViewPort->mDestCRS, mRasterViewPort->mSrcCRS );
518
519 if ( coordinateOperation.isEmpty() )
520 canRenderElevation = QgsGdalUtils::resampleSingleBandRaster( gdalDsInput.get(), gdalDsOutput.get(), alg,
521 mRasterViewPort->mSrcCRS, mRasterViewPort->mDestCRS );
522 else
523 canRenderElevation = QgsGdalUtils::resampleSingleBandRaster( gdalDsInput.get(), gdalDsOutput.get(), alg,
524 coordinateOperation.toUtf8().constData() );
525 }
526
527 if ( canRenderElevation )
528 {
529 QPoint topLeft;
530 if ( renderContext()->mapToPixel().mapRotation() != 0 )
531 {
532 // Now rendering elevation on the elevation map, we need to take care of rotation:
533 // again a resampling but this time with a geotransform.
534 const QgsMapToPixel &mtp = renderContext()->mapToPixel();
536
537 int elevMapWidth = elevMap->rawElevationImage().width();
538 int elevMapHeight = elevMap->rawElevationImage().height();
539
540 int bottom = 0;
541 int top = elevMapHeight;
542 int left = elevMapWidth;
543 int right = 0;
544
545 QList<QgsPointXY> corners;
546 corners << QgsPointXY( mRasterViewPort->mDrawnExtent.xMinimum(), mRasterViewPort->mDrawnExtent.yMinimum() )
547 << QgsPointXY( mRasterViewPort->mDrawnExtent.xMaximum(), mRasterViewPort->mDrawnExtent.yMaximum() )
548 << QgsPointXY( mRasterViewPort->mDrawnExtent.xMinimum(), mRasterViewPort->mDrawnExtent.yMaximum() )
549 << QgsPointXY( mRasterViewPort->mDrawnExtent.xMaximum(), mRasterViewPort->mDrawnExtent.yMinimum() );
550
551 for ( const QgsPointXY &corner : std::as_const( corners ) )
552 {
553 const QgsPointXY dpt = mtp.transform( corner );
554 int x = static_cast<int>( std::round( dpt.x() ) );
555 int y = static_cast<int>( std::round( dpt.y() ) );
556
557 if ( x < left )
558 left = x;
559 if ( x > right )
560 right = x;
561 if ( y < top )
562 top = y;
563 if ( y > bottom )
564 bottom = y;
565 }
566
567 const QgsPointXY origin = mtp.toMapCoordinates( left, top );
568 double gridXSize = mtp.toMapCoordinates( right, top ).distance( origin );
569 double gridYSize = mtp.toMapCoordinates( left, bottom ).distance( origin );
570 double angleRad = renderContext()->mapToPixel().mapRotation() / 180 * M_PI;
571
572 gdal::dataset_unique_ptr gdalDsInput =
573 QgsGdalUtils::blockToSingleBandMemoryDataset( mRasterViewPort->mDrawnExtent, elevationBlock.get() );
574
575 std::unique_ptr<QgsRasterBlock> rotatedElevationBlock =
576 std::make_unique<QgsRasterBlock>( elevationBlock->dataType(), right - left + 1, bottom - top + 1 );
577
578 rotatedElevationBlock->setNoDataValue( elevationBlock->noDataValue() );
579
580 gdal::dataset_unique_ptr gdalDsOutput =
581 QgsGdalUtils::blockToSingleBandMemoryDataset( angleRad, origin, gridXSize, gridYSize, rotatedElevationBlock.get() );
582
584 gdalDsInput.get(),
585 gdalDsOutput.get(),
587 {
588 elevationBlock.reset( rotatedElevationBlock.release() );
589 }
590
591 topLeft = QPoint( left, top );
592 }
593 else
594 {
595 topLeft = mRasterViewPort->mTopLeftPoint.toQPointF().toPoint();
596 }
597
599 elevationBlock.get(),
600 topLeft.y(),
601 topLeft.x(),
602 mElevationScale,
603 mElevationOffset );
604 }
605 }
606}
RasterResamplingStage
Stage at which raster resampling occurs.
Definition: qgis.h:891
@ Provider
Resampling occurs in Provider.
@ InternalLayerOpacityHandling
The renderer internally handles the raster layer's opacity, so the default layer level opacity handli...
DataType
Raster data types.
Definition: qgis.h:242
@ RenderPreviewJob
Render is a 'canvas preview' render, and shortcuts should be taken to ensure fast rendering.
@ RenderPartialOutput
Whether to make extra effort to update map image with partially rendered layers (better for interacti...
@ Render3DMap
Render is for a 3D map.
@ UseAdvancedEffects
Enable layer opacity and blending effects.
This class represents a coordinate reference system (CRS).
Contains information about the context in which a coordinate transform is executed.
QString calculateCoordinateOperation(const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination) const
Returns the Proj coordinate operation string to use when transforming from the specified source CRS t...
bool mustReverseCoordinateOperation(const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination) const
Returns true if the coordinate operation returned by calculateCoordinateOperation() for the source to...
Class for doing transforms between two map coordinate systems.
void setBallparkTransformsAreAppropriate(bool appropriate)
Sets whether approximate "ballpark" results are appropriate for this coordinate transform.
bool isValid() const
Returns true if the coordinate transform is valid, ie both the source and destination CRS have been s...
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system, which the transform will transform coordinates t...
QgsRectangle transformBoundingBox(const QgsRectangle &rectangle, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool handle180Crossover=false) const SIP_THROW(QgsCsException)
Transforms a rectangle from the source CRS to the destination CRS.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:66
Stores digital elevation model in a raster image which may get updated as a part of map layer renderi...
QImage rawElevationImage() const
Returns raw elevation image with elevations encoded as color values.
void fillWithRasterBlock(QgsRasterBlock *block, int top, int left, double zScale=1.0, double offset=0.0)
Fills the elevation map with values contains in a raster block starting from position defined by top ...
QString what() const
Definition: qgsexception.h:48
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition: qgsfeedback.h:45
static GDALResampleAlg gdalResamplingAlgorithm(QgsRasterDataProvider::ResamplingMethod method)
Returns the GDAL resampling method corresponding to the QGIS resampling method.
static bool resampleSingleBandRaster(GDALDatasetH hSrcDS, GDALDatasetH hDstDS, GDALResampleAlg resampleAlg, const char *pszCoordinateOperation)
Resamples a single band raster to the destination dataset with different resolution (and possibly wit...
static gdal::dataset_unique_ptr blockToSingleBandMemoryDataset(int pixelWidth, int pixelHeight, const QgsRectangle &extent, void *block, GDALDataType dataType)
Converts a data block to a single band GDAL memory dataset.
static QPainterPath calculatePainterClipRegion(const QList< QgsMapClippingRegion > &regions, const QgsRenderContext &context, Qgis::LayerType layerType, bool &shouldClip)
Returns a QPainterPath representing the intersection of clipping regions from context which should be...
static QList< QgsMapClippingRegion > collectClippingRegionsForLayer(const QgsRenderContext &context, const QgsMapLayer *layer)
Collects the list of map clipping regions from a context which apply to a map layer.
double zScale() const
Returns the z scale, which is a scaling factor which should be applied to z values from the layer.
virtual bool hasElevation() const
Returns true if the layer has an elevation or z component.
double zOffset() const
Returns the z offset, which is a fixed offset amount which should be added to z values from the layer...
Base class for utility classes that encapsulate information necessary for rendering of map layers.
bool mReadyToCompose
The flag must be set to false in renderer's constructor if wants to use the smarter map redraws funct...
QgsRenderContext * renderContext()
Returns the render context associated with the renderer.
QStringList errors() const
Returns list of errors (problems) that happened during the rendering.
virtual QgsRectangle extent() const
Returns the extent of the layer.
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:79
void statusChanged(const QString &status)
Emit a signal with status (e.g. to be caught by QgisApp and display a msg on status bar)
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:39
int mapHeight() const
Returns current map height in pixels.
void setMapRotation(double degrees, double cx, double cy)
Sets map rotation in degrees (clockwise).
double mapUnitsPerPixel() const
Returns the current map units per pixel.
QgsPointXY toMapCoordinates(int x, int y) const
Transforms device coordinates to map (world) coordinates.
int mapWidth() const
Returns the current map width in pixels.
QgsPointXY transform(const QgsPointXY &p) const
Transforms a point p from map (world) coordinates to device coordinates.
Definition: qgsmaptopixel.h:90
double mapRotation() const
Returns the current map rotation in degrees (clockwise).
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
A class to represent a 2D point.
Definition: qgspointxy.h:59
void setX(double x) SIP_HOLDGIL
Sets the x value of the point.
Definition: qgspointxy.h:122
double y
Definition: qgspointxy.h:63
Q_GADGET double x
Definition: qgspointxy.h:62
double distance(double x, double y) const SIP_HOLDGIL
Returns the distance between this point and a specified x, y coordinate.
Definition: qgspointxy.h:211
void setY(double y) SIP_HOLDGIL
Sets the y value of the point.
Definition: qgspointxy.h:132
QPointF toQPointF() const
Converts a point to a QPointF.
Definition: qgspointxy.h:169
Feedback object tailored for raster block reading.
void setPreviewOnly(bool preview)
set flag whether the block request is for preview purposes only
void setRenderPartialOutput(bool enable)
Set whether our painter is drawing to a temporary image used just by this layer.
Raster data container.
Base class for raster data providers.
virtual double sourceNoDataValue(int bandNo) const
Value representing no data value.
ResamplingMethod zoomedOutResamplingMethod() const
Returns resampling method for zoomed-out operations.
void setDpi(int dpi)
Sets the output device resolution.
QgsRectangle extent() const override=0
Returns the extent of the layer.
Qgis::DataType dataType(int bandNo) const override=0
Returns data type for the band specified by number.
@ ProviderHintCanPerformProviderResampling
Provider can perform resampling (to be opposed to post rendering resampling) (since QGIS 3....
QgsRasterBlock * block(int bandNo, const QgsRectangle &boundingBox, int width, int height, QgsRasterBlockFeedback *feedback=nullptr) override
Read block of data using given extent and size.
void statusChanged(const QString &) const
Emit a message to be displayed on status bar, usually used by network providers (WMS,...
ResamplingMethod zoomedInResamplingMethod() const
Returns resampling method for zoomed-in operations.
The drawing pipe for raster layers.
void draw(QPainter *p, QgsRasterViewPort *viewPort, const QgsMapToPixel *qgsMapToPixel, QgsRasterBlockFeedback *feedback=nullptr)
Draws raster data.
@ Size
Original data source size (and thus resolution) is known, it is not always available,...
virtual int xSize() const
Gets raster size.
virtual int capabilities() const
Returns a bitmask containing the supported capabilities.
virtual int ySize() const
Iterator for sequentially processing raster cells.
Raster layer specific subclass of QgsMapLayerElevationProperties.
int bandNumber() const
Returns the band number from which the elevation should be taken.
Implementation of threaded rendering for raster layers.
bool render() override
Do the rendering (based on data stored in the class).
QgsRasterLayerRenderer(QgsRasterLayer *layer, QgsRenderContext &rendererContext)
bool forceRasterRender() const override
Returns true if the renderer must be rendered to a raster paint device (e.g.
QgsFeedback * feedback() const override
Access to feedback object of the layer renderer (may be nullptr)
Implementation of map layer temporal properties for raster layers.
Qgis::TemporalIntervalMatchMethod intervalHandlingMethod() const
Returns the desired method to use when resolving a temporal interval to matching layers or bands in t...
Qgis::RasterTemporalMode mode() const
Returns the temporal properties mode.
Represents a raster layer.
int height() const
Returns the height of the (unclipped) raster.
void refreshRendererIfNeeded(QgsRasterRenderer *rasterRenderer, const QgsRectangle &extent)
Refresh renderer with new extent, if needed.
QgsMapLayerTemporalProperties * temporalProperties() override
Returns the layer's temporal properties.
QgsRasterPipe * pipe()
Returns the raster pipe.
bool ignoreExtents() const
If the ignoreExtent flag is set, the layer will also render outside the bounding box reported by the ...
QgsRasterDataProvider * dataProvider() override
Returns the source data provider.
int width() const
Returns the width of the (unclipped) raster.
QgsMapLayerElevationProperties * elevationProperties() override
Returns the layer's elevation properties.
Contains a pipeline of raster interfaces for sequential raster processing.
Definition: qgsrasterpipe.h:50
QgsRasterProjector implements approximate projection support for it calculates grid of points in sour...
Q_DECL_DEPRECATED void setCrs(const QgsCoordinateReferenceSystem &srcCRS, const QgsCoordinateReferenceSystem &destCRS, int srcDatumTransform=-1, int destDatumTransform=-1)
Sets the source and destination CRS.
Raster renderer pipe that applies colors to a raster.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
QString toString(int precision=16) const
Returns a string representation of form xmin,ymin : xmax,ymax Coordinates will be truncated to the sp...
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:193
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:183
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:188
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:198
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
Definition: qgsrectangle.h:230
void set(const QgsPointXY &p1, const QgsPointXY &p2, bool normalize=true)
Sets the rectangle from two QgsPoints.
Definition: qgsrectangle.h:122
void setMinimal() SIP_HOLDGIL
Set a rectangle so that min corner is at max and max corner is at min.
Definition: qgsrectangle.h:172
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
Definition: qgsrectangle.h:223
bool isEmpty() const
Returns true if the rectangle is empty.
Definition: qgsrectangle.h:469
QgsRectangle intersect(const QgsRectangle &rect) const
Returns the intersection with the given rectangle.
Definition: qgsrectangle.h:333
Contains information about the context of a rendering operation.
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
QPainter * painter()
Returns the destination QPainter for the render operation.
QgsExpressionContext & expressionContext()
Gets the expression context.
QgsCoordinateTransformContext transformContext() const
Returns the context's coordinate transform context, which stores various information regarding which ...
QgsElevationMap * elevationMap() const
Returns the destination elevation map for the render operation.
const QgsRectangle & extent() const
When rendering a map layer, calling this method returns the "clipping" extent for the layer (in the l...
bool testFlag(Qgis::RenderContextFlag flag) const
Check whether a particular flag is enabled.
void setDpiTarget(double dpi)
Sets the targeted dpi for rendering.
double dpiTarget() const
Returns the targeted DPI for rendering.
const QgsMapToPixel & mapToPixel() const
Returns the context's map to pixel transform, which transforms between map coordinates and device coo...
QSize deviceOutputSize() const
Returns the device output size of the render.
QgsCoordinateTransform coordinateTransform() const
Returns the current coordinate transform for the context.
Qgis::RenderContextFlags flags() const
Returns combination of flags used for rendering.
Scoped object for saving and restoring a QPainter object's state.
bool isActive() const
Returns true if the temporal property is active.
const QgsDateTimeRange & temporalRange() const
Returns the datetime range for the object.
std::unique_ptr< std::remove_pointer< GDALDatasetH >::type, GDALDatasetCloser > dataset_unique_ptr
Scoped GDAL dataset.
Definition: qgsogrutils.h:157
unsigned long long qgssize
Qgssize is used instead of size_t, because size_t is stdlib type, unknown by SIP, and it would be har...
Definition: qgis.h:4064
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:3509
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
This class provides details of the viewable area that a raster will be rendered into.
qgssize mHeight
Height, number of rows to be rendered.
QgsCoordinateReferenceSystem mDestCRS
Target coordinate system.
QgsPointXY mBottomRightPoint
Coordinate (in output device coordinate system) of bottom right corner of the part of the raster that...
QgsPointXY mTopLeftPoint
Coordinate (in output device coordinate system) of top left corner of the part of the raster that is ...
QgsCoordinateReferenceSystem mSrcCRS
Source coordinate system.
QgsRectangle mDrawnExtent
Intersection of current map extent and layer extent.
QgsCoordinateTransformContext mTransformContext
Coordinate transform context.
qgssize mWidth
Width, number of columns to be rendered.