QGIS API Documentation 3.34.0-Prizren (ffbdd678812)
Loading...
Searching...
No Matches
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#include "qgsruntimeprofiler.h"
35#include "qgsapplication.h"
36
37#include <QElapsedTimer>
38#include <QPointer>
39#include <QThread>
40
42
43QgsRasterLayerRendererFeedback::QgsRasterLayerRendererFeedback( QgsRasterLayerRenderer *r )
44 : mR( r )
45 , mMinimalPreviewInterval( 250 )
46{
48}
49
50void QgsRasterLayerRendererFeedback::onNewData()
51{
52 if ( !renderPartialOutput() )
53 return; // we were not asked for partial renders and we may not have a temporary image for overwriting...
54
55 // update only once upon a time
56 // (preview itself takes some time)
57 if ( mLastPreview.isValid() && mLastPreview.msecsTo( QTime::currentTime() ) < mMinimalPreviewInterval )
58 return;
59
60 // TODO: update only the area that got new data
61
62 QgsDebugMsgLevel( QStringLiteral( "new raster preview! %1" ).arg( mLastPreview.msecsTo( QTime::currentTime() ) ), 3 );
63 QElapsedTimer t;
64 t.start();
66 feedback.setPreviewOnly( true );
67 feedback.setRenderPartialOutput( true );
68 QgsRasterIterator iterator( mR->mPipe->last() );
69 QgsRasterDrawer drawer( &iterator );
70 drawer.draw( *( mR->renderContext() ), mR->mRasterViewPort, &feedback );
71 mR->mReadyToCompose = true;
72 QgsDebugMsgLevel( QStringLiteral( "total raster preview time: %1 ms" ).arg( t.elapsed() ), 3 );
73 mLastPreview = QTime::currentTime();
74}
75
79 : QgsMapLayerRenderer( layer->id(), &rendererContext )
80 , mLayerName( layer->name() )
81 , mLayerOpacity( layer->opacity() )
82 , mProviderCapabilities( static_cast<QgsRasterDataProvider::Capability>( layer->dataProvider()->capabilities() ) )
83 , mFeedback( new QgsRasterLayerRendererFeedback( this ) )
84 , mEnableProfile( rendererContext.flags() & Qgis::RenderContextFlag::RecordProfile )
85{
86 mReadyToCompose = false;
87
88 QElapsedTimer timer;
89 timer.start();
90
91 QgsMapToPixel mapToPixel = rendererContext.mapToPixel();
92 if ( rendererContext.mapToPixel().mapRotation() )
93 {
94 // unset rotation for the sake of local computations.
95 // Rotation will be handled by QPainter later
96 // TODO: provide a method of QgsMapToPixel to fetch map center
97 // in geographical units
98 const QgsPointXY center = mapToPixel.toMapCoordinates(
99 static_cast<int>( mapToPixel.mapWidth() / 2.0 ),
100 static_cast<int>( mapToPixel.mapHeight() / 2.0 )
101 );
102 mapToPixel.setMapRotation( 0, center.x(), center.y() );
103 }
104
105 QgsRectangle myProjectedViewExtent;
106 QgsRectangle myProjectedLayerExtent;
107
108 if ( rendererContext.coordinateTransform().isValid() )
109 {
110 QgsDebugMsgLevel( QStringLiteral( "coordinateTransform set -> project extents." ), 4 );
111 if ( rendererContext.extent().xMinimum() == std::numeric_limits<double>::lowest() &&
112 rendererContext.extent().yMinimum() == std::numeric_limits<double>::lowest() &&
113 rendererContext.extent().xMaximum() == std::numeric_limits<double>::max() &&
114 rendererContext.extent().yMaximum() == std::numeric_limits<double>::max() )
115 {
116 // We get in this situation if the view CRS is geographical and the
117 // extent goes beyond -180,-90,180,90. To avoid reprojection issues to the
118 // layer CRS, then this dummy extent is returned by QgsMapRendererJob::reprojectToLayerExtent()
119 // Don't try to reproject it now to view extent as this would return
120 // a null rectangle.
121 myProjectedViewExtent = rendererContext.extent();
122 }
123 else
124 {
125 try
126 {
127 QgsCoordinateTransform ct = rendererContext.coordinateTransform();
129 myProjectedViewExtent = ct.transformBoundingBox( rendererContext.extent() );
130 }
131 catch ( QgsCsException &cs )
132 {
133 QgsMessageLog::logMessage( QObject::tr( "Could not reproject view extent: %1" ).arg( cs.what() ), QObject::tr( "Raster" ) );
134 myProjectedViewExtent.setNull();
135 }
136 }
137
138 try
139 {
140 QgsCoordinateTransform ct = rendererContext.coordinateTransform();
142 myProjectedLayerExtent = ct.transformBoundingBox( layer->extent() );
143 }
144 catch ( QgsCsException &cs )
145 {
146 QgsMessageLog::logMessage( QObject::tr( "Could not reproject layer extent: %1" ).arg( cs.what() ), QObject::tr( "Raster" ) );
147 myProjectedLayerExtent.setNull();
148 }
149 }
150 else
151 {
152 QgsDebugMsgLevel( QStringLiteral( "coordinateTransform not set" ), 4 );
153 myProjectedViewExtent = rendererContext.extent();
154 myProjectedLayerExtent = layer->extent();
155 }
156
157 // clip raster extent to view extent
158 QgsRectangle myRasterExtent = layer->ignoreExtents() ? myProjectedViewExtent : myProjectedViewExtent.intersect( myProjectedLayerExtent );
159 if ( myRasterExtent.isEmpty() )
160 {
161 QgsDebugMsgLevel( QStringLiteral( "draw request outside view extent." ), 2 );
162 // nothing to do
163 return;
164 }
165
166 QgsDebugMsgLevel( "theViewExtent is " + rendererContext.extent().toString(), 4 );
167 QgsDebugMsgLevel( "myProjectedViewExtent is " + myProjectedViewExtent.toString(), 4 );
168 QgsDebugMsgLevel( "myProjectedLayerExtent is " + myProjectedLayerExtent.toString(), 4 );
169 QgsDebugMsgLevel( "myRasterExtent is " + myRasterExtent.toString(), 4 );
170
171 //
172 // The first thing we do is set up the QgsRasterViewPort. This struct stores all the settings
173 // relating to the size (in pixels and coordinate system units) of the raster part that is
174 // in view in the map window. It also stores the origin.
175 //
176 //this is not a class level member because every time the user pans or zooms
177 //the contents of the rasterViewPort will change
178 mRasterViewPort = new QgsRasterViewPort();
179
180 mRasterViewPort->mDrawnExtent = myRasterExtent;
181 if ( rendererContext.coordinateTransform().isValid() )
182 {
183 mRasterViewPort->mSrcCRS = layer->crs();
184 mRasterViewPort->mDestCRS = rendererContext.coordinateTransform().destinationCrs();
185 mRasterViewPort->mTransformContext = rendererContext.transformContext();
186 }
187 else
188 {
189 mRasterViewPort->mSrcCRS = QgsCoordinateReferenceSystem(); // will be invalid
190 mRasterViewPort->mDestCRS = QgsCoordinateReferenceSystem(); // will be invalid
191 }
192
193 // get dimensions of clipped raster image in device coordinate space (this is the size of the viewport)
194 mRasterViewPort->mTopLeftPoint = mapToPixel.transform( myRasterExtent.xMinimum(), myRasterExtent.yMaximum() );
195 mRasterViewPort->mBottomRightPoint = mapToPixel.transform( myRasterExtent.xMaximum(), myRasterExtent.yMinimum() );
196
197 // align to output device grid, i.e. std::floor/ceil to integers
198 // TODO: this should only be done if paint device is raster - screen, image
199 // for other devices (pdf) it can have floating point origin
200 // we could use floating point for raster devices as well, but respecting the
201 // output device grid should make it more effective as the resampling is done in
202 // the provider anyway
203 mRasterViewPort->mTopLeftPoint.setX( std::floor( mRasterViewPort->mTopLeftPoint.x() ) );
204 mRasterViewPort->mTopLeftPoint.setY( std::floor( mRasterViewPort->mTopLeftPoint.y() ) );
205 mRasterViewPort->mBottomRightPoint.setX( std::ceil( mRasterViewPort->mBottomRightPoint.x() ) );
206 mRasterViewPort->mBottomRightPoint.setY( std::ceil( mRasterViewPort->mBottomRightPoint.y() ) );
207 // recalc myRasterExtent to aligned values
208 myRasterExtent.set(
209 mapToPixel.toMapCoordinates( mRasterViewPort->mTopLeftPoint.x(),
210 mRasterViewPort->mBottomRightPoint.y() ),
211 mapToPixel.toMapCoordinates( mRasterViewPort->mBottomRightPoint.x(),
212 mRasterViewPort->mTopLeftPoint.y() )
213 );
214
215 //raster viewport top left / bottom right are already rounded to int
216 mRasterViewPort->mWidth = static_cast<qgssize>( std::abs( mRasterViewPort->mBottomRightPoint.x() - mRasterViewPort->mTopLeftPoint.x() ) );
217 mRasterViewPort->mHeight = static_cast<qgssize>( std::abs( mRasterViewPort->mBottomRightPoint.y() - mRasterViewPort->mTopLeftPoint.y() ) );
218
219 const double dpi = 25.4 * rendererContext.scaleFactor();
220 if ( mProviderCapabilities & QgsRasterDataProvider::DpiDependentData
221 && rendererContext.dpiTarget() >= 0.0 )
222 {
223 const double dpiScaleFactor = rendererContext.dpiTarget() / dpi;
224 mRasterViewPort->mWidth *= dpiScaleFactor;
225 mRasterViewPort->mHeight *= dpiScaleFactor;
226 }
227 else
228 {
229 rendererContext.setDpiTarget( -1.0 );
230 }
231
232 //the drawable area can start to get very very large when you get down displaying 2x2 or smaller, this is because
233 //mapToPixel.mapUnitsPerPixel() is less then 1,
234 //so we will just get the pixel data and then render these special cases differently in paintImageToCanvas()
235
236 QgsDebugMsgLevel( QStringLiteral( "mapUnitsPerPixel = %1" ).arg( mapToPixel.mapUnitsPerPixel() ), 3 );
237 QgsDebugMsgLevel( QStringLiteral( "mWidth = %1" ).arg( layer->width() ), 3 );
238 QgsDebugMsgLevel( QStringLiteral( "mHeight = %1" ).arg( layer->height() ), 3 );
239 QgsDebugMsgLevel( QStringLiteral( "myRasterExtent.xMinimum() = %1" ).arg( myRasterExtent.xMinimum() ), 3 );
240 QgsDebugMsgLevel( QStringLiteral( "myRasterExtent.xMaximum() = %1" ).arg( myRasterExtent.xMaximum() ), 3 );
241 QgsDebugMsgLevel( QStringLiteral( "myRasterExtent.yMinimum() = %1" ).arg( myRasterExtent.yMinimum() ), 3 );
242 QgsDebugMsgLevel( QStringLiteral( "myRasterExtent.yMaximum() = %1" ).arg( myRasterExtent.yMaximum() ), 3 );
243
244 QgsDebugMsgLevel( QStringLiteral( "mTopLeftPoint.x() = %1" ).arg( mRasterViewPort->mTopLeftPoint.x() ), 3 );
245 QgsDebugMsgLevel( QStringLiteral( "mBottomRightPoint.x() = %1" ).arg( mRasterViewPort->mBottomRightPoint.x() ), 3 );
246 QgsDebugMsgLevel( QStringLiteral( "mTopLeftPoint.y() = %1" ).arg( mRasterViewPort->mTopLeftPoint.y() ), 3 );
247 QgsDebugMsgLevel( QStringLiteral( "mBottomRightPoint.y() = %1" ).arg( mRasterViewPort->mBottomRightPoint.y() ), 3 );
248
249 QgsDebugMsgLevel( QStringLiteral( "mWidth = %1" ).arg( mRasterViewPort->mWidth ), 3 );
250 QgsDebugMsgLevel( QStringLiteral( "mHeight = %1" ).arg( mRasterViewPort->mHeight ), 3 );
251
252 // /\/\/\ - added to handle zoomed-in rasters
253
254 // TODO R->mLastViewPort = *mRasterViewPort;
255
256 // TODO: is it necessary? Probably WMS only?
257 layer->dataProvider()->setDpi( dpi );
258
259 // copy the whole raster pipe!
260 mPipe.reset( new QgsRasterPipe( *layer->pipe() ) );
261
262 QObject::connect( mPipe->provider(), &QgsRasterDataProvider::statusChanged, layer, &QgsRasterLayer::statusChanged );
263 QgsRasterRenderer *rasterRenderer = mPipe->renderer();
264 if ( rasterRenderer
265 && !( rendererContext.flags() & Qgis::RenderContextFlag::RenderPreviewJob )
266 && !( rendererContext.flags() & Qgis::RenderContextFlag::Render3DMap ) )
267 {
268 layer->refreshRendererIfNeeded( rasterRenderer, rendererContext.extent() );
269 }
270
271 mPipe->evaluateDataDefinedProperties( rendererContext.expressionContext() );
272
273 const QgsRasterLayerTemporalProperties *temporalProperties = qobject_cast< const QgsRasterLayerTemporalProperties * >( layer->temporalProperties() );
274 if ( temporalProperties->isActive() && renderContext()->isTemporal() )
275 {
276 switch ( temporalProperties->mode() )
277 {
280 break;
281
283 // in this mode we need to pass on the desired render temporal range to the data provider
284 if ( mPipe->provider()->temporalCapabilities() )
285 {
286 mPipe->provider()->temporalCapabilities()->setRequestedTemporalRange( rendererContext.temporalRange() );
287 mPipe->provider()->temporalCapabilities()->setIntervalHandlingMethod( temporalProperties->intervalHandlingMethod() );
288 }
289 break;
290 }
291 }
292 else if ( mPipe->provider()->temporalCapabilities() )
293 {
294 mPipe->provider()->temporalCapabilities()->setRequestedTemporalRange( QgsDateTimeRange() );
295 mPipe->provider()->temporalCapabilities()->setIntervalHandlingMethod( temporalProperties->intervalHandlingMethod() );
296 }
297
299
300 if ( layer->elevationProperties() && layer->elevationProperties()->hasElevation() )
301 {
303 = static_cast<QgsRasterLayerElevationProperties *>( layer->elevationProperties() );
304 mDrawElevationMap = true;
305 mElevationScale = elevProp->zScale();
306 mElevationOffset = elevProp->zOffset();
307 mElevationBand = elevProp->bandNumber();
308 }
309
310 mFeedback->setRenderContext( rendererContext );
311
312 mPipe->moveToThread( nullptr );
313
314 mPreparationTime = timer.elapsed();
315}
316
318{
319 delete mFeedback;
320
321 delete mRasterViewPort;
322}
323
325{
326 std::unique_ptr< QgsScopedRuntimeProfile > profile;
327 if ( mEnableProfile )
328 {
329 profile = std::make_unique< QgsScopedRuntimeProfile >( mLayerName, QStringLiteral( "rendering" ), layerId() );
330 if ( mPreparationTime > 0 )
331 QgsApplication::profiler()->record( QObject::tr( "Create renderer" ), mPreparationTime / 1000.0, QStringLiteral( "rendering" ) );
332 }
333
334 // Skip rendering of out of view tiles (xyz)
335 if ( !mRasterViewPort || ( renderContext()->testFlag( Qgis::RenderContextFlag::RenderPreviewJob ) &&
336 !( mProviderCapabilities &
338 return true;
339
340 mPipe->moveToThread( QThread::currentThread() );
341
342 QElapsedTimer time;
343 time.start();
344
345 std::unique_ptr< QgsScopedRuntimeProfile > preparingProfile;
346 if ( mEnableProfile )
347 {
348 preparingProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( "Preparing render" ), QStringLiteral( "rendering" ) );
349 }
350
351 //
352 //
353 // The goal here is to make as many decisions as possible early on (outside of the rendering loop)
354 // so that we can maximise performance of the rendering process. So now we check which drawing
355 // procedure to use :
356 //
357
358 const QgsScopedQPainterState painterSate( renderContext()->painter() );
359 if ( !mClippingRegions.empty() )
360 {
361 bool needsPainterClipPath = false;
362 const QPainterPath path = QgsMapClippingUtils::calculatePainterClipRegion( mClippingRegions, *renderContext(), Qgis::LayerType::Raster, needsPainterClipPath );
363 if ( needsPainterClipPath )
364 renderContext()->painter()->setClipPath( path, Qt::IntersectClip );
365 }
366
367 QgsRasterProjector *projector = mPipe->projector();
368 bool restoreOldResamplingStage = false;
369 const Qgis::RasterResamplingStage oldResamplingState = mPipe->resamplingStage();
370
371 // TODO add a method to interface to get provider and get provider
372 // params in QgsRasterProjector
373 if ( projector )
374 {
375 // Force provider resampling if reprojection is needed
376 if ( ( mPipe->provider()->providerCapabilities() & QgsRasterDataProvider::ProviderHintCanPerformProviderResampling ) &&
377 mRasterViewPort->mSrcCRS != mRasterViewPort->mDestCRS &&
378 oldResamplingState != Qgis::RasterResamplingStage::Provider )
379 {
380 restoreOldResamplingStage = true;
381 mPipe->setResamplingStage( Qgis::RasterResamplingStage::Provider );
382 }
383 projector->setCrs( mRasterViewPort->mSrcCRS, mRasterViewPort->mDestCRS, mRasterViewPort->mTransformContext );
384 }
385
386 // important -- disable SmoothPixmapTransform for raster layer renders. We want individual pixels to be clearly defined!
387 renderContext()->painter()->setRenderHint( QPainter::SmoothPixmapTransform, false );
388
389 preparingProfile.reset();
390 std::unique_ptr< QgsScopedRuntimeProfile > renderingProfile;
391 if ( mEnableProfile )
392 {
393 renderingProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( "Rendering" ), QStringLiteral( "rendering" ) );
394 }
395
396 // Drawer to pipe?
397 QgsRasterIterator iterator( mPipe->last() );
398 QgsRasterDrawer drawer( &iterator );
399 drawer.draw( *( renderContext() ), mRasterViewPort, mFeedback );
400
401 if ( mDrawElevationMap )
402 drawElevationMap();
403
404 if ( restoreOldResamplingStage )
405 {
406 mPipe->setResamplingStage( oldResamplingState );
407 }
408
409 const QStringList errors = mFeedback->errors();
410 for ( const QString &error : errors )
411 {
412 mErrors.append( error );
413 }
414
415 QgsDebugMsgLevel( QStringLiteral( "total raster draw time (ms): %1" ).arg( time.elapsed(), 5 ), 4 );
416 mReadyToCompose = true;
417
418 mPipe->moveToThread( nullptr );
419
420 return !mFeedback->isCanceled();
421}
422
424{
425 return mFeedback;
426}
427
429{
430 if ( !mRasterViewPort || !mPipe )
431 return false; // this layer is not going to get rendered
432
433 // preview of intermediate raster rendering results requires a temporary output image
435 return true;
436
437 if ( QgsRasterRenderer *renderer = mPipe->renderer() )
438 {
440 && renderContext()->testFlag( Qgis::RenderContextFlag::UseAdvancedEffects ) && ( !qgsDoubleNear( mLayerOpacity, 1.0 ) ) )
441 return true;
442 }
443
444 return false;
445}
446
447void QgsRasterLayerRenderer::drawElevationMap()
448{
449 QgsRasterDataProvider *dataProvider = mPipe->provider();
450 if ( renderContext()->elevationMap() && dataProvider )
451 {
452 double dpiScalefactor;
453
454 if ( renderContext()->dpiTarget() >= 0.0 )
455 dpiScalefactor = renderContext()->dpiTarget() / ( renderContext()->scaleFactor() * 25.4 );
456 else
457 dpiScalefactor = 1.0;
458
459 int outputWidth = static_cast<int>( static_cast<double>( mRasterViewPort->mWidth ) / dpiScalefactor * renderContext()->devicePixelRatio() );
460 int outputHeight = static_cast<int>( static_cast<double>( mRasterViewPort->mHeight ) / dpiScalefactor * renderContext()->devicePixelRatio() );
461
462 QSize viewSize = renderContext()->deviceOutputSize();
463 int viewWidth = static_cast<int>( viewSize.width() / dpiScalefactor );
464 int viewHeight = static_cast<int>( viewSize.height() / dpiScalefactor );
465
466 bool canRenderElevation = false;
467 std::unique_ptr<QgsRasterBlock> elevationBlock;
468 if ( mRasterViewPort->mSrcCRS == mRasterViewPort->mDestCRS )
469 {
470 elevationBlock.reset(
471 dataProvider->block(
472 mElevationBand,
473 mRasterViewPort->mDrawnExtent,
474 outputWidth,
475 outputHeight,
476 mFeedback ) );
477 canRenderElevation = true;
478 }
479 else
480 {
481 // Destinaton CRS is different from the source CRS.
482 // Using the raster projector lead to have big artifacts when rendering the elevation map.
483 // To get a smoother elevation map, we use GDAL resampling with coordinates transform
484 QgsRectangle viewExtentInLayerCoordinate = renderContext()->extent();
485
486 // If view extent is infinite, we use the data provider extent
487 if ( viewExtentInLayerCoordinate.xMinimum() == std::numeric_limits<double>::lowest() &&
488 viewExtentInLayerCoordinate.yMinimum() == std::numeric_limits<double>::lowest() &&
489 viewExtentInLayerCoordinate.xMaximum() == std::numeric_limits<double>::max() &&
490 viewExtentInLayerCoordinate.yMaximum() == std::numeric_limits<double>::max() )
491 {
492 viewExtentInLayerCoordinate = dataProvider->extent();
493 }
494
495 double xLayerResol = viewExtentInLayerCoordinate.width() / static_cast<double>( viewWidth );
496 double yLayerResol = viewExtentInLayerCoordinate.height() / static_cast<double>( viewHeight );
497
498 double overSampling = 1;
499 if ( mPipe->resampleFilter() )
500 overSampling = mPipe->resampleFilter()->maxOversampling();
501
502 if ( dataProvider->capabilities() & QgsRasterDataProvider::Size )
503 {
504 // If the dataprovider has size capability, we calculate the requested resolution to provider
505 double providerXResol = dataProvider->extent().width() / dataProvider->xSize();
506 double providerYResol = dataProvider->extent().height() / dataProvider->ySize();
507 overSampling = ( xLayerResol / providerXResol + yLayerResol / providerYResol ) / 2;
508 }
509
510 GDALResampleAlg alg;
511 if ( overSampling > 1 )
513 else
515
516 Qgis::DataType dataType = dataProvider->dataType( mElevationBand );
517
518 if ( dataType != Qgis::DataType::UnknownDataType ) // resampling data by GDAL is not compatible with unknown data type
519 {
520 // we need extra pixels on border to avoid effect border with resampling (at least 2 pixels band for cubic alg)
521 int sourceWidth = viewWidth + 4;
522 int sourceHeight = viewHeight + 4;
523 viewExtentInLayerCoordinate = QgsRectangle(
524 viewExtentInLayerCoordinate.xMinimum() - xLayerResol * 2,
525 viewExtentInLayerCoordinate.yMinimum() - yLayerResol * 2,
526 viewExtentInLayerCoordinate.xMaximum() + xLayerResol * 2,
527 viewExtentInLayerCoordinate.yMaximum() + yLayerResol * 2 );
528
529 // Now we can do the resampling
530 std::unique_ptr<QgsRasterBlock> sourcedata( dataProvider->block( mElevationBand, viewExtentInLayerCoordinate, sourceWidth, sourceHeight, mFeedback ) );
531 gdal::dataset_unique_ptr gdalDsInput =
532 QgsGdalUtils::blockToSingleBandMemoryDataset( viewExtentInLayerCoordinate, sourcedata.get() );
533
534
535 elevationBlock.reset( new QgsRasterBlock( dataType,
536 outputWidth,
537 outputHeight ) );
538
539 elevationBlock->setNoDataValue( dataProvider->sourceNoDataValue( mElevationBand ) );
540
541 gdal::dataset_unique_ptr gdalDsOutput =
542 QgsGdalUtils::blockToSingleBandMemoryDataset( mRasterViewPort->mDrawnExtent, elevationBlock.get() );
543
544 // For coordinate transformation, we try to obtain a coordinate operation string from the transform context.
545 // Depending of the CRS, if we can't we use GDAL transformation directly from the source and destination CRS
546 QString coordinateOperation;
547 const QgsCoordinateTransformContext &transformContext = renderContext()->transformContext();
548 if ( transformContext.mustReverseCoordinateOperation( mRasterViewPort->mSrcCRS, mRasterViewPort->mDestCRS ) )
549 coordinateOperation = transformContext.calculateCoordinateOperation( mRasterViewPort->mDestCRS, mRasterViewPort->mSrcCRS );
550 else
551 coordinateOperation = transformContext.calculateCoordinateOperation( mRasterViewPort->mSrcCRS, mRasterViewPort->mDestCRS );
552
553 if ( coordinateOperation.isEmpty() )
554 canRenderElevation = QgsGdalUtils::resampleSingleBandRaster( gdalDsInput.get(), gdalDsOutput.get(), alg,
555 mRasterViewPort->mSrcCRS, mRasterViewPort->mDestCRS );
556 else
557 canRenderElevation = QgsGdalUtils::resampleSingleBandRaster( gdalDsInput.get(), gdalDsOutput.get(), alg,
558 coordinateOperation.toUtf8().constData() );
559 }
560 }
561
562 if ( canRenderElevation )
563 {
564 QPoint topLeft;
565 if ( renderContext()->mapToPixel().mapRotation() != 0 )
566 {
567 // Now rendering elevation on the elevation map, we need to take care of rotation:
568 // again a resampling but this time with a geotransform.
569 const QgsMapToPixel &mtp = renderContext()->mapToPixel();
571
572 int elevMapWidth = elevMap->rawElevationImage().width();
573 int elevMapHeight = elevMap->rawElevationImage().height();
574
575 int bottom = 0;
576 int top = elevMapHeight;
577 int left = elevMapWidth;
578 int right = 0;
579
580 QList<QgsPointXY> corners;
581 corners << QgsPointXY( mRasterViewPort->mDrawnExtent.xMinimum(), mRasterViewPort->mDrawnExtent.yMinimum() )
582 << QgsPointXY( mRasterViewPort->mDrawnExtent.xMaximum(), mRasterViewPort->mDrawnExtent.yMaximum() )
583 << QgsPointXY( mRasterViewPort->mDrawnExtent.xMinimum(), mRasterViewPort->mDrawnExtent.yMaximum() )
584 << QgsPointXY( mRasterViewPort->mDrawnExtent.xMaximum(), mRasterViewPort->mDrawnExtent.yMinimum() );
585
586 for ( const QgsPointXY &corner : std::as_const( corners ) )
587 {
588 const QgsPointXY dpt = mtp.transform( corner );
589 int x = static_cast<int>( std::round( dpt.x() ) );
590 int y = static_cast<int>( std::round( dpt.y() ) );
591
592 if ( x < left )
593 left = x;
594 if ( x > right )
595 right = x;
596 if ( y < top )
597 top = y;
598 if ( y > bottom )
599 bottom = y;
600 }
601
602 const QgsPointXY origin = mtp.toMapCoordinates( left, top );
603 double gridXSize = mtp.toMapCoordinates( right, top ).distance( origin );
604 double gridYSize = mtp.toMapCoordinates( left, bottom ).distance( origin );
605 double angleRad = renderContext()->mapToPixel().mapRotation() / 180 * M_PI;
606
607 gdal::dataset_unique_ptr gdalDsInput =
608 QgsGdalUtils::blockToSingleBandMemoryDataset( mRasterViewPort->mDrawnExtent, elevationBlock.get() );
609
610 std::unique_ptr<QgsRasterBlock> rotatedElevationBlock =
611 std::make_unique<QgsRasterBlock>( elevationBlock->dataType(),
612 ( right - left ) * renderContext()->devicePixelRatio() + 1,
613 ( bottom - top ) * renderContext()->devicePixelRatio() + 1 );
614
615 rotatedElevationBlock->setNoDataValue( elevationBlock->noDataValue() );
616
617 gdal::dataset_unique_ptr gdalDsOutput =
618 QgsGdalUtils::blockToSingleBandMemoryDataset( angleRad, origin, gridXSize, gridYSize, rotatedElevationBlock.get() );
619
621 gdalDsInput.get(),
622 gdalDsOutput.get(),
624 {
625 elevationBlock.reset( rotatedElevationBlock.release() );
626 }
627
628 topLeft = QPoint( left, top );
629 }
630 else
631 {
632 topLeft = mRasterViewPort->mTopLeftPoint.toQPointF().toPoint();
633 }
634
636 elevationBlock.get(),
637 topLeft.y() * renderContext()->devicePixelRatio(),
638 topLeft.x() * renderContext()->devicePixelRatio(),
639 mElevationScale,
640 mElevationOffset );
641 }
642 }
643}
The Qgis class provides global constants for use throughout the application.
Definition qgis.h:54
RasterResamplingStage
Stage at which raster resampling occurs.
Definition qgis.h:1062
@ Provider
Resampling occurs in Provider.
@ InternalLayerOpacityHandling
The renderer internally handles the raster layer's opacity, so the default layer level opacity handli...
@ RedrawLayerOnly
Redraw the layer when temporal range changes, but don't apply any filtering. Useful when raster symbo...
@ TemporalRangeFromDataProvider
Mode when raster layer delegates temporal range handling to the dataprovider.
@ FixedTemporalRange
Mode when temporal properties have fixed start and end datetimes.
DataType
Raster data types.
Definition qgis.h:269
@ UnknownDataType
Unknown or unspecified type.
@ Raster
Raster layer.
@ 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.
static QgsRuntimeProfiler * profiler()
Returns the application runtime profiler.
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.
QgsRectangle transformBoundingBox(const QgsRectangle &rectangle, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool handle180Crossover=false) const
Transforms a rectangle from the source CRS to the destination CRS.
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...
Custom exception class for Coordinate Reference System related exceptions.
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
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...
QString layerId() const
Gets access to the ID of the layer rendered by this class.
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:80
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.
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.
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
double distance(double x, double y) const
Returns the distance between this point and a specified x, y coordinate.
Definition qgspointxy.h:211
void setY(double y)
Sets the y value of the point.
Definition qgspointxy.h:132
double y
Definition qgspointxy.h:63
double x
Definition qgspointxy.h:62
void setX(double x)
Sets the x value of the point.
Definition qgspointxy.h:122
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,...
@ Prefetch
Allow prefetching of out-of-view images.
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.
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.
QString toString(int precision=16) const
Returns a string representation of form xmin,ymin : xmax,ymax Coordinates will be truncated to the sp...
double xMinimum() const
Returns the x minimum value (left side of rectangle).
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
double width() const
Returns the width of the rectangle.
double xMaximum() const
Returns the x maximum value (right side of rectangle).
double yMaximum() const
Returns the y maximum value (top side of rectangle).
void set(const QgsPointXY &p1, const QgsPointXY &p2, bool normalize=true)
Sets the rectangle from two QgsPoints.
bool isEmpty() const
Returns true if the rectangle has no area.
double height() const
Returns the height of the rectangle.
void setNull()
Mark a rectangle as being null (holding no spatial information).
QgsRectangle intersect(const QgsRectangle &rect) const
Returns the intersection with the given rectangle.
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.
float devicePixelRatio() const
Returns the device pixel ratio.
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.
void record(const QString &name, double time, const QString &group="startup", const QString &id=QString())
Manually adds a profile event with the given name and total time (in seconds).
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.
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:4887
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:4332
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:39
QgsTemporalRange< QDateTime > QgsDateTimeRange
QgsRange which stores a range of date times.
Definition qgsrange.h:694
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.