Skip to content

Commit 9a6d714

Browse files
committed
[FEATURE][composer] Allow choice of CRS for map items
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)
1 parent 305f9f0 commit 9a6d714

10 files changed

Lines changed: 226 additions & 52 deletions

File tree

‎python/core/composer/qgscomposermap.sip‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ class QgsComposerMap : QgsComposerItem
128128
// QgsRectangle* currentMapExtent();
129129

130130
QgsCoordinateReferenceSystem crs() const;
131+
void setCrs( const QgsCoordinateReferenceSystem& crs );
131132

132133
PreviewMode previewMode() const;
133134
void setPreviewMode( PreviewMode m );

‎src/app/composer/qgscomposermapwidget.cpp‎

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#include "qgsmapthemecollection.h"
3838
#include "qgsmapthemes.h"
3939
#include "qgisgui.h"
40+
#include "qgscsexception.h"
4041

4142
#include <QMessageBox>
4243

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

103+
connect( mCrsSelector, &QgsProjectionSelectionWidget::crsChanged, this, &QgsComposerMapWidget::mapCrsChanged );
104+
102105
updateGuiElements();
103106
loadGridEntries();
104107
loadOverviewEntries();
@@ -260,6 +263,42 @@ void QgsComposerMapWidget::cleanUpOverviewFrameStyleSelector( QgsPanelWidget* co
260263
mComposerMap->endCommand();
261264
}
262265

266+
void QgsComposerMapWidget::mapCrsChanged( const QgsCoordinateReferenceSystem& crs )
267+
{
268+
if ( !mComposerMap )
269+
{
270+
return;
271+
}
272+
273+
if ( mComposerMap->crs() == crs )
274+
return;
275+
276+
// try to reproject to maintain extent
277+
QgsCoordinateReferenceSystem oldCrs = mComposerMap->crs();
278+
279+
bool updateExtent = false;
280+
QgsRectangle newExtent;
281+
try
282+
{
283+
QgsCoordinateTransform xForm( oldCrs, crs );
284+
QgsRectangle prevExtent = *mComposerMap->currentMapExtent();
285+
newExtent = xForm.transformBoundingBox( prevExtent );
286+
updateExtent = true;
287+
}
288+
catch ( QgsCsException & )
289+
{
290+
//transform failed, don't update extent
291+
}
292+
293+
mComposerMap->beginCommand( tr( "Map CRS changed" ) );
294+
mComposerMap->setCrs( crs );
295+
if ( updateExtent )
296+
mComposerMap->zoomToExtent( newExtent );
297+
mComposerMap->endCommand();
298+
mComposerMap->cache();
299+
mComposerMap->update();
300+
}
301+
263302
void QgsComposerMapWidget::on_mAtlasCheckBox_toggled( bool checked )
264303
{
265304
if ( !mComposerMap )
@@ -471,6 +510,23 @@ void QgsComposerMapWidget::on_mSetToMapCanvasExtentButton_clicked()
471510

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

513+
//transform?
514+
if ( QgisApp::instance()->mapCanvas()->mapSettings().destinationCrs()
515+
!= mComposerMap->crs() )
516+
{
517+
try
518+
{
519+
QgsCoordinateTransform xForm( QgisApp::instance()->mapCanvas()->mapSettings().destinationCrs(),
520+
mComposerMap->crs() );
521+
newExtent = xForm.transformBoundingBox( newExtent );
522+
}
523+
catch ( QgsCsException & )
524+
{
525+
//transform failed, better not proceed
526+
return;
527+
}
528+
}
529+
474530
mComposerMap->beginCommand( tr( "Map extent changed" ) );
475531
mComposerMap->zoomToExtent( newExtent );
476532
mComposerMap->endCommand();
@@ -487,6 +543,23 @@ void QgsComposerMapWidget::on_mViewExtentInCanvasButton_clicked()
487543

488544
if ( !currentMapExtent.isEmpty() )
489545
{
546+
//transform?
547+
if ( QgisApp::instance()->mapCanvas()->mapSettings().destinationCrs()
548+
!= mComposerMap->crs() )
549+
{
550+
try
551+
{
552+
QgsCoordinateTransform xForm( mComposerMap->crs(),
553+
QgisApp::instance()->mapCanvas()->mapSettings().destinationCrs() );
554+
currentMapExtent = xForm.transformBoundingBox( currentMapExtent );
555+
}
556+
catch ( QgsCsException & )
557+
{
558+
//transform failed, better not proceed
559+
return;
560+
}
561+
}
562+
490563
QgisApp::instance()->mapCanvas()->setExtent( currentMapExtent );
491564
QgisApp::instance()->mapCanvas()->refresh();
492565
}
@@ -532,6 +605,8 @@ void QgsComposerMapWidget::updateGuiElements()
532605

533606
blockAllSignals( true );
534607

608+
whileBlocking( mCrsSelector )->setCrs( mComposerMap->crs() );
609+
535610
//width, height, scale
536611
double scale = mComposerMap->scale();
537612

‎src/app/composer/qgscomposermapwidget.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,8 @@ class QgsComposerMapWidget: public QgsComposerItemBaseWidget, private Ui::QgsCom
121121
void updateOverviewFrameStyleFromWidget();
122122
void cleanUpOverviewFrameStyleSelector( QgsPanelWidget* container );
123123

124+
void mapCrsChanged( const QgsCoordinateReferenceSystem& crs );
125+
124126
private:
125127
QgsComposerMap* mComposerMap;
126128

‎src/core/composer/qgscomposermap.cpp‎

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,9 @@ QgsComposerMap::QgsComposerMap( QgsComposition *composition )
124124

125125
void QgsComposerMap::init()
126126
{
127+
if ( mComposition && mComposition->project() )
128+
mCrs = mComposition->project()->crs();
129+
127130
mGridStack = new QgsComposerMapGridStack( this );
128131
mOverviewStack = new QgsComposerMapOverviewStack( this );
129132
connectUpdateSlot();
@@ -205,10 +208,12 @@ QgsMapSettings QgsComposerMap::mapSettings( const QgsRectangle& extent, QSizeF s
205208
QgsExpressionContext expressionContext = createExpressionContext();
206209

207210
QgsMapSettings jobMapSettings;
211+
jobMapSettings.setDestinationCrs( mCrs );
212+
jobMapSettings.setCrsTransformEnabled( true );
208213
jobMapSettings.setExtent( extent );
209214
jobMapSettings.setOutputSize( size.toSize() );
210215
jobMapSettings.setOutputDpi( dpi );
211-
jobMapSettings.setMapUnits( crs().mapUnits() );
216+
jobMapSettings.setMapUnits( mCrs.mapUnits() );
212217
jobMapSettings.setBackgroundColor( Qt::transparent );
213218
jobMapSettings.setOutputImageFormat( ms.outputImageFormat() );
214219
jobMapSettings.setRotation( mEvaluatedMapRotation );
@@ -233,8 +238,6 @@ QgsMapSettings QgsComposerMap::mapSettings( const QgsRectangle& extent, QSizeF s
233238
}
234239
jobMapSettings.setLayers( layers );
235240
jobMapSettings.setLayerStyleOverrides( layerStyleOverridesToRender( expressionContext ) );
236-
jobMapSettings.setDestinationCrs( crs() );
237-
jobMapSettings.setCrsTransformEnabled( ms.hasCrsTransformEnabled() );
238241
jobMapSettings.setFlags( ms.flags() );
239242
jobMapSettings.setFlag( QgsMapSettings::DrawSelection, false );
240243

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

256-
jobMapSettings.datumTransformStore() = ms.datumTransformStore();
259+
jobMapSettings.datumTransformStore().setDestinationCrs( mCrs );
257260

258261
return jobMapSettings;
259262
}
@@ -873,14 +876,12 @@ QgsRectangle* QgsComposerMap::currentMapExtent()
873876

874877
QgsCoordinateReferenceSystem QgsComposerMap::crs() const
875878
{
876-
if ( mComposition )
877-
{
878-
return mComposition->mapSettings().destinationCrs();
879-
}
880-
else
881-
{
882-
return QgsCoordinateReferenceSystem();
883-
}
879+
return mCrs;
880+
}
881+
882+
void QgsComposerMap::setCrs( const QgsCoordinateReferenceSystem& crs )
883+
{
884+
mCrs = crs;
884885
}
885886

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

1277+
QDomElement crsElem = doc.createElement( QStringLiteral( "crs" ) );
1278+
mCrs.writeXml( crsElem, doc );
1279+
composerMapElem.appendChild( crsElem );
1280+
12761281
// follow map theme
12771282
composerMapElem.setAttribute( QStringLiteral( "followPreset" ), mFollowVisibilityPreset ? "true" : "false" );
12781283
composerMapElem.setAttribute( QStringLiteral( "followPresetName" ), mFollowVisibilityPresetName );
@@ -1374,6 +1379,13 @@ bool QgsComposerMap::readXml( const QDomElement& itemElem, const QDomDocument& d
13741379
setNewExtent( QgsRectangle( xmin, ymin, xmax, ymax ) );
13751380
}
13761381

1382+
QDomNodeList crsNodeList = itemElem.elementsByTagName( QStringLiteral( "crs" ) );
1383+
if ( !crsNodeList.isEmpty() )
1384+
{
1385+
QDomElement crsElem = crsNodeList.at( 0 ).toElement();
1386+
mCrs.readXml( crsElem );
1387+
}
1388+
13771389
//map rotation
13781390
if ( !qgsDoubleNear( itemElem.attribute( QStringLiteral( "mapRotation" ), QStringLiteral( "0" ) ).toDouble(), 0.0 ) )
13791391
{

‎src/core/composer/qgscomposermap.h‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,9 +170,17 @@ class CORE_EXPORT QgsComposerMap : public QgsComposerItem
170170
/**
171171
* Returns the map's coordinate reference system.
172172
* @note added in QGIS 3.0
173+
* @see setCrs()
173174
*/
174175
QgsCoordinateReferenceSystem crs() const;
175176

177+
/**
178+
* Sets the map's coordinate reference system.
179+
* @see crs()
180+
* @note added in QGIS 3.0
181+
*/
182+
void setCrs( const QgsCoordinateReferenceSystem& crs );
183+
176184
PreviewMode previewMode() const {return mPreviewMode;}
177185
void setPreviewMode( PreviewMode m );
178186

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

465+
//! Map CRS
466+
QgsCoordinateReferenceSystem mCrs;
467+
457468
// Current temporary map region in map units. This is overwritten when atlas feature changes. It's also
458469
// used when the user changes the map extent and an atlas preview is enabled. This allows the user
459470
// to manually tweak each atlas preview page without affecting the actual original map extent.

‎src/core/composer/qgscomposition.cpp‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2832,6 +2832,12 @@ bool QgsComposition::exportAsPDF( const QString& file )
28322832
void QgsComposition::georeferenceOutput( const QString& file, QgsComposerMap* map,
28332833
const QRectF& exportRegion, double dpi ) const
28342834
{
2835+
if ( !map )
2836+
map = referenceMap();
2837+
2838+
if ( !map )
2839+
return; // no reference map
2840+
28352841
if ( dpi < 0 )
28362842
dpi = printResolution();
28372843

0 commit comments

Comments
 (0)