QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
qgsmaptoolcapture.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmaptoolcapture.cpp - map tool for capturing points, lines, polygons
3  ---------------------
4  begin : January 2006
5  copyright : (C) 2006 by Martin Dobias
6  email : wonder.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 "qgsmaptoolcapture.h"
17 #include "qgsexception.h"
18 #include "qgsfeatureiterator.h"
19 #include "qgsgeometryvalidator.h"
20 #include "qgslayertreeview.h"
21 #include "qgslinestring.h"
22 #include "qgslogger.h"
23 #include "qgsmapcanvas.h"
24 #include "qgsmapcanvastracer.h"
25 #include "qgsmapmouseevent.h"
26 #include "qgspolygon.h"
27 #include "qgsrubberband.h"
28 #include "qgssnapindicator.h"
29 #include "qgsvectorlayer.h"
30 #include "qgsvertexmarker.h"
32 #include "qgsapplication.h"
34 #include "qgsproject.h"
35 #include "qgsgeometryrubberband.h"
36 
37 #include <QAction>
38 #include <QCursor>
39 #include <QPixmap>
40 #include <QStatusBar>
41 
42 
44 
46  : QgsMapToolAdvancedDigitizing( canvas, cadDockWidget )
47  , mCaptureMode( mode )
48  , mCaptureModeFromLayer( mode == CaptureNone )
49 {
50  mSnapIndicator.reset( new QgsSnapIndicator( canvas ) );
51 
52  setCursor( QgsApplication::getThemeCursor( QgsApplication::Cursor::CapturePoint ) );
53 
54  connect( canvas, &QgsMapCanvas::currentLayerChanged,
55  this, &QgsMapToolCapture::currentLayerChanged );
56 
57  QgsVectorLayer::LayerOptions layerOptions;
58  layerOptions.skipCrsValidation = true;
59  layerOptions.loadDefaultStyle = false;
60  mExtraSnapLayer = new QgsVectorLayer( QStringLiteral( "LineString?crs=" ), QStringLiteral( "extra snap" ), QStringLiteral( "memory" ), layerOptions );
61  mExtraSnapLayer->startEditing();
62  QgsFeature f;
63  mExtraSnapLayer->addFeature( f );
64  mExtraSnapFeatureId = f.id();
65 
67  this, &QgsMapToolCapture::updateExtraSnapLayer );
68 
69  currentLayerChanged( canvas->currentLayer() );
70 }
71 
73 {
74  // during tear down we have to clean up mExtraSnapLayer first, before
75  // we call stop capturing. Otherwise stopCapturing tries to access members
76  // from the mapcanvas, which is likely already being destroyed and triggering
77  // the deletion of this object...
78  mCanvas->snappingUtils()->removeExtraSnapLayer( mExtraSnapLayer );
79  mExtraSnapLayer->deleteLater();
80  mExtraSnapLayer = nullptr;
81 
82  stopCapturing();
83 
84  if ( mValidator )
85  {
86  mValidator->deleteLater();
87  mValidator = nullptr;
88  }
89 }
90 
91 QgsMapToolCapture::Capabilities QgsMapToolCapture::capabilities() const
92 {
94 }
95 
97 {
98  return technique == StraightSegments;
99 }
100 
102 {
103  if ( mTempRubberBand )
104  mTempRubberBand->show();
105 
106  mCanvas->snappingUtils()->addExtraSnapLayer( mExtraSnapLayer );
108 }
109 
111 {
112  if ( mTempRubberBand )
113  mTempRubberBand->hide();
114 
115  mSnapIndicator->setMatch( QgsPointLocator::Match() );
116 
117  mCanvas->snappingUtils()->removeExtraSnapLayer( mExtraSnapLayer );
119 }
120 
121 void QgsMapToolCapture::currentLayerChanged( QgsMapLayer *layer )
122 {
123  if ( !mCaptureModeFromLayer )
124  return;
125 
126  mCaptureMode = CaptureNone;
127 
128  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
129  if ( !vlayer )
130  {
131  return;
132  }
133 
134  switch ( vlayer->geometryType() )
135  {
137  mCaptureMode = CapturePoint;
138  break;
140  mCaptureMode = CaptureLine;
141  break;
143  mCaptureMode = CapturePolygon;
144  break;
145  default:
146  mCaptureMode = CaptureNone;
147  break;
148  }
149 
150  if ( mTempRubberBand )
151  mTempRubberBand->setRubberBandGeometryType( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry );
152 
153  resetRubberBand();
154  cadDockWidget()->switchZM();
155 }
156 
157 
158 bool QgsMapToolCapture::tracingEnabled()
159 {
161  return tracer && ( !tracer->actionEnableTracing() || tracer->actionEnableTracing()->isChecked() )
162  && ( !tracer->actionEnableSnapping() || tracer->actionEnableSnapping()->isChecked() );
163 }
164 
165 
166 QgsPointXY QgsMapToolCapture::tracingStartPoint()
167 {
168  // if we have starting point from previous trace, then preferably use that one
169  // (useful when tracing with offset)
170  if ( mTracingStartPoint != QgsPointXY() )
171  return mTracingStartPoint;
172 
173  return lastCapturedMapPoint();
174 }
175 
176 
177 bool QgsMapToolCapture::tracingMouseMove( QgsMapMouseEvent *e )
178 {
179  if ( !e->isSnapped() )
180  return false;
181 
182  QgsPointXY pt0 = tracingStartPoint();
183  if ( pt0 == QgsPointXY() )
184  return false;
185 
187  if ( !tracer )
188  return false; // this should not happen!
189 
191  QVector<QgsPointXY> points = tracer->findShortestPath( pt0, e->mapPoint(), &err );
192  if ( points.isEmpty() )
193  {
194  tracer->reportError( err, false );
195  return false;
196  }
197 
198  mTempRubberBand->reset( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry, QgsWkbTypes::LineString, firstCapturedMapPoint() );
199  mTempRubberBand->addPoint( lastCapturedMapPoint() );
200 
201  // if there is offset, we need to fix the rubber bands to make sure they are aligned correctly.
202  // There are two cases we need to sort out:
203  // 1. the last point of mRubberBand may need to be moved off the traced curve to respect the offset
204  // 2. first point of mTempRubberBand may be needed to be moved to the beginning of the offset trace
205  const QgsPoint lastPoint = lastCapturedMapPoint();
206  QgsPointXY lastPointXY( lastPoint );
207  if ( lastPointXY == pt0 && points[0] != lastPointXY )
208  {
209  if ( mRubberBand->numberOfVertices() != 0 )
210  {
211  // if rubber band had just one point, for some strange reason it contains the point twice
212  // we only want to move the last point if there are multiple points already
213  if ( mRubberBand->numberOfVertices() > 2 || ( mRubberBand->numberOfVertices() == 2 && *mRubberBand->getPoint( 0, 0 ) != *mRubberBand->getPoint( 0, 1 ) ) )
214  mRubberBand->movePoint( points[0] );
215  }
216 
217  mTempRubberBand->movePoint( 0, QgsPoint( points[0] ) );
218  }
219 
220  mTempRubberBand->movePoint( QgsPoint( points[0] ) );
221 
222  // update temporary rubberband
223  for ( int i = 1; i < points.count(); ++i ) //points added in the rubber band are 2D but will not be added to the capture curve
224  mTempRubberBand->addPoint( QgsPoint( points.at( i ) ), i == points.count() - 1 );
225 
226 
227  mTempRubberBand->addPoint( QgsPoint( points[points.size() - 1] ) );
228 
229  tracer->reportError( QgsTracer::ErrNone, false ); // clear messagebar if there was any error
230  return true;
231 }
232 
233 
234 bool QgsMapToolCapture::tracingAddVertex( const QgsPointXY &point )
235 {
237  if ( !tracer )
238  return false; // this should not happen!
239 
240  if ( mTempRubberBand->pointsCount() == 0 )
241  {
242  if ( !tracer->init() )
243  {
245  return false;
246  }
247 
248  // only accept first point if it is snapped to the graph (to vertex or edge)
249  const bool res = tracer->isPointSnapped( point );
250  if ( res )
251  {
252  mTracingStartPoint = point;
253  }
254  return false;
255  }
256 
257  QgsPointXY pt0 = tracingStartPoint();
258  if ( pt0 == QgsPointXY() )
259  return false;
260 
262  QVector<QgsPointXY> points = tracer->findShortestPath( pt0, point, &err );
263  if ( points.isEmpty() )
264  return false; // ignore the vertex - can't find path to the end point!
265 
266  // transform points
267  QgsPointSequence layerPoints;
268  QgsPoint lp; // in layer coords
269  for ( int i = 0; i < points.count(); ++i )
270  {
271  if ( nextPoint( QgsPoint( points[i] ), lp ) != 0 )
272  return false;
273  layerPoints << lp;
274  }
275 
276  // Move the last point of the captured curve to the first point on the trace string (necessary if there is offset)
277  const QgsVertexId lastVertexId( 0, 0, mCaptureCurve.numPoints() - 1 );
278  mCaptureCurve.moveVertex( lastVertexId, layerPoints.first() );
279  mSnappingMatches.removeLast();
280  mSnappingMatches.append( QgsPointLocator::Match() );
281 
282  int pointBefore = mCaptureCurve.numPoints();
283  mCaptureCurve.addCurve( new QgsLineString( layerPoints ) );
284 
285  resetRubberBand();
286 
287  // Curves de-approximation
289  {
290  // If the tool and the layer support curves
291  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() );
292  if ( vlayer && capabilities().testFlag( QgsMapToolCapture::Capability::SupportsCurves ) && vlayer->dataProvider()->capabilities().testFlag( QgsVectorDataProvider::Capability::CircularGeometries ) )
293  {
294  const QgsGeometry linear = QgsGeometry( mCaptureCurve.segmentize() );
295  const QgsGeometry curved = linear.convertToCurves(
298  );
299  mCaptureCurve = *qgsgeometry_cast<QgsCompoundCurve *>( curved.constGet() );
300  }
301  }
302 
303  // sync the snapping matches list
304  const int pointAfter = mCaptureCurve.numPoints();
305  for ( ; pointBefore < pointAfter; ++pointBefore )
306  mSnappingMatches.append( QgsPointLocator::Match() );
307 
308  tracer->reportError( QgsTracer::ErrNone, true ); // clear messagebar if there was any error
309 
310  // adjust last captured point
311  const QgsPoint lastPt = mCaptureCurve.endPoint();
312  mCaptureLastPoint = toMapCoordinates( layer(), lastPt );
313 
314  return true;
315 }
316 
317 QgsMapToolCaptureRubberBand *QgsMapToolCapture::createCurveRubberBand() const
318 {
319  QgsMapToolCaptureRubberBand *rb = new QgsMapToolCaptureRubberBand( mCanvas );
320  rb->setStrokeWidth( digitizingStrokeWidth() );
321  QColor color = digitizingStrokeColor();
322 
324  color.setAlphaF( color.alphaF() * alphaScale );
325  rb->setLineStyle( Qt::DotLine );
326  rb->setStrokeColor( color );
327 
328  const QColor fillColor = digitizingFillColor();
329  rb->setFillColor( fillColor );
330  rb->show();
331  return rb;
332 }
333 
334 QgsPoint QgsMapToolCapture::firstCapturedMapPoint()
335 {
336  return mCaptureFirstPoint;
337 }
338 
339 QgsPoint QgsMapToolCapture::lastCapturedMapPoint()
340 {
341  return mCaptureLastPoint;
342 }
343 
344 void QgsMapToolCapture::resetRubberBand()
345 {
346  if ( !mRubberBand )
347  return;
348  QgsLineString *lineString = mCaptureCurve.curveToLine();
350  mRubberBand->addGeometry( QgsGeometry( lineString ), layer() );
351 }
352 
354 {
355  return mRubberBand.release();
356 }
357 
359 {
360  mDigitizingType = enable ? QgsWkbTypes::CircularString : QgsWkbTypes::LineString;
361  if ( mTempRubberBand )
362  mTempRubberBand->setStringType( mDigitizingType );
363 }
364 
366 {
367  mStreamingEnabled = enable;
368  mStartNewCurve = true;
369  if ( enable )
370  {
372  }
373 }
374 
376 {
378  const QgsPointXY point = e->mapPoint();
379 
380  mSnapIndicator->setMatch( e->mapPointMatch() );
381 
382  const QgsPoint mapPoint = QgsPoint( point );
383 
384  if ( mCaptureMode != CapturePoint && mTempRubberBand && mCapturing )
385  {
386  bool hasTrace = false;
387 
388  if ( mStreamingEnabled )
389  {
390  if ( !mCaptureCurve.isEmpty() )
391  {
392  const QgsPoint prevPoint = mCaptureCurve.curveAt( mCaptureCurve.nCurves() - 1 )->endPoint();
393  if ( QgsPointXY( toCanvasCoordinates( toMapCoordinates( layer(), prevPoint ) ) ).distance( toCanvasCoordinates( point ) ) < mStreamingToleranceInPixels )
394  return;
395  }
396 
397  mAllowAddingStreamingPoints = true;
398  addVertex( mapPoint );
399  mAllowAddingStreamingPoints = false;
400  }
401  else if ( tracingEnabled() && mCaptureCurve.numPoints() != 0 )
402  {
403  // Store the intermediate point for circular string to retrieve after tracing mouse move if
404  // the digitizing type is circular and the temp rubber band is effectivly circular and if this point is existing
405  // Store an empty point if the digitizing type is linear ot the point is not existing (curve not complete)
406  if ( mDigitizingType == QgsWkbTypes::CircularString &&
407  mTempRubberBand->stringType() == QgsWkbTypes::CircularString &&
408  mTempRubberBand->curveIsComplete() )
409  mCircularItermediatePoint = mTempRubberBand->pointFromEnd( 1 );
410  else if ( mDigitizingType == QgsWkbTypes::LineString ||
411  !mTempRubberBand->curveIsComplete() )
412  mCircularItermediatePoint = QgsPoint();
413 
414  hasTrace = tracingMouseMove( e );
415 
416  if ( !hasTrace )
417  {
418  // Restore the temp rubber band
419  mTempRubberBand->reset( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry, mDigitizingType, firstCapturedMapPoint() );
420  mTempRubberBand->addPoint( lastCapturedMapPoint() );
421  if ( !mCircularItermediatePoint.isEmpty() )
422  {
423  mTempRubberBand->movePoint( mCircularItermediatePoint );
424  mTempRubberBand->addPoint( mCircularItermediatePoint );
425  }
426  }
427  }
428 
429  if ( !mStreamingEnabled && !hasTrace )
430  {
431  if ( mCaptureCurve.numPoints() > 0 )
432  {
433  const QgsPoint mapPt = lastCapturedMapPoint();
434 
435  if ( mTempRubberBand )
436  {
437  mTempRubberBand->movePoint( mapPoint );
438  mTempRubberBand->movePoint( 0, mapPt );
439  }
440 
441  // fix existing rubber band after tracing - the last point may have been moved if using offset
442  if ( mRubberBand->numberOfVertices() )
443  mRubberBand->movePoint( mapPt );
444  }
445  else if ( mTempRubberBand )
446  mTempRubberBand->movePoint( mapPoint );
447  }
448  }
449 } // mouseMoveEvent
450 
451 
452 int QgsMapToolCapture::nextPoint( const QgsPoint &mapPoint, QgsPoint &layerPoint )
453 {
454  if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() ) )
455  {
456  try
457  {
458  const QgsPointXY mapP( mapPoint.x(), mapPoint.y() ); //#spellok
459  layerPoint = QgsPoint( toLayerCoordinates( vlayer, mapP ) ); //transform snapped point back to layer crs //#spellok
460  if ( QgsWkbTypes::hasZ( vlayer->wkbType() ) && !layerPoint.is3D() )
462  if ( QgsWkbTypes::hasM( vlayer->wkbType() ) && !layerPoint.isMeasure() )
464  }
465  catch ( QgsCsException & )
466  {
467  QgsDebugMsg( QStringLiteral( "transformation to layer coordinate failed" ) );
468  return 2;
469  }
470  }
471  else
472  {
473  layerPoint = QgsPoint( toLayerCoordinates( layer(), mapPoint ) );
474  }
475 
476  return 0;
477 }
478 
479 int QgsMapToolCapture::nextPoint( QPoint p, QgsPoint &layerPoint, QgsPoint &mapPoint )
480 {
482  return nextPoint( mapPoint, layerPoint );
483 }
484 
486 {
487  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() );
488  QgsVectorLayer *sourceLayer = match.layer();
490  {
492  return 0;
493  }
494  else if ( !vlayer )
495  {
496  return 1;
497  }
498  else
499  {
500  if ( match.isValid() && ( match.hasVertex() || match.hasLineEndpoint() || ( QgsProject::instance()->topologicalEditing() && ( match.hasEdge() || match.hasMiddleSegment() ) ) ) && sourceLayer &&
501  ( sourceLayer->crs() == vlayer->crs() ) )
502  {
503  QgsFeature f;
504  QgsFeatureRequest request;
505  request.setFilterFid( match.featureId() );
506  const bool fetched = match.layer()->getFeatures( request ).nextFeature( f );
507  if ( fetched )
508  {
509  QgsVertexId vId;
510  if ( !f.geometry().vertexIdFromVertexNr( match.vertexIndex(), vId ) )
511  return 2;
512 
513  const QgsGeometry geom( f.geometry() );
514  if ( QgsProject::instance()->topologicalEditing() && ( match.hasEdge() || match.hasMiddleSegment() ) )
515  {
516  QgsVertexId vId2;
517  if ( !f.geometry().vertexIdFromVertexNr( match.vertexIndex() + 1, vId2 ) )
518  return 2;
519  const QgsLineString line( geom.constGet()->vertexAt( vId ), geom.constGet()->vertexAt( vId2 ) );
520 
521  layerPoint = QgsGeometryUtils::closestPoint( line, QgsPoint( match.point() ) );
522  }
523  else
524  {
525  layerPoint = geom.constGet()->vertexAt( vId );
526  if ( QgsWkbTypes::hasZ( vlayer->wkbType() ) && !layerPoint.is3D() )
527  layerPoint.addZValue( defaultZValue() );
528  if ( QgsWkbTypes::hasM( vlayer->wkbType() ) && !layerPoint.isMeasure() )
529  layerPoint.addMValue( defaultMValue() );
530  }
531 
532  // ZM support depends on the target layer
533  if ( !QgsWkbTypes::hasZ( vlayer->wkbType() ) )
534  {
535  layerPoint.dropZValue();
536  }
537 
538  if ( !QgsWkbTypes::hasM( vlayer->wkbType() ) )
539  {
540  layerPoint.dropMValue();
541  }
542 
543  return 0;
544  }
545  else
546  {
547  return 2;
548  }
549  }
550  else
551  {
552  return 1;
553  }
554  }
555 }
556 
557 int QgsMapToolCapture::addVertex( const QgsPointXY &point )
558 {
559  return addVertex( point, QgsPointLocator::Match() );
560 }
561 
562 int QgsMapToolCapture::addVertex( const QgsPointXY &point, const QgsPointLocator::Match &match )
563 {
564  if ( mode() == CaptureNone )
565  {
566  QgsDebugMsg( QStringLiteral( "invalid capture mode" ) );
567  return 2;
568  }
569 
570  if ( mCapturing && mStreamingEnabled && !mAllowAddingStreamingPoints )
571  return 0;
572 
573  QgsPoint layerPoint;
574  if ( layer() )
575  {
576  int res = fetchLayerPoint( match, layerPoint );
577  if ( res != 0 )
578  {
579  res = nextPoint( QgsPoint( point ), layerPoint );
580  if ( res != 0 )
581  {
582  return res;
583  }
584  }
585  }
586  const QgsPoint mapPoint = toMapCoordinates( layer(), layerPoint );
587 
588  if ( mCaptureMode == CapturePoint )
589  {
590  mCaptureCurve.addVertex( layerPoint );
591  mSnappingMatches.append( match );
592  }
593  else
594  {
595  if ( mCaptureFirstPoint.isEmpty() )
596  {
597  mCaptureFirstPoint = mapPoint;
598  }
599 
600  if ( !mRubberBand )
602 
603  if ( !mTempRubberBand )
604  {
605  mTempRubberBand.reset( createCurveRubberBand() );
606  mTempRubberBand->setStringType( mDigitizingType );
607  mTempRubberBand->reset( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry, mDigitizingType, mapPoint );
608  }
609 
610  bool traceCreated = false;
611  if ( tracingEnabled() )
612  {
613  traceCreated = tracingAddVertex( mapPoint );
614  }
615 
616  // keep new tracing start point if we created a trace. This is useful when tracing with
617  // offset so that the user stays "snapped"
618  mTracingStartPoint = traceCreated ? point : QgsPointXY();
619 
620  if ( !traceCreated )
621  {
622  // ordinary digitizing
623  mTempRubberBand->movePoint( mapPoint ); //move the last point of the temp rubberband before operating with it
624  if ( mTempRubberBand->curveIsComplete() ) //2 points for line and 3 points for circular
625  {
626  const QgsCurve *curve = mTempRubberBand->curve();
627  if ( curve )
628  {
629  addCurve( curve->clone() );
630  // add curve append only invalid match to mSnappingMatches,
631  // so we need to remove them and add the one from here if it is valid
632  if ( match.isValid() && mSnappingMatches.count() > 0 && !mSnappingMatches.last().isValid() )
633  {
634  mSnappingMatches.removeLast();
635  if ( mTempRubberBand->stringType() == QgsWkbTypes::CircularString )
636  {
637  // for circular string two points are added and match for intermediate point is stored
638  mSnappingMatches.removeLast();
639  mSnappingMatches.append( mCircularIntermediateMatch );
640  }
641  mSnappingMatches.append( match );
642  }
643  }
644  mCaptureLastPoint = mapPoint;
645  mTempRubberBand->reset( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry, mDigitizingType, firstCapturedMapPoint() );
646  }
647  else if ( mTempRubberBand->pointsCount() == 0 )
648  {
649  mCaptureLastPoint = mapPoint;
650  mCaptureCurve.addVertex( layerPoint );
651  mSnappingMatches.append( match );
652  }
653  else
654  {
655  if ( mTempRubberBand->stringType() == QgsWkbTypes::CircularString )
656  {
657  mCircularIntermediateMatch = match;
658  }
659  }
660 
661  mTempRubberBand->addPoint( mapPoint );
662  }
663  else
664  {
665  mTempRubberBand->reset( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry, mDigitizingType, firstCapturedMapPoint() );
666  mTempRubberBand->addPoint( lastCapturedMapPoint() );
667  }
668  }
669 
670  updateExtraSnapLayer();
671  validateGeometry();
672 
673  return 0;
674 }
675 
677 {
678  if ( !c )
679  {
680  return 1;
681  }
682 
683  if ( !mRubberBand )
684  {
686  }
687 
688  if ( mTempRubberBand )
689  {
690  mTempRubberBand->reset( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry, mDigitizingType, firstCapturedMapPoint() );
691  const QgsPoint endPt = c->endPoint();
692  mTempRubberBand->addPoint( endPt ); //add last point of c
693  }
694 
695  //transform back to layer CRS in case map CRS and layer CRS are different
697  if ( ct.isValid() )
698  {
699  c->transform( ct, Qgis::TransformDirection::Reverse );
700  }
701  const int countBefore = mCaptureCurve.vertexCount();
702  //if there is only one point, this the first digitized point that are in the this first curve added --> remove the point
703  if ( mCaptureCurve.numPoints() == 1 )
704  mCaptureCurve.removeCurve( 0 );
705 
706  // we set the extendPrevious option to true to avoid creating compound curves with many 2 vertex linestrings -- instead we prefer
707  // to extend linestring curves so that they continue the previous linestring wherever possible...
708  mCaptureCurve.addCurve( c, !mStartNewCurve );
709  mStartNewCurve = false;
710 
711  const int countAfter = mCaptureCurve.vertexCount();
712  const int addedPoint = countAfter - countBefore;
713 
714  updateExtraSnapLayer();
715 
716  for ( int i = 0; i < addedPoint; ++i )
717  mSnappingMatches.append( QgsPointLocator::Match() );
718 
719  resetRubberBand();
720 
721  return 0;
722 }
723 
725 {
726  mCaptureCurve.clear();
727  updateExtraSnapLayer();
728 }
729 
730 QList<QgsPointLocator::Match> QgsMapToolCapture::snappingMatches() const
731 {
732  return mSnappingMatches;
733 }
734 
735 void QgsMapToolCapture::undo( bool isAutoRepeat )
736 {
737  mTracingStartPoint = QgsPointXY();
738 
739  if ( mTempRubberBand )
740  {
741  if ( size() <= 1 && mTempRubberBand->pointsCount() != 0 )
742  return;
743 
744  if ( isAutoRepeat && mIgnoreSubsequentAutoRepeatUndo )
745  return;
746  mIgnoreSubsequentAutoRepeatUndo = false;
747 
748  const QgsPoint lastPoint = mTempRubberBand->lastPoint();
749 
750  if ( mTempRubberBand->stringType() == QgsWkbTypes::CircularString && mTempRubberBand->pointsCount() > 2 )
751  {
752  mTempRubberBand->removeLastPoint();
753  mTempRubberBand->movePoint( lastPoint );
754  return;
755  }
756 
757  QgsVertexId vertexToRemove;
758  vertexToRemove.part = 0;
759  vertexToRemove.ring = 0;
760  vertexToRemove.vertex = size() - 1;
761  if ( mCaptureCurve.numPoints() == 2 && mCaptureCurve.nCurves() == 1 )
762  {
763  // store the first vertex to restore if after deleting the curve
764  // because when only two vertices, removing a point remove all the curve
765  const QgsPoint fp = mCaptureCurve.startPoint();
766  mCaptureCurve.deleteVertex( vertexToRemove );
767  mCaptureCurve.addVertex( fp );
768  }
769  else
770  {
771  const int curvesBefore = mCaptureCurve.nCurves();
772  const bool lastCurveIsLineString = qgsgeometry_cast< QgsLineString * >( mCaptureCurve.curveAt( curvesBefore - 1 ) );
773 
774  const int pointsCountBefore = mCaptureCurve.numPoints();
775  mCaptureCurve.deleteVertex( vertexToRemove );
776  int pointsCountAfter = mCaptureCurve.numPoints();
777  for ( ; pointsCountAfter < pointsCountBefore; pointsCountAfter++ )
778  if ( !mSnappingMatches.empty() )
779  mSnappingMatches.removeLast();
780 
781  // if we have removed the last point in a linestring curve, then we "stick" here and ignore subsequent
782  // autorepeat undo actions until the user releases the undo key and holds it down again. This allows
783  // users to selectively remove portions of the geometry captured with the streaming mode by holding down
784  // the undo key, without risking accidental undo of non-streamed portions.
785  if ( mCaptureCurve.nCurves() < curvesBefore && lastCurveIsLineString )
786  mIgnoreSubsequentAutoRepeatUndo = true;
787  }
788 
789  updateExtraSnapLayer();
790 
791  resetRubberBand();
792 
793  mTempRubberBand->reset( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry, mDigitizingType, firstCapturedMapPoint() );
794 
795  if ( mCaptureCurve.numPoints() > 0 )
796  {
797  const QgsPoint lastPt = mCaptureCurve.endPoint();
798  mCaptureLastPoint = toMapCoordinates( layer(), lastPt );
799  mTempRubberBand->addPoint( lastCapturedMapPoint() );
800  mTempRubberBand->movePoint( lastPoint );
801  }
802 
804  validateGeometry();
805  }
806 }
807 
808 void QgsMapToolCapture::keyPressEvent( QKeyEvent *e )
809 {
810  if ( e->key() == Qt::Key_Backspace || e->key() == Qt::Key_Delete )
811  {
812  undo( e->isAutoRepeat() );
813 
814  // Override default shortcut management in MapCanvas
815  e->ignore();
816  }
817  else if ( e->key() == Qt::Key_Escape )
818  {
819  stopCapturing();
820 
821  // Override default shortcut management in MapCanvas
822  e->ignore();
823  }
824 }
825 
827 {
828  mCapturing = true;
829 }
830 
832 {
833  return mCapturing;
834 }
835 
837 {
838  mRubberBand.reset();
839 
841 
842  qDeleteAll( mGeomErrorMarkers );
843  mGeomErrorMarkers.clear();
844  mGeomErrors.clear();
845 
846  mCaptureFirstPoint = QgsPoint();
847  mCaptureLastPoint = QgsPoint();
848 
849  mTracingStartPoint = QgsPointXY();
850 
851  mCapturing = false;
852  mCaptureCurve.clear();
853  updateExtraSnapLayer();
854  mSnappingMatches.clear();
855  if ( auto *lCurrentVectorLayer = currentVectorLayer() )
856  lCurrentVectorLayer->triggerRepaint();
857 }
858 
860 {
861  mTempRubberBand.reset();
862 }
863 
865 {
866  stopCapturing();
867  clearCurve();
868 }
869 
871 {
872  mCaptureCurve.close();
873  updateExtraSnapLayer();
874 }
875 
876 void QgsMapToolCapture::validateGeometry()
877 {
879  || !( capabilities() & ValidateGeometries )
880  )
881  return;
882 
883  if ( mValidator )
884  {
885  mValidator->deleteLater();
886  mValidator = nullptr;
887  }
888 
889  mGeomErrors.clear();
890  while ( !mGeomErrorMarkers.isEmpty() )
891  {
892  delete mGeomErrorMarkers.takeFirst();
893  }
894 
895  QgsGeometry geom;
896 
897  switch ( mCaptureMode )
898  {
899  case CaptureNone:
900  case CapturePoint:
901  return;
902  case CaptureLine:
903  if ( size() < 2 )
904  return;
905  geom = QgsGeometry( mCaptureCurve.curveToLine() );
906  break;
907  case CapturePolygon:
908  if ( size() < 3 )
909  return;
910  QgsLineString *exteriorRing = mCaptureCurve.curveToLine();
911  exteriorRing->close();
912  QgsPolygon *polygon = new QgsPolygon();
913  polygon->setExteriorRing( exteriorRing );
914  geom = QgsGeometry( polygon );
915  break;
916  }
917 
918  if ( geom.isNull() )
919  return;
920 
921  Qgis::GeometryValidationEngine method = Qgis::GeometryValidationEngine::QgisInternal;
923  method = Qgis::GeometryValidationEngine::Geos;
924  mValidator = new QgsGeometryValidator( geom, nullptr, method );
925  connect( mValidator, &QgsGeometryValidator::errorFound, this, &QgsMapToolCapture::addError );
926  mValidator->start();
927  QgsDebugMsgLevel( QStringLiteral( "Validation started" ), 4 );
928 }
929 
930 void QgsMapToolCapture::addError( const QgsGeometry::Error &e )
931 {
932  mGeomErrors << e;
933  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() );
934  if ( !vlayer )
935  return;
936 
937  if ( e.hasWhere() )
938  {
940  vm->setCenter( mCanvas->mapSettings().layerToMapCoordinates( vlayer, e.where() ) );
942  vm->setPenWidth( 2 );
943  vm->setToolTip( e.what() );
944  vm->setColor( Qt::green );
945  vm->setZValue( vm->zValue() + 1 );
946  mGeomErrorMarkers << vm;
947  }
948 }
949 
951 {
952  return mCaptureCurve.numPoints();
953 }
954 
955 QVector<QgsPointXY> QgsMapToolCapture::points() const
956 {
957  QVector<QgsPointXY> pointsXY;
959 
960  return pointsXY;
961 }
962 
964 {
965  QgsPointSequence pts;
966  mCaptureCurve.points( pts );
967  return pts;
968 }
969 
970 void QgsMapToolCapture::setPoints( const QVector<QgsPointXY> &pointList )
971 {
972  QgsLineString *line = new QgsLineString( pointList );
973  mCaptureCurve.clear();
974  mCaptureCurve.addCurve( line );
975  updateExtraSnapLayer();
976  mSnappingMatches.clear();
977  for ( int i = 0; i < line->length(); ++i )
978  mSnappingMatches.append( QgsPointLocator::Match() );
979  resetRubberBand();
980 }
981 
982 void QgsMapToolCapture::setPoints( const QgsPointSequence &pointList )
983 {
984  QgsLineString *line = new QgsLineString( pointList );
985  mCaptureCurve.clear();
986  mCaptureCurve.addCurve( line );
987  updateExtraSnapLayer();
988  mSnappingMatches.clear();
989  for ( int i = 0; i < line->length(); ++i )
990  mSnappingMatches.append( QgsPointLocator::Match() );
991  resetRubberBand();
992 }
993 
994 QgsPoint QgsMapToolCapture::mapPoint( const QgsPointXY &point ) const
995 {
996  QgsPoint newPoint( QgsWkbTypes::Point, point.x(), point.y() );
997 
998  // get current layer
999  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() );
1000  if ( !vlayer )
1001  {
1002  return newPoint;
1003  }
1004 
1005  // convert to the corresponding type for a full ZM support
1006  const QgsWkbTypes::Type type = vlayer->wkbType();
1007  if ( QgsWkbTypes::hasZ( type ) && !QgsWkbTypes::hasM( type ) )
1008  {
1009  newPoint.convertTo( QgsWkbTypes::PointZ );
1010  }
1011  else if ( !QgsWkbTypes::hasZ( type ) && QgsWkbTypes::hasM( type ) )
1012  {
1013  newPoint.convertTo( QgsWkbTypes::PointM );
1014  }
1015  else if ( QgsWkbTypes::hasZ( type ) && QgsWkbTypes::hasM( type ) )
1016  {
1017  newPoint.convertTo( QgsWkbTypes::PointZM );
1018  }
1019 
1020  // set z value if necessary
1021  if ( QgsWkbTypes::hasZ( newPoint.wkbType() ) )
1022  {
1024  }
1025  // set m value if necessary
1026  if ( QgsWkbTypes::hasM( newPoint.wkbType() ) )
1027  {
1029  }
1030  return newPoint;
1031 }
1032 
1034 {
1035  QgsPoint newPoint = mapPoint( e.mapPoint() );
1036 
1037  // set z or m value from snapped point if necessary
1038  if ( QgsWkbTypes::hasZ( newPoint.wkbType() ) || QgsWkbTypes::hasM( newPoint.wkbType() ) )
1039  {
1040  // if snapped, z and m dimension are taken from the corresponding snapped
1041  // point.
1042  if ( e.isSnapped() )
1043  {
1044  const QgsPointLocator::Match match = e.mapPointMatch();
1045 
1046  if ( match.layer() )
1047  {
1048  const QgsFeature ft = match.layer()->getFeature( match.featureId() );
1049  if ( QgsWkbTypes::hasZ( match.layer()->wkbType() ) )
1050  {
1051  newPoint.setZ( ft.geometry().vertexAt( match.vertexIndex() ).z() );
1052  }
1053  if ( QgsWkbTypes::hasM( match.layer()->wkbType() ) )
1054  {
1055  newPoint.setM( ft.geometry().vertexAt( match.vertexIndex() ).m() );
1056  }
1057  }
1058  }
1059  }
1060 
1061  return newPoint;
1062 }
1063 
1064 void QgsMapToolCapture::updateExtraSnapLayer()
1065 {
1066  if ( !mExtraSnapLayer )
1067  return;
1068 
1069  if ( canvas()->snappingUtils()->config().selfSnapping() && layer() && mCaptureCurve.numPoints() >= 2 )
1070  {
1071  // the current layer may have changed
1072  mExtraSnapLayer->setCrs( layer()->crs() );
1073  QgsGeometry geom = QgsGeometry( mCaptureCurve.clone() );
1074  // we close the curve to allow snapping on last segment
1075  if ( mCaptureMode == CapturePolygon && mCaptureCurve.numPoints() >= 3 )
1076  {
1077  qgsgeometry_cast<QgsCompoundCurve *>( geom.get() )->close();
1078  }
1079  mExtraSnapLayer->changeGeometry( mExtraSnapFeatureId, geom );
1080  }
1081  else
1082  {
1083  QgsGeometry geom;
1084  mExtraSnapLayer->changeGeometry( mExtraSnapFeatureId, geom );
1085  }
1086 }
1087 
1088 QgsMapToolCaptureRubberBand::QgsMapToolCaptureRubberBand( QgsMapCanvas *mapCanvas, QgsWkbTypes::GeometryType geomType ):
1089  QgsGeometryRubberBand( mapCanvas, geomType )
1090 {
1091  setVertexDrawingEnabled( false );
1092 }
1093 
1094 QgsCurve *QgsMapToolCaptureRubberBand::curve()
1095 {
1096  if ( mPoints.empty() )
1097  return nullptr;
1098 
1099  switch ( mStringType )
1100  {
1102  return new QgsLineString( mPoints ) ;
1103  break;
1105  if ( mPoints.count() != 3 )
1106  return nullptr;
1107  return new QgsCircularString(
1108  mPoints[0],
1109  mPoints[1],
1110  mPoints[2] ) ;
1111  break;
1112  default:
1113  return nullptr;
1114  }
1115 }
1116 
1117 bool QgsMapToolCaptureRubberBand::curveIsComplete() const
1118 {
1119  return ( mStringType == QgsWkbTypes::LineString && mPoints.count() > 1 ) ||
1120  ( mStringType == QgsWkbTypes::CircularString && mPoints.count() > 2 );
1121 }
1122 
1123 void QgsMapToolCaptureRubberBand::reset( QgsWkbTypes::GeometryType geomType, QgsWkbTypes::Type stringType, const QgsPoint &firstPolygonPoint )
1124 {
1125  if ( !( geomType == QgsWkbTypes::LineGeometry || geomType == QgsWkbTypes::PolygonGeometry ) )
1126  return;
1127 
1128  mPoints.clear();
1129  mFirstPolygonPoint = firstPolygonPoint;
1130  setStringType( stringType );
1131  setRubberBandGeometryType( geomType );
1132 }
1133 
1134 void QgsMapToolCaptureRubberBand::setRubberBandGeometryType( QgsWkbTypes::GeometryType geomType )
1135 {
1137  updateCurve();
1138 }
1139 
1140 void QgsMapToolCaptureRubberBand::addPoint( const QgsPoint &point, bool doUpdate )
1141 {
1142  if ( mPoints.count() == 0 )
1143  mPoints.append( point );
1144 
1145  mPoints.append( point );
1146 
1147  if ( doUpdate )
1148  updateCurve();
1149 }
1150 
1151 void QgsMapToolCaptureRubberBand::movePoint( const QgsPoint &point )
1152 {
1153  if ( mPoints.count() > 0 )
1154  mPoints.last() = point ;
1155 
1156  updateCurve();
1157 }
1158 
1159 void QgsMapToolCaptureRubberBand::movePoint( int index, const QgsPoint &point )
1160 {
1161  if ( mPoints.count() > 0 && mPoints.size() > index )
1162  mPoints[index] = point;
1163 
1164  updateCurve();
1165 }
1166 
1167 int QgsMapToolCaptureRubberBand::pointsCount()
1168 {
1169  return mPoints.size();
1170 }
1171 
1172 QgsWkbTypes::Type QgsMapToolCaptureRubberBand::stringType() const
1173 {
1174  return mStringType;
1175 }
1176 
1177 void QgsMapToolCaptureRubberBand::setStringType( const QgsWkbTypes::Type &type )
1178 {
1179  if ( ( type != QgsWkbTypes::CircularString && type != QgsWkbTypes::LineString ) || type == mStringType )
1180  return;
1181 
1182  mStringType = type;
1183  if ( type == QgsWkbTypes::LineString && mPoints.count() == 3 )
1184  {
1185  mPoints.removeAt( 1 );
1186  }
1187 
1188  setVertexDrawingEnabled( type == QgsWkbTypes::CircularString );
1189  updateCurve();
1190 }
1191 
1192 QgsPoint QgsMapToolCaptureRubberBand::lastPoint() const
1193 {
1194  if ( mPoints.empty() )
1195  return QgsPoint();
1196 
1197  return mPoints.last();
1198 }
1199 
1200 QgsPoint QgsMapToolCaptureRubberBand::pointFromEnd( int posFromEnd ) const
1201 {
1202  if ( posFromEnd < mPoints.size() )
1203  return mPoints.at( mPoints.size() - 1 - posFromEnd );
1204  else
1205  return QgsPoint();
1206 }
1207 
1208 void QgsMapToolCaptureRubberBand::removeLastPoint()
1209 {
1210  if ( mPoints.count() > 1 )
1211  mPoints.removeLast();
1212 
1213  updateCurve();
1214 }
1215 
1216 void QgsMapToolCaptureRubberBand::setGeometry( QgsAbstractGeometry *geom )
1217 {
1219 }
1220 
1221 void QgsMapToolCaptureRubberBand::updateCurve()
1222 {
1223  std::unique_ptr<QgsCurve> curve;
1224  switch ( mStringType )
1225  {
1227  curve.reset( createLinearString() );
1228  break;
1230  curve.reset( createCircularString() );
1231  break;
1232  default:
1233  return;
1234  break;
1235  }
1236 
1237  if ( geometryType() == QgsWkbTypes::PolygonGeometry )
1238  {
1239  std::unique_ptr<QgsCurvePolygon> geom( new QgsCurvePolygon );
1240  geom->setExteriorRing( curve.release() );
1241  setGeometry( geom.release() );
1242  }
1243  else
1244  {
1245  setGeometry( curve.release() );
1246  }
1247 }
1248 
1249 QgsCurve *QgsMapToolCaptureRubberBand::createLinearString()
1250 {
1251  std::unique_ptr<QgsLineString> curve( new QgsLineString );
1252  if ( geometryType() == QgsWkbTypes::PolygonGeometry )
1253  {
1254  QgsPointSequence points = mPoints;
1255  points.prepend( mFirstPolygonPoint );
1256  curve->setPoints( points );
1257  }
1258  else
1259  curve->setPoints( mPoints );
1260 
1261  return curve.release();
1262 }
1263 
1264 QgsCurve *QgsMapToolCaptureRubberBand::createCircularString()
1265 {
1266  std::unique_ptr<QgsCircularString> curve( new QgsCircularString );
1267  curve->setPoints( mPoints );
1268  if ( geometryType() == QgsWkbTypes::PolygonGeometry )
1269  {
1270  // add a linear string to close the polygon
1271  std::unique_ptr<QgsCompoundCurve> polygonCurve( new QgsCompoundCurve );
1272  polygonCurve->addVertex( mFirstPolygonPoint );
1273  if ( !mPoints.empty() )
1274  polygonCurve->addVertex( mPoints.first() );
1275  polygonCurve->addCurve( curve.release() );
1276  return polygonCurve.release();
1277  }
1278  else
1279  return curve.release();
1280 }
1281 
T * release()
Clears the pointer and returns it.
void reset(T *p=nullptr)
Will reset the managed pointer to p.
GeometryValidationEngine
Available engines for validating geometries.
Definition: qgis.h:687
Abstract base class for all geometries.
bool is3D() const SIP_HOLDGIL
Returns true if the geometry is 3D and contains a z-value.
QgsWkbTypes::Type wkbType() const SIP_HOLDGIL
Returns the WKB type of the geometry.
bool isMeasure() const SIP_HOLDGIL
Returns true if the geometry contains m values.
The QgsAdvancedDigitizingDockWidget class is a dockable widget used to handle the CAD tools on top of...
bool cadEnabled() const
determines if CAD tools are enabled or if map tools behaves "nomally"
void switchZM()
Determines if Z or M will be enabled.
double getLineM() const
Convenient method to get the M value from the line edit wiget.
void removePreviousPoint()
Remove previous point in the CAD point list.
double getLineZ() const
Convenient method to get the Z value from the line edit wiget.
QgsPoint currentPointV2(bool *exists=nullptr) const
The last point.
QgsPoint currentPointLayerCoordinates(QgsMapLayer *layer) const
Returns the last CAD point, in a map layer's coordinates.
static QCursor getThemeCursor(Cursor cursor)
Helper to get a theme cursor.
Circular string geometry type.
Compound curve geometry type.
QgsLineString * curveToLine(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const override
Returns a new line string geometry corresponding to a segmentized approximation of the curve.
void close()
Appends first point if not already closed.
QgsPoint endPoint() const override SIP_HOLDGIL
Returns the end point of the curve.
const QgsCurve * curveAt(int i) const SIP_HOLDGIL
Returns the curve at the specified index.
int numPoints() const override SIP_HOLDGIL
Returns the number of points in the curve.
void removeCurve(int i)
Removes a curve from the geometry.
void addCurve(QgsCurve *c, bool extendPrevious=false)
Adds a curve to the geometry (takes ownership).
bool moveVertex(QgsVertexId position, const QgsPoint &newPos) override
Moves a vertex within the geometry.
bool deleteVertex(QgsVertexId position) override
Deletes a vertex within the geometry.
QgsCompoundCurve * clone() const override
Clones the geometry by performing a deep copy.
void points(QgsPointSequence &pts) const override
Returns a list of points within the curve.
void clear() override
Clears the geometry, ie reset it to a null geometry.
QgsPoint startPoint() const override SIP_HOLDGIL
Returns the starting point of the curve.
bool isEmpty() const override SIP_HOLDGIL
Returns true if the geometry is empty.
void addVertex(const QgsPoint &pt)
Adds a vertex to the end of the geometry.
int nCurves() const SIP_HOLDGIL
Returns the number of curves in the geometry.
Class for doing transforms between two map coordinate systems.
bool isValid() const
Returns true if the coordinate transform is valid, ie both the source and destination CRS have been s...
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:66
Curve polygon geometry type.
Abstract base class for curved geometry type.
Definition: qgscurve.h:36
QgsCurve * segmentize(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const override
Returns a geometry without curves.
Definition: qgscurve.cpp:175
int vertexCount(int part=0, int ring=0) const override
Returns the number of vertices of which this geometry is built.
Definition: qgscurve.cpp:180
QgsCurve * clone() const override=0
Clones the geometry by performing a deep copy.
virtual QgsPoint endPoint() const =0
Returns the end point of the curve.
bool nextFeature(QgsFeature &f)
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFilterFid(QgsFeatureId fid)
Sets the feature ID that should be fetched.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
QgsGeometry geometry
Definition: qgsfeature.h:67
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
A rubberband class for QgsAbstractGeometry (considering curved geometries).
void setGeometryType(const QgsWkbTypes::GeometryType &geometryType)
Sets which geometry is handled by the rubber band, polygon or line.
virtual void setGeometry(QgsAbstractGeometry *geom)
Sets geometry (takes ownership). Geometry is expected to be in map coordinates.
static QgsPoint closestPoint(const QgsAbstractGeometry &geometry, const QgsPoint &point)
Returns the nearest point on a segment of a geometry for the specified point.
void errorFound(const QgsGeometry::Error &error)
Sent when an error has been found during the validation process.
A geometry error.
Definition: qgsgeometry.h:2249
bool hasWhere() const
true if the location available from
QgsPointXY where() const
The coordinates at which the error is located and should be visualized.
QString what() const
A human readable error message containing details about the error.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:125
const QgsAbstractGeometry * constGet() const SIP_HOLDGIL
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
bool vertexIdFromVertexNr(int number, QgsVertexId &id) const
Calculates the vertex ID from a vertex number.
QgsPoint vertexAt(int atVertex) const
Returns coordinates of a vertex.
Q_GADGET bool isNull
Definition: qgsgeometry.h:127
QgsAbstractGeometry * get()
Returns a modifiable (non-const) reference to the underlying abstract geometry primitive.
QgsGeometry convertToCurves(double distanceTolerance=1e-8, double angleTolerance=1e-8) const
Attempts to convert a non-curved geometry into a curved geometry type (e.g.
static void convertPointList(const QVector< QgsPointXY > &input, QgsPointSequence &output)
Upgrades a point list from QgsPointXY to QgsPoint.
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:44
double length() const override SIP_HOLDGIL
Returns the planar, 2-dimensional length of the geometry.
void close()
Closes the line string by appending the first point to the end of the line, if it is not already clos...
Extension of QgsTracer that provides extra functionality:
QAction * actionEnableTracing() const
Access to action that user may use to toggle tracing on/off. May be nullptr if no action was associat...
void reportError(PathError err, bool addingVertex)
Report a path finding error to the user.
static QgsMapCanvasTracer * tracerForCanvas(QgsMapCanvas *canvas)
Retrieve instance of this class associated with given canvas (if any).
QAction * actionEnableSnapping() const
Access to action that user may use to toggle snapping on/off.
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:89
void currentLayerChanged(QgsMapLayer *layer)
Emitted when the current layer is changed.
QgsSnappingUtils * snappingUtils() const
Returns snapping utility class that is associated with map canvas.
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
QgsMapLayer * currentLayer()
returns current layer (set by legend widget)
Base class for all map layer types.
Definition: qgsmaplayer.h:73
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:79
void setCrs(const QgsCoordinateReferenceSystem &srs, bool emitSignal=true)
Sets layer's spatial reference system.
A QgsMapMouseEvent is the result of a user interaction with the mouse on a QgsMapCanvas.
bool isSnapped() const
Returns true if there is a snapped point cached.
QgsPointXY mapPoint() const
mapPoint returns the point in coordinates
QgsPointLocator::Match mapPointMatch() const
Returns the matching data from the most recently snapped point.
QgsPointXY layerToMapCoordinates(const QgsMapLayer *layer, QgsPointXY point) const
transform point coordinates from layer's CRS to output CRS
QgsCoordinateTransform layerTransform(const QgsMapLayer *layer) const
Returns the coordinate transform from layer's CRS to destination CRS.
The QgsMapToolAdvancedDigitizing class is a QgsMapTool which gives event directly in map coordinates ...
virtual void cadCanvasMoveEvent(QgsMapMouseEvent *e)
Override this method when subclassing this class.
QgsAdvancedDigitizingDockWidget * mCadDockWidget
void deactivate() override
Unregisters this maptool from the cad dock widget.
virtual QgsMapLayer * layer() const
Returns the layer associated with the map tool.
QgsAdvancedDigitizingDockWidget * cadDockWidget() const
void activate() override
Registers this maptool with the cad dock widget.
void deactivate() override
Unregisters this maptool from the cad dock widget.
void stopCapturing()
Stop capturing.
int size()
Number of points digitized.
CaptureMode mode() const
The capture mode.
QgsMapToolCapture(QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDockWidget, CaptureMode mode)
constructor
void undo(bool isAutoRepeat=false)
Removes the last vertex from mRubberBand and mCaptureList.
QgsPoint mapPoint(const QgsMapMouseEvent &e) const
Creates a QgsPoint with ZM support if necessary (according to the WkbType of the current layer).
void keyPressEvent(QKeyEvent *e) override
Intercept key events like Esc or Del to delete the last point.
Q_DECL_DEPRECATED void setPoints(const QVector< QgsPointXY > &pointList)
Set the points on which to work.
void activate() override
Registers this maptool with the cad dock widget.
@ CapturePolygon
Capture polygons.
@ CaptureNone
Do not capture / determine mode from layer geometry type.
@ CapturePoint
Capture points.
@ CaptureLine
Capture lines.
void setCircularDigitizingEnabled(bool enable)
Enable the digitizing with curve.
void deleteTempRubberBand()
Clean a temporary rubberband.
void clean() override
convenient method to clean members
QList< QgsPointLocator::Match > snappingMatches() const
Returns a list of matches for each point on the captureCurve.
~QgsMapToolCapture() override
CaptureTechnique
Capture technique.
@ StraightSegments
Default capture mode - capture occurs with straight line segments.
void closePolygon()
Close an open polygon.
Q_DECL_DEPRECATED QVector< QgsPointXY > points() const
List of digitized points.
int addCurve(QgsCurve *c)
Adds a whole curve (e.g. circularstring) to the captured geometry. Curve must be in map CRS.
int fetchLayerPoint(const QgsPointLocator::Match &match, QgsPoint &layerPoint)
Fetches the original point from the source layer if it has the same CRS as the current layer.
virtual QgsMapToolCapture::Capabilities capabilities() const
Returns flags containing the supported capabilities.
virtual bool supportsTechnique(CaptureTechnique technique) const
Returns true if the tool supports the specified capture technique.
QgsPointSequence pointsZM() const
List of digitized points.
QgsRubberBand * takeRubberBand()
Returns the rubberBand currently owned by this map tool and transfers ownership to the caller.
bool isCapturing() const
Are we currently capturing?
int addVertex(const QgsPointXY &point)
Adds a point to the rubber band (in map coordinates) and to the capture list (in layer coordinates)
@ ValidateGeometries
Tool supports geometry validation (since QGIS 3.22)
void clearCurve()
Clear capture curve.
int nextPoint(const QgsPoint &mapPoint, QgsPoint &layerPoint)
Converts a map point to layer coordinates.
void setStreamDigitizingEnabled(bool enable)
Toggles the stream digitizing mode.
void cadCanvasMoveEvent(QgsMapMouseEvent *e) override
Override this method when subclassing this class.
void startCapturing()
Start capturing.
double defaultZValue() const
Returns default Z value.
double defaultMValue() const
Returns default M value.
QgsVectorLayer * currentVectorLayer()
Returns the current vector layer of the map canvas or 0.
static QColor digitizingFillColor()
Returns fill color for rubber bands (from global settings)
QgsRubberBand * createRubberBand(QgsWkbTypes::GeometryType geometryType=QgsWkbTypes::LineGeometry, bool alternativeBand=false)
Creates a rubber band with the color/line width from the QGIS settings.
static QColor digitizingStrokeColor()
Returns stroke color for rubber bands (from global settings)
static int digitizingStrokeWidth()
Returns stroke width for rubber bands (from global settings)
QgsPoint toLayerCoordinates(const QgsMapLayer *layer, const QgsPoint &point)
Transforms a point from map coordinates to layer coordinates.
Definition: qgsmaptool.cpp:62
QgsMapCanvas * mCanvas
The pointer to the map canvas.
Definition: qgsmaptool.h:336
QgsMapCanvas * canvas() const
returns pointer to the tool's map canvas
Definition: qgsmaptool.cpp:215
QgsPointXY toMapCoordinates(QPoint point)
Transforms a point from screen coordinates to map coordinates.
Definition: qgsmaptool.cpp:41
QPoint toCanvasCoordinates(const QgsPointXY &point) const
Transforms a point from map coordinates to screen coordinates.
Definition: qgsmaptool.cpp:77
A class to represent a 2D point.
Definition: qgspointxy.h:59
double y
Definition: qgspointxy.h:63
Q_GADGET double x
Definition: qgspointxy.h:62
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
bool addMValue(double mValue=0) override
Adds a measure to the geometry, initialized to a preset value.
Definition: qgspoint.cpp:562
bool dropMValue() override
Drops any measure values which exist in the geometry.
Definition: qgspoint.cpp:603
bool isEmpty() const override SIP_HOLDGIL
Returns true if the geometry is empty.
Definition: qgspoint.cpp:767
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
Definition: qgspoint.cpp:551
Q_GADGET double x
Definition: qgspoint.h:52
void setZ(double z) SIP_HOLDGIL
Sets the point's z-coordinate.
Definition: qgspoint.h:304
QgsPoint vertexAt(QgsVertexId) const override
Returns the point corresponding to a specified vertex id.
Definition: qgspoint.cpp:525
double z
Definition: qgspoint.h:54
bool dropZValue() override
Drops any z-dimensions which exist in the geometry.
Definition: qgspoint.cpp:592
double m
Definition: qgspoint.h:55
double y
Definition: qgspoint.h:53
void setM(double m) SIP_HOLDGIL
Sets the point's m-value.
Definition: qgspoint.h:319
Polygon geometry type.
Definition: qgspolygon.h:34
void setExteriorRing(QgsCurve *ring) override
Sets the exterior ring of the polygon.
Definition: qgspolygon.cpp:219
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:467
void snappingConfigChanged(const QgsSnappingConfig &config)
Emitted whenever the configuration for snapping has changed.
bool topologicalEditing
Definition: qgsproject.h:117
A class for drawing transient features (e.g.
Definition: qgsrubberband.h:52
void movePoint(const QgsPointXY &p, int geometryIndex=0, int ringIndex=0)
Moves the rubber band point specified by index.
const QgsPointXY * getPoint(int i, int j=0, int ringIndex=0) const
Returns a vertex.
void reset(QgsWkbTypes::GeometryType geometryType=QgsWkbTypes::LineGeometry)
Clears all the geometries in this rubberband.
int numberOfVertices() const
Returns count of vertices in all lists of mPoint.
void addGeometry(const QgsGeometry &geometry, QgsMapLayer *layer, bool doUpdate=true)
Adds the geometry of an existing feature to a rubberband This is useful for multi feature highlightin...
double value(const QString &dynamicKeyPart=QString(), bool useDefaultValueOverride=false, double defaultValueOverride=0.0) const
Returns settings value.
qlonglong value(const QString &dynamicKeyPart=QString(), bool useDefaultValueOverride=false, qlonglong defaultValueOverride=0) const
Returns settings value.
static const QgsSettingsEntryBool settingsDigitizingConvertToCurve
Settings entry digitizing convert to curve.
static const QgsSettingsEntryInteger settingsDigitizingStreamTolerance
Settings entry digitizing stream tolerance.
static const QgsSettingsEntryDouble settingsDigitizingConvertToCurveDistanceTolerance
Settings entry digitizing convert to curve distance tolerance.
static const QgsSettingsEntryInteger settingsDigitizingValidateGeometries
Settings entry digitizing validate geometries.
static const QgsSettingsEntryDouble settingsDigitizingConvertToCurveAngleTolerance
Settings entry digitizing convert to curve angle tolerance.
static const QgsSettingsEntryDouble settingsDigitizingLineColorAlphaScale
Settings entry digitizing line color alpha scale.
Class that shows snapping marker on map canvas for the current snapping match.
void addExtraSnapLayer(QgsVectorLayer *vl)
Supply an extra snapping layer (typically a memory layer).
void removeExtraSnapLayer(QgsVectorLayer *vl)
Removes an extra snapping layer.
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:803
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:736
PathError
Possible errors that may happen when calling findShortestPath()
Definition: qgstracer.h:138
@ ErrNone
No error.
Definition: qgstracer.h:139
@ ErrTooManyFeatures
Max feature count threshold was reached while reading features.
Definition: qgstracer.h:140
bool init()
Build the internal data structures.
Definition: qgstracer.cpp:675
virtual Q_INVOKABLE QgsVectorDataProvider::Capabilities capabilities() const
Returns flags containing the supported capabilities.
Represents a vector layer which manages a vector based data sets.
Q_INVOKABLE QgsWkbTypes::Type wkbType() const FINAL
Returns the WKBType or WKBUnknown in case of error.
Q_INVOKABLE QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
QgsFeature getFeature(QgsFeatureId fid) const
Queries the layer for the feature with the given id.
bool changeGeometry(QgsFeatureId fid, QgsGeometry &geometry, bool skipDefaultValue=false)
Changes a feature's geometry within the layer's edit buffer (but does not immediately commit the chan...
A class for marking vertices of features using e.g.
void setPenWidth(int width)
void setCenter(const QgsPointXY &point)
Sets the center point of the marker, in map coordinates.
void setIconType(int iconType)
void setColor(const QColor &color)
Sets the stroke color for the marker.
GeometryType
The geometry types are used to group QgsWkbTypes::Type in a coarse way.
Definition: qgswkbtypes.h:141
static bool hasM(Type type) SIP_HOLDGIL
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:1130
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:70
static bool hasZ(Type type) SIP_HOLDGIL
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:1080
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
QVector< QgsPoint > QgsPointSequence
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
const QgsCoordinateReferenceSystem & crs
QgsVectorLayer * layer() const
The vector layer where the snap occurred.
QgsFeatureId featureId() const
The id of the feature to which the snapped geometry belongs.
QgsPointXY point() const
for vertex / edge match coords depending on what class returns it (geom.cache: layer coords,...
bool hasEdge() const
Returns true if the Match is an edge.
bool hasLineEndpoint() const
Returns true if the Match is a line endpoint (start or end vertex).
bool hasMiddleSegment() const
Returns true if the Match is the middle of a segment.
int vertexIndex() const
for vertex / edge match (first vertex of the edge)
bool hasVertex() const
Returns true if the Match is a vertex.
Setting options for loading vector layers.
bool skipCrsValidation
Controls whether the layer is allowed to have an invalid/unknown CRS.
bool loadDefaultStyle
Set to true if the default layer style should be loaded.
Utility class for identifying a unique vertex within a geometry.
Definition: qgsvertexid.h:31
int vertex
Vertex number.
Definition: qgsvertexid.h:95
int part
Part number.
Definition: qgsvertexid.h:89
int ring
Ring number.
Definition: qgsvertexid.h:92