QGIS API Documentation 3.99.0-Master (8e76e220402)
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 <QString>
34#include <queue>
35
36#include "moc_qgstracer.cpp"
37
38using namespace Qt::StringLiterals;
39
40typedef std::pair<int, double> DijkstraQueueItem; // first = vertex index, second = distance
41
42// utility comparator for queue items based on distance
43struct comp
44{
46 {
47 return a.second > b.second;
48 }
49};
50
51
52// TODO: move to geometry utils
53double distance2D( const QgsPolylineXY &coords )
54{
55 int np = coords.count();
56 if ( np == 0 )
57 return 0;
58
59 double x0 = coords[0].x(), y0 = coords[0].y();
60 double x1, y1;
61 double dist = 0;
62 for ( int i = 1; i < np; ++i )
63 {
64 x1 = coords[i].x();
65 y1 = coords[i].y();
66 dist += QgsGeometryUtilsBase::distance2D( x1, y1, x0, y0 );
67 x0 = x1;
68 y0 = y1;
69 }
70 return dist;
71}
72
73
74// TODO: move to geometry utils
75double closestSegment( const QgsPolylineXY &pl, const QgsPointXY &pt, int &vertexAfter, double epsilon )
76{
77 double sqrDist = std::numeric_limits<double>::max();
78 const QgsPointXY *pldata = pl.constData();
79 int plcount = pl.count();
80 double prevX = pldata[0].x(), prevY = pldata[0].y();
81 double segmentPtX, segmentPtY;
82 for ( int i = 1; i < plcount; ++i )
83 {
84 double currentX = pldata[i].x();
85 double currentY = pldata[i].y();
86 double testDist = QgsGeometryUtilsBase::sqrDistToLine( pt.x(), pt.y(), prevX, prevY, currentX, currentY, segmentPtX, segmentPtY, epsilon );
87 if ( testDist < sqrDist )
88 {
89 sqrDist = testDist;
90 vertexAfter = i;
91 }
92 prevX = currentX;
93 prevY = currentY;
94 }
95 return sqrDist;
96}
97
99
102{
103 QgsTracerGraph() = default;
104
105 struct E // bidirectional edge
106 {
108 int v1, v2;
110 QVector<QgsPointXY> coords;
111
112 int otherVertex( int v0 ) const { return v1 == v0 ? v2 : v1; }
113 double weight() const { return distance2D( coords ); }
114 };
115
116 struct V
117 {
121 QVector<int> edges;
122 };
123
125 QVector<V> v;
127 QVector<E> e;
128
130 QSet<int> inactiveEdges;
133};
134
135
136QgsTracerGraph *makeGraph( const QVector<QgsPolylineXY> &edges )
137{
139 g->joinedVertices = 0;
140 QHash<QgsPointXY, int> point2vertex;
141
142 const auto constEdges = edges;
143 for ( const QgsPolylineXY &line : constEdges )
144 {
145 QgsPointXY p1( line[0] );
146 QgsPointXY p2( line[line.count() - 1] );
147
148 int v1 = -1, v2 = -1;
149 // get or add vertex 1
150 if ( point2vertex.contains( p1 ) )
151 v1 = point2vertex.value( p1 );
152 else
153 {
154 v1 = g->v.count();
156 v.pt = p1;
157 g->v.append( v );
158 point2vertex[p1] = v1;
159 }
160
161 // get or add vertex 2
162 if ( point2vertex.contains( p2 ) )
163 v2 = point2vertex.value( p2 );
164 else
165 {
166 v2 = g->v.count();
168 v.pt = p2;
169 g->v.append( v );
170 point2vertex[p2] = v2;
171 }
172
173 // add edge
175 e.v1 = v1;
176 e.v2 = v2;
177 e.coords = line;
178 g->e.append( e );
179
180 // link edge to vertices
181 int eIdx = g->e.count() - 1;
182 g->v[v1].edges << eIdx;
183 g->v[v2].edges << eIdx;
184 }
185
186 return g;
187}
188
189
190QVector<QgsPointXY> shortestPath( const QgsTracerGraph &g, int v1, int v2 )
191{
192 if ( v1 == -1 || v2 == -1 )
193 return QVector<QgsPointXY>(); // invalid input
194
195 // priority queue to drive Dijkstra:
196 // first of the pair is vertex index, second is distance
197 std::priority_queue< DijkstraQueueItem, std::vector< DijkstraQueueItem >, comp > Q;
198
199 // shortest distances to each vertex
200 QVector<double> D( g.v.count(), std::numeric_limits<double>::max() );
201 D[v1] = 0;
202
203 // whether vertices have been already processed
204 QVector<bool> F( g.v.count() );
205
206 // using which edge there is shortest path to each vertex
207 QVector<int> S( g.v.count(), -1 );
208
209 int u = -1;
210 Q.push( DijkstraQueueItem( v1, 0 ) );
211
212 while ( !Q.empty() )
213 {
214 u = Q.top().first; // new vertex to visit
215 Q.pop();
216
217 if ( u == v2 )
218 break; // we can stop now, there won't be a shorter path
219
220 if ( F[u] )
221 continue; // ignore previously added path which is actually longer
222
223 const QgsTracerGraph::V &vu = g.v[u];
224 const int *vuEdges = vu.edges.constData();
225 int count = vu.edges.count();
226 for ( int i = 0; i < count; ++i )
227 {
228 const QgsTracerGraph::E &edge = g.e[ vuEdges[i] ];
229 int v = edge.otherVertex( u );
230 double w = edge.weight();
231 if ( !F[v] && D[u] + w < D[v] )
232 {
233 // found a shorter way to the vertex
234 D[v] = D[u] + w;
235 S[v] = vuEdges[i];
236 Q.push( DijkstraQueueItem( v, D[v] ) );
237 }
238 }
239 F[u] = true; // mark the vertex as processed (we know the fastest path to it)
240 }
241
242 if ( u != v2 ) // there's no path to the end vertex
243 return QVector<QgsPointXY>();
244
245 //qDebug("dist %f", D[u]);
246
247 QVector<QgsPointXY> points;
248 QList<int> path;
249 while ( S[u] != -1 )
250 {
251 path << S[u];
252 const QgsTracerGraph::E &e = g.e[S[u]];
253 QVector<QgsPointXY> edgePoints = e.coords;
254 if ( edgePoints[0] != g.v[u].pt )
255 std::reverse( edgePoints.begin(), edgePoints.end() );
256 if ( !points.isEmpty() )
257 points.remove( points.count() - 1 ); // chop last one (will be used from next edge)
258 points << edgePoints;
259 u = e.otherVertex( u );
260 }
261
262 std::reverse( path.begin(), path.end() );
263 std::reverse( points.begin(), points.end() );
264 return points;
265}
266
267
268int point2vertex( const QgsTracerGraph &g, const QgsPointXY &pt, double epsilon = 1e-6 )
269{
270 // TODO: use spatial index
271
272 for ( int i = 0; i < g.v.count(); ++i )
273 {
274 const QgsTracerGraph::V &v = g.v.at( i );
275 if ( v.pt == pt || ( std::fabs( v.pt.x() - pt.x() ) < epsilon && std::fabs( v.pt.y() - pt.y() ) < epsilon ) )
276 return i;
277 }
278
279 return -1;
280}
281
282
283int point2edge( const QgsTracerGraph &g, const QgsPointXY &pt, int &lineVertexAfter, double epsilon = 1e-6 )
284{
285 for ( int i = 0; i < g.e.count(); ++i )
286 {
287 if ( g.inactiveEdges.contains( i ) )
288 continue; // ignore temporarily disabled edges
289
290 const QgsTracerGraph::E &e = g.e.at( i );
291 int vertexAfter = -1;
292 double dist = closestSegment( e.coords, pt, vertexAfter, epsilon );
293 if ( dist == 0 )
294 {
295 lineVertexAfter = vertexAfter;
296 return i;
297 }
298 }
299 return -1;
300}
301
302
303void splitLinestring( const QgsPolylineXY &points, const QgsPointXY &pt, int lineVertexAfter, QgsPolylineXY &pts1, QgsPolylineXY &pts2 )
304{
305 int count1 = lineVertexAfter;
306 int count2 = points.count() - lineVertexAfter;
307
308 for ( int i = 0; i < count1; ++i )
309 pts1 << points[i];
310 if ( points[lineVertexAfter - 1] != pt )
311 pts1 << pt; // repeat if not split exactly at that point
312
313 if ( pt != points[lineVertexAfter] )
314 pts2 << pt; // repeat if not split exactly at that point
315 for ( int i = 0; i < count2; ++i )
316 pts2 << points[i + lineVertexAfter];
317}
318
319
321{
322 // find edge where the point is
323 int lineVertexAfter;
324 int eIdx = point2edge( g, pt, lineVertexAfter );
325
326 //qDebug("e: %d", eIdx);
327
328 if ( eIdx == -1 )
329 return -1;
330
331 const QgsTracerGraph::E &e = g.e[eIdx];
332 QgsTracerGraph::V &v1 = g.v[e.v1];
333 QgsTracerGraph::V &v2 = g.v[e.v2];
334
335 QgsPolylineXY out1, out2;
336 splitLinestring( e.coords, pt, lineVertexAfter, out1, out2 );
337
338 int vIdx = g.v.count();
339 int e1Idx = g.e.count();
340 int e2Idx = e1Idx + 1;
341
342 // prepare new vertex and edges
343
345 v.pt = pt;
346 v.edges << e1Idx << e2Idx;
347
349 e1.v1 = e.v1;
350 e1.v2 = vIdx;
351 e1.coords = out1;
352
354 e2.v1 = vIdx;
355 e2.v2 = e.v2;
356 e2.coords = out2;
357
358 // update edge connectivity of existing vertices
359 v1.edges.replace( v1.edges.indexOf( eIdx ), e1Idx );
360 v2.edges.replace( v2.edges.indexOf( eIdx ), e2Idx );
361 g.inactiveEdges << eIdx;
362
363 // add new vertex and edges to the graph
364 g.v.append( v );
365 g.e.append( e1 );
366 g.e.append( e2 );
367 g.joinedVertices++;
368
369 return vIdx;
370}
371
372
374{
375 // try to use existing vertex in the graph
376 int v = point2vertex( g, pt );
377 if ( v != -1 )
378 return v;
379
380 // try to add the vertex to an edge (may fail if point is not on edge)
381 return joinVertexToGraph( g, pt );
382}
383
384
386{
387 // remove extra vertices and edges
388 g.v.resize( g.v.count() - g.joinedVertices );
389 g.e.resize( g.e.count() - g.joinedVertices * 2 );
390 g.joinedVertices = 0;
391
392 // fix vertices of deactivated edges
393 for ( int eIdx : std::as_const( g.inactiveEdges ) )
394 {
395 if ( eIdx >= g.e.count() )
396 continue;
397 const QgsTracerGraph::E &e = g.e[eIdx];
398 QgsTracerGraph::V &v1 = g.v[e.v1];
399 for ( int i = 0; i < v1.edges.count(); ++i )
400 {
401 if ( v1.edges[i] >= g.e.count() )
402 v1.edges.remove( i-- );
403 }
404 v1.edges << eIdx;
405
406 QgsTracerGraph::V &v2 = g.v[e.v2];
407 for ( int i = 0; i < v2.edges.count(); ++i )
408 {
409 if ( v2.edges[i] >= g.e.count() )
410 v2.edges.remove( i-- );
411 }
412 v2.edges << eIdx;
413 }
414
415 g.inactiveEdges.clear();
416}
417
418
420{
421 QgsGeometry geom = g;
422 // segmentize curved geometries - we will use noding algorithm from GEOS
423 // to find all intersections a bit later (so we need them segmentized anyway)
425 {
426 QgsAbstractGeometry *segmentizedGeomV2 = g.constGet()->segmentize();
427 if ( !segmentizedGeomV2 )
428 return;
429
430 geom = QgsGeometry( segmentizedGeomV2 );
431 }
432
433 switch ( QgsWkbTypes::flatType( geom.wkbType() ) )
434 {
436 mpl << geom.asPolyline();
437 break;
438
440 {
441 const auto polygon = geom.asPolygon();
442 for ( const QgsPolylineXY &ring : polygon )
443 mpl << ring;
444 }
445 break;
446
448 {
449 const auto multiPolyline = geom.asMultiPolyline();
450 for ( const QgsPolylineXY &linestring : multiPolyline )
451 mpl << linestring;
452 }
453 break;
454
456 {
457 const auto multiPolygon = geom.asMultiPolygon();
458 for ( const QgsPolygonXY &polygon : multiPolygon )
459 {
460 for ( const QgsPolylineXY &ring : polygon )
461 mpl << ring;
462 }
463 }
464 break;
465
466 default:
467 break; // unknown type - do nothing
468 }
469}
470
471// -------------
472
473
474QgsTracer::QgsTracer() = default;
475
476bool QgsTracer::initGraph()
477{
478 if ( mGraph )
479 return true; // already initialized
480
481 mHasTopologyProblem = false;
482
483 QgsFeature f;
485
486 // extract linestrings
487
488 // TODO: use QgsPointLocator as a source for the linework
489
490 QElapsedTimer t1, t2, t2a, t3;
491
492 t1.start();
493 int featuresCounted = 0;
494 for ( const QgsVectorLayer *vl : std::as_const( mLayers ) )
495 {
496 QgsFeatureRequest request;
497 bool filter = false;
498 std::unique_ptr< QgsFeatureRenderer > renderer;
499 std::unique_ptr<QgsRenderContext> ctx;
500
502 if ( !enableInvisibleFeature && mRenderContext && vl->renderer() )
503 {
504 renderer.reset( vl->renderer()->clone() );
505 ctx = std::make_unique<QgsRenderContext>( *mRenderContext.get() );
506 ctx->expressionContext() << QgsExpressionContextUtils::layerScope( vl );
507
508 // setup scale for scale dependent visibility (rule based)
509 renderer->startRender( *ctx.get(), vl->fields() );
510 filter = renderer->capabilities() & QgsFeatureRenderer::Filter;
511 request.setSubsetOfAttributes( renderer->usedAttributes( *ctx.get() ), vl->fields() );
512 }
513 else
514 {
515 request.setNoAttributes();
516 }
517
518 request.setDestinationCrs( mCRS, mTransformContext );
519 if ( !mExtent.isEmpty() )
520 request.setFilterRect( mExtent );
521
522 QgsFeatureIterator fi = vl->getFeatures( request );
523 while ( fi.nextFeature( f ) )
524 {
525 if ( !f.hasGeometry() )
526 continue;
527
528 if ( filter )
529 {
530 ctx->expressionContext().setFeature( f );
531 if ( !renderer->willRenderFeature( f, *ctx.get() ) )
532 {
533 continue;
534 }
535 }
536
537 extractLinework( f.geometry(), mpl );
538
539 ++featuresCounted;
540 if ( mMaxFeatureCount != 0 && featuresCounted >= mMaxFeatureCount )
541 return false;
542 }
543
544 if ( renderer )
545 {
546 renderer->stopRender( *ctx.get() );
547 }
548 }
549 int timeExtract = t1.elapsed();
550
551 // resolve intersections
552
553 t2.start();
554
555 int timeNodingCall = 0;
556
557#if 0
558 // without noding - if data are known to be noded beforehand
559#else
560 QgsGeometry allGeom = QgsGeometry::fromMultiPolylineXY( mpl );
561
562 try
563 {
564 t2a.start();
565 // GEOSNode_r may throw an exception
566 geos::unique_ptr allGeomGeos( QgsGeos::asGeos( allGeom ) );
567 geos::unique_ptr allNoded( GEOSNode_r( QgsGeosContext::get(), allGeomGeos.get() ) );
568
569 if ( mAddPointsOnIntersections )
570 {
571 mIntersections = QgsGeometry();
572 }
573 else
574 {
575 geos::unique_ptr allPoints( GEOSGeom_extractUniquePoints_r( QgsGeosContext::get(), allGeomGeos.get() ) );
576 geos::unique_ptr nodedPoints( GEOSGeom_extractUniquePoints_r( QgsGeosContext::get(), allNoded.get() ) );
577 geos::unique_ptr intersectionNodes( GEOSDifference_r( QgsGeosContext::get(), nodedPoints.get(), allPoints.get() ) );
578 mIntersections = QgsGeos::geometryFromGeos( intersectionNodes.release() );
579 }
580
581 timeNodingCall = t2a.elapsed();
582
583 QgsGeometry noded = QgsGeos::geometryFromGeos( allNoded.release() );
584
585 mpl = noded.asMultiPolyline();
586 }
587 catch ( QgsGeosException &e )
588 {
589 // no big deal... we will just not have nicely noded linework, potentially
590 // missing some intersections
591
592 mHasTopologyProblem = true;
593
594 QgsDebugError( u"Tracer Noding Exception: %1"_s.arg( e.what() ) );
595 }
596#endif
597
598 int timeNoding = t2.elapsed();
599
600 t3.start();
601
602 mGraph.reset( makeGraph( mpl ) );
603
604 int timeMake = t3.elapsed();
605
606 Q_UNUSED( timeExtract )
607 Q_UNUSED( timeNoding )
608 Q_UNUSED( timeNodingCall )
609 Q_UNUSED( timeMake )
610 QgsDebugMsgLevel( u"tracer extract %1 ms, noding %2 ms (call %3 ms), make %4 ms"_s
611 .arg( timeExtract ).arg( timeNoding ).arg( timeNodingCall ).arg( timeMake ), 2 );
612
613 return true;
614}
615
620
621void QgsTracer::setLayers( const QList<QgsVectorLayer *> &layers )
622{
623 if ( mLayers == layers )
624 return;
625
626 for ( QgsVectorLayer *layer : std::as_const( mLayers ) )
627 {
628 disconnect( layer, &QgsVectorLayer::featureAdded, this, &QgsTracer::onFeatureAdded );
629 disconnect( layer, &QgsVectorLayer::featureDeleted, this, &QgsTracer::onFeatureDeleted );
630 disconnect( layer, &QgsVectorLayer::geometryChanged, this, &QgsTracer::onGeometryChanged );
631 disconnect( layer, &QgsVectorLayer::attributeValueChanged, this, &QgsTracer::onAttributeValueChanged );
632 disconnect( layer, &QgsVectorLayer::dataChanged, this, &QgsTracer::onDataChanged );
633 disconnect( layer, &QgsVectorLayer::styleChanged, this, &QgsTracer::onStyleChanged );
634 disconnect( layer, &QObject::destroyed, this, &QgsTracer::onLayerDestroyed );
635 }
636
637 mLayers = layers;
638
639 for ( QgsVectorLayer *layer : layers )
640 {
641 connect( layer, &QgsVectorLayer::featureAdded, this, &QgsTracer::onFeatureAdded );
642 connect( layer, &QgsVectorLayer::featureDeleted, this, &QgsTracer::onFeatureDeleted );
643 connect( layer, &QgsVectorLayer::geometryChanged, this, &QgsTracer::onGeometryChanged );
644 connect( layer, &QgsVectorLayer::attributeValueChanged, this, &QgsTracer::onAttributeValueChanged );
645 connect( layer, &QgsVectorLayer::dataChanged, this, &QgsTracer::onDataChanged );
646 connect( layer, &QgsVectorLayer::styleChanged, this, &QgsTracer::onStyleChanged );
647 connect( layer, &QObject::destroyed, this, &QgsTracer::onLayerDestroyed );
648 }
649
651}
652
654{
655 mCRS = crs;
656 mTransformContext = context;
658}
659
661{
662 mRenderContext = std::make_unique<QgsRenderContext>( *renderContext );
664}
665
667{
668 if ( mExtent == extent )
669 return;
670
671 mExtent = extent;
673}
674
676{
677 mOffset = offset;
678}
679
680void QgsTracer::offsetParameters( int &quadSegments, int &joinStyle, double &miterLimit )
681{
682 quadSegments = mOffsetSegments;
683 joinStyle = static_cast< int >( mOffsetJoinStyle );
684 miterLimit = mOffsetMiterLimit;
685}
686
687void QgsTracer::setOffsetParameters( int quadSegments, int joinStyle, double miterLimit )
688{
689 mOffsetSegments = quadSegments;
690 mOffsetJoinStyle = static_cast< Qgis::JoinStyle >( joinStyle );
691 mOffsetMiterLimit = miterLimit;
692}
693
695{
696 if ( mGraph )
697 return true;
698
699 // configuration from derived class?
700 configure();
701
702 return initGraph();
703}
704
705
707{
708 mGraph.reset( nullptr );
709}
710
711void QgsTracer::onFeatureAdded( QgsFeatureId fid )
712{
713 Q_UNUSED( fid )
715}
716
717void QgsTracer::onFeatureDeleted( QgsFeatureId fid )
718{
719 Q_UNUSED( fid )
721}
722
723void QgsTracer::onGeometryChanged( QgsFeatureId fid, const QgsGeometry &geom )
724{
725 Q_UNUSED( fid )
726 Q_UNUSED( geom )
728}
729
730void QgsTracer::onAttributeValueChanged( QgsFeatureId fid, int idx, const QVariant &value )
731{
732 Q_UNUSED( fid )
733 Q_UNUSED( idx )
734 Q_UNUSED( value )
736}
737
738void QgsTracer::onDataChanged( )
739{
741}
742
743void QgsTracer::onStyleChanged( )
744{
746}
747
748void QgsTracer::onLayerDestroyed( QObject *obj )
749{
750 // remove the layer before it is completely invalid (static_cast should be the safest cast)
751 mLayers.removeAll( static_cast<QgsVectorLayer *>( obj ) );
753}
754
755QVector<QgsPointXY> QgsTracer::findShortestPath( const QgsPointXY &p1, const QgsPointXY &p2, PathError *error )
756{
757 init(); // does nothing if the graph exists already
758 if ( !mGraph )
759 {
760 if ( error ) *error = ErrTooManyFeatures;
761 return QVector<QgsPointXY>();
762 }
763
764 QElapsedTimer t;
765 t.start();
766 int v1 = pointInGraph( *mGraph, p1 );
767 int v2 = pointInGraph( *mGraph, p2 );
768 int tPrep = t.elapsed();
769
770 if ( v1 == -1 )
771 {
772 if ( error ) *error = ErrPoint1;
773 return QVector<QgsPointXY>();
774 }
775 if ( v2 == -1 )
776 {
777 if ( error ) *error = ErrPoint2;
778 return QVector<QgsPointXY>();
779 }
780
781 QElapsedTimer t2;
782 t2.start();
783 QgsPolylineXY points = shortestPath( *mGraph, v1, v2 );
784 int tPath = t2.elapsed();
785
786 Q_UNUSED( tPrep )
787 Q_UNUSED( tPath )
788 QgsDebugMsgLevel( u"path timing: prep %1 ms, path %2 ms"_s.arg( tPrep ).arg( tPath ), 2 );
789
790 if ( points.size() > 2 && !mIntersections.isEmpty() )
791 {
792 QVector<QgsPointXY> noInts;
793 noInts.reserve( points.size() );
794 noInts.append( points.first() );
795 for ( auto it = std::next( points.begin() ), end = std::prev( points.end() ); it != end; ++it )
796 {
797 if ( mIntersections.contains( it->x(), it->y() ) )
798 {
799 // we skip points that are on a straight segment and were not on the original geometries
800 QgsPointXY nearest;
801 if ( 0 == it->sqrDistToSegment( std::prev( it )->x(),
802 std::prev( it )->y(),
803 std::next( it )->x(),
804 std::next( it )->y(),
805 nearest, 1E-12 ) )
806 {
807 continue;
808 }
809 }
810 noInts << *it;
811 }
812 noInts.append( points.last() );
813 points = noInts;
814 QgsDebugMsgLevel( u"intersection point removal timing: %1 ms"_s.arg( t2.elapsed() - tPath ), 2 );
815 }
816
817 resetGraph( *mGraph );
818
819 if ( !points.isEmpty() && mOffset != 0 )
820 {
821 QVector<QgsPointXY> pointsInput( points );
822 QgsLineString linestring( pointsInput );
823 std::unique_ptr<QgsGeometryEngine> linestringEngine( QgsGeometry::createGeometryEngine( &linestring ) );
824 std::unique_ptr<QgsAbstractGeometry> linestringOffset( linestringEngine->offsetCurve( mOffset, mOffsetSegments, mOffsetJoinStyle, mOffsetMiterLimit ) );
825 if ( QgsLineString *ls2 = qgsgeometry_cast<QgsLineString *>( linestringOffset.get() ) )
826 {
827 points.clear();
828 for ( int i = 0; i < ls2->numPoints(); ++i )
829 points << QgsPointXY( ls2->pointN( i ) );
830
831 // sometimes (with negative offset?) the resulting curve is reversed
832 if ( points.count() >= 2 )
833 {
834 QgsPointXY res1 = points.first(), res2 = points.last();
835 double diffNormal = res1.distance( p1 ) + res2.distance( p2 );
836 double diffReversed = res1.distance( p2 ) + res2.distance( p1 );
837 if ( diffReversed < diffNormal )
838 std::reverse( points.begin(), points.end() );
839 }
840 }
841 }
842
843 if ( error )
844 *error = points.isEmpty() ? ErrNoPath : ErrNone;
845
846 return points;
847}
848
850{
851 init(); // does nothing if the graph exists already
852 if ( !mGraph )
853 return false;
854
855 if ( point2vertex( *mGraph, pt ) != -1 )
856 return true;
857
858 int lineVertexAfter;
859 int e = point2edge( *mGraph, pt, lineVertexAfter );
860 return e != -1;
861}
862
864{
865 if ( enable == mAddPointsOnIntersections )
866 return;
867
868 mAddPointsOnIntersections = enable;
870}
JoinStyle
Join styles for buffers.
Definition qgis.h:2179
@ LineString
LineString.
Definition qgis.h:283
@ Polygon
Polygon.
Definition qgis.h:284
@ MultiPolygon
MultiPolygon.
Definition qgis.h:288
@ MultiLineString
MultiLineString.
Definition qgis.h:287
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:71
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:256
static QgsGeometry geometryFromGeos(GEOSGeometry *geos)
Creates a new QgsGeometry object, feeding in a geometry in GEOS format.
Definition qgsgeos.cpp:184
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:62
double distance(double x, double y) const
Returns the distance between this point and a specified x, y coordinate.
Definition qgspointxy.h:208
double y
Definition qgspointxy.h:66
double x
Definition qgspointxy.h:65
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:92
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:63
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:63
#define QgsDebugError(str)
Definition qgslogger.h:59
std::pair< int, double > DijkstraQueueItem
Definition qgstracer.cpp:40
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:75
double distance2D(const QgsPolylineXY &coords)
Definition qgstracer.cpp:53
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.
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:45