Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[FEATURE][composer] Data defined map scale, rotation and extents. Fun…
…ded by Canton of Neuchâtel, Switzerland
  • Loading branch information
nyalldawson committed Jul 5, 2014
1 parent 0fd99d2 commit 6d7c5f8
Show file tree
Hide file tree
Showing 6 changed files with 581 additions and 157 deletions.
14 changes: 11 additions & 3 deletions python/core/composer/qgscomposermap.sip
Expand Up @@ -127,7 +127,7 @@ class QgsComposerMap : QgsComposerItem
double scale() const;

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

/**Sets new Extent and changes width, height (and implicitely also scale)*/
void setNewExtent( const QgsRectangle& extent );
Expand Down Expand Up @@ -341,8 +341,14 @@ class QgsComposerMap : QgsComposerItem
way the map is drawn within the item
@note this function was added in version 2.1*/
void setMapRotation( double r );
/**Returns the rotation used for drawing the map within the composer item*/
double mapRotation() const;

/**Returns the rotation used for drawing the map within the composer item
* @returns rotation for map
* @param valueType controls whether the returned value is the user specified rotation,
* or the current evaluated rotation (which may be affected by data driven rotation
* settings).
*/
double mapRotation( PropertyValueType valueType = EvaluatedValue ) const;

void updateItem();

Expand Down Expand Up @@ -506,4 +512,6 @@ class QgsComposerMap : QgsComposerItem
void renderModeUpdateCachedImage();

void overviewExtentChanged();

virtual void refreshDataDefinedProperty( DataDefinedProperty property = AllProperties );
};
174 changes: 140 additions & 34 deletions src/app/composer/qgscomposermapwidget.cpp
Expand Up @@ -103,9 +103,39 @@ QgsComposerMapWidget::QgsComposerMapWidget( QgsComposerMap* composerMap ): QgsCo
connect( atlas, SIGNAL( coverageLayerChanged( QgsVectorLayer* ) ),
this, SLOT( atlasLayerChanged( QgsVectorLayer* ) ) );
connect( atlas, SIGNAL( toggled( bool ) ), this, SLOT( compositionAtlasToggled( bool ) ) );

// repopulate data defined buttons if atlas layer changes
connect( atlas, SIGNAL( coverageLayerChanged( QgsVectorLayer* ) ),
this, SLOT( populateDataDefinedButtons() ) );
connect( atlas, SIGNAL( toggled( bool ) ), this, SLOT( populateDataDefinedButtons() ) );
}
}

//connections for data defined buttons
connect( mScaleDDBtn, SIGNAL( dataDefinedChanged( const QString& ) ), this, SLOT( updateDataDefinedProperty( ) ) );
connect( mScaleDDBtn, SIGNAL( dataDefinedActivated( bool ) ), this, SLOT( updateDataDefinedProperty( ) ) );
connect( mScaleDDBtn, SIGNAL( dataDefinedActivated( bool ) ), mScaleLineEdit, SLOT( setDisabled( bool ) ) );

connect( mMapRotationDDBtn, SIGNAL( dataDefinedChanged( const QString& ) ), this, SLOT( updateDataDefinedProperty( ) ) );
connect( mMapRotationDDBtn, SIGNAL( dataDefinedActivated( bool ) ), this, SLOT( updateDataDefinedProperty( ) ) );
connect( mMapRotationDDBtn, SIGNAL( dataDefinedActivated( bool ) ), mMapRotationSpinBox, SLOT( setDisabled( bool ) ) );

connect( mXMinDDBtn, SIGNAL( dataDefinedChanged( const QString& ) ), this, SLOT( updateDataDefinedProperty( ) ) );
connect( mXMinDDBtn, SIGNAL( dataDefinedActivated( bool ) ), this, SLOT( updateDataDefinedProperty( ) ) );
connect( mXMinDDBtn, SIGNAL( dataDefinedActivated( bool ) ), mXMinLineEdit, SLOT( setDisabled( bool ) ) );

connect( mYMinDDBtn, SIGNAL( dataDefinedChanged( const QString& ) ), this, SLOT( updateDataDefinedProperty( ) ) );
connect( mYMinDDBtn, SIGNAL( dataDefinedActivated( bool ) ), this, SLOT( updateDataDefinedProperty( ) ) );
connect( mYMinDDBtn, SIGNAL( dataDefinedActivated( bool ) ), mYMinLineEdit, SLOT( setDisabled( bool ) ) );

connect( mXMaxDDBtn, SIGNAL( dataDefinedChanged( const QString& ) ), this, SLOT( updateDataDefinedProperty( ) ) );
connect( mXMaxDDBtn, SIGNAL( dataDefinedActivated( bool ) ), this, SLOT( updateDataDefinedProperty( ) ) );
connect( mXMaxDDBtn, SIGNAL( dataDefinedActivated( bool ) ), mXMaxLineEdit, SLOT( setDisabled( bool ) ) );

connect( mYMaxDDBtn, SIGNAL( dataDefinedChanged( const QString& ) ), this, SLOT( updateDataDefinedProperty( ) ) );
connect( mYMaxDDBtn, SIGNAL( dataDefinedActivated( bool ) ), this, SLOT( updateDataDefinedProperty( ) ) );
connect( mYMaxDDBtn, SIGNAL( dataDefinedActivated( bool ) ), mYMaxLineEdit, SLOT( setDisabled( bool ) ) );

updateOverviewSymbolMarker();
updateLineSymbolMarker();

Expand All @@ -117,6 +147,79 @@ QgsComposerMapWidget::~QgsComposerMapWidget()
{
}

void QgsComposerMapWidget::populateDataDefinedButtons()
{
QgsVectorLayer* vl = atlasCoverageLayer();

//block signals from data defined buttons
mScaleDDBtn->blockSignals( true );
mMapRotationDDBtn->blockSignals( true );
mXMinDDBtn->blockSignals( true );
mYMinDDBtn->blockSignals( true );
mXMaxDDBtn->blockSignals( true );
mYMaxDDBtn->blockSignals( true );

//initialise buttons to use atlas coverage layer
mScaleDDBtn->init( vl, mItem->dataDefinedProperty( QgsComposerItem::MapScale ),
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doubleDesc() );
mMapRotationDDBtn->init( vl, mItem->dataDefinedProperty( QgsComposerItem::MapRotation ),
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doubleDesc() );
mXMinDDBtn->init( vl, mItem->dataDefinedProperty( QgsComposerItem::MapXMin ),
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doubleDesc() );
mYMinDDBtn->init( vl, mItem->dataDefinedProperty( QgsComposerItem::MapYMin ),
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doubleDesc() );
mXMaxDDBtn->init( vl, mItem->dataDefinedProperty( QgsComposerItem::MapXMax ),
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doubleDesc() );
mYMaxDDBtn->init( vl, mItem->dataDefinedProperty( QgsComposerItem::MapYMax ),
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::doubleDesc() );

//initial state of controls - disable related controls when dd buttons are active
mScaleLineEdit->setEnabled( !mScaleDDBtn->isActive() );
mMapRotationSpinBox->setEnabled( !mMapRotationDDBtn->isActive() );
mXMinLineEdit->setEnabled( !mXMinDDBtn->isActive() );
mYMinLineEdit->setEnabled( !mYMinDDBtn->isActive() );
mXMaxLineEdit->setEnabled( !mXMaxDDBtn->isActive() );
mYMaxLineEdit->setEnabled( !mYMaxDDBtn->isActive() );

//unblock signals from data defined buttons
mScaleDDBtn->blockSignals( false );
mMapRotationDDBtn->blockSignals( false );
mXMinDDBtn->blockSignals( false );
mYMinDDBtn->blockSignals( false );
mXMaxDDBtn->blockSignals( false );
mYMaxDDBtn->blockSignals( false );
}

QgsComposerItem::DataDefinedProperty QgsComposerMapWidget::ddPropertyForWidget( QgsDataDefinedButton* widget )
{
if ( widget == mScaleDDBtn )
{
return QgsComposerItem::MapScale;
}
else if ( widget == mMapRotationDDBtn )
{
return QgsComposerItem::MapRotation;
}
else if ( widget == mXMinDDBtn )
{
return QgsComposerItem::MapXMin;
}
else if ( widget == mYMinDDBtn )
{
return QgsComposerItem::MapYMin;
}
else if ( widget == mXMaxDDBtn )
{
return QgsComposerItem::MapXMax;
}
else if ( widget == mYMaxDDBtn )
{
return QgsComposerItem::MapYMax;
}

return QgsComposerItem::NoProperty;
}

void QgsComposerMapWidget::compositionAtlasToggled( bool atlasEnabled )
{
if ( atlasEnabled )
Expand Down Expand Up @@ -328,43 +431,45 @@ void QgsComposerMapWidget::on_mMapRotationSpinBox_valueChanged( double value )

void QgsComposerMapWidget::on_mSetToMapCanvasExtentButton_clicked()
{
if ( mComposerMap )
if ( !mComposerMap )
{
QgsRectangle newExtent = mComposerMap->composition()->mapSettings().visibleExtent();

//Make sure the width/height ratio is the same as in current composer map extent.
//This is to keep the map item frame and the page layout fixed
QgsRectangle currentMapExtent = *( mComposerMap->currentMapExtent() );
double currentWidthHeightRatio = currentMapExtent.width() / currentMapExtent.height();
double newWidthHeightRatio = newExtent.width() / newExtent.height();
return;
}

if ( currentWidthHeightRatio < newWidthHeightRatio )
{
//enlarge height of new extent, ensuring the map center stays the same
double newHeight = newExtent.width() / currentWidthHeightRatio;
double deltaHeight = newHeight - newExtent.height();
newExtent.setYMinimum( newExtent.yMinimum() - deltaHeight / 2 );
newExtent.setYMaximum( newExtent.yMaximum() + deltaHeight / 2 );
}
else
{
//enlarge width of new extent, ensuring the map center stays the same
double newWidth = currentWidthHeightRatio * newExtent.height();
double deltaWidth = newWidth - newExtent.width();
newExtent.setXMinimum( newExtent.xMinimum() - deltaWidth / 2 );
newExtent.setXMaximum( newExtent.xMaximum() + deltaWidth / 2 );
}
QgsRectangle newExtent = mComposerMap->composition()->mapSettings().visibleExtent();

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

mComposerMap->beginCommand( tr( "Map extent changed" ) );
mComposerMap->setNewExtent( newExtent );
mComposerMap->endCommand();
if ( currentWidthHeightRatio < newWidthHeightRatio )
{
//enlarge height of new extent, ensuring the map center stays the same
double newHeight = newExtent.width() / currentWidthHeightRatio;
double deltaHeight = newHeight - newExtent.height();
newExtent.setYMinimum( newExtent.yMinimum() - deltaHeight / 2 );
newExtent.setYMaximum( newExtent.yMaximum() + deltaHeight / 2 );
}
else
{
//enlarge width of new extent, ensuring the map center stays the same
double newWidth = currentWidthHeightRatio * newExtent.height();
double deltaWidth = newWidth - newExtent.width();
newExtent.setXMinimum( newExtent.xMinimum() - deltaWidth / 2 );
newExtent.setXMaximum( newExtent.xMaximum() + deltaWidth / 2 );
}

//fill text into line edits
mXMinLineEdit->setText( QString::number( newExtent.xMinimum() ) );
mXMaxLineEdit->setText( QString::number( newExtent.xMaximum() ) );
mYMinLineEdit->setText( QString::number( newExtent.yMinimum() ) );
mYMaxLineEdit->setText( QString::number( newExtent.yMaximum() ) );

mComposerMap->beginCommand( tr( "Map extent changed" ) );
mComposerMap->setNewExtent( newExtent );
mComposerMap->endCommand();
}

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

mMapRotationSpinBox->setValue( mComposerMap->mapRotation() );
mMapRotationSpinBox->setValue( mComposerMap->mapRotation( QgsComposerItem::OriginalValue ) );

//keep layer list check box
if ( mComposerMap->keepLayerSet() )
Expand Down Expand Up @@ -606,8 +711,9 @@ void QgsComposerMapWidget::updateGuiElements()
mAtlasPredefinedScaleRadio->setEnabled( false );
}

blockAllSignals( false );
populateDataDefinedButtons();

blockAllSignals( false );
}

void QgsComposerMapWidget::toggleAtlasScalingOptionsByLayerType()
Expand Down
6 changes: 6 additions & 0 deletions src/app/composer/qgscomposermapwidget.h
Expand Up @@ -108,6 +108,12 @@ class QgsComposerMapWidget: public QgsComposerItemBaseWidget, private Ui::QgsCom
/**Sets the current composer map values to the GUI elements*/
virtual void updateGuiElements();

QgsComposerItem::DataDefinedProperty ddPropertyForWidget( QgsDataDefinedButton *widget );

protected slots:
/**Initializes data defined buttons to current atlas coverage layer*/
void populateDataDefinedButtons();

private slots:

/**Sets the GUI elements to the values of mPicture*/
Expand Down

0 comments on commit 6d7c5f8

Please sign in to comment.