QGIS API Documentation  3.13.0-Master (b73bd58cfb)
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 
16 #include "qgsrasterlayerrenderer.h"
17 
18 #include "qgsmessagelog.h"
19 #include "qgsrasterdataprovider.h"
20 #include "qgsrasterdrawer.h"
21 #include "qgsrasteriterator.h"
22 #include "qgsrasterlayer.h"
23 #include "qgsrasterprojector.h"
24 #include "qgsrendercontext.h"
25 #include "qgsproject.h"
26 #include "qgsexception.h"
27 
28 
30 
31 QgsRasterLayerRendererFeedback::QgsRasterLayerRendererFeedback( QgsRasterLayerRenderer *r )
32  : mR( r )
33  , mMinimalPreviewInterval( 250 )
34 {
35  setRenderPartialOutput( r->renderContext()->testFlag( QgsRenderContext::RenderPartialOutput ) );
36 }
37 
38 void QgsRasterLayerRendererFeedback::onNewData()
39 {
40  if ( !renderPartialOutput() )
41  return; // we were not asked for partial renders and we may not have a temporary image for overwriting...
42 
43  // update only once upon a time
44  // (preview itself takes some time)
45  if ( mLastPreview.isValid() && mLastPreview.msecsTo( QTime::currentTime() ) < mMinimalPreviewInterval )
46  return;
47 
48  // TODO: update only the area that got new data
49 
50  QgsDebugMsgLevel( QStringLiteral( "new raster preview! %1" ).arg( mLastPreview.msecsTo( QTime::currentTime() ) ), 3 );
51  QElapsedTimer t;
52  t.start();
53  QgsRasterBlockFeedback feedback;
54  feedback.setPreviewOnly( true );
55  feedback.setRenderPartialOutput( true );
56  QgsRasterIterator iterator( mR->mPipe->last() );
57  QgsRasterDrawer drawer( &iterator );
58  drawer.draw( mR->renderContext()->painter(), mR->mRasterViewPort, &mR->renderContext()->mapToPixel(), &feedback );
59  QgsDebugMsgLevel( QStringLiteral( "total raster preview time: %1 ms" ).arg( t.elapsed() ), 3 );
60  mLastPreview = QTime::currentTime();
61 }
62 
66  : QgsMapLayerRenderer( layer->id(), &rendererContext )
67  , mFeedback( new QgsRasterLayerRendererFeedback( this ) )
68 {
69  QgsMapToPixel mapToPixel = rendererContext.mapToPixel();
70  if ( rendererContext.mapToPixel().mapRotation() )
71  {
72  // unset rotation for the sake of local computations.
73  // Rotation will be handled by QPainter later
74  // TODO: provide a method of QgsMapToPixel to fetch map center
75  // in geographical units
76  QgsPointXY center = mapToPixel.toMapCoordinates(
77  static_cast<int>( mapToPixel.mapWidth() / 2.0 ),
78  static_cast<int>( mapToPixel.mapHeight() / 2.0 )
79  );
80  mapToPixel.setMapRotation( 0, center.x(), center.y() );
81  }
82 
83  QgsRectangle myProjectedViewExtent;
84  QgsRectangle myProjectedLayerExtent;
85 
86  if ( rendererContext.coordinateTransform().isValid() )
87  {
88  QgsDebugMsgLevel( QStringLiteral( "coordinateTransform set -> project extents." ), 4 );
89  if ( rendererContext.extent().xMinimum() == std::numeric_limits<double>::lowest() &&
90  rendererContext.extent().yMinimum() == std::numeric_limits<double>::lowest() &&
91  rendererContext.extent().xMaximum() == std::numeric_limits<double>::max() &&
92  rendererContext.extent().yMaximum() == std::numeric_limits<double>::max() )
93  {
94  // We get in this situation if the view CRS is geographical and the
95  // extent goes beyond -180,-90,180,90. To avoid reprojection issues to the
96  // layer CRS, then this dummy extent is returned by QgsMapRendererJob::reprojectToLayerExtent()
97  // Don't try to reproject it now to view extent as this would return
98  // a null rectangle.
99  myProjectedViewExtent = rendererContext.extent();
100  }
101  else
102  {
103  try
104  {
105  QgsCoordinateTransform ct = rendererContext.coordinateTransform();
107  myProjectedViewExtent = ct.transformBoundingBox( rendererContext.extent() );
108  }
109  catch ( QgsCsException &cs )
110  {
111  QgsMessageLog::logMessage( QObject::tr( "Could not reproject view extent: %1" ).arg( cs.what() ), QObject::tr( "Raster" ) );
112  myProjectedViewExtent.setMinimal();
113  }
114  }
115 
116  try
117  {
118  QgsCoordinateTransform ct = rendererContext.coordinateTransform();
120  myProjectedLayerExtent = ct.transformBoundingBox( layer->extent() );
121  }
122  catch ( QgsCsException &cs )
123  {
124  QgsMessageLog::logMessage( QObject::tr( "Could not reproject layer extent: %1" ).arg( cs.what() ), QObject::tr( "Raster" ) );
125  myProjectedLayerExtent.setMinimal();
126  }
127  }
128  else
129  {
130  QgsDebugMsgLevel( QStringLiteral( "coordinateTransform not set" ), 4 );
131  myProjectedViewExtent = rendererContext.extent();
132  myProjectedLayerExtent = layer->extent();
133  }
134 
135  // clip raster extent to view extent
136  QgsRectangle myRasterExtent = layer->ignoreExtents() ? myProjectedViewExtent : myProjectedViewExtent.intersect( myProjectedLayerExtent );
137  if ( myRasterExtent.isEmpty() )
138  {
139  QgsDebugMsgLevel( QStringLiteral( "draw request outside view extent." ), 2 );
140  // nothing to do
141  return;
142  }
143 
144  QgsDebugMsgLevel( "theViewExtent is " + rendererContext.extent().toString(), 4 );
145  QgsDebugMsgLevel( "myProjectedViewExtent is " + myProjectedViewExtent.toString(), 4 );
146  QgsDebugMsgLevel( "myProjectedLayerExtent is " + myProjectedLayerExtent.toString(), 4 );
147  QgsDebugMsgLevel( "myRasterExtent is " + myRasterExtent.toString(), 4 );
148 
149  //
150  // The first thing we do is set up the QgsRasterViewPort. This struct stores all the settings
151  // relating to the size (in pixels and coordinate system units) of the raster part that is
152  // in view in the map window. It also stores the origin.
153  //
154  //this is not a class level member because every time the user pans or zooms
155  //the contents of the rasterViewPort will change
156  mRasterViewPort = new QgsRasterViewPort();
157 
158  mRasterViewPort->mDrawnExtent = myRasterExtent;
159  if ( rendererContext.coordinateTransform().isValid() )
160  {
161  mRasterViewPort->mSrcCRS = layer->crs();
162  mRasterViewPort->mDestCRS = rendererContext.coordinateTransform().destinationCrs();
163  mRasterViewPort->mTransformContext = rendererContext.transformContext();
164  }
165  else
166  {
167  mRasterViewPort->mSrcCRS = QgsCoordinateReferenceSystem(); // will be invalid
168  mRasterViewPort->mDestCRS = QgsCoordinateReferenceSystem(); // will be invalid
169  }
170 
171  // get dimensions of clipped raster image in device coordinate space (this is the size of the viewport)
172  mRasterViewPort->mTopLeftPoint = mapToPixel.transform( myRasterExtent.xMinimum(), myRasterExtent.yMaximum() );
173  mRasterViewPort->mBottomRightPoint = mapToPixel.transform( myRasterExtent.xMaximum(), myRasterExtent.yMinimum() );
174 
175  // align to output device grid, i.e. std::floor/ceil to integers
176  // TODO: this should only be done if paint device is raster - screen, image
177  // for other devices (pdf) it can have floating point origin
178  // we could use floating point for raster devices as well, but respecting the
179  // output device grid should make it more effective as the resampling is done in
180  // the provider anyway
181  mRasterViewPort->mTopLeftPoint.setX( std::floor( mRasterViewPort->mTopLeftPoint.x() ) );
182  mRasterViewPort->mTopLeftPoint.setY( std::floor( mRasterViewPort->mTopLeftPoint.y() ) );
183  mRasterViewPort->mBottomRightPoint.setX( std::ceil( mRasterViewPort->mBottomRightPoint.x() ) );
184  mRasterViewPort->mBottomRightPoint.setY( std::ceil( mRasterViewPort->mBottomRightPoint.y() ) );
185  // recalc myRasterExtent to aligned values
186  myRasterExtent.set(
187  mapToPixel.toMapCoordinates( mRasterViewPort->mTopLeftPoint.x(),
188  mRasterViewPort->mBottomRightPoint.y() ),
189  mapToPixel.toMapCoordinates( mRasterViewPort->mBottomRightPoint.x(),
190  mRasterViewPort->mTopLeftPoint.y() )
191  );
192 
193  //raster viewport top left / bottom right are already rounded to int
194  mRasterViewPort->mWidth = static_cast<qgssize>( std::abs( mRasterViewPort->mBottomRightPoint.x() - mRasterViewPort->mTopLeftPoint.x() ) );
195  mRasterViewPort->mHeight = static_cast<qgssize>( std::abs( mRasterViewPort->mBottomRightPoint.y() - mRasterViewPort->mTopLeftPoint.y() ) );
196 
197  //the drawable area can start to get very very large when you get down displaying 2x2 or smaller, this is because
198  //mapToPixel.mapUnitsPerPixel() is less then 1,
199  //so we will just get the pixel data and then render these special cases differently in paintImageToCanvas()
200 
201  QgsDebugMsgLevel( QStringLiteral( "mapUnitsPerPixel = %1" ).arg( mapToPixel.mapUnitsPerPixel() ), 3 );
202  QgsDebugMsgLevel( QStringLiteral( "mWidth = %1" ).arg( layer->width() ), 3 );
203  QgsDebugMsgLevel( QStringLiteral( "mHeight = %1" ).arg( layer->height() ), 3 );
204  QgsDebugMsgLevel( QStringLiteral( "myRasterExtent.xMinimum() = %1" ).arg( myRasterExtent.xMinimum() ), 3 );
205  QgsDebugMsgLevel( QStringLiteral( "myRasterExtent.xMaximum() = %1" ).arg( myRasterExtent.xMaximum() ), 3 );
206  QgsDebugMsgLevel( QStringLiteral( "myRasterExtent.yMinimum() = %1" ).arg( myRasterExtent.yMinimum() ), 3 );
207  QgsDebugMsgLevel( QStringLiteral( "myRasterExtent.yMaximum() = %1" ).arg( myRasterExtent.yMaximum() ), 3 );
208 
209  QgsDebugMsgLevel( QStringLiteral( "mTopLeftPoint.x() = %1" ).arg( mRasterViewPort->mTopLeftPoint.x() ), 3 );
210  QgsDebugMsgLevel( QStringLiteral( "mBottomRightPoint.x() = %1" ).arg( mRasterViewPort->mBottomRightPoint.x() ), 3 );
211  QgsDebugMsgLevel( QStringLiteral( "mTopLeftPoint.y() = %1" ).arg( mRasterViewPort->mTopLeftPoint.y() ), 3 );
212  QgsDebugMsgLevel( QStringLiteral( "mBottomRightPoint.y() = %1" ).arg( mRasterViewPort->mBottomRightPoint.y() ), 3 );
213 
214  QgsDebugMsgLevel( QStringLiteral( "mWidth = %1" ).arg( mRasterViewPort->mWidth ), 3 );
215  QgsDebugMsgLevel( QStringLiteral( "mHeight = %1" ).arg( mRasterViewPort->mHeight ), 3 );
216 
217  // /\/\/\ - added to handle zoomed-in rasters
218 
219  // TODO R->mLastViewPort = *mRasterViewPort;
220 
221  // TODO: is it necessary? Probably WMS only?
222  layer->dataProvider()->setDpi( 25.4 * rendererContext.scaleFactor() );
223 
224 
225  // copy the whole raster pipe!
226  mPipe = new QgsRasterPipe( *layer->pipe() );
227  QgsRasterRenderer *rasterRenderer = mPipe->renderer();
228  if ( rasterRenderer && !( rendererContext.flags() & QgsRenderContext::RenderPreviewJob ) )
229  layer->refreshRendererIfNeeded( rasterRenderer, rendererContext.extent() );
230 
231  if ( layer->temporalProperties()->isActive() && renderContext()->isTemporal() )
232  {
233  switch ( layer->temporalProperties()->mode() )
234  {
236  break;
237 
239  // in this mode we need to pass on the desired render temporal range to the data provider
240  if ( mPipe->provider()->temporalCapabilities() )
241  {
242  mPipe->provider()->temporalCapabilities()->setRequestedTemporalRange( rendererContext.temporalRange() );
244  }
245  break;
246  }
247  }
248  else if ( mPipe->provider()->temporalCapabilities() )
249  {
250  mPipe->provider()->temporalCapabilities()->setRequestedTemporalRange( QgsDateTimeRange() );
252  }
253 }
254 
256 {
257  delete mFeedback;
258 
259  delete mRasterViewPort;
260  delete mPipe;
261 }
262 
264 {
265  if ( !mRasterViewPort )
266  return true; // outside of layer extent - nothing to do
267 
268  //R->draw( mPainter, mRasterViewPort, &mMapToPixel );
269 
270  QElapsedTimer time;
271  time.start();
272  //
273  //
274  // The goal here is to make as many decisions as possible early on (outside of the rendering loop)
275  // so that we can maximise performance of the rendering process. So now we check which drawing
276  // procedure to use :
277  //
278 
279  QgsRasterProjector *projector = mPipe->projector();
280 
281  // TODO add a method to interface to get provider and get provider
282  // params in QgsRasterProjector
283  if ( projector )
284  {
285  projector->setCrs( mRasterViewPort->mSrcCRS, mRasterViewPort->mDestCRS, mRasterViewPort->mTransformContext );
286  }
287 
288  // Drawer to pipe?
289  QgsRasterIterator iterator( mPipe->last() );
290  QgsRasterDrawer drawer( &iterator );
291  drawer.draw( renderContext()->painter(), mRasterViewPort, &renderContext()->mapToPixel(), mFeedback );
292 
293  const QStringList errors = mFeedback->errors();
294  for ( const QString &error : errors )
295  {
296  mErrors.append( error );
297  }
298 
299  QgsDebugMsgLevel( QStringLiteral( "total raster draw time (ms): %1" ).arg( time.elapsed(), 5 ), 4 );
300 
301  return true;
302 }
303 
305 {
306  return mFeedback;
307 }
308 
int width() const
Returns the width of the (unclipped) raster.
QgsFeedback * feedback() const override
Access to feedback object of the layer renderer (may be nullptr)
A rectangle specified with double values.
Definition: qgsrectangle.h:41
void setMapRotation(double degrees, double cx, double cy)
Set map rotation in degrees (clockwise)
void setMinimal()
Set a rectangle so that min corner is at max and max corner is at min.
Definition: qgsrectangle.h:151
Base class for processing modules.
Definition: qgsrasterpipe.h:46
Iterator for sequentially processing raster cells.
QgsCoordinateTransformContext transformContext() const
Returns the context&#39;s coordinate transform context, which stores various information regarding which ...
bool isActive() const
Returns true if the temporal property is active.
QgsRasterRenderer * renderer() const
Represents a raster layer.
double y
Definition: qgspointxy.h:48
void setIntervalHandlingMethod(IntervalHandlingMethod method)
Sets the desired method to use when resolving a temporal interval to matching layers or bands in the ...
QgsRasterPipe * pipe()
Returns the raster pipe.
A class to represent a 2D point.
Definition: qgspointxy.h:43
QgsRasterProjector * projector() const
QgsRasterInterface * last() const
QgsRasterDataProviderTemporalCapabilities * temporalCapabilities() override
Returns the provider&#39;s temporal capabilities.
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system, which the transform will transform coordinates t...
void setRenderPartialOutput(bool enable)
Set whether our painter is drawing to a temporary image used just by this layer.
Flags flags() const
Returns combination of flags used for rendering.
int height() const
Returns the height of the (unclipped) raster.
Implementation of threaded rendering for raster layers.
bool render() override
Do the rendering (based on data stored in the class)
bool isValid() const
Returns true if the coordinate transform is valid, ie both the source and destination CRS have been s...
virtual QgsRectangle extent() const
Returns the extent of the layer.
double mapRotation() const
Returns current map rotation in degrees.
QgsCoordinateReferenceSystem mDestCRS
Target coordinate system.
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
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:37
The drawing pipe for raster layers.
QgsPointXY transform(const QgsPointXY &p) const
Transform the point from map (world) coordinates to device coordinates.
QgsRasterDataProvider * dataProvider() override
Returns the source data provider.
int mapWidth() const
Returns current map width in pixels The information is only known if setRotation was used...
QString id() const
Returns the layer&#39;s unique ID, which is used to access this layer from QgsProject.
int mapHeight() const
Returns current map height in pixels.
Mode when temporal properties have fixed start and end datetimes.
Mode when raster layer delegates temporal range handling to the dataprovider.
void setPreviewOnly(bool preview)
set flag whether the block request is for preview purposes only
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
bool isEmpty() const
Returns true if the rectangle is empty.
Definition: qgsrectangle.h:437
const QgsRectangle & extent() const
When rendering a map layer, calling this method returns the "clipping" extent for the layer (in the l...
QgsRasterLayerRenderer(QgsRasterLayer *layer, QgsRenderContext &rendererContext)
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
QgsRectangle intersect(const QgsRectangle &rect) const
Returns the intersection with the given rectangle.
Definition: qgsrectangle.h:312
QString toString(int precision=16) const
Returns a string representation of form xmin,ymin : xmax,ymax Coordinates will be truncated to the sp...
QgsCoordinateTransform coordinateTransform() const
Returns the current coordinate transform for the context.
double mapUnitsPerPixel() const
Returns current map units per pixel.
void setX(double x)
Sets the x value of the point.
Definition: qgspointxy.h:107
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:703
double x
Definition: qgspointxy.h:47
TemporalMode mode() const
Returns the temporal properties mode.
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:177
QgsCoordinateReferenceSystem mSrcCRS
Source coordinate system.
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:162
QgsRasterProjector implements approximate projection support for it calculates grid of points in sour...
const QgsDateTimeRange & temporalRange() const
Returns the datetime range for the object.
Render is a &#39;canvas preview&#39; render, and shortcuts should be taken to ensure fast rendering...
bool isTemporal() const
Returns true if the object&#39;s temporal range is enabled, and the object will be filtered when renderin...
Contains information about the context of a rendering operation.
const QgsMapToPixel & mapToPixel() const
Returns the context&#39;s map to pixel transform, which transforms between map coordinates and device coo...
QgsCoordinateTransformContext mTransformContext
Coordinate transform context.
Whether to make extra effort to update map image with partially rendered layers (better for interacti...
QgsRasterLayerTemporalProperties * temporalProperties() override
Returns temporal properties associated with the raster layer.
This class represents a coordinate reference system (CRS).
QgsRasterDataProviderTemporalCapabilities::IntervalHandlingMethod intervalHandlingMethod() const
Returns the desired method to use when resolving a temporal interval to matching layers or bands in t...
void setBallparkTransformsAreAppropriate(bool appropriate)
Sets whether approximate "ballpark" results are appropriate for this coordinate transform.
bool ignoreExtents() const
If the ignoreExtent flag is set, the layer will also render outside the bounding box reported by the ...
Class for doing transforms between two map coordinate systems.
QgsRasterDataProvider * provider() const
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:167
Base class for utility classes that encapsulate information necessary for rendering of map layers...
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:172
QStringList errors() const
Returns list of errors (problems) that happened during the rendering.
void refreshRendererIfNeeded(QgsRasterRenderer *rasterRenderer, const QgsRectangle &extent)
Refresh renderer with new extent, if needed.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:65
This class provides details of the viewable area that a raster will be rendered into.
bool testFlag(Flag flag) const
Check whether a particular flag is enabled.
QgsRenderContext * renderContext()
Returns the render context associated with the renderer.
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
Feedback object tailored for raster block reading.
QgsPointXY toMapCoordinates(int x, int y) const
Transform device coordinates to map (world) coordinates.
Raster renderer pipe that applies colors to a raster.
void setDpi(int dpi)
Sets the output device resolution.
void set(const QgsPointXY &p1, const QgsPointXY &p2)
Sets the rectangle from two QgsPoints.
Definition: qgsrectangle.h:105
QgsRectangle transformBoundingBox(const QgsRectangle &rectangle, TransformDirection direction=ForwardTransform, bool handle180Crossover=false) const SIP_THROW(QgsCsException)
Transforms a rectangle from the source CRS to the destination CRS.
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:88
Q_DECL_DEPRECATED void setCrs(const QgsCoordinateReferenceSystem &srcCRS, const QgsCoordinateReferenceSystem &destCRS, int srcDatumTransform=-1, int destDatumTransform=-1)
Sets the source and destination CRS.