QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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 "qgssettings.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  if ( mTempRubberBand )
99  mTempRubberBand->show();
100 
101  mCanvas->snappingUtils()->addExtraSnapLayer( mExtraSnapLayer );
103 }
104 
106 {
107  if ( mTempRubberBand )
108  mTempRubberBand->hide();
109 
110  mSnapIndicator->setMatch( QgsPointLocator::Match() );
111 
112  mCanvas->snappingUtils()->removeExtraSnapLayer( mExtraSnapLayer );
114 }
115 
116 void QgsMapToolCapture::currentLayerChanged( QgsMapLayer *layer )
117 {
118  if ( !mCaptureModeFromLayer )
119  return;
120 
121  mCaptureMode = CaptureNone;
122 
123  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
124  if ( !vlayer )
125  {
126  return;
127  }
128 
129  switch ( vlayer->geometryType() )
130  {
132  mCaptureMode = CapturePoint;
133  break;
135  mCaptureMode = CaptureLine;
136  break;
138  mCaptureMode = CapturePolygon;
139  break;
140  default:
141  mCaptureMode = CaptureNone;
142  break;
143  }
144 
145  if ( mTempRubberBand )
146  mTempRubberBand->setRubberBandGeometryType( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry );
147 
148  resetRubberBand();
149 }
150 
151 
152 bool QgsMapToolCapture::tracingEnabled()
153 {
155  return tracer && ( !tracer->actionEnableTracing() || tracer->actionEnableTracing()->isChecked() )
156  && ( !tracer->actionEnableSnapping() || tracer->actionEnableSnapping()->isChecked() );
157 }
158 
159 
160 QgsPointXY QgsMapToolCapture::tracingStartPoint()
161 {
162  // if we have starting point from previous trace, then preferably use that one
163  // (useful when tracing with offset)
164  if ( mTracingStartPoint != QgsPointXY() )
165  return mTracingStartPoint;
166 
167  return lastCapturedMapPoint();
168 }
169 
170 
171 bool QgsMapToolCapture::tracingMouseMove( QgsMapMouseEvent *e )
172 {
173  if ( !e->isSnapped() )
174  return false;
175 
176  QgsPointXY pt0 = tracingStartPoint();
177  if ( pt0 == QgsPointXY() )
178  return false;
179 
181  if ( !tracer )
182  return false; // this should not happen!
183 
185  QVector<QgsPointXY> points = tracer->findShortestPath( pt0, e->mapPoint(), &err );
186  if ( points.isEmpty() )
187  {
188  tracer->reportError( err, false );
189  return false;
190  }
191 
192  mTempRubberBand->reset( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry, QgsWkbTypes::LineString, firstCapturedMapPoint() );
193  mTempRubberBand->addPoint( lastCapturedMapPoint() );
194 
195  // if there is offset, we need to fix the rubber bands to make sure they are aligned correctly.
196  // There are two cases we need to sort out:
197  // 1. the last point of mRubberBand may need to be moved off the traced curve to respect the offset
198  // 2. first point of mTempRubberBand may be needed to be moved to the beginning of the offset trace
199  QgsPoint lastPoint = lastCapturedMapPoint();
200  QgsPointXY lastPointXY( lastPoint );
201  if ( lastPointXY == pt0 && points[0] != lastPointXY )
202  {
203  if ( mRubberBand->numberOfVertices() != 0 )
204  {
205  // if rubber band had just one point, for some strange reason it contains the point twice
206  // we only want to move the last point if there are multiple points already
207  if ( mRubberBand->numberOfVertices() > 2 || ( mRubberBand->numberOfVertices() == 2 && *mRubberBand->getPoint( 0, 0 ) != *mRubberBand->getPoint( 0, 1 ) ) )
208  mRubberBand->movePoint( points[0] );
209  }
210 
211  mTempRubberBand->movePoint( 0, QgsPoint( points[0] ) );
212  }
213 
214  mTempRubberBand->movePoint( QgsPoint( points[0] ) );
215 
216  // update temporary rubberband
217  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
218  mTempRubberBand->addPoint( QgsPoint( points.at( i ) ), i == points.count() - 1 );
219 
220 
221  mTempRubberBand->addPoint( QgsPoint( points[points.size() - 1] ) );
222 
223  tracer->reportError( QgsTracer::ErrNone, false ); // clear messagebar if there was any error
224  return true;
225 }
226 
227 
228 bool QgsMapToolCapture::tracingAddVertex( const QgsPointXY &point )
229 {
231  if ( !tracer )
232  return false; // this should not happen!
233 
234  if ( mTempRubberBand->pointsCount() == 0 )
235  {
236  if ( !tracer->init() )
237  {
239  return false;
240  }
241 
242  // only accept first point if it is snapped to the graph (to vertex or edge)
243  bool res = tracer->isPointSnapped( point );
244  if ( res )
245  {
246  mTracingStartPoint = point;
247  }
248  return false;
249  }
250 
251  QgsPointXY pt0 = tracingStartPoint();
252  if ( pt0 == QgsPointXY() )
253  return false;
254 
256  QVector<QgsPointXY> points = tracer->findShortestPath( pt0, point, &err );
257  if ( points.isEmpty() )
258  return false; // ignore the vertex - can't find path to the end point!
259 
260  // transform points
261  QgsPointSequence layerPoints;
262  QgsPoint lp; // in layer coords
263  for ( int i = 0; i < points.count(); ++i )
264  {
265  if ( nextPoint( QgsPoint( points[i] ), lp ) != 0 )
266  return false;
267  layerPoints << lp;
268  }
269 
270  // Move the last point of the captured curve to the first point on the trace string (necessary if there is offset)
271  QgsVertexId lastVertexId( 0, 0, mCaptureCurve.numPoints() - 1 );
272  mCaptureCurve.moveVertex( lastVertexId, layerPoints.first() );
273  mSnappingMatches.removeLast();
274  mSnappingMatches.append( QgsPointLocator::Match() );
275 
276  int pointBefore = mCaptureCurve.numPoints();
277  mCaptureCurve.addCurve( new QgsLineString( layerPoints ) );
278 
279  resetRubberBand();
280 
281  // Curves de-approximation
282  QgsSettings settings;
283  if ( settings.value( QStringLiteral( "/qgis/digitizing/convert_to_curve" ), false ).toBool() )
284  {
285  // If the tool and the layer support curves
286  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() );
287  if ( capabilities().testFlag( QgsMapToolCapture::Capability::SupportsCurves ) && vlayer->dataProvider()->capabilities().testFlag( QgsVectorDataProvider::Capability::CircularGeometries ) )
288  {
289  QgsGeometry linear = QgsGeometry( mCaptureCurve.segmentize() );
290  QgsGeometry curved = linear.convertToCurves(
291  settings.value( QStringLiteral( "/qgis/digitizing/convert_to_curve_angle_tolerance" ), 1e-6 ).toDouble(),
292  settings.value( QStringLiteral( "/qgis/digitizing/convert_to_curve_distance_tolerance" ), 1e-6 ).toDouble()
293  );
294  mCaptureCurve = *qgsgeometry_cast<QgsCompoundCurve *>( curved.constGet() );
295  }
296  }
297 
298  // sync the snapping matches list
299  int pointAfter = mCaptureCurve.numPoints();
300  for ( ; pointBefore < pointAfter; ++pointBefore )
301  mSnappingMatches.append( QgsPointLocator::Match() );
302 
303  tracer->reportError( QgsTracer::ErrNone, true ); // clear messagebar if there was any error
304 
305  // adjust last captured point
306  const QgsPoint lastPt = mCaptureCurve.endPoint();
307  mCaptureLastPoint = toMapCoordinates( qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() ), lastPt );
308 
309  return true;
310 }
311 
312 QgsMapToolCaptureRubberBand *QgsMapToolCapture::createCurveRubberBand() const
313 {
314  QgsSettings settings;
315  QgsMapToolCaptureRubberBand *rb = new QgsMapToolCaptureRubberBand( mCanvas );
316  rb->setStrokeWidth( digitizingStrokeWidth() );
317  QColor color = digitizingStrokeColor();
318 
319  double alphaScale = settings.value( QStringLiteral( "qgis/digitizing/line_color_alpha_scale" ), 0.75 ).toDouble();
320  color.setAlphaF( color.alphaF() * alphaScale );
321  rb->setLineStyle( Qt::DotLine );
322  rb->setStrokeColor( color );
323 
324  QColor fillColor = digitizingFillColor();
325  rb->setFillColor( fillColor );
326  rb->show();
327  return rb;
328 }
329 
330 QgsPoint QgsMapToolCapture::firstCapturedMapPoint()
331 {
332  return mCaptureFirstPoint;
333 }
334 
335 QgsPoint QgsMapToolCapture::lastCapturedMapPoint()
336 {
337  return mCaptureLastPoint;
338 }
339 
340 void QgsMapToolCapture::resetRubberBand()
341 {
342  if ( !mRubberBand )
343  return;
344  QgsLineString *lineString = mCaptureCurve.curveToLine();
346  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() );
347  mRubberBand->addGeometry( QgsGeometry( lineString ), vlayer );
348 }
349 
351 {
352  return mRubberBand.release();
353 }
354 
356 {
357  mDigitizingType = enable ? QgsWkbTypes::CircularString : QgsWkbTypes::LineString;
358  if ( mTempRubberBand )
359  mTempRubberBand->setStringType( mDigitizingType );
360 }
361 
362 
364 {
366  QgsPointXY point = e->mapPoint();
367 
368  mSnapIndicator->setMatch( e->mapPointMatch() );
369 
370  QgsPoint mapPoint = QgsPoint( point );
371 
372  if ( mCaptureMode != CapturePoint && mTempRubberBand && mCapturing )
373  {
374  bool hasTrace = false;
375 
376 
377  if ( tracingEnabled() && mCaptureCurve.numPoints() != 0 )
378  {
379  // Store the intermediate point for circular string to retrieve after tracing mouse move if
380  // the digitizing type is circular and the temp rubber band is effectivly circular and if this point is existing
381  // Store an empty point if the digitizing type is linear ot the point is not existing (curve not complete)
382  if ( mDigitizingType == QgsWkbTypes::CircularString &&
383  mTempRubberBand->stringType() == QgsWkbTypes::CircularString &&
384  mTempRubberBand->curveIsComplete() )
385  mCircularItermediatePoint = mTempRubberBand->pointFromEnd( 1 );
386  else if ( mDigitizingType == QgsWkbTypes::LineString ||
387  !mTempRubberBand->curveIsComplete() )
388  mCircularItermediatePoint = QgsPoint();
389 
390  hasTrace = tracingMouseMove( e );
391 
392  if ( !hasTrace )
393  {
394  // Restore the temp rubber band
395  mTempRubberBand->reset( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry, mDigitizingType, firstCapturedMapPoint() );
396  mTempRubberBand->addPoint( lastCapturedMapPoint() );
397  if ( !mCircularItermediatePoint.isEmpty() )
398  {
399  mTempRubberBand->movePoint( mCircularItermediatePoint );
400  mTempRubberBand->addPoint( mCircularItermediatePoint );
401  }
402  }
403  }
404 
405  if ( !hasTrace )
406  {
407  if ( mCaptureCurve.numPoints() > 0 )
408  {
409  const QgsPoint mapPt = lastCapturedMapPoint();
410 
411  if ( mTempRubberBand )
412  {
413  mTempRubberBand->movePoint( mapPoint );
414  mTempRubberBand->movePoint( 0, mapPt );
415  }
416 
417  // fix existing rubber band after tracing - the last point may have been moved if using offset
418  if ( mRubberBand->numberOfVertices() )
419  mRubberBand->movePoint( mapPt );
420  }
421  else if ( mTempRubberBand )
422  mTempRubberBand->movePoint( mapPoint );
423  }
424  }
425 } // mouseMoveEvent
426 
427 
428 int QgsMapToolCapture::nextPoint( const QgsPoint &mapPoint, QgsPoint &layerPoint )
429 {
430  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() );
431  if ( !vlayer )
432  {
433  QgsDebugMsg( QStringLiteral( "no vector layer" ) );
434  return 1;
435  }
436  try
437  {
438  QgsPointXY mapP( mapPoint.x(), mapPoint.y() ); //#spellok
439  layerPoint = QgsPoint( toLayerCoordinates( vlayer, mapP ) ); //transform snapped point back to layer crs //#spellok
440  if ( QgsWkbTypes::hasZ( vlayer->wkbType() ) )
441  layerPoint.addZValue( defaultZValue() );
442  if ( QgsWkbTypes::hasM( vlayer->wkbType() ) )
443  layerPoint.addMValue( 0.0 );
444  }
445  catch ( QgsCsException &cse )
446  {
447  Q_UNUSED( cse )
448  QgsDebugMsg( QStringLiteral( "transformation to layer coordinate failed" ) );
449  return 2;
450  }
451 
452  return 0;
453 }
454 
455 int QgsMapToolCapture::nextPoint( QPoint p, QgsPoint &layerPoint, QgsPoint &mapPoint )
456 {
458  return nextPoint( mapPoint, layerPoint );
459 }
460 
462 {
463  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() );
464  QgsVectorLayer *sourceLayer = match.layer();
465  if ( match.isValid() && ( match.hasVertex() || ( QgsProject::instance()->topologicalEditing() && ( match.hasEdge() || match.hasMiddleSegment() ) ) ) && sourceLayer &&
466  ( sourceLayer->crs() == vlayer->crs() ) )
467  {
468  QgsFeature f;
469  QgsFeatureRequest request;
470  request.setFilterFid( match.featureId() );
471  bool fetched = match.layer()->getFeatures( request ).nextFeature( f );
472  if ( fetched )
473  {
474  QgsVertexId vId;
475  if ( !f.geometry().vertexIdFromVertexNr( match.vertexIndex(), vId ) )
476  return 2;
477 
478  const QgsGeometry geom( f.geometry() );
479  if ( QgsProject::instance()->topologicalEditing() && ( match.hasEdge() || match.hasMiddleSegment() ) )
480  {
481  QgsVertexId vId2;
482  if ( !f.geometry().vertexIdFromVertexNr( match.vertexIndex() + 1, vId2 ) )
483  return 2;
484  QgsLineString line( geom.constGet()->vertexAt( vId ), geom.constGet()->vertexAt( vId2 ) );
485 
486  layerPoint = QgsGeometryUtils::closestPoint( line, QgsPoint( match.point() ) );
487  }
488  else
489  {
490  layerPoint = geom.constGet()->vertexAt( vId );
491  if ( QgsWkbTypes::hasZ( vlayer->wkbType() ) && !layerPoint.is3D() )
492  layerPoint.addZValue( defaultZValue() );
493  if ( QgsWkbTypes::hasM( vlayer->wkbType() ) && !layerPoint.isMeasure() )
494  layerPoint.addMValue( 0.0 );
495  }
496 
497  // ZM support depends on the target layer
498  if ( !QgsWkbTypes::hasZ( vlayer->wkbType() ) )
499  {
500  layerPoint.dropZValue();
501  }
502 
503  if ( !QgsWkbTypes::hasM( vlayer->wkbType() ) )
504  {
505  layerPoint.dropMValue();
506  }
507 
508  return 0;
509  }
510  else
511  {
512  return 2;
513  }
514  }
515  else
516  {
517  return 1;
518  }
519 }
520 
521 int QgsMapToolCapture::addVertex( const QgsPointXY &point )
522 {
523  return addVertex( point, QgsPointLocator::Match() );
524 }
525 
526 int QgsMapToolCapture::addVertex( const QgsPointXY &point, const QgsPointLocator::Match &match )
527 {
528  if ( mode() == CaptureNone )
529  {
530  QgsDebugMsg( QStringLiteral( "invalid capture mode" ) );
531  return 2;
532  }
533 
534  int res;
535  QgsPoint layerPoint;
536  res = fetchLayerPoint( match, layerPoint );
537  if ( res != 0 )
538  {
539  res = nextPoint( QgsPoint( point ), layerPoint );
540  if ( res != 0 )
541  {
542  return res;
543  }
544  }
545  QgsPoint mapPoint = toMapCoordinates( canvas()->currentLayer(), layerPoint );
546 
547  if ( mCaptureMode == CapturePoint )
548  {
549  mCaptureCurve.addVertex( layerPoint );
550  mSnappingMatches.append( match );
551  }
552  else
553  {
554  if ( mCaptureFirstPoint.isEmpty() )
555  {
556  mCaptureFirstPoint = mapPoint;
557  }
558 
559  if ( !mRubberBand )
561 
562  if ( !mTempRubberBand )
563  {
564  mTempRubberBand.reset( createCurveRubberBand() );
565  mTempRubberBand->setStringType( mDigitizingType );
566  mTempRubberBand->reset( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry, mDigitizingType, mapPoint );
567  }
568 
569  bool traceCreated = false;
570  if ( tracingEnabled() )
571  {
572  traceCreated = tracingAddVertex( mapPoint );
573  }
574 
575  // keep new tracing start point if we created a trace. This is useful when tracing with
576  // offset so that the user stays "snapped"
577  mTracingStartPoint = traceCreated ? point : QgsPointXY();
578 
579  if ( !traceCreated )
580  {
581  // ordinary digitizing
582  mTempRubberBand->movePoint( mapPoint ); //move the last point of the temp rubberband before operating with it
583  if ( mTempRubberBand->curveIsComplete() ) //2 points for line and 3 points for circular
584  {
585  const QgsCurve *curve = mTempRubberBand->curve();
586  if ( curve )
587  {
588  addCurve( curve->clone() );
589  // add curve append only invalid match to mSnappingMatches,
590  // so we need to remove them and add the one from here if it is valid
591  if ( match.isValid() && mSnappingMatches.count() > 0 && !mSnappingMatches.last().isValid() )
592  {
593  mSnappingMatches.removeLast();
594  if ( mTempRubberBand->stringType() == QgsWkbTypes::CircularString )
595  {
596  // for circular string two points are added and match for intermediate point is stored
597  mSnappingMatches.removeLast();
598  mSnappingMatches.append( mCircularIntermediateMatch );
599  }
600  mSnappingMatches.append( match );
601  }
602  }
603  mCaptureLastPoint = mapPoint;
604  mTempRubberBand->reset( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry, mDigitizingType, firstCapturedMapPoint() );
605  }
606  else if ( mCaptureCurve.numPoints() == 0 )
607  {
608  mCaptureLastPoint = mapPoint;
609  mCaptureCurve.addVertex( layerPoint );
610  mSnappingMatches.append( match );
611  }
612  else
613  {
614  if ( mTempRubberBand->stringType() == QgsWkbTypes::CircularString )
615  {
616  mCircularIntermediateMatch = match;
617  }
618  }
619 
620  mTempRubberBand->addPoint( mapPoint );
621  }
622  else
623  {
624  mTempRubberBand->reset( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry, mDigitizingType, firstCapturedMapPoint() );
625  mTempRubberBand->addPoint( lastCapturedMapPoint() );
626  }
627  }
628 
629  updateExtraSnapLayer();
630  validateGeometry();
631 
632  return 0;
633 }
634 
636 {
637  if ( !c )
638  {
639  return 1;
640  }
641 
642  if ( !mRubberBand )
643  {
645  }
646 
647  if ( mTempRubberBand )
648  {
649  mTempRubberBand->reset( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry, mDigitizingType, firstCapturedMapPoint() );
650  QgsPoint endPt = c->endPoint();
651  mTempRubberBand->addPoint( endPt ); //add last point of c
652  }
653 
654  //transform back to layer CRS in case map CRS and layer CRS are different
655  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() );
657  if ( ct.isValid() )
658  {
660  }
661  int countBefore = mCaptureCurve.vertexCount();
662  //if there is only one point, this the first digitized point that are in the this first curve added --> remove the point
663  if ( mCaptureCurve.numPoints() == 1 )
664  mCaptureCurve.removeCurve( 0 );
665  mCaptureCurve.addCurve( c );
666  int countAfter = mCaptureCurve.vertexCount();
667  int addedPoint = countAfter - countBefore;
668 
669  updateExtraSnapLayer();
670 
671  for ( int i = 0; i < addedPoint; ++i )
672  mSnappingMatches.append( QgsPointLocator::Match() );
673 
674  resetRubberBand();
675 
676  return 0;
677 }
678 
680 {
681  mCaptureCurve.clear();
682  updateExtraSnapLayer();
683 }
684 
685 QList<QgsPointLocator::Match> QgsMapToolCapture::snappingMatches() const
686 {
687  return mSnappingMatches;
688 }
689 
691 {
692  mTracingStartPoint = QgsPointXY();
693 
694  if ( mTempRubberBand )
695  {
696  if ( size() <= 1 && mTempRubberBand->pointsCount() != 0 )
697  return;
698 
699  QgsPoint lastPoint = mTempRubberBand->lastPoint();
700 
701  if ( mTempRubberBand->stringType() == QgsWkbTypes::CircularString && mTempRubberBand->pointsCount() > 2 )
702  {
703  mTempRubberBand->removeLastPoint();
704  mTempRubberBand->movePoint( lastPoint );
705  return;
706  }
707 
708  QgsVertexId vertexToRemove;
709  vertexToRemove.part = 0;
710  vertexToRemove.ring = 0;
711  vertexToRemove.vertex = size() - 1;
712  if ( mCaptureCurve.numPoints() == 2 && mCaptureCurve.nCurves() == 1 )
713  {
714  // store the first vertex to restore if after deleting the curve
715  // because when only two vertices, removing a point remove all the curve
716  QgsPoint fp = mCaptureCurve.startPoint();
717  mCaptureCurve.deleteVertex( vertexToRemove );
718  mCaptureCurve.addVertex( fp );
719  }
720  else
721  {
722  int pointsCountBefore = mCaptureCurve.numPoints();
723  mCaptureCurve.deleteVertex( vertexToRemove );
724  int pointsCountAfter = mCaptureCurve.numPoints();
725  for ( ; pointsCountAfter < pointsCountBefore; pointsCountAfter++ )
726  if ( !mSnappingMatches.empty() )
727  mSnappingMatches.removeLast();
728  }
729 
730  updateExtraSnapLayer();
731 
732  resetRubberBand();
733 
734  mTempRubberBand->reset( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry, mDigitizingType, firstCapturedMapPoint() );
735 
736  if ( mCaptureCurve.numPoints() > 0 )
737  {
738  const QgsPoint lastPt = mCaptureCurve.endPoint();
739  mCaptureLastPoint = toMapCoordinates( qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() ), lastPt );
740  mTempRubberBand->addPoint( lastCapturedMapPoint() );
741  mTempRubberBand->movePoint( lastPoint );
742  }
743 
745  validateGeometry();
746  }
747 }
748 
749 void QgsMapToolCapture::keyPressEvent( QKeyEvent *e )
750 {
751  if ( e->key() == Qt::Key_Backspace || e->key() == Qt::Key_Delete )
752  {
753  undo();
754 
755  // Override default shortcut management in MapCanvas
756  e->ignore();
757  }
758  else if ( e->key() == Qt::Key_Escape )
759  {
760  stopCapturing();
761 
762  // Override default shortcut management in MapCanvas
763  e->ignore();
764  }
765 }
766 
768 {
769  mCapturing = true;
770 }
771 
773 {
774  return mCapturing;
775 }
776 
778 {
779  mRubberBand.reset();
780 
782 
783  qDeleteAll( mGeomErrorMarkers );
784  mGeomErrorMarkers.clear();
785  mGeomErrors.clear();
786 
787  mCaptureFirstPoint = QgsPoint();
788  mCaptureLastPoint = QgsPoint();
789 
790  mTracingStartPoint = QgsPointXY();
791 
792  mCapturing = false;
793  mCaptureCurve.clear();
794  updateExtraSnapLayer();
795  mSnappingMatches.clear();
796  if ( auto *lCurrentVectorLayer = currentVectorLayer() )
797  lCurrentVectorLayer->triggerRepaint();
798 }
799 
801 {
802  mTempRubberBand.reset();
803 }
804 
806 {
807  stopCapturing();
808  clearCurve();
809 }
810 
812 {
813  mCaptureCurve.close();
814  updateExtraSnapLayer();
815 }
816 
817 void QgsMapToolCapture::validateGeometry()
818 {
819  QgsSettings settings;
820  if ( settings.value( QStringLiteral( "qgis/digitizing/validate_geometries" ), 1 ).toInt() == 0 )
821  return;
822 
823  if ( mValidator )
824  {
825  mValidator->deleteLater();
826  mValidator = nullptr;
827  }
828 
829  mGeomErrors.clear();
830  while ( !mGeomErrorMarkers.isEmpty() )
831  {
832  delete mGeomErrorMarkers.takeFirst();
833  }
834 
835  QgsGeometry geom;
836 
837  switch ( mCaptureMode )
838  {
839  case CaptureNone:
840  case CapturePoint:
841  return;
842  case CaptureLine:
843  if ( size() < 2 )
844  return;
845  geom = QgsGeometry( mCaptureCurve.curveToLine() );
846  break;
847  case CapturePolygon:
848  if ( size() < 3 )
849  return;
850  QgsLineString *exteriorRing = mCaptureCurve.curveToLine();
851  exteriorRing->close();
852  QgsPolygon *polygon = new QgsPolygon();
853  polygon->setExteriorRing( exteriorRing );
854  geom = QgsGeometry( polygon );
855  break;
856  }
857 
858  if ( geom.isNull() )
859  return;
860 
862  if ( settings.value( QStringLiteral( "qgis/digitizing/validate_geometries" ), 1 ).toInt() == 2 )
864  mValidator = new QgsGeometryValidator( geom, nullptr, method );
865  connect( mValidator, &QgsGeometryValidator::errorFound, this, &QgsMapToolCapture::addError );
866  mValidator->start();
867  QgsDebugMsgLevel( QStringLiteral( "Validation started" ), 4 );
868 }
869 
870 void QgsMapToolCapture::addError( const QgsGeometry::Error &e )
871 {
872  mGeomErrors << e;
873  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() );
874  if ( !vlayer )
875  return;
876 
877  if ( e.hasWhere() )
878  {
880  vm->setCenter( mCanvas->mapSettings().layerToMapCoordinates( vlayer, e.where() ) );
882  vm->setPenWidth( 2 );
883  vm->setToolTip( e.what() );
884  vm->setColor( Qt::green );
885  vm->setZValue( vm->zValue() + 1 );
886  mGeomErrorMarkers << vm;
887  }
888 }
889 
891 {
892  return mCaptureCurve.numPoints();
893 }
894 
895 QVector<QgsPointXY> QgsMapToolCapture::points() const
896 {
897  QVector<QgsPointXY> pointsXY;
899 
900  return pointsXY;
901 }
902 
904 {
905  QgsPointSequence pts;
906  mCaptureCurve.points( pts );
907  return pts;
908 }
909 
910 void QgsMapToolCapture::setPoints( const QVector<QgsPointXY> &pointList )
911 {
912  QgsLineString *line = new QgsLineString( pointList );
913  mCaptureCurve.clear();
914  mCaptureCurve.addCurve( line );
915  updateExtraSnapLayer();
916  mSnappingMatches.clear();
917  for ( int i = 0; i < line->length(); ++i )
918  mSnappingMatches.append( QgsPointLocator::Match() );
919  resetRubberBand();
920 }
921 
922 void QgsMapToolCapture::setPoints( const QgsPointSequence &pointList )
923 {
924  QgsLineString *line = new QgsLineString( pointList );
925  mCaptureCurve.clear();
926  mCaptureCurve.addCurve( line );
927  updateExtraSnapLayer();
928  mSnappingMatches.clear();
929  for ( int i = 0; i < line->length(); ++i )
930  mSnappingMatches.append( QgsPointLocator::Match() );
931  resetRubberBand();
932 }
933 
934 QgsPoint QgsMapToolCapture::mapPoint( const QgsPointXY &point ) const
935 {
936  QgsPoint newPoint( QgsWkbTypes::Point, point.x(), point.y() );
937 
938  // get current layer
939  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() );
940  if ( !vlayer )
941  {
942  return newPoint;
943  }
944 
945  // convert to the corresponding type for a full ZM support
946  const QgsWkbTypes::Type type = vlayer->wkbType();
947  if ( QgsWkbTypes::hasZ( type ) && !QgsWkbTypes::hasM( type ) )
948  {
949  newPoint.convertTo( QgsWkbTypes::PointZ );
950  }
951  else if ( !QgsWkbTypes::hasZ( type ) && QgsWkbTypes::hasM( type ) )
952  {
953  newPoint.convertTo( QgsWkbTypes::PointM );
954  }
955  else if ( QgsWkbTypes::hasZ( type ) && QgsWkbTypes::hasM( type ) )
956  {
957  newPoint.convertTo( QgsWkbTypes::PointZM );
958  }
959 
960  // set z value if necessary
961  if ( QgsWkbTypes::hasZ( newPoint.wkbType() ) )
962  {
963  newPoint.setZ( defaultZValue() );
964  }
965 
966  return newPoint;
967 }
968 
970 {
971  QgsPoint newPoint = mapPoint( e.mapPoint() );
972 
973  // set z value from snapped point if necessary
974  if ( QgsWkbTypes::hasZ( newPoint.wkbType() ) )
975  {
976  // if snapped, z dimension is taken from the corresponding snapped
977  // point.
978  if ( e.isSnapped() )
979  {
980  const QgsPointLocator::Match match = e.mapPointMatch();
981 
982  if ( match.layer() && QgsWkbTypes::hasZ( match.layer()->wkbType() ) )
983  {
984  const QgsFeature ft = match.layer()->getFeature( match.featureId() );
985  newPoint.setZ( ft.geometry().vertexAt( match.vertexIndex() ).z() );
986  }
987  }
988  }
989 
990  return newPoint;
991 }
992 
993 void QgsMapToolCapture::updateExtraSnapLayer()
994 {
995  if ( !mExtraSnapLayer )
996  return;
997 
998  if ( canvas()->snappingUtils()->config().selfSnapping() && mCanvas->currentLayer() && mCaptureCurve.numPoints() >= 2 )
999  {
1000  // the current layer may have changed
1001  mExtraSnapLayer->setCrs( mCanvas->currentLayer()->crs() );
1002  QgsGeometry geom = QgsGeometry( mCaptureCurve.clone() );
1003  // we close the curve to allow snapping on last segment
1004  if ( mCaptureMode == CapturePolygon && mCaptureCurve.numPoints() >= 3 )
1005  {
1006  qgsgeometry_cast<QgsCompoundCurve *>( geom.get() )->close();
1007  }
1008  mExtraSnapLayer->changeGeometry( mExtraSnapFeatureId, geom );
1009  }
1010  else
1011  {
1012  QgsGeometry geom;
1013  mExtraSnapLayer->changeGeometry( mExtraSnapFeatureId, geom );
1014  }
1015 }
1016 
1017 QgsMapToolCaptureRubberBand::QgsMapToolCaptureRubberBand( QgsMapCanvas *mapCanvas, QgsWkbTypes::GeometryType geomType ):
1018  QgsGeometryRubberBand( mapCanvas, geomType )
1019 {
1020  setVertexDrawingEnabled( false );
1021 }
1022 
1023 QgsCurve *QgsMapToolCaptureRubberBand::curve()
1024 {
1025  if ( mPoints.empty() )
1026  return nullptr;
1027 
1028  switch ( mStringType )
1029  {
1031  return new QgsLineString( mPoints ) ;
1032  break;
1034  if ( mPoints.count() != 3 )
1035  return nullptr;
1036  return new QgsCircularString(
1037  mPoints[0],
1038  mPoints[1],
1039  mPoints[2] ) ;
1040  break;
1041  default:
1042  return nullptr;
1043  }
1044 }
1045 
1046 bool QgsMapToolCaptureRubberBand::curveIsComplete() const
1047 {
1048  return ( mStringType == QgsWkbTypes::LineString && mPoints.count() > 1 ) ||
1049  ( mStringType == QgsWkbTypes::CircularString && mPoints.count() > 2 );
1050 }
1051 
1052 void QgsMapToolCaptureRubberBand::reset( QgsWkbTypes::GeometryType geomType, QgsWkbTypes::Type stringType, const QgsPoint &firstPolygonPoint )
1053 {
1054  if ( !( geomType == QgsWkbTypes::LineGeometry || geomType == QgsWkbTypes::PolygonGeometry ) )
1055  return;
1056 
1057  mPoints.clear();
1058  mFirstPolygonPoint = firstPolygonPoint;
1059  setStringType( stringType );
1060  setRubberBandGeometryType( geomType );
1061 }
1062 
1063 void QgsMapToolCaptureRubberBand::setRubberBandGeometryType( QgsWkbTypes::GeometryType geomType )
1064 {
1066  updateCurve();
1067 }
1068 
1069 void QgsMapToolCaptureRubberBand::addPoint( const QgsPoint &point, bool doUpdate )
1070 {
1071  if ( mPoints.count() == 0 )
1072  mPoints.append( point );
1073 
1074  mPoints.append( point );
1075 
1076  if ( doUpdate )
1077  updateCurve();
1078 }
1079 
1080 void QgsMapToolCaptureRubberBand::movePoint( const QgsPoint &point )
1081 {
1082  if ( mPoints.count() > 0 )
1083  mPoints.last() = point ;
1084 
1085  updateCurve();
1086 }
1087 
1088 void QgsMapToolCaptureRubberBand::movePoint( int index, const QgsPoint &point )
1089 {
1090  if ( mPoints.count() > 0 && mPoints.size() > index )
1091  mPoints[index] = point;
1092 
1093  updateCurve();
1094 }
1095 
1096 int QgsMapToolCaptureRubberBand::pointsCount()
1097 {
1098  return mPoints.size();
1099 }
1100 
1101 QgsWkbTypes::Type QgsMapToolCaptureRubberBand::stringType() const
1102 {
1103  return mStringType;
1104 }
1105 
1106 void QgsMapToolCaptureRubberBand::setStringType( const QgsWkbTypes::Type &type )
1107 {
1108  if ( ( type != QgsWkbTypes::CircularString && type != QgsWkbTypes::LineString ) || type == mStringType )
1109  return;
1110 
1111  mStringType = type;
1112  if ( type == QgsWkbTypes::LineString && mPoints.count() == 3 )
1113  {
1114  mPoints.removeAt( 1 );
1115  }
1116 
1117  setVertexDrawingEnabled( type == QgsWkbTypes::CircularString );
1118  updateCurve();
1119 }
1120 
1121 QgsPoint QgsMapToolCaptureRubberBand::lastPoint() const
1122 {
1123  if ( mPoints.empty() )
1124  return QgsPoint();
1125 
1126  return mPoints.last();
1127 }
1128 
1129 QgsPoint QgsMapToolCaptureRubberBand::pointFromEnd( int posFromEnd ) const
1130 {
1131  if ( posFromEnd < mPoints.size() )
1132  return mPoints.at( mPoints.size() - 1 - posFromEnd );
1133  else
1134  return QgsPoint();
1135 }
1136 
1137 void QgsMapToolCaptureRubberBand::removeLastPoint()
1138 {
1139  if ( mPoints.count() > 1 )
1140  mPoints.removeLast();
1141 
1142  updateCurve();
1143 }
1144 
1145 void QgsMapToolCaptureRubberBand::setGeometry( QgsAbstractGeometry *geom )
1146 {
1148 }
1149 
1150 void QgsMapToolCaptureRubberBand::updateCurve()
1151 {
1152  std::unique_ptr<QgsCurve> curve;
1153  switch ( mStringType )
1154  {
1156  curve.reset( createLinearString() );
1157  break;
1159  curve.reset( createCircularString() );
1160  break;
1161  default:
1162  return;
1163  break;
1164  }
1165 
1166  if ( geometryType() == QgsWkbTypes::PolygonGeometry )
1167  {
1168  std::unique_ptr<QgsCurvePolygon> geom( new QgsCurvePolygon );
1169  geom->setExteriorRing( curve.release() );
1170  setGeometry( geom.release() );
1171  }
1172  else
1173  {
1174  setGeometry( curve.release() );
1175  }
1176 }
1177 
1178 QgsCurve *QgsMapToolCaptureRubberBand::createLinearString()
1179 {
1180  std::unique_ptr<QgsLineString> curve( new QgsLineString );
1181  if ( geometryType() == QgsWkbTypes::PolygonGeometry )
1182  {
1183  QgsPointSequence points = mPoints;
1184  points.prepend( mFirstPolygonPoint );
1185  curve->setPoints( points );
1186  }
1187  else
1188  curve->setPoints( mPoints );
1189 
1190  return curve.release();
1191 }
1192 
1193 QgsCurve *QgsMapToolCaptureRubberBand::createCircularString()
1194 {
1195  std::unique_ptr<QgsCircularString> curve( new QgsCircularString );
1196  curve->setPoints( mPoints );
1197  if ( geometryType() == QgsWkbTypes::PolygonGeometry )
1198  {
1199  // add a linear string to close the polygon
1200  std::unique_ptr<QgsCompoundCurve> polygonCurve( new QgsCompoundCurve );
1201  polygonCurve->addVertex( mFirstPolygonPoint );
1202  if ( !mPoints.empty() )
1203  polygonCurve->addVertex( mPoints.first() );
1204  polygonCurve->addCurve( curve.release() );
1205  return polygonCurve.release();
1206  }
1207  else
1208  return curve.release();
1209 }
1210 
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.
void addCurve(QgsCurve *c)
Adds a curve to the geometry (takes ownership)
QgsPoint endPoint() const override SIP_HOLDGIL
Returns the end point of the curve.
int numPoints() const override SIP_HOLDGIL
Returns the number of points in the curve.
void removeCurve(int i)
Removes a curve from the geometry.
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.
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:166
int vertexCount(int part=0, int ring=0) const override
Returns the number of vertices of which this geometry is built.
Definition: qgscurve.cpp:171
QgsCurve * clone() const override=0
Clones the geometry by performing a deep copy.
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 id, geometry and a list of field/values...
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:2059
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:2113
@ ValidatorQgisInternal
Use internal QgsGeometryValidator method.
Definition: qgsgeometry.h:2114
@ ValidatorGeos
Use GEOS validation methods.
Definition: qgsgeometry.h:2115
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:85
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:91
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
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
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.
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 undo()
Removes the last vertex from mRubberBand and mCaptureList.
void cadCanvasMoveEvent(QgsMapMouseEvent *e) override
Override this method when subclassing this class.
void startCapturing()
Start capturing.
double defaultZValue() const
Returns default Z value Use for set Z coordinate to new vertex for 2.5d geometries.
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:288
QgsMapCanvas * canvas() const
returns pointer to the tool's map canvas
Definition: qgsmaptool.cpp:195
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
A class to represent a 2D point.
Definition: qgspointxy.h:44
double y
Definition: qgspointxy.h:48
Q_GADGET double x
Definition: qgspointxy.h:47
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:38
bool addMValue(double mValue=0) override
Adds a measure to the geometry, initialized to a preset value.
Definition: qgspoint.cpp:553
bool dropMValue() override
Drops any measure values which exist in the geometry.
Definition: qgspoint.cpp:594
bool isEmpty() const override SIP_HOLDGIL
Returns true if the geometry is empty.
Definition: qgspoint.cpp:753
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
Definition: qgspoint.cpp:542
Q_GADGET double x
Definition: qgspoint.h:41
void setZ(double z) SIP_HOLDGIL
Sets the point's z-coordinate.
Definition: qgspoint.h:293
QgsPoint vertexAt(QgsVertexId) const override
Returns the point corresponding to a specified vertex id.
Definition: qgspoint.cpp:521
double z
Definition: qgspoint.h:43
bool dropZValue() override
Drops any z-dimensions which exist in the geometry.
Definition: qgspoint.cpp:583
double y
Definition: qgspoint.h:42
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:501
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:50
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.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:62
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
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:805
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:738
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:677
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 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.