Skip to content

Commit

Permalink
[layouts] API framework to allow rendering multiple map themes to lay…
Browse files Browse the repository at this point in the history
…ered exports
  • Loading branch information
nyalldawson committed Aug 20, 2019
1 parent a726f4f commit f49389d
Show file tree
Hide file tree
Showing 7 changed files with 239 additions and 11 deletions.
24 changes: 24 additions & 0 deletions python/core/auto_generated/layout/qgslayoutrendercontext.sip.in
Expand Up @@ -264,6 +264,30 @@ The default is to use no simplification.

.. seealso:: :py:func:`setSimplifyMethod`

.. versionadded:: 3.10
%End

QStringList exportThemes() const;
%Docstring
Returns a list of map themes to use during the export.

Items which handle layered exports (e.g. maps) may utilise this list to export different
representations of the item as export layers, as they iterate through these included themes.

.. seealso:: :py:func:`setExportThemes`

.. versionadded:: 3.10
%End

void setExportThemes( const QStringList &themes );
%Docstring
Sets a list of map ``themes`` to use during the export.

Items which handle layered exports (e.g. maps) may utilise this list to export different
representations of the item as export layers, as they iterate through these included themes.

.. seealso:: :py:func:`exportThemes`

.. versionadded:: 3.10
%End

Expand Down
3 changes: 3 additions & 0 deletions src/core/layout/qgslayoutexporter.cpp
Expand Up @@ -316,6 +316,7 @@ class LayoutContextSettingsRestorer
, mPreviousTextFormat( layout->renderContext().textRenderFormat() )
, mPreviousExportLayer( layout->renderContext().currentExportLayer() )
, mPreviousSimplifyMethod( layout->renderContext().simplifyMethod() )
, mExportThemes( layout->renderContext().exportThemes() )
{
}
Q_NOWARN_DEPRECATED_POP
Expand All @@ -329,6 +330,7 @@ class LayoutContextSettingsRestorer
mLayout->renderContext().setCurrentExportLayer( mPreviousExportLayer );
Q_NOWARN_DEPRECATED_POP
mLayout->renderContext().setSimplifyMethod( mPreviousSimplifyMethod );
mLayout->renderContext().setExportThemes( mExportThemes );
}

LayoutContextSettingsRestorer( const LayoutContextSettingsRestorer &other ) = delete;
Expand All @@ -341,6 +343,7 @@ class LayoutContextSettingsRestorer
QgsRenderContext::TextRenderFormat mPreviousTextFormat = QgsRenderContext::TextFormatAlwaysOutlines;
int mPreviousExportLayer = 0;
QgsVectorSimplifyMethod mPreviousSimplifyMethod;
QStringList mExportThemes;

};
///@endcond PRIVATE
Expand Down
66 changes: 55 additions & 11 deletions src/core/layout/qgslayoutitemmap.cpp
Expand Up @@ -1007,11 +1007,15 @@ int QgsLayoutItemMap::numberExportLayers() const
void QgsLayoutItemMap::startLayeredExport()
{
mCurrentExportPart = Start;
mExportThemes = mLayout->renderContext().exportThemes();
mExportThemeIt = mExportThemes.begin();
}

void QgsLayoutItemMap::stopLayeredExport()
{
mCurrentExportPart = NotLayered;
mExportThemes.clear();
mExportThemeIt = mExportThemes.begin();
}

bool QgsLayoutItemMap::nextExportPart()
Expand Down Expand Up @@ -1039,6 +1043,12 @@ bool QgsLayoutItemMap::nextExportPart()
mStagedRendererJob.reset(); // no more map layer parts
}

if ( mExportThemeIt != mExportThemes.end() && ++mExportThemeIt != mExportThemes.end() )
{
// move to next theme and continue exporting map layers
return true;
}

if ( mGridStack->hasEnabledItems() )
{
mCurrentExportPart = Grid;
Expand Down Expand Up @@ -1072,8 +1082,16 @@ bool QgsLayoutItemMap::nextExportPart()
FALLTHROUGH

case SelectionBoxes:
mCurrentExportPart = End;
return false;
if ( mExportThemeIt != mExportThemes.end() && mExportThemeIt++ != mExportThemes.end() )
{
mCurrentExportPart = Start;
return nextExportPart();
}
else
{
mCurrentExportPart = End;
return false;
}

case End:
return false;
Expand Down Expand Up @@ -1816,6 +1834,21 @@ void QgsLayoutItemMap::updateToolTip()
setToolTip( displayName() );
}

QString QgsLayoutItemMap::themeToRender( const QgsExpressionContext &context ) const
{
QString presetName;

if ( !mExportThemes.empty() && mExportThemeIt != mExportThemes.end() )
presetName = *mExportThemeIt;
else if ( mFollowVisibilityPreset )
{
presetName = mFollowVisibilityPresetName;
// preset name can be overridden by data-defined one
presetName = mDataDefinedProperties.valueAsString( QgsLayoutObject::MapStylePreset, context, presetName );
}
return presetName;
}

QList<QgsMapLayer *> QgsLayoutItemMap::layersToRender( const QgsExpressionContext *context ) const
{
QgsExpressionContext scopedContext;
Expand All @@ -1825,13 +1858,9 @@ QList<QgsMapLayer *> QgsLayoutItemMap::layersToRender( const QgsExpressionContex

QList<QgsMapLayer *> renderLayers;

if ( mFollowVisibilityPreset )
QString presetName = themeToRender( *evalContext );
if ( !presetName.isEmpty() )
{
QString presetName = mFollowVisibilityPresetName;

// preset name can be overridden by data-defined one
presetName = mDataDefinedProperties.valueAsString( QgsLayoutObject::MapStylePreset, *evalContext, presetName );

if ( mLayout->project()->mapThemeCollection()->hasMapTheme( presetName ) )
renderLayers = mLayout->project()->mapThemeCollection()->mapThemeVisibleLayers( presetName );
else // fallback to using map canvas layers
Expand Down Expand Up @@ -1886,13 +1915,28 @@ QList<QgsMapLayer *> QgsLayoutItemMap::layersToRender( const QgsExpressionContex

QMap<QString, QString> QgsLayoutItemMap::layerStyleOverridesToRender( const QgsExpressionContext &context ) const
{
if ( mFollowVisibilityPreset )
QString presetName = themeToRender( context );
if ( !presetName.isEmpty() )
{
QString presetName = mFollowVisibilityPresetName;
if ( mLayout->project()->mapThemeCollection()->hasMapTheme( presetName ) )
{
if ( presetName != mCachedLayerStyleOverridesPresetName )
{
// have to regenerate cache of style overrides
mCachedPresetLayerStyleOverrides = mLayout->project()->mapThemeCollection()->mapThemeStyleOverrides( presetName );
mCachedLayerStyleOverridesPresetName = presetName;
}

return mCachedPresetLayerStyleOverrides;
}
else
return QMap<QString, QString>();
}
else if ( mFollowVisibilityPreset )
{
QString presetName = mFollowVisibilityPresetName;
// data defined preset name?
presetName = mDataDefinedProperties.valueAsString( QgsLayoutObject::MapStylePreset, context, presetName );

if ( mLayout->project()->mapThemeCollection()->hasMapTheme( presetName ) )
{
if ( presetName.isEmpty() || presetName != mCachedLayerStyleOverridesPresetName )
Expand Down
4 changes: 4 additions & 0 deletions src/core/layout/qgslayoutitemmap.h
Expand Up @@ -776,6 +776,8 @@ class CORE_EXPORT QgsLayoutItemMap : public QgsLayoutItem
//! Resets the item tooltip to reflect current map id
void updateToolTip();

QString themeToRender( const QgsExpressionContext &context ) const;

//! Returns current layer style overrides for this map item
QMap<QString, QString> layerStyleOverridesToRender( const QgsExpressionContext &context ) const;

Expand Down Expand Up @@ -816,6 +818,8 @@ class CORE_EXPORT QgsLayoutItemMap : public QgsLayoutItem
bool shouldDrawPart( PartType part ) const;

PartType mCurrentExportPart = NotLayered;
QStringList mExportThemes;
QStringList::iterator mExportThemeIt;

/**
* Refresh the map's extents, considering data defined extent, scale and rotation
Expand Down
10 changes: 10 additions & 0 deletions src/core/layout/qgslayoutrendercontext.cpp
Expand Up @@ -110,3 +110,13 @@ void QgsLayoutRenderContext::setPagesVisible( bool visible )
{
mPagesVisible = visible;
}

QStringList QgsLayoutRenderContext::exportThemes() const
{
return mExportThemes;
}

void QgsLayoutRenderContext::setExportThemes( const QStringList &exportThemes )
{
mExportThemes = exportThemes;
}
24 changes: 24 additions & 0 deletions src/core/layout/qgslayoutrendercontext.h
Expand Up @@ -259,6 +259,28 @@ class CORE_EXPORT QgsLayoutRenderContext : public QObject
*/
const QgsVectorSimplifyMethod &simplifyMethod() const { return mSimplifyMethod; }

/**
* Returns a list of map themes to use during the export.
*
* Items which handle layered exports (e.g. maps) may utilise this list to export different
* representations of the item as export layers, as they iterate through these included themes.
*
* \see setExportThemes()
* \since QGIS 3.10
*/
QStringList exportThemes() const;

/**
* Sets a list of map \a themes to use during the export.
*
* Items which handle layered exports (e.g. maps) may utilise this list to export different
* representations of the item as export layers, as they iterate through these included themes.
*
* \see exportThemes()
* \since QGIS 3.10
*/
void setExportThemes( const QStringList &themes );

signals:

/**
Expand Down Expand Up @@ -291,6 +313,8 @@ class CORE_EXPORT QgsLayoutRenderContext : public QObject

QgsRenderContext::TextRenderFormat mTextRenderFormat = QgsRenderContext::TextFormatAlwaysOutlines;

QStringList mExportThemes;

QgsVectorSimplifyMethod mSimplifyMethod;

friend class QgsLayoutExporter;
Expand Down
119 changes: 119 additions & 0 deletions tests/src/core/testqgslayoutmap.cpp
Expand Up @@ -1212,6 +1212,125 @@ void TestQgsLayoutMap::testLayeredExport()
QVERIFY( !map->nextExportPart() );
map->stopLayeredExport();

// exporting by theme
QgsMapThemeCollection::MapThemeRecord rec;
rec.setLayerRecords( QList<QgsMapThemeCollection::MapThemeLayerRecord>()
<< QgsMapThemeCollection::MapThemeLayerRecord( linesLayer )
);

p.mapThemeCollection()->insert( QStringLiteral( "test preset" ), rec );
rec.setLayerRecords( QList<QgsMapThemeCollection::MapThemeLayerRecord>()
<< QgsMapThemeCollection::MapThemeLayerRecord( linesLayer )
<< QgsMapThemeCollection::MapThemeLayerRecord( pointsLayer )
);
p.mapThemeCollection()->insert( QStringLiteral( "test preset2" ), rec );
rec.setLayerRecords( QList<QgsMapThemeCollection::MapThemeLayerRecord>()
<< QgsMapThemeCollection::MapThemeLayerRecord( pointsLayer )
);
p.mapThemeCollection()->insert( QStringLiteral( "test preset3" ), rec );

l.renderContext().setExportThemes( QStringList() << QStringLiteral( "test preset2" ) << QStringLiteral( "test preset" ) << QStringLiteral( "test preset3" ) );


map->startLayeredExport();
QVERIFY( map->nextExportPart() );
map->createStagedRenderJob( map->extent(), QSize( 512, 512 ), 72 );
QCOMPARE( map->exportLayerDetails().name, QStringLiteral( "Map 1: Background" ) );
QVERIFY( map->exportLayerDetails().mapLayerId.isEmpty() );
QVERIFY( !map->shouldDrawPart( QgsLayoutItemMap::Grid ) );
QVERIFY( !map->shouldDrawPart( QgsLayoutItemMap::OverviewMapExtent ) );
QVERIFY( !map->shouldDrawPart( QgsLayoutItemMap::Frame ) );
QVERIFY( map->shouldDrawPart( QgsLayoutItemMap::Background ) );
QVERIFY( !map->shouldDrawPart( QgsLayoutItemMap::Layer ) );
QVERIFY( map->nextExportPart() );
QCOMPARE( map->exportLayerDetails().name, QStringLiteral( "Map 1: lines" ) );
QCOMPARE( map->exportLayerDetails().mapLayerId, linesLayer->id() );
QVERIFY( !map->shouldDrawPart( QgsLayoutItemMap::Grid ) );
QVERIFY( !map->shouldDrawPart( QgsLayoutItemMap::OverviewMapExtent ) );
QVERIFY( !map->shouldDrawPart( QgsLayoutItemMap::Frame ) );
QVERIFY( !map->shouldDrawPart( QgsLayoutItemMap::Background ) );
QVERIFY( map->shouldDrawPart( QgsLayoutItemMap::Layer ) );
QVERIFY( map->nextExportPart() );
QCOMPARE( map->exportLayerDetails().name, QStringLiteral( "Map 1: points" ) );
QCOMPARE( map->exportLayerDetails().mapLayerId, pointsLayer->id() );
QVERIFY( !map->shouldDrawPart( QgsLayoutItemMap::Grid ) );
QVERIFY( !map->shouldDrawPart( QgsLayoutItemMap::OverviewMapExtent ) );
QVERIFY( !map->shouldDrawPart( QgsLayoutItemMap::Frame ) );
QVERIFY( !map->shouldDrawPart( QgsLayoutItemMap::Background ) );
QVERIFY( map->shouldDrawPart( QgsLayoutItemMap::Layer ) );
QVERIFY( map->nextExportPart() );
// labels
QCOMPARE( map->exportLayerDetails().name, QStringLiteral( "Map 1: Labels" ) );
QVERIFY( map->exportLayerDetails().mapLayerId.isEmpty() );
QVERIFY( !map->shouldDrawPart( QgsLayoutItemMap::Grid ) );
QVERIFY( !map->shouldDrawPart( QgsLayoutItemMap::OverviewMapExtent ) );
QVERIFY( !map->shouldDrawPart( QgsLayoutItemMap::Frame ) );
QVERIFY( !map->shouldDrawPart( QgsLayoutItemMap::Background ) );
QVERIFY( map->shouldDrawPart( QgsLayoutItemMap::Layer ) );
QVERIFY( map->nextExportPart() );
// "test preset"
map->createStagedRenderJob( map->extent(), QSize( 512, 512 ), 72 );
QCOMPARE( map->exportLayerDetails().name, QStringLiteral( "Map 1: lines" ) );
QCOMPARE( map->exportLayerDetails().mapLayerId, linesLayer->id() );
QVERIFY( !map->shouldDrawPart( QgsLayoutItemMap::Grid ) );
QVERIFY( !map->shouldDrawPart( QgsLayoutItemMap::OverviewMapExtent ) );
QVERIFY( !map->shouldDrawPart( QgsLayoutItemMap::Frame ) );
QVERIFY( !map->shouldDrawPart( QgsLayoutItemMap::Background ) );
QVERIFY( map->shouldDrawPart( QgsLayoutItemMap::Layer ) );
QVERIFY( map->nextExportPart() );
// labels
QCOMPARE( map->exportLayerDetails().name, QStringLiteral( "Map 1: Labels" ) );
QVERIFY( map->exportLayerDetails().mapLayerId.isEmpty() );
QVERIFY( !map->shouldDrawPart( QgsLayoutItemMap::Grid ) );
QVERIFY( !map->shouldDrawPart( QgsLayoutItemMap::OverviewMapExtent ) );
QVERIFY( !map->shouldDrawPart( QgsLayoutItemMap::Frame ) );
QVERIFY( !map->shouldDrawPart( QgsLayoutItemMap::Background ) );
QVERIFY( map->shouldDrawPart( QgsLayoutItemMap::Layer ) );
QVERIFY( map->nextExportPart() );
map->createStagedRenderJob( map->extent(), QSize( 512, 512 ), 72 );
// "test preset 3"
QCOMPARE( map->exportLayerDetails().name, QStringLiteral( "Map 1: points" ) );
QCOMPARE( map->exportLayerDetails().mapLayerId, pointsLayer->id() );
QVERIFY( !map->shouldDrawPart( QgsLayoutItemMap::Grid ) );
QVERIFY( !map->shouldDrawPart( QgsLayoutItemMap::OverviewMapExtent ) );
QVERIFY( !map->shouldDrawPart( QgsLayoutItemMap::Frame ) );
QVERIFY( !map->shouldDrawPart( QgsLayoutItemMap::Background ) );
QVERIFY( map->shouldDrawPart( QgsLayoutItemMap::Layer ) );
QVERIFY( map->nextExportPart() );
// labels
QCOMPARE( map->exportLayerDetails().name, QStringLiteral( "Map 1: Labels" ) );
QVERIFY( map->exportLayerDetails().mapLayerId.isEmpty() );
QVERIFY( !map->shouldDrawPart( QgsLayoutItemMap::Grid ) );
QVERIFY( !map->shouldDrawPart( QgsLayoutItemMap::OverviewMapExtent ) );
QVERIFY( !map->shouldDrawPart( QgsLayoutItemMap::Frame ) );
QVERIFY( !map->shouldDrawPart( QgsLayoutItemMap::Background ) );
QVERIFY( map->shouldDrawPart( QgsLayoutItemMap::Layer ) );
QVERIFY( map->nextExportPart() );
QCOMPARE( map->exportLayerDetails().name, QStringLiteral( "Map 1: Grids" ) );
QVERIFY( map->exportLayerDetails().mapLayerId.isEmpty() );
QVERIFY( map->shouldDrawPart( QgsLayoutItemMap::Grid ) );
QVERIFY( !map->shouldDrawPart( QgsLayoutItemMap::OverviewMapExtent ) );
QVERIFY( !map->shouldDrawPart( QgsLayoutItemMap::Frame ) );
QVERIFY( !map->shouldDrawPart( QgsLayoutItemMap::Background ) );
QVERIFY( !map->shouldDrawPart( QgsLayoutItemMap::Layer ) );
QVERIFY( map->nextExportPart() );
QCOMPARE( map->exportLayerDetails().name, QStringLiteral( "Map 1: Overviews" ) );
QVERIFY( map->exportLayerDetails().mapLayerId.isEmpty() );
QVERIFY( !map->shouldDrawPart( QgsLayoutItemMap::Grid ) );
QVERIFY( map->shouldDrawPart( QgsLayoutItemMap::OverviewMapExtent ) );
QVERIFY( !map->shouldDrawPart( QgsLayoutItemMap::Frame ) );
QVERIFY( !map->shouldDrawPart( QgsLayoutItemMap::Background ) );
QVERIFY( !map->shouldDrawPart( QgsLayoutItemMap::Layer ) );
QVERIFY( map->nextExportPart() );
QCOMPARE( map->exportLayerDetails().name, QStringLiteral( "Map 1: Frame" ) );
QVERIFY( map->exportLayerDetails().mapLayerId.isEmpty() );
QVERIFY( !map->shouldDrawPart( QgsLayoutItemMap::Grid ) );
QVERIFY( !map->shouldDrawPart( QgsLayoutItemMap::OverviewMapExtent ) );
QVERIFY( map->shouldDrawPart( QgsLayoutItemMap::Frame ) );
QVERIFY( !map->shouldDrawPart( QgsLayoutItemMap::Background ) );
QVERIFY( !map->shouldDrawPart( QgsLayoutItemMap::Layer ) );
QVERIFY( !map->nextExportPart() );
map->stopLayeredExport();
}

void TestQgsLayoutMap::testLayeredExportLabelsByLayer()
Expand Down

0 comments on commit f49389d

Please sign in to comment.