QGIS API Documentation 3.43.0-Master (261ee7da134)
qgsalgorithmclimb.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsalgorithmclimb.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
18#include "qgsalgorithmclimb.h"
19
21
22QString QgsClimbAlgorithm::name() const
23{
24 return QStringLiteral( "climbalongline" );
25}
26
27QString QgsClimbAlgorithm::displayName() const
28{
29 return QObject::tr( "Climb along line" );
30}
31
32QStringList QgsClimbAlgorithm::tags() const
33{
34 return QObject::tr( "line,climb,descent,elevation" ).split( ',' );
35}
36
37QString QgsClimbAlgorithm::group() const
38{
39 return QObject::tr( "Vector analysis" );
40}
41
42QString QgsClimbAlgorithm::groupId() const
43{
44 return QStringLiteral( "vectoranalysis" );
45}
46
47QString QgsClimbAlgorithm::shortHelpString() const
48{
49 return QObject::tr( "This algorithm calculates the total climb and descent along line geometries.\n\n"
50 "Input layer must have Z values present. If Z values are not available, the \"Drape\" (set Z "
51 "value from raster) algorithm may be used to add Z values from a DEM layer.\n\n"
52 "The output layer is a copy of the input layer with additional fields that contain the total "
53 "climb, total descent, the minimum elevation and the maximum elevation for each line geometry."
54 "If the input layer contains fields with the same names as these added fields, they will be "
55 "renamed (field names will be altered to \"name_2\", \"name_3\", etc, finding the first "
56 "non-duplicate name)." );
57}
58
59QString QgsClimbAlgorithm::shortDescription() const
60{
61 return QObject::tr( "Calculates the total climb and descent along line geometries with Z values." );
62}
63
64QgsClimbAlgorithm *QgsClimbAlgorithm::createInstance() const
65{
66 return new QgsClimbAlgorithm();
67}
68
69void QgsClimbAlgorithm::initAlgorithm( const QVariantMap & )
70{
71 addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Line layer" ), QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorLine ) ) );
72 addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Climb layer" ) ) );
73 // TODO QGIS 4.0 harmonize output names with the rest of algorithms (use underscore to separate words)
74 addOutput( new QgsProcessingOutputNumber( QStringLiteral( "TOTALCLIMB" ), QObject::tr( "Total climb" ) ) );
75 addOutput( new QgsProcessingOutputNumber( QStringLiteral( "TOTALDESCENT" ), QObject::tr( "Total descent" ) ) );
76 addOutput( new QgsProcessingOutputNumber( QStringLiteral( "MINELEVATION" ), QObject::tr( "Minimum elevation" ) ) );
77 addOutput( new QgsProcessingOutputNumber( QStringLiteral( "MAXELEVATION" ), QObject::tr( "Maximum elevation" ) ) );
78}
79
80QVariantMap QgsClimbAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
81{
82 std::unique_ptr<QgsProcessingFeatureSource> source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
83 if ( !source )
84 {
85 throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
86 }
87
88 if ( !QgsWkbTypes::hasZ( source->wkbType() ) )
89 {
90 throw QgsProcessingException( QObject::tr( "The layer does not have Z values. If you have a DEM, use the Drape algorithm to extract Z values." ) );
91 }
92
93 QgsFields outputFields = source->fields();
94 QgsFields newFields;
95 newFields.append( QgsField( QStringLiteral( "climb" ), QMetaType::Type::Double ) );
96 newFields.append( QgsField( QStringLiteral( "descent" ), QMetaType::Type::Double ) );
97 newFields.append( QgsField( QStringLiteral( "minelev" ), QMetaType::Type::Double ) );
98 newFields.append( QgsField( QStringLiteral( "maxelev" ), QMetaType::Type::Double ) );
99 outputFields = QgsProcessingUtils::combineFields( outputFields, newFields );
100
101 QString dest;
102 std::unique_ptr<QgsFeatureSink> sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, outputFields, source->wkbType(), source->sourceCrs() ) );
103 if ( !sink )
104 throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
105
106 double totalClimb = 0;
107 double totalDescent = 0;
108 double minElevation = std::numeric_limits<double>::max();
109 double maxElevation = -std::numeric_limits<double>::max();
110
111 QStringList noGeometry;
112 QStringList noZValue;
113
114 QgsFeatureIterator it = source->getFeatures();
115 double step = source->featureCount() > 0 ? 100.0 / source->featureCount() : 1;
116 int i = 0;
117
118 QgsFeature f;
119 while ( it.nextFeature( f ) )
120 {
121 if ( feedback->isCanceled() )
122 {
123 break;
124 }
125
126 if ( !f.hasGeometry() )
127 {
128 noGeometry.append( QObject::tr( "Feature: %1" ).arg( f.id() ) );
129 continue;
130 }
131
132 double climb = 0;
133 double descent = 0;
134 double minElev = std::numeric_limits<double>::max();
135 double maxElev = -std::numeric_limits<double>::max();
136 // Handle multipart geometries
137 const QgsGeometry g = f.geometry();
138 int partNumber = 0;
139 for ( auto partIt = g.const_parts_begin(); partIt != g.const_parts_end(); ++partIt, ++partNumber )
140 {
141 bool first = true;
142 double z = 0;
143 double previousZ = 0;
144 const QgsAbstractGeometry *part = *partIt;
145 int vertexNumber = 0;
146 for ( auto it = part->vertices_begin(); it != part->vertices_end(); ++it, ++vertexNumber )
147 {
148 z = QgsPoint( *it ).z();
149 if ( std::isnan( z ) )
150 {
151 noZValue.append( QObject::tr( "Feature: %1, part: %2, point: %3" ).arg( f.id(), partNumber, vertexNumber ) );
152 continue;
153 }
154 if ( first )
155 {
156 previousZ = z;
157 minElev = z;
158 maxElev = z;
159 first = false;
160 }
161 else
162 {
163 double diff = z - previousZ;
164 if ( diff > 0 )
165 {
166 climb += diff;
167 }
168 else
169 {
170 descent -= diff;
171 }
172 minElev = std::min( minElev, z );
173 maxElev = std::max( maxElev, z );
174 }
175 previousZ = z;
176 }
177 totalClimb += climb;
178 totalDescent += descent;
179 }
180
181 QgsAttributes attrs = f.attributes();
182 attrs << climb << descent << minElev << maxElev;
183 f.setAttributes( attrs );
184 if ( !sink->addFeature( f, QgsFeatureSink::FastInsert ) )
185 {
186 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
187 }
188 minElevation = std::min( minElevation, minElev );
189 maxElevation = std::max( maxElevation, maxElev );
190
191 feedback->setProgress( i * step );
192 i++;
193 }
194
195 sink->finalize();
196
197 if ( !noGeometry.empty() )
198 {
199 feedback->pushInfo( QObject::tr( "The following features do not have geometry: %1" ).arg( noGeometry.join( QStringLiteral( ", " ) ) ) );
200 }
201 if ( !noZValue.empty() )
202 {
203 feedback->pushInfo( QObject::tr( "The following points do not have Z value: %1" ).arg( noZValue.join( QStringLiteral( ", " ) ) ) );
204 }
205
206 QVariantMap results;
207 results.insert( QStringLiteral( "OUTPUT" ), dest );
208 results.insert( QStringLiteral( "TOTALCLIMB" ), totalClimb );
209 results.insert( QStringLiteral( "TOTALDESCENT" ), totalDescent );
210 results.insert( QStringLiteral( "MINELEVATION" ), minElevation );
211 results.insert( QStringLiteral( "MAXELEVATION" ), maxElevation );
212 return results;
213}
214
@ VectorLine
Vector line layers.
Abstract base class for all geometries.
vertex_iterator vertices_end() const
Returns STL-style iterator pointing to the imaginary vertex after the last vertex of the geometry.
vertex_iterator vertices_begin() const
Returns STL-style iterator pointing to the first vertex of the geometry.
A vector of attributes.
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
QgsFeatureId id
Definition qgsfeature.h:66
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
QgsGeometry geometry
Definition qgsfeature.h:69
bool hasGeometry() const
Returns true if the feature has an associated geometry.
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
A geometry is the spatial representation of a feature.
QgsAbstractGeometry::const_part_iterator const_parts_begin() const
Returns STL-style const iterator pointing to the first part of the geometry.
QgsAbstractGeometry::const_part_iterator const_parts_end() const
Returns STL-style iterator pointing to the imaginary part after the last part of the geometry.
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
double z
Definition qgspoint.h:54
Contains information about the context in which a processing algorithm is executed.
Custom exception class for processing related exceptions.
Base class for providing feedback from a processing algorithm.
virtual void pushInfo(const QString &info)
Pushes a general informational message from the algorithm.
A numeric output for processing algorithms.
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).
static bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.