Skip to content

Commit

Permalink
[FEATURE][composer] Allow choice of CRS for map items
Browse files Browse the repository at this point in the history
This allows the CRS for map items to differ from the canvas/project
CRS. It also allows different map items to have different CRS,
eg an overview map can be set to a different CRS to the main map.

An unfortunate side effect of this change and the ongoing work
to separate compositions from canvas is that datum transforms
are no longer supported in composer. This cannot be fixed until
the datum transform store is rewritten to not depend on canvas
(ie, it's also broken for upcoming multi-canvas work)
  • Loading branch information
nyalldawson committed Jan 17, 2017
1 parent 305f9f0 commit 9a6d714
Show file tree
Hide file tree
Showing 10 changed files with 226 additions and 52 deletions.
1 change: 1 addition & 0 deletions python/core/composer/qgscomposermap.sip
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ class QgsComposerMap : QgsComposerItem
// QgsRectangle* currentMapExtent();

QgsCoordinateReferenceSystem crs() const;
void setCrs( const QgsCoordinateReferenceSystem& crs );

PreviewMode previewMode() const;
void setPreviewMode( PreviewMode m );
Expand Down
75 changes: 75 additions & 0 deletions src/app/composer/qgscomposermapwidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include "qgsmapthemecollection.h"
#include "qgsmapthemes.h"
#include "qgisgui.h"
#include "qgscsexception.h"

#include <QMessageBox>

Expand Down Expand Up @@ -99,6 +100,8 @@ QgsComposerMapWidget::QgsComposerMapWidget( QgsComposerMap* composerMap )
connect( mOverviewFrameMapComboBox, SIGNAL( itemChanged( QgsComposerItem* ) ), this, SLOT( overviewMapChanged( QgsComposerItem* ) ) );
}

connect( mCrsSelector, &QgsProjectionSelectionWidget::crsChanged, this, &QgsComposerMapWidget::mapCrsChanged );

updateGuiElements();
loadGridEntries();
loadOverviewEntries();
Expand Down Expand Up @@ -260,6 +263,42 @@ void QgsComposerMapWidget::cleanUpOverviewFrameStyleSelector( QgsPanelWidget* co
mComposerMap->endCommand();
}

void QgsComposerMapWidget::mapCrsChanged( const QgsCoordinateReferenceSystem& crs )
{
if ( !mComposerMap )
{
return;
}

if ( mComposerMap->crs() == crs )
return;

// try to reproject to maintain extent
QgsCoordinateReferenceSystem oldCrs = mComposerMap->crs();

bool updateExtent = false;
QgsRectangle newExtent;
try
{
QgsCoordinateTransform xForm( oldCrs, crs );
QgsRectangle prevExtent = *mComposerMap->currentMapExtent();
newExtent = xForm.transformBoundingBox( prevExtent );
updateExtent = true;
}
catch ( QgsCsException & )
{
//transform failed, don't update extent
}

mComposerMap->beginCommand( tr( "Map CRS changed" ) );
mComposerMap->setCrs( crs );
if ( updateExtent )
mComposerMap->zoomToExtent( newExtent );
mComposerMap->endCommand();
mComposerMap->cache();
mComposerMap->update();
}

void QgsComposerMapWidget::on_mAtlasCheckBox_toggled( bool checked )
{
if ( !mComposerMap )
Expand Down Expand Up @@ -471,6 +510,23 @@ void QgsComposerMapWidget::on_mSetToMapCanvasExtentButton_clicked()

QgsRectangle newExtent = QgisApp::instance()->mapCanvas()->mapSettings().visibleExtent();

//transform?
if ( QgisApp::instance()->mapCanvas()->mapSettings().destinationCrs()
!= mComposerMap->crs() )
{
try
{
QgsCoordinateTransform xForm( QgisApp::instance()->mapCanvas()->mapSettings().destinationCrs(),
mComposerMap->crs() );
newExtent = xForm.transformBoundingBox( newExtent );
}
catch ( QgsCsException & )
{
//transform failed, better not proceed
return;
}
}

mComposerMap->beginCommand( tr( "Map extent changed" ) );
mComposerMap->zoomToExtent( newExtent );
mComposerMap->endCommand();
Expand All @@ -487,6 +543,23 @@ void QgsComposerMapWidget::on_mViewExtentInCanvasButton_clicked()

if ( !currentMapExtent.isEmpty() )
{
//transform?
if ( QgisApp::instance()->mapCanvas()->mapSettings().destinationCrs()
!= mComposerMap->crs() )
{
try
{
QgsCoordinateTransform xForm( mComposerMap->crs(),
QgisApp::instance()->mapCanvas()->mapSettings().destinationCrs() );
currentMapExtent = xForm.transformBoundingBox( currentMapExtent );
}
catch ( QgsCsException & )
{
//transform failed, better not proceed
return;
}
}

QgisApp::instance()->mapCanvas()->setExtent( currentMapExtent );
QgisApp::instance()->mapCanvas()->refresh();
}
Expand Down Expand Up @@ -532,6 +605,8 @@ void QgsComposerMapWidget::updateGuiElements()

blockAllSignals( true );

whileBlocking( mCrsSelector )->setCrs( mComposerMap->crs() );

//width, height, scale
double scale = mComposerMap->scale();

Expand Down
2 changes: 2 additions & 0 deletions src/app/composer/qgscomposermapwidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ class QgsComposerMapWidget: public QgsComposerItemBaseWidget, private Ui::QgsCom
void updateOverviewFrameStyleFromWidget();
void cleanUpOverviewFrameStyleSelector( QgsPanelWidget* container );

void mapCrsChanged( const QgsCoordinateReferenceSystem& crs );

private:
QgsComposerMap* mComposerMap;

Expand Down
36 changes: 24 additions & 12 deletions src/core/composer/qgscomposermap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ QgsComposerMap::QgsComposerMap( QgsComposition *composition )

void QgsComposerMap::init()
{
if ( mComposition && mComposition->project() )
mCrs = mComposition->project()->crs();

mGridStack = new QgsComposerMapGridStack( this );
mOverviewStack = new QgsComposerMapOverviewStack( this );
connectUpdateSlot();
Expand Down Expand Up @@ -205,10 +208,12 @@ QgsMapSettings QgsComposerMap::mapSettings( const QgsRectangle& extent, QSizeF s
QgsExpressionContext expressionContext = createExpressionContext();

QgsMapSettings jobMapSettings;
jobMapSettings.setDestinationCrs( mCrs );
jobMapSettings.setCrsTransformEnabled( true );
jobMapSettings.setExtent( extent );
jobMapSettings.setOutputSize( size.toSize() );
jobMapSettings.setOutputDpi( dpi );
jobMapSettings.setMapUnits( crs().mapUnits() );
jobMapSettings.setMapUnits( mCrs.mapUnits() );
jobMapSettings.setBackgroundColor( Qt::transparent );
jobMapSettings.setOutputImageFormat( ms.outputImageFormat() );
jobMapSettings.setRotation( mEvaluatedMapRotation );
Expand All @@ -233,8 +238,6 @@ QgsMapSettings QgsComposerMap::mapSettings( const QgsRectangle& extent, QSizeF s
}
jobMapSettings.setLayers( layers );
jobMapSettings.setLayerStyleOverrides( layerStyleOverridesToRender( expressionContext ) );
jobMapSettings.setDestinationCrs( crs() );
jobMapSettings.setCrsTransformEnabled( ms.hasCrsTransformEnabled() );
jobMapSettings.setFlags( ms.flags() );
jobMapSettings.setFlag( QgsMapSettings::DrawSelection, false );

Expand All @@ -253,7 +256,7 @@ QgsMapSettings QgsComposerMap::mapSettings( const QgsRectangle& extent, QSizeF s
jobMapSettings.setFlag( QgsMapSettings::DrawEditingInfo, false );
jobMapSettings.setFlag( QgsMapSettings::UseAdvancedEffects, mComposition->useAdvancedEffects() ); // respect the composition's useAdvancedEffects flag

jobMapSettings.datumTransformStore() = ms.datumTransformStore();
jobMapSettings.datumTransformStore().setDestinationCrs( mCrs );

return jobMapSettings;
}
Expand Down Expand Up @@ -873,14 +876,12 @@ QgsRectangle* QgsComposerMap::currentMapExtent()

QgsCoordinateReferenceSystem QgsComposerMap::crs() const
{
if ( mComposition )
{
return mComposition->mapSettings().destinationCrs();
}
else
{
return QgsCoordinateReferenceSystem();
}
return mCrs;
}

void QgsComposerMap::setCrs( const QgsCoordinateReferenceSystem& crs )
{
mCrs = crs;
}

const QgsRectangle* QgsComposerMap::currentMapExtent() const
Expand Down Expand Up @@ -1273,6 +1274,10 @@ bool QgsComposerMap::writeXml( QDomElement& elem, QDomDocument & doc ) const
extentElem.setAttribute( QStringLiteral( "ymax" ), qgsDoubleToString( mExtent.yMaximum() ) );
composerMapElem.appendChild( extentElem );

QDomElement crsElem = doc.createElement( QStringLiteral( "crs" ) );
mCrs.writeXml( crsElem, doc );
composerMapElem.appendChild( crsElem );

// follow map theme
composerMapElem.setAttribute( QStringLiteral( "followPreset" ), mFollowVisibilityPreset ? "true" : "false" );
composerMapElem.setAttribute( QStringLiteral( "followPresetName" ), mFollowVisibilityPresetName );
Expand Down Expand Up @@ -1374,6 +1379,13 @@ bool QgsComposerMap::readXml( const QDomElement& itemElem, const QDomDocument& d
setNewExtent( QgsRectangle( xmin, ymin, xmax, ymax ) );
}

QDomNodeList crsNodeList = itemElem.elementsByTagName( QStringLiteral( "crs" ) );
if ( !crsNodeList.isEmpty() )
{
QDomElement crsElem = crsNodeList.at( 0 ).toElement();
mCrs.readXml( crsElem );
}

//map rotation
if ( !qgsDoubleNear( itemElem.attribute( QStringLiteral( "mapRotation" ), QStringLiteral( "0" ) ).toDouble(), 0.0 ) )
{
Expand Down
11 changes: 11 additions & 0 deletions src/core/composer/qgscomposermap.h
Original file line number Diff line number Diff line change
Expand Up @@ -170,9 +170,17 @@ class CORE_EXPORT QgsComposerMap : public QgsComposerItem
/**
* Returns the map's coordinate reference system.
* @note added in QGIS 3.0
* @see setCrs()
*/
QgsCoordinateReferenceSystem crs() const;

/**
* Sets the map's coordinate reference system.
* @see crs()
* @note added in QGIS 3.0
*/
void setCrs( const QgsCoordinateReferenceSystem& crs );

PreviewMode previewMode() const {return mPreviewMode;}
void setPreviewMode( PreviewMode m );

Expand Down Expand Up @@ -454,6 +462,9 @@ class CORE_EXPORT QgsComposerMap : public QgsComposerItem
// so that full rectangle in paper is used.
QgsRectangle mExtent;

//! Map CRS
QgsCoordinateReferenceSystem mCrs;

// Current temporary map region in map units. This is overwritten when atlas feature changes. It's also
// used when the user changes the map extent and an atlas preview is enabled. This allows the user
// to manually tweak each atlas preview page without affecting the actual original map extent.
Expand Down
6 changes: 6 additions & 0 deletions src/core/composer/qgscomposition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2832,6 +2832,12 @@ bool QgsComposition::exportAsPDF( const QString& file )
void QgsComposition::georeferenceOutput( const QString& file, QgsComposerMap* map,
const QRectF& exportRegion, double dpi ) const
{
if ( !map )
map = referenceMap();

if ( !map )
return; // no reference map

if ( dpi < 0 )
dpi = printResolution();

Expand Down
Loading

0 comments on commit 9a6d714

Please sign in to comment.