QGIS API Documentation  3.20.0-Odense (decaadbb31)
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"
31 #include "qgssettingsregistrycore.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 }
155 
156 
157 bool QgsMapToolCapture::tracingEnabled()
158 {
160  return tracer && ( !tracer->actionEnableTracing() || tracer->actionEnableTracing()->isChecked() )
161  && ( !tracer->actionEnableSnapping() || tracer->actionEnableSnapping()->isChecked() );
162 }
163 
164 
165 QgsPointXY QgsMapToolCapture::tracingStartPoint()
166 {
167  // if we have starting point from previous trace, then preferably use that one
168  // (useful when tracing with offset)
169  if ( mTracingStartPoint != QgsPointXY() )
170  return mTracingStartPoint;
171 
172  return lastCapturedMapPoint();
173 }
174 
175 
176 bool QgsMapToolCapture::tracingMouseMove( QgsMapMouseEvent *e )
177 {
178  if ( !e->isSnapped() )
179  return false;
180 
181  QgsPointXY pt0 = tracingStartPoint();
182  if ( pt0 == QgsPointXY() )
183  return false;
184 
186  if ( !tracer )
187  return false; // this should not happen!
188 
190  QVector<QgsPointXY> points = tracer->findShortestPath( pt0, e->mapPoint(), &err );
191  if ( points.isEmpty() )
192  {
193  tracer->reportError( err, false );
194  return false;
195  }
196 
197  mTempRubberBand->reset( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry, QgsWkbTypes::LineString, firstCapturedMapPoint() );
198  mTempRubberBand->addPoint( lastCapturedMapPoint() );
199 
200  // if there is offset, we need to fix the rubber bands to make sure they are aligned correctly.
201  // There are two cases we need to sort out:
202  // 1. the last point of mRubberBand may need to be moved off the traced curve to respect the offset
203  // 2. first point of mTempRubberBand may be needed to be moved to the beginning of the offset trace
204  QgsPoint lastPoint = lastCapturedMapPoint();
205  QgsPointXY lastPointXY( lastPoint );
206  if ( lastPointXY == pt0 && points[0] != lastPointXY )
207  {
208  if ( mRubberBand->numberOfVertices() != 0 )
209  {
210  // if rubber band had just one point, for some strange reason it contains the point twice
211  // we only want to move the last point if there are multiple points already
212  if ( mRubberBand->numberOfVertices() > 2 || ( mRubberBand->numberOfVertices() == 2 && *mRubberBand->getPoint( 0, 0 ) != *mRubberBand->getPoint( 0, 1 ) ) )
213  mRubberBand->movePoint( points[0] );
214  }
215 
216  mTempRubberBand->movePoint( 0, QgsPoint( points[0] ) );
217  }
218 
219  mTempRubberBand->movePoint( QgsPoint( points[0] ) );
220 
221  // update temporary rubberband
222  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
223  mTempRubberBand->addPoint( QgsPoint( points.at( i ) ), i == points.count() - 1 );
224 
225 
226  mTempRubberBand->addPoint( QgsPoint( points[points.size() - 1] ) );
227 
228  tracer->reportError( QgsTracer::ErrNone, false ); // clear messagebar if there was any error
229  return true;
230 }
231 
232 
233 bool QgsMapToolCapture::tracingAddVertex( const QgsPointXY &point )
234 {
236  if ( !tracer )
237  return false; // this should not happen!
238 
239  if ( mTempRubberBand->pointsCount() == 0 )
240  {
241  if ( !tracer->init() )
242  {
244  return false;
245  }
246 
247  // only accept first point if it is snapped to the graph (to vertex or edge)
248  bool res = tracer->isPointSnapped( point );
249  if ( res )
250  {
251  mTracingStartPoint = point;
252  }
253  return false;
254  }
255 
256  QgsPointXY pt0 = tracingStartPoint();
257  if ( pt0 == QgsPointXY() )
258  return false;
259 
261  QVector<QgsPointXY> points = tracer->findShortestPath( pt0, point, &err );
262  if ( points.isEmpty() )
263  return false; // ignore the vertex - can't find path to the end point!
264 
265  // transform points
266  QgsPointSequence layerPoints;
267  QgsPoint lp; // in layer coords
268  for ( int i = 0; i < points.count(); ++i )
269  {
270  if ( nextPoint( QgsPoint( points[i] ), lp ) != 0 )
271  return false;
272  layerPoints << lp;
273  }
274 
275  // Move the last point of the captured curve to the first point on the trace string (necessary if there is offset)
276  QgsVertexId lastVertexId( 0, 0, mCaptureCurve.numPoints() - 1 );
277  mCaptureCurve.moveVertex( lastVertexId, layerPoints.first() );
278  mSnappingMatches.removeLast();
279  mSnappingMatches.append( QgsPointLocator::Match() );
280 
281  int pointBefore = mCaptureCurve.numPoints();
282  mCaptureCurve.addCurve( new QgsLineString( layerPoints ) );
283 
284  resetRubberBand();
285 
286  // Curves de-approximation
287  if ( QgsSettingsRegistryCore::settingsDigitizingConvertToCurve.value() )
288  {
289  // If the tool and the layer support curves
290  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() );
291  if ( capabilities().testFlag( QgsMapToolCapture::Capability::SupportsCurves ) && vlayer->dataProvider()->capabilities().testFlag( QgsVectorDataProvider::Capability::CircularGeometries ) )
292  {
293  QgsGeometry linear = QgsGeometry( mCaptureCurve.segmentize() );
294  QgsGeometry curved = linear.convertToCurves(
295  QgsSettingsRegistryCore::settingsDigitizingConvertToCurveAngleTolerance.value(),
296  QgsSettingsRegistryCore::settingsDigitizingConvertToCurveDistanceTolerance.value()
297  );
298  mCaptureCurve = *qgsgeometry_cast<QgsCompoundCurve *>( curved.constGet() );
299  }
300  }
301 
302  // sync the snapping matches list
303  int pointAfter = mCaptureCurve.numPoints();
304  for ( ; pointBefore < pointAfter; ++pointBefore )
305  mSnappingMatches.append( QgsPointLocator::Match() );
306 
307  tracer->reportError( QgsTracer::ErrNone, true ); // clear messagebar if there was any error
308 
309  // adjust last captured point
310  const QgsPoint lastPt = mCaptureCurve.endPoint();
311  mCaptureLastPoint = toMapCoordinates( qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() ), lastPt );
312 
313  return true;
314 }
315 
316 QgsMapToolCaptureRubberBand *QgsMapToolCapture::createCurveRubberBand() const
317 {
318  QgsMapToolCaptureRubberBand *rb = new QgsMapToolCaptureRubberBand( mCanvas );
319  rb->setStrokeWidth( digitizingStrokeWidth() );
320  QColor color = digitizingStrokeColor();
321 
322  double alphaScale = QgsSettingsRegistryCore::settingsDigitizingLineColorAlphaScale.value();
323  color.setAlphaF( color.alphaF() * alphaScale );
324  rb->setLineStyle( Qt::DotLine );
325  rb->setStrokeColor( color );
326 
327  QColor fillColor = digitizingFillColor();
328  rb->setFillColor( fillColor );
329  rb->show();
330  return rb;
331 }
332 
333 QgsPoint QgsMapToolCapture::firstCapturedMapPoint()
334 {
335  return mCaptureFirstPoint;
336 }
337 
338 QgsPoint QgsMapToolCapture::lastCapturedMapPoint()
339 {
340  return mCaptureLastPoint;
341 }
342 
343 void QgsMapToolCapture::resetRubberBand()
344 {
345  if ( !mRubberBand )
346  return;
347  QgsLineString *lineString = mCaptureCurve.curveToLine();
349  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() );
350  mRubberBand->addGeometry( QgsGeometry( lineString ), vlayer );
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  {
371  mStreamingToleranceInPixels = QgsSettingsRegistryCore::settingsDigitizingStreamTolerance.value();
372  }
373 }
374 
376 {
378  QgsPointXY point = e->mapPoint();
379 
380  mSnapIndicator->setMatch( e->mapPointMatch() );
381 
382  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  QgsPoint prevPoint = mCaptureCurve.curveAt( mCaptureCurve.nCurves() - 1 )->endPoint();
393  if ( QgsPointXY( toCanvasCoordinates( toMapCoordinates( mCanvas->currentLayer(), 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  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() );
455  if ( !vlayer )
456  {
457  QgsDebugMsg( QStringLiteral( "no vector layer" ) );
458  return 1;
459  }
460  try
461  {
462  QgsPointXY mapP( mapPoint.x(), mapPoint.y() ); //#spellok
463  layerPoint = QgsPoint( toLayerCoordinates( vlayer, mapP ) ); //transform snapped point back to layer crs //#spellok
464  if ( QgsWkbTypes::hasZ( vlayer->wkbType() ) )
465  layerPoint.addZValue( defaultZValue() );
466  if ( QgsWkbTypes::hasM( vlayer->wkbType() ) )
467  layerPoint.addMValue( defaultMValue() );
468  }
469  catch ( QgsCsException &cse )
470  {
471  Q_UNUSED( cse )
472  QgsDebugMsg( QStringLiteral( "transformation to layer coordinate failed" ) );
473  return 2;
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 *>( mCanvas->currentLayer() );
488  QgsVectorLayer *sourceLayer = match.layer();
489  if ( match.isValid() && ( match.hasVertex() || match.hasLineEndpoint() || ( QgsProject::instance()->topologicalEditing() && ( match.hasEdge() || match.hasMiddleSegment() ) ) ) && sourceLayer &&
490  ( sourceLayer->crs() == vlayer->crs() ) )
491  {
492  QgsFeature f;
493  QgsFeatureRequest request;
494  request.setFilterFid( match.featureId() );
495  bool fetched = match.layer()->getFeatures( request ).nextFeature( f );
496  if ( fetched )
497  {
498  QgsVertexId vId;
499  if ( !f.geometry().vertexIdFromVertexNr( match.vertexIndex(), vId ) )
500  return 2;
501 
502  const QgsGeometry geom( f.geometry() );
503  if ( QgsProject::instance()->topologicalEditing() && ( match.hasEdge() || match.hasMiddleSegment() ) )
504  {
505  QgsVertexId vId2;
506  if ( !f.geometry().vertexIdFromVertexNr( match.vertexIndex() + 1, vId2 ) )
507  return 2;
508  QgsLineString line( geom.constGet()->vertexAt( vId ), geom.constGet()->vertexAt( vId2 ) );
509 
510  layerPoint = QgsGeometryUtils::closestPoint( line, QgsPoint( match.point() ) );
511  }
512  else
513  {
514  layerPoint = geom.constGet()->vertexAt( vId );
515  if ( QgsWkbTypes::hasZ( vlayer->wkbType() ) && !layerPoint.is3D() )
516  layerPoint.addZValue( defaultZValue() );
517  if ( QgsWkbTypes::hasM( vlayer->wkbType() ) && !layerPoint.isMeasure() )
518  layerPoint.addMValue( defaultMValue() );
519  }
520 
521  // ZM support depends on the target layer
522  if ( !QgsWkbTypes::hasZ( vlayer->wkbType() ) )
523  {
524  layerPoint.dropZValue();
525  }
526 
527  if ( !QgsWkbTypes::hasM( vlayer->wkbType() ) )
528  {
529  layerPoint.dropMValue();
530  }
531 
532  return 0;
533  }
534  else
535  {
536  return 2;
537  }
538  }
539  else
540  {
541  return 1;
542  }
543 }
544 
545 int QgsMapToolCapture::addVertex( const QgsPointXY &point )
546 {
547  return addVertex( point, QgsPointLocator::Match() );
548 }
549 
550 int QgsMapToolCapture::addVertex( const QgsPointXY &point, const QgsPointLocator::Match &match )
551 {
552  if ( mode() == CaptureNone )
553  {
554  QgsDebugMsg( QStringLiteral( "invalid capture mode" ) );
555  return 2;
556  }
557 
558  if ( mCapturing && mStreamingEnabled && !mAllowAddingStreamingPoints )
559  return 0;
560 
561  int res;
562  QgsPoint layerPoint;
563  res = fetchLayerPoint( match, layerPoint );
564  if ( res != 0 )
565  {
566  res = nextPoint( QgsPoint( point ), layerPoint );
567  if ( res != 0 )
568  {
569  return res;
570  }
571  }
572  QgsPoint mapPoint = toMapCoordinates( canvas()->currentLayer(), layerPoint );
573 
574  if ( mCaptureMode == CapturePoint )
575  {
576  mCaptureCurve.addVertex( layerPoint );
577  mSnappingMatches.append( match );
578  }
579  else
580  {
581  if ( mCaptureFirstPoint.isEmpty() )
582  {
583  mCaptureFirstPoint = mapPoint;
584  }
585 
586  if ( !mRubberBand )
588 
589  if ( !mTempRubberBand )
590  {
591  mTempRubberBand.reset( createCurveRubberBand() );
592  mTempRubberBand->setStringType( mDigitizingType );
593  mTempRubberBand->reset( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry, mDigitizingType, mapPoint );
594  }
595 
596  bool traceCreated = false;
597  if ( tracingEnabled() )
598  {
599  traceCreated = tracingAddVertex( mapPoint );
600  }
601 
602  // keep new tracing start point if we created a trace. This is useful when tracing with
603  // offset so that the user stays "snapped"
604  mTracingStartPoint = traceCreated ? point : QgsPointXY();
605 
606  if ( !traceCreated )
607  {
608  // ordinary digitizing
609  mTempRubberBand->movePoint( mapPoint ); //move the last point of the temp rubberband before operating with it
610  if ( mTempRubberBand->curveIsComplete() ) //2 points for line and 3 points for circular
611  {
612  const QgsCurve *curve = mTempRubberBand->curve();
613  if ( curve )
614  {
615  addCurve( curve->clone() );
616  // add curve append only invalid match to mSnappingMatches,
617  // so we need to remove them and add the one from here if it is valid
618  if ( match.isValid() && mSnappingMatches.count() > 0 && !mSnappingMatches.last().isValid() )
619  {
620  mSnappingMatches.removeLast();
621  if ( mTempRubberBand->stringType() == QgsWkbTypes::CircularString )
622  {
623  // for circular string two points are added and match for intermediate point is stored
624  mSnappingMatches.removeLast();
625  mSnappingMatches.append( mCircularIntermediateMatch );
626  }
627  mSnappingMatches.append( match );
628  }
629  }
630  mCaptureLastPoint = mapPoint;
631  mTempRubberBand->reset( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry, mDigitizingType, firstCapturedMapPoint() );
632  }
633  else if ( mTempRubberBand->pointsCount() == 0 )
634  {
635  mCaptureLastPoint = mapPoint;
636  mCaptureCurve.addVertex( layerPoint );
637  mSnappingMatches.append( match );
638  }
639  else
640  {
641  if ( mTempRubberBand->stringType() == QgsWkbTypes::CircularString )
642  {
643  mCircularIntermediateMatch = match;
644  }
645  }
646 
647  mTempRubberBand->addPoint( mapPoint );
648  }
649  else
650  {
651  mTempRubberBand->reset( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry, mDigitizingType, firstCapturedMapPoint() );
652  mTempRubberBand->addPoint( lastCapturedMapPoint() );
653  }
654  }
655 
656  updateExtraSnapLayer();
657  validateGeometry();
658 
659  return 0;
660 }
661 
663 {
664  if ( !c )
665  {
666  return 1;
667  }
668 
669  if ( !mRubberBand )
670  {
672  }
673 
674  if ( mTempRubberBand )
675  {
676  mTempRubberBand->reset( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry, mDigitizingType, firstCapturedMapPoint() );
677  QgsPoint endPt = c->endPoint();
678  mTempRubberBand->addPoint( endPt ); //add last point of c
679  }
680 
681  //transform back to layer CRS in case map CRS and layer CRS are different
682  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() );
684  if ( ct.isValid() )
685  {
687  }
688  int countBefore = mCaptureCurve.vertexCount();
689  //if there is only one point, this the first digitized point that are in the this first curve added --> remove the point
690  if ( mCaptureCurve.numPoints() == 1 )
691  mCaptureCurve.removeCurve( 0 );
692 
693  // we set the extendPrevious option to true to avoid creating compound curves with many 2 vertex linestrings -- instead we prefer
694  // to extend linestring curves so that they continue the previous linestring wherever possible...
695  mCaptureCurve.addCurve( c, !mStartNewCurve );
696  mStartNewCurve = false;
697 
698  int countAfter = mCaptureCurve.vertexCount();
699  int addedPoint = countAfter - countBefore;
700 
701  updateExtraSnapLayer();
702 
703  for ( int i = 0; i < addedPoint; ++i )
704  mSnappingMatches.append( QgsPointLocator::Match() );
705 
706  resetRubberBand();
707 
708  return 0;
709 }
710 
712 {
713  mCaptureCurve.clear();
714  updateExtraSnapLayer();
715 }
716 
717 QList<QgsPointLocator::Match> QgsMapToolCapture::snappingMatches() const
718 {
719  return mSnappingMatches;
720 }
721 
722 void QgsMapToolCapture::undo( bool isAutoRepeat )
723 {
724  mTracingStartPoint = QgsPointXY();
725 
726  if ( mTempRubberBand )
727  {
728  if ( size() <= 1 && mTempRubberBand->pointsCount() != 0 )
729  return;
730 
731  if ( isAutoRepeat && mIgnoreSubsequentAutoRepeatUndo )
732  return;
733  mIgnoreSubsequentAutoRepeatUndo = false;
734 
735  QgsPoint lastPoint = mTempRubberBand->lastPoint();
736 
737  if ( mTempRubberBand->stringType() == QgsWkbTypes::CircularString && mTempRubberBand->pointsCount() > 2 )
738  {
739  mTempRubberBand->removeLastPoint();
740  mTempRubberBand->movePoint( lastPoint );
741  return;
742  }
743 
744  QgsVertexId vertexToRemove;
745  vertexToRemove.part = 0;
746  vertexToRemove.ring = 0;
747  vertexToRemove.vertex = size() - 1;
748  if ( mCaptureCurve.numPoints() == 2 && mCaptureCurve.nCurves() == 1 )
749  {
750  // store the first vertex to restore if after deleting the curve
751  // because when only two vertices, removing a point remove all the curve
752  QgsPoint fp = mCaptureCurve.startPoint();
753  mCaptureCurve.deleteVertex( vertexToRemove );
754  mCaptureCurve.addVertex( fp );
755  }
756  else
757  {
758  const int curvesBefore = mCaptureCurve.nCurves();
759  const bool lastCurveIsLineString = qgsgeometry_cast< QgsLineString * >( mCaptureCurve.curveAt( curvesBefore - 1 ) );
760 
761  int pointsCountBefore = mCaptureCurve.numPoints();
762  mCaptureCurve.deleteVertex( vertexToRemove );
763  int pointsCountAfter = mCaptureCurve.numPoints();
764  for ( ; pointsCountAfter < pointsCountBefore; pointsCountAfter++ )
765  if ( !mSnappingMatches.empty() )
766  mSnappingMatches.removeLast();
767 
768  // if we have removed the last point in a linestring curve, then we "stick" here and ignore subsequent
769  // autorepeat undo actions until the user releases the undo key and holds it down again. This allows
770  // users to selectively remove portions of the geometry captured with the streaming mode by holding down
771  // the undo key, without risking accidental undo of non-streamed portions.
772  if ( mCaptureCurve.nCurves() < curvesBefore && lastCurveIsLineString )
773  mIgnoreSubsequentAutoRepeatUndo = true;
774  }
775 
776  updateExtraSnapLayer();
777 
778  resetRubberBand();
779 
780  mTempRubberBand->reset( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry, mDigitizingType, firstCapturedMapPoint() );
781 
782  if ( mCaptureCurve.numPoints() > 0 )
783  {
784  const QgsPoint lastPt = mCaptureCurve.endPoint();
785  mCaptureLastPoint = toMapCoordinates( qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() ), lastPt );
786  mTempRubberBand->addPoint( lastCapturedMapPoint() );
787  mTempRubberBand->movePoint( lastPoint );
788  }
789 
791  validateGeometry();
792  }
793 }
794 
795 void QgsMapToolCapture::keyPressEvent( QKeyEvent *e )
796 {
797  if ( e->key() == Qt::Key_Backspace || e->key() == Qt::Key_Delete )
798  {
799  undo( e->isAutoRepeat() );
800 
801  // Override default shortcut management in MapCanvas
802  e->ignore();
803  }
804  else if ( e->key() == Qt::Key_Escape )
805  {
806  stopCapturing();
807 
808  // Override default shortcut management in MapCanvas
809  e->ignore();
810  }
811 }
812 
814 {
815  mCapturing = true;
816 }
817 
819 {
820  return mCapturing;
821 }
822 
824 {
825  mRubberBand.reset();
826 
828 
829  qDeleteAll( mGeomErrorMarkers );
830  mGeomErrorMarkers.clear();
831  mGeomErrors.clear();
832 
833  mCaptureFirstPoint = QgsPoint();
834  mCaptureLastPoint = QgsPoint();
835 
836  mTracingStartPoint = QgsPointXY();
837 
838  mCapturing = false;
839  mCaptureCurve.clear();
840  updateExtraSnapLayer();
841  mSnappingMatches.clear();
842  if ( auto *lCurrentVectorLayer = currentVectorLayer() )
843  lCurrentVectorLayer->triggerRepaint();
844 }
845 
847 {
848  mTempRubberBand.reset();
849 }
850 
852 {
853  stopCapturing();
854  clearCurve();
855 }
856 
858 {
859  mCaptureCurve.close();
860  updateExtraSnapLayer();
861 }
862 
863 void QgsMapToolCapture::validateGeometry()
864 {
865  if ( QgsSettingsRegistryCore::settingsDigitizingValidateGeometries.value() == 0 )
866  return;
867 
868  if ( mValidator )
869  {
870  mValidator->deleteLater();
871  mValidator = nullptr;
872  }
873 
874  mGeomErrors.clear();
875  while ( !mGeomErrorMarkers.isEmpty() )
876  {
877  delete mGeomErrorMarkers.takeFirst();
878  }
879 
880  QgsGeometry geom;
881 
882  switch ( mCaptureMode )
883  {
884  case CaptureNone:
885  case CapturePoint:
886  return;
887  case CaptureLine:
888  if ( size() < 2 )
889  return;
890  geom = QgsGeometry( mCaptureCurve.curveToLine() );
891  break;
892  case CapturePolygon:
893  if ( size() < 3 )
894  return;
895  QgsLineString *exteriorRing = mCaptureCurve.curveToLine();
896  exteriorRing->close();
897  QgsPolygon *polygon = new QgsPolygon();
898  polygon->setExteriorRing( exteriorRing );
899  geom = QgsGeometry( polygon );
900  break;
901  }
902 
903  if ( geom.isNull() )
904  return;
905 
907  if ( QgsSettingsRegistryCore::settingsDigitizingValidateGeometries.value() == 2 )
909  mValidator = new QgsGeometryValidator( geom, nullptr, method );
910  connect( mValidator, &QgsGeometryValidator::errorFound, this, &QgsMapToolCapture::addError );
911  mValidator->start();
912  QgsDebugMsgLevel( QStringLiteral( "Validation started" ), 4 );
913 }
914 
915 void QgsMapToolCapture::addError( const QgsGeometry::Error &e )
916 {
917  mGeomErrors << e;
918  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() );
919  if ( !vlayer )
920  return;
921 
922  if ( e.hasWhere() )
923  {
925  vm->setCenter( mCanvas->mapSettings().layerToMapCoordinates( vlayer, e.where() ) );
927  vm->setPenWidth( 2 );
928  vm->setToolTip( e.what() );
929  vm->setColor( Qt::green );
930  vm->setZValue( vm->zValue() + 1 );
931  mGeomErrorMarkers << vm;
932  }
933 }
934 
936 {
937  return mCaptureCurve.numPoints();
938 }
939 
940 QVector<QgsPointXY> QgsMapToolCapture::points() const
941 {
942  QVector<QgsPointXY> pointsXY;
944 
945  return pointsXY;
946 }
947 
949 {
950  QgsPointSequence pts;
951  mCaptureCurve.points( pts );
952  return pts;
953 }
954 
955 void QgsMapToolCapture::setPoints( const QVector<QgsPointXY> &pointList )
956 {
957  QgsLineString *line = new QgsLineString( pointList );
958  mCaptureCurve.clear();
959  mCaptureCurve.addCurve( line );
960  updateExtraSnapLayer();
961  mSnappingMatches.clear();
962  for ( int i = 0; i < line->length(); ++i )
963  mSnappingMatches.append( QgsPointLocator::Match() );
964  resetRubberBand();
965 }
966 
967 void QgsMapToolCapture::setPoints( const QgsPointSequence &pointList )
968 {
969  QgsLineString *line = new QgsLineString( pointList );
970  mCaptureCurve.clear();
971  mCaptureCurve.addCurve( line );
972  updateExtraSnapLayer();
973  mSnappingMatches.clear();
974  for ( int i = 0; i < line->length(); ++i )
975  mSnappingMatches.append( QgsPointLocator::Match() );
976  resetRubberBand();
977 }
978 
979 QgsPoint QgsMapToolCapture::mapPoint( const QgsPointXY &point ) const
980 {
981  QgsPoint newPoint( QgsWkbTypes::Point, point.x(), point.y() );
982 
983  // get current layer
984  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() );
985  if ( !vlayer )
986  {
987  return newPoint;
988  }
989 
990  // convert to the corresponding type for a full ZM support
991  const QgsWkbTypes::Type type = vlayer->wkbType();
992  if ( QgsWkbTypes::hasZ( type ) && !QgsWkbTypes::hasM( type ) )
993  {
994  newPoint.convertTo( QgsWkbTypes::PointZ );
995  }
996  else if ( !QgsWkbTypes::hasZ( type ) && QgsWkbTypes::hasM( type ) )
997  {
998  newPoint.convertTo( QgsWkbTypes::PointM );
999  }
1000  else if ( QgsWkbTypes::hasZ( type ) && QgsWkbTypes::hasM( type ) )
1001  {
1002  newPoint.convertTo( QgsWkbTypes::PointZM );
1003  }
1004 
1005  // set z value if necessary
1006  if ( QgsWkbTypes::hasZ( newPoint.wkbType() ) )
1007  {
1008  newPoint.setZ( defaultZValue() );
1009  }
1010  // set m value if necessary
1011  if ( QgsWkbTypes::hasM( newPoint.wkbType() ) )
1012  {
1013  newPoint.setM( defaultMValue() );
1014  }
1015 
1016  return newPoint;
1017 }
1018 
1020 {
1021  QgsPoint newPoint = mapPoint( e.mapPoint() );
1022 
1023  // set z value from snapped point if necessary
1024  if ( QgsWkbTypes::hasZ( newPoint.wkbType() ) )
1025  {
1026  // if snapped, z dimension is taken from the corresponding snapped
1027  // point.
1028  if ( e.isSnapped() )
1029  {
1030  const QgsPointLocator::Match match = e.mapPointMatch();
1031 
1032  if ( match.layer() && QgsWkbTypes::hasZ( match.layer()->wkbType() ) )
1033  {
1034  const QgsFeature ft = match.layer()->getFeature( match.featureId() );
1035  newPoint.setZ( ft.geometry().vertexAt( match.vertexIndex() ).z() );
1036  }
1037  }
1038  }
1039 
1040  return newPoint;
1041 }
1042 
1043 void QgsMapToolCapture::updateExtraSnapLayer()
1044 {
1045  if ( !mExtraSnapLayer )
1046  return;
1047 
1048  if ( canvas()->snappingUtils()->config().selfSnapping() && mCanvas->currentLayer() && mCaptureCurve.numPoints() >= 2 )
1049  {
1050  // the current layer may have changed
1051  mExtraSnapLayer->setCrs( mCanvas->currentLayer()->crs() );
1052  QgsGeometry geom = QgsGeometry( mCaptureCurve.clone() );
1053  // we close the curve to allow snapping on last segment
1054  if ( mCaptureMode == CapturePolygon && mCaptureCurve.numPoints() >= 3 )
1055  {
1056  qgsgeometry_cast<QgsCompoundCurve *>( geom.get() )->close();
1057  }
1058  mExtraSnapLayer->changeGeometry( mExtraSnapFeatureId, geom );
1059  }
1060  else
1061  {
1062  QgsGeometry geom;
1063  mExtraSnapLayer->changeGeometry( mExtraSnapFeatureId, geom );
1064  }
1065 }
1066 
1067 QgsMapToolCaptureRubberBand::QgsMapToolCaptureRubberBand( QgsMapCanvas *mapCanvas, QgsWkbTypes::GeometryType geomType ):
1068  QgsGeometryRubberBand( mapCanvas, geomType )
1069 {
1070  setVertexDrawingEnabled( false );
1071 }
1072 
1073 QgsCurve *QgsMapToolCaptureRubberBand::curve()
1074 {
1075  if ( mPoints.empty() )
1076  return nullptr;
1077 
1078  switch ( mStringType )
1079  {
1081  return new QgsLineString( mPoints ) ;
1082  break;
1084  if ( mPoints.count() != 3 )
1085  return nullptr;
1086  return new QgsCircularString(
1087  mPoints[0],
1088  mPoints[1],
1089  mPoints[2] ) ;
1090  break;
1091  default:
1092  return nullptr;
1093  }
1094 }
1095 
1096 bool QgsMapToolCaptureRubberBand::curveIsComplete() const
1097 {
1098  return ( mStringType == QgsWkbTypes::LineString && mPoints.count() > 1 ) ||
1099  ( mStringType == QgsWkbTypes::CircularString && mPoints.count() > 2 );
1100 }
1101 
1102 void QgsMapToolCaptureRubberBand::reset( QgsWkbTypes::GeometryType geomType, QgsWkbTypes::Type stringType, const QgsPoint &firstPolygonPoint )
1103 {
1104  if ( !( geomType == QgsWkbTypes::LineGeometry || geomType == QgsWkbTypes::PolygonGeometry ) )
1105  return;
1106 
1107  mPoints.clear();
1108  mFirstPolygonPoint = firstPolygonPoint;
1109  setStringType( stringType );
1110  setRubberBandGeometryType( geomType );
1111 }
1112 
1113 void QgsMapToolCaptureRubberBand::setRubberBandGeometryType( QgsWkbTypes::GeometryType geomType )
1114 {
1116  updateCurve();
1117 }
1118 
1119 void QgsMapToolCaptureRubberBand::addPoint( const QgsPoint &point, bool doUpdate )
1120 {
1121  if ( mPoints.count() == 0 )
1122  mPoints.append( point );
1123 
1124  mPoints.append( point );
1125 
1126  if ( doUpdate )
1127  updateCurve();
1128 }
1129 
1130 void QgsMapToolCaptureRubberBand::movePoint( const QgsPoint &point )
1131 {
1132  if ( mPoints.count() > 0 )
1133  mPoints.last() = point ;
1134 
1135  updateCurve();
1136 }
1137 
1138 void QgsMapToolCaptureRubberBand::movePoint( int index, const QgsPoint &point )
1139 {
1140  if ( mPoints.count() > 0 && mPoints.size() > index )
1141  mPoints[index] = point;
1142 
1143  updateCurve();
1144 }
1145 
1146 int QgsMapToolCaptureRubberBand::pointsCount()
1147 {
1148  return mPoints.size();
1149 }
1150 
1151 QgsWkbTypes::Type QgsMapToolCaptureRubberBand::stringType() const
1152 {
1153  return mStringType;
1154 }
1155 
1156 void QgsMapToolCaptureRubberBand::setStringType( const QgsWkbTypes::Type &type )
1157 {
1158  if ( ( type != QgsWkbTypes::CircularString && type != QgsWkbTypes::LineString ) || type == mStringType )
1159  return;
1160 
1161  mStringType = type;
1162  if ( type == QgsWkbTypes::LineString && mPoints.count() == 3 )
1163  {
1164  mPoints.removeAt( 1 );
1165  }
1166 
1167  setVertexDrawingEnabled( type == QgsWkbTypes::CircularString );
1168  updateCurve();
1169 }
1170 
1171 QgsPoint QgsMapToolCaptureRubberBand::lastPoint() const
1172 {
1173  if ( mPoints.empty() )
1174  return QgsPoint();
1175 
1176  return mPoints.last();
1177 }
1178 
1179 QgsPoint QgsMapToolCaptureRubberBand::pointFromEnd( int posFromEnd ) const
1180 {
1181  if ( posFromEnd < mPoints.size() )
1182  return mPoints.at( mPoints.size() - 1 - posFromEnd );
1183  else
1184  return QgsPoint();
1185 }
1186 
1187 void QgsMapToolCaptureRubberBand::removeLastPoint()
1188 {
1189  if ( mPoints.count() > 1 )
1190  mPoints.removeLast();
1191 
1192  updateCurve();
1193 }
1194 
1195 void QgsMapToolCaptureRubberBand::setGeometry( QgsAbstractGeometry *geom )
1196 {
1198 }
1199 
1200 void QgsMapToolCaptureRubberBand::updateCurve()
1201 {
1202  std::unique_ptr<QgsCurve> curve;
1203  switch ( mStringType )
1204  {
1206  curve.reset( createLinearString() );
1207  break;
1209  curve.reset( createCircularString() );
1210  break;
1211  default:
1212  return;
1213  break;
1214  }
1215 
1216  if ( geometryType() == QgsWkbTypes::PolygonGeometry )
1217  {
1218  std::unique_ptr<QgsCurvePolygon> geom( new QgsCurvePolygon );
1219  geom->setExteriorRing( curve.release() );
1220  setGeometry( geom.release() );
1221  }
1222  else
1223  {
1224  setGeometry( curve.release() );
1225  }
1226 }
1227 
1228 QgsCurve *QgsMapToolCaptureRubberBand::createLinearString()
1229 {
1230  std::unique_ptr<QgsLineString> curve( new QgsLineString );
1231  if ( geometryType() == QgsWkbTypes::PolygonGeometry )
1232  {
1233  QgsPointSequence points = mPoints;
1234  points.prepend( mFirstPolygonPoint );
1235  curve->setPoints( points );
1236  }
1237  else
1238  curve->setPoints( mPoints );
1239 
1240  return curve.release();
1241 }
1242 
1243 QgsCurve *QgsMapToolCaptureRubberBand::createCircularString()
1244 {
1245  std::unique_ptr<QgsCircularString> curve( new QgsCircularString );
1246  curve->setPoints( mPoints );
1247  if ( geometryType() == QgsWkbTypes::PolygonGeometry )
1248  {
1249  // add a linear string to close the polygon
1250  std::unique_ptr<QgsCompoundCurve> polygonCurve( new QgsCompoundCurve );
1251  polygonCurve->addVertex( mFirstPolygonPoint );
1252  if ( !mPoints.empty() )
1253  polygonCurve->addVertex( mPoints.first() );
1254  polygonCurve->addCurve( curve.release() );
1255  return polygonCurve.release();
1256  }
1257  else
1258  return curve.release();
1259 }
1260 
T * release()
Clears the pointer and returns it.
void reset(T *p=nullptr)
Will reset the managed pointer to p.
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...
void removePreviousPoint()
Remove previous point in the CAD point list.
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.
@ ReverseTransform
Transform from destination to source CRS.
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:174
int vertexCount(int part=0, int ring=0) const override
Returns the number of vertices of which this geometry is built.
Definition: qgscurve.cpp:179
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 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:2268
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:124
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:126
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.
ValidationMethod
Available methods for validating geometries.
Definition: qgsgeometry.h:2322
@ ValidatorQgisInternal
Use internal QgsGeometryValidator method.
Definition: qgsgeometry.h:2323
@ ValidatorGeos
Use GEOS validation methods.
Definition: qgsgeometry.h:2324
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:86
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:70
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:76
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.
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)
@ NoCapabilities
No specific capabilities.
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)
QgsMapCanvas * mCanvas
The pointer to the map canvas.
Definition: qgsmaptool.h:297
QgsMapCanvas * canvas() const
returns pointer to the tool's map canvas
Definition: qgsmaptool.cpp:199
QgsPointXY toMapCoordinates(QPoint point)
Transforms a point from screen coordinates to map coordinates.
Definition: qgsmaptool.cpp:41
QgsPointXY toLayerCoordinates(const QgsMapLayer *layer, QPoint point)
Transforms a point from screen coordinates to layer coordinates.
Definition: qgsmaptool.cpp:51
QPoint toCanvasCoordinates(const QgsPointXY &point) const
Transforms a point from map coordinates to screen coordinates.
Definition: qgsmaptool.cpp:72
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
double distance(double x, double y) const SIP_HOLDGIL
Returns the distance between this point and a specified x, y coordinate.
Definition: qgspointxy.h:211
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 y
Definition: qgspoint.h:53
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:115
A class for drawing transient features (e.g.
Definition: qgsrubberband.h:51
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 addGeometry(const QgsGeometry &geometry, QgsVectorLayer *layer, bool doUpdate=true)
Adds the geometry of an existing feature to a rubberband This is useful for multi feature highlightin...
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.
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:802
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:735
PathError
Possible errors that may happen when calling findShortestPath()
Definition: qgstracer.h:133
@ ErrNone
No error.
Definition: qgstracer.h:134
@ ErrTooManyFeatures
Max feature count threshold was reached while reading features.
Definition: qgstracer.h:135
bool init()
Build the internal data structures.
Definition: qgstracer.cpp:674
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:1100
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:1050
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
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.
int vertex
Vertex number.
int part
Part number.
int ring
Ring number.