Skip to content

Commit 4a554db

Browse files
committedNov 22, 2013
Initial support for rendering of rasters
1 parent 5b6393f commit 4a554db

File tree

7 files changed

+230
-139
lines changed

7 files changed

+230
-139
lines changed
 

‎src/core/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ SET(QGIS_CORE_SRCS
66
qgsmapsettings.cpp
77
qgsmaprendererjob.cpp
88
qgsvectorlayerrenderer.cpp
9+
raster/qgsrasterlayerrenderer.cpp
910

1011
gps/qgsgpsconnection.cpp
1112
gps/qgsgpsconnectionregistry.cpp

‎src/core/qgsmaplayerrenderer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#define QGSMAPLAYERRENDERER_H
33

44
#include <QList>
5+
#include <QString>
56

67
/**
78
* Base class for utility classes that encapsulate information necessary

‎src/core/raster/qgsrasterlayer.cpp

Lines changed: 8 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ email : tim at linfiniti.com
3131
#include "qgsrasterdrawer.h"
3232
#include "qgsrasteriterator.h"
3333
#include "qgsrasterlayer.h"
34+
#include "qgsrasterlayerrenderer.h"
3435
#include "qgsrasterprojector.h"
3536
#include "qgsrasterrange.h"
3637
#include "qgsrasterrendererregistry.h"
@@ -251,6 +252,11 @@ void QgsRasterLayer::reload()
251252
}
252253
}
253254

255+
QgsMapLayerRenderer *QgsRasterLayer::createMapRenderer( QgsRenderContext& rendererContext )
256+
{
257+
return new QgsRasterLayerRenderer( this, rendererContext );
258+
}
259+
254260
bool QgsRasterLayer::draw( QgsRenderContext& rendererContext )
255261
{
256262
QgsDebugMsg( "entered. (renderContext)" );
@@ -263,144 +269,8 @@ bool QgsRasterLayer::draw( QgsRenderContext& rendererContext )
263269
return false;
264270
}
265271

266-
const QgsMapToPixel& theQgsMapToPixel = rendererContext.mapToPixel();
267-
268-
QgsRectangle myProjectedViewExtent;
269-
QgsRectangle myProjectedLayerExtent;
270-
271-
if ( rendererContext.coordinateTransform() )
272-
{
273-
QgsDebugMsg( "coordinateTransform set -> project extents." );
274-
try
275-
{
276-
myProjectedViewExtent = rendererContext.coordinateTransform()->transformBoundingBox( rendererContext.extent() );
277-
}
278-
catch ( QgsCsException &cs )
279-
{
280-
QgsMessageLog::logMessage( tr( "Could not reproject view extent: %1" ).arg( cs.what() ), tr( "Raster" ) );
281-
myProjectedViewExtent.setMinimal();
282-
}
283-
284-
try
285-
{
286-
myProjectedLayerExtent = rendererContext.coordinateTransform()->transformBoundingBox( extent() );
287-
}
288-
catch ( QgsCsException &cs )
289-
{
290-
QgsMessageLog::logMessage( tr( "Could not reproject layer extent: %1" ).arg( cs.what() ), tr( "Raster" ) );
291-
myProjectedViewExtent.setMinimal();
292-
}
293-
}
294-
else
295-
{
296-
QgsDebugMsg( "coordinateTransform not set" );
297-
myProjectedViewExtent = rendererContext.extent();
298-
myProjectedLayerExtent = extent();
299-
}
300-
301-
QPainter* theQPainter = rendererContext.painter();
302-
303-
if ( !theQPainter )
304-
{
305-
return false;
306-
}
307-
308-
// clip raster extent to view extent
309-
QgsRectangle myRasterExtent = myProjectedViewExtent.intersect( &myProjectedLayerExtent );
310-
if ( myRasterExtent.isEmpty() )
311-
{
312-
QgsDebugMsg( "draw request outside view extent." );
313-
// nothing to do
314-
return true;
315-
}
316-
317-
QgsDebugMsg( "theViewExtent is " + rendererContext.extent().toString() );
318-
QgsDebugMsg( "myProjectedViewExtent is " + myProjectedViewExtent.toString() );
319-
QgsDebugMsg( "myProjectedLayerExtent is " + myProjectedLayerExtent.toString() );
320-
QgsDebugMsg( "myRasterExtent is " + myRasterExtent.toString() );
321-
322-
//
323-
// The first thing we do is set up the QgsRasterViewPort. This struct stores all the settings
324-
// relating to the size (in pixels and coordinate system units) of the raster part that is
325-
// in view in the map window. It also stores the origin.
326-
//
327-
//this is not a class level member because every time the user pans or zooms
328-
//the contents of the rasterViewPort will change
329-
QgsRasterViewPort *myRasterViewPort = new QgsRasterViewPort();
330-
331-
myRasterViewPort->mDrawnExtent = myRasterExtent;
332-
if ( rendererContext.coordinateTransform() )
333-
{
334-
myRasterViewPort->mSrcCRS = crs();
335-
myRasterViewPort->mDestCRS = rendererContext.coordinateTransform()->destCRS();
336-
}
337-
else
338-
{
339-
myRasterViewPort->mSrcCRS = QgsCoordinateReferenceSystem(); // will be invalid
340-
myRasterViewPort->mDestCRS = QgsCoordinateReferenceSystem(); // will be invalid
341-
}
342-
343-
// get dimensions of clipped raster image in device coordinate space (this is the size of the viewport)
344-
myRasterViewPort->mTopLeftPoint = theQgsMapToPixel.transform( myRasterExtent.xMinimum(), myRasterExtent.yMaximum() );
345-
myRasterViewPort->mBottomRightPoint = theQgsMapToPixel.transform( myRasterExtent.xMaximum(), myRasterExtent.yMinimum() );
346-
347-
// align to output device grid, i.e. floor/ceil to integers
348-
// TODO: this should only be done if paint device is raster - screen, image
349-
// for other devices (pdf) it can have floating point origin
350-
// we could use floating point for raster devices as well, but respecting the
351-
// output device grid should make it more effective as the resampling is done in
352-
// the provider anyway
353-
myRasterViewPort->mTopLeftPoint.setX( floor( myRasterViewPort->mTopLeftPoint.x() ) );
354-
myRasterViewPort->mTopLeftPoint.setY( floor( myRasterViewPort->mTopLeftPoint.y() ) );
355-
myRasterViewPort->mBottomRightPoint.setX( ceil( myRasterViewPort->mBottomRightPoint.x() ) );
356-
myRasterViewPort->mBottomRightPoint.setY( ceil( myRasterViewPort->mBottomRightPoint.y() ) );
357-
// recalc myRasterExtent to aligned values
358-
myRasterExtent.set(
359-
theQgsMapToPixel.toMapCoordinatesF( myRasterViewPort->mTopLeftPoint.x(),
360-
myRasterViewPort->mBottomRightPoint.y() ),
361-
theQgsMapToPixel.toMapCoordinatesF( myRasterViewPort->mBottomRightPoint.x(),
362-
myRasterViewPort->mTopLeftPoint.y() )
363-
);
364-
365-
//raster viewport top left / bottom right are already rounded to int
366-
myRasterViewPort->mWidth = static_cast<int>( myRasterViewPort->mBottomRightPoint.x() - myRasterViewPort->mTopLeftPoint.x() );
367-
myRasterViewPort->mHeight = static_cast<int>( myRasterViewPort->mBottomRightPoint.y() - myRasterViewPort->mTopLeftPoint.y() );
368-
369-
370-
//the drawable area can start to get very very large when you get down displaying 2x2 or smaller, this is becasue
371-
//theQgsMapToPixel.mapUnitsPerPixel() is less then 1,
372-
//so we will just get the pixel data and then render these special cases differently in paintImageToCanvas()
373-
374-
QgsDebugMsgLevel( QString( "mapUnitsPerPixel = %1" ).arg( theQgsMapToPixel.mapUnitsPerPixel() ), 3 );
375-
QgsDebugMsgLevel( QString( "mWidth = %1" ).arg( width() ), 3 );
376-
QgsDebugMsgLevel( QString( "mHeight = %1" ).arg( height() ), 3 );
377-
QgsDebugMsgLevel( QString( "myRasterExtent.xMinimum() = %1" ).arg( myRasterExtent.xMinimum() ), 3 );
378-
QgsDebugMsgLevel( QString( "myRasterExtent.xMaximum() = %1" ).arg( myRasterExtent.xMaximum() ), 3 );
379-
QgsDebugMsgLevel( QString( "myRasterExtent.yMinimum() = %1" ).arg( myRasterExtent.yMinimum() ), 3 );
380-
QgsDebugMsgLevel( QString( "myRasterExtent.yMaximum() = %1" ).arg( myRasterExtent.yMaximum() ), 3 );
381-
382-
QgsDebugMsgLevel( QString( "mTopLeftPoint.x() = %1" ).arg( myRasterViewPort->mTopLeftPoint.x() ), 3 );
383-
QgsDebugMsgLevel( QString( "mBottomRightPoint.x() = %1" ).arg( myRasterViewPort->mBottomRightPoint.x() ), 3 );
384-
QgsDebugMsgLevel( QString( "mTopLeftPoint.y() = %1" ).arg( myRasterViewPort->mTopLeftPoint.y() ), 3 );
385-
QgsDebugMsgLevel( QString( "mBottomRightPoint.y() = %1" ).arg( myRasterViewPort->mBottomRightPoint.y() ), 3 );
386-
387-
QgsDebugMsgLevel( QString( "mWidth = %1" ).arg( myRasterViewPort->mWidth ), 3 );
388-
QgsDebugMsgLevel( QString( "mHeight = %1" ).arg( myRasterViewPort->mHeight ), 3 );
389-
390-
// /\/\/\ - added to handle zoomed-in rasters
391-
392-
mLastViewPort = *myRasterViewPort;
393-
394-
// TODO: is it necessary? Probably WMS only?
395-
mDataProvider->setDpi( rendererContext.rasterScaleFactor() * 25.4 * rendererContext.scaleFactor() );
396-
397-
draw( theQPainter, myRasterViewPort, &theQgsMapToPixel );
398-
399-
delete myRasterViewPort;
400-
QgsDebugMsg( "exiting." );
401-
402-
return true;
403-
272+
QgsRasterLayerRenderer renderer( this, rendererContext );
273+
return renderer.render();
404274
}
405275

406276
void QgsRasterLayer::draw( QPainter * theQPainter,

‎src/core/raster/qgsrasterlayer.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,11 @@ class CORE_EXPORT QgsRasterLayer : public QgsMapLayer
288288
@note added in version 1.6*/
289289
virtual void reload();
290290

291+
/** Return new instance of QgsMapLayerRenderer that will be used for rendering of given context
292+
* @note added in 2.1
293+
*/
294+
virtual QgsMapLayerRenderer* createMapRenderer( QgsRenderContext& rendererContext );
295+
291296
/** \brief This is called when the view on the raster layer needs to be redrawn */
292297
bool draw( QgsRenderContext& rendererContext );
293298

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
#include "qgsrasterlayerrenderer.h"
2+
3+
#include "qgsmessagelog.h"
4+
#include "qgsrasterdrawer.h"
5+
#include "qgsrasteriterator.h"
6+
#include "qgsrasterlayer.h"
7+
8+
9+
QgsRasterLayerRenderer::QgsRasterLayerRenderer( QgsRasterLayer* layer, QgsRenderContext& rendererContext )
10+
: mRasterViewPort( 0 )
11+
, mPipe( 0 )
12+
{
13+
14+
mPainter = rendererContext.painter();
15+
const QgsMapToPixel& theQgsMapToPixel = rendererContext.mapToPixel();
16+
mMapToPixel = &theQgsMapToPixel;
17+
18+
QgsRectangle myProjectedViewExtent;
19+
QgsRectangle myProjectedLayerExtent;
20+
21+
if ( rendererContext.coordinateTransform() )
22+
{
23+
QgsDebugMsg( "coordinateTransform set -> project extents." );
24+
try
25+
{
26+
myProjectedViewExtent = rendererContext.coordinateTransform()->transformBoundingBox( rendererContext.extent() );
27+
}
28+
catch ( QgsCsException &cs )
29+
{
30+
QgsMessageLog::logMessage( QObject::tr( "Could not reproject view extent: %1" ).arg( cs.what() ), QObject::tr( "Raster" ) );
31+
myProjectedViewExtent.setMinimal();
32+
}
33+
34+
try
35+
{
36+
myProjectedLayerExtent = rendererContext.coordinateTransform()->transformBoundingBox( layer->extent() );
37+
}
38+
catch ( QgsCsException &cs )
39+
{
40+
QgsMessageLog::logMessage( QObject::tr( "Could not reproject layer extent: %1" ).arg( cs.what() ), QObject::tr( "Raster" ) );
41+
myProjectedViewExtent.setMinimal();
42+
}
43+
}
44+
else
45+
{
46+
QgsDebugMsg( "coordinateTransform not set" );
47+
myProjectedViewExtent = rendererContext.extent();
48+
myProjectedLayerExtent = layer->extent();
49+
}
50+
51+
// clip raster extent to view extent
52+
QgsRectangle myRasterExtent = myProjectedViewExtent.intersect( &myProjectedLayerExtent );
53+
if ( myRasterExtent.isEmpty() )
54+
{
55+
QgsDebugMsg( "draw request outside view extent." );
56+
// nothing to do
57+
return;
58+
}
59+
60+
QgsDebugMsg( "theViewExtent is " + rendererContext.extent().toString() );
61+
QgsDebugMsg( "myProjectedViewExtent is " + myProjectedViewExtent.toString() );
62+
QgsDebugMsg( "myProjectedLayerExtent is " + myProjectedLayerExtent.toString() );
63+
QgsDebugMsg( "myRasterExtent is " + myRasterExtent.toString() );
64+
65+
//
66+
// The first thing we do is set up the QgsRasterViewPort. This struct stores all the settings
67+
// relating to the size (in pixels and coordinate system units) of the raster part that is
68+
// in view in the map window. It also stores the origin.
69+
//
70+
//this is not a class level member because every time the user pans or zooms
71+
//the contents of the rasterViewPort will change
72+
mRasterViewPort = new QgsRasterViewPort();
73+
74+
mRasterViewPort->mDrawnExtent = myRasterExtent;
75+
if ( rendererContext.coordinateTransform() )
76+
{
77+
mRasterViewPort->mSrcCRS = layer->crs();
78+
mRasterViewPort->mDestCRS = rendererContext.coordinateTransform()->destCRS();
79+
}
80+
else
81+
{
82+
mRasterViewPort->mSrcCRS = QgsCoordinateReferenceSystem(); // will be invalid
83+
mRasterViewPort->mDestCRS = QgsCoordinateReferenceSystem(); // will be invalid
84+
}
85+
86+
// get dimensions of clipped raster image in device coordinate space (this is the size of the viewport)
87+
mRasterViewPort->mTopLeftPoint = theQgsMapToPixel.transform( myRasterExtent.xMinimum(), myRasterExtent.yMaximum() );
88+
mRasterViewPort->mBottomRightPoint = theQgsMapToPixel.transform( myRasterExtent.xMaximum(), myRasterExtent.yMinimum() );
89+
90+
// align to output device grid, i.e. floor/ceil to integers
91+
// TODO: this should only be done if paint device is raster - screen, image
92+
// for other devices (pdf) it can have floating point origin
93+
// we could use floating point for raster devices as well, but respecting the
94+
// output device grid should make it more effective as the resampling is done in
95+
// the provider anyway
96+
mRasterViewPort->mTopLeftPoint.setX( floor( mRasterViewPort->mTopLeftPoint.x() ) );
97+
mRasterViewPort->mTopLeftPoint.setY( floor( mRasterViewPort->mTopLeftPoint.y() ) );
98+
mRasterViewPort->mBottomRightPoint.setX( ceil( mRasterViewPort->mBottomRightPoint.x() ) );
99+
mRasterViewPort->mBottomRightPoint.setY( ceil( mRasterViewPort->mBottomRightPoint.y() ) );
100+
// recalc myRasterExtent to aligned values
101+
myRasterExtent.set(
102+
theQgsMapToPixel.toMapCoordinatesF( mRasterViewPort->mTopLeftPoint.x(),
103+
mRasterViewPort->mBottomRightPoint.y() ),
104+
theQgsMapToPixel.toMapCoordinatesF( mRasterViewPort->mBottomRightPoint.x(),
105+
mRasterViewPort->mTopLeftPoint.y() )
106+
);
107+
108+
//raster viewport top left / bottom right are already rounded to int
109+
mRasterViewPort->mWidth = static_cast<int>( mRasterViewPort->mBottomRightPoint.x() - mRasterViewPort->mTopLeftPoint.x() );
110+
mRasterViewPort->mHeight = static_cast<int>( mRasterViewPort->mBottomRightPoint.y() - mRasterViewPort->mTopLeftPoint.y() );
111+
112+
113+
//the drawable area can start to get very very large when you get down displaying 2x2 or smaller, this is becasue
114+
//theQgsMapToPixel.mapUnitsPerPixel() is less then 1,
115+
//so we will just get the pixel data and then render these special cases differently in paintImageToCanvas()
116+
117+
QgsDebugMsgLevel( QString( "mapUnitsPerPixel = %1" ).arg( theQgsMapToPixel.mapUnitsPerPixel() ), 3 );
118+
QgsDebugMsgLevel( QString( "mWidth = %1" ).arg( layer->width() ), 3 );
119+
QgsDebugMsgLevel( QString( "mHeight = %1" ).arg( layer->height() ), 3 );
120+
QgsDebugMsgLevel( QString( "myRasterExtent.xMinimum() = %1" ).arg( myRasterExtent.xMinimum() ), 3 );
121+
QgsDebugMsgLevel( QString( "myRasterExtent.xMaximum() = %1" ).arg( myRasterExtent.xMaximum() ), 3 );
122+
QgsDebugMsgLevel( QString( "myRasterExtent.yMinimum() = %1" ).arg( myRasterExtent.yMinimum() ), 3 );
123+
QgsDebugMsgLevel( QString( "myRasterExtent.yMaximum() = %1" ).arg( myRasterExtent.yMaximum() ), 3 );
124+
125+
QgsDebugMsgLevel( QString( "mTopLeftPoint.x() = %1" ).arg( mRasterViewPort->mTopLeftPoint.x() ), 3 );
126+
QgsDebugMsgLevel( QString( "mBottomRightPoint.x() = %1" ).arg( mRasterViewPort->mBottomRightPoint.x() ), 3 );
127+
QgsDebugMsgLevel( QString( "mTopLeftPoint.y() = %1" ).arg( mRasterViewPort->mTopLeftPoint.y() ), 3 );
128+
QgsDebugMsgLevel( QString( "mBottomRightPoint.y() = %1" ).arg( mRasterViewPort->mBottomRightPoint.y() ), 3 );
129+
130+
QgsDebugMsgLevel( QString( "mWidth = %1" ).arg( mRasterViewPort->mWidth ), 3 );
131+
QgsDebugMsgLevel( QString( "mHeight = %1" ).arg( mRasterViewPort->mHeight ), 3 );
132+
133+
// /\/\/\ - added to handle zoomed-in rasters
134+
135+
// TODO R->mLastViewPort = *mRasterViewPort;
136+
137+
// TODO: is it necessary? Probably WMS only?
138+
layer->dataProvider()->setDpi( rendererContext.rasterScaleFactor() * 25.4 * rendererContext.scaleFactor() );
139+
140+
141+
// copy the whole raster pipe!
142+
mPipe = new QgsRasterPipe( *layer->pipe() );
143+
}
144+
145+
QgsRasterLayerRenderer::~QgsRasterLayerRenderer()
146+
{
147+
delete mRasterViewPort;
148+
delete mPipe;
149+
}
150+
151+
bool QgsRasterLayerRenderer::render()
152+
{
153+
if ( !mRasterViewPort )
154+
return true; // outside of layer extent - nothing to do
155+
156+
//R->draw( mPainter, mRasterViewPort, &mMapToPixel );
157+
158+
QTime time;
159+
time.start();
160+
//
161+
//
162+
// The goal here is to make as many decisions as possible early on (outside of the rendering loop)
163+
// so that we can maximise performance of the rendering process. So now we check which drawing
164+
// procedure to use :
165+
//
166+
167+
QgsRasterProjector *projector = mPipe->projector();
168+
169+
// TODO add a method to interface to get provider and get provider
170+
// params in QgsRasterProjector
171+
if ( projector )
172+
{
173+
projector->setCRS( mRasterViewPort->mSrcCRS, mRasterViewPort->mDestCRS );
174+
}
175+
176+
// Drawer to pipe?
177+
QgsRasterIterator iterator( mPipe->last() );
178+
QgsRasterDrawer drawer( &iterator );
179+
drawer.draw( mPainter, mRasterViewPort, mMapToPixel );
180+
181+
QgsDebugMsg( QString( "total raster draw time (ms): %1" ).arg( time.elapsed(), 5 ) );
182+
183+
return true;
184+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#ifndef QGSRASTERLAYERRENDERER_H
2+
#define QGSRASTERLAYERRENDERER_H
3+
4+
#include "qgsmaplayerrenderer.h"
5+
6+
class QPainter;
7+
8+
class QgsMapToPixel;
9+
class QgsRasterLayer;
10+
class QgsRasterPipe;
11+
class QgsRasterViewPort;
12+
class QgsRenderContext;
13+
14+
class QgsRasterLayerRenderer : public QgsMapLayerRenderer
15+
{
16+
public:
17+
QgsRasterLayerRenderer( QgsRasterLayer* layer, QgsRenderContext& rendererContext );
18+
~QgsRasterLayerRenderer();
19+
20+
virtual bool render();
21+
22+
protected:
23+
24+
QPainter* mPainter;
25+
const QgsMapToPixel* mMapToPixel;
26+
QgsRasterViewPort* mRasterViewPort;
27+
28+
QgsRasterPipe* mPipe;
29+
};
30+
31+
#endif // QGSRASTERLAYERRENDERER_H

‎src/providers/gdal/qgsgdalprovider.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2363,7 +2363,6 @@ void QgsGdalProvider::initBaseDataset()
23632363
!crsFromWkt( GDALGetGCPProjection( mGdalDataset ) ) )
23642364
{
23652365
QgsDebugMsg( "No valid CRS identified" );
2366-
mCrs.validate();
23672366
}
23682367

23692368
//set up the coordinat transform - in the case of raster this is mainly used to convert

0 commit comments

Comments
 (0)
Please sign in to comment.