QGIS API Documentation  2.7.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
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( 0 )
27  , mPipe( 0 )
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  QgsDebugMsg( "coordinateTransform set -> project extents." );
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  myProjectedViewExtent.setMinimal();
72  }
73  }
74  else
75  {
76  QgsDebugMsg( "coordinateTransform not set" );
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  QgsDebugMsg( "theViewExtent is " + rendererContext.extent().toString() );
91  QgsDebugMsg( "myProjectedViewExtent is " + myProjectedViewExtent.toString() );
92  QgsDebugMsg( "myProjectedLayerExtent is " + myProjectedLayerExtent.toString() );
93  QgsDebugMsg( "myRasterExtent is " + myRasterExtent.toString() );
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  QgsDebugMsg( QString( "total raster draw time (ms): %1" ).arg( time.elapsed(), 5 ) );
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:188
#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
QgsRasterInterface * last() const
Definition: qgsrasterpipe.h:86
const QgsRectangle & extent() const
double scaleFactor() const
const QgsCoordinateTransform * coordinateTransform() const
double mapRotation() const
Return current map rotation in degrees.
double x() const
Definition: qgspoint.h:126
static void logMessage(QString message, QString tag=QString::null, MessageLevel level=WARNING)
add a message to the instance (and create it if necessary)
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 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:193
double xMaximum() const
Get the x maximum value (right side of rectangle)
Definition: qgsrectangle.h:178
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:34
QgsRasterLayerRenderer(QgsRasterLayer *layer, QgsRenderContext &rendererContext)
double rasterScaleFactor() const
double mapUnitsPerPixel() const
Return current map units per pixel.
QgsRasterProjector * projector() const
A class to represent a point.
Definition: qgspoint.h:63
void setX(double x)
Definition: qgspoint.h:103
QgsCoordinateReferenceSystem mSrcCRS
Source coordinate system.
QgsPoint toMapCoordinatesF(double x, double y) const
QgsPoint toMapCoordinates(int x, int y) const
QString what() const
Definition: qgsexception.h:35
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
Definition: qgspoint.h:134
Base class for utility classes that encapsulate information necessary for rendering of map layers...
const QgsCoordinateReferenceSystem & crs() const
Returns layer's spatial reference system.
QgsRasterDataProvider * dataProvider()
Returns the data provider.
Custom exception class for Coordinate Reference System related exceptions.
virtual bool render()
Do the rendering (based on data stored in the class)
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:183
void setDpi(int dpi)
Sets the output device resolution.
QgsRectangle transformBoundingBox(const QgsRectangle &theRect, TransformDirection direction=ForwardTransform, const bool handle180Crossover=false) const
int width() const
Accessor that returns the width of the (unclipped) raster.
#define tr(sourceText)