QGIS API Documentation  2.17.0-Master (8784312)
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 {
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  try
56  {
57  myProjectedViewExtent = rendererContext.coordinateTransform()->transformBoundingBox( rendererContext.extent() );
58  }
59  catch ( QgsCsException &cs )
60  {
61  QgsMessageLog::logMessage( QObject::tr( "Could not reproject view extent: %1" ).arg( cs.what() ), QObject::tr( "Raster" ) );
62  myProjectedViewExtent.setMinimal();
63  }
64 
65  try
66  {
67  myProjectedLayerExtent = rendererContext.coordinateTransform()->transformBoundingBox( layer->extent() );
68  }
69  catch ( QgsCsException &cs )
70  {
71  QgsMessageLog::logMessage( QObject::tr( "Could not reproject layer extent: %1" ).arg( cs.what() ), QObject::tr( "Raster" ) );
72  myProjectedLayerExtent.setMinimal();
73  }
74  }
75  else
76  {
77  QgsDebugMsgLevel( "coordinateTransform not set", 4 );
78  myProjectedViewExtent = rendererContext.extent();
79  myProjectedLayerExtent = layer->extent();
80  }
81 
82  // clip raster extent to view extent
83  QgsRectangle myRasterExtent = myProjectedViewExtent.intersect( &myProjectedLayerExtent );
84  if ( myRasterExtent.isEmpty() )
85  {
86  QgsDebugMsg( "draw request outside view extent." );
87  // nothing to do
88  return;
89  }
90 
91  QgsDebugMsgLevel( "theViewExtent is " + rendererContext.extent().toString(), 4 );
92  QgsDebugMsgLevel( "myProjectedViewExtent is " + myProjectedViewExtent.toString(), 4 );
93  QgsDebugMsgLevel( "myProjectedLayerExtent is " + myProjectedLayerExtent.toString(), 4 );
94  QgsDebugMsgLevel( "myRasterExtent is " + myRasterExtent.toString(), 4 );
95 
96  //
97  // The first thing we do is set up the QgsRasterViewPort. This struct stores all the settings
98  // relating to the size (in pixels and coordinate system units) of the raster part that is
99  // in view in the map window. It also stores the origin.
100  //
101  //this is not a class level member because every time the user pans or zooms
102  //the contents of the rasterViewPort will change
104 
105  mRasterViewPort->mDrawnExtent = myRasterExtent;
106  if ( rendererContext.coordinateTransform() )
107  {
108  mRasterViewPort->mSrcCRS = layer->crs();
109  mRasterViewPort->mDestCRS = rendererContext.coordinateTransform()->destCRS();
110  mRasterViewPort->mSrcDatumTransform = rendererContext.coordinateTransform()->sourceDatumTransform();
111  mRasterViewPort->mDestDatumTransform = rendererContext.coordinateTransform()->destinationDatumTransform();
112  }
113  else
114  {
115  mRasterViewPort->mSrcCRS = QgsCoordinateReferenceSystem(); // will be invalid
116  mRasterViewPort->mDestCRS = QgsCoordinateReferenceSystem(); // will be invalid
117  mRasterViewPort->mSrcDatumTransform = -1;
118  mRasterViewPort->mDestDatumTransform = -1;
119  }
120 
121  // get dimensions of clipped raster image in device coordinate space (this is the size of the viewport)
122  mRasterViewPort->mTopLeftPoint = mapToPixel.transform( myRasterExtent.xMinimum(), myRasterExtent.yMaximum() );
123  mRasterViewPort->mBottomRightPoint = mapToPixel.transform( myRasterExtent.xMaximum(), myRasterExtent.yMinimum() );
124 
125  // align to output device grid, i.e. floor/ceil to integers
126  // TODO: this should only be done if paint device is raster - screen, image
127  // for other devices (pdf) it can have floating point origin
128  // we could use floating point for raster devices as well, but respecting the
129  // output device grid should make it more effective as the resampling is done in
130  // the provider anyway
131  mRasterViewPort->mTopLeftPoint.setX( floor( mRasterViewPort->mTopLeftPoint.x() ) );
132  mRasterViewPort->mTopLeftPoint.setY( floor( mRasterViewPort->mTopLeftPoint.y() ) );
133  mRasterViewPort->mBottomRightPoint.setX( ceil( mRasterViewPort->mBottomRightPoint.x() ) );
134  mRasterViewPort->mBottomRightPoint.setY( ceil( mRasterViewPort->mBottomRightPoint.y() ) );
135  // recalc myRasterExtent to aligned values
136  myRasterExtent.set(
137  mapToPixel.toMapCoordinatesF( mRasterViewPort->mTopLeftPoint.x(),
138  mRasterViewPort->mBottomRightPoint.y() ),
139  mapToPixel.toMapCoordinatesF( mRasterViewPort->mBottomRightPoint.x(),
140  mRasterViewPort->mTopLeftPoint.y() )
141  );
142 
143  //raster viewport top left / bottom right are already rounded to int
144  mRasterViewPort->mWidth = static_cast<int>( mRasterViewPort->mBottomRightPoint.x() - mRasterViewPort->mTopLeftPoint.x() );
145  mRasterViewPort->mHeight = static_cast<int>( mRasterViewPort->mBottomRightPoint.y() - mRasterViewPort->mTopLeftPoint.y() );
146 
147  //the drawable area can start to get very very large when you get down displaying 2x2 or smaller, this is becasue
148  //mapToPixel.mapUnitsPerPixel() is less then 1,
149  //so we will just get the pixel data and then render these special cases differently in paintImageToCanvas()
150 
151  QgsDebugMsgLevel( QString( "mapUnitsPerPixel = %1" ).arg( mapToPixel.mapUnitsPerPixel() ), 3 );
152  QgsDebugMsgLevel( QString( "mWidth = %1" ).arg( layer->width() ), 3 );
153  QgsDebugMsgLevel( QString( "mHeight = %1" ).arg( layer->height() ), 3 );
154  QgsDebugMsgLevel( QString( "myRasterExtent.xMinimum() = %1" ).arg( myRasterExtent.xMinimum() ), 3 );
155  QgsDebugMsgLevel( QString( "myRasterExtent.xMaximum() = %1" ).arg( myRasterExtent.xMaximum() ), 3 );
156  QgsDebugMsgLevel( QString( "myRasterExtent.yMinimum() = %1" ).arg( myRasterExtent.yMinimum() ), 3 );
157  QgsDebugMsgLevel( QString( "myRasterExtent.yMaximum() = %1" ).arg( myRasterExtent.yMaximum() ), 3 );
158 
159  QgsDebugMsgLevel( QString( "mTopLeftPoint.x() = %1" ).arg( mRasterViewPort->mTopLeftPoint.x() ), 3 );
160  QgsDebugMsgLevel( QString( "mBottomRightPoint.x() = %1" ).arg( mRasterViewPort->mBottomRightPoint.x() ), 3 );
161  QgsDebugMsgLevel( QString( "mTopLeftPoint.y() = %1" ).arg( mRasterViewPort->mTopLeftPoint.y() ), 3 );
162  QgsDebugMsgLevel( QString( "mBottomRightPoint.y() = %1" ).arg( mRasterViewPort->mBottomRightPoint.y() ), 3 );
163 
164  QgsDebugMsgLevel( QString( "mWidth = %1" ).arg( mRasterViewPort->mWidth ), 3 );
165  QgsDebugMsgLevel( QString( "mHeight = %1" ).arg( mRasterViewPort->mHeight ), 3 );
166 
167  // /\/\/\ - added to handle zoomed-in rasters
168 
169  // TODO R->mLastViewPort = *mRasterViewPort;
170 
171  // TODO: is it necessary? Probably WMS only?
172  layer->dataProvider()->setDpi( rendererContext.rasterScaleFactor() * 25.4 * rendererContext.scaleFactor() );
173 
174 
175  // copy the whole raster pipe!
176  mPipe = new QgsRasterPipe( *layer->pipe() );
177 }
178 
180 {
181  delete mRasterViewPort;
182  delete mPipe;
183 }
184 
186 {
187  if ( !mRasterViewPort )
188  return true; // outside of layer extent - nothing to do
189 
190  //R->draw( mPainter, mRasterViewPort, &mMapToPixel );
191 
192  QTime time;
193  time.start();
194  //
195  //
196  // The goal here is to make as many decisions as possible early on (outside of the rendering loop)
197  // so that we can maximise performance of the rendering process. So now we check which drawing
198  // procedure to use :
199  //
200 
201  QgsRasterProjector *projector = mPipe->projector();
202 
203  // TODO add a method to interface to get provider and get provider
204  // params in QgsRasterProjector
205  if ( projector )
206  {
208  }
209 
210  // Drawer to pipe?
211  QgsRasterIterator iterator( mPipe->last() );
212  QgsRasterDrawer drawer( &iterator );
213  drawer.draw( mPainter, mRasterViewPort, mMapToPixel, &mContext );
214 
215  QgsDebugMsgLevel( QString( "total raster draw time (ms): %1" ).arg( time.elapsed(), 5 ), 4 );
216 
217  return true;
218 }
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:185
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:117
void setX(double x)
Sets the x value of the point.
Definition: qgspoint.h:162
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:193
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.