QGIS API Documentation 4.1.0-Master (5bf3c20f3c9)
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
67 = 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 );
68 angle->setIsDynamic( true );
69 angle->setDynamicPropertyDefinition( QgsPropertyDefinition( u"ANGLE"_s, QObject::tr( "Angle in degrees" ), QgsPropertyDefinition::Double ) );
70 angle->setDynamicLayerParameterName( u"INPUT"_s );
71 addParameter( angle.release() );
72
73 addParameter(
74 new QgsProcessingParameterEnum( u"SIDE"_s, QObject::tr( "Side to create the transects" ), QStringList() << QObject::tr( "Left" ) << QObject::tr( "Right" ) << QObject::tr( "Both" ), false, 2 )
75 );
76
77 auto direction
78 = std::make_unique<QgsProcessingParameterEnum>( u"DIRECTION"_s, QObject::tr( "Direction" ), QStringList() << QObject::tr( "Right to Left" ) << QObject::tr( "Left to Right" ), false, 0, true );
79 direction->setGuiDefaultValueOverride( 1 );
80 addParameter( direction.release() );
81
82 addParameter( new QgsProcessingParameterFeatureSink( u"OUTPUT"_s, QObject::tr( "Transect" ), Qgis::ProcessingSourceType::VectorLine ) );
83}
84
85QVariantMap QgsTransectAlgorithmBase::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
86{
87 mOrientation = static_cast<QgsTransectAlgorithmBase::Side>( parameterAsInt( parameters, u"SIDE"_s, context ) );
88 mAngle = fabs( parameterAsDouble( parameters, u"ANGLE"_s, context ) );
89 mDynamicAngle = QgsProcessingParameters::isDynamic( parameters, u"ANGLE"_s );
90 if ( mDynamicAngle )
91 mAngleProperty = parameters.value( u"ANGLE"_s ).value<QgsProperty>();
92
93 mLength = parameterAsDouble( parameters, u"LENGTH"_s, context );
94 mDynamicLength = QgsProcessingParameters::isDynamic( parameters, u"LENGTH"_s );
95 if ( mDynamicLength )
96 mLengthProperty = parameters.value( u"LENGTH"_s ).value<QgsProperty>();
97
98 if ( mOrientation == QgsTransectAlgorithmBase::Both )
99 mLength /= 2.0;
100
101 mDirection = static_cast<QgsTransectAlgorithmBase::Direction>( parameterAsInt( parameters, u"DIRECTION"_s, context ) );
102
103 // Let subclass prepare their specific parameters
104 if ( !prepareAlgorithmTransectParameters( parameters, context, feedback ) )
105 return QVariantMap();
106
107 std::unique_ptr<QgsFeatureSource> source( parameterAsSource( parameters, u"INPUT"_s, context ) );
108 if ( !source )
109 throw QgsProcessingException( invalidSourceError( parameters, u"INPUT"_s ) );
110
111 QgsExpressionContext expressionContext = createExpressionContext( parameters, context, dynamic_cast<QgsProcessingFeatureSource *>( source.get() ) );
112
113 QgsFields newFields;
114 newFields.append( QgsField( u"TR_FID"_s, QMetaType::Type::Int, QString(), 20 ) );
115 newFields.append( QgsField( u"TR_ID"_s, QMetaType::Type::Int, QString(), 20 ) );
116 newFields.append( QgsField( u"TR_SEGMENT"_s, QMetaType::Type::Int, QString(), 20 ) );
117 newFields.append( QgsField( u"TR_ANGLE"_s, QMetaType::Type::Double, QString(), 5, 2 ) );
118 newFields.append( QgsField( u"TR_LENGTH"_s, QMetaType::Type::Double, QString(), 20, 6 ) );
119 newFields.append( QgsField( u"TR_ORIENT"_s, QMetaType::Type::Int, QString(), 1 ) );
120 QgsFields fields = QgsProcessingUtils::combineFields( source->fields(), newFields );
121
123 if ( QgsWkbTypes::hasZ( source->wkbType() ) )
124 outputWkb = QgsWkbTypes::addZ( outputWkb );
125 if ( QgsWkbTypes::hasM( source->wkbType() ) )
126 outputWkb = QgsWkbTypes::addM( outputWkb );
127
128 QString dest;
129 std::unique_ptr<QgsFeatureSink> sink( parameterAsSink( parameters, u"OUTPUT"_s, context, dest, fields, outputWkb, source->sourceCrs(), QgsFeatureSink::RegeneratePrimaryKey ) );
130 if ( !sink )
131 throw QgsProcessingException( invalidSinkError( parameters, u"OUTPUT"_s ) );
132
133 QgsFeatureIterator features = source->getFeatures();
134
135 int current = -1;
136 int number = 0;
137 const double step = source->featureCount() > 0 ? 100.0 / source->featureCount() : 1;
138 QgsFeature feat;
139
140 while ( features.nextFeature( feat ) )
141 {
142 current++;
143 if ( feedback->isCanceled() )
144 {
145 break;
146 }
147
148 feedback->setProgress( current * step );
149 if ( !feat.hasGeometry() )
150 continue;
151
152 QgsGeometry inputGeometry = feat.geometry();
153
154 if ( mDynamicLength || mDynamicAngle )
155 {
156 expressionContext.setFeature( feat );
157 }
158
159 double evaluatedLength = mLength;
160 if ( mDynamicLength )
161 evaluatedLength = mLengthProperty.valueAsDouble( context.expressionContext(), mLength );
162 double evaluatedAngle = mAngle;
163 if ( mDynamicAngle )
164 evaluatedAngle = mAngleProperty.valueAsDouble( context.expressionContext(), mAngle );
165
166 // Segmentize curved geometries to convert them to straight line segments
167 if ( QgsWkbTypes::isCurvedType( inputGeometry.wkbType() ) )
168 {
169 inputGeometry = QgsGeometry( inputGeometry.constGet()->segmentize() );
170 }
171
172 inputGeometry.convertToMultiType();
174
175 for ( int part = 0; part < multiLine->numGeometries(); ++part )
176 {
177 const QgsLineString *lineString = multiLine->lineStringN( part );
178 if ( !lineString )
179 continue;
180
181 // Let subclass generate sampling points using their specific strategy
182 std::vector<QgsPoint> samplingPoints = generateSamplingPoints( *lineString, parameters, context );
183
184 for ( int i = 0; i < static_cast<int>( samplingPoints.size() ); ++i )
185 {
186 const QgsPoint &pt = samplingPoints[i];
187
188 // Let subclass calculate azimuth using their specific method
189 double azimuth = calculateAzimuth( *lineString, pt, i );
190
191 QgsFeature outFeat;
192 QgsAttributes attrs = feat.attributes();
193 attrs << current << number << i + 1 << evaluatedAngle << ( ( mOrientation == QgsTransectAlgorithmBase::Both ) ? evaluatedLength * 2 : evaluatedLength ) << static_cast<int>( mOrientation );
194 outFeat.setAttributes( attrs );
195 outFeat.setGeometry( calcTransect( pt, azimuth, evaluatedLength, mOrientation, evaluatedAngle, mDirection ) );
196 if ( !sink->addFeature( outFeat, QgsFeatureSink::FastInsert ) )
197 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, u"OUTPUT"_s ) );
198 number++;
199 }
200 }
201 }
202
203 sink->finalize();
204
205 QVariantMap outputs;
206 outputs.insert( u"OUTPUT"_s, dest );
207 return outputs;
208}
209
210QgsGeometry QgsTransectAlgorithmBase::calcTransect(
211 const QgsPoint &point, const double angleAtVertex, const double length, const QgsTransectAlgorithmBase::Side orientation, const double angle, const QgsTransectAlgorithmBase::Direction direction
212)
213{
214 // Transect is built from right to left relative to the reference line direction.
215 QgsPoint pStart; // start point of the transect
216 QgsPoint pEnd; // end point of the transect
217
218 QgsPolyline transect;
219
220 switch ( orientation )
221 {
222 case QgsTransectAlgorithmBase::Right:
223 pStart = point.project( length, angle + 180.0 / M_PI * angleAtVertex );
224 pEnd = point;
225 break;
226
227 case QgsTransectAlgorithmBase::Left:
228 pEnd = point.project( -length, angle + 180.0 / M_PI * angleAtVertex );
229 pStart = point;
230 break;
231
232 case QgsTransectAlgorithmBase::Both:
233 pStart = point.project( length, angle + 180.0 / M_PI * angleAtVertex );
234 pEnd = point.project( -length, angle + 180.0 / M_PI * angleAtVertex );
235 break;
236 }
237
238 // Direction determines the transect orientation.
239 // The 'start' and 'end' points are defined relative to the input line's direction.
240 // For 'Both' sides transects, pStart is the right point and pEnd is the left point.
241 // - RightToLeft: Builds the transect from its start point to its end point.
242 // - 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).
243 if ( direction == QgsTransectAlgorithmBase::LeftToRight )
244 {
245 transect.append( pEnd );
246 transect.append( pStart );
247 }
248 else // RightToLeft
249 {
250 transect.append( pStart );
251 transect.append( pEnd );
252 }
253 return QgsGeometry::fromPolyline( transect );
254}
255
@ VectorLine
Vector line layers.
Definition qgis.h:3649
@ RegeneratesPrimaryKey
Algorithm always drops any existing primary keys or FID values and regenerates them in outputs.
Definition qgis.h:3734
QFlags< ProcessingAlgorithmDocumentationFlag > ProcessingAlgorithmDocumentationFlags
Flags describing algorithm behavior for documentation purposes.
Definition qgis.h:3745
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:294
@ LineString
LineString.
Definition qgis.h:297
@ Double
Double/float values.
Definition qgis.h:3921
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: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
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:734
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:56
@ DoublePositive
Positive double value (including 0).
Definition qgsproperty.h:57
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