QGIS API Documentation  3.4.15-Madeira (e83d02e274)
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->mContext.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  QgsDebugMsg( QStringLiteral( "new raster preview! %1" ).arg( mLastPreview.msecsTo( QTime::currentTime() ) ) );
51  QTime 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->mPainter, mR->mRasterViewPort, mR->mMapToPixel, &feedback );
59  QgsDebugMsg( QStringLiteral( "total raster preview time: %1 ms" ).arg( t.elapsed() ) );
60  mLastPreview = QTime::currentTime();
61 }
62 
66  : QgsMapLayerRenderer( layer->id() )
67  , mContext( rendererContext )
68  , mFeedback( new QgsRasterLayerRendererFeedback( this ) )
69 {
70  mPainter = rendererContext.painter();
71  const QgsMapToPixel &qgsMapToPixel = rendererContext.mapToPixel();
72  mMapToPixel = &qgsMapToPixel;
73 
74  QgsMapToPixel mapToPixel = qgsMapToPixel;
75  if ( mapToPixel.mapRotation() )
76  {
77  // unset rotation for the sake of local computations.
78  // Rotation will be handled by QPainter later
79  // TODO: provide a method of QgsMapToPixel to fetch map center
80  // in geographical units
81  QgsPointXY center = mapToPixel.toMapCoordinates(
82  static_cast<int>( mapToPixel.mapWidth() / 2.0 ),
83  static_cast<int>( mapToPixel.mapHeight() / 2.0 )
84  );
85  mapToPixel.setMapRotation( 0, center.x(), center.y() );
86  }
87 
88  QgsRectangle myProjectedViewExtent;
89  QgsRectangle myProjectedLayerExtent;
90 
91  if ( rendererContext.coordinateTransform().isValid() )
92  {
93  QgsDebugMsgLevel( QStringLiteral( "coordinateTransform set -> project extents." ), 4 );
94  if ( rendererContext.extent().xMinimum() == std::numeric_limits<double>::lowest() &&
95  rendererContext.extent().yMinimum() == std::numeric_limits<double>::lowest() &&
96  rendererContext.extent().xMaximum() == std::numeric_limits<double>::max() &&
97  rendererContext.extent().yMaximum() == std::numeric_limits<double>::max() )
98  {
99  // We get in this situation if the view CRS is geographical and the
100  // extent goes beyond -180,-90,180,90. To avoid reprojection issues to the
101  // layer CRS, then this dummy extent is returned by QgsMapRendererJob::reprojectToLayerExtent()
102  // Don't try to reproject it now to view extent as this would return
103  // a null rectangle.
104  myProjectedViewExtent = rendererContext.extent();
105  }
106  else
107  {
108  try
109  {
110  myProjectedViewExtent = rendererContext.coordinateTransform().transformBoundingBox( rendererContext.extent() );
111  }
112  catch ( QgsCsException &cs )
113  {
114  QgsMessageLog::logMessage( QObject::tr( "Could not reproject view extent: %1" ).arg( cs.what() ), QObject::tr( "Raster" ) );
115  myProjectedViewExtent.setMinimal();
116  }
117  }
118 
119  try
120  {
121  myProjectedLayerExtent = rendererContext.coordinateTransform().transformBoundingBox( layer->extent() );
122  }
123  catch ( QgsCsException &cs )
124  {
125  QgsMessageLog::logMessage( QObject::tr( "Could not reproject layer extent: %1" ).arg( cs.what() ), QObject::tr( "Raster" ) );
126  myProjectedLayerExtent.setMinimal();
127  }
128  }
129  else
130  {
131  QgsDebugMsgLevel( QStringLiteral( "coordinateTransform not set" ), 4 );
132  myProjectedViewExtent = rendererContext.extent();
133  myProjectedLayerExtent = layer->extent();
134  }
135 
136  // clip raster extent to view extent
137  QgsRectangle myRasterExtent = myProjectedViewExtent.intersect( myProjectedLayerExtent );
138  if ( myRasterExtent.isEmpty() )
139  {
140  QgsDebugMsg( QStringLiteral( "draw request outside view extent." ) );
141  // nothing to do
142  return;
143  }
144 
145  QgsDebugMsgLevel( "theViewExtent is " + rendererContext.extent().toString(), 4 );
146  QgsDebugMsgLevel( "myProjectedViewExtent is " + myProjectedViewExtent.toString(), 4 );
147  QgsDebugMsgLevel( "myProjectedLayerExtent is " + myProjectedLayerExtent.toString(), 4 );
148  QgsDebugMsgLevel( "myRasterExtent is " + myRasterExtent.toString(), 4 );
149 
150  //
151  // The first thing we do is set up the QgsRasterViewPort. This struct stores all the settings
152  // relating to the size (in pixels and coordinate system units) of the raster part that is
153  // in view in the map window. It also stores the origin.
154  //
155  //this is not a class level member because every time the user pans or zooms
156  //the contents of the rasterViewPort will change
157  mRasterViewPort = new QgsRasterViewPort();
158 
159  mRasterViewPort->mDrawnExtent = myRasterExtent;
160  if ( rendererContext.coordinateTransform().isValid() )
161  {
162  mRasterViewPort->mSrcCRS = layer->crs();
163  mRasterViewPort->mDestCRS = rendererContext.coordinateTransform().destinationCrs();
164  mRasterViewPort->mSrcDatumTransform = rendererContext.coordinateTransform().sourceDatumTransformId();
165  mRasterViewPort->mDestDatumTransform = rendererContext.coordinateTransform().destinationDatumTransformId();
166  }
167  else
168  {
169  mRasterViewPort->mSrcCRS = QgsCoordinateReferenceSystem(); // will be invalid
170  mRasterViewPort->mDestCRS = QgsCoordinateReferenceSystem(); // will be invalid
171  mRasterViewPort->mSrcDatumTransform = -1;
172  mRasterViewPort->mDestDatumTransform = -1;
173  }
174 
175  // get dimensions of clipped raster image in device coordinate space (this is the size of the viewport)
176  mRasterViewPort->mTopLeftPoint = mapToPixel.transform( myRasterExtent.xMinimum(), myRasterExtent.yMaximum() );
177  mRasterViewPort->mBottomRightPoint = mapToPixel.transform( myRasterExtent.xMaximum(), myRasterExtent.yMinimum() );
178 
179  // align to output device grid, i.e. std::floor/ceil to integers
180  // TODO: this should only be done if paint device is raster - screen, image
181  // for other devices (pdf) it can have floating point origin
182  // we could use floating point for raster devices as well, but respecting the
183  // output device grid should make it more effective as the resampling is done in
184  // the provider anyway
185  mRasterViewPort->mTopLeftPoint.setX( std::floor( mRasterViewPort->mTopLeftPoint.x() ) );
186  mRasterViewPort->mTopLeftPoint.setY( std::floor( mRasterViewPort->mTopLeftPoint.y() ) );
187  mRasterViewPort->mBottomRightPoint.setX( std::ceil( mRasterViewPort->mBottomRightPoint.x() ) );
188  mRasterViewPort->mBottomRightPoint.setY( std::ceil( mRasterViewPort->mBottomRightPoint.y() ) );
189  // recalc myRasterExtent to aligned values
190  myRasterExtent.set(
191  mapToPixel.toMapCoordinates( mRasterViewPort->mTopLeftPoint.x(),
192  mRasterViewPort->mBottomRightPoint.y() ),
193  mapToPixel.toMapCoordinates( mRasterViewPort->mBottomRightPoint.x(),
194  mRasterViewPort->mTopLeftPoint.y() )
195  );
196 
197  //raster viewport top left / bottom right are already rounded to int
198  mRasterViewPort->mWidth = static_cast<int>( mRasterViewPort->mBottomRightPoint.x() - mRasterViewPort->mTopLeftPoint.x() );
199  mRasterViewPort->mHeight = static_cast<int>( mRasterViewPort->mBottomRightPoint.y() - mRasterViewPort->mTopLeftPoint.y() );
200 
201  //the drawable area can start to get very very large when you get down displaying 2x2 or smaller, this is because
202  //mapToPixel.mapUnitsPerPixel() is less then 1,
203  //so we will just get the pixel data and then render these special cases differently in paintImageToCanvas()
204 
205  QgsDebugMsgLevel( QStringLiteral( "mapUnitsPerPixel = %1" ).arg( mapToPixel.mapUnitsPerPixel() ), 3 );
206  QgsDebugMsgLevel( QStringLiteral( "mWidth = %1" ).arg( layer->width() ), 3 );
207  QgsDebugMsgLevel( QStringLiteral( "mHeight = %1" ).arg( layer->height() ), 3 );
208  QgsDebugMsgLevel( QStringLiteral( "myRasterExtent.xMinimum() = %1" ).arg( myRasterExtent.xMinimum() ), 3 );
209  QgsDebugMsgLevel( QStringLiteral( "myRasterExtent.xMaximum() = %1" ).arg( myRasterExtent.xMaximum() ), 3 );
210  QgsDebugMsgLevel( QStringLiteral( "myRasterExtent.yMinimum() = %1" ).arg( myRasterExtent.yMinimum() ), 3 );
211  QgsDebugMsgLevel( QStringLiteral( "myRasterExtent.yMaximum() = %1" ).arg( myRasterExtent.yMaximum() ), 3 );
212 
213  QgsDebugMsgLevel( QStringLiteral( "mTopLeftPoint.x() = %1" ).arg( mRasterViewPort->mTopLeftPoint.x() ), 3 );
214  QgsDebugMsgLevel( QStringLiteral( "mBottomRightPoint.x() = %1" ).arg( mRasterViewPort->mBottomRightPoint.x() ), 3 );
215  QgsDebugMsgLevel( QStringLiteral( "mTopLeftPoint.y() = %1" ).arg( mRasterViewPort->mTopLeftPoint.y() ), 3 );
216  QgsDebugMsgLevel( QStringLiteral( "mBottomRightPoint.y() = %1" ).arg( mRasterViewPort->mBottomRightPoint.y() ), 3 );
217 
218  QgsDebugMsgLevel( QStringLiteral( "mWidth = %1" ).arg( mRasterViewPort->mWidth ), 3 );
219  QgsDebugMsgLevel( QStringLiteral( "mHeight = %1" ).arg( mRasterViewPort->mHeight ), 3 );
220 
221  // /\/\/\ - added to handle zoomed-in rasters
222 
223  // TODO R->mLastViewPort = *mRasterViewPort;
224 
225  // TODO: is it necessary? Probably WMS only?
226  layer->dataProvider()->setDpi( 25.4 * rendererContext.scaleFactor() );
227 
228 
229  // copy the whole raster pipe!
230  mPipe = new QgsRasterPipe( *layer->pipe() );
231  QgsRasterRenderer *rasterRenderer = mPipe->renderer();
232  if ( rasterRenderer && !( rendererContext.flags() & QgsRenderContext::RenderPreviewJob ) )
233  layer->refreshRendererIfNeeded( rasterRenderer, rendererContext.extent() );
234 }
235 
237 {
238  delete mFeedback;
239 
240  delete mRasterViewPort;
241  delete mPipe;
242 }
243 
245 {
246  if ( !mRasterViewPort )
247  return true; // outside of layer extent - nothing to do
248 
249  //R->draw( mPainter, mRasterViewPort, &mMapToPixel );
250 
251  QTime time;
252  time.start();
253  //
254  //
255  // The goal here is to make as many decisions as possible early on (outside of the rendering loop)
256  // so that we can maximise performance of the rendering process. So now we check which drawing
257  // procedure to use :
258  //
259 
260  QgsRasterProjector *projector = mPipe->projector();
261 
262  // TODO add a method to interface to get provider and get provider
263  // params in QgsRasterProjector
264  if ( projector )
265  {
266  projector->setCrs( mRasterViewPort->mSrcCRS, mRasterViewPort->mDestCRS, mRasterViewPort->mSrcDatumTransform, mRasterViewPort->mDestDatumTransform );
267  }
268 
269  // Drawer to pipe?
270  QgsRasterIterator iterator( mPipe->last() );
271  QgsRasterDrawer drawer( &iterator );
272  drawer.draw( mPainter, mRasterViewPort, mMapToPixel, mFeedback );
273 
274  const QStringList errors = mFeedback->errors();
275  for ( const QString &error : errors )
276  {
277  mErrors.append( error );
278  }
279 
280  QgsDebugMsgLevel( QStringLiteral( "total raster draw time (ms): %1" ).arg( time.elapsed(), 5 ), 4 );
281 
282  return true;
283 }
284 
286 {
287  return mFeedback;
288 }
289 
int mapWidth() const
Returns current map width in pixels The information is only known if setRotation was used...
QgsFeedback * feedback() const override
Access to feedback object of the layer renderer (may be null)
A rectangle specified with double values.
Definition: qgsrectangle.h:40
bool isEmpty() const
Returns true if the rectangle is empty.
Definition: qgsrectangle.h:425
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:150
Base class for processing modules.
Definition: qgsrasterpipe.h:46
Iterator for sequentially processing raster cells.
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:171
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
This class provides qgis with the ability to render raster datasets onto the mapcanvas.
double y
Definition: qgspointxy.h:48
QgsRasterPipe * pipe()
Returns the raster pipe.
A class to represent a 2D point.
Definition: qgspointxy.h:43
QgsRasterInterface * last() const
void setRenderPartialOutput(bool enable)
Set whether our painter is drawing to a temporary image used just by this layer.
const QgsRectangle & extent() const
When rendering a map layer, calling this method returns the "clipping" extent for the layer (in the l...
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
Implementation of threaded rendering for raster layers.
double mapRotation() const
Returns current map rotation in degrees.
bool render() override
Do the rendering (based on data stored in the class)
QgsCoordinateReferenceSystem mDestCRS
Target coordinate system.
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system, which the transform will transform coordinates t...
void setCrs(const QgsCoordinateReferenceSystem &srcCRS, const QgsCoordinateReferenceSystem &destCRS, int srcDatumTransform=-1, int destDatumTransform=-1)
Sets the source and destination CRS.
Base class for feedback objects to be used for cancellation of something running in a worker thread...
Definition: qgsfeedback.h:44
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:36
The drawing pipe for raster layers.
QgsRasterDataProvider * dataProvider() override
Returns the layer&#39;s data provider.
int height() const
Returns the height of the (unclipped) raster.
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:176
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:161
void setPreviewOnly(bool preview)
set flag whether the block request is for preview purposes only
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
virtual QgsRectangle extent() const
Returns the extent of the layer.
QgsRasterLayerRenderer(QgsRasterLayer *layer, QgsRenderContext &rendererContext)
int sourceDatumTransformId() const
Returns the ID of the datum transform to use when projecting from the source CRS. ...
int destinationDatumTransformId() const
Returns the ID of the datum transform to use when projecting to the destination CRS.
QString toString(int precision=16) const
Returns a string representation of form xmin,ymin : xmax,ymax Coordinates will be truncated to the sp...
QStringList errors() const
Returns list of errors (problems) that happened during the rendering.
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).
QString id() const
Returns the layer&#39;s unique ID, which is used to access this layer from QgsProject.
double mapUnitsPerPixel() const
Returns current map units per pixel.
QgsRasterProjector * projector() const
QgsPointXY toMapCoordinates(int x, int y) const
Transform device coordinates to map (world) coordinates.
void setX(double x)
Sets the x value of the point.
Definition: qgspointxy.h:104
double x
Definition: qgspointxy.h:47
QgsRectangle intersect(const QgsRectangle &rect) const
Returns the intersection with the given rectangle.
Definition: qgsrectangle.h:311
QgsCoordinateReferenceSystem mSrcCRS
Source coordinate system.
QgsRasterProjector implements approximate projection support for it calculates grid of points in sour...
Render is a &#39;canvas preview&#39; render, and shortcuts should be taken to ensure fast rendering...
QString what() const
Definition: qgsexception.h:48
Contains information about the context of a rendering operation.
QPainter * painter()
Returns the destination QPainter for the render operation.
Whether to make extra effort to update map image with partially rendered layers (better for interacti...
This class represents a coordinate reference system (CRS).
Flags flags() const
Returns combination of flags used for rendering.
bool testFlag(Flag flag) const
Check whether a particular flag is enabled.
int mapHeight() const
Returns current map height in pixels.
const QgsMapToPixel & mapToPixel() const
Returns the context&#39;s map to pixel transform, which transforms between map coordinates and device coo...
Base class for utility classes that encapsulate information necessary for rendering of map layers...
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.
QgsCoordinateTransform coordinateTransform() const
Returns the current coordinate transform for the context.
QgsPointXY transform(const QgsPointXY &p) const
Transform the point from map (world) coordinates to device coordinates.
Feedback object tailored for raster block reading.
QgsRasterRenderer * renderer() const
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:166
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:104
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:70
bool isValid() const
Returns true if the coordinate transform is valid, ie both the source and destination CRS have been s...
int width() const
Returns the width of the (unclipped) raster.