QGIS API Documentation  2.8.2-Wien
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups 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  myProjectedLayerExtent.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 }