Skip to content
Permalink
Browse files

Merge pull request #3618 from nyalldawson/true_north

[composer] Allow syncing pictures to true north (fixes #192)
  • Loading branch information
nyalldawson committed Oct 18, 2016
2 parents 2835cad + e8be0ed commit 89cc6454b989de1746779b2f648a32d2863ebada
@@ -1,4 +1,3 @@
PyQgsComposerPicture
PyQgsJSONUtils
PyQgsLocalServer
PyQgsPalLabelingServer
@@ -30,6 +30,13 @@ class QgsComposerPicture: QgsComposerItem
Unknown
};

//! Method for syncing rotation to a map's North direction
enum NorthMode
{
GridNorth, /*!< Align to grid north */
TrueNorth, /*!< Align to true north */
};

QgsComposerPicture( QgsComposition *composition /TransferThis/);
~QgsComposerPicture();

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

/**
* Returns the mode used to align the picture to a map's North.
* @see setNorthMode()
* @see northOffset()
* @note added in QGIS 2.18
*/
NorthMode northMode() const;

/**
* Sets the mode used to align the picture to a map's North.
* @see northMode()
* @see setNorthOffset()
* @note added in QGIS 2.18
*/
void setNorthMode( NorthMode mode );

/**
* Returns the offset added to the picture's rotation from a map's North.
* @see setNorthOffset()
* @see northMode()
* @note added in QGIS 2.18
*/
double northOffset() const;

/**
* Sets the offset added to the picture's rotation from a map's North.
* @see northOffset()
* @see setNorthMode()
* @note added in QGIS 2.18
*/
void setNorthOffset( double offset );

/** Returns the resize mode used for drawing the picture within the composer
* item's frame.
* @returns resize mode of picture
@@ -21,6 +21,7 @@
%Include qgsaggregatecalculator.sip
%Include qgsattributetableconfig.sip
%Include qgsattributeeditorelement.sip
%Include qgsbearingutils.sip
%Include qgsbrowsermodel.sip
%Include qgsclipper.sip
%Include qgscolorramp.sip
@@ -0,0 +1,21 @@
/**
* \class QgsBearingUtils
* \ingroup core
* Utilities for calculating bearings and directions.
* \note Added in version 2.18
*/
class QgsBearingUtils
{
%TypeHeaderCode
#include <qgsbearingutils.h>
%End
public:

/**
* Returns the direction to true north from a specified point and for a specified
* coordinate reference system. The returned value is in degrees clockwise from
* vertical. An exception will be thrown if the bearing could not be calculated.
*/
static double bearingTrueNorth( const QgsCoordinateReferenceSystem& crs,
const QgsPoint& point );
};
@@ -45,6 +45,13 @@ QgsComposerPictureWidget::QgsComposerPictureWidget( QgsComposerPicture* picture
mOutlineColorButton->setColorDialogTitle( tr( "Select outline color" ) );
mOutlineColorButton->setContext( "composer" );

mNorthTypeComboBox->blockSignals( true );
mNorthTypeComboBox->addItem( tr( "Grid north" ), QgsComposerPicture::GridNorth );
mNorthTypeComboBox->addItem( tr( "True north" ), QgsComposerPicture::TrueNorth );
mNorthTypeComboBox->blockSignals( false );
mPictureRotationOffsetSpinBox->setClearValue( 0.0 );
mPictureRotationSpinBox->setClearValue( 0.0 );

//add widget for general composer item properties
QgsComposerItemWidget* itemPropertiesWidget = new QgsComposerItemWidget( this, picture );
mainLayout->addWidget( itemPropertiesWidget );
@@ -270,6 +277,8 @@ void QgsComposerPictureWidget::on_mRotationFromComposerMapCheckBox_stateChanged(
mPicture->setRotationMap( -1 );
mPictureRotationSpinBox->setEnabled( true );
mComposerMapComboBox->setEnabled( false );
mNorthTypeComboBox->setEnabled( false );
mPictureRotationOffsetSpinBox->setEnabled( false );
mPicture->setPictureRotation( mPictureRotationSpinBox->value() );
}
else
@@ -278,6 +287,8 @@ void QgsComposerPictureWidget::on_mRotationFromComposerMapCheckBox_stateChanged(
int mapId = map ? map->id() : -1;
mPicture->setRotationMap( mapId );
mPictureRotationSpinBox->setEnabled( false );
mNorthTypeComboBox->setEnabled( true );
mPictureRotationOffsetSpinBox->setEnabled( true );
mComposerMapComboBox->setEnabled( true );
}
mPicture->endCommand();
@@ -325,6 +336,8 @@ void QgsComposerPictureWidget::setGuiElementValues()
mPictureLineEdit->blockSignals( true );
mComposerMapComboBox->blockSignals( true );
mRotationFromComposerMapCheckBox->blockSignals( true );
mNorthTypeComboBox->blockSignals( true );
mPictureRotationOffsetSpinBox->blockSignals( true );
mResizeModeComboBox->blockSignals( true );
mAnchorPointComboBox->blockSignals( true );
mFillColorButton->blockSignals( true );
@@ -345,13 +358,19 @@ void QgsComposerPictureWidget::setGuiElementValues()
mRotationFromComposerMapCheckBox->setCheckState( Qt::Checked );
mPictureRotationSpinBox->setEnabled( false );
mComposerMapComboBox->setEnabled( true );
mNorthTypeComboBox->setEnabled( true );
mPictureRotationOffsetSpinBox->setEnabled( true );
}
else
{
mRotationFromComposerMapCheckBox->setCheckState( Qt::Unchecked );
mPictureRotationSpinBox->setEnabled( true );
mComposerMapComboBox->setEnabled( false );
mNorthTypeComboBox->setEnabled( false );
mPictureRotationOffsetSpinBox->setEnabled( false );
}
mNorthTypeComboBox->setCurrentIndex( mNorthTypeComboBox->findData( mPicture->northMode() ) );
mPictureRotationOffsetSpinBox->setValue( mPicture->northOffset() );

mResizeModeComboBox->setCurrentIndex(( int )mPicture->resizeMode() );
//disable picture rotation for non-zoom modes
@@ -379,6 +398,8 @@ void QgsComposerPictureWidget::setGuiElementValues()
mPictureRotationSpinBox->blockSignals( false );
mPictureLineEdit->blockSignals( false );
mComposerMapComboBox->blockSignals( false );
mNorthTypeComboBox->blockSignals( false );
mPictureRotationOffsetSpinBox->blockSignals( false );
mResizeModeComboBox->blockSignals( false );
mAnchorPointComboBox->blockSignals( false );
mFillColorButton->blockSignals( false );
@@ -655,6 +676,22 @@ void QgsComposerPictureWidget::on_mOutlineWidthSpinBox_valueChanged( double d )
mPicture->update();
}

void QgsComposerPictureWidget::on_mPictureRotationOffsetSpinBox_valueChanged( double d )
{
mPicture->beginCommand( tr( "Picture North offset changed" ), QgsComposerMergeCommand::ComposerPictureNorthOffset );
mPicture->setNorthOffset( d );
mPicture->endCommand();
mPicture->update();
}

void QgsComposerPictureWidget::on_mNorthTypeComboBox_currentIndexChanged( int index )
{
mPicture->beginCommand( tr( "Picture North mode changed" ) );
mPicture->setNorthMode( static_cast< QgsComposerPicture::NorthMode >( mNorthTypeComboBox->itemData( index ).toInt() ) );
mPicture->endCommand();
mPicture->update();
}

void QgsComposerPictureWidget::resizeEvent( QResizeEvent * event )
{
Q_UNUSED( event );
@@ -70,6 +70,8 @@ class QgsComposerPictureWidget: public QgsComposerItemBaseWidget, private Ui::Qg
void on_mFillColorButton_colorChanged( const QColor& color );
void on_mOutlineColorButton_colorChanged( const QColor& color );
void on_mOutlineWidthSpinBox_valueChanged( double d );
void on_mPictureRotationOffsetSpinBox_valueChanged( double d );
void on_mNorthTypeComboBox_currentIndexChanged( int index );

private:
QgsComposerPicture* mPicture;
@@ -85,6 +85,7 @@ SET(QGIS_CORE_SRCS
qgsaggregatecalculator.cpp
qgsattributetableconfig.cpp
qgsattributeeditorelement.cpp
qgsbearingutils.cpp
qgsbrowsermodel.cpp
qgscachedfeatureiterator.cpp
qgscacheindex.cpp
@@ -600,6 +601,7 @@ SET(QGIS_CORE_HDRS
qgsannotation.h
qgsattributetableconfig.h
qgsattributeeditorelement.h
qgsbearingutils.h
qgscachedfeatureiterator.h
qgscacheindex.h
qgscacheindexfeatureid.h
@@ -118,6 +118,7 @@ class CORE_EXPORT QgsComposerMergeCommand: public QgsComposerItemCommand
ComposerPictureRotation,
ComposerPictureFillColor,
ComposerPictureOutlineColor,
ComposerPictureNorthOffset,
// composer scalebar
ScaleBarLineWidth,
ScaleBarHeight,
@@ -29,6 +29,8 @@
#include "qgssymbollayerutils.h"
#include "qgssvgcache.h"
#include "qgslogger.h"
#include "qgsbearingutils.h"
#include "qgsmapsettings.h"

#include <QDomDocument>
#include <QDomElement>
@@ -46,6 +48,8 @@ QgsComposerPicture::QgsComposerPicture( QgsComposition *composition )
, mMode( Unknown )
, mPictureRotation( 0 )
, mRotationMap( nullptr )
, mNorthMode( GridNorth )
, mNorthOffset( 0.0 )
, mResizeMode( QgsComposerPicture::Zoom )
, mPictureAnchor( UpperLeft )
, mSvgFillColor( QColor( 255, 255, 255 ) )
@@ -63,6 +67,8 @@ QgsComposerPicture::QgsComposerPicture()
, mMode( Unknown )
, mPictureRotation( 0 )
, mRotationMap( nullptr )
, mNorthMode( GridNorth )
, mNorthOffset( 0.0 )
, mResizeMode( QgsComposerPicture::Zoom )
, mPictureAnchor( UpperLeft )
, mSvgFillColor( QColor( 255, 255, 255 ) )
@@ -408,6 +414,43 @@ void QgsComposerPicture::remotePictureLoaded()
mLoaded = true;
}

void QgsComposerPicture::updateMapRotation()
{
if ( !mRotationMap )
return;

// take map rotation
double rotation = mRotationMap->mapRotation();

// handle true north
switch ( mNorthMode )
{
case GridNorth:
break; // nothing to do

case TrueNorth:
{
QgsPoint center = mRotationMap->currentMapExtent()->center();
QgsCoordinateReferenceSystem crs = mComposition->mapSettings().destinationCrs();

try
{
double bearing = QgsBearingUtils::bearingTrueNorth( crs, center );
rotation += bearing;
}
catch ( QgsException& e )
{
Q_UNUSED( e );
QgsDebugMsg( QString( "Caught exception %1" ).arg( e.what() ) );
}
break;
}
}

rotation += mNorthOffset;
setPictureRotation( rotation );
}

void QgsComposerPicture::loadPicture( const QString &path )
{
if ( path.startsWith( "http" ) )
@@ -633,7 +676,8 @@ void QgsComposerPicture::setRotationMap( int composerMapId )

if ( composerMapId == -1 ) //disable rotation from map
{
QObject::disconnect( mRotationMap, SIGNAL( mapRotationChanged( double ) ), this, SLOT( setPictureRotation( double ) ) );
disconnect( mRotationMap, SIGNAL( mapRotationChanged( double ) ), this, SLOT( updateMapRotation() ) );
disconnect( mRotationMap, SIGNAL( extentChanged() ), this, SLOT( updateMapRotation() ) );
mRotationMap = nullptr;
}

@@ -644,10 +688,12 @@ void QgsComposerPicture::setRotationMap( int composerMapId )
}
if ( mRotationMap )
{
QObject::disconnect( mRotationMap, SIGNAL( mapRotationChanged( double ) ), this, SLOT( setPictureRotation( double ) ) );
disconnect( mRotationMap, SIGNAL( mapRotationChanged( double ) ), this, SLOT( updateMapRotation() ) );
disconnect( mRotationMap, SIGNAL( extentChanged() ), this, SLOT( updateMapRotation() ) );
}
mPictureRotation = map->mapRotation();
QObject::connect( map, SIGNAL( mapRotationChanged( double ) ), this, SLOT( setPictureRotation( double ) ) );
connect( map, SIGNAL( mapRotationChanged( double ) ), this, SLOT( updateMapRotation() ) );
connect( map, SIGNAL( extentChanged() ), this, SLOT( updateMapRotation() ) );
mRotationMap = map;
update();
emit pictureRotationChanged( mPictureRotation );
@@ -722,6 +768,8 @@ bool QgsComposerPicture::writeXml( QDomElement& elem, QDomDocument & doc ) const
{
composerPictureElem.setAttribute( "mapId", mRotationMap->id() );
}
composerPictureElem.setAttribute( "northMode", mNorthMode );
composerPictureElem.setAttribute( "northOffset", mNorthOffset );

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

//rotation map
mNorthMode = static_cast< NorthMode >( itemElem.attribute( "northMode", "0" ).toInt() );
mNorthOffset = itemElem.attribute( "northOffset", "0" ).toDouble();

int rotationMapId = itemElem.attribute( "mapId", "-1" ).toInt();
if ( rotationMapId == -1 )
{
@@ -798,10 +849,12 @@ bool QgsComposerPicture::readXml( const QDomElement& itemElem, const QDomDocumen

if ( mRotationMap )
{
QObject::disconnect( mRotationMap, SIGNAL( mapRotationChanged( double ) ), this, SLOT( setRotation( double ) ) );
disconnect( mRotationMap, SIGNAL( mapRotationChanged( double ) ), this, SLOT( updateMapRotation() ) );
disconnect( mRotationMap, SIGNAL( extentChanged() ), this, SLOT( updateMapRotation() ) );
}
mRotationMap = mComposition->getComposerMapById( rotationMapId );
QObject::connect( mRotationMap, SIGNAL( mapRotationChanged( double ) ), this, SLOT( setRotation( double ) ) );
connect( mRotationMap, SIGNAL( mapRotationChanged( double ) ), this, SLOT( updateMapRotation() ) );
connect( mRotationMap, SIGNAL( extentChanged() ), this, SLOT( updateMapRotation() ) );
}

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

void QgsComposerPicture::setNorthMode( QgsComposerPicture::NorthMode mode )
{
mNorthMode = mode;
updateMapRotation();
}

void QgsComposerPicture::setNorthOffset( double offset )
{
mNorthOffset = offset;
updateMapRotation();
}

void QgsComposerPicture::setPictureAnchor( QgsComposerItem::ItemPositionMode anchor )
{
mPictureAnchor = anchor;

0 comments on commit 89cc645

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