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