Skip to content

Commit

Permalink
[feature][layouts] Add option to set geopdf group name for items
Browse files Browse the repository at this point in the history
This new setting, located in the layout item "Rendering" section,
allows users to set an optional "group name" for use in GeoPDF
exports. When set, a matching layer tree group will be created
in the exported GeoPDF and the item will only be visible when
this group is checked.

This allows content to be selectively displayed as a group by
viewers of the GeoPDF. Eg, it can allow extra layout content
such as descriptive labels or legends to only be shown when
layers from the group are visible, making GeoPDF export much
more flexible.
  • Loading branch information
nyalldawson committed Jul 9, 2024
1 parent b45c614 commit d48a0e6
Show file tree
Hide file tree
Showing 11 changed files with 519 additions and 70 deletions.
1 change: 1 addition & 0 deletions python/PyQt6/core/auto_additions/qgslayoutitem.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
QgsLayoutItem.UndoMarginRight = QgsLayoutItem.UndoCommand.UndoMarginRight
QgsLayoutItem.UndoSetId = QgsLayoutItem.UndoCommand.UndoSetId
QgsLayoutItem.UndoRotation = QgsLayoutItem.UndoCommand.UndoRotation
QgsLayoutItem.UndoExportLayerName = QgsLayoutItem.UndoCommand.UndoExportLayerName
QgsLayoutItem.UndoShapeStyle = QgsLayoutItem.UndoCommand.UndoShapeStyle
QgsLayoutItem.UndoShapeCornerRadius = QgsLayoutItem.UndoCommand.UndoShapeCornerRadius
QgsLayoutItem.UndoNodeMove = QgsLayoutItem.UndoCommand.UndoNodeMove
Expand Down
41 changes: 40 additions & 1 deletion python/PyQt6/core/auto_generated/layout/qgslayoutitem.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ Base class for graphical items within a :py:class:`QgsLayout`.
UndoMarginRight,
UndoSetId,
UndoRotation,
UndoExportLayerName,
UndoShapeStyle,
UndoShapeCornerRadius,
UndoNodeMove,
Expand Down Expand Up @@ -432,13 +433,49 @@ Sets the item's parent ``group``.

virtual ExportLayerBehavior exportLayerBehavior() const;
%Docstring
Returns the behavior of this item during exporting to layered exports (e.g. SVG).
Returns the behavior of this item during exporting to layered exports (e.g. SVG or GeoPDF).

.. seealso:: :py:func:`numberExportLayers`

.. seealso:: :py:func:`exportLayerDetails`

.. seealso:: :py:func:`exportLayerName`

.. versionadded:: 3.10
%End

QString exportLayerName() const;
%Docstring
Returns the name for this item during exporting to layered exports (e.g. SVG or GeoPDF).

By default this is an empty string, which indicates that the item does not need to be placed in any specific
layer and will automatically be grouped with other items where possible.

If the layer name is non-empty, then the item will be placed in a group with the corresponding name
during layered exports.

.. seealso:: :py:func:`setExportLayerName`

.. seealso:: :py:func:`exportLayerBehavior`

.. versionadded:: 3.40
%End

void setExportLayerName( const QString &name );
%Docstring
Sets the ``name`` for this item during exporting to layered exports (e.g. SVG or GeoPDF).

If ``name`` is an empty string then the item does not need to be placed in any specific
layer and will automatically be grouped with other items where possible.

If the layer ``name`` is non-empty, then the item will be placed in a group with the corresponding name
during layered exports.

.. seealso:: :py:func:`exportLayerName`

.. seealso:: :py:func:`exportLayerBehavior`

.. versionadded:: 3.40
%End

virtual int numberExportLayers() const /Deprecated/;
Expand Down Expand Up @@ -502,6 +539,8 @@ Moves to the next export part for a multi-layered export item, during a multi-la
double opacity;

QString mapTheme;

QString groupName;
};

virtual QgsLayoutItem::ExportLayerDetail exportLayerDetails() const;
Expand Down
41 changes: 40 additions & 1 deletion python/core/auto_generated/layout/qgslayoutitem.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ Base class for graphical items within a :py:class:`QgsLayout`.
UndoMarginRight,
UndoSetId,
UndoRotation,
UndoExportLayerName,
UndoShapeStyle,
UndoShapeCornerRadius,
UndoNodeMove,
Expand Down Expand Up @@ -432,13 +433,49 @@ Sets the item's parent ``group``.

virtual ExportLayerBehavior exportLayerBehavior() const;
%Docstring
Returns the behavior of this item during exporting to layered exports (e.g. SVG).
Returns the behavior of this item during exporting to layered exports (e.g. SVG or GeoPDF).

.. seealso:: :py:func:`numberExportLayers`

.. seealso:: :py:func:`exportLayerDetails`

.. seealso:: :py:func:`exportLayerName`

.. versionadded:: 3.10
%End

QString exportLayerName() const;
%Docstring
Returns the name for this item during exporting to layered exports (e.g. SVG or GeoPDF).

By default this is an empty string, which indicates that the item does not need to be placed in any specific
layer and will automatically be grouped with other items where possible.

If the layer name is non-empty, then the item will be placed in a group with the corresponding name
during layered exports.

.. seealso:: :py:func:`setExportLayerName`

.. seealso:: :py:func:`exportLayerBehavior`

.. versionadded:: 3.40
%End

void setExportLayerName( const QString &name );
%Docstring
Sets the ``name`` for this item during exporting to layered exports (e.g. SVG or GeoPDF).

If ``name`` is an empty string then the item does not need to be placed in any specific
layer and will automatically be grouped with other items where possible.

If the layer ``name`` is non-empty, then the item will be placed in a group with the corresponding name
during layered exports.

.. seealso:: :py:func:`exportLayerName`

.. seealso:: :py:func:`exportLayerBehavior`

.. versionadded:: 3.40
%End

virtual int numberExportLayers() const /Deprecated/;
Expand Down Expand Up @@ -502,6 +539,8 @@ Moves to the next export part for a multi-layered export item, during a multi-la
double opacity;

QString mapTheme;

QString groupName;
};

virtual QgsLayoutItem::ExportLayerDetail exportLayerDetails() const;
Expand Down
20 changes: 18 additions & 2 deletions src/core/layout/qgslayoutexporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,7 @@ QgsLayoutExporter::ExportResult QgsLayoutExporter::exportToPdf( const QString &f
component.mapLayerId = layerDetail.mapLayerId;
component.opacity = layerDetail.opacity;
component.compositionMode = layerDetail.compositionMode;
component.group = layerDetail.groupName;
if ( !layerDetail.mapTheme.isEmpty() )
{
component.group = layerDetail.mapTheme;
Expand Down Expand Up @@ -1777,6 +1778,7 @@ QgsLayoutExporter::ExportResult QgsLayoutExporter::handleLayeredExport( const QL

int prevType = -1;
QgsLayoutItem::ExportLayerBehavior prevItemBehavior = QgsLayoutItem::CanGroupWithAnyOtherItem;
QString previousItemGroup;
unsigned int layerId = 1;
QgsLayoutItem::ExportLayerDetail layerDetails;
itemHider.hideAll();
Expand All @@ -1787,9 +1789,20 @@ QgsLayoutExporter::ExportResult QgsLayoutExporter::handleLayeredExport( const QL
QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( item );

bool canPlaceInExistingLayer = false;
QString thisItemExportGroupName;
if ( layoutItem )
{
switch ( layoutItem->exportLayerBehavior() )
QgsLayoutItem::ExportLayerBehavior itemExportBehavior = layoutItem->exportLayerBehavior();
thisItemExportGroupName = layoutItem->exportLayerName();
if ( !thisItemExportGroupName.isEmpty() )
{
if ( thisItemExportGroupName != previousItemGroup && !currentLayerItems.empty() )
itemExportBehavior = QgsLayoutItem::MustPlaceInOwnLayer;
else
layerDetails.groupName = thisItemExportGroupName;
}

switch ( itemExportBehavior )
{
case QgsLayoutItem::CanGroupWithAnyOtherItem:
{
Expand Down Expand Up @@ -1838,12 +1851,14 @@ QgsLayoutExporter::ExportResult QgsLayoutExporter::handleLayeredExport( const QL
canPlaceInExistingLayer = false;
break;
}
prevItemBehavior = layoutItem->exportLayerBehavior();
prevItemBehavior = itemExportBehavior;
prevType = layoutItem->type();
previousItemGroup = thisItemExportGroupName;
}
else
{
prevItemBehavior = QgsLayoutItem::MustPlaceInOwnLayer;
previousItemGroup.clear();
}

if ( canPlaceInExistingLayer )
Expand Down Expand Up @@ -1899,6 +1914,7 @@ QgsLayoutExporter::ExportResult QgsLayoutExporter::handleLayeredExport( const QL
{
currentLayerItems << item;
}
layerDetails.groupName = thisItemExportGroupName;
}
}
if ( !currentLayerItems.isEmpty() )
Expand Down
12 changes: 12 additions & 0 deletions src/core/layout/qgslayoutitem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,16 @@ QgsLayoutItem::ExportLayerBehavior QgsLayoutItem::exportLayerBehavior() const
return CanGroupWithAnyOtherItem;
}

QString QgsLayoutItem::exportLayerName() const
{
return mExportLayerName;
}

void QgsLayoutItem::setExportLayerName( const QString &name )
{
mExportLayerName = name;
}

int QgsLayoutItem::numberExportLayers() const
{
return 0;
Expand Down Expand Up @@ -633,6 +643,7 @@ bool QgsLayoutItem::writeXml( QDomElement &parentElement, QDomDocument &doc, con
element.setAttribute( QStringLiteral( "size" ), mItemSize.encodeSize() );
element.setAttribute( QStringLiteral( "itemRotation" ), QString::number( mItemRotation ) );
element.setAttribute( QStringLiteral( "groupUuid" ), mParentGroupUuid );
element.setAttribute( QStringLiteral( "exportLayer" ), mExportLayerName );

element.setAttribute( QStringLiteral( "zValue" ), QString::number( zValue() ) );
element.setAttribute( QStringLiteral( "visibility" ), isVisible() );
Expand Down Expand Up @@ -823,6 +834,7 @@ bool QgsLayoutItem::readXml( const QDomElement &element, const QDomDocument &doc

mExcludeFromExports = element.attribute( QStringLiteral( "excludeFromExports" ), QStringLiteral( "0" ) ).toInt();
mEvaluatedExcludeFromExports = mExcludeFromExports;
mExportLayerName = element.attribute( QStringLiteral( "exportLayer" ) );

const bool result = readPropertiesFromElement( element, doc, context );

Expand Down
45 changes: 44 additions & 1 deletion src/core/layout/qgslayoutitem.h
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ class CORE_EXPORT QgsLayoutItem : public QgsLayoutObject, public QGraphicsRectIt
UndoMarginRight, //!< Right margin (since QGIS 3.30)
UndoSetId, //!< Change item ID
UndoRotation, //!< Rotation adjustment
UndoExportLayerName, //!< Export layer name (since QGIS 3.40)
UndoShapeStyle, //!< Shape symbol style
UndoShapeCornerRadius, //!< Shape corner radius
UndoNodeMove, //!< Node move
Expand Down Expand Up @@ -464,13 +465,46 @@ class CORE_EXPORT QgsLayoutItem : public QgsLayoutObject, public QGraphicsRectIt
};

/**
* Returns the behavior of this item during exporting to layered exports (e.g. SVG).
* Returns the behavior of this item during exporting to layered exports (e.g. SVG or GeoPDF).
*
* \see numberExportLayers()
* \see exportLayerDetails()
* \see exportLayerName()
*
* \since QGIS 3.10
*/
virtual ExportLayerBehavior exportLayerBehavior() const;

/**
* Returns the name for this item during exporting to layered exports (e.g. SVG or GeoPDF).
*
* By default this is an empty string, which indicates that the item does not need to be placed in any specific
* layer and will automatically be grouped with other items where possible.
*
* If the layer name is non-empty, then the item will be placed in a group with the corresponding name
* during layered exports.
*
* \see setExportLayerName()
* \see exportLayerBehavior()
* \since QGIS 3.40
*/
QString exportLayerName() const;

/**
* Sets the \a name for this item during exporting to layered exports (e.g. SVG or GeoPDF).
*
* If \a name is an empty string then the item does not need to be placed in any specific
* layer and will automatically be grouped with other items where possible.
*
* If the layer \a name is non-empty, then the item will be placed in a group with the corresponding name
* during layered exports.
*
* \see exportLayerName()
* \see exportLayerBehavior()
* \since QGIS 3.40
*/
void setExportLayerName( const QString &name );

/**
* Returns the number of layers that this item requires for exporting during layered exports (e.g. SVG).
* Returns 0 if this item is to be placed on the same layer as the previous item,
Expand Down Expand Up @@ -540,6 +574,13 @@ class CORE_EXPORT QgsLayoutItem : public QgsLayoutObject, public QGraphicsRectIt

//! Associated map theme, or an empty string if this export layer does not need to be associated with a map theme
QString mapTheme;

/**
* Associated group name, if this layer is associated with an export group.
*
* \since QGIS 3.40
*/
QString groupName;
};

/**
Expand Down Expand Up @@ -1307,6 +1348,8 @@ class CORE_EXPORT QgsLayoutItem : public QgsLayoutObject, public QGraphicsRectIt
//! Whether item should be excluded in exports
bool mExcludeFromExports = false;

QString mExportLayerName;

/**
* Temporary evaluated item exclusion. Data defined properties may mean
* this value differs from mExcludeFromExports.
Expand Down
Loading

0 comments on commit d48a0e6

Please sign in to comment.