Skip to content

Commit

Permalink
Write out rendered features to temporary layers on finalize
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Aug 17, 2019
1 parent afca6ef commit 3b5388b
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 4 deletions.
38 changes: 37 additions & 1 deletion src/core/layout/qgslayoutgeopdfexporter.cpp
Expand Up @@ -20,6 +20,7 @@
#include "qgslayout.h"
#include "qgslogger.h"
#include "qgsgeometry.h"
#include "qgsvectorfilewriter.h"
#include <QMutex>
#include <QMutexLocker>

Expand Down Expand Up @@ -54,6 +55,9 @@ class QgsGeoPdfRenderedFeatureHandler: public QgsRenderedFeatureHandlerInterface
// transform from map item coordinates to page coordinates
transformed.transform( mMapToLayoutTransform );

// always convert to multitype, to make things consistent
transformed.convertToMultiType();

// we (currently) don't REALLY need a mutex here, because layout maps are always rendered using a single threaded operation.
// but we'll play it safe, just in case this changes in future.
QMutexLocker locker( &mMutex );
Expand Down Expand Up @@ -102,7 +106,7 @@ QMap<QString, QVector<QgsLayoutGeoPdfExporter::RenderedFeature> > QgsLayoutGeoPd
return mMapHandlers.value( map )->renderedFeatures;
}

void QgsLayoutGeoPdfExporter::finalize()
bool QgsLayoutGeoPdfExporter::finalize()
{
// collate all the features from different maps which belong to the same layer, replace their geometries with the rendered feature bounds
for ( auto mapIt = mMapHandlers.constBegin(); mapIt != mMapHandlers.constEnd(); ++mapIt )
Expand All @@ -121,5 +125,37 @@ void QgsLayoutGeoPdfExporter::finalize()
}
}
}

return saveTemporaryLayers();
}

bool QgsLayoutGeoPdfExporter::saveTemporaryLayers()
{
for ( auto it = mCollatedFeatures.constBegin(); it != mCollatedFeatures.constEnd(); ++it )
{
const QString filePath = mTemporaryDir.filePath( it.key() + QStringLiteral( ".gpkg" ) );

mTemporaryFilePaths.insert( it.key(), filePath );
// write out features to disk
const QgsFeatureList features = it.value();
QgsVectorFileWriter writer( filePath, QString(), features.first().fields(), features.first().geometry().wkbType() );
if ( writer.hasError() )
{
mErrorMessage = writer.errorMessage();
QgsDebugMsg( mErrorMessage );
return false;
}
for ( const QgsFeature &feature : features )
{
QgsFeature f = feature;
if ( !writer.addFeature( f, QgsFeatureSink::FastInsert ) )
{
mErrorMessage = writer.errorMessage();
QgsDebugMsg( mErrorMessage );
return false;
}
}
}
return true;
}

17 changes: 16 additions & 1 deletion src/core/layout/qgslayoutgeopdfexporter.h
Expand Up @@ -19,6 +19,7 @@
#include "qgis_core.h"
#include "qgslayoutitemmap.h"
#include <QList>
#include <QTemporaryDir>

#define SIP_NO_FILE

Expand Down Expand Up @@ -88,15 +89,29 @@ class CORE_EXPORT QgsLayoutGeoPdfExporter

/**
* To be called after the rendering operation is complete.
*
* Returns TRUE if the operation was successful, or FALSE if an error occurred. If an error occurred, it
* can be retrieved by calling errorMessage().
*/
void finalize();
bool finalize();

/**
* Returns the last error message encountered during the export.
*/
QString errorMessage() { return mErrorMessage; }

private:

QgsLayout *mLayout = nullptr;
QHash< QgsLayoutItemMap *, QgsGeoPdfRenderedFeatureHandler * > mMapHandlers;

QMap< QString, QgsFeatureList > mCollatedFeatures;
QMap< QString, QString> mTemporaryFilePaths;

QString mErrorMessage;
QTemporaryDir mTemporaryDir;

bool saveTemporaryLayers();

friend class TestQgsLayoutGeoPdfExport;
};
Expand Down
20 changes: 18 additions & 2 deletions tests/src/core/testqgslayoutgeopdfexport.cpp
Expand Up @@ -155,7 +155,7 @@ void TestQgsLayoutGeoPdfExport::testCollectingFeatures()
QCOMPARE( pointFeature3.attribute( 3 ).toInt(), 1 );
QCOMPARE( pointFeature3.attribute( 4 ).toInt(), 1 );
QCOMPARE( pointFeature3.attribute( 5 ).toInt(), 2 );
QCOMPARE( pointGeometry3.asWkt( 1 ), QStringLiteral( "Polygon ((123.8 63.8, 135.1 63.8, 135.1 75, 123.8 75, 123.8 63.8))" ) );
QCOMPARE( pointGeometry3.asWkt( 1 ), QStringLiteral( "MultiPolygon (((123.8 63.8, 135.1 63.8, 135.1 75, 123.8 75, 123.8 63.8)))" ) );

// check second map
pointFeatures = geoPdfExporter.renderedFeatures( map2 ).value( pointsLayer->id() );
Expand All @@ -178,7 +178,7 @@ void TestQgsLayoutGeoPdfExport::testCollectingFeatures()
QCOMPARE( pointFeature3b.attribute( 3 ).toInt(), 1 );
QCOMPARE( pointFeature3b.attribute( 4 ).toInt(), 1 );
QCOMPARE( pointFeature3b.attribute( 5 ).toInt(), 2 );
QCOMPARE( pointGeometry3b.asWkt( 1 ), QStringLiteral( "Polygon ((167 102, 178.2 102, 178.2 113.3, 167 113.3, 167 102))" ) );
QCOMPARE( pointGeometry3b.asWkt( 1 ), QStringLiteral( "MultiPolygon (((167 102, 178.2 102, 178.2 113.3, 167 113.3, 167 102)))" ) );

// finalize and test collation
geoPdfExporter.finalize();
Expand All @@ -187,6 +187,22 @@ void TestQgsLayoutGeoPdfExport::testCollectingFeatures()
QCOMPARE( collatedFeatures.count(), 2 );
QCOMPARE( collatedFeatures.value( pointsLayer->id() ).count(), 32 );
QCOMPARE( collatedFeatures.value( linesLayer->id() ).count(), 6 );

QVERIFY( geoPdfExporter.saveTemporaryLayers() );
QVERIFY( geoPdfExporter.errorMessage().isEmpty() );

// check layers were written
QCOMPARE( geoPdfExporter.mTemporaryFilePaths.count(), 2 );
std::unique_ptr< QgsVectorLayer > pointTemp = qgis::make_unique< QgsVectorLayer >( geoPdfExporter.mTemporaryFilePaths.value( pointsLayer->id(), QString() ) );
QVERIFY( pointTemp->isValid() );
QCOMPARE( pointTemp->featureCount(), 32 );
QCOMPARE( pointTemp->wkbType(), QgsWkbTypes::MultiPolygon );
pointTemp.reset();
std::unique_ptr< QgsVectorLayer > lineTemp = qgis::make_unique< QgsVectorLayer >( geoPdfExporter.mTemporaryFilePaths.value( linesLayer->id(), QString() ) );
QVERIFY( lineTemp->isValid() );
QCOMPARE( lineTemp->featureCount(), 6 );
QCOMPARE( lineTemp->wkbType(), QgsWkbTypes::MultiLineString );
lineTemp.reset();
}

QGSTEST_MAIN( TestQgsLayoutGeoPdfExport )
Expand Down

0 comments on commit 3b5388b

Please sign in to comment.