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