QGIS API Documentation 3.99.0-Master (2fe06baccd8)
Loading...
Searching...
No Matches
qgstracer.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgstracer.cpp
3 --------------------------------------
4 Date : January 2016
5 Copyright : (C) 2016 by Martin Dobias
6 Email : wonder dot sk at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16#include "qgstracer.h"
17
18#include <memory>
19#include <vector>
20
22#include "qgsfeatureiterator.h"
23#include "qgsgeometry.h"
24#include "qgsgeometryutils.h"
25#include "qgsgeos.h"
26#include "qgslogger.h"
27#include "qgsrendercontext.h"
28#include "qgsrenderer.h"
31#include "qgsvectorlayer.h"
32
33#include <queue>
34
35#include "moc_qgstracer.cpp"
36
37typedef std::pair<int, double> DijkstraQueueItem; // first = vertex index, second = distance
38
39// utility comparator for queue items based on distance
40struct comp
41{
43 {
44 return a.second > b.second;
45 }
46};
47
48
49// TODO: move to geometry utils
50double distance2D( const QgsPolylineXY &coords )
51{
52 int np = coords.count();
53 if ( np == 0 )
54 return 0;
55
56 double x0 = coords[0].x(), y0 = coords[0].y();
57 double x1, y1;
58 double dist = 0;
59 for ( int i = 1; i < np; ++i )
60 {
61 x1 = coords[i].x();
62 y1 = coords[i].y();
63 dist += QgsGeometryUtilsBase::distance2D( x1, y1, x0, y0 );
64 x0 = x1;
65 y0 = y1;
66 }
67 return dist;
68}
69
70
71// TODO: move to geometry utils
72double closestSegment( const QgsPolylineXY &pl, const QgsPointXY &pt, int &vertexAfter, double epsilon )
73{
74 double sqrDist = std::numeric_limits<double>::max();
75 const QgsPointXY *pldata = pl.constData();
76 int plcount = pl.count();
77 double prevX = pldata[0].x(), prevY = pldata[0].y();
78 double segmentPtX, segmentPtY;
79 for ( int i = 1; i < plcount; ++i )
80 {
81 double currentX = pldata[i].x();
82 double currentY = pldata[i].y();
83 double testDist = QgsGeometryUtilsBase::sqrDistToLine( pt.x(), pt.y(), prevX, prevY, currentX, currentY, segmentPtX, segmentPtY, epsilon );
84 if ( testDist < sqrDist )
85 {
86 sqrDist = testDist;
87 vertexAfter = i;
88 }
89 prevX = currentX;
90 prevY = currentY;
91 }
92 return sqrDist;
93}
94
96
99{
100 QgsTracerGraph() = default;
101
102 struct E // bidirectional edge
103 {
105 int v1, v2;
107 QVector<QgsPointXY> coords;
108
109 int otherVertex( int v0 ) const { return v1 == v0 ? v2 : v1; }
110 double weight() const { return distance2D( coords ); }
111 };
112
113 struct V
114 {
118 QVector<int> edges;
119 };
120
122 QVector<V> v;
124 QVector<E> e;
125
127 QSet<int> inactiveEdges;
130};
131
132
133QgsTracerGraph *makeGraph( const QVector<QgsPolylineXY> &edges )
134{
136 g->joinedVertices = 0;
137 QHash<QgsPointXY, int> point2vertex;
138
139 const auto constEdges = edges;
140 for ( const QgsPolylineXY &line : constEdges )
141 {
142 QgsPointXY p1( line[0] );
143 QgsPointXY p2( line[line.count() - 1] );
144
145 int v1 = -1, v2 = -1;
146 // get or add vertex 1
147 if ( point2vertex.contains( p1 ) )
148 v1 = point2vertex.value( p1 );
149 else
150 {
151 v1 = g->v.count();
153 v.pt = p1;
154 g->v.append( v );
155 point2vertex[p1] = v1;
156 }
157
158 // get or add vertex 2
159 if ( point2vertex.contains( p2 ) )
160 v2 = point2vertex.value( p2 );
161 else
162 {
163 v2 = g->v.count();
165 v.pt = p2;
166 g->v.append( v );
167 point2vertex[p2] = v2;
168 }
169
170 // add edge
172 e.v1 = v1;
173 e.v2 = v2;
174 e.coords = line;
175 g->e.append( e );
176
177 // link edge to vertices
178 int eIdx = g->e.count() - 1;
179 g->v[v1].edges << eIdx;
180 g->v[v2].edges << eIdx;
181 }
182
183 return g;
184}
185
186
187QVector<QgsPointXY> shortestPath( const QgsTracerGraph &g, int v1, int v2 )
188{
189 if ( v1 == -1 || v2 == -1 )
190 return QVector<QgsPointXY>(); // invalid input
191
192 // priority queue to drive Dijkstra:
193 // first of the pair is vertex index, second is distance
194 std::priority_queue< DijkstraQueueItem, std::vector< DijkstraQueueItem >, comp > Q;
195
196 // shortest distances to each vertex
197 QVector<double> D( g.v.count(), std::numeric_limits<double>::max() );
198 D[v1] = 0;
199
200 // whether vertices have been already processed
201 QVector<bool> F( g.v.count() );
202
203 // using which edge there is shortest path to each vertex
204 QVector<int> S( g.v.count(), -1 );
205
206 int u = -1;
207 Q.push( DijkstraQueueItem( v1, 0 ) );
208
209 while ( !Q.empty() )
210 {
211 u = Q.top().first; // new vertex to visit
212 Q.pop();
213
214 if ( u == v2 )
215 break; // we can stop now, there won't be a shorter path
216
217 if ( F[u] )
218 continue; // ignore previously added path which is actually longer
219
220 const QgsTracerGraph::V &vu = g.v[u];
221 const int *vuEdges = vu.edges.constData();
222 int count = vu.edges.count();
223 for ( int i = 0; i < count; ++i )
224 {
225 const QgsTracerGraph::E &edge = g.e[ vuEdges[i] ];
226 int v = edge.otherVertex( u );
227 double w = edge.weight();
228 if ( !F[v] && D[u] + w < D[v] )
229 {
230 // found a shorter way to the vertex
231 D[v] = D[u] + w;
232 S[v] = vuEdges[i];
233 Q.push( DijkstraQueueItem( v, D[v] ) );
234 }
235 }
236 F[u] = true; // mark the vertex as processed (we know the fastest path to it)
237 }
238
239 if ( u != v2 ) // there's no path to the end vertex
240 return QVector<QgsPointXY>();
241
242 //qDebug("dist %f", D[u]);
243
244 QVector<QgsPointXY> points;
245 QList<int> path;
246 while ( S[u] != -1 )
247 {
248 path << S[u];
249 const QgsTracerGraph::E &e = g.e[S[u]];
250 QVector<QgsPointXY> edgePoints = e.coords;
251 if ( edgePoints[0] != g.v[u].pt )
252 std::reverse( edgePoints.begin(), edgePoints.end() );
253 if ( !points.isEmpty() )
254 points.remove( points.count() - 1 ); // chop last one (will be used from next edge)
255 points << edgePoints;
256 u = e.otherVertex( u );
257 }
258
259 std::reverse( path.begin(), path.end() );
260 std::reverse( points.begin(), points.end() );
261 return points;
262}
263
264
265int point2vertex( const QgsTracerGraph &g, const QgsPointXY &pt, double epsilon = 1e-6 )
266{
267 // TODO: use spatial index
268
269 for ( int i = 0; i < g.v.count(); ++i )
270 {
271 const QgsTracerGraph::V &v = g.v.at( i );
272 if ( v.pt == pt || ( std::fabs( v.pt.x() - pt.x() ) < epsilon && std::fabs( v.pt.y() - pt.y() ) < epsilon ) )
273 return i;
274 }
275
276 return -1;
277}
278
279
280int point2edge( const QgsTracerGraph &g, const QgsPointXY &pt, int &lineVertexAfter, double epsilon = 1e-6 )
281{
282 for ( int i = 0; i < g.e.count(); ++i )
283 {
284 if ( g.inactiveEdges.contains( i ) )
285 continue; // ignore temporarily disabled edges
286
287 const QgsTracerGraph::E &e = g.e.at( i );
288 int vertexAfter = -1;
289 double dist = closestSegment( e.coords, pt, vertexAfter, epsilon );
290 if ( dist == 0 )
291 {
292 lineVertexAfter = vertexAfter;
293 return i;
294 }
295 }
296 return -1;
297}
298
299
300void splitLinestring( const QgsPolylineXY &points, const QgsPointXY &pt, int lineVertexAfter, QgsPolylineXY &pts1, QgsPolylineXY &pts2 )
301{
302 int count1 = lineVertexAfter;
303 int count2 = points.count() - lineVertexAfter;
304
305 for ( int i = 0; i < count1; ++i )
306 pts1 << points[i];
307 if ( points[lineVertexAfter - 1] != pt )
308 pts1 << pt; // repeat if not split exactly at that point
309
310 if ( pt != points[lineVertexAfter] )
311 pts2 << pt; // repeat if not split exactly at that point
312 for ( int i = 0; i < count2; ++i )
313 pts2 << points[i + lineVertexAfter];
314}
315
316
318{
319 // find edge where the point is
320 int lineVertexAfter;
321 int eIdx = point2edge( g, pt, lineVertexAfter );
322
323 //qDebug("e: %d", eIdx);
324
325 if ( eIdx == -1 )
326 return -1;
327
328 const QgsTracerGraph::E &e = g.e[eIdx];
329 QgsTracerGraph::V &v1 = g.v[e.v1];
330 QgsTracerGraph::V &v2 = g.v[e.v2];
331
332 QgsPolylineXY out1, out2;
333 splitLinestring( e.coords, pt, lineVertexAfter, out1, out2 );
334
335 int vIdx = g.v.count();
336 int e1Idx = g.e.count();
337 int e2Idx = e1Idx + 1;
338
339 // prepare new vertex and edges
340
342 v.pt = pt;
343 v.edges << e1Idx << e2Idx;
344
346 e1.v1 = e.v1;
347 e1.v2 = vIdx;
348 e1.coords = out1;
349
351 e2.v1 = vIdx;
352 e2.v2 = e.v2;
353 e2.coords = out2;
354
355 // update edge connectivity of existing vertices
356 v1.edges.replace( v1.edges.indexOf( eIdx ), e1Idx );
357 v2.edges.replace( v2.edges.indexOf( eIdx ), e2Idx );
358 g.inactiveEdges << eIdx;
359
360 // add new vertex and edges to the graph
361 g.v.append( v );
362 g.e.append( e1 );
363 g.e.append( e2 );
364 g.joinedVertices++;
365
366 return vIdx;
367}
368
369
371{
372 // try to use existing vertex in the graph
373 int v = point2vertex( g, pt );
374 if ( v != -1 )
375 return v;
376
377 // try to add the vertex to an edge (may fail if point is not on edge)
378 return joinVertexToGraph( g, pt );
379}
380
381
383{
384 // remove extra vertices and edges
385 g.v.resize( g.v.count() - g.joinedVertices );
386 g.e.resize( g.e.count() - g.joinedVertices * 2 );
387 g.joinedVertices = 0;
388
389 // fix vertices of deactivated edges
390 for ( int eIdx : std::as_const( g.inactiveEdges ) )
391 {
392 if ( eIdx >= g.e.count() )
393 continue;
394 const QgsTracerGraph::E &e = g.e[eIdx];
395 QgsTracerGraph::V &v1 = g.v[e.v1];
396 for ( int i = 0; i < v1.edges.count(); ++i )
397 {
398 if ( v1.edges[i] >= g.e.count() )
399 v1.edges.remove( i-- );
400 }
401 v1.edges << eIdx;
402
403 QgsTracerGraph::V &v2 = g.v[e.v2];
404 for ( int i = 0; i < v2.edges.count(); ++i )
405 {
406 if ( v2.edges[i] >= g.e.count() )
407 v2.edges.remove( i-- );
408 }
409 v2.edges << eIdx;
410 }
411
412 g.inactiveEdges.clear();
413}
414
415
417{
418 QgsGeometry geom = g;
419 // segmentize curved geometries - we will use noding algorithm from GEOS
420 // to find all intersections a bit later (so we need them segmentized anyway)
422 {
423 QgsAbstractGeometry *segmentizedGeomV2 = g.constGet()->segmentize();
424 if ( !segmentizedGeomV2 )
425 return;
426
427 geom = QgsGeometry( segmentizedGeomV2 );
428 }
429
430 switch ( QgsWkbTypes::flatType( geom.wkbType() ) )
431 {
433 mpl << geom.asPolyline();
434 break;
435
437 {
438 const auto polygon = geom.asPolygon();
439 for ( const QgsPolylineXY &ring : polygon )
440 mpl << ring;
441 }
442 break;
443
445 {
446 const auto multiPolyline = geom.asMultiPolyline();
447 for ( const QgsPolylineXY &linestring : multiPolyline )
448 mpl << linestring;
449 }
450 break;
451
453 {
454 const auto multiPolygon = geom.asMultiPolygon();
455 for ( const QgsPolygonXY &polygon : multiPolygon )
456 {
457 for ( const QgsPolylineXY &ring : polygon )
458 mpl << ring;
459 }
460 }
461 break;
462
463 default:
464 break; // unknown type - do nothing
465 }
466}
467
468// -------------
469
470
471QgsTracer::QgsTracer() = default;
472
473bool QgsTracer::initGraph()
474{
475 if ( mGraph )
476 return true; // already initialized
477
478 mHasTopologyProblem = false;
479
480 QgsFeature f;
482
483 // extract linestrings
484
485 // TODO: use QgsPointLocator as a source for the linework
486
487 QElapsedTimer t1, t2, t2a, t3;
488
489 t1.start();
490 int featuresCounted = 0;
491 for ( const QgsVectorLayer *vl : std::as_const( mLayers ) )
492 {
493 QgsFeatureRequest request;
494 bool filter = false;
495 std::unique_ptr< QgsFeatureRenderer > renderer;
496 std::unique_ptr<QgsRenderContext> ctx;
497
499 if ( !enableInvisibleFeature && mRenderContext && vl->renderer() )
500 {
501 renderer.reset( vl->renderer()->clone() );
502 ctx = std::make_unique<QgsRenderContext>( *mRenderContext.get() );
503 ctx->expressionContext() << QgsExpressionContextUtils::layerScope( vl );
504
505 // setup scale for scale dependent visibility (rule based)
506 renderer->startRender( *ctx.get(), vl->fields() );
507 filter = renderer->capabilities() & QgsFeatureRenderer::Filter;
508 request.setSubsetOfAttributes( renderer->usedAttributes( *ctx.get() ), vl->fields() );
509 }
510 else
511 {
512 request.setNoAttributes();
513 }
514
515 request.setDestinationCrs( mCRS, mTransformContext );
516 if ( !mExtent.isEmpty() )
517 request.setFilterRect( mExtent );
518
519 QgsFeatureIterator fi = vl->getFeatures( request );
520 while ( fi.nextFeature( f ) )
521 {
522 if ( !f.hasGeometry() )
523 continue;
524
525 if ( filter )
526 {
527 ctx->expressionContext().setFeature( f );
528 if ( !renderer->willRenderFeature( f, *ctx.get() ) )
529 {
530 continue;
531 }
532 }
533
534 extractLinework( f.geometry(), mpl );
535
536 ++featuresCounted;
537 if ( mMaxFeatureCount != 0 && featuresCounted >= mMaxFeatureCount )
538 return false;
539 }
540
541 if ( renderer )
542 {
543 renderer->stopRender( *ctx.get() );
544 }
545 }
546 int timeExtract = t1.elapsed();
547
548 // resolve intersections
549
550 t2.start();
551
552 int timeNodingCall = 0;
553
554#if 0
555 // without noding - if data are known to be noded beforehand
556#else
557 QgsGeometry allGeom = QgsGeometry::fromMultiPolylineXY( mpl );
558
559 try
560 {
561 t2a.start();
562 // GEOSNode_r may throw an exception
563 geos::unique_ptr allGeomGeos( QgsGeos::asGeos( allGeom ) );
564 geos::unique_ptr allNoded( GEOSNode_r( QgsGeosContext::get(), allGeomGeos.get() ) );
565
566 if ( mAddPointsOnIntersections )
567 {
568 mIntersections = QgsGeometry();
569 }
570 else
571 {
572 geos::unique_ptr allPoints( GEOSGeom_extractUniquePoints_r( QgsGeosContext::get(), allGeomGeos.get() ) );
573 geos::unique_ptr nodedPoints( GEOSGeom_extractUniquePoints_r( QgsGeosContext::get(), allNoded.get() ) );
574 geos::unique_ptr intersectionNodes( GEOSDifference_r( QgsGeosContext::get(), nodedPoints.get(), allPoints.get() ) );
575 mIntersections = QgsGeos::geometryFromGeos( intersectionNodes.release() );
576 }
577
578 timeNodingCall = t2a.elapsed();
579
580 QgsGeometry noded = QgsGeos::geometryFromGeos( allNoded.release() );
581
582 mpl = noded.asMultiPolyline();
583 }
584 catch ( QgsGeosException &e )
585 {
586 // no big deal... we will just not have nicely noded linework, potentially
587 // missing some intersections
588
589 mHasTopologyProblem = true;
590
591 QgsDebugError( QStringLiteral( "Tracer Noding Exception: %1" ).arg( e.what() ) );
592 }
593#endif
594
595 int timeNoding = t2.elapsed();
596
597 t3.start();
598
599 mGraph.reset( makeGraph( mpl ) );
600
601 int timeMake = t3.elapsed();
602
603 Q_UNUSED( timeExtract )
604 Q_UNUSED( timeNoding )
605 Q_UNUSED( timeNodingCall )
606 Q_UNUSED( timeMake )
607 QgsDebugMsgLevel( QStringLiteral( "tracer extract %1 ms, noding %2 ms (call %3 ms), make %4 ms" )
608 .arg( timeExtract ).arg( timeNoding ).arg( timeNodingCall ).arg( timeMake ), 2 );
609
610 return true;
611}
612
617
618void QgsTracer::setLayers( const QList<QgsVectorLayer *> &layers )
619{
620 if ( mLayers == layers )
621 return;
622
623 for ( QgsVectorLayer *layer : std::as_const( mLayers ) )
624 {
625 disconnect( layer, &QgsVectorLayer::featureAdded, this, &QgsTracer::onFeatureAdded );
626 disconnect( layer, &QgsVectorLayer::featureDeleted, this, &QgsTracer::onFeatureDeleted );
627 disconnect( layer, &QgsVectorLayer::geometryChanged, this, &QgsTracer::onGeometryChanged );
628 disconnect( layer, &QgsVectorLayer::attributeValueChanged, this, &QgsTracer::onAttributeValueChanged );
629 disconnect( layer, &QgsVectorLayer::dataChanged, this, &QgsTracer::onDataChanged );
630 disconnect( layer, &QgsVectorLayer::styleChanged, this, &QgsTracer::onStyleChanged );
631 disconnect( layer, &QObject::destroyed, this, &QgsTracer::onLayerDestroyed );
632 }
633
634 mLayers = layers;
635
636 for ( QgsVectorLayer *layer : layers )
637 {
638 connect( layer, &QgsVectorLayer::featureAdded, this, &QgsTracer::onFeatureAdded );
639 connect( layer, &QgsVectorLayer::featureDeleted, this, &QgsTracer::onFeatureDeleted );
640 connect( layer, &QgsVectorLayer::geometryChanged, this, &QgsTracer::onGeometryChanged );
641 connect( layer, &QgsVectorLayer::attributeValueChanged, this, &QgsTracer::onAttributeValueChanged );
642 connect( layer, &QgsVectorLayer::dataChanged, this, &QgsTracer::onDataChanged );
643 connect( layer, &QgsVectorLayer::styleChanged, this, &QgsTracer::onStyleChanged );
644 connect( layer, &QObject::destroyed, this, &QgsTracer::onLayerDestroyed );
645 }
646
648}
649
651{
652 mCRS = crs;
653 mTransformContext = context;
655}
656
658{
659 mRenderContext = std::make_unique<QgsRenderContext>( *renderContext );
661}
662
664{
665 if ( mExtent == extent )
666 return;
667
668 mExtent = extent;
670}
671
673{
674 mOffset = offset;
675}
676
677void QgsTracer::offsetParameters( int &quadSegments, int &joinStyle, double &miterLimit )
678{
679 quadSegments = mOffsetSegments;
680 joinStyle = static_cast< int >( mOffsetJoinStyle );
681 miterLimit = mOffsetMiterLimit;
682}
683
684void QgsTracer::setOffsetParameters( int quadSegments, int joinStyle, double miterLimit )
685{
686 mOffsetSegments = quadSegments;
687 mOffsetJoinStyle = static_cast< Qgis::JoinStyle >( joinStyle );
688 mOffsetMiterLimit = miterLimit;
689}
690
692{
693 if ( mGraph )
694 return true;
695
696 // configuration from derived class?
697 configure();
698
699 return initGraph();
700}
701
702
704{
705 mGraph.reset( nullptr );
706}
707
708void QgsTracer::onFeatureAdded( QgsFeatureId fid )
709{
710 Q_UNUSED( fid )
712}
713
714void QgsTracer::onFeatureDeleted( QgsFeatureId fid )
715{
716 Q_UNUSED( fid )
718}
719
720void QgsTracer::onGeometryChanged( QgsFeatureId fid, const QgsGeometry &geom )
721{
722 Q_UNUSED( fid )
723 Q_UNUSED( geom )
725}
726
727void QgsTracer::onAttributeValueChanged( QgsFeatureId fid, int idx, const QVariant &value )
728{
729 Q_UNUSED( fid )
730 Q_UNUSED( idx )
731 Q_UNUSED( value )
733}
734
735void QgsTracer::onDataChanged( )
736{
738}
739
740void QgsTracer::onStyleChanged( )
741{
743}
744
745void QgsTracer::onLayerDestroyed( QObject *obj )
746{
747 // remove the layer before it is completely invalid (static_cast should be the safest cast)
748 mLayers.removeAll( static_cast<QgsVectorLayer *>( obj ) );
750}
751
752QVector<QgsPointXY> QgsTracer::findShortestPath( const QgsPointXY &p1, const QgsPointXY &p2, PathError *error )
753{
754 init(); // does nothing if the graph exists already
755 if ( !mGraph )
756 {
757 if ( error ) *error = ErrTooManyFeatures;
758 return QVector<QgsPointXY>();
759 }
760
761 QElapsedTimer t;
762 t.start();
763 int v1 = pointInGraph( *mGraph, p1 );
764 int v2 = pointInGraph( *mGraph, p2 );
765 int tPrep = t.elapsed();
766
767 if ( v1 == -1 )
768 {
769 if ( error ) *error = ErrPoint1;
770 return QVector<QgsPointXY>();
771 }
772 if ( v2 == -1 )
773 {
774 if ( error ) *error = ErrPoint2;
775 return QVector<QgsPointXY>();
776 }
777
778 QElapsedTimer t2;
779 t2.start();
780 QgsPolylineXY points = shortestPath( *mGraph, v1, v2 );
781 int tPath = t2.elapsed();
782
783 Q_UNUSED( tPrep )
784 Q_UNUSED( tPath )
785 QgsDebugMsgLevel( QStringLiteral( "path timing: prep %1 ms, path %2 ms" ).arg( tPrep ).arg( tPath ), 2 );
786
787 if ( points.size() > 2 && !mIntersections.isEmpty() )
788 {
789 QVector<QgsPointXY> noInts;
790 noInts.reserve( points.size() );
791 noInts.append( points.first() );
792 for ( auto it = std::next( points.begin() ), end = std::prev( points.end() ); it != end; ++it )
793 {
794 if ( mIntersections.contains( it->x(), it->y() ) )
795 {
796 // we skip points that are on a straight segment and were not on the original geometries
797 QgsPointXY nearest;
798 if ( 0 == it->sqrDistToSegment( std::prev( it )->x(),
799 std::prev( it )->y(),
800 std::next( it )->x(),
801 std::next( it )->y(),
802 nearest, 1E-12 ) )
803 {
804 continue;
805 }
806 }
807 noInts << *it;
808 }
809 noInts.append( points.last() );
810 points = noInts;
811 QgsDebugMsgLevel( QStringLiteral( "intersection point removal timing: %1 ms" ).arg( t2.elapsed() - tPath ), 2 );
812 }
813
814 resetGraph( *mGraph );
815
816 if ( !points.isEmpty() && mOffset != 0 )
817 {
818 QVector<QgsPointXY> pointsInput( points );
819 QgsLineString linestring( pointsInput );
820 std::unique_ptr<QgsGeometryEngine> linestringEngine( QgsGeometry::createGeometryEngine( &linestring ) );
821 std::unique_ptr<QgsAbstractGeometry> linestringOffset( linestringEngine->offsetCurve( mOffset, mOffsetSegments, mOffsetJoinStyle, mOffsetMiterLimit ) );
822 if ( QgsLineString *ls2 = qgsgeometry_cast<QgsLineString *>( linestringOffset.get() ) )
823 {
824 points.clear();
825 for ( int i = 0; i < ls2->numPoints(); ++i )
826 points << QgsPointXY( ls2->pointN( i ) );
827
828 // sometimes (with negative offset?) the resulting curve is reversed
829 if ( points.count() >= 2 )
830 {
831 QgsPointXY res1 = points.first(), res2 = points.last();
832 double diffNormal = res1.distance( p1 ) + res2.distance( p2 );
833 double diffReversed = res1.distance( p2 ) + res2.distance( p1 );
834 if ( diffReversed < diffNormal )
835 std::reverse( points.begin(), points.end() );
836 }
837 }
838 }
839
840 if ( error )
841 *error = points.isEmpty() ? ErrNoPath : ErrNone;
842
843 return points;
844}
845
847{
848 init(); // does nothing if the graph exists already
849 if ( !mGraph )
850 return false;
851
852 if ( point2vertex( *mGraph, pt ) != -1 )
853 return true;
854
855 int lineVertexAfter;
856 int e = point2edge( *mGraph, pt, lineVertexAfter );
857 return e != -1;
858}
859
861{
862 if ( enable == mAddPointsOnIntersections )
863 return;
864
865 mAddPointsOnIntersections = enable;
867}
JoinStyle
Join styles for buffers.
Definition qgis.h:2121
@ LineString
LineString.
Definition qgis.h:280
@ Polygon
Polygon.
Definition qgis.h:281
@ MultiPolygon
MultiPolygon.
Definition qgis.h:285
@ MultiLineString
MultiLineString.
Definition qgis.h:284
Abstract base class for all geometries.
virtual QgsAbstractGeometry * segmentize(double tolerance=M_PI/180., SegmentationToleranceType toleranceType=MaximumAngle) const
Returns a version of the geometry without curves.
Represents a coordinate reference system (CRS).
Contains information about the context in which a coordinate transform is executed.
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
@ Filter
Features may be filtered, i.e. some features may not be rendered (categorized, rule based ....
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
QgsFeatureRequest & setDestinationCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets the destination crs for feature's geometries.
QgsFeatureRequest & setNoAttributes()
Set that no attributes will be fetched.
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
QgsGeometry geometry
Definition qgsfeature.h:69
bool hasGeometry() const
Returns true if the feature has an associated geometry.
static double distance2D(double x1, double y1, double x2, double y2)
Returns the 2D distance between (x1, y1) and (x2, y2).
static double sqrDistToLine(double ptX, double ptY, double x1, double y1, double x2, double y2, double &minDistX, double &minDistY, double epsilon)
Returns the squared distance between a point and a line.
A geometry is the spatial representation of a feature.
QgsMultiPolygonXY asMultiPolygon() const
Returns the contents of the geometry as a multi-polygon.
static QgsGeometry fromMultiPolylineXY(const QgsMultiPolylineXY &multiline)
Creates a new geometry from a QgsMultiPolylineXY object.
QgsPolygonXY asPolygon() const
Returns the contents of the geometry as a polygon.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
QgsPolylineXY asPolyline() const
Returns the contents of the geometry as a polyline.
QgsMultiPolylineXY asMultiPolyline() const
Returns the contents of the geometry as a multi-linestring.
Qgis::WkbType wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.).
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry, double precision=0.0, Qgis::GeosCreationFlags flags=Qgis::GeosCreationFlag::SkipEmptyInteriorRings)
Creates and returns a new geometry engine representing the specified geometry using precision on a gr...
static GEOSContextHandle_t get()
Returns a thread local instance of a GEOS context, safe for use in the current thread.
static geos::unique_ptr asGeos(const QgsGeometry &geometry, double precision=0, Qgis::GeosCreationFlags flags=Qgis::GeosCreationFlags())
Returns a geos geometry - caller takes ownership of the object (should be deleted with GEOSGeom_destr...
Definition qgsgeos.cpp:260
static QgsGeometry geometryFromGeos(GEOSGeometry *geos)
Creates a new QgsGeometry object, feeding in a geometry in GEOS format.
Definition qgsgeos.cpp:188
Line string geometry type, with support for z-dimension and m-values.
void styleChanged()
Signal emitted whenever a change affects the layer's style.
void dataChanged()
Data of layer changed.
Represents a 2D point.
Definition qgspointxy.h:60
double distance(double x, double y) const
Returns the distance between this point and a specified x, y coordinate.
Definition qgspointxy.h:206
double y
Definition qgspointxy.h:64
double x
Definition qgspointxy.h:63
A rectangle specified with double values.
Contains information about the context of a rendering operation.
T value(const QString &dynamicKeyPart=QString()) const
Returns settings value.
static const QgsSettingsEntryBool * settingsDigitizingSnapInvisibleFeature
Settings entry digitizing snap invisible feature.
void setRenderContext(const QgsRenderContext *renderContext)
Sets the renderContext used for tracing only on visible features.
void setExtent(const QgsRectangle &extent)
Sets extent to which graph's features will be limited (empty extent means no limit).
bool isPointSnapped(const QgsPointXY &pt)
Find out whether the point is snapped to a vertex or edge (i.e. it can be used for tracing start/stop...
QVector< QgsPointXY > findShortestPath(const QgsPointXY &p1, const QgsPointXY &p2, PathError *error=nullptr)
Given two points, find the shortest path and return points on the way.
PathError
Possible errors that may happen when calling findShortestPath().
Definition qgstracer.h:133
@ ErrNoPath
Points are not connected in the graph.
Definition qgstracer.h:138
@ ErrPoint2
End point cannot be joined to the graph.
Definition qgstracer.h:137
@ ErrPoint1
Start point cannot be joined to the graph.
Definition qgstracer.h:136
@ ErrNone
No error.
Definition qgstracer.h:134
@ ErrTooManyFeatures
Max feature count threshold was reached while reading features.
Definition qgstracer.h:135
void setOffset(double offset)
Set offset in map units that should be applied to the traced paths returned from findShortestPath().
QgsTracer()
Constructor for QgsTracer.
QgsRectangle extent() const
Gets extent to which graph's features will be limited (empty extent means no limit).
Definition qgstracer.h:79
~QgsTracer() override
void setLayers(const QList< QgsVectorLayer * > &layers)
Sets layers used for tracing.
double offset() const
Gets offset in map units that should be applied to the traced paths returned from findShortestPath().
Definition qgstracer.h:87
void offsetParameters(int &quadSegments, int &joinStyle, double &miterLimit)
Gets extra parameters for offset curve algorithm (used when offset is non-zero).
bool init()
Build the internal data structures.
void setOffsetParameters(int quadSegments, int joinStyle, double miterLimit)
Set extra parameters for offset curve algorithm (used when offset is non-zero).
void invalidateGraph()
Destroy the existing graph structure if any (de-initialize).
QList< QgsVectorLayer * > layers() const
Gets layers used for tracing.
Definition qgstracer.h:56
void setAddPointsOnIntersectionsEnabled(bool enable)
When enable is true, the shortest path's straight segments will include vertices where the input laye...
virtual void configure()
Allows derived classes to setup the settings just before the tracer is initialized.
Definition qgstracer.h:171
void setDestinationCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets the crs and transform context used for tracing.
Represents a vector layer which manages a vector based dataset.
void attributeValueChanged(QgsFeatureId fid, int idx, const QVariant &value)
Emitted whenever an attribute value change is done in the edit buffer.
void featureAdded(QgsFeatureId fid)
Emitted when a new feature has been added to the layer.
void featureDeleted(QgsFeatureId fid)
Emitted when a feature has been deleted.
void geometryChanged(QgsFeatureId fid, const QgsGeometry &geometry)
Emitted whenever a geometry change is done in the edit buffer.
static Q_INVOKABLE bool isCurvedType(Qgis::WkbType type)
Returns true if the WKB type is a curved type or can contain curved geometries.
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
std::unique_ptr< GEOSGeometry, GeosDeleter > unique_ptr
Scoped GEOS pointer.
Definition qgsgeos.h:114
T qgsgeometry_cast(QgsAbstractGeometry *geom)
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
QVector< QgsPolylineXY > QgsPolygonXY
Polygon: first item of the list is outer ring, inner rings (if any) start from second item.
Definition qgsgeometry.h:90
QVector< QgsPolylineXY > QgsMultiPolylineXY
A collection of QgsPolylines that share a common collection of attributes.
QVector< QgsPointXY > QgsPolylineXY
Polyline as represented as a vector of two-dimensional points.
Definition qgsgeometry.h:61
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:61
#define QgsDebugError(str)
Definition qgslogger.h:57
std::pair< int, double > DijkstraQueueItem
Definition qgstracer.cpp:37
void splitLinestring(const QgsPolylineXY &points, const QgsPointXY &pt, int lineVertexAfter, QgsPolylineXY &pts1, QgsPolylineXY &pts2)
int pointInGraph(QgsTracerGraph &g, const QgsPointXY &pt)
void extractLinework(const QgsGeometry &g, QgsMultiPolylineXY &mpl)
int point2vertex(const QgsTracerGraph &g, const QgsPointXY &pt, double epsilon=1e-6)
int point2edge(const QgsTracerGraph &g, const QgsPointXY &pt, int &lineVertexAfter, double epsilon=1e-6)
void resetGraph(QgsTracerGraph &g)
double closestSegment(const QgsPolylineXY &pl, const QgsPointXY &pt, int &vertexAfter, double epsilon)
Definition qgstracer.cpp:72
double distance2D(const QgsPolylineXY &coords)
Definition qgstracer.cpp:50
int joinVertexToGraph(QgsTracerGraph &g, const QgsPointXY &pt)
QgsTracerGraph * makeGraph(const QVector< QgsPolylineXY > &edges)
QVector< QgsPointXY > shortestPath(const QgsTracerGraph &g, int v1, int v2)
int v1
vertices that the edge connects
int otherVertex(int v0) const
QVector< QgsPointXY > coords
coordinates of the edge (including endpoints)
double weight() const
QVector< int > edges
indices of adjacent edges (used in Dijkstra algorithm)
QgsPointXY pt
location of the vertex
Simple graph structure for shortest path search.
Definition qgstracer.cpp:99
QSet< int > inactiveEdges
Temporarily removed edges.
int joinedVertices
Temporarily added vertices (for each there are two extra edges).
QgsTracerGraph()=default
QVector< E > e
Edges of the graph.
QVector< V > v
Vertices of the graph.
bool operator()(DijkstraQueueItem a, DijkstraQueueItem b) const
Definition qgstracer.cpp:42