Skip to content
Permalink
Browse files

[geopdf] Ensure that layer order in GeoPDF layer tree matches project

layer order

Fixes #34910
  • Loading branch information
nyalldawson committed Jun 5, 2020
1 parent e2ec260 commit ceb1dbb86812f0e34cfd61d8e2ab616e80577197
@@ -654,6 +654,7 @@ QgsLayoutExporter::ExportResult QgsLayoutExporter::exportToPdf( const QString &f

details.customLayerTreeGroups = geoPdfExporter->customLayerTreeGroups();
details.initialLayerVisibility = geoPdfExporter->initialLayerVisibility();
details.layerOrder = geoPdfExporter->layerOrder();
details.includeFeatures = settings.includeGeoPdfFeatures;
details.useOgcBestPracticeFormatGeoreferencing = settings.useOgcBestPracticeFormatGeoreferencing;
details.useIso32000ExtensionFormatGeoreferencing = settings.useIso32000ExtensionFormatGeoreferencing;
@@ -22,6 +22,7 @@
#include "qgsgeometry.h"
#include "qgsvectorlayer.h"
#include "qgsvectorfilewriter.h"
#include "qgslayertree.h"

#include <gdal.h>
#include "qgsgdalutils.h"
@@ -143,6 +144,10 @@ QgsLayoutGeoPdfExporter::QgsLayoutGeoPdfExporter( QgsLayout *layout )
mMapHandlers.insert( map, handler );
map->addRenderedFeatureHandler( handler );
}

const QList< QgsMapLayer * > layerOrder = mLayout->project()->layerTreeRoot()->layerOrder();
for ( const QgsMapLayer *layer : layerOrder )
mLayerOrder << layer->id();
}

QgsLayoutGeoPdfExporter::~QgsLayoutGeoPdfExporter()
@@ -68,6 +68,14 @@ class CORE_EXPORT QgsLayoutGeoPdfExporter : public QgsAbstractGeoPdfExporter
*/
QMap< QString, bool > initialLayerVisibility() const { return mInitialLayerVisibility; }

/**
* Optional list of map layer IDs in the order they should be shown in the generated GeoPDF layer tree.
* Layer IDs earlier in the list will appear higher in the GeoPDF layer tree.
*
* \since QGIS 3.14
*/
QStringList layerOrder() const { return mLayerOrder; }

private:

VectorComponentDetail componentDetailForLayerId( const QString &layerId ) override;
@@ -77,6 +85,7 @@ class CORE_EXPORT QgsLayoutGeoPdfExporter : public QgsAbstractGeoPdfExporter

QMap< QString, bool > mInitialLayerVisibility;
QMap< QString, QString > mCustomLayerTreeGroups;
QStringList mLayerOrder;

friend class TestQgsLayoutGeoPdfExport;
};
@@ -257,13 +257,12 @@ QString QgsAbstractGeoPdfExporter::createCompositionXml( const QList<ComponentLa
}
compositionElem.appendChild( metadata );

// layertree
QDomElement layerTree = doc.createElement( QStringLiteral( "LayerTree" ) );
//layerTree.setAttribute( QStringLiteral("displayOnlyOnVisiblePages"), QStringLiteral("true"));
QMap< QString, QSet< QString > > createdLayerIds;
QMap< QString, QDomElement > groupLayerMap;
QMap< QString, QString > customGroupNamesToIds;

QMultiMap< QString, QDomElement > pendingLayerTreeElements;

if ( details.includeFeatures )
{
for ( const VectorComponentDetail &component : qgis::as_const( mVectorComponents ) )
@@ -289,14 +288,14 @@ QString QgsAbstractGeoPdfExporter::createCompositionXml( const QList<ComponentLa
group.setAttribute( QStringLiteral( "name" ), component.group );
group.setAttribute( QStringLiteral( "initiallyVisible" ), groupLayerMap.empty() ? QStringLiteral( "true" ) : QStringLiteral( "false" ) );
group.setAttribute( QStringLiteral( "mutuallyExclusiveGroupId" ), QStringLiteral( "__mutually_exclusive_groups__" ) );
layerTree.appendChild( group );
pendingLayerTreeElements.insert( component.mapLayerId, group );
group.appendChild( layer );
groupLayerMap[ component.group ] = group;
}
}
else
{
layerTree.appendChild( layer );
pendingLayerTreeElements.insert( component.mapLayerId, layer );
}

createdLayerIds[ component.group ].insert( component.mapLayerId );
@@ -329,19 +328,23 @@ QString QgsAbstractGeoPdfExporter::createCompositionXml( const QList<ComponentLa
group.setAttribute( QStringLiteral( "name" ), component.group );
group.setAttribute( QStringLiteral( "initiallyVisible" ), groupLayerMap.empty() ? QStringLiteral( "true" ) : QStringLiteral( "false" ) );
group.setAttribute( QStringLiteral( "mutuallyExclusiveGroupId" ), QStringLiteral( "__mutually_exclusive_groups__" ) );
layerTree.appendChild( group );
pendingLayerTreeElements.insert( component.mapLayerId, group );
group.appendChild( layer );
groupLayerMap[ component.group ] = group;
}
}
else
{
layerTree.appendChild( layer );
pendingLayerTreeElements.insert( component.mapLayerId, layer );
}

createdLayerIds[ component.group ].insert( component.mapLayerId );
}

// layertree
QDomElement layerTree = doc.createElement( QStringLiteral( "LayerTree" ) );
//layerTree.setAttribute( QStringLiteral("displayOnlyOnVisiblePages"), QStringLiteral("true"));

// create custom layer tree entries
for ( auto it = details.customLayerTreeGroups.constBegin(); it != details.customLayerTreeGroups.constEnd(); ++it )
{
@@ -357,6 +360,25 @@ QString QgsAbstractGeoPdfExporter::createCompositionXml( const QList<ComponentLa
layerTree.appendChild( layer );
}

// start by adding layer tree elements with known layer orders
for ( const QString &layerId : details.layerOrder )
{
const QList< QDomElement> elements = pendingLayerTreeElements.values( layerId );
for ( const QDomElement &element : elements )
layerTree.appendChild( element );
}
// then add all the rest (those we don't have an explicit order for)
for ( auto it = pendingLayerTreeElements.constBegin(); it != pendingLayerTreeElements.constEnd(); ++it )
{
if ( details.layerOrder.contains( it.key() ) )
{
// already added this one, just above...
continue;
}

layerTree.appendChild( it.value() );
}

compositionElem.appendChild( layerTree );

// pages
@@ -267,6 +267,15 @@ class CORE_EXPORT QgsAbstractGeoPdfExporter
*/
QMap< QString, bool > initialLayerVisibility;

/**
* Optional list of layer IDs, in the order desired to appear in the generated GeoPDF file.
*
* Layers appearing earlier in the list will show earlier in the GeoPDF layer tree list.
*
* \since QGIS 3.14
*/
QStringList layerOrder;

};

/**
@@ -214,6 +214,7 @@ bool QgsMapRendererTask::run()
exportDetails.dpi = mMapSettings.outputDpi();

exportDetails.layerIdToPdfLayerTreeNameMap = mLayerIdToLayerNameMap;
exportDetails.layerOrder = mMapLayerOrder;

if ( mSaveWorldFile )
{
@@ -453,6 +454,7 @@ void QgsMapRendererTask::prepare()
for ( const QgsMapLayer *layer : layers )
{
mLayerIdToLayerNameMap.insert( layer->id(), layer->name() );
mMapLayerOrder << layer->id();
}

mJob.reset( new QgsMapRendererStagedRenderJob( mMapSettings, QgsMapRendererStagedRenderJob::RenderLabelsByMapLayer ) );
@@ -157,6 +157,7 @@ class CORE_EXPORT QgsMapRendererTask : public QgsTask
QList< QgsAnnotation * > mAnnotations;
QList< QgsMapDecoration * > mDecorations;
QMap< QString, QString> mLayerIdToLayerNameMap;
QStringList mMapLayerOrder;

int mError = 0;

@@ -227,6 +227,7 @@ void TestQgsGeoPdfExport::testComposition()
QgsAbstractGeoPdfExporter::ExportDetails details;
details.layerIdToPdfLayerTreeNameMap.insert( QStringLiteral( "layer1" ), QStringLiteral( "my first layer" ) );
details.initialLayerVisibility.insert( QStringLiteral( "layer2" ), false );
details.layerOrder = QStringList() << QStringLiteral( "layer2" );
QString composition = geoPdfExporter.createCompositionXml( renderedLayers, details );
QgsDebugMsg( composition );
QDomDocument doc;
@@ -254,15 +255,13 @@ void TestQgsGeoPdfExport::testComposition()
QDomNodeList layerTreeList = doc.elementsByTagName( QStringLiteral( "LayerTree" ) ).at( 0 ).toElement().childNodes();
QCOMPARE( layerTreeList.count(), 2 );

layer1Idx = layerTreeList.at( 0 ).toElement().attribute( QStringLiteral( "id" ) ) == QStringLiteral( "layer1" ) ? 0 : 1;
layer2Idx = layer1Idx == 0 ? 1 : 0;
QCOMPARE( layerTreeList.at( layer1Idx ).toElement().attribute( QStringLiteral( "id" ) ), QStringLiteral( "layer1" ) );
QCOMPARE( layerTreeList.at( layer1Idx ).toElement().attribute( QStringLiteral( "name" ) ), QStringLiteral( "my first layer" ) );
QCOMPARE( layerTreeList.at( layer1Idx ).toElement().attribute( QStringLiteral( "initiallyVisible" ) ), QStringLiteral( "true" ) );
QCOMPARE( layerTreeList.at( 1 ).toElement().attribute( QStringLiteral( "id" ) ), QStringLiteral( "layer1" ) );
QCOMPARE( layerTreeList.at( 1 ).toElement().attribute( QStringLiteral( "name" ) ), QStringLiteral( "my first layer" ) );
QCOMPARE( layerTreeList.at( 1 ).toElement().attribute( QStringLiteral( "initiallyVisible" ) ), QStringLiteral( "true" ) );

QCOMPARE( layerTreeList.at( layer2Idx ).toElement().attribute( QStringLiteral( "id" ) ), QStringLiteral( "layer2" ) );
QCOMPARE( layerTreeList.at( layer2Idx ).toElement().attribute( QStringLiteral( "name" ) ), QStringLiteral( "name layer2" ) );
QCOMPARE( layerTreeList.at( layer2Idx ).toElement().attribute( QStringLiteral( "initiallyVisible" ) ), QStringLiteral( "false" ) );
QCOMPARE( layerTreeList.at( 0 ).toElement().attribute( QStringLiteral( "id" ) ), QStringLiteral( "layer2" ) );
QCOMPARE( layerTreeList.at( 0 ).toElement().attribute( QStringLiteral( "name" ) ), QStringLiteral( "name layer2" ) );
QCOMPARE( layerTreeList.at( 0 ).toElement().attribute( QStringLiteral( "initiallyVisible" ) ), QStringLiteral( "false" ) );
}

void TestQgsGeoPdfExport::testMetadata()

0 comments on commit ceb1dbb

Please sign in to comment.
You can’t perform that action at this time.