28QString QgsPointsToPathsAlgorithm::name()
const
30 return QStringLiteral(
"pointstopath" );
33QString QgsPointsToPathsAlgorithm::displayName()
const
35 return QObject::tr(
"Points to path" );
38QString QgsPointsToPathsAlgorithm::shortHelpString()
const
40 return QObject::tr(
"This algorithm takes a point layer and connects its features creating a new line layer.\n\n"
41 "An attribute or expression may be specified to define the order the points should be connected. "
42 "If no order expression is specified, the feature ID is used.\n\n"
43 "A natural sort can be used when sorting by a string attribute "
44 "or expression (ie. place 'a9' before 'a10').\n\n"
45 "An attribute or expression can be selected to group points having the same value into the same resulting line." );
48QStringList QgsPointsToPathsAlgorithm::tags()
const
50 return QObject::tr(
"create,lines,points,connect,convert,join,path" ).split(
',' );
53QString QgsPointsToPathsAlgorithm::group()
const
55 return QObject::tr(
"Vector creation" );
58QString QgsPointsToPathsAlgorithm::groupId()
const
60 return QStringLiteral(
"vectorcreation" );
63void QgsPointsToPathsAlgorithm::initAlgorithm(
const QVariantMap & )
68 QObject::tr(
"Create closed paths" ),
false,
true ) );
70 QObject::tr(
"Order expression" ), QVariant(), QStringLiteral(
"INPUT" ),
true ) );
72 QObject::tr(
"Sort text containing numbers naturally" ),
false,
true ) );
74 QObject::tr(
"Path group expression" ), QVariant(), QStringLiteral(
"INPUT" ),
true ) );
79 QObject::tr(
"Directory for text output" ), QVariant(),
true,
false ) );
87 addParameter( orderField );
91 addParameter( groupField );
93 QObject::tr(
"Date format (if order field is DateTime)" ), QVariant(),
false,
true );
95 addParameter( dateFormat );
98QgsPointsToPathsAlgorithm *QgsPointsToPathsAlgorithm::createInstance()
const
100 return new QgsPointsToPathsAlgorithm();
105 std::unique_ptr< QgsProcessingFeatureSource > source( parameterAsSource( parameters, QStringLiteral(
"INPUT" ), context ) );
109 const bool closePaths = parameterAsBool( parameters, QStringLiteral(
"CLOSE_PATH" ), context );
111 QString orderExpressionString = parameterAsString( parameters, QStringLiteral(
"ORDER_EXPRESSION" ), context );
112 const QString orderFieldString = parameterAsString( parameters, QStringLiteral(
"ORDER_FIELD" ), context );
113 if ( ! orderFieldString.isEmpty() )
118 QString dateFormat = parameterAsString( parameters, QStringLiteral(
"DATE_FORMAT" ), context );
119 if ( ! dateFormat.isEmpty() )
121 QVector< QPair< QString, QString > > codeMap;
122 codeMap << QPair< QString, QString >(
"%%",
"%" )
123 << QPair< QString, QString >(
"%a",
"ddd" )
124 << QPair< QString, QString >(
"%A",
"dddd" )
125 << QPair< QString, QString >(
"%w",
"" )
126 << QPair< QString, QString >(
"%d",
"dd" )
127 << QPair< QString, QString >(
"%b",
"MMM" )
128 << QPair< QString, QString >(
"%B",
"MMMM" )
129 << QPair< QString, QString >(
"%m",
"MM" )
130 << QPair< QString, QString >(
"%y",
"yy" )
131 << QPair< QString, QString >(
"%Y",
"yyyy" )
132 << QPair< QString, QString >(
"%H",
"hh" )
133 << QPair< QString, QString >(
"%I",
"hh" )
134 << QPair< QString, QString >(
"%p",
"AP" )
135 << QPair< QString, QString >(
"%M",
"mm" )
136 << QPair< QString, QString >(
"%S",
"ss" )
137 << QPair< QString, QString >(
"%f",
"zzz" )
138 << QPair< QString, QString >(
"%z",
"" )
139 << QPair< QString, QString >(
"%Z",
"" )
140 << QPair< QString, QString >(
"%j",
"" )
141 << QPair< QString, QString >(
"%U",
"" )
142 << QPair< QString, QString >(
"%W",
"" )
143 << QPair< QString, QString >(
"%c",
"" )
144 << QPair< QString, QString >(
"%x",
"" )
145 << QPair< QString, QString >(
"%X",
"" )
146 << QPair< QString, QString >(
"%G",
"yyyy" )
147 << QPair< QString, QString >(
"%u",
"" )
148 << QPair< QString, QString >(
"%V",
"" );
149 for (
const auto &pair : codeMap )
151 dateFormat.replace( pair.first, pair.second );
153 orderExpressionString = QString(
"to_datetime(%1, '%2')" ).arg( orderExpressionString ).arg( dateFormat );
156 else if ( orderExpressionString.isEmpty() )
159 orderExpressionString = QString(
"$id" );
161 QgsExpressionContext expressionContext = createExpressionContext( parameters, context, source.get() );
166 QStringList requiredFields = QStringList( orderExpression.
referencedColumns().values() );
167 orderExpression.
prepare( &expressionContext );
169 QMetaType::Type orderFieldType = QMetaType::Type::QString;
170 if ( orderExpression.
isField() )
172 const int orderFieldIndex = source->fields().indexFromName( orderExpression.
referencedColumns().values().first() );
173 orderFieldType = source->fields().field( orderFieldIndex ).type();
177 QString groupExpressionString = parameterAsString( parameters, QStringLiteral(
"GROUP_EXPRESSION" ), context );
179 const QString groupFieldString = parameterAsString( parameters, QStringLiteral(
"GROUP_FIELD" ), context );
180 if ( ! groupFieldString.isEmpty() )
188 if ( ! groupExpressionString.isEmpty() )
191 const QgsField field = groupExpression.
isField() ? source->fields().field( requiredFields.last() ) : QStringLiteral(
"group" );
192 outputFields.
append( field );
197 const bool naturalSort = parameterAsBool( parameters, QStringLiteral(
"NATURAL_SORT" ), context );
199 collator.setNumericMode(
true );
208 std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral(
"OUTPUT" ), context, dest, outputFields, wkbType, source->sourceCrs() ) );
212 const QString textDir = parameterAsString( parameters, QStringLiteral(
"OUTPUT_TEXT_DIR" ), context );
213 if ( !textDir.isEmpty() && !QDir().mkpath( textDir ) )
223 QHash< QVariant, QVector< QPair< QVariant, QgsPoint > > > allPoints;
228 const double totalPoints = source->featureCount() > 0 ? 100.0 / source->featureCount() : 0;
229 long currentPoint = 0;
237 feedback->
setProgress( 0.5 * currentPoint * totalPoints );
242 const QVariant orderValue = orderExpression.
evaluate( &expressionContext );
243 const QVariant groupValue = groupExpressionString.isEmpty() ? QVariant() : groupExpression.evaluate( &expressionContext );
245 if ( ! allPoints.contains( groupValue ) )
246 allPoints[ groupValue ] = QVector< QPair< QVariant, QgsPoint > >();
250 const QgsMultiPoint mp( *qgsgeometry_cast< const QgsMultiPoint * >( geom ) );
251 for (
auto pit = mp.const_parts_begin(); pit != mp.const_parts_end(); ++pit )
253 if (
const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( *pit ) )
255 allPoints[ groupValue ] << qMakePair( orderValue, *point );
261 if (
const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom ) )
263 allPoints[ groupValue ] << qMakePair( orderValue, *point );
272 QHashIterator< QVariant, QVector< QPair< QVariant, QgsPoint > > > hit( allPoints );
274 while ( hit.hasNext() )
281 QVector< QPair< QVariant, QgsPoint > > pairs = hit.value();
285 std::stable_sort( pairs.begin(),
287 [&collator](
const QPair< const QVariant, QgsPoint > &pair1,
288 const QPair< const QVariant, QgsPoint > &pair2 )
290 return collator.compare( pair1.first.toString(), pair2.first.toString() ) < 0;
295 std::stable_sort( pairs.begin(),
297 [](
const QPair< const QVariant, QgsPoint > &pair1,
298 const QPair< const QVariant, QgsPoint > &pair2 )
300 return qgsVariantLessThan( pair1.first, pair2.first );
305 QVector<QgsPoint> pathPoints;
306 for (
auto pit = pairs.constBegin(); pit != pairs.constEnd(); ++pit )
312 feedback->
setProgress( 50 + 0.5 * currentPoint * totalPoints );
313 pathPoints.append( pit->second );
316 if ( pathPoints.size() < 2 )
318 feedback->
pushInfo( QObject::tr(
"Skipping path with group %1 : insufficient vertices" ).arg( hit.key().toString() ) );
321 if ( closePaths && pathPoints.size() > 2 && pathPoints.constFirst() != pathPoints.constLast() )
322 pathPoints.append( pathPoints.constFirst() );
326 if ( ! groupExpressionString.isEmpty() )
327 attrs.append( hit.key() );
328 attrs.append( pairs.first().first );
329 attrs.append( pairs.last().first );
335 if ( !textDir.isEmpty() )
337 const QString filename = QDir( textDir ).filePath( hit.key().toString() + QString(
".txt" ) );
338 QFile textFile( filename );
339 if ( !textFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
342 QTextStream out( &textFile );
343#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
344 out.setCodec(
"UTF-8" );
346 out << QString(
"angle=Azimuth\n"
347 "heading=Coordinate_System\n"
348 "dist_units=Default\n"
351 "[data]\n" ).arg( pathPoints.at( 0 ).x() ).arg( pathPoints.at( 0 ).y() );
353 for (
int i = 1; i < pathPoints.size(); ++i )
355 const double angle = pathPoints.at( i - 1 ).azimuth( pathPoints.at( i ) );
359 distance = da.
measureLine( pathPoints.at( i - 1 ), pathPoints.at( i ) );
365 out << QString(
"%1;%2;90\n" ).arg( angle ).arg( distance );
375 outputs.insert( QStringLiteral(
"OUTPUT" ), dest );
376 outputs.insert( QStringLiteral(
"NUM_PATHS" ), pathCount );
377 if ( !textDir.isEmpty() )
379 outputs.insert( QStringLiteral(
"OUTPUT_TEXT_DIR" ), textDir );
@ VectorPoint
Vector point layers.
@ VectorLine
Vector line layers.
@ SkipGeometryValidityChecks
Invalid geometry checks should always be skipped. This flag can be useful for algorithms which always...
WkbType
The WKB type describes the number of dimensions a geometry has.
@ Hidden
Parameter is hidden and should not be shown to users.
Abstract base class for all geometries.
Custom exception class for Coordinate Reference System related exceptions.
A general purpose distance and area calculator, capable of performing ellipsoid based calculations.
double measureLine(const QVector< QgsPointXY > &points) const
Measures the length of a line with multiple segments.
void setSourceCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets source spatial reference system crs.
bool setEllipsoid(const QString &ellipsoid)
Sets the ellipsoid by its acronym.
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.
Class for parsing and evaluation of expressions (formerly called "search strings").
bool prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
QString parserErrorString() const
Returns parser error.
bool isField() const
Checks whether an expression consists only of a single field reference.
QSet< QString > referencedColumns() const
Gets list of columns referenced by the expression.
static QString quotedColumnRef(QString name)
Returns a quoted column reference (in double quotes)
QVariant evaluate()
Evaluate the feature and return the result.
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.
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
@ 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...
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
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.
void setProgress(double progress)
Sets the current progress for the feedback object.
Encapsulate a field in an attribute table or data source.
Container of fields for a vector layer.
bool append(const QgsField &field, Qgis::FieldOrigin origin=Qgis::FieldOrigin::Provider, int originIndex=-1)
Appends a field.
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.
Multi point geometry collection.
Point geometry type, with support for z-dimension and m-values.
Contains information about the context in which a processing algorithm is executed.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context.
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.
virtual void pushInfo(const QString &info)
Pushes a general informational message from the algorithm.
virtual void setProgressText(const QString &text)
Sets a progress report text string.
A numeric output for processing algorithms.
A boolean parameter for processing algorithms.
void setFlags(Qgis::ProcessingParameterFlags flags)
Sets the flags associated with the parameter.
Qgis::ProcessingParameterFlags flags() const
Returns any flags associated with the parameter.
An expression parameter for processing algorithms.
A feature sink output for processing algorithms.
An input feature source (such as vector layers) parameter for processing algorithms.
A vector layer or feature source field parameter for processing algorithms.
A folder destination parameter, for specifying the destination path for a folder created by the algor...
A string parameter for processing algorithms.
static bool isMultiType(Qgis::WkbType type)
Returns true if the WKB type is a multi type.
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 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.
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)