Skip to content

Commit

Permalink
[FEATURE] Composer map - optionally store layer styles
Browse files Browse the repository at this point in the history
There is a new check box that allows the user to tell whether a map should
keep the layer styles (it will store the state when the check box is checked).
The stored layer styles keep a snapshot of each layer's configuration
instead of just keeping name of the style.

This solves issues with styles and visibility presets in composer which were
not completely compatible.

This code has been funded by Tuscany Region (Italy) - SITA (CIG: 6002233F59)
and commissioned to Gis3W s.a.s.
  • Loading branch information
wonder-sk committed Jan 19, 2015
1 parent 2d663b5 commit 5266fd9
Show file tree
Hide file tree
Showing 14 changed files with 239 additions and 48 deletions.
7 changes: 7 additions & 0 deletions python/core/composer/qgscomposermap.sip
Expand Up @@ -208,10 +208,17 @@ class QgsComposerMap : QgsComposerItem
/**Stores the current layer set of the qgis mapcanvas in mLayerSet*/
void storeCurrentLayerSet();

/**Getter for flag that determines if current styles of layers should be overridden by previously stored styles. @note added in 2.8 */
bool keepLayerStyles() const;
/**Setter for flag that determines if current styles of layers should be overridden by previously stored styles. @note added in 2.8 */
void setKeepLayerStyles( bool enabled );

/**Getter for stored overrides of styles for layers. @note added in 2.8 */
QMap<QString, QString> layerStyleOverrides() const;
/**Setter for stored overrides of styles for layers. @note added in 2.8 */
void setLayerStyleOverrides( const QMap<QString, QString>& overrides );
/**Stores the current layer styles into style overrides. @note added in 2.8 */
void storeCurrentLayerStyles();

// Set cache outdated
void setCacheUpdated( bool u = false );
Expand Down
14 changes: 12 additions & 2 deletions python/core/qgsmaplayerstylemanager.sip
Expand Up @@ -8,14 +8,17 @@ class QgsMapLayerStyle
//! construct invalid style
QgsMapLayerStyle();

//! construct style from QML definition (XML)
explicit QgsMapLayerStyle( const QString& xmlData );

//! Tell whether the style is valid (i.e. there is something stored in it)
bool isValid() const;

//! Remove any stored style data (will get invalid)
void clear();

//! Return information about the style - for debugging purposes only
QString dump() const;
//! Return XML content of the style
QString xmlData() const;

//! Store layer's active style information in the instance
void readFromLayer( QgsMapLayer* layer );
Expand Down Expand Up @@ -69,4 +72,11 @@ class QgsMapLayerStyleManager
//! Set a different style as the current style - will apply it to the layer
//! @return true on success
bool setCurrentStyle( const QString& name );

//! Temporarily apply a different style to the layer. The argument
//! can be either a style name or a full QML style definition.
//! Each call must be paired with restoreOverrideStyle()
bool setOverrideStyle( const QString& styleDef );
//! Restore the original store after a call to setOverrideStyle()
bool restoreOverrideStyle();
};
44 changes: 34 additions & 10 deletions src/app/composer/qgscomposermapwidget.cpp
Expand Up @@ -23,6 +23,7 @@
#include "qgscomposermapwidget.h"
#include "qgscomposeritemwidget.h"
#include "qgscomposition.h"
#include "qgsmaplayerstylemanager.h"
#include "qgsmaprenderer.h"
#include "qgsstylev2.h"
#include "qgssymbolv2.h"
Expand Down Expand Up @@ -272,14 +273,14 @@ void QgsComposerMapWidget::aboutToShowVisibilityPresetsMenu()
if ( !menu )
return;

QgsVisibilityPresets::PresetRecord rec = QgsVisibilityPresets::instance()->currentStateFromLayerList( mComposerMap->layerSet(), mComposerMap->layerStyleOverrides() );

menu->clear();
foreach ( QString presetName, QgsVisibilityPresets::instance()->presets() )
{
QAction* a = menu->addAction( presetName, this, SLOT( visibilityPresetSelected() ) );
a->setCheckable( true );
if ( rec == QgsVisibilityPresets::instance()->presetState( presetName ) )
QStringList layers = QgsVisibilityPresets::instance()->presetVisibleLayers( presetName );
QMap<QString, QString> styles = QgsVisibilityPresets::instance()->presetStyleOverrides( presetName );
if ( layers == mComposerMap->layerSet() && styles == mComposerMap->layerStyleOverrides() )
a->setChecked( true );
}

Expand All @@ -293,19 +294,16 @@ void QgsComposerMapWidget::visibilityPresetSelected()
if ( !action )
return;

QStringList lst = QgsVisibilityPresets::instance()->presetVisibleLayers( action->text() );
QString presetName = action->text();
QStringList lst = QgsVisibilityPresets::instance()->presetVisibleLayers( presetName );
if ( mComposerMap )
{
mKeepLayerListCheckBox->setChecked( true );
mComposerMap->setLayerSet( lst );
mComposerMap->setLayerStyleOverrides( QgsVisibilityPresets::instance()->presetState( action->text() ).mPerLayerCurrentStyle );

// TODO: switching of styles and per-layer checked legend nodes currently does not play well with each other
// because checked nodes are applied immediately while style is applied only when rendering the map
mKeepLayerStylesCheckBox->setChecked( true );

// also apply legend node check states
foreach ( QString layerID, lst )
QgsVisibilityPresets::instance()->applyPresetCheckedLegendNodesToLayer( action->text(), layerID );
mComposerMap->setLayerStyleOverrides( QgsVisibilityPresets::instance()->presetStyleOverrides( presetName ) );

mComposerMap->cache();
mComposerMap->update();
Expand Down Expand Up @@ -646,6 +644,8 @@ void QgsComposerMapWidget::updateGuiElements()
mKeepLayerListCheckBox->setCheckState( Qt::Unchecked );
}

mKeepLayerStylesCheckBox->setCheckState( mComposerMap->keepLayerStyles() ? Qt::Checked : Qt::Unchecked );

//draw canvas items
if ( mComposerMap->drawCanvasItems() )
{
Expand Down Expand Up @@ -763,6 +763,7 @@ void QgsComposerMapWidget::blockAllSignals( bool b )
mAtlasFixedScaleRadio->blockSignals( b );
mAtlasMarginRadio->blockSignals( b );
mKeepLayerListCheckBox->blockSignals( b );
mKeepLayerStylesCheckBox->blockSignals( b );
mSetToMapCanvasExtentButton->blockSignals( b );
mUpdatePreviewButton->blockSignals( b );

Expand Down Expand Up @@ -877,6 +878,29 @@ void QgsComposerMapWidget::on_mKeepLayerListCheckBox_stateChanged( int state )
QStringList emptyLayerSet;
mComposerMap->setLayerSet( emptyLayerSet );
mComposerMap->setKeepLayerSet( false );

mKeepLayerStylesCheckBox->setChecked( Qt::Unchecked );
}

mKeepLayerStylesCheckBox->setEnabled( state == Qt::Checked );
}

void QgsComposerMapWidget::on_mKeepLayerStylesCheckBox_stateChanged( int state )
{
if ( !mComposerMap )
{
return;
}

if ( state == Qt::Checked )
{
mComposerMap->storeCurrentLayerStyles();
mComposerMap->setKeepLayerStyles( true );
}
else
{
mComposerMap->setLayerStyleOverrides( QMap<QString, QString>() );
mComposerMap->setKeepLayerStyles( false );
}
}

Expand Down
1 change: 1 addition & 0 deletions src/app/composer/qgscomposermapwidget.h
Expand Up @@ -45,6 +45,7 @@ class QgsComposerMapWidget: public QgsComposerItemBaseWidget, private Ui::QgsCom
void on_mViewExtentInCanvasButton_clicked();
void on_mUpdatePreviewButton_clicked();
void on_mKeepLayerListCheckBox_stateChanged( int state );
void on_mKeepLayerStylesCheckBox_stateChanged( int state );
void on_mDrawCanvasItemsCheckBox_stateChanged( int state );
void on_mOverviewFrameMapComboBox_currentIndexChanged( const QString& text );
void on_mOverviewFrameStyleButton_clicked();
Expand Down
37 changes: 36 additions & 1 deletion src/app/qgsvisibilitypresets.cpp
Expand Up @@ -136,7 +136,7 @@ QgsVisibilityPresets::PresetRecord QgsVisibilityPresets::currentStateFromLayerLi
rec.mVisibleLayerIDs << layerID;
addPerLayerCheckedLegendSymbols( rec );
addPerLayerCurrentStyle( rec );
foreach (const QString& layerID, layerStyleOverrides.keys() )
foreach ( const QString& layerID, layerStyleOverrides.keys() )
rec.mPerLayerCurrentStyle[layerID] = layerStyleOverrides[layerID];
return rec;
}
Expand Down Expand Up @@ -225,6 +225,41 @@ void QgsVisibilityPresets::applyPresetCheckedLegendNodesToLayer( const QString&
}
}


QMap<QString, QString> QgsVisibilityPresets::presetStyleOverrides( const QString& presetName )
{
QMap<QString, QString> styleOverrides;
if ( !mPresets.contains( presetName ) )
return styleOverrides;

QStringList lst = QgsVisibilityPresets::instance()->presetVisibleLayers( presetName );
const QgsVisibilityPresets::PresetRecord& rec = mPresets[presetName];
foreach ( const QString& layerID, lst )
{
QgsMapLayer* layer = QgsMapLayerRegistry::instance()->mapLayer( layerID );
if ( !layer )
continue;

// use either the stored style name or the current one if none has been stored
QString overrideStyleName = rec.mPerLayerCurrentStyle.value( layerID, layer->styleManager()->currentStyle() );

// store original style and temporarily apply a style
layer->styleManager()->setOverrideStyle( overrideStyleName );

// set the checked legend nodes
applyPresetCheckedLegendNodesToLayer( presetName, layerID );

// save to overrides
QgsMapLayerStyle layerStyle;
layerStyle.readFromLayer( layer );
styleOverrides[layerID] = layerStyle.xmlData();

layer->styleManager()->restoreOverrideStyle();
}
return styleOverrides;
}


QMenu* QgsVisibilityPresets::menu()
{
return mMenu;
Expand Down
3 changes: 3 additions & 0 deletions src/app/qgsvisibilitypresets.h
Expand Up @@ -89,6 +89,9 @@ class QgsVisibilityPresets : public QObject
//! Create preset record given a list of visible layers (needs to store per-layer checked legend symbols)
PresetRecord currentStateFromLayerList( const QStringList& layerIDs, const QMap<QString, QString>& layerStyleOverrides );

//! Get layer style overrides (for QgsMapSettings) of the visible layers for given preset
QMap<QString, QString> presetStyleOverrides( const QString& presetName );

signals:
void presetsChanged();

Expand Down
57 changes: 50 additions & 7 deletions src/core/composer/qgscomposermap.cpp
Expand Up @@ -52,6 +52,7 @@ QgsComposerMap::QgsComposerMap( QgsComposition *composition, int x, int y, int w
, mMapRotation( 0 )
, mEvaluatedMapRotation( 0 )
, mKeepLayerSet( false )
, mKeepLayerStyles( false )
, mUpdatesEnabled( true )
, mMapCanvas( 0 )
, mDrawCanvasItems( true )
Expand Down Expand Up @@ -96,6 +97,7 @@ QgsComposerMap::QgsComposerMap( QgsComposition *composition )
, mMapRotation( 0 )
, mEvaluatedMapRotation( 0 )
, mKeepLayerSet( false )
, mKeepLayerStyles( false )
, mUpdatesEnabled( true )
, mMapCanvas( 0 )
, mDrawCanvasItems( true )
Expand Down Expand Up @@ -1262,12 +1264,26 @@ bool QgsComposerMap::writeXML( QDomElement& elem, QDomDocument & doc ) const
QDomElement layerElem = doc.createElement( "Layer" );
QDomText layerIdText = doc.createTextNode( *layerIt );
layerElem.appendChild( layerIdText );
if ( mLayerStyleOverrides.contains( *layerIt ) )
layerElem.setAttribute( "style", mLayerStyleOverrides[*layerIt] );
layerSetElem.appendChild( layerElem );
}
composerMapElem.appendChild( layerSetElem );

// override styles
if ( mKeepLayerStyles )
{
QDomElement stylesElem = doc.createElement( "LayerStyles" );
QMap<QString, QString>::const_iterator styleIt = mLayerStyleOverrides.constBegin();
for ( ; styleIt != mLayerStyleOverrides.constEnd(); ++styleIt )
{
QDomElement styleElem = doc.createElement( "LayerStyle" );
styleElem.setAttribute( "layerid", styleIt.key() );
QgsMapLayerStyle style( styleIt.value() );
style.writeXml( styleElem );
stylesElem.appendChild( styleElem );
}
composerMapElem.appendChild( stylesElem );
}

//write a dummy "Grid" element to prevent crashes on pre 2.5 versions (refs #10905)
QDomElement gridElem = doc.createElement( "Grid" );
composerMapElem.appendChild( gridElem );
Expand Down Expand Up @@ -1372,12 +1388,27 @@ bool QgsComposerMap::readXML( const QDomElement& itemElem, const QDomDocument& d
{
const QDomElement& layerIdElement = layerIdNodeList.at( i ).toElement();
layerSet << layerIdElement.text();
if ( layerIdElement.hasAttribute( "style" ) )
mLayerStyleOverrides.insert( layerSet.last(), layerIdElement.attribute( "style" ) );
}
}
mLayerSet = layerSet;

// override styles
QDomNodeList layerStylesNodeList = itemElem.elementsByTagName( "LayerStyles" );
mKeepLayerStyles = layerStylesNodeList.size() > 0;
if ( mKeepLayerStyles )
{
QDomElement layerStylesElem = layerStylesNodeList.at( 0 ).toElement();
QDomNodeList layerStyleNodeList = layerStylesElem.elementsByTagName( "LayerStyle" );
for ( int i = 0; i < layerStyleNodeList.size(); ++i )
{
const QDomElement& layerStyleElement = layerStyleNodeList.at( i ).toElement();
QString layerId = layerStyleElement.attribute( "layerid" );
QgsMapLayerStyle style;
style.readXml( layerStyleElement );
mLayerStyleOverrides.insert( layerId, style.xmlData() );
}
}

mDrawing = false;
mNumCachedLayers = 0;
mCacheUpdated = false;
Expand Down Expand Up @@ -1515,12 +1546,24 @@ void QgsComposerMap::storeCurrentLayerSet()
{
mLayerSet = mComposition->mapSettings().layers();

// also store styles associated with the layers
if ( mKeepLayerStyles )
{
// also store styles associated with the layers
storeCurrentLayerStyles();
}
}

void QgsComposerMap::storeCurrentLayerStyles()
{
mLayerStyleOverrides.clear();
foreach ( const QString& layerID, mLayerSet )
{
if ( QgsMapLayer* ml = QgsMapLayerRegistry::instance()->mapLayer( layerID ) )
mLayerStyleOverrides.insert( layerID, ml->styleManager()->currentStyle() );
if ( QgsMapLayer* layer = QgsMapLayerRegistry::instance()->mapLayer( layerID ) )
{
QgsMapLayerStyle style;
style.readFromLayer( layer );
mLayerStyleOverrides.insert( layerID, style.xmlData() );
}
}
}

Expand Down
9 changes: 9 additions & 0 deletions src/core/composer/qgscomposermap.h
Expand Up @@ -247,10 +247,17 @@ class CORE_EXPORT QgsComposerMap : public QgsComposerItem
/**Stores the current layer set of the qgis mapcanvas in mLayerSet*/
void storeCurrentLayerSet();

/**Getter for flag that determines if current styles of layers should be overridden by previously stored styles. @note added in 2.8 */
bool keepLayerStyles() const { return mKeepLayerStyles; }
/**Setter for flag that determines if current styles of layers should be overridden by previously stored styles. @note added in 2.8 */
void setKeepLayerStyles( bool enabled ) { mKeepLayerStyles = enabled; }

/**Getter for stored overrides of styles for layers. @note added in 2.8 */
QMap<QString, QString> layerStyleOverrides() const { return mLayerStyleOverrides; }
/**Setter for stored overrides of styles for layers. @note added in 2.8 */
void setLayerStyleOverrides( const QMap<QString, QString>& overrides ) { mLayerStyleOverrides = overrides; }
/**Stores the current layer styles into style overrides. @note added in 2.8 */
void storeCurrentLayerStyles();

// Set cache outdated
void setCacheUpdated( bool u = false );
Expand Down Expand Up @@ -852,6 +859,8 @@ class CORE_EXPORT QgsComposerMap : public QgsComposerItem

/**Stored layer list (used if layer live-link mKeepLayerSet is disabled)*/
QStringList mLayerSet;

bool mKeepLayerStyles;
/**Stored style names (value) to be used with particular layer IDs (key) instead of default style */
QMap<QString, QString> mLayerStyleOverrides;

Expand Down

0 comments on commit 5266fd9

Please sign in to comment.