mirror of
https://github.com/qgis/QGIS.git
synced 2025-03-03 00:02:25 -05:00
[FEATURE][processing] Export print layout atlas as image(s) algorithm
This commit is contained in:
parent
88b3f43f9f
commit
40a37c5990
@ -91,6 +91,7 @@ SET(QGIS_ANALYSIS_SRCS
|
||||
processing/qgsalgorithmjoinbynearest.cpp
|
||||
processing/qgsalgorithmjoinwithlines.cpp
|
||||
processing/qgsalgorithmkmeansclustering.cpp
|
||||
processing/qgsalgorithmlayoutatlastoimage.cpp
|
||||
processing/qgsalgorithmlayouttoimage.cpp
|
||||
processing/qgsalgorithmlayouttopdf.cpp
|
||||
processing/qgsalgorithmlinedensity.cpp
|
||||
|
250
src/analysis/processing/qgsalgorithmlayoutatlastoimage.cpp
Normal file
250
src/analysis/processing/qgsalgorithmlayoutatlastoimage.cpp
Normal file
@ -0,0 +1,250 @@
|
||||
/***************************************************************************
|
||||
qgsalgorithmlayoutatlastoimage.cpp
|
||||
---------------------
|
||||
begin : June 2020
|
||||
copyright : (C) 2020 by Mathieu Pellerin
|
||||
email : nirvn dot asia at gmail dot com
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "qgsalgorithmlayoutatlastoimage.h"
|
||||
#include "qgslayout.h"
|
||||
#include "qgslayoutatlas.h"
|
||||
#include "qgsprintlayout.h"
|
||||
#include "qgsprocessingoutputs.h"
|
||||
#include "qgslayoutexporter.h"
|
||||
|
||||
#include <QImageWriter>
|
||||
|
||||
///@cond PRIVATE
|
||||
|
||||
QString QgsLayoutAtlasToImageAlgorithm::name() const
|
||||
{
|
||||
return QStringLiteral( "printlayoutatlastoimage" );
|
||||
}
|
||||
|
||||
QString QgsLayoutAtlasToImageAlgorithm::displayName() const
|
||||
{
|
||||
return QObject::tr( "Export print layout atlas as image(s)" );
|
||||
}
|
||||
|
||||
QStringList QgsLayoutAtlasToImageAlgorithm::tags() const
|
||||
{
|
||||
return QObject::tr( "layout,atlas,composer,composition,save,png,jpeg,jpg" ).split( ',' );
|
||||
}
|
||||
|
||||
QString QgsLayoutAtlasToImageAlgorithm::group() const
|
||||
{
|
||||
return QObject::tr( "Cartography" );
|
||||
}
|
||||
|
||||
QString QgsLayoutAtlasToImageAlgorithm::groupId() const
|
||||
{
|
||||
return QStringLiteral( "cartography" );
|
||||
}
|
||||
|
||||
QString QgsLayoutAtlasToImageAlgorithm::shortDescription() const
|
||||
{
|
||||
return QObject::tr( "Exports a print layout atlas as one or more images." );
|
||||
}
|
||||
|
||||
QString QgsLayoutAtlasToImageAlgorithm::shortHelpString() const
|
||||
{
|
||||
return QObject::tr( "This algorithm outputs a print layout atkas as one or more images file (e.g. PNG or JPEG images)." );
|
||||
}
|
||||
|
||||
void QgsLayoutAtlasToImageAlgorithm::initAlgorithm( const QVariantMap & )
|
||||
{
|
||||
addParameter( new QgsProcessingParameterLayout( QStringLiteral( "LAYOUT" ), QObject::tr( "Print layout" ) ) );
|
||||
|
||||
addParameter( new QgsProcessingParameterVectorLayer( QStringLiteral( "COVERAGE_LAYER" ), QObject::tr( "Atlas coverage layer" ) ) );
|
||||
|
||||
addParameter( new QgsProcessingParameterExpression( QStringLiteral( "FILTER_EXPRESSION" ), QObject::tr( "Atlas coverage layer's filter expression" ), QString(), QStringLiteral( "COVERAGE_LAYER" ), true ) );
|
||||
|
||||
addParameter( new QgsProcessingParameterExpression( QStringLiteral( "SORTBY_EXPRESSION" ), QObject::tr( "Atlas coverage layer's feature sort expression" ), QString(), QStringLiteral( "COVERAGE_LAYER" ), true ) );
|
||||
addParameter( new QgsProcessingParameterBoolean( QStringLiteral( "SORTBY_REVERSE" ), QObject::tr( "Reverse sort order (used when a sort expression is provided)" ), false, true ) );
|
||||
|
||||
addParameter( new QgsProcessingParameterExpression( QStringLiteral( "FILENAME_EXPRESSION" ), QObject::tr( "Output filename expression" ), QStringLiteral( "'output_'||@atlas_featurenumber" ), QStringLiteral( "COVERAGE_LAYER" ) ) );
|
||||
|
||||
addParameter( new QgsProcessingParameterFile( QStringLiteral( "FOLDER" ), QObject::tr( "Output folder" ), QgsProcessingParameterFile::Folder ) );
|
||||
|
||||
QStringList imageFormats;
|
||||
const auto supportedImageFormats { QImageWriter::supportedImageFormats() };
|
||||
for ( const auto format : QImageWriter::supportedImageFormats() )
|
||||
{
|
||||
if ( format == QByteArray( "svg" ) )
|
||||
continue;
|
||||
imageFormats << QString( format );
|
||||
}
|
||||
std::unique_ptr< QgsProcessingParameterEnum > extensionParam = qgis::make_unique< QgsProcessingParameterEnum >( QStringLiteral( "EXTENSION" ), QObject::tr( "Image format" ), imageFormats, false, imageFormats.indexOf( QStringLiteral( "png" ) ) );
|
||||
extensionParam->setFlags( extensionParam->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
|
||||
addParameter( extensionParam.release() );
|
||||
|
||||
std::unique_ptr< QgsProcessingParameterNumber > dpiParam = qgis::make_unique< QgsProcessingParameterNumber >( QStringLiteral( "DPI" ), QObject::tr( "DPI (leave blank for default layout DPI)" ), QgsProcessingParameterNumber::Double, QVariant(), true, 0 );
|
||||
dpiParam->setFlags( dpiParam->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
|
||||
addParameter( dpiParam.release() );
|
||||
|
||||
std::unique_ptr< QgsProcessingParameterBoolean > appendGeorefParam = qgis::make_unique< QgsProcessingParameterBoolean >( QStringLiteral( "GEOREFERENCE" ), QObject::tr( "Generate world file" ), true );
|
||||
appendGeorefParam->setFlags( appendGeorefParam->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
|
||||
addParameter( appendGeorefParam.release() );
|
||||
|
||||
std::unique_ptr< QgsProcessingParameterBoolean > exportRDFParam = qgis::make_unique< QgsProcessingParameterBoolean >( QStringLiteral( "INCLUDE_METADATA" ), QObject::tr( "Export RDF metadata (title, author, etc.)" ), true );
|
||||
exportRDFParam->setFlags( exportRDFParam->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
|
||||
addParameter( exportRDFParam.release() );
|
||||
|
||||
std::unique_ptr< QgsProcessingParameterBoolean > antialias = qgis::make_unique< QgsProcessingParameterBoolean >( QStringLiteral( "ANTIALIAS" ), QObject::tr( "Enable antialiasing" ), true );
|
||||
antialias->setFlags( antialias->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
|
||||
addParameter( antialias.release() );
|
||||
}
|
||||
|
||||
QgsProcessingAlgorithm::Flags QgsLayoutAtlasToImageAlgorithm::flags() const
|
||||
{
|
||||
return QgsProcessingAlgorithm::flags() | FlagNoThreading;
|
||||
}
|
||||
|
||||
QgsLayoutAtlasToImageAlgorithm *QgsLayoutAtlasToImageAlgorithm::createInstance() const
|
||||
{
|
||||
return new QgsLayoutAtlasToImageAlgorithm();
|
||||
}
|
||||
|
||||
QVariantMap QgsLayoutAtlasToImageAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
|
||||
{
|
||||
// this needs to be done in main thread, layouts are not thread safe
|
||||
QgsPrintLayout *layout = parameterAsLayout( parameters, QStringLiteral( "LAYOUT" ), context );
|
||||
if ( !layout )
|
||||
throw QgsProcessingException( QObject::tr( "Cannot find layout with name \"%1\"" ).arg( parameters.value( QStringLiteral( "LAYOUT" ) ).toString() ) );
|
||||
|
||||
QString error;
|
||||
QgsLayoutAtlas *atlas = layout->atlas();
|
||||
|
||||
QgsVectorLayer *previousCoverageLayer = atlas->coverageLayer();
|
||||
const bool previousEnabled = atlas->enabled();
|
||||
const QString previousFilenameExpression = atlas->filenameExpression();
|
||||
const QString previousFilterExpression = atlas->filterExpression();
|
||||
const bool previousSortFeatures = atlas->sortFeatures();
|
||||
const bool previousSortAscending = atlas->sortAscending();
|
||||
const QString previousSortExpression = atlas->sortExpression();
|
||||
|
||||
auto restoreAtlas = [ atlas,
|
||||
previousCoverageLayer,
|
||||
previousEnabled,
|
||||
previousFilenameExpression,
|
||||
previousFilterExpression,
|
||||
previousSortFeatures,
|
||||
previousSortAscending,
|
||||
previousSortExpression ]()
|
||||
{
|
||||
QString error;
|
||||
atlas->setEnabled( previousEnabled );
|
||||
atlas->setCoverageLayer( previousCoverageLayer );
|
||||
atlas->setFilenameExpression( previousFilenameExpression, error );
|
||||
atlas->setFilterExpression( previousFilterExpression, error );
|
||||
atlas->setSortFeatures( previousSortFeatures );
|
||||
atlas->setSortAscending( previousSortAscending );
|
||||
atlas->setSortExpression( previousSortExpression );
|
||||
};
|
||||
|
||||
QgsVectorLayer *layer = parameterAsVectorLayer( parameters, QStringLiteral( "COVERAGE_LAYER" ), context );
|
||||
atlas->setEnabled( true );
|
||||
atlas->setCoverageLayer( layer );
|
||||
|
||||
QString expression = parameterAsString( parameters, QStringLiteral( "FILENAME_EXPRESSION" ), context );
|
||||
atlas->setFilenameExpression( expression, error );
|
||||
if ( !error.isEmpty() )
|
||||
{
|
||||
restoreAtlas();
|
||||
throw QgsProcessingException( QObject::tr( "Error setting atlas filename expression" ) );
|
||||
}
|
||||
|
||||
expression = parameterAsString( parameters, QStringLiteral( "FILTER_EXPRESSION" ), context );
|
||||
atlas->setFilterExpression( expression, error );
|
||||
|
||||
expression = parameterAsString( parameters, QStringLiteral( "SORTBY_EXPRESSION" ), context );
|
||||
if ( !expression.isEmpty() )
|
||||
{
|
||||
const bool sortByReverse = parameterAsBool( parameters, QStringLiteral( "SORTBY_REVERSE" ), context );
|
||||
atlas->setSortFeatures( true );
|
||||
atlas->setSortExpression( expression );
|
||||
atlas->setSortAscending( !sortByReverse );
|
||||
}
|
||||
else
|
||||
{
|
||||
atlas->setSortFeatures( false );
|
||||
}
|
||||
|
||||
const QString directory = parameterAsFileOutput( parameters, QStringLiteral( "FOLDER" ), context );
|
||||
QString fileName = QDir( directory ).filePath( QStringLiteral( "atlas" ) );
|
||||
|
||||
QStringList imageFormats;
|
||||
const auto supportedImageFormats { QImageWriter::supportedImageFormats() };
|
||||
for ( const auto format : QImageWriter::supportedImageFormats() )
|
||||
{
|
||||
if ( format == QByteArray( "svg" ) )
|
||||
continue;
|
||||
imageFormats << QString( format );
|
||||
}
|
||||
int idx = parameterAsEnum( parameters, QStringLiteral( "EXTENSION" ), context );
|
||||
QString extension = '.' + imageFormats.at( idx );
|
||||
|
||||
QgsLayoutExporter exporter( layout );
|
||||
QgsLayoutExporter::ImageExportSettings settings;
|
||||
|
||||
if ( parameters.value( QStringLiteral( "DPI" ) ).isValid() )
|
||||
{
|
||||
settings.dpi = parameterAsDouble( parameters, QStringLiteral( "DPI" ), context );
|
||||
}
|
||||
|
||||
settings.exportMetadata = parameterAsBool( parameters, QStringLiteral( "INCLUDE_METADATA" ), context );
|
||||
settings.generateWorldFile = parameterAsBool( parameters, QStringLiteral( "GEOREFERENCE" ), context );
|
||||
|
||||
if ( parameterAsBool( parameters, QStringLiteral( "ANTIALIAS" ), context ) )
|
||||
settings.flags = settings.flags | QgsLayoutRenderContext::FlagAntialiasing;
|
||||
else
|
||||
settings.flags = settings.flags & ~QgsLayoutRenderContext::FlagAntialiasing;
|
||||
|
||||
QgsLayoutExporter::ExportResult result = exporter.exportToImage( atlas, fileName, extension, settings, error, feedback );
|
||||
restoreAtlas();
|
||||
|
||||
switch ( result )
|
||||
{
|
||||
case QgsLayoutExporter::Success:
|
||||
{
|
||||
feedback->pushInfo( QObject::tr( "Successfully exported layout to %1" ).arg( QDir::toNativeSeparators( directory ) ) );
|
||||
break;
|
||||
}
|
||||
|
||||
case QgsLayoutExporter::FileError:
|
||||
throw QgsProcessingException( QObject::tr( "Cannot write to %1.\n\nThis file may be open in another application." ).arg( QDir::toNativeSeparators( directory ) ) );
|
||||
|
||||
case QgsLayoutExporter::MemoryError:
|
||||
throw QgsProcessingException( QObject::tr( "Trying to create the image "
|
||||
"resulted in a memory overflow.\n\n"
|
||||
"Please try a lower resolution or a smaller paper size." ) );
|
||||
|
||||
case QgsLayoutExporter::IteratorError:
|
||||
throw QgsProcessingException( QObject::tr( "Error encountered while exporting atlas." ) );
|
||||
|
||||
case QgsLayoutExporter::SvgLayerError:
|
||||
case QgsLayoutExporter::PrintError:
|
||||
case QgsLayoutExporter::Canceled:
|
||||
// no meaning for imageexports, will not be encountered
|
||||
break;
|
||||
}
|
||||
|
||||
feedback->setProgress( 100 );
|
||||
|
||||
QVariantMap outputs;
|
||||
outputs.insert( QStringLiteral( "FOLDER" ), directory );
|
||||
return outputs;
|
||||
}
|
||||
|
||||
///@endcond
|
||||
|
60
src/analysis/processing/qgsalgorithmlayoutatlastoimage.h
Normal file
60
src/analysis/processing/qgsalgorithmlayoutatlastoimage.h
Normal file
@ -0,0 +1,60 @@
|
||||
/***************************************************************************
|
||||
qgsalgorithmlayoutatlastoimage.h
|
||||
---------------------
|
||||
begin : June 2020
|
||||
copyright : (C) 2020 by Mathieu Pellerin
|
||||
email : nirvn dot asia at gmail dot com
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef QGSALGORITHMLAYOUTATLASTOIMAGE_H
|
||||
#define QGSALGORITHMLAYOUTATLASTOIMAGE_H
|
||||
|
||||
#define SIP_NO_FILE
|
||||
|
||||
#include "qgis_sip.h"
|
||||
#include "qgsprocessingalgorithm.h"
|
||||
|
||||
///@cond PRIVATE
|
||||
|
||||
/**
|
||||
* Native export layout to image algorithm.
|
||||
*/
|
||||
class QgsLayoutAtlasToImageAlgorithm : public QgsProcessingAlgorithm
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
QgsLayoutAtlasToImageAlgorithm() = default;
|
||||
void initAlgorithm( const QVariantMap &configuration = QVariantMap() ) override;
|
||||
Flags flags() const override;
|
||||
QString name() const override;
|
||||
QString displayName() const override;
|
||||
QStringList tags() const override;
|
||||
QString group() const override;
|
||||
QString groupId() const override;
|
||||
QString shortDescription() const override;
|
||||
QString shortHelpString() const override;
|
||||
QgsLayoutAtlasToImageAlgorithm *createInstance() const override SIP_FACTORY;
|
||||
|
||||
protected:
|
||||
|
||||
QVariantMap processAlgorithm( const QVariantMap ¶meters,
|
||||
QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
|
||||
|
||||
|
||||
};
|
||||
|
||||
///@endcond PRIVATE
|
||||
|
||||
#endif // QGSALGORITHMLAYOUTATLASTOIMAGE_H
|
||||
|
||||
|
@ -87,6 +87,7 @@
|
||||
#include "qgsalgorithminterpolatepoint.h"
|
||||
#include "qgsalgorithmintersection.h"
|
||||
#include "qgsalgorithmkmeansclustering.h"
|
||||
#include "qgsalgorithmlayoutatlastoimage.h"
|
||||
#include "qgsalgorithmlayouttoimage.h"
|
||||
#include "qgsalgorithmlayouttopdf.h"
|
||||
#include "qgsalgorithmlinedensity.h"
|
||||
@ -313,6 +314,7 @@ void QgsNativeAlgorithms::loadAlgorithms()
|
||||
addAlgorithm( new QgsKMeansClusteringAlgorithm() );
|
||||
addAlgorithm( new QgsLayerToBookmarksAlgorithm() );
|
||||
addAlgorithm( new QgsLayoutMapExtentToLayerAlgorithm() );
|
||||
addAlgorithm( new QgsLayoutAtlasToImageAlgorithm() );
|
||||
addAlgorithm( new QgsLayoutToImageAlgorithm() );
|
||||
addAlgorithm( new QgsLayoutToPdfAlgorithm() );
|
||||
addAlgorithm( new QgsLineDensityAlgorithm() );
|
||||
|
@ -5913,6 +5913,16 @@ font-style: italic;</string>
|
||||
<string>Obstacles</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_17">
|
||||
<item>
|
||||
<widget class="QLabel" name="mLblNoObstacle">
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>When activated, featuring acting as obstacles discourage labels and diagrams from covering them.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_16" stretch="0,0,0,0">
|
||||
<item>
|
||||
@ -5928,7 +5938,7 @@ font-style: italic;</string>
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Discourage labels and diagrams from covering features</string>
|
||||
<string>Features act as obstacles</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
Loading…
x
Reference in New Issue
Block a user