Skip to content

Commit 92c8fdd

Browse files
committed
[FEATURE][processing] Add new algorithm "Print layout map extent to layer"
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.
1 parent a38e9e6 commit 92c8fdd

File tree

5 files changed

+370
-0
lines changed

5 files changed

+370
-0
lines changed

src/analysis/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ SET(QGIS_ANALYSIS_SRCS
4747
processing/qgsalgorithmextractbyexpression.cpp
4848
processing/qgsalgorithmextractbyextent.cpp
4949
processing/qgsalgorithmextractbylocation.cpp
50+
processing/qgsalgorithmextractlayoutmapextent.cpp
5051
processing/qgsalgorithmextractzmvalues.cpp
5152
processing/qgsalgorithmextractvertices.cpp
5253
processing/qgsalgorithmfiledownloader.cpp
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
/***************************************************************************
2+
qgsalgorithmextractlayoutmapextent.cpp
3+
---------------------
4+
begin : March 2019
5+
copyright : (C) 2019 by Nyall Dawson
6+
email : nyall dot dawson at gmail dot com
7+
***************************************************************************/
8+
9+
/***************************************************************************
10+
* *
11+
* This program is free software; you can redistribute it and/or modify *
12+
* it under the terms of the GNU General Public License as published by *
13+
* the Free Software Foundation; either version 2 of the License, or *
14+
* (at your option) any later version. *
15+
* *
16+
***************************************************************************/
17+
18+
#include "qgsalgorithmextractlayoutmapextent.h"
19+
#include "layout/qgslayoutitemregistry.h"
20+
#include "layout/qgslayoutitemmap.h"
21+
#include "layout/qgslayout.h"
22+
#include "layout/qgsprintlayout.h"
23+
#include "qgsprocessingoutputs.h"
24+
25+
///@cond PRIVATE
26+
27+
QString QgsLayoutMapExtentToLayerAlgorithm::name() const
28+
{
29+
return QStringLiteral( "printlayoutmapextenttolayer" );
30+
}
31+
32+
QString QgsLayoutMapExtentToLayerAlgorithm::displayName() const
33+
{
34+
return QObject::tr( "Print layout map extent to layer" );
35+
}
36+
37+
QStringList QgsLayoutMapExtentToLayerAlgorithm::tags() const
38+
{
39+
return QObject::tr( "layout,composer,composition,visible" ).split( ',' );
40+
}
41+
42+
QString QgsLayoutMapExtentToLayerAlgorithm::group() const
43+
{
44+
return QObject::tr( "Cartography" );
45+
}
46+
47+
QString QgsLayoutMapExtentToLayerAlgorithm::groupId() const
48+
{
49+
return QStringLiteral( "cartography" );
50+
}
51+
52+
QString QgsLayoutMapExtentToLayerAlgorithm::shortDescription() const
53+
{
54+
return QObject::tr( "Creates a polygon layer containing the extent of a print layout map item." );
55+
}
56+
57+
void QgsLayoutMapExtentToLayerAlgorithm::initAlgorithm( const QVariantMap & )
58+
{
59+
addParameter( new QgsProcessingParameterLayout( QStringLiteral( "LAYOUT" ), QObject::tr( "Print layout" ) ) );
60+
addParameter( new QgsProcessingParameterLayoutItem( QStringLiteral( "MAP" ), QObject::tr( "Map item" ), QVariant(), QStringLiteral( "LAYOUT" ), QgsLayoutItemRegistry::LayoutMap, true ) );
61+
auto crsParam = qgis::make_unique< QgsProcessingParameterCrs >( QStringLiteral( "CRS" ), QObject::tr( "Override CRS" ), QVariant(), true );
62+
crsParam->setFlags( crsParam->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
63+
addParameter( crsParam.release() );
64+
addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Extent" ), QgsProcessing::TypeVectorPolygon ) );
65+
addOutput( new QgsProcessingOutputNumber( QStringLiteral( "WIDTH" ), QObject::tr( "Map width" ) ) );
66+
addOutput( new QgsProcessingOutputNumber( QStringLiteral( "HEIGHT" ), QObject::tr( "Map height" ) ) );
67+
addOutput( new QgsProcessingOutputNumber( QStringLiteral( "SCALE" ), QObject::tr( "Map scale" ) ) );
68+
addOutput( new QgsProcessingOutputNumber( QStringLiteral( "ROTATION" ), QObject::tr( "Map rotation" ) ) );
69+
}
70+
71+
QString QgsLayoutMapExtentToLayerAlgorithm::shortHelpString() const
72+
{
73+
return QObject::tr( "This algorithm creates a polygon layer containing the extent of a print layout map item (or items), "
74+
"with attributes specifying the map size (in layout units), scale and rotatation.\n\n"
75+
"If the map item parameter is specified, then only the matching map extent will be exported. If it "
76+
"is not specified, all map extents from the layout will be exported.\n\n"
77+
"Optionally, a specific output CRS can be specified. If it is not specified, the original map "
78+
"item CRS will be used." );
79+
}
80+
81+
QgsLayoutMapExtentToLayerAlgorithm *QgsLayoutMapExtentToLayerAlgorithm::createInstance() const
82+
{
83+
return new QgsLayoutMapExtentToLayerAlgorithm();
84+
}
85+
86+
bool QgsLayoutMapExtentToLayerAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
87+
{
88+
// this needs to be done in main thread, layouts are not thread safe
89+
QgsPrintLayout *layout = parameterAsLayout( parameters, QStringLiteral( "LAYOUT" ), context );
90+
if ( !layout )
91+
throw QgsProcessingException( QObject::tr( "Cannot find layout with name \"%1\"" ).arg( parameters.value( QStringLiteral( "LAYOUT" ) ).toString() ) );
92+
93+
QgsLayoutItemMap *map = qobject_cast< QgsLayoutItemMap * >( parameterAsLayoutItem( parameters, QStringLiteral( "MAP" ), context, layout ) );
94+
if ( !map && parameters.value( QStringLiteral( "MAP" ) ).isValid() )
95+
throw QgsProcessingException( QObject::tr( "Cannot find matching map item with ID %1" ).arg( parameters.value( QStringLiteral( "MAP" ) ).toString() ) );
96+
97+
QList< QgsLayoutItemMap *> maps;
98+
if ( map )
99+
maps << map;
100+
else
101+
layout->layoutItems( maps );
102+
103+
QgsCoordinateReferenceSystem overrideCrs = parameterAsCrs( parameters, QStringLiteral( "CRS" ), context );
104+
105+
mFeatures.reserve( maps.size() );
106+
for ( QgsLayoutItemMap *map : maps )
107+
{
108+
if ( !mCrs.isValid() )
109+
mCrs = !overrideCrs.isValid() ? map->crs() : overrideCrs;
110+
111+
QgsGeometry extent = QgsGeometry::fromQPolygonF( map->visibleExtentPolygon() );
112+
if ( map->crs() != mCrs )
113+
{
114+
try
115+
{
116+
extent.transform( QgsCoordinateTransform( map->crs(), mCrs, context.transformContext() ) );
117+
}
118+
catch ( QgsCsException & )
119+
{
120+
feedback->reportError( QObject::tr( "Error reprojecting map to destination CRS" ) );
121+
continue;
122+
}
123+
}
124+
125+
mWidth = map->rect().width();
126+
mHeight = map->rect().height();
127+
mScale = map->scale();
128+
mRotation = map->mapRotation();
129+
130+
QgsFeature f;
131+
f.setAttributes( QgsAttributes() << map->displayName() << mWidth << mHeight << mScale << mRotation );
132+
f.setGeometry( extent );
133+
134+
mFeatures << f;
135+
}
136+
return true;
137+
}
138+
139+
QVariantMap QgsLayoutMapExtentToLayerAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
140+
{
141+
QgsFields fields;
142+
fields.append( QgsField( QStringLiteral( "map" ), QVariant::String ) );
143+
fields.append( QgsField( QStringLiteral( "width" ), QVariant::Double ) );
144+
fields.append( QgsField( QStringLiteral( "height" ), QVariant::Double ) );
145+
fields.append( QgsField( QStringLiteral( "scale" ), QVariant::Double ) );
146+
fields.append( QgsField( QStringLiteral( "rotation" ), QVariant::Double ) );
147+
148+
QString dest;
149+
std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, fields, QgsWkbTypes::Polygon, mCrs ) );
150+
if ( !sink )
151+
throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
152+
153+
for ( QgsFeature &f : mFeatures )
154+
sink->addFeature( f, QgsFeatureSink::FastInsert );
155+
156+
feedback->setProgress( 100 );
157+
158+
QVariantMap outputs;
159+
outputs.insert( QStringLiteral( "OUTPUT" ), dest );
160+
// these numeric outputs only have value for single-map layouts
161+
outputs.insert( QStringLiteral( "WIDTH" ), mFeatures.size() == 1 ? mWidth : QVariant() );
162+
outputs.insert( QStringLiteral( "HEIGHT" ), mFeatures.size() == 1 ? mHeight : QVariant() );
163+
outputs.insert( QStringLiteral( "SCALE" ), mFeatures.size() == 1 ? mScale : QVariant() );
164+
outputs.insert( QStringLiteral( "ROTATION" ), mFeatures.size() == 1 ? mRotation : QVariant() );
165+
return outputs;
166+
}
167+
168+
///@endcond
169+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/***************************************************************************
2+
qgsalgorithmextractlayoutmapextent.h
3+
---------------------
4+
begin : March 2019
5+
copyright : (C) 2019 by Nyall Dawson
6+
email : nyall dot dawson at gmail dot com
7+
***************************************************************************/
8+
9+
/***************************************************************************
10+
* *
11+
* This program is free software; you can redistribute it and/or modify *
12+
* it under the terms of the GNU General Public License as published by *
13+
* the Free Software Foundation; either version 2 of the License, or *
14+
* (at your option) any later version. *
15+
* *
16+
***************************************************************************/
17+
18+
#ifndef QGSALGORITHMEXTRACTLAYOUTMAPEXTENT_H
19+
#define QGSALGORITHMEXTRACTLAYOUTMAPEXTENT_H
20+
21+
#define SIP_NO_FILE
22+
23+
#include "qgis_sip.h"
24+
#include "qgsprocessingalgorithm.h"
25+
26+
///@cond PRIVATE
27+
28+
/**
29+
* Native layout map extent to layer algorithm.
30+
*/
31+
class QgsLayoutMapExtentToLayerAlgorithm : public QgsProcessingAlgorithm
32+
{
33+
34+
public:
35+
36+
QgsLayoutMapExtentToLayerAlgorithm() = default;
37+
void initAlgorithm( const QVariantMap &configuration = QVariantMap() ) override;
38+
QString name() const override;
39+
QString displayName() const override;
40+
QStringList tags() const override;
41+
QString group() const override;
42+
QString groupId() const override;
43+
QString shortDescription() const override;
44+
QString shortHelpString() const override;
45+
QgsLayoutMapExtentToLayerAlgorithm *createInstance() const override SIP_FACTORY;
46+
47+
protected:
48+
bool prepareAlgorithm( const QVariantMap &parameters,
49+
QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
50+
QVariantMap processAlgorithm( const QVariantMap &parameters,
51+
QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
52+
53+
private:
54+
QgsFeatureList mFeatures;
55+
double mWidth = 0;
56+
double mHeight = 0;
57+
double mScale = 0;
58+
double mRotation = 0;
59+
QgsCoordinateReferenceSystem mCrs;
60+
61+
};
62+
63+
///@endcond PRIVATE
64+
65+
#endif // QGSALGORITHMEXTRACTLAYOUTMAPEXTENT_H
66+
67+

src/analysis/processing/qgsnativealgorithms.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#include "qgsalgorithmextractbyexpression.h"
4343
#include "qgsalgorithmextractbyextent.h"
4444
#include "qgsalgorithmextractbylocation.h"
45+
#include "qgsalgorithmextractlayoutmapextent.h"
4546
#include "qgsalgorithmextractvertices.h"
4647
#include "qgsalgorithmextractzmvalues.h"
4748
#include "qgsalgorithmfiledownloader.h"
@@ -191,6 +192,7 @@ void QgsNativeAlgorithms::loadAlgorithms()
191192
addAlgorithm( new QgsJoinByAttributeAlgorithm() );
192193
addAlgorithm( new QgsJoinWithLinesAlgorithm() );
193194
addAlgorithm( new QgsKMeansClusteringAlgorithm() );
195+
addAlgorithm( new QgsLayoutMapExtentToLayerAlgorithm() );
194196
addAlgorithm( new QgsLineIntersectionAlgorithm() );
195197
addAlgorithm( new QgsLineSubstringAlgorithm() );
196198
addAlgorithm( new QgsLoadLayerAlgorithm() );

0 commit comments

Comments
 (0)