Skip to content

Commit

Permalink
Add blending mode for overview frame, option to invert overview frame
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Apr 29, 2013
1 parent 1959182 commit 171973a
Show file tree
Hide file tree
Showing 10 changed files with 222 additions and 8 deletions.
12 changes: 12 additions & 0 deletions python/core/composer/qgscomposermap.sip
Expand Up @@ -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();
Expand Down
23 changes: 23 additions & 0 deletions src/app/composer/qgscomposermapwidget.cpp
Expand Up @@ -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() )
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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 )
Expand Down
2 changes: 2 additions & 0 deletions src/app/composer/qgscomposermapwidget.h
Expand Up @@ -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();
Expand Down
70 changes: 63 additions & 7 deletions src/core/composer/qgscomposermap.cpp
Expand Up @@ -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 ),
Expand Down Expand Up @@ -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 ),
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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() ) );
Expand Down Expand Up @@ -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() )
{
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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()
Expand Down
13 changes: 13 additions & 0 deletions src/core/composer/qgscomposermap.h
Expand Up @@ -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; }

Expand Down Expand Up @@ -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();
Expand Down
22 changes: 22 additions & 0 deletions src/ui/qgscomposermapwidgetbase.ui
Expand Up @@ -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>
Expand All @@ -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/>
Expand Down
36 changes: 36 additions & 0 deletions tests/src/core/testqgscomposermap.cpp
Expand Up @@ -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

Expand Down Expand Up @@ -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;
Expand Down
52 changes: 51 additions & 1 deletion tests/src/python/test_qgscomposermap.py
Expand Up @@ -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,
Expand Down Expand Up @@ -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):
Expand Down
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 171973a

Please sign in to comment.