Skip to content
Permalink
Browse files

Merge pull request #567 from nyalldawson/overview

Add blending mode for overview frame, option to invert overview frame
  • Loading branch information
nyalldawson committed Apr 30, 2013
2 parents 6e6ff40 + 171973a commit 4c42cb1ea58e90f4a5b0cf843ea64f2dd229f0b6
@@ -278,6 +278,18 @@ class QgsComposerMap : QgsComposerItem
void setOverviewFrameMapSymbol( QgsFillSymbolV2* symbol /Transfer/ );
QgsFillSymbolV2* overviewFrameMapSymbol();

/* Returns the blending mode for the overview frame
@note added in version 1.9*/
const QPainter::CompositionMode overviewBlendMode() const;
/* Sets the blending mode for the overview frame
@note added in version 1.9*/
void setOverviewBlendMode( const QPainter::CompositionMode blendMode );

/**Sets flag if overview frame should be inverted
@note this function was added in version 1.9*/
void setOverviewInverted( bool inverted );
bool overviewInverted() const;

/**Sets mId to a number not yet used in the composition. mId is kept if it is not in use.
Usually, this function is called before adding the composer map to the composition*/
void assignFreeId();
@@ -299,6 +299,10 @@ void QgsComposerMapWidget::updateGuiElements()
//overview frame
int overviewMapFrameId = mComposerMap->overviewFrameMapId();
mOverviewFrameMapComboBox->setCurrentIndex( mOverviewFrameMapComboBox->findData( overviewMapFrameId ) );
//overview frame blending mode
mOverviewBlendModeComboBox->setBlendMode( mComposerMap->overviewBlendMode() );
//overview inverted
mOverviewInvertCheckbox->setChecked( mComposerMap->overviewInverted() );

//grid
if ( mComposerMap->gridEnabled() )
@@ -437,6 +441,8 @@ void QgsComposerMapWidget::blockAllSignals( bool b )
mFrameWidthSpinBox->blockSignals( b );
mOverviewFrameMapComboBox->blockSignals( b );
mOverviewFrameStyleButton->blockSignals( b );
mOverviewBlendModeComboBox->blockSignals( b );
mOverviewInvertCheckbox->blockSignals( b );
}

void QgsComposerMapWidget::on_mUpdatePreviewButton_clicked()
@@ -559,6 +565,23 @@ void QgsComposerMapWidget::on_mOverviewFrameStyleButton_clicked()
}
}

void QgsComposerMapWidget::on_mOverviewBlendModeComboBox_currentIndexChanged( int index )
{
Q_UNUSED( index );
if ( mComposerMap )
{
mComposerMap->setOverviewBlendMode( mOverviewBlendModeComboBox->blendMode() );
}

}
void QgsComposerMapWidget::on_mOverviewInvertCheckbox_toggled( bool state )
{
if ( mComposerMap )
{
mComposerMap->setOverviewInverted( state );
}
}

void QgsComposerMapWidget::on_mGridCheckBox_toggled( bool state )
{
if ( !mComposerMap )
@@ -44,6 +44,8 @@ class QgsComposerMapWidget: public QWidget, private Ui::QgsComposerMapWidgetBase
void on_mDrawCanvasItemsCheckBox_stateChanged( int state );
void on_mOverviewFrameMapComboBox_currentIndexChanged( const QString& text );
void on_mOverviewFrameStyleButton_clicked();
void on_mOverviewBlendModeComboBox_currentIndexChanged( int index );
void on_mOverviewInvertCheckbox_toggled( bool state );

void on_mXMinLineEdit_editingFinished();
void on_mXMaxLineEdit_editingFinished();
@@ -40,7 +40,7 @@

QgsComposerMap::QgsComposerMap( QgsComposition *composition, int x, int y, int width, int height )
: QgsComposerItem( x, y, width, height, composition ), mKeepLayerSet( false ),
mOverviewFrameMapId( -1 ), mGridEnabled( false ), mGridStyle( Solid ),
mOverviewFrameMapId( -1 ), mOverviewBlendMode( QPainter::CompositionMode_SourceOver ), mOverviewInverted( false ), mGridEnabled( false ), mGridStyle( Solid ),
mGridIntervalX( 0.0 ), mGridIntervalY( 0.0 ), mGridOffsetX( 0.0 ), mGridOffsetY( 0.0 ), mGridAnnotationPrecision( 3 ), mShowGridAnnotation( false ),
mLeftGridAnnotationPosition( OutsideMapFrame ), mRightGridAnnotationPosition( OutsideMapFrame ), mTopGridAnnotationPosition( OutsideMapFrame ),
mBottomGridAnnotationPosition( OutsideMapFrame ), mAnnotationFrameDistance( 1.0 ), mLeftGridAnnotationDirection( Horizontal ), mRightGridAnnotationDirection( Horizontal ),
@@ -82,7 +82,8 @@ QgsComposerMap::QgsComposerMap( QgsComposition *composition, int x, int y, int w
}

QgsComposerMap::QgsComposerMap( QgsComposition *composition )
: QgsComposerItem( 0, 0, 10, 10, composition ), mKeepLayerSet( false ), mOverviewFrameMapId( -1 ), mGridEnabled( false ), mGridStyle( Solid ),
: QgsComposerItem( 0, 0, 10, 10, composition ), mKeepLayerSet( false ), mOverviewFrameMapId( -1 ),
mOverviewBlendMode( QPainter::CompositionMode_SourceOver ), mOverviewInverted( false ), mGridEnabled( false ), mGridStyle( Solid ),
mGridIntervalX( 0.0 ), mGridIntervalY( 0.0 ), mGridOffsetX( 0.0 ), mGridOffsetY( 0.0 ), mGridAnnotationPrecision( 3 ), mShowGridAnnotation( false ),
mLeftGridAnnotationPosition( OutsideMapFrame ), mRightGridAnnotationPosition( OutsideMapFrame ), mTopGridAnnotationPosition( OutsideMapFrame ),
mBottomGridAnnotationPosition( OutsideMapFrame ), mAnnotationFrameDistance( 1.0 ), mLeftGridAnnotationDirection( Horizontal ), mRightGridAnnotationDirection( Horizontal ),
@@ -283,7 +284,7 @@ void QgsComposerMap::paint( QPainter* painter, const QStyleOptionGraphicsItem* i
//Qt 4.4.0 and 4.4.1 have problems with recursive paintings
//QgsComposerMap::cache() and QgsComposerMap::update() need to be called by
//client functions

//Background color is already included in cached image, so no need to draw

QgsRectangle requestRectangle;
@@ -682,10 +683,20 @@ bool QgsComposerMap::writeXML( QDomElement& elem, QDomDocument & doc ) const
//overview map frame
QDomElement overviewFrameElem = doc.createElement( "overviewFrame" );
overviewFrameElem.setAttribute( "overviewFrameMap", mOverviewFrameMapId );
overviewFrameElem.setAttribute( "overviewBlendMode", QgsMapRenderer::getBlendModeEnum( mOverviewBlendMode ) );
if ( mOverviewInverted )
{
overviewFrameElem.setAttribute( "overviewInverted", "true" );
}
else
{
overviewFrameElem.setAttribute( "overviewInverted", "false" );
}
QDomElement overviewFrameStyleElem = QgsSymbolLayerV2Utils::saveSymbol( QString(), mOverviewFrameMapSymbol, doc );
overviewFrameElem.appendChild( overviewFrameStyleElem );
composerMapElem.appendChild( overviewFrameElem );


//extent
QDomElement extentElem = doc.createElement( "Extent" );
extentElem.setAttribute( "xmin", QString::number( mExtent.xMinimum() ) );
@@ -779,6 +790,18 @@ bool QgsComposerMap::readXML( const QDomElement& itemElem, const QDomDocument& d
if ( !overviewFrameElem.isNull() )
{
setOverviewFrameMap( overviewFrameElem.attribute( "overviewFrameMap", "-1" ).toInt() );
setOverviewBlendMode( QgsMapRenderer::getCompositionMode(( QgsMapRenderer::BlendMode ) overviewFrameElem.attribute( "overviewBlendMode", "0" ).toUInt() ) );

QString overviewInvertedFlag = overviewFrameElem.attribute( "overviewInverted" );
if ( overviewInvertedFlag.compare( "true", Qt::CaseInsensitive ) == 0 )
{
setOverviewInverted( true );
}
else
{
setOverviewInverted( false );
}

QDomElement overviewFrameSymbolElem = overviewFrameElem.firstChildElement( "symbol" );
if ( !overviewFrameSymbolElem.isNull() )
{
@@ -1699,6 +1722,18 @@ void QgsComposerMap::setOverviewFrameMapSymbol( QgsFillSymbolV2* symbol )
mOverviewFrameMapSymbol = symbol;
}

void QgsComposerMap::setOverviewBlendMode( QPainter::CompositionMode blendMode )
{
mOverviewBlendMode = blendMode;
update();
}

void QgsComposerMap::setOverviewInverted( bool inverted )
{
mOverviewInverted = inverted;
update();
}

void QgsComposerMap::setGridLineSymbol( QgsLineSymbolV2* symbol )
{
delete mGridLineSymbol;
@@ -2043,17 +2078,38 @@ void QgsComposerMap::drawOverviewMapExtent( QPainter* p )
context.setRasterScaleFactor( mComposition->printResolution() / 25.4 );
}

QPolygonF polygon;
p->save();
p->setCompositionMode( mOverviewBlendMode );
mOverviewFrameMapSymbol->startRender( context );

//construct a polygon corresponding to the intersecting map extent
QPolygonF intersectPolygon;
double x = ( intersectRect.xMinimum() - thisExtent.xMinimum() ) / thisExtent.width() * rect().width();
double y = ( thisExtent.yMaximum() - intersectRect.yMaximum() ) / thisExtent.height() * rect().height();
double width = intersectRect.width() / thisExtent.width() * rect().width();
double height = intersectRect.height() / thisExtent.height() * rect().height();
polygon << QPointF( x, y ) << QPointF( x + width, y ) << QPointF( x + width, y + height ) << QPointF( x, y + height ) << QPointF( x, y );
intersectPolygon << QPointF( x, y ) << QPointF( x + width, y ) << QPointF( x + width, y + height ) << QPointF( x, y + height ) << QPointF( x, y );

QList<QPolygonF> rings; //empty list
mOverviewFrameMapSymbol->startRender( context );
mOverviewFrameMapSymbol->renderPolygon( polygon, &rings, 0, context );
if ( !mOverviewInverted )
{
//Render the intersecting map extent
mOverviewFrameMapSymbol->renderPolygon( intersectPolygon, &rings, 0, context );;
}
else
{
//We are inverting the overview frame (ie, shading outside the intersecting extent)
//Construct a polygon corresponding to the overview map extent
QPolygonF outerPolygon;
outerPolygon << QPointF( 0, 0 ) << QPointF( rect().width(), 0 ) << QPointF( rect().width(), rect().height() ) << QPointF( 0, rect().height() ) << QPointF( 0, 0 );

//Intersecting extent is an inner ring for the shaded area
rings.append( intersectPolygon );
mOverviewFrameMapSymbol->renderPolygon( outerPolygon, &rings, 0, context );
}

mOverviewFrameMapSymbol->stopRender( context );
p->restore();
}

void QgsComposerMap::createDefaultOverviewFrameSymbol()
@@ -312,6 +312,16 @@ class CORE_EXPORT QgsComposerMap : public QgsComposerItem
void setOverviewFrameMapSymbol( QgsFillSymbolV2* symbol );
QgsFillSymbolV2* overviewFrameMapSymbol() { return mOverviewFrameMapSymbol; }

/** Returns the overview's blending mode */
QPainter::CompositionMode overviewBlendMode() const {return mOverviewBlendMode;}
/** Sets the overview's blending mode*/
void setOverviewBlendMode( QPainter::CompositionMode blendMode );

/** Returns true if the overview frame is inverted */
bool overviewInverted() const {return mOverviewInverted;}
/** Sets the overview's inversion mode*/
void setOverviewInverted( bool inverted );

void setGridLineSymbol( QgsLineSymbolV2* symbol );
QgsLineSymbolV2* gridLineSymbol() { return mGridLineSymbol; }

@@ -380,6 +390,9 @@ class CORE_EXPORT QgsComposerMap : public QgsComposerItem
/**Drawing style for overview farme*/
QgsFillSymbolV2* mOverviewFrameMapSymbol;
QgsLineSymbolV2* mGridLineSymbol;
/**Blend mode for overview*/
QPainter::CompositionMode mOverviewBlendMode;
bool mOverviewInverted;

/**Establishes signal/slot connection for update in case of layer change*/
void connectUpdateSlot();
@@ -663,6 +663,23 @@
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="mOverviewBlendModeLabel">
<property name="text">
<string>Overview blending mode</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QgsBlendModeComboBox" name="mOverviewBlendModeComboBox"/>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="mOverviewInvertCheckbox">
<property name="text">
<string>Invert overview</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@@ -680,6 +697,11 @@
<header location="global">qgscollapsiblegroupbox.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QgsBlendModeComboBox</class>
<extends>QComboBox</extends>
<header>qgsblendmodecombobox.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
@@ -37,6 +37,8 @@ class TestQgsComposerMap: public QObject
void render(); //test if rendering of the composition with composr map is correct
void grid(); //test if grid and grid annotation works
void overviewMap(); //test if overview map frame works
void overviewMapBlending(); //test if blend modes with overview map frame works
void overviewMapInvert(); //test if invert of overview map frame works
void uniqueId(); //test if map id is adapted when doing copy paste
void zebraStyle(); //test zebra map border style

@@ -135,6 +137,40 @@ void TestQgsComposerMap::overviewMap()
QVERIFY( testResult );
}

void TestQgsComposerMap::overviewMapBlending()
{
QgsComposerMap* overviewMapBlend = new QgsComposerMap( mComposition, 20, 130, 70, 70 );
overviewMapBlend->setFrameEnabled( true );
mComposition->addComposerMap( overviewMapBlend );
mComposerMap->setNewExtent( QgsRectangle( 785462.375, 3341423.125, 789262.375, 3343323.125 ) ); //zoom in
overviewMapBlend->setNewExtent( QgsRectangle( 781662.375, 3339523.125, 793062.375, 3350923.125 ) );
overviewMapBlend->setOverviewFrameMap( mComposerMap->id() );
overviewMapBlend->setOverviewBlendMode( QPainter::CompositionMode_Multiply );

QgsCompositionChecker checker( "Composer map overview blending", mComposition, QString( QString( TEST_DATA_DIR ) + QDir::separator() +
"control_images" + QDir::separator() + "expected_composermap" + QDir::separator() + "composermap_landsat_overview_blend.png" ) );
bool testResult = checker.testComposition();
mComposition->removeComposerItem( overviewMapBlend );
QVERIFY( testResult );
}

void TestQgsComposerMap::overviewMapInvert()
{
QgsComposerMap* overviewMapInvert = new QgsComposerMap( mComposition, 20, 130, 70, 70 );
overviewMapInvert->setFrameEnabled( true );
mComposition->addComposerMap( overviewMapInvert );
mComposerMap->setNewExtent( QgsRectangle( 785462.375, 3341423.125, 789262.375, 3343323.125 ) ); //zoom in
overviewMapInvert->setNewExtent( QgsRectangle( 781662.375, 3339523.125, 793062.375, 3350923.125 ) );
overviewMapInvert->setOverviewFrameMap( mComposerMap->id() );
overviewMapInvert->setOverviewInverted( true );

QgsCompositionChecker checker( "Composer map overview invert", mComposition, QString( QString( TEST_DATA_DIR ) + QDir::separator() +
"control_images" + QDir::separator() + "expected_composermap" + QDir::separator() + "composermap_landsat_overview_invert.png" ) );
bool testResult = checker.testComposition();
mComposition->removeComposerItem( overviewMapInvert );
QVERIFY( testResult );
}

void TestQgsComposerMap::uniqueId()
{
QDomDocument doc;
@@ -16,6 +16,7 @@
from PyQt4.QtCore import (QStringList,
QFileInfo)
from PyQt4.QtXml import QDomDocument
from PyQt4.QtGui import QPainter

from qgis.core import (QgsComposerMap,
QgsRectangle,
@@ -126,7 +127,56 @@ def testOverviewMap(self):
self.mComposition.removeComposerItem(overviewMap)
assert myTestResult == True, myMessage


def testOverviewMapBlend(self):
overviewMap = QgsComposerMap(self.mComposition, 20, 130, 70, 70)
overviewMap.setFrameEnabled(True)
self.mComposition.addComposerMap(overviewMap)
# zoom in
myRectangle = QgsRectangle(785462.375, 3341423.125,
789262.375, 3343323.125)
self.mComposerMap.setNewExtent(myRectangle)
myRectangle2 = QgsRectangle(781662.375, 3339523.125,
793062.375, 3350923.125)
overviewMap.setNewExtent(myRectangle2)
overviewMap.setOverviewFrameMap(self.mComposerMap.id())
overviewMap.setOverviewBlendMode(QPainter.CompositionMode_Multiply)
checker = QgsCompositionChecker()
myPngPath = os.path.join(TEST_DATA_DIR,
'control_images',
'expected_composermap',
'composermap_landsat_overview_blend.png')
myTestResult, myMessage = checker.testComposition(
'Composer map overview blending',
self.mComposition,
myPngPath)
self.mComposition.removeComposerItem(overviewMap)
assert myTestResult == True, myMessage

def testOverviewMapInvert(self):
overviewMap = QgsComposerMap(self.mComposition, 20, 130, 70, 70)
overviewMap.setFrameEnabled(True)
self.mComposition.addComposerMap(overviewMap)
# zoom in
myRectangle = QgsRectangle(785462.375, 3341423.125,
789262.375, 3343323.125)
self.mComposerMap.setNewExtent(myRectangle)
myRectangle2 = QgsRectangle(781662.375, 3339523.125,
793062.375, 3350923.125)
overviewMap.setNewExtent(myRectangle2)
overviewMap.setOverviewFrameMap(self.mComposerMap.id())
overviewMap.setOverviewInverted(True)
checker = QgsCompositionChecker()
myPngPath = os.path.join(TEST_DATA_DIR,
'control_images',
'expected_composermap',
'composermap_landsat_overview_invert.png')
myTestResult, myMessage = checker.testComposition(
'Composer map overview inverted',
self.mComposition,
myPngPath)
self.mComposition.removeComposerItem(overviewMap)
assert myTestResult == True, myMessage

# Fails because addItemsFromXML has been commented out in sip
@expectedFailure
def testuniqueId(self):
Binary file not shown.
Binary file not shown.

0 comments on commit 4c42cb1

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