QGIS API Documentation  3.25.0-Master (10b47c2603)
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"
38 #include "qgsgui.h"
39 
40 #include <QAction>
41 #include <QCursor>
42 #include <QPixmap>
43 #include <QStatusBar>
44 
45 
47  : QgsMapToolAdvancedDigitizing( canvas, cadDockWidget )
48  , mCaptureMode( mode )
49  , mCaptureModeFromLayer( mode == CaptureNone )
50 {
51  mSnapIndicator.reset( new QgsSnapIndicator( canvas ) );
52 
53  setCursor( QgsApplication::getThemeCursor( QgsApplication::Cursor::CapturePoint ) );
54 
56  this, &QgsMapToolCapture::currentLayerChanged );
57 
58  QgsVectorLayer::LayerOptions layerOptions;
59  layerOptions.skipCrsValidation = true;
60  layerOptions.loadDefaultStyle = false;
61  mExtraSnapLayer = new QgsVectorLayer( QStringLiteral( "LineString?crs=" ), QStringLiteral( "extra snap" ), QStringLiteral( "memory" ), layerOptions );
62  mExtraSnapLayer->startEditing();
63  QgsFeature f;
64  mExtraSnapLayer->addFeature( f );
65  mExtraSnapFeatureId = f.id();
66 
68  this, &QgsMapToolCapture::updateExtraSnapLayer );
69 
70  currentLayerChanged( canvas->currentLayer() );
71 }
72 
74 {
75  // during tear down we have to clean up mExtraSnapLayer first, before
76  // we call stop capturing. Otherwise stopCapturing tries to access members
77  // from the mapcanvas, which is likely already being destroyed and triggering
78  // the deletion of this object...
79  mCanvas->snappingUtils()->removeExtraSnapLayer( mExtraSnapLayer );
80  mExtraSnapLayer->deleteLater();
81  mExtraSnapLayer = nullptr;
82 
83  stopCapturing();
84 
85  if ( mValidator )
86  {
87  mValidator->deleteLater();
88  mValidator = nullptr;
89  }
90 }
91 
92 QgsMapToolCapture::Capabilities QgsMapToolCapture::capabilities() const
93 {
95 }
96 
98 {
99  switch ( technique )
100  {
102  return true;
106  return false;
107  }
109 }
110 
112 {
113  if ( mTempRubberBand )
114  mTempRubberBand->show();
115 
116  mCanvas->snappingUtils()->addExtraSnapLayer( mExtraSnapLayer );
118 
119  if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape && mCurrentShapeMapTool )
120  mCurrentShapeMapTool->activate( mCaptureMode, mCaptureLastPoint );
121 }
122 
124 {
125  if ( mTempRubberBand )
126  mTempRubberBand->hide();
127 
128  mSnapIndicator->setMatch( QgsPointLocator::Match() );
129 
130  mCanvas->snappingUtils()->removeExtraSnapLayer( mExtraSnapLayer );
131 
132  if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape && mCurrentShapeMapTool )
133  mCurrentShapeMapTool->deactivate();
134 
136 }
137 
138 void QgsMapToolCapture::currentLayerChanged( QgsMapLayer *layer )
139 {
140  if ( !mCaptureModeFromLayer )
141  return;
142 
143  mCaptureMode = CaptureNone;
144 
145  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
146  if ( !vlayer )
147  {
148  return;
149  }
150 
151  switch ( vlayer->geometryType() )
152  {
154  mCaptureMode = CapturePoint;
155  break;
157  mCaptureMode = CaptureLine;
158  break;
160  mCaptureMode = CapturePolygon;
161  break;
162  default:
163  mCaptureMode = CaptureNone;
164  break;
165  }
166 
167  if ( mTempRubberBand )
168  mTempRubberBand->setRubberBandGeometryType( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry );
169 
170  resetRubberBand();
171  cadDockWidget()->switchZM();
172 }
173 
174 
175 bool QgsMapToolCapture::tracingEnabled()
176 {
178  return tracer && ( !tracer->actionEnableTracing() || tracer->actionEnableTracing()->isChecked() )
179  && ( !tracer->actionEnableSnapping() || tracer->actionEnableSnapping()->isChecked() );
180 }
181 
182 
183 QgsPointXY QgsMapToolCapture::tracingStartPoint()
184 {
185  // if we have starting point from previous trace, then preferably use that one
186  // (useful when tracing with offset)
187  if ( mTracingStartPoint != QgsPointXY() )
188  return mTracingStartPoint;
189 
190  return mCaptureLastPoint;
191 }
192 
193 
194 bool QgsMapToolCapture::tracingMouseMove( QgsMapMouseEvent *e )
195 {
196  if ( !e->isSnapped() )
197  return false;
198 
199  QgsPointXY pt0 = tracingStartPoint();
200  if ( pt0 == QgsPointXY() )
201  return false;
202 
204  if ( !tracer )
205  return false; // this should not happen!
206 
208  QVector<QgsPointXY> points = tracer->findShortestPath( pt0, e->mapPoint(), &err );
209  if ( points.isEmpty() )
210  {
211  tracer->reportError( err, false );
212  return false;
213  }
214 
215  mTempRubberBand->reset( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry, QgsWkbTypes::LineString, mCaptureFirstPoint );
216  mTempRubberBand->addPoint( mCaptureLastPoint );
217 
218  // if there is offset, we need to fix the rubber bands to make sure they are aligned correctly.
219  // There are two cases we need to sort out:
220  // 1. the last point of mRubberBand may need to be moved off the traced curve to respect the offset
221  // 2. first point of mTempRubberBand may be needed to be moved to the beginning of the offset trace
222  const QgsPoint lastPoint = mCaptureLastPoint;
223  QgsPointXY lastPointXY( lastPoint );
224  if ( lastPointXY == pt0 && points[0] != lastPointXY )
225  {
226  if ( mRubberBand->numberOfVertices() != 0 )
227  {
228  // if rubber band had just one point, for some strange reason it contains the point twice
229  // we only want to move the last point if there are multiple points already
230  if ( mRubberBand->numberOfVertices() > 2 || ( mRubberBand->numberOfVertices() == 2 && *mRubberBand->getPoint( 0, 0 ) != *mRubberBand->getPoint( 0, 1 ) ) )
231  mRubberBand->movePoint( points[0] );
232  }
233 
234  mTempRubberBand->movePoint( 0, QgsPoint( points[0] ) );
235  }
236 
237  mTempRubberBand->movePoint( QgsPoint( points[0] ) );
238 
239  // update temporary rubberband
240  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
241  mTempRubberBand->addPoint( QgsPoint( points.at( i ) ), i == points.count() - 1 );
242 
243 
244  mTempRubberBand->addPoint( QgsPoint( points[points.size() - 1] ) );
245 
246  tracer->reportError( QgsTracer::ErrNone, false ); // clear messagebar if there was any error
247  return true;
248 }
249 
250 
251 bool QgsMapToolCapture::tracingAddVertex( const QgsPointXY &point )
252 {
254  if ( !tracer )
255  return false; // this should not happen!
256 
257  if ( mTempRubberBand->pointsCount() == 0 )
258  {
259  if ( !tracer->init() )
260  {
262  return false;
263  }
264 
265  // only accept first point if it is snapped to the graph (to vertex or edge)
266  const bool res = tracer->isPointSnapped( point );
267  if ( res )
268  {
269  mTracingStartPoint = point;
270  }
271  return false;
272  }
273 
274  QgsPointXY pt0 = tracingStartPoint();
275  if ( pt0 == QgsPointXY() )
276  return false;
277 
279  QVector<QgsPointXY> points = tracer->findShortestPath( pt0, point, &err );
280  if ( points.isEmpty() )
281  return false; // ignore the vertex - can't find path to the end point!
282 
283  // transform points
284  QgsPointSequence layerPoints;
285  QgsPoint lp; // in layer coords
286  for ( int i = 0; i < points.count(); ++i )
287  {
288  if ( nextPoint( QgsPoint( points[i] ), lp ) != 0 )
289  return false;
290  layerPoints << lp;
291  }
292 
293  // Move the last point of the captured curve to the first point on the trace string (necessary if there is offset)
294  const QgsVertexId lastVertexId( 0, 0, mCaptureCurve.numPoints() - 1 );
295  mCaptureCurve.moveVertex( lastVertexId, layerPoints.first() );
296  mSnappingMatches.removeLast();
297  mSnappingMatches.append( QgsPointLocator::Match() );
298 
299  int pointBefore = mCaptureCurve.numPoints();
300  addCurve( new QgsLineString( layerPoints ) );
301 
302  resetRubberBand();
303 
304  // Curves de-approximation
306  {
307  // If the tool and the layer support curves
308  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() );
309  if ( vlayer && capabilities().testFlag( QgsMapToolCapture::Capability::SupportsCurves ) && vlayer->dataProvider()->capabilities().testFlag( QgsVectorDataProvider::Capability::CircularGeometries ) )
310  {
311  const QgsGeometry linear = QgsGeometry( mCaptureCurve.segmentize() );
312  const QgsGeometry curved = linear.convertToCurves(
315  );
317  {
318  mCaptureCurve.clear();
319  mCaptureCurve.addCurve( qgsgeometry_cast< const QgsCurve * >( curved.constGet() )->clone() );
320  }
321  else
322  {
323  mCaptureCurve = *qgsgeometry_cast<QgsCompoundCurve *>( curved.constGet() );
324  }
325  }
326  }
327 
328  // sync the snapping matches list
329  const int pointAfter = mCaptureCurve.numPoints();
330  for ( ; pointBefore < pointAfter; ++pointBefore )
331  mSnappingMatches.append( QgsPointLocator::Match() );
332 
333  tracer->reportError( QgsTracer::ErrNone, true ); // clear messagebar if there was any error
334 
335  // adjust last captured point
336  const QgsPoint lastPt = mCaptureCurve.endPoint();
337  mCaptureLastPoint = toMapCoordinates( layer(), lastPt );
338 
339  return true;
340 }
341 
342 QgsMapToolCaptureRubberBand *QgsMapToolCapture::createCurveRubberBand() const
343 {
344  QgsMapToolCaptureRubberBand *rb = new QgsMapToolCaptureRubberBand( mCanvas );
345  rb->setStrokeWidth( digitizingStrokeWidth() );
346  QColor color = digitizingStrokeColor();
347 
349  color.setAlphaF( color.alphaF() * alphaScale );
350  rb->setLineStyle( Qt::DotLine );
351  rb->setStrokeColor( color );
352 
353  const QColor fillColor = digitizingFillColor();
354  rb->setFillColor( fillColor );
355  rb->show();
356  return rb;
357 }
358 
359 void QgsMapToolCapture::resetRubberBand()
360 {
361  if ( !mRubberBand )
362  return;
363  QgsLineString *lineString = mCaptureCurve.curveToLine();
364 
366  mRubberBand->addGeometry( QgsGeometry( lineString ), layer() );
367 }
368 
370 {
371  return mRubberBand.release();
372 }
373 
375 {
376  if ( enable )
378  else
380 }
381 
383 {
384  if ( enable )
386  else
388 }
389 
391 {
392  if ( mCurrentCaptureTechnique == technique )
393  return;
394 
395  mStartNewCurve = true;
396 
397  if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape && mCurrentShapeMapTool )
398  {
399  mCurrentShapeMapTool->deactivate();
400  clean();
401  }
402 
403  switch ( technique )
404  {
406  mLineDigitizingType = QgsWkbTypes::LineString;
407  break;
409  mLineDigitizingType = QgsWkbTypes::CircularString;
410  break;
412  mLineDigitizingType = QgsWkbTypes::LineString;
414  break;
416  mLineDigitizingType = QgsWkbTypes::LineString;
417  break;
418 
419  }
420 
421  if ( mTempRubberBand )
422  mTempRubberBand->setStringType( mLineDigitizingType );
423 
424  mCurrentCaptureTechnique = technique;
425 
426  if ( technique == Qgis::CaptureTechnique::Shape && mCurrentShapeMapTool && isActive() )
427  {
428  clean();
429  mCurrentShapeMapTool->activate( mCaptureMode, mCaptureLastPoint );
430  }
431 }
432 
434 {
435  if ( mCurrentShapeMapTool )
436  {
437  if ( shapeMapToolMetadata && mCurrentShapeMapTool->id() == shapeMapToolMetadata->id() )
438  return;
439  if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape )
440  mCurrentShapeMapTool->deactivate();
441  mCurrentShapeMapTool->deleteLater();
442  }
443 
444  mCurrentShapeMapTool = shapeMapToolMetadata ? shapeMapToolMetadata->factory( this ) : nullptr;
445 
446  if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape && isActive() )
447  {
448  clean();
449  mCurrentShapeMapTool->activate( mCaptureMode, mCaptureLastPoint );
450  }
451 }
452 
454 {
456 
457  const QgsPointXY point = e->mapPoint();
458 
459  mSnapIndicator->setMatch( e->mapPointMatch() );
460 
461  if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape )
462  {
463  if ( !mCurrentShapeMapTool )
464  {
465  emit messageEmitted( tr( "Cannot capture a shape without a shape tool defined" ), Qgis::MessageLevel::Warning );
466  }
467  else
468  {
469  if ( !mTempRubberBand )
470  {
471  mTempRubberBand.reset( createCurveRubberBand() );
472  mTempRubberBand->setStringType( mLineDigitizingType );
473  mTempRubberBand->setRubberBandGeometryType( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry );
474  }
475 
476  mCurrentShapeMapTool->cadCanvasMoveEvent( e, mCaptureMode );
477  return;
478  }
479  }
480  else
481  {
482  const QgsPoint mapPoint = QgsPoint( point );
483 
484  if ( mCaptureMode != CapturePoint && mTempRubberBand && mCapturing )
485  {
486  bool hasTrace = false;
487 
488  if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Streaming )
489  {
490  if ( !mCaptureCurve.isEmpty() )
491  {
492  const QgsPoint prevPoint = mCaptureCurve.curveAt( mCaptureCurve.nCurves() - 1 )->endPoint();
493  if ( QgsPointXY( toCanvasCoordinates( toMapCoordinates( layer(), prevPoint ) ) ).distance( toCanvasCoordinates( point ) ) < mStreamingToleranceInPixels )
494  return;
495  }
496 
497  mAllowAddingStreamingPoints = true;
498  addVertex( mapPoint );
499  mAllowAddingStreamingPoints = false;
500  }
501  else if ( tracingEnabled() && mCaptureCurve.numPoints() != 0 )
502  {
503  // Store the intermediate point for circular string to retrieve after tracing mouse move if
504  // the digitizing type is circular and the temp rubber band is effectivly circular and if this point is existing
505  // Store an empty point if the digitizing type is linear ot the point is not existing (curve not complete)
506  if ( mLineDigitizingType == QgsWkbTypes::CircularString &&
507  mTempRubberBand->stringType() == QgsWkbTypes::CircularString &&
508  mTempRubberBand->curveIsComplete() )
509  mCircularItermediatePoint = mTempRubberBand->pointFromEnd( 1 );
510  else if ( mLineDigitizingType == QgsWkbTypes::LineString ||
511  !mTempRubberBand->curveIsComplete() )
512  mCircularItermediatePoint = QgsPoint();
513 
514  hasTrace = tracingMouseMove( e );
515 
516  if ( !hasTrace )
517  {
518  // Restore the temp rubber band
519  mTempRubberBand->reset( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry, mLineDigitizingType, mCaptureFirstPoint );
520  mTempRubberBand->addPoint( mCaptureLastPoint );
521  if ( !mCircularItermediatePoint.isEmpty() )
522  {
523  mTempRubberBand->movePoint( mCircularItermediatePoint );
524  mTempRubberBand->addPoint( mCircularItermediatePoint );
525  }
526  }
527  }
528 
529  if ( mCurrentCaptureTechnique != Qgis::CaptureTechnique::Streaming && !hasTrace )
530  {
531  if ( mCaptureCurve.numPoints() > 0 )
532  {
533  const QgsPoint mapPt = mCaptureLastPoint;
534 
535  if ( mTempRubberBand )
536  {
537  mTempRubberBand->movePoint( mapPoint );
538  mTempRubberBand->movePoint( 0, mapPt );
539  }
540 
541  // fix existing rubber band after tracing - the last point may have been moved if using offset
542  if ( mRubberBand->numberOfVertices() )
543  mRubberBand->movePoint( mapPt );
544  }
545  else if ( mTempRubberBand )
546  mTempRubberBand->movePoint( mapPoint );
547  }
548  }
549  }
550 } // mouseMoveEvent
551 
552 
553 int QgsMapToolCapture::nextPoint( const QgsPoint &mapPoint, QgsPoint &layerPoint )
554 {
555  if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() ) )
556  {
557  try
558  {
559  const QgsPointXY mapP( mapPoint.x(), mapPoint.y() ); //#spellok
560  layerPoint = QgsPoint( toLayerCoordinates( vlayer, mapP ) ); //transform snapped point back to layer crs //#spellok
561  if ( QgsWkbTypes::hasZ( vlayer->wkbType() ) && !layerPoint.is3D() )
563  if ( QgsWkbTypes::hasM( vlayer->wkbType() ) && !layerPoint.isMeasure() )
565  }
566  catch ( QgsCsException & )
567  {
568  QgsDebugMsg( QStringLiteral( "transformation to layer coordinate failed" ) );
569  return 2;
570  }
571  }
572  else
573  {
574  layerPoint = QgsPoint( toLayerCoordinates( layer(), mapPoint ) );
575  }
576 
577  return 0;
578 }
579 
580 int QgsMapToolCapture::nextPoint( QPoint p, QgsPoint &layerPoint, QgsPoint &mapPoint )
581 {
583  return nextPoint( mapPoint, layerPoint );
584 }
585 
587 {
588  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() );
589  QgsVectorLayer *sourceLayer = match.layer();
591  {
593  return 0;
594  }
595  else if ( !vlayer )
596  {
597  return 1;
598  }
599  else
600  {
601  if ( match.isValid() && ( match.hasVertex() || match.hasLineEndpoint() || ( QgsProject::instance()->topologicalEditing() && ( match.hasEdge() || match.hasMiddleSegment() ) ) ) && sourceLayer &&
602  ( sourceLayer->crs() == vlayer->crs() ) )
603  {
604  QgsFeature f;
605  QgsFeatureRequest request;
606  request.setFilterFid( match.featureId() );
607  const bool fetched = match.layer()->getFeatures( request ).nextFeature( f );
608  if ( fetched )
609  {
610  QgsVertexId vId;
611  if ( !f.geometry().vertexIdFromVertexNr( match.vertexIndex(), vId ) )
612  return 2;
613 
614  const QgsGeometry geom( f.geometry() );
615  if ( QgsProject::instance()->topologicalEditing() && ( match.hasEdge() || match.hasMiddleSegment() ) )
616  {
617  QgsVertexId vId2;
618  if ( !f.geometry().vertexIdFromVertexNr( match.vertexIndex() + 1, vId2 ) )
619  return 2;
620  const QgsLineString line( geom.constGet()->vertexAt( vId ), geom.constGet()->vertexAt( vId2 ) );
621 
622  layerPoint = QgsGeometryUtils::closestPoint( line, QgsPoint( match.point() ) );
623  }
624  else
625  {
626  layerPoint = geom.constGet()->vertexAt( vId );
627  if ( QgsWkbTypes::hasZ( vlayer->wkbType() ) && !layerPoint.is3D() )
628  layerPoint.addZValue( defaultZValue() );
629  if ( QgsWkbTypes::hasM( vlayer->wkbType() ) && !layerPoint.isMeasure() )
630  layerPoint.addMValue( defaultMValue() );
631  }
632 
633  // ZM support depends on the target layer
634  if ( !QgsWkbTypes::hasZ( vlayer->wkbType() ) )
635  {
636  layerPoint.dropZValue();
637  }
638 
639  if ( !QgsWkbTypes::hasM( vlayer->wkbType() ) )
640  {
641  layerPoint.dropMValue();
642  }
643 
644  return 0;
645  }
646  else
647  {
648  return 2;
649  }
650  }
651  else
652  {
653  return 1;
654  }
655  }
656 }
657 
659 {
660  return addVertex( point, QgsPointLocator::Match() );
661 }
662 
664 {
665  if ( mode() == CaptureNone )
666  {
667  QgsDebugMsg( QStringLiteral( "invalid capture mode" ) );
668  return 2;
669  }
670 
671  if ( mCapturing && mCurrentCaptureTechnique == Qgis::CaptureTechnique::Streaming && !mAllowAddingStreamingPoints )
672  return 0;
673 
674  QgsPoint layerPoint;
675  if ( layer() )
676  {
677  int res = fetchLayerPoint( match, layerPoint );
678  if ( res != 0 )
679  {
680  res = nextPoint( QgsPoint( point ), layerPoint );
681  if ( res != 0 )
682  {
683  return res;
684  }
685  }
686  }
687  else
688  {
689  layerPoint = QgsPoint( point );
690  }
691  const QgsPoint mapPoint = toMapCoordinates( layer(), layerPoint );
692 
693  if ( mCaptureMode == CapturePoint )
694  {
695  mCaptureCurve.addVertex( layerPoint );
696  mSnappingMatches.append( match );
697  }
698  else
699  {
700  if ( mCaptureFirstPoint.isEmpty() )
701  {
702  mCaptureFirstPoint = mapPoint;
703  }
704 
705  if ( !mRubberBand )
707 
708  if ( !mTempRubberBand )
709  {
710  mTempRubberBand.reset( createCurveRubberBand() );
711  mTempRubberBand->setStringType( mLineDigitizingType );
712  mTempRubberBand->reset( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry, mLineDigitizingType, mapPoint );
713  }
714 
715  bool traceCreated = false;
716  if ( tracingEnabled() )
717  {
718  traceCreated = tracingAddVertex( mapPoint );
719  }
720 
721  // keep new tracing start point if we created a trace. This is useful when tracing with
722  // offset so that the user stays "snapped"
723  mTracingStartPoint = traceCreated ? point : QgsPointXY();
724 
725  if ( !traceCreated )
726  {
727  // ordinary digitizing
728  mTempRubberBand->movePoint( mapPoint ); //move the last point of the temp rubberband before operating with it
729  if ( mTempRubberBand->curveIsComplete() ) //2 points for line and 3 points for circular
730  {
731  if ( QgsCurve *curve = mTempRubberBand->curve() )
732  {
733  addCurve( curve );
734  // add curve append only invalid match to mSnappingMatches,
735  // so we need to remove them and add the one from here if it is valid
736  if ( match.isValid() && mSnappingMatches.count() > 0 && !mSnappingMatches.last().isValid() )
737  {
738  mSnappingMatches.removeLast();
739  if ( mTempRubberBand->stringType() == QgsWkbTypes::CircularString )
740  {
741  // for circular string two points are added and match for intermediate point is stored
742  mSnappingMatches.removeLast();
743  mSnappingMatches.append( mCircularIntermediateMatch );
744  }
745  mSnappingMatches.append( match );
746  }
747  }
748  mCaptureLastPoint = mapPoint;
749  mTempRubberBand->reset( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry, mLineDigitizingType, mCaptureFirstPoint );
750  }
751  else if ( mTempRubberBand->pointsCount() == 0 )
752  {
753  mCaptureLastPoint = mapPoint;
754  mCaptureCurve.addVertex( layerPoint );
755  mSnappingMatches.append( match );
756  }
757  else
758  {
759  if ( mTempRubberBand->stringType() == QgsWkbTypes::CircularString )
760  {
761  mCircularIntermediateMatch = match;
762  }
763  }
764 
765  mTempRubberBand->addPoint( mapPoint );
766  }
767  else
768  {
769  mTempRubberBand->reset( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry, mLineDigitizingType, mCaptureFirstPoint );
770  mTempRubberBand->addPoint( mCaptureLastPoint );
771  }
772  }
773 
774  updateExtraSnapLayer();
775  validateGeometry();
776 
777  return 0;
778 }
779 
781 {
782  if ( !c )
783  {
784  return 1;
785  }
786 
787  if ( !mRubberBand )
788  {
790  }
791 
792  if ( mTempRubberBand )
793  {
794  mTempRubberBand->reset( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry, mLineDigitizingType, mCaptureFirstPoint );
795  const QgsPoint endPt = c->endPoint();
796  mTempRubberBand->addPoint( endPt ); //add last point of c
797  }
798 
799  //transform back to layer CRS in case map CRS and layer CRS are different
801  if ( ct.isValid() )
802  {
803  c->transform( ct, Qgis::TransformDirection::Reverse );
804  }
805  const int countBefore = mCaptureCurve.vertexCount();
806  //if there is only one point, this the first digitized point that are in the this first curve added --> remove the point
807  if ( mCaptureCurve.numPoints() == 1 )
808  mCaptureCurve.removeCurve( 0 );
809 
810  // we set the extendPrevious option to true to avoid creating compound curves with many 2 vertex linestrings -- instead we prefer
811  // to extend linestring curves so that they continue the previous linestring wherever possible...
812  mCaptureCurve.addCurve( c, !mStartNewCurve );
813  mStartNewCurve = false;
814 
815  const int countAfter = mCaptureCurve.vertexCount();
816  const int addedPoint = countAfter - countBefore;
817 
818  updateExtraSnapLayer();
819 
820  for ( int i = 0; i < addedPoint; ++i )
821  mSnappingMatches.append( QgsPointLocator::Match() );
822 
823  resetRubberBand();
824 
825  return 0;
826 }
827 
829 {
830  mCaptureCurve.clear();
831  updateExtraSnapLayer();
832 }
833 
834 QList<QgsPointLocator::Match> QgsMapToolCapture::snappingMatches() const
835 {
836  return mSnappingMatches;
837 }
838 
839 void QgsMapToolCapture::undo( bool isAutoRepeat )
840 {
841  mTracingStartPoint = QgsPointXY();
842 
843  if ( mTempRubberBand )
844  {
845  if ( size() <= 1 && mTempRubberBand->pointsCount() != 0 )
846  return;
847 
848  if ( isAutoRepeat && mIgnoreSubsequentAutoRepeatUndo )
849  return;
850  mIgnoreSubsequentAutoRepeatUndo = false;
851 
852  const QgsPoint lastPoint = mTempRubberBand->lastPoint();
853 
854  if ( mTempRubberBand->stringType() == QgsWkbTypes::CircularString && mTempRubberBand->pointsCount() > 2 )
855  {
856  mTempRubberBand->removeLastPoint();
857  mTempRubberBand->movePoint( lastPoint );
858  return;
859  }
860 
861  QgsVertexId vertexToRemove;
862  vertexToRemove.part = 0;
863  vertexToRemove.ring = 0;
864  vertexToRemove.vertex = size() - 1;
865  if ( mCaptureCurve.numPoints() == 2 && mCaptureCurve.nCurves() == 1 )
866  {
867  // store the first vertex to restore if after deleting the curve
868  // because when only two vertices, removing a point remove all the curve
869  const QgsPoint fp = mCaptureCurve.startPoint();
870  mCaptureCurve.deleteVertex( vertexToRemove );
871  mCaptureCurve.addVertex( fp );
872  }
873  else
874  {
875  const int curvesBefore = mCaptureCurve.nCurves();
876  const bool lastCurveIsLineString = qgsgeometry_cast< QgsLineString * >( mCaptureCurve.curveAt( curvesBefore - 1 ) );
877 
878  const int pointsCountBefore = mCaptureCurve.numPoints();
879  mCaptureCurve.deleteVertex( vertexToRemove );
880  int pointsCountAfter = mCaptureCurve.numPoints();
881  for ( ; pointsCountAfter < pointsCountBefore; pointsCountAfter++ )
882  if ( !mSnappingMatches.empty() )
883  mSnappingMatches.removeLast();
884 
885  // if we have removed the last point in a linestring curve, then we "stick" here and ignore subsequent
886  // autorepeat undo actions until the user releases the undo key and holds it down again. This allows
887  // users to selectively remove portions of the geometry captured with the streaming mode by holding down
888  // the undo key, without risking accidental undo of non-streamed portions.
889  if ( mCaptureCurve.nCurves() < curvesBefore && lastCurveIsLineString )
890  mIgnoreSubsequentAutoRepeatUndo = true;
891  }
892 
893  updateExtraSnapLayer();
894 
895  resetRubberBand();
896 
897  mTempRubberBand->reset( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry, mLineDigitizingType, mCaptureFirstPoint );
898 
899  if ( mCaptureCurve.numPoints() > 0 )
900  {
901  const QgsPoint lastPt = mCaptureCurve.endPoint();
902  mCaptureLastPoint = toMapCoordinates( layer(), lastPt );
903  mTempRubberBand->addPoint( mCaptureLastPoint );
904  mTempRubberBand->movePoint( lastPoint );
905  }
906 
908  validateGeometry();
909  }
910 }
911 
913 {
914  if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape && mCurrentShapeMapTool )
915  {
916  mCurrentShapeMapTool->keyPressEvent( e );
917  if ( e->isAccepted() )
918  return;
919  }
920 
921  // this is backwards, but we can't change now without breaking api because
922  // forever QgsMapTools have had to explicitly mark events as ignored in order to
923  // indicate that they've consumed the event and that the default behavior should not
924  // be applied..!
925  // see QgsMapCanvas::keyPressEvent
926  e->accept();
927 
928  if ( e->key() == Qt::Key_Backspace || e->key() == Qt::Key_Delete )
929  {
930  if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape && mCurrentShapeMapTool )
931  {
932  if ( !e->isAutoRepeat() )
933  {
934  mCurrentShapeMapTool->undo();
935  }
936  }
937  else
938  {
939  undo( e->isAutoRepeat() );
940  }
941 
942  // Override default shortcut management in MapCanvas
943  e->ignore();
944  }
945  else if ( e->key() == Qt::Key_Escape )
946  {
947  if ( mCurrentShapeMapTool )
948  mCurrentShapeMapTool->clean();
949 
950  stopCapturing();
951 
952  // Override default shortcut management in MapCanvas
953  e->ignore();
954  }
955 }
956 
958 {
959  mCapturing = true;
960 }
961 
963 {
964  return mCapturing;
965 }
966 
968 {
969  mRubberBand.reset();
970 
972 
973  qDeleteAll( mGeomErrorMarkers );
974  mGeomErrorMarkers.clear();
975  mGeomErrors.clear();
976 
977  mCaptureFirstPoint = QgsPoint();
978  mCaptureLastPoint = QgsPoint();
979 
980  mTracingStartPoint = QgsPointXY();
981 
982  mCapturing = false;
983  mCaptureCurve.clear();
984  updateExtraSnapLayer();
985  mSnappingMatches.clear();
986  if ( auto *lCurrentVectorLayer = currentVectorLayer() )
987  lCurrentVectorLayer->triggerRepaint();
988 }
989 
991 {
992  mTempRubberBand.reset();
993 }
994 
996 {
997  stopCapturing();
998  if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape && mCurrentShapeMapTool )
999  mCurrentShapeMapTool->clean();
1000 
1001  clearCurve();
1002 }
1003 
1005 {
1006  mCaptureCurve.close();
1007  updateExtraSnapLayer();
1008 }
1009 
1010 void QgsMapToolCapture::validateGeometry()
1011 {
1013  || !( capabilities() & ValidateGeometries )
1014  )
1015  return;
1016 
1017  if ( mValidator )
1018  {
1019  mValidator->deleteLater();
1020  mValidator = nullptr;
1021  }
1022 
1023  mGeomErrors.clear();
1024  while ( !mGeomErrorMarkers.isEmpty() )
1025  {
1026  delete mGeomErrorMarkers.takeFirst();
1027  }
1028 
1029  QgsGeometry geom;
1030 
1031  switch ( mCaptureMode )
1032  {
1033  case CaptureNone:
1034  case CapturePoint:
1035  return;
1036  case CaptureLine:
1037  if ( size() < 2 )
1038  return;
1039  geom = QgsGeometry( mCaptureCurve.curveToLine() );
1040  break;
1041  case CapturePolygon:
1042  if ( size() < 3 )
1043  return;
1044  QgsLineString *exteriorRing = mCaptureCurve.curveToLine();
1045  exteriorRing->close();
1046  QgsPolygon *polygon = new QgsPolygon();
1047  polygon->setExteriorRing( exteriorRing );
1048  geom = QgsGeometry( polygon );
1049  break;
1050  }
1051 
1052  if ( geom.isNull() )
1053  return;
1054 
1055  Qgis::GeometryValidationEngine method = Qgis::GeometryValidationEngine::QgisInternal;
1057  method = Qgis::GeometryValidationEngine::Geos;
1058  mValidator = new QgsGeometryValidator( geom, nullptr, method );
1059  connect( mValidator, &QgsGeometryValidator::errorFound, this, &QgsMapToolCapture::addError );
1060  mValidator->start();
1061  QgsDebugMsgLevel( QStringLiteral( "Validation started" ), 4 );
1062 }
1063 
1064 void QgsMapToolCapture::addError( const QgsGeometry::Error &e )
1065 {
1066  mGeomErrors << e;
1067  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() );
1068  if ( !vlayer )
1069  return;
1070 
1071  if ( e.hasWhere() )
1072  {
1074  vm->setCenter( mCanvas->mapSettings().layerToMapCoordinates( vlayer, e.where() ) );
1076  vm->setPenWidth( 2 );
1077  vm->setToolTip( e.what() );
1078  vm->setColor( Qt::green );
1079  vm->setZValue( vm->zValue() + 1 );
1080  mGeomErrorMarkers << vm;
1081  }
1082 }
1083 
1085 {
1086  return mCaptureCurve.numPoints();
1087 }
1088 
1089 QVector<QgsPointXY> QgsMapToolCapture::points() const
1090 {
1091  QVector<QgsPointXY> pointsXY;
1092  QgsGeometry::convertPointList( pointsZM(), pointsXY );
1093 
1094  return pointsXY;
1095 }
1096 
1098 {
1099  QgsPointSequence pts;
1100  mCaptureCurve.points( pts );
1101  return pts;
1102 }
1103 
1104 void QgsMapToolCapture::setPoints( const QVector<QgsPointXY> &pointList )
1105 {
1106  QgsLineString *line = new QgsLineString( pointList );
1107  mCaptureCurve.clear();
1108  mCaptureCurve.addCurve( line );
1109  updateExtraSnapLayer();
1110  mSnappingMatches.clear();
1111  for ( int i = 0; i < line->length(); ++i )
1112  mSnappingMatches.append( QgsPointLocator::Match() );
1113  resetRubberBand();
1114 }
1115 
1117 {
1118  QgsLineString *line = new QgsLineString( pointList );
1119  mCaptureCurve.clear();
1120  mCaptureCurve.addCurve( line );
1121  updateExtraSnapLayer();
1122  mSnappingMatches.clear();
1123  for ( int i = 0; i < line->length(); ++i )
1124  mSnappingMatches.append( QgsPointLocator::Match() );
1125  resetRubberBand();
1126 }
1127 
1129 {
1130  QgsPoint newPoint( QgsWkbTypes::Point, point.x(), point.y() );
1131 
1132  // get current layer
1133  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() );
1134  if ( !vlayer )
1135  {
1136  return newPoint;
1137  }
1138 
1139  // convert to the corresponding type for a full ZM support
1140  const QgsWkbTypes::Type type = vlayer->wkbType();
1141  if ( QgsWkbTypes::hasZ( type ) && !QgsWkbTypes::hasM( type ) )
1142  {
1143  newPoint.convertTo( QgsWkbTypes::PointZ );
1144  }
1145  else if ( !QgsWkbTypes::hasZ( type ) && QgsWkbTypes::hasM( type ) )
1146  {
1147  newPoint.convertTo( QgsWkbTypes::PointM );
1148  }
1149  else if ( QgsWkbTypes::hasZ( type ) && QgsWkbTypes::hasM( type ) )
1150  {
1151  newPoint.convertTo( QgsWkbTypes::PointZM );
1152  }
1153 
1154  // set z value if necessary
1155  if ( QgsWkbTypes::hasZ( newPoint.wkbType() ) )
1156  {
1158  }
1159  // set m value if necessary
1160  if ( QgsWkbTypes::hasM( newPoint.wkbType() ) )
1161  {
1163  }
1164  return newPoint;
1165 }
1166 
1168 {
1169  QgsPoint newPoint = mapPoint( e.mapPoint() );
1170 
1171  // set z or m value from snapped point if necessary
1172  if ( QgsWkbTypes::hasZ( newPoint.wkbType() ) || QgsWkbTypes::hasM( newPoint.wkbType() ) )
1173  {
1174  // if snapped, z and m dimension are taken from the corresponding snapped
1175  // point.
1176  if ( e.isSnapped() )
1177  {
1178  const QgsPointLocator::Match match = e.mapPointMatch();
1179 
1180  if ( match.layer() )
1181  {
1182  const QgsFeature ft = match.layer()->getFeature( match.featureId() );
1183  if ( QgsWkbTypes::hasZ( match.layer()->wkbType() ) )
1184  {
1185  newPoint.setZ( ft.geometry().vertexAt( match.vertexIndex() ).z() );
1186  }
1187  if ( QgsWkbTypes::hasM( match.layer()->wkbType() ) )
1188  {
1189  newPoint.setM( ft.geometry().vertexAt( match.vertexIndex() ).m() );
1190  }
1191  }
1192  }
1193  }
1194 
1195  return newPoint;
1196 }
1197 
1198 void QgsMapToolCapture::updateExtraSnapLayer()
1199 {
1200  if ( !mExtraSnapLayer )
1201  return;
1202 
1203  if ( canvas()->snappingUtils()->config().selfSnapping() && layer() && mCaptureCurve.numPoints() >= 2 )
1204  {
1205  // the current layer may have changed
1206  mExtraSnapLayer->setCrs( layer()->crs() );
1207  QgsGeometry geom = QgsGeometry( mCaptureCurve.clone() );
1208  // we close the curve to allow snapping on last segment
1209  if ( mCaptureMode == CapturePolygon && mCaptureCurve.numPoints() >= 3 )
1210  {
1211  qgsgeometry_cast<QgsCompoundCurve *>( geom.get() )->close();
1212  }
1213  mExtraSnapLayer->changeGeometry( mExtraSnapFeatureId, geom );
1214  }
1215  else
1216  {
1217  QgsGeometry geom;
1218  mExtraSnapLayer->changeGeometry( mExtraSnapFeatureId, geom );
1219  }
1220 }
1221 
1222 
1224 {
1225  // POINT CAPTURING
1226  if ( mode() == CapturePoint )
1227  {
1228  if ( e->button() != Qt::LeftButton )
1229  return;
1230 
1231  QgsPoint savePoint; //point in layer coordinates
1232  bool isMatchPointZ = false;
1233  bool isMatchPointM = false;
1234  try
1235  {
1236  QgsPoint fetchPoint;
1237  int res = fetchLayerPoint( e->mapPointMatch(), fetchPoint );
1238  isMatchPointZ = QgsWkbTypes::hasZ( fetchPoint.wkbType() );
1239  isMatchPointM = QgsWkbTypes::hasM( fetchPoint.wkbType() );
1240 
1241  if ( res == 0 )
1242  {
1243  QgsWkbTypes::Type geomType = QgsWkbTypes::Type::Point;
1244  if ( isMatchPointM && isMatchPointZ )
1245  {
1246  geomType = QgsWkbTypes::Type::PointZM;
1247  }
1248  else if ( isMatchPointM )
1249  {
1250  geomType = QgsWkbTypes::Type::PointM;
1251  }
1252  else if ( isMatchPointZ )
1253  {
1254  geomType = QgsWkbTypes::Type::PointZ;
1255  }
1256  savePoint = QgsPoint( geomType, fetchPoint.x(), fetchPoint.y(), fetchPoint.z(), fetchPoint.m() );
1257  }
1258  else
1259  {
1261 
1262  savePoint = QgsPoint( point.x(), point.y(), fetchPoint.z(), fetchPoint.m() );
1263  }
1264  }
1265  catch ( QgsCsException &cse )
1266  {
1267  Q_UNUSED( cse )
1268  emit messageEmitted( tr( "Cannot transform the point to the layer's coordinate system" ), Qgis::MessageLevel::Warning );
1269  return;
1270  }
1271 
1272  QgsGeometry g( std::make_unique<QgsPoint>( savePoint ) );
1273 
1274  // The snapping result needs to be added so it's available in the @snapping_results variable of default value etc. expression contexts
1275  addVertex( e->mapPoint(), e->mapPointMatch() );
1276 
1277  geometryCaptured( g );
1278  pointCaptured( savePoint );
1279 
1280  stopCapturing();
1281 
1282  // we are done with digitizing for now so instruct advanced digitizing dock to reset its CAD points
1284  }
1285 
1286  // LINE AND POLYGON CAPTURING
1287  else if ( mode() == CaptureLine || mode() == CapturePolygon )
1288  {
1289  bool digitizingFinished = false;
1290 
1291  if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape )
1292  {
1293  if ( !mCurrentShapeMapTool )
1294  {
1295  emit messageEmitted( tr( "Cannot capture a shape without a shape tool defined" ), Qgis::MessageLevel::Warning );
1296  return;
1297  }
1298  else
1299  {
1300  if ( !mTempRubberBand )
1301  {
1302  mTempRubberBand.reset( createCurveRubberBand() );
1303  mTempRubberBand->setStringType( mLineDigitizingType );
1304  mTempRubberBand->setRubberBandGeometryType( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry );
1305  }
1306 
1307  digitizingFinished = mCurrentShapeMapTool->cadCanvasReleaseEvent( e, mCaptureMode );
1308  if ( digitizingFinished )
1309  mCurrentShapeMapTool->clean();
1310  }
1311  }
1312  else // i.e. not shape
1313  {
1314  //add point to list and to rubber band
1315  if ( e->button() == Qt::LeftButton )
1316  {
1317  const int error = addVertex( e->mapPoint(), e->mapPointMatch() );
1318  if ( error == 2 )
1319  {
1320  //problem with coordinate transformation
1321  emit messageEmitted( tr( "Cannot transform the point to the layers coordinate system" ), Qgis::MessageLevel::Warning );
1322  return;
1323  }
1324 
1325  startCapturing();
1326  }
1327  else if ( e->button() == Qt::RightButton )
1328  {
1329  // End of string
1331 
1332  //lines: bail out if there are not at least two vertices
1333  if ( mode() == CaptureLine && size() < 2 )
1334  {
1335  stopCapturing();
1336  return;
1337  }
1338 
1339  //polygons: bail out if there are not at least two vertices
1340  if ( mode() == CapturePolygon && size() < 3 )
1341  {
1342  stopCapturing();
1343  return;
1344  }
1345 
1346  if ( mode() == CapturePolygon || e->modifiers() == Qt::ShiftModifier )
1347  {
1348  closePolygon();
1349  }
1350 
1351  digitizingFinished = true;
1352  }
1353  }
1354 
1355  if ( digitizingFinished )
1356  {
1357  QgsGeometry g;
1358  QgsCurve *curveToAdd = captureCurve()->clone();
1359 
1360  if ( mode() == CaptureLine )
1361  {
1362  g = QgsGeometry( curveToAdd );
1363  geometryCaptured( g );
1364  lineCaptured( curveToAdd );
1365  }
1366  else
1367  {
1368  QgsCurvePolygon *poly = new QgsCurvePolygon();
1369  poly->setExteriorRing( curveToAdd );
1370  g = QgsGeometry( poly );
1371  geometryCaptured( g );
1372  polygonCaptured( poly );
1373  }
1374 
1375  stopCapturing();
1376  }
1377  }
1378 }
T * release()
Clears the pointer and returns it.
void reset(T *p=nullptr)
Will reset the managed pointer to p.
CaptureTechnique
Capture technique.
Definition: qgis.h:153
@ Shape
Digitize shapes.
@ StraightSegments
Default capture mode - capture occurs with straight line segments.
@ CircularString
Capture in circular strings.
@ Streaming
Streaming points digitizing mode (points are automatically added as the mouse cursor moves).
GeometryValidationEngine
Available engines for validating geometries.
Definition: qgis.h:805
bool is3D() const SIP_HOLDGIL
Returns true if the geometry is 3D and contains a z-value.
virtual QgsPoint vertexAt(QgsVertexId id) const =0
Returns the point corresponding to a specified vertex id.
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 clearPoints()
Removes all points from the CAD point list.
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.
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.
virtual void setExteriorRing(QgsCurve *ring)
Sets the exterior ring of the polygon.
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
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
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:2405
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.
QgsWkbTypes::Type wkbType() const SIP_HOLDGIL
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
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:90
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.
QgsPointXY mapToLayerCoordinates(const QgsMapLayer *layer, QgsPointXY point) const
transform point coordinates from output CRS to layer's 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.
void activate() override
Registers this maptool with the cad dock widget.
CaptureMode
Different capture modes.
@ CapturePolygon
Capture polygons.
@ CaptureNone
Do not capture / determine mode from layer geometry type.
@ CapturePoint
Capture points.
@ CaptureLine
Capture lines.
Q_DECL_DEPRECATED void setCircularDigitizingEnabled(bool enable)
Enable the digitizing with curve.
void deleteTempRubberBand()
Clean a temporary rubberband.
void clean() override
convenient method to clean members
~QgsMapToolCapture() override
void closePolygon()
Close an open polygon.
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.
QgsPointSequence pointsZM() const
List of digitized points.
Q_DECL_DEPRECATED void setPoints(const QVector< QgsPointXY > &pointList)
Set the points on which to work.
QList< QgsPointLocator::Match > snappingMatches() const
Returns a list of matches for each point on the captureCurve.
Q_DECL_DEPRECATED QVector< QgsPointXY > points() const
List of digitized points.
bool isCapturing() const
Are we currently capturing?
virtual bool supportsTechnique(Qgis::CaptureTechnique technique) const
Returns true if the tool supports the specified capture technique.
void setCurrentShapeMapTool(const QgsMapToolShapeMetadata *shapeMapToolMetadata)
Sets the current shape tool.
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 setCurrentCaptureTechnique(Qgis::CaptureTechnique technique)
Sets the current capture if it is supported by the map tool.
virtual QgsMapToolCapture::Capabilities capabilities() const
Returns flags containing the supported capabilities.
void clearCurve()
Clear capture curve.
int nextPoint(const QgsPoint &mapPoint, QgsPoint &layerPoint)
Converts a map point to layer coordinates.
const QgsCompoundCurve * captureCurve() const
Gets the capture curve.
Q_DECL_DEPRECATED 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.
QgsRubberBand * takeRubberBand()
Returns the rubberBand currently owned by this map tool and transfers ownership to the caller.
void cadCanvasReleaseEvent(QgsMapMouseEvent *e) override
Override this method when subclassing this class.
static double defaultMValue()
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)
static double defaultZValue()
Returns default Z value.
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)
virtual void deactivate()
Deactivates the map tool.
virtual void cadCanvasMoveEvent(QgsMapMouseEvent *e, QgsMapToolCapture::CaptureMode mode)=0
Called for a mouse move event.
QString id() const
Returns the id of the shape tool (equivalent to the one from the metadata)
virtual void undo()
Called to undo last action (last point added)
virtual void activate(QgsMapToolCapture::CaptureMode mode, const QgsPoint &lastCapturedMapPoint)
Activates the map tool with the last captured map point.
virtual void clean()
Called to clean the map tool (after canceling the operation or when the digitization has finished)
virtual bool cadCanvasReleaseEvent(QgsMapMouseEvent *e, QgsMapToolCapture::CaptureMode mode)=0
Called for a mouse release event Must return true if the digitization has ended and the geometry is c...
virtual void keyPressEvent(QKeyEvent *e)
Filters a key press event Ignores the event in default implementation.
QgsMapToolShapeMetadata is a base class for shape map tools metadata to be used in QgsMapToolShapeReg...
virtual QString id() const =0
Unique ID for the shape map tool.
virtual QgsMapToolShapeAbstract * factory(QgsMapToolCapture *parentlTool) const SIP_FACTORY=0
Creates the shape map tool for the given parentTool Caller takes ownership of the returned object.
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
virtual void setCursor(const QCursor &cursor)
Sets a user defined cursor.
Definition: qgsmaptool.cpp:160
void messageEmitted(const QString &message, Qgis::MessageLevel=Qgis::MessageLevel::Info)
emit a message
QPoint toCanvasCoordinates(const QgsPointXY &point) const
Transforms a point from map coordinates to screen coordinates.
Definition: qgsmaptool.cpp:77
bool isActive() const
Returns if the current map tool active on the map canvas.
Definition: qgsmaptool.cpp:145
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
bool convertTo(QgsWkbTypes::Type type) override
Converts the geometry to a specified type.
Definition: qgspoint.cpp:620
void setZ(double z) SIP_HOLDGIL
Sets the point's z-coordinate.
Definition: qgspoint.h:304
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:479
void snappingConfigChanged(const QgsSnappingConfig &config)
Emitted whenever the configuration for snapping has changed.
bool topologicalEditing
Definition: qgsproject.h:120
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...
T value(const QString &dynamicKeyPart=QString()) 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.
Q_INVOKABLE bool startEditing()
Makes the layer editable.
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 addFeature(QgsFeature &feature, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) FINAL
Adds a single feature to the sink.
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.
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 Type flatType(Type type) SIP_HOLDGIL
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:732
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
#define BUILTIN_UNREACHABLE
Definition: qgis.h:2704
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