QGIS API Documentation  2.99.0-Master (dcec6bb)
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 
29  : QgsMapLayerRenderer( layer->id() )
30  , mRasterViewPort( nullptr )
31  , mPipe( nullptr )
32  , mContext( rendererContext )
33  , mFeedback( new Feedback( this ) )
34 {
35  mPainter = rendererContext.painter();
36  const QgsMapToPixel &qgsMapToPixel = rendererContext.mapToPixel();
37  mMapToPixel = &qgsMapToPixel;
38 
39  QgsMapToPixel mapToPixel = qgsMapToPixel;
40  if ( mapToPixel.mapRotation() )
41  {
42  // unset rotation for the sake of local computations.
43  // Rotation will be handled by QPainter later
44  // TODO: provide a method of QgsMapToPixel to fetch map center
45  // in geographical units
46  QgsPointXY center = mapToPixel.toMapCoordinates(
47  mapToPixel.mapWidth() / 2.0,
48  mapToPixel.mapHeight() / 2.0
49  );
50  mapToPixel.setMapRotation( 0, center.x(), center.y() );
51  }
52 
53  QgsRectangle myProjectedViewExtent;
54  QgsRectangle myProjectedLayerExtent;
55 
56  if ( rendererContext.coordinateTransform().isValid() )
57  {
58  QgsDebugMsgLevel( "coordinateTransform set -> project extents.", 4 );
59  try
60  {
61  myProjectedViewExtent = rendererContext.coordinateTransform().transformBoundingBox( rendererContext.extent() );
62  }
63  catch ( QgsCsException &cs )
64  {
65  QgsMessageLog::logMessage( QObject::tr( "Could not reproject view extent: %1" ).arg( cs.what() ), QObject::tr( "Raster" ) );
66  myProjectedViewExtent.setMinimal();
67  }
68 
69  try
70  {
71  myProjectedLayerExtent = rendererContext.coordinateTransform().transformBoundingBox( layer->extent() );
72  }
73  catch ( QgsCsException &cs )
74  {
75  QgsMessageLog::logMessage( QObject::tr( "Could not reproject layer extent: %1" ).arg( cs.what() ), QObject::tr( "Raster" ) );
76  myProjectedLayerExtent.setMinimal();
77  }
78  }
79  else
80  {
81  QgsDebugMsgLevel( "coordinateTransform not set", 4 );
82  myProjectedViewExtent = rendererContext.extent();
83  myProjectedLayerExtent = layer->extent();
84  }
85 
86  // clip raster extent to view extent
87  QgsRectangle myRasterExtent = myProjectedViewExtent.intersect( &myProjectedLayerExtent );
88  if ( myRasterExtent.isEmpty() )
89  {
90  QgsDebugMsg( "draw request outside view extent." );
91  // nothing to do
92  return;
93  }
94 
95  QgsDebugMsgLevel( "theViewExtent is " + rendererContext.extent().toString(), 4 );
96  QgsDebugMsgLevel( "myProjectedViewExtent is " + myProjectedViewExtent.toString(), 4 );
97  QgsDebugMsgLevel( "myProjectedLayerExtent is " + myProjectedLayerExtent.toString(), 4 );
98  QgsDebugMsgLevel( "myRasterExtent is " + myRasterExtent.toString(), 4 );
99 
100  //
101  // The first thing we do is set up the QgsRasterViewPort. This struct stores all the settings
102  // relating to the size (in pixels and coordinate system units) of the raster part that is
103  // in view in the map window. It also stores the origin.
104  //
105  //this is not a class level member because every time the user pans or zooms
106  //the contents of the rasterViewPort will change
108 
109  mRasterViewPort->mDrawnExtent = myRasterExtent;
110  if ( rendererContext.coordinateTransform().isValid() )
111  {
112  mRasterViewPort->mSrcCRS = layer->crs();
113  mRasterViewPort->mDestCRS = rendererContext.coordinateTransform().destinationCrs();
114  mRasterViewPort->mSrcDatumTransform = rendererContext.coordinateTransform().sourceDatumTransform();
115  mRasterViewPort->mDestDatumTransform = rendererContext.coordinateTransform().destinationDatumTransform();
116  }
117  else
118  {
119  mRasterViewPort->mSrcCRS = QgsCoordinateReferenceSystem(); // will be invalid
120  mRasterViewPort->mDestCRS = QgsCoordinateReferenceSystem(); // will be invalid
121  mRasterViewPort->mSrcDatumTransform = -1;
122  mRasterViewPort->mDestDatumTransform = -1;
123  }
124 
125  // get dimensions of clipped raster image in device coordinate space (this is the size of the viewport)
126  mRasterViewPort->mTopLeftPoint = mapToPixel.transform( myRasterExtent.xMinimum(), myRasterExtent.yMaximum() );
127  mRasterViewPort->mBottomRightPoint = mapToPixel.transform( myRasterExtent.xMaximum(), myRasterExtent.yMinimum() );
128 
129  // align to output device grid, i.e. floor/ceil to integers
130  // TODO: this should only be done if paint device is raster - screen, image
131  // for other devices (pdf) it can have floating point origin
132  // we could use floating point for raster devices as well, but respecting the
133  // output device grid should make it more effective as the resampling is done in
134  // the provider anyway
135  mRasterViewPort->mTopLeftPoint.setX( floor( mRasterViewPort->mTopLeftPoint.x() ) );
136  mRasterViewPort->mTopLeftPoint.setY( floor( mRasterViewPort->mTopLeftPoint.y() ) );
137  mRasterViewPort->mBottomRightPoint.setX( ceil( mRasterViewPort->mBottomRightPoint.x() ) );
138  mRasterViewPort->mBottomRightPoint.setY( ceil( mRasterViewPort->mBottomRightPoint.y() ) );
139  // recalc myRasterExtent to aligned values
140  myRasterExtent.set(
141  mapToPixel.toMapCoordinatesF( mRasterViewPort->mTopLeftPoint.x(),
142  mRasterViewPort->mBottomRightPoint.y() ),
143  mapToPixel.toMapCoordinatesF( mRasterViewPort->mBottomRightPoint.x(),
144  mRasterViewPort->mTopLeftPoint.y() )
145  );
146 
147  //raster viewport top left / bottom right are already rounded to int
148  mRasterViewPort->mWidth = static_cast<int>( mRasterViewPort->mBottomRightPoint.x() - mRasterViewPort->mTopLeftPoint.x() );
149  mRasterViewPort->mHeight = static_cast<int>( mRasterViewPort->mBottomRightPoint.y() - mRasterViewPort->mTopLeftPoint.y() );
150 
151  //the drawable area can start to get very very large when you get down displaying 2x2 or smaller, this is because
152  //mapToPixel.mapUnitsPerPixel() is less then 1,
153  //so we will just get the pixel data and then render these special cases differently in paintImageToCanvas()
154 
155  QgsDebugMsgLevel( QString( "mapUnitsPerPixel = %1" ).arg( mapToPixel.mapUnitsPerPixel() ), 3 );
156  QgsDebugMsgLevel( QString( "mWidth = %1" ).arg( layer->width() ), 3 );
157  QgsDebugMsgLevel( QString( "mHeight = %1" ).arg( layer->height() ), 3 );
158  QgsDebugMsgLevel( QString( "myRasterExtent.xMinimum() = %1" ).arg( myRasterExtent.xMinimum() ), 3 );
159  QgsDebugMsgLevel( QString( "myRasterExtent.xMaximum() = %1" ).arg( myRasterExtent.xMaximum() ), 3 );
160  QgsDebugMsgLevel( QString( "myRasterExtent.yMinimum() = %1" ).arg( myRasterExtent.yMinimum() ), 3 );
161  QgsDebugMsgLevel( QString( "myRasterExtent.yMaximum() = %1" ).arg( myRasterExtent.yMaximum() ), 3 );
162 
163  QgsDebugMsgLevel( QString( "mTopLeftPoint.x() = %1" ).arg( mRasterViewPort->mTopLeftPoint.x() ), 3 );
164  QgsDebugMsgLevel( QString( "mBottomRightPoint.x() = %1" ).arg( mRasterViewPort->mBottomRightPoint.x() ), 3 );
165  QgsDebugMsgLevel( QString( "mTopLeftPoint.y() = %1" ).arg( mRasterViewPort->mTopLeftPoint.y() ), 3 );
166  QgsDebugMsgLevel( QString( "mBottomRightPoint.y() = %1" ).arg( mRasterViewPort->mBottomRightPoint.y() ), 3 );
167 
168  QgsDebugMsgLevel( QString( "mWidth = %1" ).arg( mRasterViewPort->mWidth ), 3 );
169  QgsDebugMsgLevel( QString( "mHeight = %1" ).arg( mRasterViewPort->mHeight ), 3 );
170 
171  // /\/\/\ - added to handle zoomed-in rasters
172 
173  // TODO R->mLastViewPort = *mRasterViewPort;
174 
175  // TODO: is it necessary? Probably WMS only?
176  layer->dataProvider()->setDpi( 25.4 * rendererContext.scaleFactor() );
177 
178 
179  // copy the whole raster pipe!
180  mPipe = new QgsRasterPipe( *layer->pipe() );
181  QgsRasterRenderer *rasterRenderer = mPipe->renderer();
182  if ( rasterRenderer )
183  layer->refreshRendererIfNeeded( rasterRenderer, rendererContext.extent() );
184 }
185 
187 {
188  delete mFeedback;
189 
190  delete mRasterViewPort;
191  delete mPipe;
192 }
193 
195 {
196  if ( !mRasterViewPort )
197  return true; // outside of layer extent - nothing to do
198 
199  //R->draw( mPainter, mRasterViewPort, &mMapToPixel );
200 
201  QTime time;
202  time.start();
203  //
204  //
205  // The goal here is to make as many decisions as possible early on (outside of the rendering loop)
206  // so that we can maximise performance of the rendering process. So now we check which drawing
207  // procedure to use :
208  //
209 
210  QgsRasterProjector *projector = mPipe->projector();
211 
212  // TODO add a method to interface to get provider and get provider
213  // params in QgsRasterProjector
214  if ( projector )
215  {
217  }
218 
219  // Drawer to pipe?
220  QgsRasterIterator iterator( mPipe->last() );
221  QgsRasterDrawer drawer( &iterator );
222  drawer.draw( mPainter, mRasterViewPort, mMapToPixel, mFeedback );
223 
224  QgsDebugMsgLevel( QString( "total raster draw time (ms): %1" ).arg( time.elapsed(), 5 ), 4 );
225 
226  return true;
227 }
228 
230 {
231  return mFeedback;
232 }
233 
235  : mR( r )
236  , mMinimalPreviewInterval( 250 )
237 {
239 }
240 
242 {
243  if ( !renderPartialOutput() )
244  return; // we were not asked for partial renders and we may not have a temporary image for overwriting...
245 
246  // update only once upon a time
247  // (preview itself takes some time)
248  if ( mLastPreview.isValid() && mLastPreview.msecsTo( QTime::currentTime() ) < mMinimalPreviewInterval )
249  return;
250 
251  // TODO: update only the area that got new data
252 
253  QgsDebugMsg( QString( "new raster preview! %1" ).arg( mLastPreview.msecsTo( QTime::currentTime() ) ) );
254  QTime t;
255  t.start();
257  feedback.setPreviewOnly( true );
258  feedback.setRenderPartialOutput( true );
259  QgsRasterIterator iterator( mR->mPipe->last() );
260  QgsRasterDrawer drawer( &iterator );
261  drawer.draw( mR->mPainter, mR->mRasterViewPort, mR->mMapToPixel, &feedback );
262  QgsDebugMsg( QString( "total raster preview time: %1 ms" ).arg( t.elapsed() ) );
263  mLastPreview = QTime::currentTime();
264 }
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:38
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:45
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.
QgsRasterRenderer * renderer() const
#define QgsDebugMsg(str)
Definition: qgslogger.h:37
This class provides qgis with the ability to render raster datasets onto the mapcanvas.
double y
Definition: qgspointxy.h:47
QgsRasterPipe * pipe()
Get raster pipe.
A class to represent a 2D point.
Definition: qgspointxy.h:42
const QgsMapToPixel * mMapToPixel
QgsRasterProjector * projector() const
QgsRasterInterface * last() const
QgsRectangle transformBoundingBox(const QgsRectangle &rectangle, TransformDirection direction=ForwardTransform, const bool handle180Crossover=false) const
Transforms a rectangle from the source CRS to the destination CRS.
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system, which the transform will transform coordinates t...
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.
virtual 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
Return current map rotation in degrees.
QgsCoordinateReferenceSystem mDestCRS
Target coordinate system.
QString what() const
Definition: qgsexception.h:48
void setCrs(const QgsCoordinateReferenceSystem &srcCRS, const QgsCoordinateReferenceSystem &destCRS, int srcDatumTransform=-1, int destDatumTransform=-1)
set source and destination CRS
Base class for feedback objects to be used for cancelation of something running in a worker thread...
Definition: qgsfeedback.h:43
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:35
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
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:38
bool isEmpty() const
Returns true if the rectangle is empty.
const QgsRectangle & extent() const
QgsRasterLayerRenderer(QgsRasterLayer *layer, QgsRenderContext &rendererContext)
QString toString(int precision=16) const
Returns a string representation of form xmin,ymin : xmax,ymax Coordinates will be truncated to the sp...
QgsCoordinateReferenceSystem crs() const
Returns the layer&#39;s spatial reference system.
QgsCoordinateTransform coordinateTransform() const
Returns the current coordinate transform for the context, or an invalid transform is no coordinate tr...
double mapUnitsPerPixel() const
Return current map units per pixel.
void setX(double x)
Sets the x value of the point.
Definition: qgspointxy.h:100
double x
Definition: qgspointxy.h:46
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:106
QgsCoordinateReferenceSystem mSrcCRS
Source coordinate system.
Feedback * mFeedback
feedback class for cancelation and preview generation
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:91
QgsRasterProjector implements approximate projection support for it calculates grid of points in sour...
static void logMessage(const QString &message, const QString &tag=QString(), MessageLevel level=QgsMessageLog::WARNING)
add a message to the instance (and create it if necessary)
QgsRasterViewPort * mRasterViewPort
Contains information about the context of a rendering operation.
QPainter * painter()
Returns the destination QPainter for the render operation.
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...
This class represents a coordinate reference system (CRS).
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:96
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:101
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:64
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.
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 toMapCoordinatesF(double x, double y) const
Transform device coordinates to map (world) coordinates.
QgsPointXY toMapCoordinates(int x, int y) const
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.