diff --git a/src/analysis/CMakeLists.txt b/src/analysis/CMakeLists.txt index 99ee8cdfac0..7077f5fa42d 100644 --- a/src/analysis/CMakeLists.txt +++ b/src/analysis/CMakeLists.txt @@ -88,6 +88,8 @@ SET(QGIS_ANALYSIS_SRCS processing/qgsalgorithmjoinbynearest.cpp processing/qgsalgorithmjoinwithlines.cpp processing/qgsalgorithmkmeansclustering.cpp + processing/qgsalgorithmlayouttoimage.cpp + processing/qgsalgorithmlayouttopdf.cpp processing/qgsalgorithmlinedensity.cpp processing/qgsalgorithmlineintersection.cpp processing/qgsalgorithmlinesubstring.cpp @@ -409,6 +411,7 @@ INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/src/core/labeling ${CMAKE_SOURCE_DIR}/src/core/processing ${CMAKE_SOURCE_DIR}/src/core/raster + ${CMAKE_SOURCE_DIR}/src/core/layout ${CMAKE_SOURCE_DIR}/src/core/mesh ${CMAKE_SOURCE_DIR}/src/core/providers/meshmemory ${CMAKE_SOURCE_DIR}/src/core/symbology diff --git a/src/analysis/processing/qgsalgorithmlayouttoimage.cpp b/src/analysis/processing/qgsalgorithmlayouttoimage.cpp new file mode 100644 index 00000000000..954d6c0320f --- /dev/null +++ b/src/analysis/processing/qgsalgorithmlayouttoimage.cpp @@ -0,0 +1,168 @@ +/*************************************************************************** + qgsalgorithmlayouttoimage.cpp + --------------------- + begin : June 2020 + copyright : (C) 2020 by Nyall Dawson + email : nyall dot dawson 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 "qgsalgorithmlayouttoimage.h" +#include "qgslayout.h" +#include "qgsprintlayout.h" +#include "qgsprocessingoutputs.h" +#include "qgslayoutexporter.h" +#include + +///@cond PRIVATE + +QString QgsLayoutToImageAlgorithm::name() const +{ + return QStringLiteral( "printlayouttoimage" ); +} + +QString QgsLayoutToImageAlgorithm::displayName() const +{ + return QObject::tr( "Export print layout as image" ); +} + +QStringList QgsLayoutToImageAlgorithm::tags() const +{ + return QObject::tr( "layout,composer,composition,save,png,jpeg,jpg" ).split( ',' ); +} + +QString QgsLayoutToImageAlgorithm::group() const +{ + return QObject::tr( "Cartography" ); +} + +QString QgsLayoutToImageAlgorithm::groupId() const +{ + return QStringLiteral( "cartography" ); +} + +QString QgsLayoutToImageAlgorithm::shortDescription() const +{ + return QObject::tr( "Exports a print layout as an image." ); +} + +QString QgsLayoutToImageAlgorithm::shortHelpString() const +{ + return QObject::tr( "This algorithm outputs a print layout as an image file (e.g. PNG or JPEG images)." ); +} + +void QgsLayoutToImageAlgorithm::initAlgorithm( const QVariantMap & ) +{ + addParameter( new QgsProcessingParameterLayout( QStringLiteral( "LAYOUT" ), QObject::tr( "Print layout" ) ) ); + + 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() ); + + QStringList imageFilters; + const auto supportedImageFormats { QImageWriter::supportedImageFormats() }; + for ( const QByteArray &format : supportedImageFormats ) + { + if ( format == "svg" ) + continue; + + QString longName = format.toUpper() + QObject::tr( " format" ); + QString glob = "*." + format; + + if ( format == "png" && !imageFilters.empty() ) + imageFilters.insert( 0, QStringLiteral( "%1 (%2 %3)" ).arg( longName, glob.toLower(), glob.toUpper() ) ); + else + imageFilters.append( QStringLiteral( "%1 (%2 %3)" ).arg( longName, glob.toLower(), glob.toUpper() ) ); + } + + addParameter( new QgsProcessingParameterFileDestination( QStringLiteral( "OUTPUT" ), QObject::tr( "Image file" ), imageFilters.join( QStringLiteral( ";;" ) ) ) ); +} + +QgsProcessingAlgorithm::Flags QgsLayoutToImageAlgorithm::flags() const +{ + return QgsProcessingAlgorithm::flags() | FlagNoThreading; +} + +QgsLayoutToImageAlgorithm *QgsLayoutToImageAlgorithm::createInstance() const +{ + return new QgsLayoutToImageAlgorithm(); +} + +QVariantMap QgsLayoutToImageAlgorithm::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() ) ); + + const QString dest = parameterAsFileOutput( parameters, QStringLiteral( "OUTPUT" ), context ); + + 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; + + switch ( exporter.exportToImage( dest, settings ) ) + { + case QgsLayoutExporter::Success: + { + feedback->pushInfo( QObject::tr( "Successfully exported layout to %1" ).arg( QDir::toNativeSeparators( dest ) ) ); + break; + } + + case QgsLayoutExporter::FileError: + throw QgsProcessingException( QObject::tr( "Cannot write to %1.\n\nThis file may be open in another application." ).arg( QDir::toNativeSeparators( dest ) ) ); + + 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::SvgLayerError: + case QgsLayoutExporter::IteratorError: + case QgsLayoutExporter::Canceled: + case QgsLayoutExporter::PrintError: + // no meaning for imageexports, will not be encountered + break; + } + + feedback->setProgress( 100 ); + + QVariantMap outputs; + outputs.insert( QStringLiteral( "OUTPUT" ), dest ); + return outputs; +} + +///@endcond + diff --git a/src/analysis/processing/qgsalgorithmlayouttoimage.h b/src/analysis/processing/qgsalgorithmlayouttoimage.h new file mode 100644 index 00000000000..d295f8e419b --- /dev/null +++ b/src/analysis/processing/qgsalgorithmlayouttoimage.h @@ -0,0 +1,60 @@ +/*************************************************************************** + qgsalgorithmlayouttoimage.h + --------------------- + begin : June 2020 + copyright : (C) 2020 by Nyall Dawson + email : nyall dot dawson 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 QGSALGORITHMLAYOUTTOIMAGE_H +#define QGSALGORITHMLAYOUTTOIMAGE_H + +#define SIP_NO_FILE + +#include "qgis_sip.h" +#include "qgsprocessingalgorithm.h" + +///@cond PRIVATE + +/** + * Native export layout to image algorithm. + */ +class QgsLayoutToImageAlgorithm : public QgsProcessingAlgorithm +{ + + public: + + QgsLayoutToImageAlgorithm() = 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; + QgsLayoutToImageAlgorithm *createInstance() const override SIP_FACTORY; + + protected: + + QVariantMap processAlgorithm( const QVariantMap ¶meters, + QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override; + + +}; + +///@endcond PRIVATE + +#endif // QGSALGORITHMLAYOUTTOIMAGE_H + + diff --git a/src/analysis/processing/qgsalgorithmlayouttopdf.cpp b/src/analysis/processing/qgsalgorithmlayouttopdf.cpp new file mode 100644 index 00000000000..abe5f67296d --- /dev/null +++ b/src/analysis/processing/qgsalgorithmlayouttopdf.cpp @@ -0,0 +1,175 @@ +/*************************************************************************** + qgsalgorithmlayouttopdf.cpp + --------------------- + begin : June 2020 + copyright : (C) 2020 by Nyall Dawson + email : nyall dot dawson 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 "qgsalgorithmlayouttopdf.h" +#include "qgslayout.h" +#include "qgsprintlayout.h" +#include "qgsprocessingoutputs.h" +#include "qgslayoutexporter.h" + +///@cond PRIVATE + +QString QgsLayoutToPdfAlgorithm::name() const +{ + return QStringLiteral( "printlayouttopdf" ); +} + +QString QgsLayoutToPdfAlgorithm::displayName() const +{ + return QObject::tr( "Export print layout as PDF" ); +} + +QStringList QgsLayoutToPdfAlgorithm::tags() const +{ + return QObject::tr( "layout,composer,composition,save" ).split( ',' ); +} + +QString QgsLayoutToPdfAlgorithm::group() const +{ + return QObject::tr( "Cartography" ); +} + +QString QgsLayoutToPdfAlgorithm::groupId() const +{ + return QStringLiteral( "cartography" ); +} + +QString QgsLayoutToPdfAlgorithm::shortDescription() const +{ + return QObject::tr( "Exports a print layout as a PDF." ); +} + +QString QgsLayoutToPdfAlgorithm::shortHelpString() const +{ + return QObject::tr( "This algorithm outputs a print layout as a PDF file." ); +} + +void QgsLayoutToPdfAlgorithm::initAlgorithm( const QVariantMap & ) +{ + addParameter( new QgsProcessingParameterLayout( QStringLiteral( "LAYOUT" ), QObject::tr( "Print layout" ) ) ); + + 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 > forceVectorParam = qgis::make_unique< QgsProcessingParameterBoolean >( QStringLiteral( "FORCE_VECTOR" ), QObject::tr( "Always export as vectors" ), false ); + forceVectorParam->setFlags( forceVectorParam->flags() | QgsProcessingParameterDefinition::FlagAdvanced ); + addParameter( forceVectorParam.release() ); + + std::unique_ptr< QgsProcessingParameterBoolean > appendGeorefParam = qgis::make_unique< QgsProcessingParameterBoolean >( QStringLiteral( "GEOREFERENCE" ), QObject::tr( "Append georeference information" ), 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 > disableTiled = qgis::make_unique< QgsProcessingParameterBoolean >( QStringLiteral( "DISABLE_TILED" ), QObject::tr( "Disable tiled raster layer exports" ), false ); + disableTiled->setFlags( disableTiled->flags() | QgsProcessingParameterDefinition::FlagAdvanced ); + addParameter( disableTiled.release() ); + + std::unique_ptr< QgsProcessingParameterBoolean > simplify = qgis::make_unique< QgsProcessingParameterBoolean >( QStringLiteral( "SIMPLIFY" ), QObject::tr( "Simplify geometries to reduce output file size" ), true ); + simplify->setFlags( simplify->flags() | QgsProcessingParameterDefinition::FlagAdvanced ); + addParameter( simplify.release() ); + + QStringList textExportOptions + { + QObject::tr( "Always Export Text as Paths (Recommended" ), + QObject::tr( "Always Export Text as Text Objects" ) + }; + + std::unique_ptr< QgsProcessingParameterEnum > textFormat = qgis::make_unique< QgsProcessingParameterEnum >( QStringLiteral( "TEXT_FORMAT" ), QObject::tr( "Text export" ), textExportOptions, false, 0 ); + textFormat->setFlags( textFormat->flags() | QgsProcessingParameterDefinition::FlagAdvanced ); + addParameter( textFormat.release() ); + + addParameter( new QgsProcessingParameterFileDestination( QStringLiteral( "OUTPUT" ), QObject::tr( "PDF file" ), QObject::tr( "PDF Format" ) + " (*.pdf *.PDF)" ) ); +} + +QgsProcessingAlgorithm::Flags QgsLayoutToPdfAlgorithm::flags() const +{ + return QgsProcessingAlgorithm::flags() | FlagNoThreading; +} + +QgsLayoutToPdfAlgorithm *QgsLayoutToPdfAlgorithm::createInstance() const +{ + return new QgsLayoutToPdfAlgorithm(); +} + +QVariantMap QgsLayoutToPdfAlgorithm::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() ) ); + + const QString dest = parameterAsFileOutput( parameters, QStringLiteral( "OUTPUT" ), context ); + + QgsLayoutExporter exporter( layout ); + QgsLayoutExporter::PdfExportSettings settings; + + if ( parameters.value( QStringLiteral( "DPI" ) ).isValid() ) + { + settings.dpi = parameterAsDouble( parameters, QStringLiteral( "DPI" ), context ); + } + + settings.forceVectorOutput = parameterAsBool( parameters, QStringLiteral( "FORCE_VECTOR" ), context ); + settings.appendGeoreference = parameterAsBool( parameters, QStringLiteral( "GEOREFERENCE" ), context ); + settings.exportMetadata = parameterAsBool( parameters, QStringLiteral( "INCLUDE_METADATA" ), context ); + settings.exportMetadata = parameterAsBool( parameters, QStringLiteral( "INCLUDE_METADATA" ), context ); + settings.simplifyGeometries = parameterAsBool( parameters, QStringLiteral( "SIMPLIFY" ), context ); + settings.textRenderFormat = parameterAsEnum( parameters, QStringLiteral( "TEXT_FORMAT" ), context ) == 0 ? QgsRenderContext::TextFormatAlwaysOutlines : QgsRenderContext::TextFormatAlwaysText; + + if ( parameterAsBool( parameters, QStringLiteral( "DISABLE_TILED" ), context ) ) + settings.flags = settings.flags | QgsLayoutRenderContext::FlagDisableTiledRasterLayerRenders; + else + settings.flags = settings.flags & ~QgsLayoutRenderContext::FlagDisableTiledRasterLayerRenders; + + switch ( exporter.exportToPdf( dest, settings ) ) + { + case QgsLayoutExporter::Success: + { + feedback->pushInfo( QObject::tr( "Successfully exported layout to %1" ).arg( QDir::toNativeSeparators( dest ) ) ); + break; + } + + case QgsLayoutExporter::FileError: + throw QgsProcessingException( QObject::tr( "Cannot write to %1.\n\nThis file may be open in another application." ).arg( QDir::toNativeSeparators( dest ) ) ); + + case QgsLayoutExporter::PrintError: + throw QgsProcessingException( QObject::tr( "Could not create print device." ) ); + + case QgsLayoutExporter::MemoryError: + throw QgsProcessingException( QObject::tr( "Exporting the PDF " + "resulted in a memory overflow.\n\n" + "Please try a lower resolution or a smaller paper size." ) ); + + case QgsLayoutExporter::SvgLayerError: + case QgsLayoutExporter::IteratorError: + case QgsLayoutExporter::Canceled: + // no meaning for PDF exports, will not be encountered + break; + } + + feedback->setProgress( 100 ); + + QVariantMap outputs; + outputs.insert( QStringLiteral( "OUTPUT" ), dest ); + return outputs; +} + +///@endcond + diff --git a/src/analysis/processing/qgsalgorithmlayouttopdf.h b/src/analysis/processing/qgsalgorithmlayouttopdf.h new file mode 100644 index 00000000000..9e9a90a8d06 --- /dev/null +++ b/src/analysis/processing/qgsalgorithmlayouttopdf.h @@ -0,0 +1,60 @@ +/*************************************************************************** + qgsalgorithmlayouttopdf.h + --------------------- + begin : June 2020 + copyright : (C) 2020 by Nyall Dawson + email : nyall dot dawson 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 QGSALGORITHMLAYOUTTOPDF_H +#define QGSALGORITHMLAYOUTTOPDF_H + +#define SIP_NO_FILE + +#include "qgis_sip.h" +#include "qgsprocessingalgorithm.h" + +///@cond PRIVATE + +/** + * Native export layout to PDF algorithm. + */ +class QgsLayoutToPdfAlgorithm : public QgsProcessingAlgorithm +{ + + public: + + QgsLayoutToPdfAlgorithm() = 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; + QgsLayoutToPdfAlgorithm *createInstance() const override SIP_FACTORY; + + protected: + + QVariantMap processAlgorithm( const QVariantMap ¶meters, + QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override; + + +}; + +///@endcond PRIVATE + +#endif // QGSALGORITHMLAYOUTTOPDF_H + + diff --git a/src/analysis/processing/qgsnativealgorithms.cpp b/src/analysis/processing/qgsnativealgorithms.cpp index 2667662c752..bea6697312c 100644 --- a/src/analysis/processing/qgsnativealgorithms.cpp +++ b/src/analysis/processing/qgsnativealgorithms.cpp @@ -84,6 +84,8 @@ #include "qgsalgorithminterpolatepoint.h" #include "qgsalgorithmintersection.h" #include "qgsalgorithmkmeansclustering.h" +#include "qgsalgorithmlayouttoimage.h" +#include "qgsalgorithmlayouttopdf.h" #include "qgsalgorithmlinedensity.h" #include "qgsalgorithmlineintersection.h" #include "qgsalgorithmlinesubstring.h" @@ -301,6 +303,8 @@ void QgsNativeAlgorithms::loadAlgorithms() addAlgorithm( new QgsKMeansClusteringAlgorithm() ); addAlgorithm( new QgsLayerToBookmarksAlgorithm() ); addAlgorithm( new QgsLayoutMapExtentToLayerAlgorithm() ); + addAlgorithm( new QgsLayoutToImageAlgorithm() ); + addAlgorithm( new QgsLayoutToPdfAlgorithm() ); addAlgorithm( new QgsLineDensityAlgorithm() ); addAlgorithm( new QgsLineIntersectionAlgorithm() ); addAlgorithm( new QgsLineSubstringAlgorithm() );