QGIS API Documentation 3.43.0-Master (c4a2e9c6d2f)
qgsalgorithmexportgeometryattributes.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsalgorithmexportgeometryattributes.cpp
3 ---------------------
4 begin : February 2025
5 copyright : (C) 2025 by Alexander Bruy
6 email : alexander dot bruy 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
19#include "qgsunittypes.h"
21#include "qgscurve.h"
22
24
25QString QgsExportGeometryAttributesAlgorithm::name() const
26{
27 return QStringLiteral( "exportaddgeometrycolumns" );
28}
29
30QString QgsExportGeometryAttributesAlgorithm::displayName() const
31{
32 return QObject::tr( "Add geometry attributes" );
33}
34
35QStringList QgsExportGeometryAttributesAlgorithm::tags() const
36{
37 return QObject::tr( "export,add,information,measurements,areas,lengths,perimeters,latitudes,longitudes,x,y,z,extract,points,lines,polygons,sinuosity,fields" ).split( ',' );
38}
39
40QString QgsExportGeometryAttributesAlgorithm::group() const
41{
42 return QObject::tr( "Vector geometry" );
43}
44
45QString QgsExportGeometryAttributesAlgorithm::groupId() const
46{
47 return QStringLiteral( "vectorgeometry" );
48}
49
50QString QgsExportGeometryAttributesAlgorithm::shortHelpString() const
51{
52 return QObject::tr( "Computes geometric properties of the features in a vector layer. Algorithm generates a new "
53 "vector layer with the same content as the input one, but with additional attributes in its "
54 "attributes table, containing geometric measurements.\n\n"
55 "Depending on the geometry type of the vector layer, the attributes added to the table will "
56 "be different." );
57}
58
59QgsExportGeometryAttributesAlgorithm *QgsExportGeometryAttributesAlgorithm::createInstance() const
60{
61 return new QgsExportGeometryAttributesAlgorithm();
62}
63
64void QgsExportGeometryAttributesAlgorithm::initAlgorithm( const QVariantMap & )
65{
66 addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ), QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorAnyGeometry ) ) );
67
68 const QStringList options = QStringList()
69 << QObject::tr( "Cartesian Calculations in Layer's CRS" )
70 << QObject::tr( "Cartesian Calculations in Project's CRS" )
71 << QObject::tr( "Ellipsoidal Calculations" );
72 addParameter( new QgsProcessingParameterEnum( QStringLiteral( "METHOD" ), QObject::tr( "Calculate using" ), options, false, 0 ) );
73 addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Added geometry info" ) ) );
74}
75
76bool QgsExportGeometryAttributesAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
77{
78 Q_UNUSED( parameters );
79
80 mProjectCrs = context.project()->crs();
81 return true;
82}
83
84QVariantMap QgsExportGeometryAttributesAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
85{
86 std::unique_ptr<QgsProcessingFeatureSource> source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
87 if ( !source )
88 throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
89
90 const int method = parameterAsEnum( parameters, QStringLiteral( "METHOD" ), context );
91
92 const Qgis::WkbType wkbType = source->wkbType();
93 QgsFields fields = source->fields();
94 QgsFields newFields;
95
96 bool exportZ = false;
97 bool exportM = false;
99 {
100 newFields.append( QgsField( QStringLiteral( "area" ), QMetaType::Type::Double ) );
101 newFields.append( QgsField( QStringLiteral( "perimeter" ), QMetaType::Type::Double ) );
102 }
104 {
105 newFields.append( QgsField( QStringLiteral( "length" ), QMetaType::Type::Double ) );
106 if ( !QgsWkbTypes::isMultiType( wkbType ) )
107 {
108 newFields.append( QgsField( QStringLiteral( "straightdis" ), QMetaType::Type::Double ) );
109 newFields.append( QgsField( QStringLiteral( "sinuosity" ), QMetaType::Type::Double ) );
110 }
111 }
112 else
113 {
114 if ( QgsWkbTypes::isMultiType( wkbType ) )
115 {
116 newFields.append( QgsField( QStringLiteral( "numparts" ), QMetaType::Type::Int ) );
117 }
118 else
119 {
120 newFields.append( QgsField( QStringLiteral( "xcoord" ), QMetaType::Type::Double ) );
121 newFields.append( QgsField( QStringLiteral( "ycoord" ), QMetaType::Type::Double ) );
122 if ( QgsWkbTypes::hasZ( wkbType ) )
123 {
124 newFields.append( QgsField( QStringLiteral( "zcoord" ), QMetaType::Type::Double ) );
125 exportZ = true;
126 }
127 if ( QgsWkbTypes::hasM( wkbType ) )
128 {
129 newFields.append( QgsField( QStringLiteral( "mvalue" ), QMetaType::Type::Double ) );
130 exportM = true;
131 }
132 }
133 }
134
135 fields = QgsProcessingUtils::combineFields( fields, newFields );
136
137 QString dest;
138 std::unique_ptr<QgsFeatureSink> sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, fields, wkbType, source->sourceCrs() ) );
139 if ( !sink )
140 {
141 throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
142 }
143
144 QgsCoordinateTransform transform;
145 mDa = QgsDistanceArea();
146
147 if ( method == 2 )
148 {
149 mDa.setSourceCrs( source->sourceCrs(), context.transformContext() );
150 mDa.setEllipsoid( context.ellipsoid() );
151 mDistanceConversionFactor = QgsUnitTypes::fromUnitToUnitFactor( mDa.lengthUnits(), context.distanceUnit() );
152 mAreaConversionFactor = QgsUnitTypes::fromUnitToUnitFactor( mDa.areaUnits(), context.areaUnit() );
153 }
154 else if ( method == 1 )
155 {
156 if ( !context.project() )
157 {
158 throw QgsProcessingException( QObject::tr( "No project is available in this context" ) );
159 }
160 transform = QgsCoordinateTransform( source->sourceCrs(), mProjectCrs, context.transformContext() );
161 }
162
163 QgsFeatureIterator it = source->getFeatures();
164 const double step = source->featureCount() > 0 ? 100.0 / source->featureCount() : 0;
165 long i = 0;
166 QgsFeature f;
167
168 while ( it.nextFeature( f ) )
169 {
170 if ( feedback->isCanceled() )
171 {
172 break;
173 }
174
175 QgsFeature outputFeature( f );
176 QgsAttributes attrs = f.attributes();
177 QgsGeometry geom = f.geometry();
178
179 if ( !geom.isNull() )
180 {
181 if ( transform.isValid() )
182 {
183 try
184 {
185 geom.transform( transform );
186 }
187 catch ( QgsCsException &e )
188 {
189 throw QgsProcessingException( QObject::tr( "Could not transform feature to project's CRS: %1" ).arg( e.what() ) );
190 }
191 }
192
193 if ( geom.type() == Qgis::GeometryType::Point )
194 {
195 attrs << pointAttributes( geom, exportZ, exportM );
196 }
197 else if ( geom.type() == Qgis::GeometryType::Polygon )
198 {
199 attrs << polygonAttributes( geom );
200 }
201 else
202 {
203 attrs << lineAttributes( geom );
204 }
205 }
206
207 // ensure consistent count of attributes - otherwise null geometry features will have incorrect attribute
208 // length and provider may reject them
209 while ( attrs.size() < fields.size() )
210 {
211 attrs.append( QVariant() );
212 }
213
214 outputFeature.setAttributes( attrs );
215 if ( !sink->addFeature( outputFeature, QgsFeatureSink::FastInsert ) )
216 {
217 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
218 }
219
220 i++;
221 feedback->setProgress( i * step );
222 }
223
224 sink->finalize();
225
226 QVariantMap results;
227 results.insert( QStringLiteral( "OUTPUT" ), dest );
228 return results;
229}
230
231QgsAttributes QgsExportGeometryAttributesAlgorithm::pointAttributes( const QgsGeometry &geom, const bool exportZ, const bool exportM )
232{
233 QgsAttributes attrs;
234
235 if ( !geom.isMultipart() )
236 {
237 auto point = qgsgeometry_cast<const QgsPoint *>( geom.constGet() );
238 attrs.append( point->x() );
239 attrs.append( point->y() );
240 // add point Z/M
241 if ( exportZ )
242 {
243 attrs.append( point->z() );
244 }
245 if ( exportM )
246 {
247 attrs.append( point->m() );
248 }
249 }
250 else
251 {
252 attrs.append( qgsgeometry_cast<const QgsGeometryCollection *>( geom.constGet() )->numGeometries() );
253 }
254 return attrs;
255}
256
257QgsAttributes QgsExportGeometryAttributesAlgorithm::lineAttributes( const QgsGeometry &geom )
258{
259 QgsAttributes attrs;
260
261 if ( geom.isMultipart() )
262 {
263 attrs.append( mDistanceConversionFactor * mDa.measureLength( geom ) );
264 }
265 else
266 {
267 auto curve = qgsgeometry_cast<const QgsCurve *>( geom.constGet() );
268 const QgsPoint p1 = curve->startPoint();
269 const QgsPoint p2 = curve->endPoint();
270 const double straightDistance = mDistanceConversionFactor * mDa.measureLine( QgsPointXY( p1 ), QgsPointXY( p2 ) );
271 const double sinuosity = curve->sinuosity();
272 attrs.append( mDistanceConversionFactor * mDa.measureLength( geom ) );
273 attrs.append( straightDistance );
274 attrs.append( std::isnan( sinuosity ) ? QVariant() : sinuosity );
275 }
276
277 return attrs;
278}
279
280QgsAttributes QgsExportGeometryAttributesAlgorithm::polygonAttributes( const QgsGeometry &geom )
281{
282 const double area = mAreaConversionFactor * mDa.measureArea( geom );
283 const double perimeter = mDistanceConversionFactor * mDa.measurePerimeter( geom );
284
285 return QgsAttributes() << area << perimeter;
286}
287
@ VectorAnyGeometry
Any vector layer with geometry.
@ Polygon
Polygons.
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:256
A vector of attributes.
Handles coordinate transforms between two coordinate systems.
bool isValid() const
Returns true if the coordinate transform is valid, ie both the source and destination CRS have been s...
Custom exception class for Coordinate Reference System related exceptions.
A general purpose distance and area calculator, capable of performing ellipsoid based calculations.
QString what() const
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
@ FastInsert
Use faster inserts, at the cost of updating the passed features to reflect changes made at the provid...
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:58
QgsAttributes attributes
Definition qgsfeature.h:67
QgsGeometry geometry
Definition qgsfeature.h:69
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:53
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition qgsfeedback.h:61
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:53
Container of fields for a vector layer.
Definition qgsfields.h:46
bool append(const QgsField &field, Qgis::FieldOrigin origin=Qgis::FieldOrigin::Provider, int originIndex=-1)
Appends a field.
Definition qgsfields.cpp:70
int size() const
Returns number of items.
A geometry is the spatial representation of a feature.
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false)
Transforms this geometry as described by the coordinate transform ct.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
Qgis::GeometryType type
bool isMultipart() const
Returns true if WKB of the geometry is of WKBMulti* type.
Represents a 2D point.
Definition qgspointxy.h:60
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
Contains information about the context in which a processing algorithm is executed.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context.
Qgis::AreaUnit areaUnit() const
Returns the area unit to use for area calculations.
QgsProject * project() const
Returns the project in which the algorithm is being executed.
Qgis::DistanceUnit distanceUnit() const
Returns the distance unit to use for distance calculations.
QString ellipsoid() const
Returns the ellipsoid to use for distance and area calculations.
Custom exception class for processing related exceptions.
Base class for providing feedback from a processing algorithm.
An enum based parameter for processing algorithms, allowing for selection from predefined values.
A feature sink output for processing algorithms.
An input feature source (such as vector layers) parameter for processing algorithms.
static QgsFields combineFields(const QgsFields &fieldsA, const QgsFields &fieldsB, const QString &fieldsBPrefix=QString())
Combines two field lists, avoiding duplicate field names (in a case-insensitive manner).
QgsCoordinateReferenceSystem crs
Definition qgsproject.h:112
static Q_INVOKABLE double fromUnitToUnitFactor(Qgis::DistanceUnit fromUnit, Qgis::DistanceUnit toUnit)
Returns the conversion factor between the specified distance units.
static Qgis::GeometryType geometryType(Qgis::WkbType type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
static bool isMultiType(Qgis::WkbType type)
Returns true if the WKB type is a multi type.
static bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
static bool hasM(Qgis::WkbType type)
Tests whether a WKB type contains m values.