QGIS API Documentation  2.14.0-Essen
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 {
29 
30  mPainter = rendererContext.painter();
31  const QgsMapToPixel& theQgsMapToPixel = rendererContext.mapToPixel();
32  mMapToPixel = &theQgsMapToPixel;
33 
34  QgsMapToPixel mapToPixel = theQgsMapToPixel;
35  if ( mapToPixel.mapRotation() )
36  {
37  // unset rotation for the sake of local computations.
38  // Rotation will be handled by QPainter later
39  // TODO: provide a method of QgsMapToPixel to fetch map center
40  // in geographical units
41  QgsPoint center = mapToPixel.toMapCoordinates(
42  mapToPixel.mapWidth() / 2.0,
43  mapToPixel.mapHeight() / 2.0
44  );
45  mapToPixel.setMapRotation( 0, center.x(), center.y() );
46  }
47 
48  QgsRectangle myProjectedViewExtent;
49  QgsRectangle myProjectedLayerExtent;
50 
51  if ( rendererContext.coordinateTransform() )
52  {
53  QgsDebugMsgLevel( "coordinateTransform set -> project extents.", 4 );
54  try
55  {
56  myProjectedViewExtent = rendererContext.coordinateTransform()->transformBoundingBox( rendererContext.extent() );
57  }
58  catch ( QgsCsException &cs )
59  {
60  QgsMessageLog::logMessage( QObject::tr( "Could not reproject view extent: %1" ).arg( cs.what() ), QObject::tr( "Raster" ) );
61  myProjectedViewExtent.setMinimal();
62  }
63 
64  try
65  {
66  myProjectedLayerExtent = rendererContext.coordinateTransform()->transformBoundingBox( layer->extent() );
67  }
68  catch ( QgsCsException &cs )
69  {
70  QgsMessageLog::logMessage( QObject::tr( "Could not reproject layer extent: %1" ).arg( cs.what() ), QObject::tr( "Raster" ) );
71  myProjectedLayerExtent.setMinimal();
72  }
73  }
74  else
75  {
76  QgsDebugMsgLevel( "coordinateTransform not set", 4 );
77  myProjectedViewExtent = rendererContext.extent();
78  myProjectedLayerExtent = layer->extent();
79  }
80 
81  // clip raster extent to view extent
82  QgsRectangle myRasterExtent = myProjectedViewExtent.intersect( &myProjectedLayerExtent );
83  if ( myRasterExtent.isEmpty() )
84  {
85  QgsDebugMsg( "draw request outside view extent." );
86  // nothing to do
87  return;
88  }
89 
90  QgsDebugMsgLevel( "theViewExtent is " + rendererContext.extent().toString(), 4 );
91  QgsDebugMsgLevel( "myProjectedViewExtent is " + myProjectedViewExtent.toString(), 4 );
92  QgsDebugMsgLevel( "myProjectedLayerExtent is " + myProjectedLayerExtent.toString(), 4 );
93  QgsDebugMsgLevel( "myRasterExtent is " + myRasterExtent.toString(), 4 );
94 
95  //
96  // The first thing we do is set up the QgsRasterViewPort. This struct stores all the settings
97  // relating to the size (in pixels and coordinate system units) of the raster part that is
98  // in view in the map window. It also stores the origin.
99  //
100  //this is not a class level member because every time the user pans or zooms
101  //the contents of the rasterViewPort will change
103 
104  mRasterViewPort->mDrawnExtent = myRasterExtent;
105  if ( rendererContext.coordinateTransform() )
106  {
107  mRasterViewPort->mSrcCRS = layer->crs();
108  mRasterViewPort->mDestCRS = rendererContext.coordinateTransform()->destCRS();
109  mRasterViewPort->mSrcDatumTransform = rendererContext.coordinateTransform()->sourceDatumTransform();
110  mRasterViewPort->mDestDatumTransform = rendererContext.coordinateTransform()->destinationDatumTransform();
111  }
112  else
113  {
114  mRasterViewPort->mSrcCRS = QgsCoordinateReferenceSystem(); // will be invalid
115  mRasterViewPort->mDestCRS = QgsCoordinateReferenceSystem(); // will be invalid
116  mRasterViewPort->mSrcDatumTransform = -1;
117  mRasterViewPort->mDestDatumTransform = -1;
118  }
119 
120  // get dimensions of clipped raster image in device coordinate space (this is the size of the viewport)
121  mRasterViewPort->mTopLeftPoint = mapToPixel.transform( myRasterExtent.xMinimum(), myRasterExtent.yMaximum() );
122  mRasterViewPort->mBottomRightPoint = mapToPixel.transform( myRasterExtent.xMaximum(), myRasterExtent.yMinimum() );
123 
124  // align to output device grid, i.e. floor/ceil to integers
125  // TODO: this should only be done if paint device is raster - screen, image
126  // for other devices (pdf) it can have floating point origin
127  // we could use floating point for raster devices as well, but respecting the
128  // output device grid should make it more effective as the resampling is done in
129  // the provider anyway
130  mRasterViewPort->mTopLeftPoint.setX( floor( mRasterViewPort->mTopLeftPoint.x() ) );
131  mRasterViewPort->mTopLeftPoint.setY( floor( mRasterViewPort->mTopLeftPoint.y() ) );
132  mRasterViewPort->mBottomRightPoint.setX( ceil( mRasterViewPort->mBottomRightPoint.x() ) );
133  mRasterViewPort->mBottomRightPoint.setY( ceil( mRasterViewPort->mBottomRightPoint.y() ) );
134  // recalc myRasterExtent to aligned values
135  myRasterExtent.set(
136  mapToPixel.toMapCoordinatesF( mRasterViewPort->mTopLeftPoint.x(),
137  mRasterViewPort->mBottomRightPoint.y() ),
138  mapToPixel.toMapCoordinatesF( mRasterViewPort->mBottomRightPoint.x(),
139  mRasterViewPort->mTopLeftPoint.y() )
140  );
141 
142  //raster viewport top left / bottom right are already rounded to int
143  mRasterViewPort->mWidth = static_cast<int>( mRasterViewPort->mBottomRightPoint.x() - mRasterViewPort->mTopLeftPoint.x() );
144  mRasterViewPort->mHeight = static_cast<int>( mRasterViewPort->mBottomRightPoint.y() - mRasterViewPort->mTopLeftPoint.y() );
145 
146  //the drawable area can start to get very very large when you get down displaying 2x2 or smaller, this is becasue
147  //mapToPixel.mapUnitsPerPixel() is less then 1,
148  //so we will just get the pixel data and then render these special cases differently in paintImageToCanvas()
149 
150  QgsDebugMsgLevel( QString( "mapUnitsPerPixel = %1" ).arg( mapToPixel.mapUnitsPerPixel() ), 3 );
151  QgsDebugMsgLevel( QString( "mWidth = %1" ).arg( layer->width() ), 3 );
152  QgsDebugMsgLevel( QString( "mHeight = %1" ).arg( layer->height() ), 3 );
153  QgsDebugMsgLevel( QString( "myRasterExtent.xMinimum() = %1" ).arg( myRasterExtent.xMinimum() ), 3 );
154  QgsDebugMsgLevel( QString( "myRasterExtent.xMaximum() = %1" ).arg( myRasterExtent.xMaximum() ), 3 );
155  QgsDebugMsgLevel( QString( "myRasterExtent.yMinimum() = %1" ).arg( myRasterExtent.yMinimum() ), 3 );
156  QgsDebugMsgLevel( QString( "myRasterExtent.yMaximum() = %1" ).arg( myRasterExtent.yMaximum() ), 3 );
157 
158  QgsDebugMsgLevel( QString( "mTopLeftPoint.x() = %1" ).arg( mRasterViewPort->mTopLeftPoint.x() ), 3 );
159  QgsDebugMsgLevel( QString( "mBottomRightPoint.x() = %1" ).arg( mRasterViewPort->mBottomRightPoint.x() ), 3 );
160  QgsDebugMsgLevel( QString( "mTopLeftPoint.y() = %1" ).arg( mRasterViewPort->mTopLeftPoint.y() ), 3 );
161  QgsDebugMsgLevel( QString( "mBottomRightPoint.y() = %1" ).arg( mRasterViewPort->mBottomRightPoint.y() ), 3 );
162 
163  QgsDebugMsgLevel( QString( "mWidth = %1" ).arg( mRasterViewPort->mWidth ), 3 );
164  QgsDebugMsgLevel( QString( "mHeight = %1" ).arg( mRasterViewPort->mHeight ), 3 );
165 
166  // /\/\/\ - added to handle zoomed-in rasters
167 
168  // TODO R->mLastViewPort = *mRasterViewPort;
169 
170  // TODO: is it necessary? Probably WMS only?
171  layer->dataProvider()->setDpi( rendererContext.rasterScaleFactor() * 25.4 * rendererContext.scaleFactor() );
172 
173 
174  // copy the whole raster pipe!
175  mPipe = new QgsRasterPipe( *layer->pipe() );
176 }
177 
179 {
180  delete mRasterViewPort;
181  delete mPipe;
182 }
183 
185 {
186  if ( !mRasterViewPort )
187  return true; // outside of layer extent - nothing to do
188 
189  //R->draw( mPainter, mRasterViewPort, &mMapToPixel );
190 
191  QTime time;
192  time.start();
193  //
194  //
195  // The goal here is to make as many decisions as possible early on (outside of the rendering loop)
196  // so that we can maximise performance of the rendering process. So now we check which drawing
197  // procedure to use :
198  //
199 
200  QgsRasterProjector *projector = mPipe->projector();
201 
202  // TODO add a method to interface to get provider and get provider
203  // params in QgsRasterProjector
204  if ( projector )
205  {
207  }
208 
209  // Drawer to pipe?
210  QgsRasterIterator iterator( mPipe->last() );
211  QgsRasterDrawer drawer( &iterator );
212  drawer.draw( mPainter, mRasterViewPort, mMapToPixel );
213 
214  QgsDebugMsgLevel( QString( "total raster draw time (ms): %1" ).arg( time.elapsed(), 5 ), 4 );
215 
216  return true;
217 }
int mapWidth() const
Return current map width in pixels The information is only known if setRotation was used...
A rectangle specified with double values.
Definition: qgsrectangle.h:35
bool isEmpty() const
test if rectangle is empty.
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.
Base class for processing modules.
Definition: qgsrasterpipe.h:41
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.
double yMaximum() const
Get the y maximum value (top side of rectangle)
Definition: qgsrectangle.h:197
#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
QgsPoint transform(const QgsPoint &p) const
Transform the point from map (world) coordinates to device coordinates.
QgsRasterInterface * last() const
Definition: qgsrasterpipe.h:86
const QgsRectangle & extent() const
double scaleFactor() const
const QgsCoordinateTransform * coordinateTransform() const
QString tr(const char *sourceText, const char *disambiguation, int n)
double mapRotation() const
Return current map rotation in degrees.
virtual bool render() override
Do the rendering (based on data stored in the class)
double x() const
Get the x value of the point.
Definition: qgspoint.h:128
QgsCoordinateReferenceSystem mDestCRS
Target coordinate system.
void set(const QgsPoint &p1, const QgsPoint &p2)
Set the rectangle from two QgsPoints.
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:34
The drawing pipe for raster layers.
int elapsed() const
int height() const
Accessor that returns the height of the (unclipped) raster.
double yMinimum() const
Get the y minimum value (bottom side of rectangle)
Definition: qgsrectangle.h:202
double xMaximum() const
Get the x maximum value (right side of rectangle)
Definition: qgsrectangle.h:187
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:34
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)
double rasterScaleFactor() const
double mapUnitsPerPixel() const
Return current map units per pixel.
QgsRasterProjector * projector() const
A class to represent a point.
Definition: qgspoint.h:65
void setX(double x)
Sets the x value of the point.
Definition: qgspoint.h:105
QgsCoordinateReferenceSystem mSrcCRS
Source coordinate system.
QgsPoint toMapCoordinatesF(double x, double y) const
Transform device coordinates to map (world) coordinates.
QgsPoint toMapCoordinates(int x, int y) const
QString what() const
Definition: qgsexception.h:36
QgsRasterViewPort * mRasterViewPort
Contains information about the context of a rendering operation.
QPainter * painter()
QgsRectangle intersect(const QgsRectangle *rect) const
return the intersection with the given rectangle
Class for storing a coordinate reference system (CRS)
int mapHeight() const
Return current map height in pixels.
const QgsMapToPixel & mapToPixel() const
double y() const
Get the y value of the point.
Definition: qgspoint.h:136
Base class for utility classes that encapsulate information necessary for rendering of map layers...
const QgsCoordinateReferenceSystem & crs() const
Returns layer&#39;s spatial reference system.
void start()
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.
const QgsCoordinateReferenceSystem & destCRS() const
virtual QgsRectangle extent()
Return the extent of the layer.
QString toString(bool automaticPrecision=false) const
returns string representation of form xmin,ymin xmax,ymax
double xMinimum() const
Get the x minimum value (left side of rectangle)
Definition: qgsrectangle.h:192
void setDpi(int dpi)
Sets the output device resolution.
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...
int width() const
Accessor that returns the width of the (unclipped) raster.