QGIS API Documentation 3.99.0-Master (357b655ed83)
Loading...
Searching...
No Matches
qgsalgorithmtransectbase.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsalgorithmtransectbase.cpp
3 ----------------------------
4 begin : September 2025
5 copyright : (C) 2025 by Loïc Bartoletti
6 email : loic dot bartoletti at oslandia 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
20#include "qgslinestring.h"
21#include "qgsmultilinestring.h"
22
23#include <QString>
24
25using namespace Qt::StringLiterals;
26
28
29QString QgsTransectAlgorithmBase::group() const
30{
31 return QObject::tr( "Vector geometry" );
32}
33
34QString QgsTransectAlgorithmBase::groupId() const
35{
36 return u"vectorgeometry"_s;
37}
38
39QStringList QgsTransectAlgorithmBase::tags() const
40{
41 return QObject::tr( "transect,station,lines,extend" ).split( ',' );
42}
43
44QString QgsTransectAlgorithmBase::shortDescription() const
45{
46 return QObject::tr( "Creates transects for (multi)linestrings." );
47}
48
49Qgis::ProcessingAlgorithmDocumentationFlags QgsTransectAlgorithmBase::documentationFlags() const
50{
52}
53
54void QgsTransectAlgorithmBase::initAlgorithm( const QVariantMap & )
55{
56 addParameter( new QgsProcessingParameterFeatureSource( u"INPUT"_s, QObject::tr( "Input layer" ), QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorLine ) ) );
57
58 addAlgorithmParams();
59
60 auto length = std::make_unique<QgsProcessingParameterDistance>( u"LENGTH"_s, QObject::tr( "Length of the transect" ), 5.0, u"INPUT"_s, false, 0 );
61 length->setIsDynamic( true );
62 length->setDynamicPropertyDefinition( QgsPropertyDefinition( u"LENGTH"_s, QObject::tr( "Length of the transect" ), QgsPropertyDefinition::DoublePositive ) );
63 length->setDynamicLayerParameterName( u"INPUT"_s );
64 addParameter( length.release() );
65
66 auto angle = std::make_unique<QgsProcessingParameterNumber>( u"ANGLE"_s, QObject::tr( "Angle in degrees from the original line at the vertices" ), Qgis::ProcessingNumberParameterType::Double, 90.0, false, 0, 360 );
67 angle->setIsDynamic( true );
68 angle->setDynamicPropertyDefinition( QgsPropertyDefinition( u"ANGLE"_s, QObject::tr( "Angle in degrees" ), QgsPropertyDefinition::Double ) );
69 angle->setDynamicLayerParameterName( u"INPUT"_s );
70 addParameter( angle.release() );
71
72 addParameter( new QgsProcessingParameterEnum( u"SIDE"_s, QObject::tr( "Side to create the transects" ), QStringList() << QObject::tr( "Left" ) << QObject::tr( "Right" ) << QObject::tr( "Both" ), false, 2 ) );
73
74 auto direction = std::make_unique<QgsProcessingParameterEnum>( u"DIRECTION"_s, QObject::tr( "Direction" ), QStringList() << QObject::tr( "Right to Left" ) << QObject::tr( "Left to Right" ), false, 0, true );
75 direction->setGuiDefaultValueOverride( 1 );
76 addParameter( direction.release() );
77
78 addParameter( new QgsProcessingParameterFeatureSink( u"OUTPUT"_s, QObject::tr( "Transect" ), Qgis::ProcessingSourceType::VectorLine ) );
79}
80
81QVariantMap QgsTransectAlgorithmBase::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
82{
83 mOrientation = static_cast<QgsTransectAlgorithmBase::Side>( parameterAsInt( parameters, u"SIDE"_s, context ) );
84 mAngle = fabs( parameterAsDouble( parameters, u"ANGLE"_s, context ) );
85 mDynamicAngle = QgsProcessingParameters::isDynamic( parameters, u"ANGLE"_s );
86 if ( mDynamicAngle )
87 mAngleProperty = parameters.value( u"ANGLE"_s ).value<QgsProperty>();
88
89 mLength = parameterAsDouble( parameters, u"LENGTH"_s, context );
90 mDynamicLength = QgsProcessingParameters::isDynamic( parameters, u"LENGTH"_s );
91 if ( mDynamicLength )
92 mLengthProperty = parameters.value( u"LENGTH"_s ).value<QgsProperty>();
93
94 if ( mOrientation == QgsTransectAlgorithmBase::Both )
95 mLength /= 2.0;
96
97 mDirection = static_cast<QgsTransectAlgorithmBase::Direction>( parameterAsInt( parameters, u"DIRECTION"_s, context ) );
98
99 // Let subclass prepare their specific parameters
100 if ( !prepareAlgorithmTransectParameters( parameters, context, feedback ) )
101 return QVariantMap();
102
103 std::unique_ptr<QgsFeatureSource> source( parameterAsSource( parameters, u"INPUT"_s, context ) );
104 if ( !source )
105 throw QgsProcessingException( invalidSourceError( parameters, u"INPUT"_s ) );
106
107 QgsExpressionContext expressionContext = createExpressionContext( parameters, context, dynamic_cast<QgsProcessingFeatureSource *>( source.get() ) );
108
109 QgsFields newFields;
110 newFields.append( QgsField( u"TR_FID"_s, QMetaType::Type::Int, QString(), 20 ) );
111 newFields.append( QgsField( u"TR_ID"_s, QMetaType::Type::Int, QString(), 20 ) );
112 newFields.append( QgsField( u"TR_SEGMENT"_s, QMetaType::Type::Int, QString(), 20 ) );
113 newFields.append( QgsField( u"TR_ANGLE"_s, QMetaType::Type::Double, QString(), 5, 2 ) );
114 newFields.append( QgsField( u"TR_LENGTH"_s, QMetaType::Type::Double, QString(), 20, 6 ) );
115 newFields.append( QgsField( u"TR_ORIENT"_s, QMetaType::Type::Int, QString(), 1 ) );
116 QgsFields fields = QgsProcessingUtils::combineFields( source->fields(), newFields );
117
119 if ( QgsWkbTypes::hasZ( source->wkbType() ) )
120 outputWkb = QgsWkbTypes::addZ( outputWkb );
121 if ( QgsWkbTypes::hasM( source->wkbType() ) )
122 outputWkb = QgsWkbTypes::addM( outputWkb );
123
124 QString dest;
125 std::unique_ptr<QgsFeatureSink> sink( parameterAsSink( parameters, u"OUTPUT"_s, context, dest, fields, outputWkb, source->sourceCrs(), QgsFeatureSink::RegeneratePrimaryKey ) );
126 if ( !sink )
127 throw QgsProcessingException( invalidSinkError( parameters, u"OUTPUT"_s ) );
128
129 QgsFeatureIterator features = source->getFeatures();
130
131 int current = -1;
132 int number = 0;
133 const double step = source->featureCount() > 0 ? 100.0 / source->featureCount() : 1;
134 QgsFeature feat;
135
136 while ( features.nextFeature( feat ) )
137 {
138 current++;
139 if ( feedback->isCanceled() )
140 {
141 break;
142 }
143
144 feedback->setProgress( current * step );
145 if ( !feat.hasGeometry() )
146 continue;
147
148 QgsGeometry inputGeometry = feat.geometry();
149
150 if ( mDynamicLength || mDynamicAngle )
151 {
152 expressionContext.setFeature( feat );
153 }
154
155 double evaluatedLength = mLength;
156 if ( mDynamicLength )
157 evaluatedLength = mLengthProperty.valueAsDouble( context.expressionContext(), mLength );
158 double evaluatedAngle = mAngle;
159 if ( mDynamicAngle )
160 evaluatedAngle = mAngleProperty.valueAsDouble( context.expressionContext(), mAngle );
161
162 // Segmentize curved geometries to convert them to straight line segments
163 if ( QgsWkbTypes::isCurvedType( inputGeometry.wkbType() ) )
164 {
165 inputGeometry = QgsGeometry( inputGeometry.constGet()->segmentize() );
166 }
167
168 inputGeometry.convertToMultiType();
170
171 for ( int part = 0; part < multiLine->numGeometries(); ++part )
172 {
173 const QgsLineString *lineString = multiLine->lineStringN( part );
174 if ( !lineString )
175 continue;
176
177 // Let subclass generate sampling points using their specific strategy
178 std::vector<QgsPoint> samplingPoints = generateSamplingPoints( *lineString, parameters, context );
179
180 for ( int i = 0; i < static_cast<int>( samplingPoints.size() ); ++i )
181 {
182 const QgsPoint &pt = samplingPoints[i];
183
184 // Let subclass calculate azimuth using their specific method
185 double azimuth = calculateAzimuth( *lineString, pt, i );
186
187 QgsFeature outFeat;
188 QgsAttributes attrs = feat.attributes();
189 attrs << current << number << i + 1 << evaluatedAngle
190 << ( ( mOrientation == QgsTransectAlgorithmBase::Both ) ? evaluatedLength * 2 : evaluatedLength )
191 << static_cast<int>( mOrientation );
192 outFeat.setAttributes( attrs );
193 outFeat.setGeometry( calcTransect( pt, azimuth, evaluatedLength, mOrientation, evaluatedAngle, mDirection ) );
194 if ( !sink->addFeature( outFeat, QgsFeatureSink::FastInsert ) )
195 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, u"OUTPUT"_s ) );
196 number++;
197 }
198 }
199 }
200
201 sink->finalize();
202
203 QVariantMap outputs;
204 outputs.insert( u"OUTPUT"_s, dest );
205 return outputs;
206}
207
208QgsGeometry QgsTransectAlgorithmBase::calcTransect( const QgsPoint &point, const double angleAtVertex, const double length, const QgsTransectAlgorithmBase::Side orientation, const double angle, const QgsTransectAlgorithmBase::Direction direction )
209{
210 // Transect is built from right to left relative to the reference line direction.
211 QgsPoint pStart; // start point of the transect
212 QgsPoint pEnd; // end point of the transect
213
214 QgsPolyline transect;
215
216 switch ( orientation )
217 {
218 case QgsTransectAlgorithmBase::Right:
219 pStart = point.project( length, angle + 180.0 / M_PI * angleAtVertex );
220 pEnd = point;
221 break;
222
223 case QgsTransectAlgorithmBase::Left:
224 pEnd = point.project( -length, angle + 180.0 / M_PI * angleAtVertex );
225 pStart = point;
226 break;
227
228 case QgsTransectAlgorithmBase::Both:
229 pStart = point.project( length, angle + 180.0 / M_PI * angleAtVertex );
230 pEnd = point.project( -length, angle + 180.0 / M_PI * angleAtVertex );
231 break;
232 }
233
234 // Direction determines the transect orientation.
235 // The 'start' and 'end' points are defined relative to the input line's direction.
236 // For 'Both' sides transects, pStart is the right point and pEnd is the left point.
237 // - RightToLeft: Builds the transect from its start point to its end point.
238 // - LeftToRight: Builds the transect from its end point to its start point (this is the hydraulic convention, from left bank to right bank looking downstream).
239 if ( direction == QgsTransectAlgorithmBase::LeftToRight )
240 {
241 transect.append( pEnd );
242 transect.append( pStart );
243 }
244 else // RightToLeft
245 {
246 transect.append( pStart );
247 transect.append( pEnd );
248 }
249 return QgsGeometry::fromPolyline( transect );
250}
251
@ VectorLine
Vector line layers.
Definition qgis.h:3606
@ RegeneratesPrimaryKey
Algorithm always drops any existing primary keys or FID values and regenerates them in outputs.
Definition qgis.h:3690
QFlags< ProcessingAlgorithmDocumentationFlag > ProcessingAlgorithmDocumentationFlags
Flags describing algorithm behavior for documentation purposes.
Definition qgis.h:3701
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:280
@ LineString
LineString.
Definition qgis.h:283
@ Double
Double/float values.
Definition qgis.h:3875
virtual QgsAbstractGeometry * segmentize(double tolerance=M_PI/180., SegmentationToleranceType toleranceType=MaximumAngle) const
Returns a version of the geometry without curves.
A vector of attributes.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
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...
@ RegeneratePrimaryKey
This flag indicates, that a primary key field cannot be guaranteed to be unique and the sink should i...
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
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.
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:55
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition qgsfeedback.h:63
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:76
int numGeometries() const
Returns the number of geometries within the collection.
A geometry is the spatial representation of a feature.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
static QgsGeometry fromPolyline(const QgsPolyline &polyline)
Creates a new LineString geometry from a list of QgsPoint points.
bool convertToMultiType()
Converts single type geometry into multitype geometry e.g.
Qgis::WkbType wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.).
Line string geometry type, with support for z-dimension and m-values.
Multi line string geometry collection.
QgsLineString * lineStringN(int index)
Returns the line string with the specified index.
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:53
QgsPoint project(double distance, double azimuth, double inclination=90.0) const
Returns a new point which corresponds to this point projected by a specified distance with specified ...
Definition qgspoint.cpp:728
Contains information about the context in which a processing algorithm is executed.
QgsExpressionContext & expressionContext()
Returns the expression context.
Custom exception class for processing related exceptions.
QgsFeatureSource subclass which proxies methods to an underlying QgsFeatureSource,...
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 bool isDynamic(const QVariantMap &parameters, const QString &name)
Returns true if the parameter with matching name is a dynamic parameter, and must be evaluated once f...
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).
Definition for a property.
Definition qgsproperty.h:47
@ Double
Double value (including negative values).
Definition qgsproperty.h:57
@ DoublePositive
Positive double value (including 0).
Definition qgsproperty.h:58
A store for object properties.
double valueAsDouble(const QgsExpressionContext &context, double defaultValue=0.0, bool *ok=nullptr) const
Calculates the current value of the property and interprets it as a double.
static Qgis::WkbType addM(Qgis::WkbType type)
Adds the m dimension to a WKB type and returns the new type.
static Qgis::WkbType addZ(Qgis::WkbType type)
Adds the z dimension to a WKB type and returns the new type.
static Q_INVOKABLE bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
static Q_INVOKABLE bool hasM(Qgis::WkbType type)
Tests whether a WKB type contains m values.
static Q_INVOKABLE bool isCurvedType(Qgis::WkbType type)
Returns true if the WKB type is a curved type or can contain curved geometries.
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored).
T qgsgeometry_cast(QgsAbstractGeometry *geom)
QgsPointSequence QgsPolyline
Polyline as represented as a vector of points.
Definition qgsgeometry.h:72