Skip to content

Commit 6d7c5f8

Browse files
committed
[FEATURE][composer] Data defined map scale, rotation and extents. Funded by Canton of Neuchâtel, Switzerland
1 parent 0fd99d2 commit 6d7c5f8

File tree

6 files changed

+581
-157
lines changed

6 files changed

+581
-157
lines changed

python/core/composer/qgscomposermap.sip

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ class QgsComposerMap : QgsComposerItem
127127
double scale() const;
128128

129129
/**Sets new scale and changes only mExtent*/
130-
void setNewScale( double scaleDenominator );
130+
void setNewScale( double scaleDenominator, bool forceUpdate = true );
131131

132132
/**Sets new Extent and changes width, height (and implicitely also scale)*/
133133
void setNewExtent( const QgsRectangle& extent );
@@ -341,8 +341,14 @@ class QgsComposerMap : QgsComposerItem
341341
way the map is drawn within the item
342342
@note this function was added in version 2.1*/
343343
void setMapRotation( double r );
344-
/**Returns the rotation used for drawing the map within the composer item*/
345-
double mapRotation() const;
344+
345+
/**Returns the rotation used for drawing the map within the composer item
346+
* @returns rotation for map
347+
* @param valueType controls whether the returned value is the user specified rotation,
348+
* or the current evaluated rotation (which may be affected by data driven rotation
349+
* settings).
350+
*/
351+
double mapRotation( PropertyValueType valueType = EvaluatedValue ) const;
346352

347353
void updateItem();
348354

@@ -506,4 +512,6 @@ class QgsComposerMap : QgsComposerItem
506512
void renderModeUpdateCachedImage();
507513

508514
void overviewExtentChanged();
515+
516+
virtual void refreshDataDefinedProperty( DataDefinedProperty property = AllProperties );
509517
};

src/app/composer/qgscomposermapwidget.cpp

Lines changed: 140 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,39 @@ QgsComposerMapWidget::QgsComposerMapWidget( QgsComposerMap* composerMap ): QgsCo
103103
connect( atlas, SIGNAL( coverageLayerChanged( QgsVectorLayer* ) ),
104104
this, SLOT( atlasLayerChanged( QgsVectorLayer* ) ) );
105105
connect( atlas, SIGNAL( toggled( bool ) ), this, SLOT( compositionAtlasToggled( bool ) ) );
106+
107+
// repopulate data defined buttons if atlas layer changes
108+
connect( atlas, SIGNAL( coverageLayerChanged( QgsVectorLayer* ) ),
109+
this, SLOT( populateDataDefinedButtons() ) );
110+
connect( atlas, SIGNAL( toggled( bool ) ), this, SLOT( populateDataDefinedButtons() ) );
106111
}
107112
}
108113

114+
//connections for data defined buttons
115+
connect( mScaleDDBtn, SIGNAL( dataDefinedChanged( const QString& ) ), this, SLOT( updateDataDefinedProperty( ) ) );
116+
connect( mScaleDDBtn, SIGNAL( dataDefinedActivated( bool ) ), this, SLOT( updateDataDefinedProperty( ) ) );
117+
connect( mScaleDDBtn, SIGNAL( dataDefinedActivated( bool ) ), mScaleLineEdit, SLOT( setDisabled( bool ) ) );
118+
119+
connect( mMapRotationDDBtn, SIGNAL( dataDefinedChanged( const QString& ) ), this, SLOT( updateDataDefinedProperty( ) ) );
120+
connect( mMapRotationDDBtn, SIGNAL( dataDefinedActivated( bool ) ), this, SLOT( updateDataDefinedProperty( ) ) );
121+
connect( mMapRotationDDBtn, SIGNAL( dataDefinedActivated( bool ) ), mMapRotationSpinBox, SLOT( setDisabled( bool ) ) );
122+
123+
connect( mXMinDDBtn, SIGNAL( dataDefinedChanged( const QString& ) ), this, SLOT( updateDataDefinedProperty( ) ) );
124+
connect( mXMinDDBtn, SIGNAL( dataDefinedActivated( bool ) ), this, SLOT( updateDataDefinedProperty( ) ) );
125+
connect( mXMinDDBtn, SIGNAL( dataDefinedActivated( bool ) ), mXMinLineEdit, SLOT( setDisabled( bool ) ) );
126+
127+
connect( mYMinDDBtn, SIGNAL( dataDefinedChanged( const QString& ) ), this, SLOT( updateDataDefinedProperty( ) ) );
128+
connect( mYMinDDBtn, SIGNAL( dataDefinedActivated( bool ) ), this, SLOT( updateDataDefinedProperty( ) ) );
129+
connect( mYMinDDBtn, SIGNAL( dataDefinedActivated( bool ) ), mYMinLineEdit, SLOT( setDisabled( bool ) ) );
130+
131+
connect( mXMaxDDBtn, SIGNAL( dataDefinedChanged( const QString& ) ), this, SLOT( updateDataDefinedProperty( ) ) );
132+
connect( mXMaxDDBtn, SIGNAL( dataDefinedActivated( bool ) ), this, SLOT( updateDataDefinedProperty( ) ) );
133+
connect( mXMaxDDBtn, SIGNAL( dataDefinedActivated( bool ) ), mXMaxLineEdit, SLOT( setDisabled( bool ) ) );
134+
135+
connect( mYMaxDDBtn, SIGNAL( dataDefinedChanged( const QString& ) ), this, SLOT( updateDataDefinedProperty( ) ) );
136+
connect( mYMaxDDBtn, SIGNAL( dataDefinedActivated( bool ) ), this, SLOT( updateDataDefinedProperty( ) ) );
137+
connect( mYMaxDDBtn, SIGNAL( dataDefinedActivated( bool ) ), mYMaxLineEdit, SLOT( setDisabled( bool ) ) );
138+
109139
updateOverviewSymbolMarker();
110140
updateLineSymbolMarker();
111141

@@ -117,6 +147,79 @@ QgsComposerMapWidget::~QgsComposerMapWidget()
117147
{
118148
}
119149

150+
void QgsComposerMapWidget::populateDataDefinedButtons()
151+
{
152+
QgsVectorLayer* vl = atlasCoverageLayer();
153+
154+
//block signals from data defined buttons
155+
mScaleDDBtn->blockSignals( true );
156+
mMapRotationDDBtn->blockSignals( true );
157+
mXMinDDBtn->blockSignals( true );
158+
mYMinDDBtn->blockSignals( true );
159+
mXMaxDDBtn->blockSignals( true );
160+
mYMaxDDBtn->blockSignals( true );
161+
162+
//initialise buttons to use atlas coverage layer
163+
mScaleDDBtn->init( vl, mItem->dataDefinedProperty( QgsComposerItem::MapScale ),
164+
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doubleDesc() );
165+
mMapRotationDDBtn->init( vl, mItem->dataDefinedProperty( QgsComposerItem::MapRotation ),
166+
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doubleDesc() );
167+
mXMinDDBtn->init( vl, mItem->dataDefinedProperty( QgsComposerItem::MapXMin ),
168+
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doubleDesc() );
169+
mYMinDDBtn->init( vl, mItem->dataDefinedProperty( QgsComposerItem::MapYMin ),
170+
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doubleDesc() );
171+
mXMaxDDBtn->init( vl, mItem->dataDefinedProperty( QgsComposerItem::MapXMax ),
172+
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doubleDesc() );
173+
mYMaxDDBtn->init( vl, mItem->dataDefinedProperty( QgsComposerItem::MapYMax ),
174+
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doubleDesc() );
175+
176+
//initial state of controls - disable related controls when dd buttons are active
177+
mScaleLineEdit->setEnabled( !mScaleDDBtn->isActive() );
178+
mMapRotationSpinBox->setEnabled( !mMapRotationDDBtn->isActive() );
179+
mXMinLineEdit->setEnabled( !mXMinDDBtn->isActive() );
180+
mYMinLineEdit->setEnabled( !mYMinDDBtn->isActive() );
181+
mXMaxLineEdit->setEnabled( !mXMaxDDBtn->isActive() );
182+
mYMaxLineEdit->setEnabled( !mYMaxDDBtn->isActive() );
183+
184+
//unblock signals from data defined buttons
185+
mScaleDDBtn->blockSignals( false );
186+
mMapRotationDDBtn->blockSignals( false );
187+
mXMinDDBtn->blockSignals( false );
188+
mYMinDDBtn->blockSignals( false );
189+
mXMaxDDBtn->blockSignals( false );
190+
mYMaxDDBtn->blockSignals( false );
191+
}
192+
193+
QgsComposerItem::DataDefinedProperty QgsComposerMapWidget::ddPropertyForWidget( QgsDataDefinedButton* widget )
194+
{
195+
if ( widget == mScaleDDBtn )
196+
{
197+
return QgsComposerItem::MapScale;
198+
}
199+
else if ( widget == mMapRotationDDBtn )
200+
{
201+
return QgsComposerItem::MapRotation;
202+
}
203+
else if ( widget == mXMinDDBtn )
204+
{
205+
return QgsComposerItem::MapXMin;
206+
}
207+
else if ( widget == mYMinDDBtn )
208+
{
209+
return QgsComposerItem::MapYMin;
210+
}
211+
else if ( widget == mXMaxDDBtn )
212+
{
213+
return QgsComposerItem::MapXMax;
214+
}
215+
else if ( widget == mYMaxDDBtn )
216+
{
217+
return QgsComposerItem::MapYMax;
218+
}
219+
220+
return QgsComposerItem::NoProperty;
221+
}
222+
120223
void QgsComposerMapWidget::compositionAtlasToggled( bool atlasEnabled )
121224
{
122225
if ( atlasEnabled )
@@ -328,43 +431,45 @@ void QgsComposerMapWidget::on_mMapRotationSpinBox_valueChanged( double value )
328431

329432
void QgsComposerMapWidget::on_mSetToMapCanvasExtentButton_clicked()
330433
{
331-
if ( mComposerMap )
434+
if ( !mComposerMap )
332435
{
333-
QgsRectangle newExtent = mComposerMap->composition()->mapSettings().visibleExtent();
334-
335-
//Make sure the width/height ratio is the same as in current composer map extent.
336-
//This is to keep the map item frame and the page layout fixed
337-
QgsRectangle currentMapExtent = *( mComposerMap->currentMapExtent() );
338-
double currentWidthHeightRatio = currentMapExtent.width() / currentMapExtent.height();
339-
double newWidthHeightRatio = newExtent.width() / newExtent.height();
436+
return;
437+
}
340438

341-
if ( currentWidthHeightRatio < newWidthHeightRatio )
342-
{
343-
//enlarge height of new extent, ensuring the map center stays the same
344-
double newHeight = newExtent.width() / currentWidthHeightRatio;
345-
double deltaHeight = newHeight - newExtent.height();
346-
newExtent.setYMinimum( newExtent.yMinimum() - deltaHeight / 2 );
347-
newExtent.setYMaximum( newExtent.yMaximum() + deltaHeight / 2 );
348-
}
349-
else
350-
{
351-
//enlarge width of new extent, ensuring the map center stays the same
352-
double newWidth = currentWidthHeightRatio * newExtent.height();
353-
double deltaWidth = newWidth - newExtent.width();
354-
newExtent.setXMinimum( newExtent.xMinimum() - deltaWidth / 2 );
355-
newExtent.setXMaximum( newExtent.xMaximum() + deltaWidth / 2 );
356-
}
439+
QgsRectangle newExtent = mComposerMap->composition()->mapSettings().visibleExtent();
357440

358-
//fill text into line edits
359-
mXMinLineEdit->setText( QString::number( newExtent.xMinimum() ) );
360-
mXMaxLineEdit->setText( QString::number( newExtent.xMaximum() ) );
361-
mYMinLineEdit->setText( QString::number( newExtent.yMinimum() ) );
362-
mYMaxLineEdit->setText( QString::number( newExtent.yMaximum() ) );
441+
//Make sure the width/height ratio is the same as in current composer map extent.
442+
//This is to keep the map item frame and the page layout fixed
443+
QgsRectangle currentMapExtent = *( mComposerMap->currentMapExtent() );
444+
double currentWidthHeightRatio = currentMapExtent.width() / currentMapExtent.height();
445+
double newWidthHeightRatio = newExtent.width() / newExtent.height();
363446

364-
mComposerMap->beginCommand( tr( "Map extent changed" ) );
365-
mComposerMap->setNewExtent( newExtent );
366-
mComposerMap->endCommand();
447+
if ( currentWidthHeightRatio < newWidthHeightRatio )
448+
{
449+
//enlarge height of new extent, ensuring the map center stays the same
450+
double newHeight = newExtent.width() / currentWidthHeightRatio;
451+
double deltaHeight = newHeight - newExtent.height();
452+
newExtent.setYMinimum( newExtent.yMinimum() - deltaHeight / 2 );
453+
newExtent.setYMaximum( newExtent.yMaximum() + deltaHeight / 2 );
367454
}
455+
else
456+
{
457+
//enlarge width of new extent, ensuring the map center stays the same
458+
double newWidth = currentWidthHeightRatio * newExtent.height();
459+
double deltaWidth = newWidth - newExtent.width();
460+
newExtent.setXMinimum( newExtent.xMinimum() - deltaWidth / 2 );
461+
newExtent.setXMaximum( newExtent.xMaximum() + deltaWidth / 2 );
462+
}
463+
464+
//fill text into line edits
465+
mXMinLineEdit->setText( QString::number( newExtent.xMinimum() ) );
466+
mXMaxLineEdit->setText( QString::number( newExtent.xMaximum() ) );
467+
mYMinLineEdit->setText( QString::number( newExtent.yMinimum() ) );
468+
mYMaxLineEdit->setText( QString::number( newExtent.yMaximum() ) );
469+
470+
mComposerMap->beginCommand( tr( "Map extent changed" ) );
471+
mComposerMap->setNewExtent( newExtent );
472+
mComposerMap->endCommand();
368473
}
369474

370475
void QgsComposerMapWidget::on_mViewExtentInCanvasButton_clicked()
@@ -473,7 +578,7 @@ void QgsComposerMapWidget::updateGuiElements()
473578
mYMinLineEdit->setText( QString::number( composerMapExtent.yMinimum(), 'f', 3 ) );
474579
mYMaxLineEdit->setText( QString::number( composerMapExtent.yMaximum(), 'f', 3 ) );
475580

476-
mMapRotationSpinBox->setValue( mComposerMap->mapRotation() );
581+
mMapRotationSpinBox->setValue( mComposerMap->mapRotation( QgsComposerItem::OriginalValue ) );
477582

478583
//keep layer list check box
479584
if ( mComposerMap->keepLayerSet() )
@@ -606,8 +711,9 @@ void QgsComposerMapWidget::updateGuiElements()
606711
mAtlasPredefinedScaleRadio->setEnabled( false );
607712
}
608713

609-
blockAllSignals( false );
714+
populateDataDefinedButtons();
610715

716+
blockAllSignals( false );
611717
}
612718

613719
void QgsComposerMapWidget::toggleAtlasScalingOptionsByLayerType()

src/app/composer/qgscomposermapwidget.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,12 @@ class QgsComposerMapWidget: public QgsComposerItemBaseWidget, private Ui::QgsCom
108108
/**Sets the current composer map values to the GUI elements*/
109109
virtual void updateGuiElements();
110110

111+
QgsComposerItem::DataDefinedProperty ddPropertyForWidget( QgsDataDefinedButton *widget );
112+
113+
protected slots:
114+
/**Initializes data defined buttons to current atlas coverage layer*/
115+
void populateDataDefinedButtons();
116+
111117
private slots:
112118

113119
/**Sets the GUI elements to the values of mPicture*/

0 commit comments

Comments
 (0)