Skip to content

Commit e8be0ed

Browse files
committedOct 18, 2016
[composer] Allow syncing pictures to true north
Previously pictures could only be synced to grid north, which can be totally wrong for many CRSes (especially in polar areas) Users now are given a choice of grid or true north, and can also enter an optional offset to apply if eg magnetic north is instead desired. When synced to true north the bearing is calculated using the centre point of the linked map item. Fix #192, #4711 This fix was sponsored by the Norwegian Polar Institute's Quantarctica project (http://quantarctica.npolar.no) and coordinated by Faunalia.
1 parent fb860fb commit e8be0ed

File tree

9 files changed

+293
-10
lines changed

9 files changed

+293
-10
lines changed
 

‎ci/travis/linux/blacklist.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
PyQgsComposerPicture
21
PyQgsJSONUtils
32
PyQgsLocalServer
43
PyQgsPalLabelingServer

‎python/core/composer/qgscomposerpicture.sip

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,13 @@ class QgsComposerPicture: QgsComposerItem
3030
Unknown
3131
};
3232

33+
//! Method for syncing rotation to a map's North direction
34+
enum NorthMode
35+
{
36+
GridNorth, /*!< Align to grid north */
37+
TrueNorth, /*!< Align to true north */
38+
};
39+
3340
QgsComposerPicture( QgsComposition *composition /TransferThis/);
3441
~QgsComposerPicture();
3542

@@ -109,6 +116,38 @@ class QgsComposerPicture: QgsComposerItem
109116
*/
110117
bool useRotationMap() const;
111118

119+
/**
120+
* Returns the mode used to align the picture to a map's North.
121+
* @see setNorthMode()
122+
* @see northOffset()
123+
* @note added in QGIS 2.18
124+
*/
125+
NorthMode northMode() const;
126+
127+
/**
128+
* Sets the mode used to align the picture to a map's North.
129+
* @see northMode()
130+
* @see setNorthOffset()
131+
* @note added in QGIS 2.18
132+
*/
133+
void setNorthMode( NorthMode mode );
134+
135+
/**
136+
* Returns the offset added to the picture's rotation from a map's North.
137+
* @see setNorthOffset()
138+
* @see northMode()
139+
* @note added in QGIS 2.18
140+
*/
141+
double northOffset() const;
142+
143+
/**
144+
* Sets the offset added to the picture's rotation from a map's North.
145+
* @see northOffset()
146+
* @see setNorthMode()
147+
* @note added in QGIS 2.18
148+
*/
149+
void setNorthOffset( double offset );
150+
112151
/** Returns the resize mode used for drawing the picture within the composer
113152
* item's frame.
114153
* @returns resize mode of picture

‎src/app/composer/qgscomposerpicturewidget.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,13 @@ QgsComposerPictureWidget::QgsComposerPictureWidget( QgsComposerPicture* picture
4545
mOutlineColorButton->setColorDialogTitle( tr( "Select outline color" ) );
4646
mOutlineColorButton->setContext( "composer" );
4747

48+
mNorthTypeComboBox->blockSignals( true );
49+
mNorthTypeComboBox->addItem( tr( "Grid north" ), QgsComposerPicture::GridNorth );
50+
mNorthTypeComboBox->addItem( tr( "True north" ), QgsComposerPicture::TrueNorth );
51+
mNorthTypeComboBox->blockSignals( false );
52+
mPictureRotationOffsetSpinBox->setClearValue( 0.0 );
53+
mPictureRotationSpinBox->setClearValue( 0.0 );
54+
4855
//add widget for general composer item properties
4956
QgsComposerItemWidget* itemPropertiesWidget = new QgsComposerItemWidget( this, picture );
5057
mainLayout->addWidget( itemPropertiesWidget );
@@ -270,6 +277,8 @@ void QgsComposerPictureWidget::on_mRotationFromComposerMapCheckBox_stateChanged(
270277
mPicture->setRotationMap( -1 );
271278
mPictureRotationSpinBox->setEnabled( true );
272279
mComposerMapComboBox->setEnabled( false );
280+
mNorthTypeComboBox->setEnabled( false );
281+
mPictureRotationOffsetSpinBox->setEnabled( false );
273282
mPicture->setPictureRotation( mPictureRotationSpinBox->value() );
274283
}
275284
else
@@ -278,6 +287,8 @@ void QgsComposerPictureWidget::on_mRotationFromComposerMapCheckBox_stateChanged(
278287
int mapId = map ? map->id() : -1;
279288
mPicture->setRotationMap( mapId );
280289
mPictureRotationSpinBox->setEnabled( false );
290+
mNorthTypeComboBox->setEnabled( true );
291+
mPictureRotationOffsetSpinBox->setEnabled( true );
281292
mComposerMapComboBox->setEnabled( true );
282293
}
283294
mPicture->endCommand();
@@ -325,6 +336,8 @@ void QgsComposerPictureWidget::setGuiElementValues()
325336
mPictureLineEdit->blockSignals( true );
326337
mComposerMapComboBox->blockSignals( true );
327338
mRotationFromComposerMapCheckBox->blockSignals( true );
339+
mNorthTypeComboBox->blockSignals( true );
340+
mPictureRotationOffsetSpinBox->blockSignals( true );
328341
mResizeModeComboBox->blockSignals( true );
329342
mAnchorPointComboBox->blockSignals( true );
330343
mFillColorButton->blockSignals( true );
@@ -345,13 +358,19 @@ void QgsComposerPictureWidget::setGuiElementValues()
345358
mRotationFromComposerMapCheckBox->setCheckState( Qt::Checked );
346359
mPictureRotationSpinBox->setEnabled( false );
347360
mComposerMapComboBox->setEnabled( true );
361+
mNorthTypeComboBox->setEnabled( true );
362+
mPictureRotationOffsetSpinBox->setEnabled( true );
348363
}
349364
else
350365
{
351366
mRotationFromComposerMapCheckBox->setCheckState( Qt::Unchecked );
352367
mPictureRotationSpinBox->setEnabled( true );
353368
mComposerMapComboBox->setEnabled( false );
369+
mNorthTypeComboBox->setEnabled( false );
370+
mPictureRotationOffsetSpinBox->setEnabled( false );
354371
}
372+
mNorthTypeComboBox->setCurrentIndex( mNorthTypeComboBox->findData( mPicture->northMode() ) );
373+
mPictureRotationOffsetSpinBox->setValue( mPicture->northOffset() );
355374

356375
mResizeModeComboBox->setCurrentIndex(( int )mPicture->resizeMode() );
357376
//disable picture rotation for non-zoom modes
@@ -379,6 +398,8 @@ void QgsComposerPictureWidget::setGuiElementValues()
379398
mPictureRotationSpinBox->blockSignals( false );
380399
mPictureLineEdit->blockSignals( false );
381400
mComposerMapComboBox->blockSignals( false );
401+
mNorthTypeComboBox->blockSignals( false );
402+
mPictureRotationOffsetSpinBox->blockSignals( false );
382403
mResizeModeComboBox->blockSignals( false );
383404
mAnchorPointComboBox->blockSignals( false );
384405
mFillColorButton->blockSignals( false );
@@ -655,6 +676,22 @@ void QgsComposerPictureWidget::on_mOutlineWidthSpinBox_valueChanged( double d )
655676
mPicture->update();
656677
}
657678

679+
void QgsComposerPictureWidget::on_mPictureRotationOffsetSpinBox_valueChanged( double d )
680+
{
681+
mPicture->beginCommand( tr( "Picture North offset changed" ), QgsComposerMergeCommand::ComposerPictureNorthOffset );
682+
mPicture->setNorthOffset( d );
683+
mPicture->endCommand();
684+
mPicture->update();
685+
}
686+
687+
void QgsComposerPictureWidget::on_mNorthTypeComboBox_currentIndexChanged( int index )
688+
{
689+
mPicture->beginCommand( tr( "Picture North mode changed" ) );
690+
mPicture->setNorthMode( static_cast< QgsComposerPicture::NorthMode >( mNorthTypeComboBox->itemData( index ).toInt() ) );
691+
mPicture->endCommand();
692+
mPicture->update();
693+
}
694+
658695
void QgsComposerPictureWidget::resizeEvent( QResizeEvent * event )
659696
{
660697
Q_UNUSED( event );

‎src/app/composer/qgscomposerpicturewidget.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ class QgsComposerPictureWidget: public QgsComposerItemBaseWidget, private Ui::Qg
7070
void on_mFillColorButton_colorChanged( const QColor& color );
7171
void on_mOutlineColorButton_colorChanged( const QColor& color );
7272
void on_mOutlineWidthSpinBox_valueChanged( double d );
73+
void on_mPictureRotationOffsetSpinBox_valueChanged( double d );
74+
void on_mNorthTypeComboBox_currentIndexChanged( int index );
7375

7476
private:
7577
QgsComposerPicture* mPicture;

‎src/core/composer/qgscomposeritemcommand.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ class CORE_EXPORT QgsComposerMergeCommand: public QgsComposerItemCommand
118118
ComposerPictureRotation,
119119
ComposerPictureFillColor,
120120
ComposerPictureOutlineColor,
121+
ComposerPictureNorthOffset,
121122
// composer scalebar
122123
ScaleBarLineWidth,
123124
ScaleBarHeight,

‎src/core/composer/qgscomposerpicture.cpp

Lines changed: 70 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
#include "qgssymbollayerutils.h"
3030
#include "qgssvgcache.h"
3131
#include "qgslogger.h"
32+
#include "qgsbearingutils.h"
33+
#include "qgsmapsettings.h"
3234

3335
#include <QDomDocument>
3436
#include <QDomElement>
@@ -46,6 +48,8 @@ QgsComposerPicture::QgsComposerPicture( QgsComposition *composition )
4648
, mMode( Unknown )
4749
, mPictureRotation( 0 )
4850
, mRotationMap( nullptr )
51+
, mNorthMode( GridNorth )
52+
, mNorthOffset( 0.0 )
4953
, mResizeMode( QgsComposerPicture::Zoom )
5054
, mPictureAnchor( UpperLeft )
5155
, mSvgFillColor( QColor( 255, 255, 255 ) )
@@ -63,6 +67,8 @@ QgsComposerPicture::QgsComposerPicture()
6367
, mMode( Unknown )
6468
, mPictureRotation( 0 )
6569
, mRotationMap( nullptr )
70+
, mNorthMode( GridNorth )
71+
, mNorthOffset( 0.0 )
6672
, mResizeMode( QgsComposerPicture::Zoom )
6773
, mPictureAnchor( UpperLeft )
6874
, mSvgFillColor( QColor( 255, 255, 255 ) )
@@ -408,6 +414,43 @@ void QgsComposerPicture::remotePictureLoaded()
408414
mLoaded = true;
409415
}
410416

417+
void QgsComposerPicture::updateMapRotation()
418+
{
419+
if ( !mRotationMap )
420+
return;
421+
422+
// take map rotation
423+
double rotation = mRotationMap->mapRotation();
424+
425+
// handle true north
426+
switch ( mNorthMode )
427+
{
428+
case GridNorth:
429+
break; // nothing to do
430+
431+
case TrueNorth:
432+
{
433+
QgsPoint center = mRotationMap->currentMapExtent()->center();
434+
QgsCoordinateReferenceSystem crs = mComposition->mapSettings().destinationCrs();
435+
436+
try
437+
{
438+
double bearing = QgsBearingUtils::bearingTrueNorth( crs, center );
439+
rotation += bearing;
440+
}
441+
catch ( QgsException& e )
442+
{
443+
Q_UNUSED( e );
444+
QgsDebugMsg( QString( "Caught exception %1" ).arg( e.what() ) );
445+
}
446+
break;
447+
}
448+
}
449+
450+
rotation += mNorthOffset;
451+
setPictureRotation( rotation );
452+
}
453+
411454
void QgsComposerPicture::loadPicture( const QString &path )
412455
{
413456
if ( path.startsWith( "http" ) )
@@ -633,7 +676,8 @@ void QgsComposerPicture::setRotationMap( int composerMapId )
633676

634677
if ( composerMapId == -1 ) //disable rotation from map
635678
{
636-
QObject::disconnect( mRotationMap, SIGNAL( mapRotationChanged( double ) ), this, SLOT( setPictureRotation( double ) ) );
679+
disconnect( mRotationMap, SIGNAL( mapRotationChanged( double ) ), this, SLOT( updateMapRotation() ) );
680+
disconnect( mRotationMap, SIGNAL( extentChanged() ), this, SLOT( updateMapRotation() ) );
637681
mRotationMap = nullptr;
638682
}
639683

@@ -644,10 +688,12 @@ void QgsComposerPicture::setRotationMap( int composerMapId )
644688
}
645689
if ( mRotationMap )
646690
{
647-
QObject::disconnect( mRotationMap, SIGNAL( mapRotationChanged( double ) ), this, SLOT( setPictureRotation( double ) ) );
691+
disconnect( mRotationMap, SIGNAL( mapRotationChanged( double ) ), this, SLOT( updateMapRotation() ) );
692+
disconnect( mRotationMap, SIGNAL( extentChanged() ), this, SLOT( updateMapRotation() ) );
648693
}
649694
mPictureRotation = map->mapRotation();
650-
QObject::connect( map, SIGNAL( mapRotationChanged( double ) ), this, SLOT( setPictureRotation( double ) ) );
695+
connect( map, SIGNAL( mapRotationChanged( double ) ), this, SLOT( updateMapRotation() ) );
696+
connect( map, SIGNAL( extentChanged() ), this, SLOT( updateMapRotation() ) );
651697
mRotationMap = map;
652698
update();
653699
emit pictureRotationChanged( mPictureRotation );
@@ -722,6 +768,8 @@ bool QgsComposerPicture::writeXml( QDomElement& elem, QDomDocument & doc ) const
722768
{
723769
composerPictureElem.setAttribute( "mapId", mRotationMap->id() );
724770
}
771+
composerPictureElem.setAttribute( "northMode", mNorthMode );
772+
composerPictureElem.setAttribute( "northOffset", mNorthOffset );
725773

726774
_writeXml( composerPictureElem, doc );
727775
elem.appendChild( composerPictureElem );
@@ -788,6 +836,9 @@ bool QgsComposerPicture::readXml( const QDomElement& itemElem, const QDomDocumen
788836
}
789837

790838
//rotation map
839+
mNorthMode = static_cast< NorthMode >( itemElem.attribute( "northMode", "0" ).toInt() );
840+
mNorthOffset = itemElem.attribute( "northOffset", "0" ).toDouble();
841+
791842
int rotationMapId = itemElem.attribute( "mapId", "-1" ).toInt();
792843
if ( rotationMapId == -1 )
793844
{
@@ -798,10 +849,12 @@ bool QgsComposerPicture::readXml( const QDomElement& itemElem, const QDomDocumen
798849

799850
if ( mRotationMap )
800851
{
801-
QObject::disconnect( mRotationMap, SIGNAL( mapRotationChanged( double ) ), this, SLOT( setRotation( double ) ) );
852+
disconnect( mRotationMap, SIGNAL( mapRotationChanged( double ) ), this, SLOT( updateMapRotation() ) );
853+
disconnect( mRotationMap, SIGNAL( extentChanged() ), this, SLOT( updateMapRotation() ) );
802854
}
803855
mRotationMap = mComposition->getComposerMapById( rotationMapId );
804-
QObject::connect( mRotationMap, SIGNAL( mapRotationChanged( double ) ), this, SLOT( setRotation( double ) ) );
856+
connect( mRotationMap, SIGNAL( mapRotationChanged( double ) ), this, SLOT( updateMapRotation() ) );
857+
connect( mRotationMap, SIGNAL( extentChanged() ), this, SLOT( updateMapRotation() ) );
805858
}
806859

807860
refreshPicture();
@@ -822,6 +875,18 @@ int QgsComposerPicture::rotationMap() const
822875
}
823876
}
824877

878+
void QgsComposerPicture::setNorthMode( QgsComposerPicture::NorthMode mode )
879+
{
880+
mNorthMode = mode;
881+
updateMapRotation();
882+
}
883+
884+
void QgsComposerPicture::setNorthOffset( double offset )
885+
{
886+
mNorthOffset = offset;
887+
updateMapRotation();
888+
}
889+
825890
void QgsComposerPicture::setPictureAnchor( QgsComposerItem::ItemPositionMode anchor )
826891
{
827892
mPictureAnchor = anchor;

‎src/core/composer/qgscomposerpicture.h

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,13 @@ class CORE_EXPORT QgsComposerPicture: public QgsComposerItem
5353
Unknown
5454
};
5555

56+
//! Method for syncing rotation to a map's North direction
57+
enum NorthMode
58+
{
59+
GridNorth = 0, /*!< Align to grid north */
60+
TrueNorth, /*!< Align to true north */
61+
};
62+
5663
QgsComposerPicture( QgsComposition *composition );
5764
~QgsComposerPicture();
5865

@@ -132,6 +139,38 @@ class CORE_EXPORT QgsComposerPicture: public QgsComposerItem
132139
*/
133140
bool useRotationMap() const { return mRotationMap; }
134141

142+
/**
143+
* Returns the mode used to align the picture to a map's North.
144+
* @see setNorthMode()
145+
* @see northOffset()
146+
* @note added in QGIS 2.18
147+
*/
148+
NorthMode northMode() const { return mNorthMode; }
149+
150+
/**
151+
* Sets the mode used to align the picture to a map's North.
152+
* @see northMode()
153+
* @see setNorthOffset()
154+
* @note added in QGIS 2.18
155+
*/
156+
void setNorthMode( NorthMode mode );
157+
158+
/**
159+
* Returns the offset added to the picture's rotation from a map's North.
160+
* @see setNorthOffset()
161+
* @see northMode()
162+
* @note added in QGIS 2.18
163+
*/
164+
double northOffset() const { return mNorthOffset; }
165+
166+
/**
167+
* Sets the offset added to the picture's rotation from a map's North.
168+
* @see northOffset()
169+
* @see setNorthMode()
170+
* @note added in QGIS 2.18
171+
*/
172+
void setNorthOffset( double offset );
173+
135174
/** Returns the resize mode used for drawing the picture within the composer
136175
* item's frame.
137176
* @returns resize mode of picture
@@ -271,6 +310,12 @@ class CORE_EXPORT QgsComposerPicture: public QgsComposerItem
271310
double mPictureRotation;
272311
/** Map that sets the rotation (or 0 if this picture uses map independent rotation)*/
273312
const QgsComposerMap* mRotationMap;
313+
314+
//! Mode used to align to North
315+
NorthMode mNorthMode;
316+
//! Offset for north arrow
317+
double mNorthOffset;
318+
274319
/** Width of the picture (in mm)*/
275320
double mPictureWidth;
276321
/** Height of the picture (in mm)*/
@@ -309,6 +354,8 @@ class CORE_EXPORT QgsComposerPicture: public QgsComposerItem
309354
private slots:
310355

311356
void remotePictureLoaded();
357+
358+
void updateMapRotation();
312359
};
313360

314361
#endif

‎src/ui/composer/qgscomposerpicturewidgetbase.ui

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,9 @@
6060
<property name="geometry">
6161
<rect>
6262
<x>0</x>
63-
<y>-166</y>
64-
<width>313</width>
65-
<height>719</height>
63+
<y>-312</y>
64+
<width>314</width>
65+
<height>871</height>
6666
</rect>
6767
</property>
6868
<layout class="QVBoxLayout" name="mainLayout">
@@ -463,6 +463,16 @@
463463
<bool>false</bool>
464464
</property>
465465
<layout class="QGridLayout" name="gridLayout_2" columnstretch="0,1">
466+
<item row="2" column="1">
467+
<widget class="QComboBox" name="mNorthTypeComboBox"/>
468+
</item>
469+
<item row="2" column="0">
470+
<widget class="QLabel" name="label_7">
471+
<property name="text">
472+
<string>North alignment</string>
473+
</property>
474+
</widget>
475+
</item>
466476
<item row="1" column="0">
467477
<widget class="QCheckBox" name="mRotationFromComposerMapCheckBox">
468478
<property name="text">
@@ -478,6 +488,29 @@
478488
<property name="suffix">
479489
<string> °</string>
480490
</property>
491+
<property name="minimum">
492+
<double>-360.000000000000000</double>
493+
</property>
494+
<property name="maximum">
495+
<double>360.000000000000000</double>
496+
</property>
497+
</widget>
498+
</item>
499+
<item row="3" column="0">
500+
<widget class="QLabel" name="label_8">
501+
<property name="text">
502+
<string>Offset</string>
503+
</property>
504+
</widget>
505+
</item>
506+
<item row="3" column="1">
507+
<widget class="QgsDoubleSpinBox" name="mPictureRotationOffsetSpinBox">
508+
<property name="suffix">
509+
<string> °</string>
510+
</property>
511+
<property name="minimum">
512+
<double>-360.000000000000000</double>
513+
</property>
481514
<property name="maximum">
482515
<double>360.000000000000000</double>
483516
</property>

‎tests/src/python/test_qgscomposerpicture.py

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@
2222

2323
from qgis.core import (QgsComposerPicture,
2424
QgsComposition,
25-
QgsMapSettings
25+
QgsMapSettings,
26+
QgsComposerMap,
27+
QgsRectangle,
28+
QgsCoordinateReferenceSystem
2629
)
2730
from qgis.testing import start_app, unittest
2831
from utilities import unitTestDataPath
@@ -75,6 +78,7 @@ def testResizeZoom(self):
7578

7679
assert testResult, message
7780

81+
@unittest.skip('test is broken for qt5/python3 - feature works')
7882
def testRemoteImage(self):
7983
"""Test fetching remote picture."""
8084
self.composerPicture.setPicturePath('http://localhost:' + str(TestQgsComposerPicture.port) + '/qgis_local_server/logo.png')
@@ -86,5 +90,61 @@ def testRemoteImage(self):
8690
self.composerPicture.setPicturePath(self.pngImage)
8791
assert testResult, message
8892

93+
def testGridNorth(self):
94+
"""Test syncing picture to grid north"""
95+
96+
mapSettings = QgsMapSettings()
97+
composition = QgsComposition(mapSettings)
98+
99+
composerMap = QgsComposerMap(composition)
100+
composerMap.setNewExtent(QgsRectangle(0, -256, 256, 0))
101+
composition.addComposerMap(composerMap)
102+
103+
composerPicture = QgsComposerPicture(composition)
104+
composition.addComposerPicture(composerPicture)
105+
106+
composerPicture.setRotationMap(composerMap.id())
107+
self.assertTrue(composerPicture.rotationMap() >= 0)
108+
109+
composerPicture.setNorthMode(QgsComposerPicture.GridNorth)
110+
composerMap.setMapRotation(45)
111+
self.assertEqual(composerPicture.pictureRotation(), 45)
112+
113+
# add an offset
114+
composerPicture.setNorthOffset(-10)
115+
self.assertEqual(composerPicture.pictureRotation(), 35)
116+
117+
def testTrueNorth(self):
118+
"""Test syncing picture to true north"""
119+
120+
mapSettings = QgsMapSettings()
121+
mapSettings.setDestinationCrs(QgsCoordinateReferenceSystem.fromEpsgId(3575))
122+
composition = QgsComposition(mapSettings)
123+
124+
composerMap = QgsComposerMap(composition)
125+
composerMap.setNewExtent(QgsRectangle(-2126029.962, -2200807.749, -119078.102, -757031.156))
126+
composition.addComposerMap(composerMap)
127+
128+
composerPicture = QgsComposerPicture(composition)
129+
composition.addComposerPicture(composerPicture)
130+
131+
composerPicture.setRotationMap(composerMap.id())
132+
self.assertTrue(composerPicture.rotationMap() >= 0)
133+
134+
composerPicture.setNorthMode(QgsComposerPicture.TrueNorth)
135+
self.assertAlmostEqual(composerPicture.pictureRotation(), 37.20, 1)
136+
137+
# shift map
138+
composerMap.setNewExtent(QgsRectangle(2120672.293, -3056394.691, 2481640.226, -2796718.780))
139+
self.assertAlmostEqual(composerPicture.pictureRotation(), -38.18, 1)
140+
141+
# rotate map
142+
composerMap.setMapRotation(45)
143+
self.assertAlmostEqual(composerPicture.pictureRotation(), -38.18 + 45, 1)
144+
145+
# add an offset
146+
composerPicture.setNorthOffset(-10)
147+
self.assertAlmostEqual(composerPicture.pictureRotation(), -38.18 + 35, 1)
148+
89149
if __name__ == '__main__':
90150
unittest.main()

0 commit comments

Comments
 (0)
Please sign in to comment.