Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[FEATURE][processing] Add new algorithm "Print layout map extent to l…
…ayer" This algorithm creates a polygon layer containing the extent of a print layout map item, with attributes specifying the map size (in layout units), scale and rotatation. The main use case is when you want to create an advanced overview indicator and the inbuilt layout tools to do this don't suffice.
- Loading branch information
1 parent
a38e9e6
commit 92c8fdd
Showing
5 changed files
with
370 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
169 changes: 169 additions & 0 deletions
169
src/analysis/processing/qgsalgorithmextractlayoutmapextent.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
/*************************************************************************** | ||
qgsalgorithmextractlayoutmapextent.cpp | ||
--------------------- | ||
begin : March 2019 | ||
copyright : (C) 2019 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 "qgsalgorithmextractlayoutmapextent.h" | ||
#include "layout/qgslayoutitemregistry.h" | ||
#include "layout/qgslayoutitemmap.h" | ||
#include "layout/qgslayout.h" | ||
#include "layout/qgsprintlayout.h" | ||
#include "qgsprocessingoutputs.h" | ||
|
||
///@cond PRIVATE | ||
|
||
QString QgsLayoutMapExtentToLayerAlgorithm::name() const | ||
{ | ||
return QStringLiteral( "printlayoutmapextenttolayer" ); | ||
} | ||
|
||
QString QgsLayoutMapExtentToLayerAlgorithm::displayName() const | ||
{ | ||
return QObject::tr( "Print layout map extent to layer" ); | ||
} | ||
|
||
QStringList QgsLayoutMapExtentToLayerAlgorithm::tags() const | ||
{ | ||
return QObject::tr( "layout,composer,composition,visible" ).split( ',' ); | ||
} | ||
|
||
QString QgsLayoutMapExtentToLayerAlgorithm::group() const | ||
{ | ||
return QObject::tr( "Cartography" ); | ||
} | ||
|
||
QString QgsLayoutMapExtentToLayerAlgorithm::groupId() const | ||
{ | ||
return QStringLiteral( "cartography" ); | ||
} | ||
|
||
QString QgsLayoutMapExtentToLayerAlgorithm::shortDescription() const | ||
{ | ||
return QObject::tr( "Creates a polygon layer containing the extent of a print layout map item." ); | ||
} | ||
|
||
void QgsLayoutMapExtentToLayerAlgorithm::initAlgorithm( const QVariantMap & ) | ||
{ | ||
addParameter( new QgsProcessingParameterLayout( QStringLiteral( "LAYOUT" ), QObject::tr( "Print layout" ) ) ); | ||
addParameter( new QgsProcessingParameterLayoutItem( QStringLiteral( "MAP" ), QObject::tr( "Map item" ), QVariant(), QStringLiteral( "LAYOUT" ), QgsLayoutItemRegistry::LayoutMap, true ) ); | ||
auto crsParam = qgis::make_unique< QgsProcessingParameterCrs >( QStringLiteral( "CRS" ), QObject::tr( "Override CRS" ), QVariant(), true ); | ||
crsParam->setFlags( crsParam->flags() | QgsProcessingParameterDefinition::FlagAdvanced ); | ||
addParameter( crsParam.release() ); | ||
addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Extent" ), QgsProcessing::TypeVectorPolygon ) ); | ||
addOutput( new QgsProcessingOutputNumber( QStringLiteral( "WIDTH" ), QObject::tr( "Map width" ) ) ); | ||
addOutput( new QgsProcessingOutputNumber( QStringLiteral( "HEIGHT" ), QObject::tr( "Map height" ) ) ); | ||
addOutput( new QgsProcessingOutputNumber( QStringLiteral( "SCALE" ), QObject::tr( "Map scale" ) ) ); | ||
addOutput( new QgsProcessingOutputNumber( QStringLiteral( "ROTATION" ), QObject::tr( "Map rotation" ) ) ); | ||
} | ||
|
||
QString QgsLayoutMapExtentToLayerAlgorithm::shortHelpString() const | ||
{ | ||
return QObject::tr( "This algorithm creates a polygon layer containing the extent of a print layout map item (or items), " | ||
"with attributes specifying the map size (in layout units), scale and rotatation.\n\n" | ||
"If the map item parameter is specified, then only the matching map extent will be exported. If it " | ||
"is not specified, all map extents from the layout will be exported.\n\n" | ||
"Optionally, a specific output CRS can be specified. If it is not specified, the original map " | ||
"item CRS will be used." ); | ||
} | ||
|
||
QgsLayoutMapExtentToLayerAlgorithm *QgsLayoutMapExtentToLayerAlgorithm::createInstance() const | ||
{ | ||
return new QgsLayoutMapExtentToLayerAlgorithm(); | ||
} | ||
|
||
bool QgsLayoutMapExtentToLayerAlgorithm::prepareAlgorithm( 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() ) ); | ||
|
||
QgsLayoutItemMap *map = qobject_cast< QgsLayoutItemMap * >( parameterAsLayoutItem( parameters, QStringLiteral( "MAP" ), context, layout ) ); | ||
if ( !map && parameters.value( QStringLiteral( "MAP" ) ).isValid() ) | ||
throw QgsProcessingException( QObject::tr( "Cannot find matching map item with ID %1" ).arg( parameters.value( QStringLiteral( "MAP" ) ).toString() ) ); | ||
|
||
QList< QgsLayoutItemMap *> maps; | ||
if ( map ) | ||
maps << map; | ||
else | ||
layout->layoutItems( maps ); | ||
|
||
QgsCoordinateReferenceSystem overrideCrs = parameterAsCrs( parameters, QStringLiteral( "CRS" ), context ); | ||
|
||
mFeatures.reserve( maps.size() ); | ||
for ( QgsLayoutItemMap *map : maps ) | ||
{ | ||
if ( !mCrs.isValid() ) | ||
mCrs = !overrideCrs.isValid() ? map->crs() : overrideCrs; | ||
|
||
QgsGeometry extent = QgsGeometry::fromQPolygonF( map->visibleExtentPolygon() ); | ||
if ( map->crs() != mCrs ) | ||
{ | ||
try | ||
{ | ||
extent.transform( QgsCoordinateTransform( map->crs(), mCrs, context.transformContext() ) ); | ||
} | ||
catch ( QgsCsException & ) | ||
{ | ||
feedback->reportError( QObject::tr( "Error reprojecting map to destination CRS" ) ); | ||
continue; | ||
} | ||
} | ||
|
||
mWidth = map->rect().width(); | ||
mHeight = map->rect().height(); | ||
mScale = map->scale(); | ||
mRotation = map->mapRotation(); | ||
|
||
QgsFeature f; | ||
f.setAttributes( QgsAttributes() << map->displayName() << mWidth << mHeight << mScale << mRotation ); | ||
f.setGeometry( extent ); | ||
|
||
mFeatures << f; | ||
} | ||
return true; | ||
} | ||
|
||
QVariantMap QgsLayoutMapExtentToLayerAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) | ||
{ | ||
QgsFields fields; | ||
fields.append( QgsField( QStringLiteral( "map" ), QVariant::String ) ); | ||
fields.append( QgsField( QStringLiteral( "width" ), QVariant::Double ) ); | ||
fields.append( QgsField( QStringLiteral( "height" ), QVariant::Double ) ); | ||
fields.append( QgsField( QStringLiteral( "scale" ), QVariant::Double ) ); | ||
fields.append( QgsField( QStringLiteral( "rotation" ), QVariant::Double ) ); | ||
|
||
QString dest; | ||
std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, fields, QgsWkbTypes::Polygon, mCrs ) ); | ||
if ( !sink ) | ||
throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) ); | ||
|
||
for ( QgsFeature &f : mFeatures ) | ||
sink->addFeature( f, QgsFeatureSink::FastInsert ); | ||
|
||
feedback->setProgress( 100 ); | ||
|
||
QVariantMap outputs; | ||
outputs.insert( QStringLiteral( "OUTPUT" ), dest ); | ||
// these numeric outputs only have value for single-map layouts | ||
outputs.insert( QStringLiteral( "WIDTH" ), mFeatures.size() == 1 ? mWidth : QVariant() ); | ||
outputs.insert( QStringLiteral( "HEIGHT" ), mFeatures.size() == 1 ? mHeight : QVariant() ); | ||
outputs.insert( QStringLiteral( "SCALE" ), mFeatures.size() == 1 ? mScale : QVariant() ); | ||
outputs.insert( QStringLiteral( "ROTATION" ), mFeatures.size() == 1 ? mRotation : QVariant() ); | ||
return outputs; | ||
} | ||
|
||
///@endcond | ||
|
67 changes: 67 additions & 0 deletions
67
src/analysis/processing/qgsalgorithmextractlayoutmapextent.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
/*************************************************************************** | ||
qgsalgorithmextractlayoutmapextent.h | ||
--------------------- | ||
begin : March 2019 | ||
copyright : (C) 2019 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 QGSALGORITHMEXTRACTLAYOUTMAPEXTENT_H | ||
#define QGSALGORITHMEXTRACTLAYOUTMAPEXTENT_H | ||
|
||
#define SIP_NO_FILE | ||
|
||
#include "qgis_sip.h" | ||
#include "qgsprocessingalgorithm.h" | ||
|
||
///@cond PRIVATE | ||
|
||
/** | ||
* Native layout map extent to layer algorithm. | ||
*/ | ||
class QgsLayoutMapExtentToLayerAlgorithm : public QgsProcessingAlgorithm | ||
{ | ||
|
||
public: | ||
|
||
QgsLayoutMapExtentToLayerAlgorithm() = default; | ||
void initAlgorithm( const QVariantMap &configuration = QVariantMap() ) 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; | ||
QgsLayoutMapExtentToLayerAlgorithm *createInstance() const override SIP_FACTORY; | ||
|
||
protected: | ||
bool prepareAlgorithm( const QVariantMap ¶meters, | ||
QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override; | ||
QVariantMap processAlgorithm( const QVariantMap ¶meters, | ||
QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override; | ||
|
||
private: | ||
QgsFeatureList mFeatures; | ||
double mWidth = 0; | ||
double mHeight = 0; | ||
double mScale = 0; | ||
double mRotation = 0; | ||
QgsCoordinateReferenceSystem mCrs; | ||
|
||
}; | ||
|
||
///@endcond PRIVATE | ||
|
||
#endif // QGSALGORITHMEXTRACTLAYOUTMAPEXTENT_H | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.