Skip to content

Commit 171973a

Browse files
committed
Add blending mode for overview frame, option to invert overview frame
1 parent 1959182 commit 171973a

File tree

10 files changed

+222
-8
lines changed

10 files changed

+222
-8
lines changed

python/core/composer/qgscomposermap.sip

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,18 @@ class QgsComposerMap : QgsComposerItem
278278
void setOverviewFrameMapSymbol( QgsFillSymbolV2* symbol /Transfer/ );
279279
QgsFillSymbolV2* overviewFrameMapSymbol();
280280

281+
/* Returns the blending mode for the overview frame
282+
@note added in version 1.9*/
283+
const QPainter::CompositionMode overviewBlendMode() const;
284+
/* Sets the blending mode for the overview frame
285+
@note added in version 1.9*/
286+
void setOverviewBlendMode( const QPainter::CompositionMode blendMode );
287+
288+
/**Sets flag if overview frame should be inverted
289+
@note this function was added in version 1.9*/
290+
void setOverviewInverted( bool inverted );
291+
bool overviewInverted() const;
292+
281293
/**Sets mId to a number not yet used in the composition. mId is kept if it is not in use.
282294
Usually, this function is called before adding the composer map to the composition*/
283295
void assignFreeId();

src/app/composer/qgscomposermapwidget.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,10 @@ void QgsComposerMapWidget::updateGuiElements()
299299
//overview frame
300300
int overviewMapFrameId = mComposerMap->overviewFrameMapId();
301301
mOverviewFrameMapComboBox->setCurrentIndex( mOverviewFrameMapComboBox->findData( overviewMapFrameId ) );
302+
//overview frame blending mode
303+
mOverviewBlendModeComboBox->setBlendMode( mComposerMap->overviewBlendMode() );
304+
//overview inverted
305+
mOverviewInvertCheckbox->setChecked( mComposerMap->overviewInverted() );
302306

303307
//grid
304308
if ( mComposerMap->gridEnabled() )
@@ -437,6 +441,8 @@ void QgsComposerMapWidget::blockAllSignals( bool b )
437441
mFrameWidthSpinBox->blockSignals( b );
438442
mOverviewFrameMapComboBox->blockSignals( b );
439443
mOverviewFrameStyleButton->blockSignals( b );
444+
mOverviewBlendModeComboBox->blockSignals( b );
445+
mOverviewInvertCheckbox->blockSignals( b );
440446
}
441447

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

568+
void QgsComposerMapWidget::on_mOverviewBlendModeComboBox_currentIndexChanged( int index )
569+
{
570+
Q_UNUSED( index );
571+
if ( mComposerMap )
572+
{
573+
mComposerMap->setOverviewBlendMode( mOverviewBlendModeComboBox->blendMode() );
574+
}
575+
576+
}
577+
void QgsComposerMapWidget::on_mOverviewInvertCheckbox_toggled( bool state )
578+
{
579+
if ( mComposerMap )
580+
{
581+
mComposerMap->setOverviewInverted( state );
582+
}
583+
}
584+
562585
void QgsComposerMapWidget::on_mGridCheckBox_toggled( bool state )
563586
{
564587
if ( !mComposerMap )

src/app/composer/qgscomposermapwidget.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ class QgsComposerMapWidget: public QWidget, private Ui::QgsComposerMapWidgetBase
4444
void on_mDrawCanvasItemsCheckBox_stateChanged( int state );
4545
void on_mOverviewFrameMapComboBox_currentIndexChanged( const QString& text );
4646
void on_mOverviewFrameStyleButton_clicked();
47+
void on_mOverviewBlendModeComboBox_currentIndexChanged( int index );
48+
void on_mOverviewInvertCheckbox_toggled( bool state );
4749

4850
void on_mXMinLineEdit_editingFinished();
4951
void on_mXMaxLineEdit_editingFinished();

src/core/composer/qgscomposermap.cpp

Lines changed: 63 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040

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

8484
QgsComposerMap::QgsComposerMap( QgsComposition *composition )
85-
: QgsComposerItem( 0, 0, 10, 10, composition ), mKeepLayerSet( false ), mOverviewFrameMapId( -1 ), mGridEnabled( false ), mGridStyle( Solid ),
85+
: QgsComposerItem( 0, 0, 10, 10, composition ), mKeepLayerSet( false ), mOverviewFrameMapId( -1 ),
86+
mOverviewBlendMode( QPainter::CompositionMode_SourceOver ), mOverviewInverted( false ), mGridEnabled( false ), mGridStyle( Solid ),
8687
mGridIntervalX( 0.0 ), mGridIntervalY( 0.0 ), mGridOffsetX( 0.0 ), mGridOffsetY( 0.0 ), mGridAnnotationPrecision( 3 ), mShowGridAnnotation( false ),
8788
mLeftGridAnnotationPosition( OutsideMapFrame ), mRightGridAnnotationPosition( OutsideMapFrame ), mTopGridAnnotationPosition( OutsideMapFrame ),
8889
mBottomGridAnnotationPosition( OutsideMapFrame ), mAnnotationFrameDistance( 1.0 ), mLeftGridAnnotationDirection( Horizontal ), mRightGridAnnotationDirection( Horizontal ),
@@ -283,7 +284,7 @@ void QgsComposerMap::paint( QPainter* painter, const QStyleOptionGraphicsItem* i
283284
//Qt 4.4.0 and 4.4.1 have problems with recursive paintings
284285
//QgsComposerMap::cache() and QgsComposerMap::update() need to be called by
285286
//client functions
286-
287+
287288
//Background color is already included in cached image, so no need to draw
288289

289290
QgsRectangle requestRectangle;
@@ -682,10 +683,20 @@ bool QgsComposerMap::writeXML( QDomElement& elem, QDomDocument & doc ) const
682683
//overview map frame
683684
QDomElement overviewFrameElem = doc.createElement( "overviewFrame" );
684685
overviewFrameElem.setAttribute( "overviewFrameMap", mOverviewFrameMapId );
686+
overviewFrameElem.setAttribute( "overviewBlendMode", QgsMapRenderer::getBlendModeEnum( mOverviewBlendMode ) );
687+
if ( mOverviewInverted )
688+
{
689+
overviewFrameElem.setAttribute( "overviewInverted", "true" );
690+
}
691+
else
692+
{
693+
overviewFrameElem.setAttribute( "overviewInverted", "false" );
694+
}
685695
QDomElement overviewFrameStyleElem = QgsSymbolLayerV2Utils::saveSymbol( QString(), mOverviewFrameMapSymbol, doc );
686696
overviewFrameElem.appendChild( overviewFrameStyleElem );
687697
composerMapElem.appendChild( overviewFrameElem );
688698

699+
689700
//extent
690701
QDomElement extentElem = doc.createElement( "Extent" );
691702
extentElem.setAttribute( "xmin", QString::number( mExtent.xMinimum() ) );
@@ -779,6 +790,18 @@ bool QgsComposerMap::readXML( const QDomElement& itemElem, const QDomDocument& d
779790
if ( !overviewFrameElem.isNull() )
780791
{
781792
setOverviewFrameMap( overviewFrameElem.attribute( "overviewFrameMap", "-1" ).toInt() );
793+
setOverviewBlendMode( QgsMapRenderer::getCompositionMode(( QgsMapRenderer::BlendMode ) overviewFrameElem.attribute( "overviewBlendMode", "0" ).toUInt() ) );
794+
795+
QString overviewInvertedFlag = overviewFrameElem.attribute( "overviewInverted" );
796+
if ( overviewInvertedFlag.compare( "true", Qt::CaseInsensitive ) == 0 )
797+
{
798+
setOverviewInverted( true );
799+
}
800+
else
801+
{
802+
setOverviewInverted( false );
803+
}
804+
782805
QDomElement overviewFrameSymbolElem = overviewFrameElem.firstChildElement( "symbol" );
783806
if ( !overviewFrameSymbolElem.isNull() )
784807
{
@@ -1699,6 +1722,18 @@ void QgsComposerMap::setOverviewFrameMapSymbol( QgsFillSymbolV2* symbol )
16991722
mOverviewFrameMapSymbol = symbol;
17001723
}
17011724

1725+
void QgsComposerMap::setOverviewBlendMode( QPainter::CompositionMode blendMode )
1726+
{
1727+
mOverviewBlendMode = blendMode;
1728+
update();
1729+
}
1730+
1731+
void QgsComposerMap::setOverviewInverted( bool inverted )
1732+
{
1733+
mOverviewInverted = inverted;
1734+
update();
1735+
}
1736+
17021737
void QgsComposerMap::setGridLineSymbol( QgsLineSymbolV2* symbol )
17031738
{
17041739
delete mGridLineSymbol;
@@ -2043,17 +2078,38 @@ void QgsComposerMap::drawOverviewMapExtent( QPainter* p )
20432078
context.setRasterScaleFactor( mComposition->printResolution() / 25.4 );
20442079
}
20452080

2046-
QPolygonF polygon;
2081+
p->save();
2082+
p->setCompositionMode( mOverviewBlendMode );
2083+
mOverviewFrameMapSymbol->startRender( context );
2084+
2085+
//construct a polygon corresponding to the intersecting map extent
2086+
QPolygonF intersectPolygon;
20472087
double x = ( intersectRect.xMinimum() - thisExtent.xMinimum() ) / thisExtent.width() * rect().width();
20482088
double y = ( thisExtent.yMaximum() - intersectRect.yMaximum() ) / thisExtent.height() * rect().height();
20492089
double width = intersectRect.width() / thisExtent.width() * rect().width();
20502090
double height = intersectRect.height() / thisExtent.height() * rect().height();
2051-
polygon << QPointF( x, y ) << QPointF( x + width, y ) << QPointF( x + width, y + height ) << QPointF( x, y + height ) << QPointF( x, y );
2091+
intersectPolygon << QPointF( x, y ) << QPointF( x + width, y ) << QPointF( x + width, y + height ) << QPointF( x, y + height ) << QPointF( x, y );
20522092

20532093
QList<QPolygonF> rings; //empty list
2054-
mOverviewFrameMapSymbol->startRender( context );
2055-
mOverviewFrameMapSymbol->renderPolygon( polygon, &rings, 0, context );
2094+
if ( !mOverviewInverted )
2095+
{
2096+
//Render the intersecting map extent
2097+
mOverviewFrameMapSymbol->renderPolygon( intersectPolygon, &rings, 0, context );;
2098+
}
2099+
else
2100+
{
2101+
//We are inverting the overview frame (ie, shading outside the intersecting extent)
2102+
//Construct a polygon corresponding to the overview map extent
2103+
QPolygonF outerPolygon;
2104+
outerPolygon << QPointF( 0, 0 ) << QPointF( rect().width(), 0 ) << QPointF( rect().width(), rect().height() ) << QPointF( 0, rect().height() ) << QPointF( 0, 0 );
2105+
2106+
//Intersecting extent is an inner ring for the shaded area
2107+
rings.append( intersectPolygon );
2108+
mOverviewFrameMapSymbol->renderPolygon( outerPolygon, &rings, 0, context );
2109+
}
2110+
20562111
mOverviewFrameMapSymbol->stopRender( context );
2112+
p->restore();
20572113
}
20582114

20592115
void QgsComposerMap::createDefaultOverviewFrameSymbol()

src/core/composer/qgscomposermap.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,16 @@ class CORE_EXPORT QgsComposerMap : public QgsComposerItem
312312
void setOverviewFrameMapSymbol( QgsFillSymbolV2* symbol );
313313
QgsFillSymbolV2* overviewFrameMapSymbol() { return mOverviewFrameMapSymbol; }
314314

315+
/** Returns the overview's blending mode */
316+
QPainter::CompositionMode overviewBlendMode() const {return mOverviewBlendMode;}
317+
/** Sets the overview's blending mode*/
318+
void setOverviewBlendMode( QPainter::CompositionMode blendMode );
319+
320+
/** Returns true if the overview frame is inverted */
321+
bool overviewInverted() const {return mOverviewInverted;}
322+
/** Sets the overview's inversion mode*/
323+
void setOverviewInverted( bool inverted );
324+
315325
void setGridLineSymbol( QgsLineSymbolV2* symbol );
316326
QgsLineSymbolV2* gridLineSymbol() { return mGridLineSymbol; }
317327

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

384397
/**Establishes signal/slot connection for update in case of layer change*/
385398
void connectUpdateSlot();

src/ui/qgscomposermapwidgetbase.ui

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -663,6 +663,23 @@
663663
</property>
664664
</widget>
665665
</item>
666+
<item row="2" column="0">
667+
<widget class="QLabel" name="mOverviewBlendModeLabel">
668+
<property name="text">
669+
<string>Overview blending mode</string>
670+
</property>
671+
</widget>
672+
</item>
673+
<item row="2" column="1">
674+
<widget class="QgsBlendModeComboBox" name="mOverviewBlendModeComboBox"/>
675+
</item>
676+
<item row="3" column="0">
677+
<widget class="QCheckBox" name="mOverviewInvertCheckbox">
678+
<property name="text">
679+
<string>Invert overview</string>
680+
</property>
681+
</widget>
682+
</item>
666683
</layout>
667684
</widget>
668685
</item>
@@ -680,6 +697,11 @@
680697
<header location="global">qgscollapsiblegroupbox.h</header>
681698
<container>1</container>
682699
</customwidget>
700+
<customwidget>
701+
<class>QgsBlendModeComboBox</class>
702+
<extends>QComboBox</extends>
703+
<header>qgsblendmodecombobox.h</header>
704+
</customwidget>
683705
</customwidgets>
684706
<resources/>
685707
<connections/>

tests/src/core/testqgscomposermap.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ class TestQgsComposerMap: public QObject
3737
void render(); //test if rendering of the composition with composr map is correct
3838
void grid(); //test if grid and grid annotation works
3939
void overviewMap(); //test if overview map frame works
40+
void overviewMapBlending(); //test if blend modes with overview map frame works
41+
void overviewMapInvert(); //test if invert of overview map frame works
4042
void uniqueId(); //test if map id is adapted when doing copy paste
4143
void zebraStyle(); //test zebra map border style
4244

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

140+
void TestQgsComposerMap::overviewMapBlending()
141+
{
142+
QgsComposerMap* overviewMapBlend = new QgsComposerMap( mComposition, 20, 130, 70, 70 );
143+
overviewMapBlend->setFrameEnabled( true );
144+
mComposition->addComposerMap( overviewMapBlend );
145+
mComposerMap->setNewExtent( QgsRectangle( 785462.375, 3341423.125, 789262.375, 3343323.125 ) ); //zoom in
146+
overviewMapBlend->setNewExtent( QgsRectangle( 781662.375, 3339523.125, 793062.375, 3350923.125 ) );
147+
overviewMapBlend->setOverviewFrameMap( mComposerMap->id() );
148+
overviewMapBlend->setOverviewBlendMode( QPainter::CompositionMode_Multiply );
149+
150+
QgsCompositionChecker checker( "Composer map overview blending", mComposition, QString( QString( TEST_DATA_DIR ) + QDir::separator() +
151+
"control_images" + QDir::separator() + "expected_composermap" + QDir::separator() + "composermap_landsat_overview_blend.png" ) );
152+
bool testResult = checker.testComposition();
153+
mComposition->removeComposerItem( overviewMapBlend );
154+
QVERIFY( testResult );
155+
}
156+
157+
void TestQgsComposerMap::overviewMapInvert()
158+
{
159+
QgsComposerMap* overviewMapInvert = new QgsComposerMap( mComposition, 20, 130, 70, 70 );
160+
overviewMapInvert->setFrameEnabled( true );
161+
mComposition->addComposerMap( overviewMapInvert );
162+
mComposerMap->setNewExtent( QgsRectangle( 785462.375, 3341423.125, 789262.375, 3343323.125 ) ); //zoom in
163+
overviewMapInvert->setNewExtent( QgsRectangle( 781662.375, 3339523.125, 793062.375, 3350923.125 ) );
164+
overviewMapInvert->setOverviewFrameMap( mComposerMap->id() );
165+
overviewMapInvert->setOverviewInverted( true );
166+
167+
QgsCompositionChecker checker( "Composer map overview invert", mComposition, QString( QString( TEST_DATA_DIR ) + QDir::separator() +
168+
"control_images" + QDir::separator() + "expected_composermap" + QDir::separator() + "composermap_landsat_overview_invert.png" ) );
169+
bool testResult = checker.testComposition();
170+
mComposition->removeComposerItem( overviewMapInvert );
171+
QVERIFY( testResult );
172+
}
173+
138174
void TestQgsComposerMap::uniqueId()
139175
{
140176
QDomDocument doc;

tests/src/python/test_qgscomposermap.py

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from PyQt4.QtCore import (QStringList,
1717
QFileInfo)
1818
from PyQt4.QtXml import QDomDocument
19+
from PyQt4.QtGui import QPainter
1920

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

129-
130+
def testOverviewMapBlend(self):
131+
overviewMap = QgsComposerMap(self.mComposition, 20, 130, 70, 70)
132+
overviewMap.setFrameEnabled(True)
133+
self.mComposition.addComposerMap(overviewMap)
134+
# zoom in
135+
myRectangle = QgsRectangle(785462.375, 3341423.125,
136+
789262.375, 3343323.125)
137+
self.mComposerMap.setNewExtent(myRectangle)
138+
myRectangle2 = QgsRectangle(781662.375, 3339523.125,
139+
793062.375, 3350923.125)
140+
overviewMap.setNewExtent(myRectangle2)
141+
overviewMap.setOverviewFrameMap(self.mComposerMap.id())
142+
overviewMap.setOverviewBlendMode(QPainter.CompositionMode_Multiply)
143+
checker = QgsCompositionChecker()
144+
myPngPath = os.path.join(TEST_DATA_DIR,
145+
'control_images',
146+
'expected_composermap',
147+
'composermap_landsat_overview_blend.png')
148+
myTestResult, myMessage = checker.testComposition(
149+
'Composer map overview blending',
150+
self.mComposition,
151+
myPngPath)
152+
self.mComposition.removeComposerItem(overviewMap)
153+
assert myTestResult == True, myMessage
154+
155+
def testOverviewMapInvert(self):
156+
overviewMap = QgsComposerMap(self.mComposition, 20, 130, 70, 70)
157+
overviewMap.setFrameEnabled(True)
158+
self.mComposition.addComposerMap(overviewMap)
159+
# zoom in
160+
myRectangle = QgsRectangle(785462.375, 3341423.125,
161+
789262.375, 3343323.125)
162+
self.mComposerMap.setNewExtent(myRectangle)
163+
myRectangle2 = QgsRectangle(781662.375, 3339523.125,
164+
793062.375, 3350923.125)
165+
overviewMap.setNewExtent(myRectangle2)
166+
overviewMap.setOverviewFrameMap(self.mComposerMap.id())
167+
overviewMap.setOverviewInverted(True)
168+
checker = QgsCompositionChecker()
169+
myPngPath = os.path.join(TEST_DATA_DIR,
170+
'control_images',
171+
'expected_composermap',
172+
'composermap_landsat_overview_invert.png')
173+
myTestResult, myMessage = checker.testComposition(
174+
'Composer map overview inverted',
175+
self.mComposition,
176+
myPngPath)
177+
self.mComposition.removeComposerItem(overviewMap)
178+
assert myTestResult == True, myMessage
179+
130180
# Fails because addItemsFromXML has been commented out in sip
131181
@expectedFailure
132182
def testuniqueId(self):
175 KB
Loading
170 KB
Loading

0 commit comments

Comments
 (0)