QGIS API Documentation  3.6.0-Noosa (5873452)
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 
19 #include "qgsfeatureiterator.h"
20 #include "qgsgeometry.h"
21 #include "qgsgeometryutils.h"
22 #include "qgsgeos.h"
23 #include "qgslogger.h"
24 #include "qgsvectorlayer.h"
25 #include "qgsexception.h"
26 #include "qgsrenderer.h"
27 #include "qgssettings.h"
29 
30 #include <queue>
31 #include <vector>
32 
33 typedef std::pair<int, double> DijkstraQueueItem; // first = vertex index, second = distance
34 
35 // utility comparator for queue items based on distance
36 struct comp
37 {
39  {
40  return a.second > b.second;
41  }
42 };
43 
44 
45 // TODO: move to geometry utils
46 double distance2D( const QgsPolylineXY &coords )
47 {
48  int np = coords.count();
49  if ( np == 0 )
50  return 0;
51 
52  double x0 = coords[0].x(), y0 = coords[0].y();
53  double x1, y1;
54  double dist = 0;
55  for ( int i = 1; i < np; ++i )
56  {
57  x1 = coords[i].x();
58  y1 = coords[i].y();
59  dist += std::sqrt( ( x1 - x0 ) * ( x1 - x0 ) + ( y1 - y0 ) * ( y1 - y0 ) );
60  x0 = x1;
61  y0 = y1;
62  }
63  return dist;
64 }
65 
66 
67 // TODO: move to geometry utils
68 double closestSegment( const QgsPolylineXY &pl, const QgsPointXY &pt, int &vertexAfter, double epsilon )
69 {
70  double sqrDist = std::numeric_limits<double>::max();
71  const QgsPointXY *pldata = pl.constData();
72  int plcount = pl.count();
73  double prevX = pldata[0].x(), prevY = pldata[0].y();
74  double segmentPtX, segmentPtY;
75  for ( int i = 1; i < plcount; ++i )
76  {
77  double currentX = pldata[i].x();
78  double currentY = pldata[i].y();
79  double testDist = QgsGeometryUtils::sqrDistToLine( pt.x(), pt.y(), prevX, prevY, currentX, currentY, segmentPtX, segmentPtY, epsilon );
80  if ( testDist < sqrDist )
81  {
82  sqrDist = testDist;
83  vertexAfter = i;
84  }
85  prevX = currentX;
86  prevY = currentY;
87  }
88  return sqrDist;
89 }
90 
92 
95 {
96  QgsTracerGraph() = default;
97 
98  struct E // bidirectional edge
99  {
101  int v1, v2;
103  QVector<QgsPointXY> coords;
104 
105  int otherVertex( int v0 ) const { return v1 == v0 ? v2 : v1; }
106  double weight() const { return distance2D( coords ); }
107  };
108 
109  struct V
110  {
114  QVector<int> edges;
115  };
116 
118  QVector<V> v;
120  QVector<E> e;
121 
123  QSet<int> inactiveEdges;
125  int joinedVertices{ 0 };
126 };
127 
128 
129 QgsTracerGraph *makeGraph( const QVector<QgsPolylineXY> &edges )
130 {
131  QgsTracerGraph *g = new QgsTracerGraph();
132  g->joinedVertices = 0;
133  QHash<QgsPointXY, int> point2vertex;
134 
135  Q_FOREACH ( const QgsPolylineXY &line, edges )
136  {
137  QgsPointXY p1( line[0] );
138  QgsPointXY p2( line[line.count() - 1] );
139 
140  int v1 = -1, v2 = -1;
141  // get or add vertex 1
142  if ( point2vertex.contains( p1 ) )
143  v1 = point2vertex.value( p1 );
144  else
145  {
146  v1 = g->v.count();
148  v.pt = p1;
149  g->v.append( v );
150  point2vertex[p1] = v1;
151  }
152 
153  // get or add vertex 2
154  if ( point2vertex.contains( p2 ) )
155  v2 = point2vertex.value( p2 );
156  else
157  {
158  v2 = g->v.count();
160  v.pt = p2;
161  g->v.append( v );
162  point2vertex[p2] = v2;
163  }
164 
165  // add edge
167  e.v1 = v1;
168  e.v2 = v2;
169  e.coords = line;
170  g->e.append( e );
171 
172  // link edge to vertices
173  int eIdx = g->e.count() - 1;
174  g->v[v1].edges << eIdx;
175  g->v[v2].edges << eIdx;
176  }
177 
178  return g;
179 }
180 
181 
182 QVector<QgsPointXY> shortestPath( const QgsTracerGraph &g, int v1, int v2 )
183 {
184  if ( v1 == -1 || v2 == -1 )
185  return QVector<QgsPointXY>(); // invalid input
186 
187  // priority queue to drive Dijkstra:
188  // first of the pair is vertex index, second is distance
189  std::priority_queue< DijkstraQueueItem, std::vector< DijkstraQueueItem >, comp > Q;
190 
191  // shortest distances to each vertex
192  QVector<double> D( g.v.count(), std::numeric_limits<double>::max() );
193  D[v1] = 0;
194 
195  // whether vertices have been already processed
196  QVector<bool> F( g.v.count() );
197 
198  // using which edge there is shortest path to each vertex
199  QVector<int> S( g.v.count(), -1 );
200 
201  int u = -1;
202  Q.push( DijkstraQueueItem( v1, 0 ) );
203 
204  while ( !Q.empty() )
205  {
206  u = Q.top().first; // new vertex to visit
207  Q.pop();
208 
209  if ( u == v2 )
210  break; // we can stop now, there won't be a shorter path
211 
212  if ( F[u] )
213  continue; // ignore previously added path which is actually longer
214 
215  const QgsTracerGraph::V &vu = g.v[u];
216  const int *vuEdges = vu.edges.constData();
217  int count = vu.edges.count();
218  for ( int i = 0; i < count; ++i )
219  {
220  const QgsTracerGraph::E &edge = g.e[ vuEdges[i] ];
221  int v = edge.otherVertex( u );
222  double w = edge.weight();
223  if ( !F[v] && D[u] + w < D[v] )
224  {
225  // found a shorter way to the vertex
226  D[v] = D[u] + w;
227  S[v] = vuEdges[i];
228  Q.push( DijkstraQueueItem( v, D[v] ) );
229  }
230  }
231  F[u] = true; // mark the vertex as processed (we know the fastest path to it)
232  }
233 
234  if ( u != v2 ) // there's no path to the end vertex
235  return QVector<QgsPointXY>();
236 
237  //qDebug("dist %f", D[u]);
238 
239  QVector<QgsPointXY> points;
240  QList<int> path;
241  while ( S[u] != -1 )
242  {
243  path << S[u];
244  const QgsTracerGraph::E &e = g.e[S[u]];
245  QVector<QgsPointXY> edgePoints = e.coords;
246  if ( edgePoints[0] != g.v[u].pt )
247  std::reverse( edgePoints.begin(), edgePoints.end() );
248  if ( !points.isEmpty() )
249  points.remove( points.count() - 1 ); // chop last one (will be used from next edge)
250  points << edgePoints;
251  u = e.otherVertex( u );
252  }
253 
254  std::reverse( path.begin(), path.end() );
255  //Q_FOREACH (int x, path)
256  // qDebug("e: %d", x);
257 
258  std::reverse( points.begin(), points.end() );
259  return points;
260 }
261 
262 
263 int point2vertex( const QgsTracerGraph &g, const QgsPointXY &pt, double epsilon = 1e-6 )
264 {
265  // TODO: use spatial index
266 
267  for ( int i = 0; i < g.v.count(); ++i )
268  {
269  const QgsTracerGraph::V &v = g.v.at( i );
270  if ( v.pt == pt || ( std::fabs( v.pt.x() - pt.x() ) < epsilon && std::fabs( v.pt.y() - pt.y() ) < epsilon ) )
271  return i;
272  }
273 
274  return -1;
275 }
276 
277 
278 int point2edge( const QgsTracerGraph &g, const QgsPointXY &pt, int &lineVertexAfter, double epsilon = 1e-6 )
279 {
280  int vertexAfter;
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  double dist = closestSegment( e.coords, pt, vertexAfter, epsilon );
289  if ( dist == 0 )
290  {
291  lineVertexAfter = vertexAfter; //NOLINT
292  return i;
293  }
294  }
295  return -1;
296 }
297 
298 
299 void splitLinestring( const QgsPolylineXY &points, const QgsPointXY &pt, int lineVertexAfter, QgsPolylineXY &pts1, QgsPolylineXY &pts2 )
300 {
301  int count1 = lineVertexAfter;
302  int count2 = points.count() - lineVertexAfter;
303 
304  for ( int i = 0; i < count1; ++i )
305  pts1 << points[i];
306  if ( points[lineVertexAfter - 1] != pt )
307  pts1 << pt; // repeat if not split exactly at that point
308 
309  if ( pt != points[lineVertexAfter] )
310  pts2 << pt; // repeat if not split exactly at that point
311  for ( int i = 0; i < count2; ++i )
312  pts2 << points[i + lineVertexAfter];
313 }
314 
315 
317 {
318  // find edge where the point is
319  int lineVertexAfter;
320  int eIdx = point2edge( g, pt, lineVertexAfter );
321 
322  //qDebug("e: %d", eIdx);
323 
324  if ( eIdx == -1 )
325  return -1;
326 
327  const QgsTracerGraph::E &e = g.e[eIdx];
328  QgsTracerGraph::V &v1 = g.v[e.v1];
329  QgsTracerGraph::V &v2 = g.v[e.v2];
330 
331  QgsPolylineXY out1, out2;
332  splitLinestring( e.coords, pt, lineVertexAfter, out1, out2 );
333 
334  int vIdx = g.v.count();
335  int e1Idx = g.e.count();
336  int e2Idx = e1Idx + 1;
337 
338  // prepare new vertex and edges
339 
341  v.pt = pt;
342  v.edges << e1Idx << e2Idx;
343 
345  e1.v1 = e.v1;
346  e1.v2 = vIdx;
347  e1.coords = out1;
348 
350  e2.v1 = vIdx;
351  e2.v2 = e.v2;
352  e2.coords = out2;
353 
354  // update edge connectivity of existing vertices
355  v1.edges.replace( v1.edges.indexOf( eIdx ), e1Idx );
356  v2.edges.replace( v2.edges.indexOf( eIdx ), e2Idx );
357  g.inactiveEdges << eIdx;
358 
359  // add new vertex and edges to the graph
360  g.v.append( v );
361  g.e.append( e1 );
362  g.e.append( e2 );
363  g.joinedVertices++;
364 
365  return vIdx;
366 }
367 
368 
370 {
371  // try to use existing vertex in the graph
372  int v = point2vertex( g, pt );
373  if ( v != -1 )
374  return v;
375 
376  // try to add the vertex to an edge (may fail if point is not on edge)
377  return joinVertexToGraph( g, pt );
378 }
379 
380 
382 {
383  // remove extra vertices and edges
384  g.v.resize( g.v.count() - g.joinedVertices );
385  g.e.resize( g.e.count() - g.joinedVertices * 2 );
386  g.joinedVertices = 0;
387 
388  // fix vertices of deactivated edges
389  Q_FOREACH ( int eIdx, g.inactiveEdges )
390  {
391  if ( eIdx >= g.e.count() )
392  continue;
393  const QgsTracerGraph::E &e = g.e[eIdx];
394  QgsTracerGraph::V &v1 = g.v[e.v1];
395  for ( int i = 0; i < v1.edges.count(); ++i )
396  {
397  if ( v1.edges[i] >= g.e.count() )
398  v1.edges.remove( i-- );
399  }
400  v1.edges << eIdx;
401 
402  QgsTracerGraph::V &v2 = g.v[e.v2];
403  for ( int i = 0; i < v2.edges.count(); ++i )
404  {
405  if ( v2.edges[i] >= g.e.count() )
406  v2.edges.remove( i-- );
407  }
408  v2.edges << eIdx;
409  }
410 
411  g.inactiveEdges.clear();
412 }
413 
414 
416 {
417  QgsGeometry geom = g;
418  // segmentize curved geometries - we will use noding algorithm from GEOS
419  // to find all intersections a bit later (so we need them segmentized anyway)
420  if ( QgsWkbTypes::isCurvedType( g.wkbType() ) )
421  {
422  QgsAbstractGeometry *segmentizedGeomV2 = g.constGet()->segmentize();
423  if ( !segmentizedGeomV2 )
424  return;
425 
426  geom = QgsGeometry( segmentizedGeomV2 );
427  }
428 
429  switch ( QgsWkbTypes::flatType( geom.wkbType() ) )
430  {
432  mpl << geom.asPolyline();
433  break;
434 
436  Q_FOREACH ( const QgsPolylineXY &ring, geom.asPolygon() )
437  mpl << ring;
438  break;
439 
441  Q_FOREACH ( const QgsPolylineXY &linestring, geom.asMultiPolyline() )
442  mpl << linestring;
443  break;
444 
446  Q_FOREACH ( const QgsPolygonXY &polygon, geom.asMultiPolygon() )
447  Q_FOREACH ( const QgsPolylineXY &ring, polygon )
448  mpl << ring;
449  break;
450 
451  default:
452  break; // unknown type - do nothing
453  }
454 }
455 
456 // -------------
457 
458 
459 QgsTracer::QgsTracer() = default;
460 
461 bool QgsTracer::initGraph()
462 {
463  if ( mGraph )
464  return true; // already initialized
465 
466  mHasTopologyProblem = false;
467 
468  QgsFeature f;
469  QgsMultiPolylineXY mpl;
470 
471  // extract linestrings
472 
473  // TODO: use QgsPointLocator as a source for the linework
474 
475  QTime t1, t2, t2a, t3;
476 
477  t1.start();
478  int featuresCounted = 0;
479  bool enableInvisibleFeature = QgsSettings().value( QStringLiteral( "/qgis/digitizing/snap_invisible_feature" ), false ).toBool();
480  for ( const QgsVectorLayer *vl : qgis::as_const( mLayers ) )
481  {
482  QgsFeatureRequest request;
483  bool filter = false;
484  std::unique_ptr< QgsFeatureRenderer > renderer;
485  std::unique_ptr<QgsRenderContext> ctx;
486 
487  if ( !enableInvisibleFeature && mRenderContext && vl->renderer() )
488  {
489  renderer.reset( vl->renderer()->clone() );
490  ctx.reset( new QgsRenderContext( *mRenderContext.get() ) );
492 
493  // setup scale for scale dependent visibility (rule based)
494  renderer->startRender( *ctx.get(), vl->fields() );
495  filter = renderer->capabilities() & QgsFeatureRenderer::Filter;
496  request.setSubsetOfAttributes( renderer->usedAttributes( *ctx.get() ), vl->fields() );
497  }
498  else
499  {
500  request.setNoAttributes();
501  }
502 
503  request.setDestinationCrs( mCRS, mTransformContext );
504  if ( !mExtent.isEmpty() )
505  request.setFilterRect( mExtent );
506 
507  QgsFeatureIterator fi = vl->getFeatures( request );
508  while ( fi.nextFeature( f ) )
509  {
510  if ( !f.hasGeometry() )
511  continue;
512 
513  if ( filter )
514  {
515  ctx->expressionContext().setFeature( f );
516  if ( !renderer->willRenderFeature( f, *ctx.get() ) )
517  {
518  continue;
519  }
520  }
521 
522  extractLinework( f.geometry(), mpl );
523 
524  ++featuresCounted;
525  if ( mMaxFeatureCount != 0 && featuresCounted >= mMaxFeatureCount )
526  return false;
527  }
528 
529  if ( renderer )
530  {
531  renderer->stopRender( *ctx.get() );
532  }
533  }
534  int timeExtract = t1.elapsed();
535 
536  // resolve intersections
537 
538  t2.start();
539 
540  int timeNodingCall = 0;
541 
542 #if 0
543  // without noding - if data are known to be noded beforehand
544 #else
546 
547  try
548  {
549  t2a.start();
550  // GEOSNode_r may throw an exception
551  geos::unique_ptr allGeomGeos( QgsGeos::asGeos( allGeom ) );
552  geos::unique_ptr allNoded( GEOSNode_r( QgsGeos::getGEOSHandler(), allGeomGeos.get() ) );
553  timeNodingCall = t2a.elapsed();
554 
555  QgsGeometry noded = QgsGeos::geometryFromGeos( allNoded.release() );
556 
557  mpl = noded.asMultiPolyline();
558  }
559  catch ( GEOSException &e )
560  {
561  // no big deal... we will just not have nicely noded linework, potentially
562  // missing some intersections
563 
564  mHasTopologyProblem = true;
565 
566  QgsDebugMsg( QStringLiteral( "Tracer Noding Exception: %1" ).arg( e.what() ) );
567  }
568 #endif
569 
570  int timeNoding = t2.elapsed();
571 
572  t3.start();
573 
574  mGraph.reset( makeGraph( mpl ) );
575 
576  int timeMake = t3.elapsed();
577 
578  Q_UNUSED( timeExtract );
579  Q_UNUSED( timeNoding );
580  Q_UNUSED( timeNodingCall );
581  Q_UNUSED( timeMake );
582  QgsDebugMsg( QStringLiteral( "tracer extract %1 ms, noding %2 ms (call %3 ms), make %4 ms" )
583  .arg( timeExtract ).arg( timeNoding ).arg( timeNodingCall ).arg( timeMake ) );
584 
585  return true;
586 }
587 
589 {
590  invalidateGraph();
591 }
592 
593 void QgsTracer::setLayers( const QList<QgsVectorLayer *> &layers )
594 {
595  if ( mLayers == layers )
596  return;
597 
598  Q_FOREACH ( QgsVectorLayer *layer, mLayers )
599  {
600  disconnect( layer, &QgsVectorLayer::featureAdded, this, &QgsTracer::onFeatureAdded );
601  disconnect( layer, &QgsVectorLayer::featureDeleted, this, &QgsTracer::onFeatureDeleted );
602  disconnect( layer, &QgsVectorLayer::geometryChanged, this, &QgsTracer::onGeometryChanged );
603  disconnect( layer, &QgsVectorLayer::attributeValueChanged, this, &QgsTracer::onAttributeValueChanged );
604  disconnect( layer, &QgsVectorLayer::dataChanged, this, &QgsTracer::onDataChanged );
605  disconnect( layer, &QgsVectorLayer::styleChanged, this, &QgsTracer::onStyleChanged );
606  disconnect( layer, &QObject::destroyed, this, &QgsTracer::onLayerDestroyed );
607  }
608 
609  mLayers = layers;
610 
611  Q_FOREACH ( QgsVectorLayer *layer, mLayers )
612  {
613  connect( layer, &QgsVectorLayer::featureAdded, this, &QgsTracer::onFeatureAdded );
614  connect( layer, &QgsVectorLayer::featureDeleted, this, &QgsTracer::onFeatureDeleted );
615  connect( layer, &QgsVectorLayer::geometryChanged, this, &QgsTracer::onGeometryChanged );
616  connect( layer, &QgsVectorLayer::attributeValueChanged, this, &QgsTracer::onAttributeValueChanged );
617  connect( layer, &QgsVectorLayer::dataChanged, this, &QgsTracer::onDataChanged );
618  connect( layer, &QgsVectorLayer::styleChanged, this, &QgsTracer::onStyleChanged );
619  connect( layer, &QObject::destroyed, this, &QgsTracer::onLayerDestroyed );
620  }
621 
622  invalidateGraph();
623 }
624 
626 {
627  mCRS = crs;
628  mTransformContext = context;
629  invalidateGraph();
630 }
631 
632 void QgsTracer::setRenderContext( const QgsRenderContext *renderContext )
633 {
634  mRenderContext.reset( new QgsRenderContext( *renderContext ) );
635  invalidateGraph();
636 }
637 
638 void QgsTracer::setExtent( const QgsRectangle &extent )
639 {
640  if ( mExtent == extent )
641  return;
642 
643  mExtent = extent;
644  invalidateGraph();
645 }
646 
647 void QgsTracer::setOffset( double offset )
648 {
649  mOffset = offset;
650 }
651 
652 void QgsTracer::offsetParameters( int &quadSegments, int &joinStyle, double &miterLimit )
653 {
654  quadSegments = mOffsetSegments;
655  joinStyle = mOffsetJoinStyle;
656  miterLimit = mOffsetMiterLimit;
657 }
658 
659 void QgsTracer::setOffsetParameters( int quadSegments, int joinStyle, double miterLimit )
660 {
661  mOffsetSegments = quadSegments;
662  mOffsetJoinStyle = joinStyle;
663  mOffsetMiterLimit = miterLimit;
664 }
665 
667 {
668  if ( mGraph )
669  return true;
670 
671  // configuration from derived class?
672  configure();
673 
674  return initGraph();
675 }
676 
677 
679 {
680  mGraph.reset( nullptr );
681 }
682 
683 void QgsTracer::onFeatureAdded( QgsFeatureId fid )
684 {
685  Q_UNUSED( fid );
686  invalidateGraph();
687 }
688 
689 void QgsTracer::onFeatureDeleted( QgsFeatureId fid )
690 {
691  Q_UNUSED( fid );
692  invalidateGraph();
693 }
694 
695 void QgsTracer::onGeometryChanged( QgsFeatureId fid, const QgsGeometry &geom )
696 {
697  Q_UNUSED( fid );
698  Q_UNUSED( geom );
699  invalidateGraph();
700 }
701 
702 void QgsTracer::onAttributeValueChanged( QgsFeatureId fid, int idx, const QVariant &value )
703 {
704  Q_UNUSED( fid );
705  Q_UNUSED( idx );
706  Q_UNUSED( value );
707  invalidateGraph();
708 }
709 
710 void QgsTracer::onDataChanged( )
711 {
712  invalidateGraph();
713 }
714 
715 void QgsTracer::onStyleChanged( )
716 {
717  invalidateGraph();
718 }
719 
720 void QgsTracer::onLayerDestroyed( QObject *obj )
721 {
722  // remove the layer before it is completely invalid (static_cast should be the safest cast)
723  mLayers.removeAll( static_cast<QgsVectorLayer *>( obj ) );
724  invalidateGraph();
725 }
726 
727 QVector<QgsPointXY> QgsTracer::findShortestPath( const QgsPointXY &p1, const QgsPointXY &p2, PathError *error )
728 {
729  init(); // does nothing if the graph exists already
730  if ( !mGraph )
731  {
732  if ( error ) *error = ErrTooManyFeatures;
733  return QVector<QgsPointXY>();
734  }
735 
736  QTime t;
737  t.start();
738  int v1 = pointInGraph( *mGraph, p1 );
739  int v2 = pointInGraph( *mGraph, p2 );
740  int tPrep = t.elapsed();
741 
742  if ( v1 == -1 )
743  {
744  if ( error ) *error = ErrPoint1;
745  return QVector<QgsPointXY>();
746  }
747  if ( v2 == -1 )
748  {
749  if ( error ) *error = ErrPoint2;
750  return QVector<QgsPointXY>();
751  }
752 
753  QTime t2;
754  t2.start();
755  QgsPolylineXY points = shortestPath( *mGraph, v1, v2 );
756  int tPath = t2.elapsed();
757 
758  Q_UNUSED( tPrep );
759  Q_UNUSED( tPath );
760  QgsDebugMsg( QStringLiteral( "path timing: prep %1 ms, path %2 ms" ).arg( tPrep ).arg( tPath ) );
761 
762  resetGraph( *mGraph );
763 
764  if ( !points.isEmpty() && mOffset != 0 )
765  {
766  QVector<QgsPointXY> pointsInput( points );
767  QgsLineString linestring( pointsInput );
768  std::unique_ptr<QgsGeometryEngine> linestringEngine( QgsGeometry::createGeometryEngine( &linestring ) );
769  std::unique_ptr<QgsAbstractGeometry> linestringOffset( linestringEngine->offsetCurve( mOffset, mOffsetSegments, mOffsetJoinStyle, mOffsetMiterLimit ) );
770  if ( QgsLineString *ls2 = qgsgeometry_cast<QgsLineString *>( linestringOffset.get() ) )
771  {
772  points.clear();
773  for ( int i = 0; i < ls2->numPoints(); ++i )
774  points << QgsPointXY( ls2->pointN( i ) );
775 
776  // sometimes (with negative offset?) the resulting curve is reversed
777  if ( points.count() >= 2 )
778  {
779  QgsPointXY res1 = points.first(), res2 = points.last();
780  double diffNormal = res1.distance( p1 ) + res2.distance( p2 );
781  double diffReversed = res1.distance( p2 ) + res2.distance( p1 );
782  if ( diffReversed < diffNormal )
783  std::reverse( points.begin(), points.end() );
784  }
785  }
786  }
787 
788  if ( error )
789  *error = points.isEmpty() ? ErrNoPath : ErrNone;
790 
791  return points;
792 }
793 
795 {
796  init(); // does nothing if the graph exists already
797  if ( !mGraph )
798  return false;
799 
800  if ( point2vertex( *mGraph, pt ) != -1 )
801  return true;
802 
803  int lineVertexAfter;
804  int e = point2edge( *mGraph, pt, lineVertexAfter );
805  return e != -1;
806 }
QVector< E > e
Edges of the graph.
Definition: qgstracer.cpp:120
QgsFeatureRequest & setDestinationCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets the destination crs for feature&#39;s geometries.
Wrapper for iterator of features from vector data provider or vector layer.
A rectangle specified with double values.
Definition: qgsrectangle.h:41
~QgsTracer() override
Definition: qgstracer.cpp:588
double weight() const
Definition: qgstracer.cpp:106
int pointInGraph(QgsTracerGraph &g, const QgsPointXY &pt)
Definition: qgstracer.cpp:369
QVector< QgsPointXY > coords
coordinates of the edge (including endpoints)
Definition: qgstracer.cpp:103
This class is a composition of two QSettings instances:
Definition: qgssettings.h:58
int point2vertex(const QgsTracerGraph &g, const QgsPointXY &pt, double epsilon=1e-6)
Definition: qgstracer.cpp:263
void setRenderContext(const QgsRenderContext *renderContext)
Sets the renderContext used for tracing only on visible features.
Definition: qgstracer.cpp:632
int joinedVertices
Temporarily added vertices (for each there are two extra edges)
Definition: qgstracer.cpp:125
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
Features may be filtered, i.e. some features may not be rendered (categorized, rule based ...
Definition: qgsrenderer.h:244
QgsWkbTypes::Type wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
double y
Definition: qgspointxy.h:48
A class to represent a 2D point.
Definition: qgspointxy.h:43
QgsExpressionContext * expressionContext()
Returns the expression context used to evaluate filter expressions.
static QgsGeometry fromMultiPolylineXY(const QgsMultiPolylineXY &multiline)
Creates a new geometry from a QgsMultiPolylineXY object.
void setExtent(const QgsRectangle &extent)
Sets extent to which graph&#39;s features will be limited (empty extent means no limit) ...
Definition: qgstracer.cpp:638
QVector< QgsPolylineXY > QgsPolygonXY
Polygon: first item of the list is outer ring, inner rings (if any) start from second item...
Definition: qgsgeometry.h:68
qint64 QgsFeatureId
Definition: qgsfeatureid.h:25
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
QgsTracerGraph * makeGraph(const QVector< QgsPolylineXY > &edges)
Definition: qgstracer.cpp:129
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:106
void splitLinestring(const QgsPolylineXY &points, const QgsPointXY &pt, int lineVertexAfter, QgsPolylineXY &pts1, QgsPolylineXY &pts2)
Definition: qgstracer.cpp:299
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
const QgsCoordinateReferenceSystem & crs
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:197
int joinVertexToGraph(QgsTracerGraph &g, const QgsPointXY &pt)
Definition: qgstracer.cpp:316
void featureDeleted(QgsFeatureId fid)
Emitted when a feature has been deleted.
std::pair< int, double > DijkstraQueueItem
Definition: qgstracer.cpp:33
void styleChanged()
Signal emitted whenever a change affects the layer&#39;s style.
static GEOSContextHandle_t getGEOSHandler()
Definition: qgsgeos.cpp:2777
QVector< QgsPolylineXY > QgsMultiPolylineXY
A collection of QgsPolylines that share a common collection of attributes.
Definition: qgsgeometry.h:78
QgsMultiPolylineXY asMultiPolyline() const
Returns the contents of the geometry as a multi-linestring.
void setOffsetParameters(int quadSegments, int joinStyle, double miterLimit)
Set extra parameters for offset curve algorithm (used when offset is non-zero)
Definition: qgstracer.cpp:659
void setDestinationCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets the crs and transform context used for tracing.
Definition: qgstracer.cpp:625
QSet< int > inactiveEdges
Temporarily removed edges.
Definition: qgstracer.cpp:123
QgsFeatureRequest & setNoAttributes()
Set that no attributes will be fetched.
QgsPolygonXY asPolygon() const
Returns the contents of the geometry as a polygon.
std::unique_ptr< GEOSGeometry, GeosDeleter > unique_ptr
Scoped GEOS pointer.
Definition: qgsgeos.h:79
PathError
Possible errors that may happen when calling findShortestPath()
Definition: qgstracer.h:132
This class wraps a request for features to a vector layer (or directly its vector data provider)...
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
void setOffset(double offset)
Set offset in map units that should be applied to the traced paths returned from findShortestPath().
Definition: qgstracer.cpp:647
int point2edge(const QgsTracerGraph &g, const QgsPointXY &pt, int &lineVertexAfter, double epsilon=1e-6)
Definition: qgstracer.cpp:278
void geometryChanged(QgsFeatureId fid, const QgsGeometry &geometry)
Is emitted whenever a geometry change is done in the edit buffer.
QVector< QgsPointXY > shortestPath(const QgsTracerGraph &g, int v1, int v2)
Definition: qgstracer.cpp:182
void attributeValueChanged(QgsFeatureId fid, int idx, const QVariant &value)
Is 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.
Simple graph structure for shortest path search.
Definition: qgstracer.cpp:94
Abstract base class for all geometries.
Contains information about the context in which a coordinate transform is executed.
double distance(double x, double y) const
Returns the distance between this point and a specified x, y coordinate.
Definition: qgspointxy.h:190
double x
Definition: qgspointxy.h:47
void resetGraph(QgsTracerGraph &g)
Definition: qgstracer.cpp:381
double closestSegment(const QgsPolylineXY &pl, const QgsPointXY &pt, int &vertexAfter, double epsilon)
Definition: qgstracer.cpp:68
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry)
Creates and returns a new geometry engine.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
double distance2D(const QgsPolylineXY &coords)
Definition: qgstracer.cpp:46
QgsTracer()
Constructor for QgsTracer.
QVector< V > v
Vertices of the graph.
Definition: qgstracer.cpp:118
QVector< QgsPointXY > QgsPolylineXY
Polyline as represented as a vector of two-dimensional points.
Definition: qgsgeometry.h:44
Contains information about the context of a rendering operation.
QVector< int > edges
indices of adjacent edges (used in Dijkstra algorithm)
Definition: qgstracer.cpp:114
static geos::unique_ptr asGeos(const QgsGeometry &geometry, double precision=0)
Returns a geos geometry - caller takes ownership of the object (should be deleted with GEOSGeom_destr...
Definition: qgsgeos.cpp:166
static bool isCurvedType(Type type)
Returns true if the WKB type is a curved type or can contain curved geometries.
Definition: qgswkbtypes.h:608
void invalidateGraph()
Destroy the existing graph structure if any (de-initialize)
Definition: qgstracer.cpp:678
void extractLinework(const QgsGeometry &g, QgsMultiPolylineXY &mpl)
Definition: qgstracer.cpp:415
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:43
QgsPointXY pt
location of the vertex
Definition: qgstracer.cpp:112
This class represents a coordinate reference system (CRS).
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.
Definition: qgstracer.cpp:727
void dataChanged()
Data of layer changed.
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.
bool init()
Build the internal data structures.
Definition: qgstracer.cpp:666
void offsetParameters(int &quadSegments, int &joinStyle, double &miterLimit)
Gets extra parameters for offset curve algorithm (used when offset is non-zero)
Definition: qgstracer.cpp:652
QgsPolylineXY asPolyline() const
Returns the contents of the geometry as a polyline.
QgsGeometry geometry
Definition: qgsfeature.h:67
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
bool nextFeature(QgsFeature &f)
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...
Definition: qgstracer.cpp:794
static QgsGeometry geometryFromGeos(GEOSGeometry *geos)
Creates a new QgsGeometry object, feeding in a geometry in GEOS format.
Definition: qgsgeos.cpp:153
int otherVertex(int v0) const
Definition: qgstracer.cpp:105
bool operator()(DijkstraQueueItem a, DijkstraQueueItem b)
Definition: qgstracer.cpp:38
Represents a vector layer which manages a vector based data sets.
static Type flatType(Type type)
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:429
virtual QgsAbstractGeometry * segmentize(double tolerance=M_PI/180., SegmentationToleranceType toleranceType=MaximumAngle) const
Returns a version of the geometry without curves.
QgsMultiPolygonXY asMultiPolygon() const
Returns the contents of the geometry as a multi-polygon.
void setLayers(const QList< QgsVectorLayer *> &layers)
Sets layers used for tracing.
Definition: qgstracer.cpp:593
int v1
vertices that the edge connects
Definition: qgstracer.cpp:101