Skip to content
Permalink
Browse files

Initial support for rendering of rasters

  • Loading branch information
wonder-sk committed Nov 22, 2013
1 parent 5b6393f commit 4a554dbc4f6f72a0112a847d8811da69a0ef8662
@@ -6,6 +6,7 @@ SET(QGIS_CORE_SRCS
qgsmapsettings.cpp
qgsmaprendererjob.cpp
qgsvectorlayerrenderer.cpp
raster/qgsrasterlayerrenderer.cpp

gps/qgsgpsconnection.cpp
gps/qgsgpsconnectionregistry.cpp
@@ -2,6 +2,7 @@
#define QGSMAPLAYERRENDERER_H

#include <QList>
#include <QString>

/**
* Base class for utility classes that encapsulate information necessary
@@ -31,6 +31,7 @@ email : tim at linfiniti.com
#include "qgsrasterdrawer.h"
#include "qgsrasteriterator.h"
#include "qgsrasterlayer.h"
#include "qgsrasterlayerrenderer.h"
#include "qgsrasterprojector.h"
#include "qgsrasterrange.h"
#include "qgsrasterrendererregistry.h"
@@ -251,6 +252,11 @@ void QgsRasterLayer::reload()
}
}

QgsMapLayerRenderer *QgsRasterLayer::createMapRenderer( QgsRenderContext& rendererContext )
{
return new QgsRasterLayerRenderer( this, rendererContext );
}

bool QgsRasterLayer::draw( QgsRenderContext& rendererContext )
{
QgsDebugMsg( "entered. (renderContext)" );
@@ -263,144 +269,8 @@ bool QgsRasterLayer::draw( QgsRenderContext& rendererContext )
return false;
}

const QgsMapToPixel& theQgsMapToPixel = rendererContext.mapToPixel();

QgsRectangle myProjectedViewExtent;
QgsRectangle myProjectedLayerExtent;

if ( rendererContext.coordinateTransform() )
{
QgsDebugMsg( "coordinateTransform set -> project extents." );
try
{
myProjectedViewExtent = rendererContext.coordinateTransform()->transformBoundingBox( rendererContext.extent() );
}
catch ( QgsCsException &cs )
{
QgsMessageLog::logMessage( tr( "Could not reproject view extent: %1" ).arg( cs.what() ), tr( "Raster" ) );
myProjectedViewExtent.setMinimal();
}

try
{
myProjectedLayerExtent = rendererContext.coordinateTransform()->transformBoundingBox( extent() );
}
catch ( QgsCsException &cs )
{
QgsMessageLog::logMessage( tr( "Could not reproject layer extent: %1" ).arg( cs.what() ), tr( "Raster" ) );
myProjectedViewExtent.setMinimal();
}
}
else
{
QgsDebugMsg( "coordinateTransform not set" );
myProjectedViewExtent = rendererContext.extent();
myProjectedLayerExtent = extent();
}

QPainter* theQPainter = rendererContext.painter();

if ( !theQPainter )
{
return false;
}

// clip raster extent to view extent
QgsRectangle myRasterExtent = myProjectedViewExtent.intersect( &myProjectedLayerExtent );
if ( myRasterExtent.isEmpty() )
{
QgsDebugMsg( "draw request outside view extent." );
// nothing to do
return true;
}

QgsDebugMsg( "theViewExtent is " + rendererContext.extent().toString() );
QgsDebugMsg( "myProjectedViewExtent is " + myProjectedViewExtent.toString() );
QgsDebugMsg( "myProjectedLayerExtent is " + myProjectedLayerExtent.toString() );
QgsDebugMsg( "myRasterExtent is " + myRasterExtent.toString() );

//
// The first thing we do is set up the QgsRasterViewPort. This struct stores all the settings
// relating to the size (in pixels and coordinate system units) of the raster part that is
// in view in the map window. It also stores the origin.
//
//this is not a class level member because every time the user pans or zooms
//the contents of the rasterViewPort will change
QgsRasterViewPort *myRasterViewPort = new QgsRasterViewPort();

myRasterViewPort->mDrawnExtent = myRasterExtent;
if ( rendererContext.coordinateTransform() )
{
myRasterViewPort->mSrcCRS = crs();
myRasterViewPort->mDestCRS = rendererContext.coordinateTransform()->destCRS();
}
else
{
myRasterViewPort->mSrcCRS = QgsCoordinateReferenceSystem(); // will be invalid
myRasterViewPort->mDestCRS = QgsCoordinateReferenceSystem(); // will be invalid
}

// get dimensions of clipped raster image in device coordinate space (this is the size of the viewport)
myRasterViewPort->mTopLeftPoint = theQgsMapToPixel.transform( myRasterExtent.xMinimum(), myRasterExtent.yMaximum() );
myRasterViewPort->mBottomRightPoint = theQgsMapToPixel.transform( myRasterExtent.xMaximum(), myRasterExtent.yMinimum() );

// align to output device grid, i.e. floor/ceil to integers
// TODO: this should only be done if paint device is raster - screen, image
// for other devices (pdf) it can have floating point origin
// we could use floating point for raster devices as well, but respecting the
// output device grid should make it more effective as the resampling is done in
// the provider anyway
myRasterViewPort->mTopLeftPoint.setX( floor( myRasterViewPort->mTopLeftPoint.x() ) );
myRasterViewPort->mTopLeftPoint.setY( floor( myRasterViewPort->mTopLeftPoint.y() ) );
myRasterViewPort->mBottomRightPoint.setX( ceil( myRasterViewPort->mBottomRightPoint.x() ) );
myRasterViewPort->mBottomRightPoint.setY( ceil( myRasterViewPort->mBottomRightPoint.y() ) );
// recalc myRasterExtent to aligned values
myRasterExtent.set(
theQgsMapToPixel.toMapCoordinatesF( myRasterViewPort->mTopLeftPoint.x(),
myRasterViewPort->mBottomRightPoint.y() ),
theQgsMapToPixel.toMapCoordinatesF( myRasterViewPort->mBottomRightPoint.x(),
myRasterViewPort->mTopLeftPoint.y() )
);

//raster viewport top left / bottom right are already rounded to int
myRasterViewPort->mWidth = static_cast<int>( myRasterViewPort->mBottomRightPoint.x() - myRasterViewPort->mTopLeftPoint.x() );
myRasterViewPort->mHeight = static_cast<int>( myRasterViewPort->mBottomRightPoint.y() - myRasterViewPort->mTopLeftPoint.y() );


//the drawable area can start to get very very large when you get down displaying 2x2 or smaller, this is becasue
//theQgsMapToPixel.mapUnitsPerPixel() is less then 1,
//so we will just get the pixel data and then render these special cases differently in paintImageToCanvas()

QgsDebugMsgLevel( QString( "mapUnitsPerPixel = %1" ).arg( theQgsMapToPixel.mapUnitsPerPixel() ), 3 );
QgsDebugMsgLevel( QString( "mWidth = %1" ).arg( width() ), 3 );
QgsDebugMsgLevel( QString( "mHeight = %1" ).arg( height() ), 3 );
QgsDebugMsgLevel( QString( "myRasterExtent.xMinimum() = %1" ).arg( myRasterExtent.xMinimum() ), 3 );
QgsDebugMsgLevel( QString( "myRasterExtent.xMaximum() = %1" ).arg( myRasterExtent.xMaximum() ), 3 );
QgsDebugMsgLevel( QString( "myRasterExtent.yMinimum() = %1" ).arg( myRasterExtent.yMinimum() ), 3 );
QgsDebugMsgLevel( QString( "myRasterExtent.yMaximum() = %1" ).arg( myRasterExtent.yMaximum() ), 3 );

QgsDebugMsgLevel( QString( "mTopLeftPoint.x() = %1" ).arg( myRasterViewPort->mTopLeftPoint.x() ), 3 );
QgsDebugMsgLevel( QString( "mBottomRightPoint.x() = %1" ).arg( myRasterViewPort->mBottomRightPoint.x() ), 3 );
QgsDebugMsgLevel( QString( "mTopLeftPoint.y() = %1" ).arg( myRasterViewPort->mTopLeftPoint.y() ), 3 );
QgsDebugMsgLevel( QString( "mBottomRightPoint.y() = %1" ).arg( myRasterViewPort->mBottomRightPoint.y() ), 3 );

QgsDebugMsgLevel( QString( "mWidth = %1" ).arg( myRasterViewPort->mWidth ), 3 );
QgsDebugMsgLevel( QString( "mHeight = %1" ).arg( myRasterViewPort->mHeight ), 3 );

// /\/\/\ - added to handle zoomed-in rasters

mLastViewPort = *myRasterViewPort;

// TODO: is it necessary? Probably WMS only?
mDataProvider->setDpi( rendererContext.rasterScaleFactor() * 25.4 * rendererContext.scaleFactor() );

draw( theQPainter, myRasterViewPort, &theQgsMapToPixel );

delete myRasterViewPort;
QgsDebugMsg( "exiting." );

return true;

QgsRasterLayerRenderer renderer( this, rendererContext );
return renderer.render();
}

void QgsRasterLayer::draw( QPainter * theQPainter,
@@ -288,6 +288,11 @@ class CORE_EXPORT QgsRasterLayer : public QgsMapLayer
@note added in version 1.6*/
virtual void reload();

/** Return new instance of QgsMapLayerRenderer that will be used for rendering of given context
* @note added in 2.1
*/
virtual QgsMapLayerRenderer* createMapRenderer( QgsRenderContext& rendererContext );

/** \brief This is called when the view on the raster layer needs to be redrawn */
bool draw( QgsRenderContext& rendererContext );

@@ -0,0 +1,184 @@
#include "qgsrasterlayerrenderer.h"

#include "qgsmessagelog.h"
#include "qgsrasterdrawer.h"
#include "qgsrasteriterator.h"
#include "qgsrasterlayer.h"


QgsRasterLayerRenderer::QgsRasterLayerRenderer( QgsRasterLayer* layer, QgsRenderContext& rendererContext )
: mRasterViewPort( 0 )
, mPipe( 0 )
{

mPainter = rendererContext.painter();
const QgsMapToPixel& theQgsMapToPixel = rendererContext.mapToPixel();
mMapToPixel = &theQgsMapToPixel;

QgsRectangle myProjectedViewExtent;
QgsRectangle myProjectedLayerExtent;

if ( rendererContext.coordinateTransform() )
{
QgsDebugMsg( "coordinateTransform set -> project extents." );
try
{
myProjectedViewExtent = rendererContext.coordinateTransform()->transformBoundingBox( rendererContext.extent() );
}
catch ( QgsCsException &cs )
{
QgsMessageLog::logMessage( QObject::tr( "Could not reproject view extent: %1" ).arg( cs.what() ), QObject::tr( "Raster" ) );
myProjectedViewExtent.setMinimal();
}

try
{
myProjectedLayerExtent = rendererContext.coordinateTransform()->transformBoundingBox( layer->extent() );
}
catch ( QgsCsException &cs )
{
QgsMessageLog::logMessage( QObject::tr( "Could not reproject layer extent: %1" ).arg( cs.what() ), QObject::tr( "Raster" ) );
myProjectedViewExtent.setMinimal();
}
}
else
{
QgsDebugMsg( "coordinateTransform not set" );
myProjectedViewExtent = rendererContext.extent();
myProjectedLayerExtent = layer->extent();
}

// clip raster extent to view extent
QgsRectangle myRasterExtent = myProjectedViewExtent.intersect( &myProjectedLayerExtent );
if ( myRasterExtent.isEmpty() )
{
QgsDebugMsg( "draw request outside view extent." );
// nothing to do
return;
}

QgsDebugMsg( "theViewExtent is " + rendererContext.extent().toString() );
QgsDebugMsg( "myProjectedViewExtent is " + myProjectedViewExtent.toString() );
QgsDebugMsg( "myProjectedLayerExtent is " + myProjectedLayerExtent.toString() );
QgsDebugMsg( "myRasterExtent is " + myRasterExtent.toString() );

//
// The first thing we do is set up the QgsRasterViewPort. This struct stores all the settings
// relating to the size (in pixels and coordinate system units) of the raster part that is
// in view in the map window. It also stores the origin.
//
//this is not a class level member because every time the user pans or zooms
//the contents of the rasterViewPort will change
mRasterViewPort = new QgsRasterViewPort();

mRasterViewPort->mDrawnExtent = myRasterExtent;
if ( rendererContext.coordinateTransform() )
{
mRasterViewPort->mSrcCRS = layer->crs();
mRasterViewPort->mDestCRS = rendererContext.coordinateTransform()->destCRS();
}
else
{
mRasterViewPort->mSrcCRS = QgsCoordinateReferenceSystem(); // will be invalid
mRasterViewPort->mDestCRS = QgsCoordinateReferenceSystem(); // will be invalid
}

// get dimensions of clipped raster image in device coordinate space (this is the size of the viewport)
mRasterViewPort->mTopLeftPoint = theQgsMapToPixel.transform( myRasterExtent.xMinimum(), myRasterExtent.yMaximum() );
mRasterViewPort->mBottomRightPoint = theQgsMapToPixel.transform( myRasterExtent.xMaximum(), myRasterExtent.yMinimum() );

// align to output device grid, i.e. floor/ceil to integers
// TODO: this should only be done if paint device is raster - screen, image
// for other devices (pdf) it can have floating point origin
// we could use floating point for raster devices as well, but respecting the
// output device grid should make it more effective as the resampling is done in
// the provider anyway
mRasterViewPort->mTopLeftPoint.setX( floor( mRasterViewPort->mTopLeftPoint.x() ) );
mRasterViewPort->mTopLeftPoint.setY( floor( mRasterViewPort->mTopLeftPoint.y() ) );
mRasterViewPort->mBottomRightPoint.setX( ceil( mRasterViewPort->mBottomRightPoint.x() ) );
mRasterViewPort->mBottomRightPoint.setY( ceil( mRasterViewPort->mBottomRightPoint.y() ) );
// recalc myRasterExtent to aligned values
myRasterExtent.set(
theQgsMapToPixel.toMapCoordinatesF( mRasterViewPort->mTopLeftPoint.x(),
mRasterViewPort->mBottomRightPoint.y() ),
theQgsMapToPixel.toMapCoordinatesF( mRasterViewPort->mBottomRightPoint.x(),
mRasterViewPort->mTopLeftPoint.y() )
);

//raster viewport top left / bottom right are already rounded to int
mRasterViewPort->mWidth = static_cast<int>( mRasterViewPort->mBottomRightPoint.x() - mRasterViewPort->mTopLeftPoint.x() );
mRasterViewPort->mHeight = static_cast<int>( mRasterViewPort->mBottomRightPoint.y() - mRasterViewPort->mTopLeftPoint.y() );


//the drawable area can start to get very very large when you get down displaying 2x2 or smaller, this is becasue
//theQgsMapToPixel.mapUnitsPerPixel() is less then 1,
//so we will just get the pixel data and then render these special cases differently in paintImageToCanvas()

QgsDebugMsgLevel( QString( "mapUnitsPerPixel = %1" ).arg( theQgsMapToPixel.mapUnitsPerPixel() ), 3 );
QgsDebugMsgLevel( QString( "mWidth = %1" ).arg( layer->width() ), 3 );
QgsDebugMsgLevel( QString( "mHeight = %1" ).arg( layer->height() ), 3 );
QgsDebugMsgLevel( QString( "myRasterExtent.xMinimum() = %1" ).arg( myRasterExtent.xMinimum() ), 3 );
QgsDebugMsgLevel( QString( "myRasterExtent.xMaximum() = %1" ).arg( myRasterExtent.xMaximum() ), 3 );
QgsDebugMsgLevel( QString( "myRasterExtent.yMinimum() = %1" ).arg( myRasterExtent.yMinimum() ), 3 );
QgsDebugMsgLevel( QString( "myRasterExtent.yMaximum() = %1" ).arg( myRasterExtent.yMaximum() ), 3 );

QgsDebugMsgLevel( QString( "mTopLeftPoint.x() = %1" ).arg( mRasterViewPort->mTopLeftPoint.x() ), 3 );
QgsDebugMsgLevel( QString( "mBottomRightPoint.x() = %1" ).arg( mRasterViewPort->mBottomRightPoint.x() ), 3 );
QgsDebugMsgLevel( QString( "mTopLeftPoint.y() = %1" ).arg( mRasterViewPort->mTopLeftPoint.y() ), 3 );
QgsDebugMsgLevel( QString( "mBottomRightPoint.y() = %1" ).arg( mRasterViewPort->mBottomRightPoint.y() ), 3 );

QgsDebugMsgLevel( QString( "mWidth = %1" ).arg( mRasterViewPort->mWidth ), 3 );
QgsDebugMsgLevel( QString( "mHeight = %1" ).arg( mRasterViewPort->mHeight ), 3 );

// /\/\/\ - added to handle zoomed-in rasters

// TODO R->mLastViewPort = *mRasterViewPort;

// TODO: is it necessary? Probably WMS only?
layer->dataProvider()->setDpi( rendererContext.rasterScaleFactor() * 25.4 * rendererContext.scaleFactor() );


// copy the whole raster pipe!
mPipe = new QgsRasterPipe( *layer->pipe() );
}

QgsRasterLayerRenderer::~QgsRasterLayerRenderer()
{
delete mRasterViewPort;
delete mPipe;
}

bool QgsRasterLayerRenderer::render()
{
if ( !mRasterViewPort )
return true; // outside of layer extent - nothing to do

//R->draw( mPainter, mRasterViewPort, &mMapToPixel );

QTime time;
time.start();
//
//
// The goal here is to make as many decisions as possible early on (outside of the rendering loop)
// so that we can maximise performance of the rendering process. So now we check which drawing
// procedure to use :
//

QgsRasterProjector *projector = mPipe->projector();

// TODO add a method to interface to get provider and get provider
// params in QgsRasterProjector
if ( projector )
{
projector->setCRS( mRasterViewPort->mSrcCRS, mRasterViewPort->mDestCRS );
}

// Drawer to pipe?
QgsRasterIterator iterator( mPipe->last() );
QgsRasterDrawer drawer( &iterator );
drawer.draw( mPainter, mRasterViewPort, mMapToPixel );

QgsDebugMsg( QString( "total raster draw time (ms): %1" ).arg( time.elapsed(), 5 ) );

return true;
}

0 comments on commit 4a554db

Please sign in to comment.
You can’t perform that action at this time.