QGIS API Documentation 3.99.0-Master (c22de0620c0)
Loading...
Searching...
No Matches
qgsalgorithmvalidatenetwork.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsalgorithmvalidatenetwork.cpp
3 -------------------------------
4 begin : January 2026
5 copyright : (C) 2026 by Nyall Dawson
6 email : nyall dot dawson 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
19
20#include "qgis.h"
21#include "qgsapplication.h"
22#include "qgsgraph.h"
23#include "qgsgraphbuilder.h"
24#include "qgslinestring.h"
28
29#include <QString>
30
31using namespace Qt::StringLiterals;
32
34
35QString QgsValidateNetworkAlgorithm::name() const
36{
37 return u"validatenetwork"_s;
38}
39
40QString QgsValidateNetworkAlgorithm::displayName() const
41{
42 return QObject::tr( "Validate network" );
43}
44
45QStringList QgsValidateNetworkAlgorithm::tags() const
46{
47 return QObject::tr( "topological,topology,check,graph,shortest,path" ).split( ',' );
48}
49
50QString QgsValidateNetworkAlgorithm::group() const
51{
52 return QObject::tr( "Network analysis" );
53}
54
55QString QgsValidateNetworkAlgorithm::groupId() const
56{
57 return u"networkanalysis"_s;
58}
59
60QIcon QgsValidateNetworkAlgorithm::icon() const
61{
62 return QgsApplication::getThemeIcon( u"/algorithms/mAlgorithmNetworkAnalysis.svg"_s );
63}
64
65QString QgsValidateNetworkAlgorithm::svgIconPath() const
66{
67 return QgsApplication::iconPath( u"/algorithms/mAlgorithmNetworkAnalysis.svg"_s );
68}
69
70QString QgsValidateNetworkAlgorithm::shortDescription() const
71{
72 return QObject::tr( "Validates a network line layer, identifying data and topology errors that may affect network analysis tools." );
73}
74
75QString QgsValidateNetworkAlgorithm::shortHelpString() const
76{
77 return QObject::tr( "This algorithm analyzes a network vector layer to identify data and topology errors "
78 "that may affect network analysis tools (like shortest path).\n\n"
79 "Optional checks include:\n\n"
80 "1. Validating the 'Direction' field to ensure all direction field values in the input layer "
81 "match the configured forward/backward/both values. Errors will be reported if the direction field "
82 "value is non-null and does not match one of the configured values.\n"
83 "2. Checking node-to-node separation. This check identifies nodes from the network graph that "
84 "are closer to other nodes than the specified tolerance distance. This often indicates missed "
85 "snaps or short segments in the input layer. In the case that a node violates this condition with multiple other "
86 "nodes, only the closest violation will be reported.\n"
87 "3. Checking node-to-segment separation: This check identifies nodes that are closer to a line "
88 "segment (e.g. a graph edge) than the specified tolerance distance, without being connected to it. In the case "
89 "that a node violates this condition with multiple other edges, only the closest violation will be reported.\n\n"
90 "Topology checks (node-to-node and node-to-segment) can optionally be restricted to only evaluate nodes that are topological dead-ends (connected to only one other distinct node). This is useful for specifically targeting dangles or undershoots.\n\n"
91 "Two layers are output by this algorithm:\n"
92 "1. An output containing features from the original network layer which failed the direction validation checks.\n"
93 "2. An output representing the problematic node locations with a 'error' field explaining the error. This is "
94 "a line layer, where the output features join the problematic node to the node or "
95 "segment which failed the tolerance checks." );
96}
97
98QgsValidateNetworkAlgorithm *QgsValidateNetworkAlgorithm::createInstance() const
99{
100 return new QgsValidateNetworkAlgorithm();
101}
102
103void QgsValidateNetworkAlgorithm::initAlgorithm( const QVariantMap & )
104{
105 addParameter( new QgsProcessingParameterFeatureSource( u"INPUT"_s, QObject::tr( "Vector layer representing network" ), QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorLine ) ) );
106
107 auto separationNodeNodeParam = std::make_unique<QgsProcessingParameterDistance>( u"TOLERANCE_NODE_NODE"_s, QObject::tr( "Minimum separation between nodes" ), QVariant(), u"INPUT"_s, true );
108 separationNodeNodeParam->setFlags( separationNodeNodeParam->flags() | Qgis::ProcessingParameterFlag::Optional );
109 separationNodeNodeParam->setHelp( QObject::tr( "The minimum allowed distance between two distinct graph nodes.\n\n"
110 "Nodes closer than this distance (but not identical) will be flagged as errors.\n\n"
111 "Leave empty to disable this check." ) );
112 addParameter( separationNodeNodeParam.release() );
113
114 auto separationNodeSegmentParam = std::make_unique<QgsProcessingParameterDistance>( u"TOLERANCE_NODE_SEGMENT"_s, QObject::tr( "Minimum separation between nodes and non-noded segments" ), QVariant(), u"INPUT"_s, true );
115 separationNodeSegmentParam->setFlags( separationNodeSegmentParam->flags() | Qgis::ProcessingParameterFlag::Optional );
116 separationNodeSegmentParam->setHelp( QObject::tr( "The minimum allowed distance between a graph node and a graph edge (segment) "
117 "that is not connected to the node.\n\n"
118 "Nodes closer to a segment than this distance "
119 "will be flagged. Leave empty to disable this check." ) );
120 addParameter( separationNodeSegmentParam.release() );
121
122 auto endpointsOnlyParam = std::make_unique<QgsProcessingParameterBoolean>( u"ENDPOINTS_ONLY"_s, QObject::tr( "Only check for errors at end points" ), false );
123 endpointsOnlyParam->setHelp( QObject::tr( "If checked, topology checks (node-to-node and node-to-segment) will only be evaluated for nodes that are topological dead-ends (connected to only one other distinct node)." ) );
124 addParameter( endpointsOnlyParam.release() );
125
126 auto directionField = std::make_unique<QgsProcessingParameterField>( u"DIRECTION_FIELD"_s, QObject::tr( "Direction field" ), QVariant(), u"INPUT"_s, Qgis::ProcessingFieldParameterDataType::Any, false, true );
127 directionField->setHelp( QObject::tr( "The attribute field specifying the direction of traffic flow for each segment." ) );
128 addParameter( directionField.release() );
129
130 auto forwardValue = std::make_unique<QgsProcessingParameterString>( u"VALUE_FORWARD"_s, QObject::tr( "Value for forward direction" ), QVariant(), false, true );
131 forwardValue->setHelp( QObject::tr( "The string value in the direction field that indicates one-way traffic in the digitized direction." ) );
132 addParameter( forwardValue.release() );
133
134 auto backwardValue = std::make_unique<QgsProcessingParameterString>( u"VALUE_BACKWARD"_s, QObject::tr( "Value for backward direction" ), QVariant(), false, true );
135 backwardValue->setHelp( QObject::tr( "The string value in the direction field that indicates one-way traffic opposite to the digitized direction." ) );
136 addParameter( backwardValue.release() );
137
138 auto bothValue = std::make_unique<QgsProcessingParameterString>( u"VALUE_BOTH"_s, QObject::tr( "Value for both directions" ), QVariant(), false, true );
139 bothValue->setHelp( QObject::tr( "The string value in the direction field that indicates two-way traffic." ) );
140 addParameter( bothValue.release() );
141
142 std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterDistance>( u"TOLERANCE"_s, QObject::tr( "Topology tolerance" ), 0, u"INPUT"_s, false, 0 );
143 tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );
144 addParameter( tolerance.release() );
145
146 auto invalidNetworkOutput = std::make_unique< QgsProcessingParameterFeatureSink >( u"OUTPUT_INVALID_NETWORK"_s, QObject::tr( "Invalid network features" ), Qgis::ProcessingSourceType::VectorLine, QVariant(), true, true );
147 invalidNetworkOutput->setHelp( QObject::tr( "Output line layer containing geometries representing features from the network layer with validity errors.\n\n"
148 "This output includes an attribute explaining why each feature is invalid." ) );
149 addParameter( invalidNetworkOutput.release() );
150
151 addOutput( new QgsProcessingOutputNumber( u"COUNT_INVALID_NETWORK_FEATURES"_s, QObject::tr( "Count of invalid network features" ) ) );
152
153 auto invalidNodeOutput = std::make_unique< QgsProcessingParameterFeatureSink >( u"OUTPUT_INVALID_NODES"_s, QObject::tr( "Invalid network nodes" ), Qgis::ProcessingSourceType::VectorLine, QVariant(), true, true );
154 invalidNodeOutput->setHelp( QObject::tr( "Output line layer containing geometries representing nodes from the network layer with validity errors.\n\n"
155 "This output includes an attribute explaining why each node is invalid." ) );
156 addParameter( invalidNodeOutput.release() );
157
158 addOutput( new QgsProcessingOutputNumber( u"COUNT_INVALID_NODES"_s, QObject::tr( "Count of invalid network nodes" ) ) );
159}
160
161QVariantMap QgsValidateNetworkAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
162{
163 std::unique_ptr<QgsFeatureSource> networkSource( parameterAsSource( parameters, u"INPUT"_s, context ) );
164 if ( !networkSource )
165 throw QgsProcessingException( invalidSourceError( parameters, u"INPUT"_s ) );
166
167 const QString directionFieldName = parameterAsString( parameters, u"DIRECTION_FIELD"_s, context );
168 const QString forwardValue = parameterAsString( parameters, u"VALUE_FORWARD"_s, context );
169 const QString backwardValue = parameterAsString( parameters, u"VALUE_BACKWARD"_s, context );
170 const QString bothValue = parameterAsString( parameters, u"VALUE_BOTH"_s, context );
171 const double tolerance = parameterAsDouble( parameters, u"TOLERANCE"_s, context );
172
173 const bool checkEndpointsOnly = parameterAsBoolean( parameters, u"ENDPOINTS_ONLY"_s, context );
174
175 double toleranceNodeToNode = 0;
176 bool checkNodeToNodeDistance = false;
177 if ( parameters.value( u"TOLERANCE_NODE_NODE"_s ).isValid() )
178 {
179 toleranceNodeToNode = parameterAsDouble( parameters, u"TOLERANCE_NODE_NODE"_s, context );
180 checkNodeToNodeDistance = ( toleranceNodeToNode > 0 );
181 }
182
183 double toleranceNodeToSegment = 0;
184 bool checkNodeToSegmentDistance = false;
185 if ( parameters.value( u"TOLERANCE_NODE_SEGMENT"_s ).isValid() )
186 {
187 toleranceNodeToSegment = parameterAsDouble( parameters, u"TOLERANCE_NODE_SEGMENT"_s, context );
188 checkNodeToSegmentDistance = ( toleranceNodeToSegment > 0 );
189 }
190
191 QgsFields newNetworkErrorFields;
192 newNetworkErrorFields.append( QgsField( u"error"_s, QMetaType::Type::QString ) );
193 const QgsFields networkErrorFields = QgsProcessingUtils::combineFields( networkSource->fields(), newNetworkErrorFields );
194
195 QString networkErrorDest;
196 std::unique_ptr<QgsFeatureSink> networkErrorSink( parameterAsSink( parameters, u"OUTPUT_INVALID_NETWORK"_s, context, networkErrorDest, networkErrorFields, networkSource->wkbType(), networkSource->sourceCrs() ) );
197
198 QgsFields nodeErrorFields;
199 nodeErrorFields.append( QgsField( u"error"_s, QMetaType::Type::QString ) );
200
201 QString nodeErrorDest;
202 std::unique_ptr<QgsFeatureSink> nodeErrorSink( parameterAsSink( parameters, u"OUTPUT_INVALID_NODES"_s, context, nodeErrorDest, nodeErrorFields, Qgis::WkbType::LineString, networkSource->sourceCrs() ) );
203
204 QgsProcessingMultiStepFeedback multiFeedback( 4, feedback );
205 multiFeedback.setStepWeights( { 10, 40, 10, 40 } );
206 multiFeedback.setCurrentStep( 0 );
207
208 QVariantMap outputs;
209 if ( networkErrorSink )
210 outputs.insert( u"OUTPUT_INVALID_NETWORK"_s, networkErrorDest );
211 if ( nodeErrorSink )
212 outputs.insert( u"OUTPUT_INVALID_NODES"_s, nodeErrorDest );
213
214 // attribute validation
215 int directionFieldIdx = -1;
216 long long countInvalidFeatures = 0;
217 if ( !directionFieldName.isEmpty() )
218 {
219 directionFieldIdx = networkSource->fields().lookupField( directionFieldName );
220 if ( directionFieldIdx < 0 )
221 {
222 throw QgsProcessingException( QObject::tr( "Missing field %1 in input layer" ).arg( directionFieldName ) );
223 }
224
225 multiFeedback.pushInfo( QObject::tr( "Validating direction attributes…" ) );
226 const long long count = networkSource->featureCount();
227 long long current = 0;
228 const double step = count > 0 ? 100.0 / static_cast< double >( count ) : 1;
229
230 QgsFeatureIterator fit = networkSource->getFeatures();
231 QgsFeature feature;
232 while ( fit.nextFeature( feature ) )
233 {
234 if ( multiFeedback.isCanceled() )
235 break;
236
237 const QVariant val = feature.attribute( directionFieldIdx );
238 if ( !QgsVariantUtils::isNull( val ) )
239 {
240 const QString directionValueString = val.toString();
241 if ( directionValueString != forwardValue && directionValueString != backwardValue && directionValueString != bothValue )
242 {
243 if ( networkErrorSink )
244 {
245 QgsFeature outputFeature = feature;
246 QgsAttributes outputFeatureAttrs = outputFeature.attributes();
247 outputFeatureAttrs.append( QObject::tr( "Invalid direction value: '%1'" ).arg( directionValueString ) );
248 outputFeature.setAttributes( outputFeatureAttrs );
249 if ( !networkErrorSink->addFeature( outputFeature, QgsFeatureSink::FastInsert ) )
250 {
251 throw QgsProcessingException( writeFeatureError( networkErrorSink.get(), parameters, u"OUTPUT_INVALID_NETWORK"_s ) );
252 }
253 }
254 countInvalidFeatures++;
255 }
256 }
257
258 current++;
259 multiFeedback.setProgress( static_cast< double >( current ) * step );
260 }
261
262 if ( networkErrorSink )
263 {
264 networkErrorSink->finalize();
265 }
266 }
267
268 outputs.insert( u"COUNT_INVALID_NETWORK_FEATURES"_s, countInvalidFeatures );
269 if ( countInvalidFeatures > 0 )
270 {
271 multiFeedback.reportError( QObject::tr( "Found %1 invalid network features" ).arg( countInvalidFeatures ) );
272 }
273
274 if ( !checkNodeToNodeDistance && !checkNodeToSegmentDistance )
275 {
276 // nothing more to do
277 return outputs;
278 }
279
280 multiFeedback.pushInfo( QObject::tr( "Building graph for topology validation…" ) );
281 multiFeedback.setCurrentStep( 1 );
282
283 QgsVectorLayerDirector director( networkSource.get(), directionFieldIdx, forwardValue, backwardValue, bothValue, QgsVectorLayerDirector::DirectionBoth );
284 QgsGraphBuilder builder( networkSource->sourceCrs(), true, tolerance, context.ellipsoid() );
285
286 QVector<QgsPointXY> snappedPoints;
287 director.makeGraph( &builder, {}, snappedPoints, &multiFeedback );
288
289 std::unique_ptr<QgsGraph> graph( builder.takeGraph() );
290
291 if ( multiFeedback.isCanceled() )
292 return outputs;
293
294 multiFeedback.pushInfo( QObject::tr( "Indexing graph nodes and edges…" ) );
295 multiFeedback.setCurrentStep( 2 );
296
297 // better index choice for point node index -- we satisfy the requirements
298 // of point geometries only, finalized once before reading
299 QgsSpatialIndexKDBush nodeIndex;
300 // standard QgsSpatialIndex for edges -- we can't use the faster KDBush index for these, as that is point only
302
303 const int vertexCount = graph->vertexCount();
304
305 const long long totalGraphElements = ( checkNodeToNodeDistance ? vertexCount : 0 ) + ( checkNodeToSegmentDistance ? graph->edgeCount() : 0 );
306 const double indexStep = totalGraphElements > 0 ? 100.0 / static_cast< double >( totalGraphElements ) : 1;
307 long long elementsProcessed = 0;
308
309 if ( checkNodeToNodeDistance )
310 {
311 for ( int i = 0; i < vertexCount; ++i )
312 {
313 if ( multiFeedback.isCanceled() )
314 break;
315 nodeIndex.addFeature( i, graph->vertex( i ).point() );
316 elementsProcessed++;
317 multiFeedback.setProgress( static_cast< double >( elementsProcessed ) * indexStep );
318 }
319 nodeIndex.finalize();
320 }
321
322 if ( checkNodeToSegmentDistance )
323 {
324 for ( int i = 0; i < graph->edgeCount(); ++i )
325 {
326 if ( multiFeedback.isCanceled() )
327 break;
328
329 const QgsGraphEdge &edge = graph->edge( i );
330 const QgsPointXY p1 = graph->vertex( edge.fromVertex() ).point();
331 const QgsPointXY p2 = graph->vertex( edge.toVertex() ).point();
332
333 edgeIndex.addFeature( i, QgsRectangle( p1, p2 ) );
334 elementsProcessed++;
335 multiFeedback.setProgress( static_cast< double >( elementsProcessed ) * indexStep );
336 }
337 }
338
339 // perform topology checks
340 multiFeedback.pushInfo( QObject::tr( "Validating graph topology…" ) );
341 multiFeedback.setCurrentStep( 2 );
342
343 const double topoStep = vertexCount > 0 ? 100.0 / vertexCount : 1;
344
345 struct NodeError
346 {
347 long long id = 0;
348 QgsPointXY pt;
349 double distance = std::numeric_limits<double>::max();
350 };
351
352 QSet< QPair< long long, long long > > alreadyReportedNodes;
353 long long countInvalidNodes = 0;
354
355 for ( long long i = 0; i < vertexCount; ++i )
356 {
357 if ( multiFeedback.isCanceled() )
358 break;
359
360 const QgsGraphVertex &v = graph->vertex( i );
361 const QgsPointXY &pt = v.point();
362
363 // whether we need to perform validation on this node
364 bool evaluateNode = true;
365
366 if ( checkEndpointsOnly )
367 {
368 // count unique neighbors to handle bidirectional segments (A->B and B->A) counting as one connection
369 QSet<int> adjacentNodeIndices;
370 for ( int edgeId : v.outgoingEdges() )
371 {
372 adjacentNodeIndices.insert( graph->edge( edgeId ).toVertex() );
373 }
374 for ( int edgeId : v.incomingEdges() )
375 {
376 adjacentNodeIndices.insert( graph->edge( edgeId ).fromVertex() );
377 }
378 if ( adjacentNodeIndices.count() != 1 )
379 {
380 evaluateNode = false;
381 }
382 }
383
384 if ( evaluateNode && checkNodeToNodeDistance )
385 {
386 const std::vector< QgsVectorLayerDirector::VertexSourceInfo > &fidsFirstNode = director.sourcesForVertex( i );
387
388 const QList<QgsSpatialIndexKDBushData> candidates = nodeIndex.intersects(
389 QgsRectangle::fromCenterAndSize( pt, toleranceNodeToNode * 2, toleranceNodeToNode * 2 )
390 );
391
392 // only keep the closest violation
393 NodeError closestError;
394 for ( const QgsSpatialIndexKDBushData &data : candidates )
395 {
396 // skip self
397 if ( data.id == i )
398 continue;
399
400 // ignore nodes which are directly connected to each other
401 bool skip = false;
402 for ( const int edge : v.incomingEdges() )
403 {
404 if ( graph->edge( edge ).fromVertex() == i )
405 {
406 skip = true;
407 break;
408 }
409 }
410 if ( skip )
411 continue;
412 for ( const int edge : v.outgoingEdges() )
413 {
414 if ( graph->edge( edge ).toVertex() == data.id )
415 {
416 skip = true;
417 break;
418 }
419 }
420 if ( skip )
421 continue;
422
423 const std::vector<QgsVectorLayerDirector::VertexSourceInfo> &fidsSecondNode = director.sourcesForVertex( data.id );
424
425 bool shareCommonFeature = false;
426 for ( const QgsVectorLayerDirector::VertexSourceInfo &info1 : fidsFirstNode )
427 {
428 for ( const QgsVectorLayerDirector::VertexSourceInfo &info2 : fidsSecondNode )
429 {
430 if ( info1 == info2 )
431 {
432 shareCommonFeature = true;
433 break;
434 }
435 }
436 if ( shareCommonFeature )
437 break;
438 }
439
440 if ( shareCommonFeature )
441 {
442 // if there is a common feature joining these nodes, then don't consider them as invalid
443 continue;
444 }
445
446 const double distanceNodeToNode = pt.distance( data.point() );
447 if ( distanceNodeToNode < toleranceNodeToNode && distanceNodeToNode < closestError.distance )
448 {
449 closestError.distance = distanceNodeToNode;
450 closestError.id = data.id;
451 closestError.pt = data.point();
452 }
453 }
454
455 if ( !closestError.pt.isEmpty() )
456 {
457 const QPair< long long, long long > nodeId = qMakePair( std::min( closestError.id, i ), std::max( closestError.id, i ) );
458 if ( alreadyReportedNodes.contains( nodeId ) )
459 {
460 // already reported this -- eg when checking the other node in the pair
461 continue;
462 }
463 alreadyReportedNodes.insert( nodeId );
464
465 if ( nodeErrorSink )
466 {
467 QgsFeature nodeErrorFeature( nodeErrorFields );
468 nodeErrorFeature.setGeometry( std::make_unique< QgsLineString >( QVector<QgsPointXY>() << pt << closestError.pt ) );
469 nodeErrorFeature.setAttributes( QgsAttributes() << QObject::tr( "Node too close to adjacent node (%1 < %2)" ).arg( closestError.distance ).arg( toleranceNodeToNode ) );
470 if ( !nodeErrorSink->addFeature( nodeErrorFeature, QgsFeatureSink::FastInsert ) )
471 throw QgsProcessingException( writeFeatureError( nodeErrorSink.get(), parameters, u"OUTPUT_INVALID_NODES"_s ) );
472 }
473 countInvalidNodes++;
474 }
475 }
476
477 if ( evaluateNode && checkNodeToSegmentDistance )
478 {
479 // only keep the closest violation
480 NodeError closestError;
481
482 const QList<QgsFeatureId> edgeIds = edgeIndex.intersects( QgsRectangle::fromCenterAndSize( pt, toleranceNodeToSegment * 2, toleranceNodeToSegment * 2 ) );
483 for ( QgsFeatureId edgeIdx : edgeIds )
484 {
485 const QgsGraphEdge &edge = graph->edge( static_cast< int >( edgeIdx ) );
486 // skip edges connected to this node
487 if ( edge.fromVertex() == i || edge.toVertex() == i )
488 continue;
489
490 const QgsPointXY p1 = graph->vertex( edge.fromVertex() ).point();
491 const QgsPointXY p2 = graph->vertex( edge.toVertex() ).point();
492
493 QgsPointXY closestPt;
494 const double distanceToSegment = std::sqrt( pt.sqrDistToSegment( p1.x(), p1.y(), p2.x(), p2.y(), closestPt ) );
495 if ( distanceToSegment >= toleranceNodeToSegment )
496 continue;
497
498 // we don't consider this a node-to-segment error if the closest point is actually one of the segment endpoints.
499 // in that case it's a node-to-NODE error.
500 if ( closestPt.compare( p1 ) || closestPt.compare( p2 ) )
501 {
502 continue;
503 }
504
505 if ( distanceToSegment > closestError.distance )
506 {
507 continue;
508 }
509 closestError.distance = distanceToSegment;
510 closestError.pt = closestPt;
511 }
512
513 if ( !closestError.pt.isEmpty() )
514 {
515 if ( nodeErrorSink )
516 {
517 QgsFeature nodeErrorFeature( nodeErrorFields );
518 nodeErrorFeature.setGeometry( std::make_unique< QgsLineString >( QVector<QgsPointXY>() << pt << closestError.pt ) );
519 nodeErrorFeature.setAttributes( QgsAttributes() << QObject::tr( "Node too close to non-noded segment (%1 < %2)" ).arg( closestError.distance ).arg( toleranceNodeToSegment ) );
520 if ( !nodeErrorSink->addFeature( nodeErrorFeature, QgsFeatureSink::FastInsert ) )
521 throw QgsProcessingException( writeFeatureError( nodeErrorSink.get(), parameters, u"OUTPUT_INVALID_NODES"_s ) );
522 }
523 countInvalidNodes++;
524 }
525 }
526
527 multiFeedback.setProgress( static_cast< double >( i ) * topoStep );
528 }
529
530 if ( nodeErrorSink )
531 {
532 nodeErrorSink->finalize();
533 }
534
535 feedback->setProgress( 100 );
536 if ( countInvalidNodes > 0 )
537 {
538 multiFeedback.reportError( QObject::tr( "Found %1 invalid network nodes" ).arg( countInvalidNodes ) );
539 }
540
541 outputs.insert( u"COUNT_INVALID_NODES"_s, countInvalidNodes );
542
543 return outputs;
544}
545
@ VectorLine
Vector line layers.
Definition qgis.h:3621
@ LineString
LineString.
Definition qgis.h:294
@ Advanced
Parameter is an advanced parameter which should be hidden from users by default.
Definition qgis.h:3849
@ Optional
Parameter is optional.
Definition qgis.h:3851
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
static QString iconPath(const QString &iconFile)
Returns path to the desired icon file.
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
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
Q_INVOKABLE QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
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
Used for making the QgsGraph object.
Represents an edge in a graph.
Definition qgsgraph.h:44
int fromVertex() const
Returns the index of the vertex at the start of this edge.
Definition qgsgraph.cpp:180
int toVertex() const
Returns the index of the vertex at the end of this edge.
Definition qgsgraph.cpp:185
Represents vertex in a graph.
Definition qgsgraph.h:89
QgsGraphEdgeIds outgoingEdges() const
Returns outgoing edge ids, i.e.
Definition qgsgraph.cpp:200
QgsGraphEdgeIds incomingEdges() const
Returns the incoming edge ids, i.e.
Definition qgsgraph.cpp:195
QgsPointXY point() const
Returns point associated with graph vertex.
Definition qgsgraph.cpp:205
Represents a 2D point.
Definition qgspointxy.h:62
double distance(double x, double y) const
Returns the distance between this point and a specified x, y coordinate.
Definition qgspointxy.h:208
bool compare(const QgsPointXY &other, double epsilon=4 *std::numeric_limits< double >::epsilon()) const
Compares this point with another point with a fuzzy tolerance.
Definition qgspointxy.h:255
double y
Definition qgspointxy.h:66
double x
Definition qgspointxy.h:65
double sqrDistToSegment(double x1, double y1, double x2, double y2, QgsPointXY &minDistPoint, double epsilon=Qgis::DEFAULT_SEGMENT_EPSILON) const
Returns the minimum distance between this point and a segment.
Contains information about the context in which a processing algorithm is executed.
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.
Processing feedback object for multi-step operations.
A numeric 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).
A rectangle specified with double values.
static QgsRectangle fromCenterAndSize(const QgsPointXY &center, double width, double height)
Creates a new rectangle, given the specified center point and width and height.
A container for data stored inside a QgsSpatialIndexKDBush index.
A very fast static spatial index for 2D points based on a flat KD-tree.
void finalize()
Finalizes the index after manually adding features.
QList< QgsSpatialIndexKDBushData > intersects(const QgsRectangle &rectangle) const
Returns the list of features which fall within the specified rectangle.
bool addFeature(QgsFeatureId id, const QgsPointXY &point)
Adds a single feature to the index.
A spatial index for QgsFeature objects.
@ FlagStoreFeatureGeometries
Indicates that the spatial index should also store feature geometries. This requires more memory,...
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
Determines creating a graph from a vector line layer.
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
Represents information about a graph node's source vertex.