QGIS API Documentation  2.18.21-Las Palmas (9fba24a)
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 "qgsrasterdrawer.h"
20 #include "qgsrasteriterator.h"
21 #include "qgsrasterlayer.h"
22 
23 
25  : QgsMapLayerRenderer( layer->id() )
26  , mRasterViewPort( nullptr )
27  , mPipe( nullptr )
28  , mContext( rendererContext )
29  , mFeedback( new Feedback( this ) )
30 {
31  mPainter = rendererContext.painter();
32  const QgsMapToPixel& theQgsMapToPixel = rendererContext.mapToPixel();
33  mMapToPixel = &theQgsMapToPixel;
34 
35  QgsMapToPixel mapToPixel = theQgsMapToPixel;
36  if ( mapToPixel.mapRotation() )
37  {
38  // unset rotation for the sake of local computations.
39  // Rotation will be handled by QPainter later
40  // TODO: provide a method of QgsMapToPixel to fetch map center
41  // in geographical units
42  QgsPoint center = mapToPixel.toMapCoordinates(
43  mapToPixel.mapWidth() / 2.0,
44  mapToPixel.mapHeight() / 2.0
45  );
46  mapToPixel.setMapRotation( 0, center.x(), center.y() );
47  }
48 
49  QgsRectangle myProjectedViewExtent;
50  QgsRectangle myProjectedLayerExtent;
51 
52  if ( rendererContext.coordinateTransform() )
53  {
54  QgsDebugMsgLevel( "coordinateTransform set -> project extents.", 4 );
55  if ( rendererContext.extent().xMinimum() == -DBL_MAX &&
56  rendererContext.extent().yMinimum() == -DBL_MAX &&
57  rendererContext.extent().xMaximum() == DBL_MAX &&
58  rendererContext.extent().yMaximum() == DBL_MAX )
59  {
60  // We get in this situation if the view CRS is geographical and the
61  // extent goes beyond -180,-90,180,90. To avoid reprojection issues to the
62  // layer CRS, then this dummy extent is returned by QgsMapRendererJob::reprojectToLayerExtent()
63  // Don't try to reproject it now to view extent as this would return
64  // a null rectangle.
65  myProjectedViewExtent = rendererContext.extent();
66  }
67  else
68  {
69  try
70  {
71  myProjectedViewExtent = rendererContext.coordinateTransform()->transformBoundingBox( rendererContext.extent() );
72  }
73  catch ( QgsCsException &cs )
74  {
75  QgsMessageLog::logMessage( QObject::tr( "Could not reproject view extent: %1" ).arg( cs.what() ), QObject::tr( "Raster" ) );
76  myProjectedViewExtent.setMinimal();
77  }
78  }
79 
80  try
81  {
82  myProjectedLayerExtent = rendererContext.coordinateTransform()->transformBoundingBox( layer->extent() );
83  }
84  catch ( QgsCsException &cs )
85  {
86  QgsMessageLog::logMessage( QObject::tr( "Could not reproject layer extent: %1" ).arg( cs.what() ), QObject::tr( "Raster" ) );
87  myProjectedLayerExtent.setMinimal();
88  }
89  }
90  else
91  {
92  QgsDebugMsgLevel( "coordinateTransform not set", 4 );
93  myProjectedViewExtent = rendererContext.extent();
94  myProjectedLayerExtent = layer->extent();
95  }
96 
97  // clip raster extent to view extent
98  QgsRectangle myRasterExtent = myProjectedViewExtent.intersect( &myProjectedLayerExtent );
99  if ( myRasterExtent.isEmpty() )
100  {
101  QgsDebugMsg( "draw request outside view extent." );
102  // nothing to do
103  return;
104  }
105 
106  QgsDebugMsgLevel( "theViewExtent is " + rendererContext.extent().toString(), 4 );
107  QgsDebugMsgLevel( "myProjectedViewExtent is " + myProjectedViewExtent.toString(), 4 );
108  QgsDebugMsgLevel( "myProjectedLayerExtent is " + myProjectedLayerExtent.toString(), 4 );
109  QgsDebugMsgLevel( "myRasterExtent is " + myRasterExtent.toString(), 4 );
110 
111  //
112  // The first thing we do is set up the QgsRasterViewPort. This struct stores all the settings
113  // relating to the size (in pixels and coordinate system units) of the raster part that is
114  // in view in the map window. It also stores the origin.
115  //
116  //this is not a class level member because every time the user pans or zooms
117  //the contents of the rasterViewPort will change
119 
120  mRasterViewPort->mDrawnExtent = myRasterExtent;
121  if ( rendererContext.coordinateTransform() )
122  {
123  mRasterViewPort->mSrcCRS = layer->crs();
124  mRasterViewPort->mDestCRS = rendererContext.coordinateTransform()->destCRS();
125  mRasterViewPort->mSrcDatumTransform = rendererContext.coordinateTransform()->sourceDatumTransform();
126  mRasterViewPort->mDestDatumTransform = rendererContext.coordinateTransform()->destinationDatumTransform();
127  }
128  else
129  {
130  mRasterViewPort->mSrcCRS = QgsCoordinateReferenceSystem(); // will be invalid
131  mRasterViewPort->mDestCRS = QgsCoordinateReferenceSystem(); // will be invalid
132  mRasterViewPort->mSrcDatumTransform = -1;
133  mRasterViewPort->mDestDatumTransform = -1;
134  }
135 
136  // get dimensions of clipped raster image in device coordinate space (this is the size of the viewport)
137  mRasterViewPort->mTopLeftPoint = mapToPixel.transform( myRasterExtent.xMinimum(), myRasterExtent.yMaximum() );
138  mRasterViewPort->mBottomRightPoint = mapToPixel.transform( myRasterExtent.xMaximum(), myRasterExtent.yMinimum() );
139 
140  // align to output device grid, i.e. floor/ceil to integers
141  // TODO: this should only be done if paint device is raster - screen, image
142  // for other devices (pdf) it can have floating point origin
143  // we could use floating point for raster devices as well, but respecting the
144  // output device grid should make it more effective as the resampling is done in
145  // the provider anyway
146  mRasterViewPort->mTopLeftPoint.setX( floor( mRasterViewPort->mTopLeftPoint.x() ) );
147  mRasterViewPort->mTopLeftPoint.setY( floor( mRasterViewPort->mTopLeftPoint.y() ) );
148  mRasterViewPort->mBottomRightPoint.setX( ceil( mRasterViewPort->mBottomRightPoint.x() ) );
149  mRasterViewPort->mBottomRightPoint.setY( ceil( mRasterViewPort->mBottomRightPoint.y() ) );
150  // recalc myRasterExtent to aligned values
151  myRasterExtent.set(
152  mapToPixel.toMapCoordinatesF( mRasterViewPort->mTopLeftPoint.x(),
153  mRasterViewPort->mBottomRightPoint.y() ),
154  mapToPixel.toMapCoordinatesF( mRasterViewPort->mBottomRightPoint.x(),
155  mRasterViewPort->mTopLeftPoint.y() )
156  );
157 
158  //raster viewport top left / bottom right are already rounded to int
159  mRasterViewPort->mWidth = static_cast<int>( mRasterViewPort->mBottomRightPoint.x() - mRasterViewPort->mTopLeftPoint.x() );
160  mRasterViewPort->mHeight = static_cast<int>( mRasterViewPort->mBottomRightPoint.y() - mRasterViewPort->mTopLeftPoint.y() );
161 
162  //the drawable area can start to get very very large when you get down displaying 2x2 or smaller, this is becasue
163  //mapToPixel.mapUnitsPerPixel() is less then 1,
164  //so we will just get the pixel data and then render these special cases differently in paintImageToCanvas()
165 
166  QgsDebugMsgLevel( QString( "mapUnitsPerPixel = %1" ).arg( mapToPixel.mapUnitsPerPixel() ), 3 );
167  QgsDebugMsgLevel( QString( "mWidth = %1" ).arg( layer->width() ), 3 );
168  QgsDebugMsgLevel( QString( "mHeight = %1" ).arg( layer->height() ), 3 );
169  QgsDebugMsgLevel( QString( "myRasterExtent.xMinimum() = %1" ).arg( myRasterExtent.xMinimum() ), 3 );
170  QgsDebugMsgLevel( QString( "myRasterExtent.xMaximum() = %1" ).arg( myRasterExtent.xMaximum() ), 3 );
171  QgsDebugMsgLevel( QString( "myRasterExtent.yMinimum() = %1" ).arg( myRasterExtent.yMinimum() ), 3 );
172  QgsDebugMsgLevel( QString( "myRasterExtent.yMaximum() = %1" ).arg( myRasterExtent.yMaximum() ), 3 );
173 
174  QgsDebugMsgLevel( QString( "mTopLeftPoint.x() = %1" ).arg( mRasterViewPort->mTopLeftPoint.x() ), 3 );
175  QgsDebugMsgLevel( QString( "mBottomRightPoint.x() = %1" ).arg( mRasterViewPort->mBottomRightPoint.x() ), 3 );
176  QgsDebugMsgLevel( QString( "mTopLeftPoint.y() = %1" ).arg( mRasterViewPort->mTopLeftPoint.y() ), 3 );
177  QgsDebugMsgLevel( QString( "mBottomRightPoint.y() = %1" ).arg( mRasterViewPort->mBottomRightPoint.y() ), 3 );
178 
179  QgsDebugMsgLevel( QString( "mWidth = %1" ).arg( mRasterViewPort->mWidth ), 3 );
180  QgsDebugMsgLevel( QString( "mHeight = %1" ).arg( mRasterViewPort->mHeight ), 3 );
181 
182  // /\/\/\ - added to handle zoomed-in rasters
183 
184  // TODO R->mLastViewPort = *mRasterViewPort;
185 
186  // TODO: is it necessary? Probably WMS only?
187  layer->dataProvider()->setDpi( rendererContext.rasterScaleFactor() * 25.4 * rendererContext.scaleFactor() );
188 
189 
190  // copy the whole raster pipe!
191  mPipe = new QgsRasterPipe( *layer->pipe() );
192 }
193 
195 {
196  delete mFeedback;
197 
198  delete mRasterViewPort;
199  delete mPipe;
200 }
201 
203 {
204  if ( !mRasterViewPort )
205  return true; // outside of layer extent - nothing to do
206 
207  //R->draw( mPainter, mRasterViewPort, &mMapToPixel );
208 
209  QTime time;
210  time.start();
211  //
212  //
213  // The goal here is to make as many decisions as possible early on (outside of the rendering loop)
214  // so that we can maximise performance of the rendering process. So now we check which drawing
215  // procedure to use :
216  //
217 
218  QgsRasterProjector *projector = mPipe->projector();
219 
220  // TODO add a method to interface to get provider and get provider
221  // params in QgsRasterProjector
222  if ( projector )
223  {
225  }
226 
227  // Drawer to pipe?
228  QgsRasterIterator iterator( mPipe->last() );
229  QgsRasterDrawer drawer( &iterator );
230  drawer.draw( mPainter, mRasterViewPort, mMapToPixel, nullptr, mFeedback );
231 
232  QgsDebugMsgLevel( QString( "total raster draw time (ms): %1" ).arg( time.elapsed(), 5 ), 4 );
233 
234  return true;
235 }
236 
238 {
239  return mFeedback;
240 }
241 
243  : mR( r )
244  , mMinimalPreviewInterval( 250 )
245 {
247 }
248 
250 {
251  if ( !renderPartialOutput() )
252  return; // we were not asked for partial renders and we may not have a temporary image for overwriting...
253 
254  // update only once upon a time
255  // (preview itself takes some time)
256  if ( mLastPreview.isValid() && mLastPreview.msecsTo( QTime::currentTime() ) < mMinimalPreviewInterval )
257  return;
258 
259  // TODO: update only the area that got new data
260 
261  QgsDebugMsg( QString( "new raster preview! %1" ).arg( mLastPreview.msecsTo( QTime::currentTime() ) ) );
262  QTime t;
263  t.start();
265  feedback.setPreviewOnly( true );
266  feedback.setRenderPartialOutput( true );
267  QgsRasterIterator iterator( mR->mPipe->last() );
268  QgsRasterDrawer drawer( &iterator );
269  drawer.draw( mR->mPainter, mR->mRasterViewPort, mR->mMapToPixel, nullptr, &feedback );
270  QgsDebugMsg( QString( "total raster preview time: %1 ms" ).arg( t.elapsed() ) );
271  mLastPreview = QTime::currentTime();
272 }
int width() const
Accessor that returns the width of the (unclipped) raster.
virtual QgsFeedback * feedback() const override
Access to feedback object of the layer renderer (may be null)
A rectangle specified with double values.
Definition: qgsrectangle.h:35
void setMapRotation(double degrees, double cx, double cy)
Set map rotation in degrees (clockwise)
bool renderPartialOutput() const
Whether our painter is drawing to a temporary image used just by this layer.
void setMinimal()
Set a rectangle so that min corner is at max and max corner is at min.
Base class for processing modules.
Definition: qgsrasterpipe.h:42
void setCRS(const QgsCoordinateReferenceSystem &theSrcCRS, const QgsCoordinateReferenceSystem &theDestCRS, int srcDatumTransform=-1, int destDatumTransform=-1)
set source and destination CRS
Iterator for sequentially processing raster cells.
Specific internal feedback class to provide preview of raster layer rendering.
Feedback(QgsRasterLayerRenderer *r)
Create feedback object based on our layer renderer.
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
This class provides qgis with the ability to render raster datasets onto the mapcanvas.
QgsRasterPipe * pipe()
Get raster pipe.
const QgsMapToPixel * mMapToPixel
QgsRasterProjector * projector() const
const QgsCoordinateReferenceSystem & crs() const
Returns layer&#39;s spatial reference system.
QgsRasterInterface * last() const
Definition: qgsrasterpipe.h:87
bool isValid() const
QgsPoint toMapCoordinates(int x, int y) const
int msecsTo(const QTime &t) const
QgsRectangle intersect(const QgsRectangle *rect) const
return the intersection with the given rectangle
void setRenderPartialOutput(bool enable)
Set whether our painter is drawing to a temporary image used just by this layer.
int height() const
Accessor that returns the height of the (unclipped) raster.
Implementation of threaded rendering for raster layers.
QString tr(const char *sourceText, const char *disambiguation, int n)
virtual bool render() override
Do the rendering (based on data stored in the class)
double y() const
Get the y value of the point.
Definition: qgspoint.h:193
double mapRotation() const
Return current map rotation in degrees.
QgsCoordinateReferenceSystem mDestCRS
Target coordinate system.
QString what() const
Definition: qgsexception.h:36
void set(const QgsPoint &p1, const QgsPoint &p2)
Set the rectangle from two QgsPoints.
Base class for feedback objects to be used for cancellation of something running in a worker thread...
Definition: qgsfeedback.h:40
QgsRectangle transformBoundingBox(const QgsRectangle &theRect, TransformDirection direction=ForwardTransform, const bool handle180Crossover=false) const
Transform a QgsRectangle to the dest Coordinate system If the direction is ForwardTransform then coor...
QgsPoint transform(const QgsPoint &p) const
Transform the point from map (world) coordinates to device coordinates.
QString toString(bool automaticPrecision=false) const
returns string representation of form xmin,ymin xmax,ymax
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:34
The drawing pipe for raster layers.
int elapsed() const
int mapWidth() const
Return current map width in pixels The information is only known if setRotation was used...
int mapHeight() const
Return current map height in pixels.
void setPreviewOnly(bool preview)
set flag whether the block request is for preview purposes only
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:34
bool isEmpty() const
test if rectangle is empty.
const QgsRectangle & extent() const
QgsRasterLayerRenderer(QgsRasterLayer *layer, QgsRenderContext &rendererContext)
static void logMessage(const QString &message, const QString &tag=QString::null, MessageLevel level=WARNING)
add a message to the instance (and create it if necessary)
const QgsCoordinateTransform * coordinateTransform() const
double mapUnitsPerPixel() const
Return current map units per pixel.
A class to represent a point.
Definition: qgspoint.h:117
void setX(double x)
Sets the x value of the point.
Definition: qgspoint.h:162
double yMinimum() const
Get the y minimum value (bottom side of rectangle)
Definition: qgsrectangle.h:202
QgsCoordinateReferenceSystem mSrcCRS
Source coordinate system.
Feedback * mFeedback
feedback class for cancellation and preview generation
double xMaximum() const
Get the x maximum value (right side of rectangle)
Definition: qgsrectangle.h:187
QgsRasterProjector implements approximate projection support for it calculates grid of points in sour...
QTime currentTime()
QgsRasterViewPort * mRasterViewPort
Contains information about the context of a rendering operation.
QPainter * painter()
const QgsMapToPixel & mapToPixel() const
virtual void onNewData() override
when notified of new data in data provider it launches a preview draw of the raster ...
Whether to make extra effort to update map image with partially rendered layers (better for interacti...
Class for storing a coordinate reference system (CRS)
const QgsCoordinateReferenceSystem & destCRS() const
double xMinimum() const
Get the x minimum value (left side of rectangle)
Definition: qgsrectangle.h:192
Base class for utility classes that encapsulate information necessary for rendering of map layers...
void start()
double yMaximum() const
Get the y maximum value (top side of rectangle)
Definition: qgsrectangle.h:197
QgsRasterDataProvider * dataProvider()
Returns the data provider.
Custom exception class for Coordinate Reference System related exceptions.
This class provides details of the viewable area that a raster will be rendered into.
double rasterScaleFactor() const
bool testFlag(Flag flag) const
Check whether a particular flag is enabled.
QgsPoint toMapCoordinatesF(double x, double y) const
Transform device coordinates to map (world) coordinates.
virtual QgsRectangle extent()
Return the extent of the layer.
double scaleFactor() const
Feedback object tailored for raster block reading.
void setDpi(int dpi)
Sets the output device resolution.
double x() const
Get the x value of the point.
Definition: qgspoint.h:185