QGIS API Documentation  3.12.1-BucureČ™ti (121cc00ff0)
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 
18 #include "qgsexception.h"
19 #include "qgsfeatureiterator.h"
20 #include "qgsgeometryvalidator.h"
21 #include "qgslayertreeview.h"
22 #include "qgslinestring.h"
23 #include "qgslogger.h"
24 #include "qgsmapcanvas.h"
25 #include "qgsmapcanvastracer.h"
26 #include "qgsmapmouseevent.h"
27 #include "qgspolygon.h"
28 #include "qgsrubberband.h"
29 #include "qgssnapindicator.h"
30 #include "qgsvectorlayer.h"
31 #include "qgsvertexmarker.h"
32 #include "qgssettings.h"
33 #include "qgsapplication.h"
35 #include "qgsproject.h"
36 
37 #include <QAction>
38 #include <QCursor>
39 #include <QPixmap>
40 #include <QStatusBar>
41 
42 
44  : QgsMapToolAdvancedDigitizing( canvas, cadDockWidget )
45  , mCaptureMode( mode )
46  , mCaptureModeFromLayer( mode == CaptureNone )
47 {
48  mSnapIndicator.reset( new QgsSnapIndicator( canvas ) );
49 
50  setCursor( QgsApplication::getThemeCursor( QgsApplication::Cursor::CapturePoint ) );
51 
52  connect( canvas, &QgsMapCanvas::currentLayerChanged,
53  this, &QgsMapToolCapture::currentLayerChanged );
54 
55  currentLayerChanged( canvas->currentLayer() );
56 }
57 
59 {
60  stopCapturing();
61 
62  if ( mValidator )
63  {
64  mValidator->deleteLater();
65  mValidator = nullptr;
66  }
67 }
68 
70 {
71  if ( mTempRubberBand )
72  mTempRubberBand->show();
73 
75 }
76 
78 {
79  if ( mTempRubberBand )
80  mTempRubberBand->hide();
81 
82  mSnapIndicator->setMatch( QgsPointLocator::Match() );
83 
85 }
86 
87 void QgsMapToolCapture::currentLayerChanged( QgsMapLayer *layer )
88 {
89  if ( !mCaptureModeFromLayer )
90  return;
91 
92  mCaptureMode = CaptureNone;
93 
94  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
95  if ( !vlayer )
96  {
97  return;
98  }
99 
100  switch ( vlayer->geometryType() )
101  {
103  mCaptureMode = CapturePoint;
104  break;
106  mCaptureMode = CaptureLine;
107  break;
109  mCaptureMode = CapturePolygon;
110  break;
111  default:
112  mCaptureMode = CaptureNone;
113  break;
114  }
115 }
116 
117 
118 bool QgsMapToolCapture::tracingEnabled()
119 {
121  return tracer && ( !tracer->actionEnableTracing() || tracer->actionEnableTracing()->isChecked() )
122  && ( !tracer->actionEnableSnapping() || tracer->actionEnableSnapping()->isChecked() );
123 }
124 
125 
126 QgsPointXY QgsMapToolCapture::tracingStartPoint()
127 {
128  try
129  {
130  QgsMapLayer *layer = mCanvas->currentLayer();
131  if ( !layer )
132  return QgsPointXY();
133 
134  // if we have starting point from previous trace, then preferably use that one
135  // (useful when tracing with offset)
136  if ( mTracingStartPoint != QgsPointXY() )
137  return mTracingStartPoint;
138 
139  QgsPoint v = mCaptureCurve.endPoint();
140  return toMapCoordinates( layer, QgsPointXY( v.x(), v.y() ) );
141  }
142  catch ( QgsCsException & )
143  {
144  QgsDebugMsg( QStringLiteral( "transformation to layer coordinate failed" ) );
145  return QgsPointXY();
146  }
147 }
148 
149 
150 bool QgsMapToolCapture::tracingMouseMove( QgsMapMouseEvent *e )
151 {
152  if ( !e->isSnapped() )
153  return false;
154 
155  QgsPointXY pt0 = tracingStartPoint();
156  if ( pt0 == QgsPointXY() )
157  return false;
158 
160  if ( !tracer )
161  return false; // this should not happen!
162 
163  mTempRubberBand->reset( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry );
164 
166  QVector<QgsPointXY> points = tracer->findShortestPath( pt0, e->mapPoint(), &err );
167  if ( points.isEmpty() )
168  {
169  tracer->reportError( err, false );
170  return false;
171  }
172 
173  if ( mCaptureMode == CapturePolygon )
174  mTempRubberBand->addPoint( *mRubberBand->getPoint( 0, 0 ), false );
175 
176  // if there is offset, we need to fix the rubber bands to make sure they are aligned correctly.
177  // There are two cases we need to sort out:
178  // 1. the last point of mRubberBand may need to be moved off the traced curve to respect the offset
179  // 2. extra first point of mTempRubberBand may be needed if there is gap between where mRubberBand ends and trace starts
180  if ( mRubberBand->numberOfVertices() != 0 )
181  {
182  QgsPointXY lastPoint = *mRubberBand->getPoint( 0, mRubberBand->numberOfVertices() - 1 );
183  if ( lastPoint == pt0 && points[0] != lastPoint )
184  {
185  // if rubber band had just one point, for some strange reason it contains the point twice
186  // we only want to move the last point if there are multiple points already
187  if ( mRubberBand->numberOfVertices() > 2 || ( mRubberBand->numberOfVertices() == 2 && *mRubberBand->getPoint( 0, 0 ) != *mRubberBand->getPoint( 0, 1 ) ) )
188  mRubberBand->movePoint( points[0] );
189  }
190  else
191  {
192  mTempRubberBand->addPoint( lastPoint, false );
193  }
194  }
195 
196  // update rubberband
197  for ( int i = 0; i < points.count(); ++i )
198  mTempRubberBand->addPoint( points.at( i ), i == points.count() - 1 );
199 
200  tracer->reportError( QgsTracer::ErrNone, false ); // clear messagebar if there was any error
201  return true;
202 }
203 
204 
205 bool QgsMapToolCapture::tracingAddVertex( const QgsPointXY &point )
206 {
208  if ( !tracer )
209  return false; // this should not happen!
210 
211  if ( mCaptureCurve.numPoints() == 0 )
212  {
213  if ( !tracer->init() )
214  {
216  return false;
217  }
218 
219  // only accept first point if it is snapped to the graph (to vertex or edge)
220  bool res = tracer->isPointSnapped( point );
221  if ( res )
222  {
223  QgsPoint layerPoint;
224  nextPoint( QgsPoint( point ), layerPoint ); // assuming the transform went fine earlier
225 
226  mRubberBand->addPoint( point );
227  mCaptureCurve.addVertex( layerPoint );
228  mSnappingMatches.append( QgsPointLocator::Match() );
229  }
230  return res;
231  }
232 
233  QgsPointXY pt0 = tracingStartPoint();
234  if ( pt0 == QgsPointXY() )
235  return false;
236 
238  QVector<QgsPointXY> points = tracer->findShortestPath( pt0, point, &err );
239  if ( points.isEmpty() )
240  return false; // ignore the vertex - can't find path to the end point!
241 
242  if ( !mCaptureCurve.isEmpty() )
243  {
244  QgsPoint lp; // in layer coords
245  if ( nextPoint( QgsPoint( pt0 ), lp ) != 0 )
246  return false;
247  QgsPoint last;
249  mCaptureCurve.pointAt( mCaptureCurve.numPoints() - 1, last, type );
250  if ( last == lp )
251  {
252  // remove the last point in the curve if it is the same as our first point
253  if ( mCaptureCurve.numPoints() != 2 )
254  mCaptureCurve.deleteVertex( QgsVertexId( 0, 0, mCaptureCurve.numPoints() - 1 ) );
255  else
256  {
257  // there is a strange behavior in deleteVertex() that with just two points
258  // the whole curve is cleared - so we need to do this little dance to work it around
259  QgsPoint first = mCaptureCurve.startPoint();
260  mCaptureCurve.clear();
261  mCaptureCurve.addVertex( first );
262  }
263  // for unknown reasons, rubber band has 2 points even if only one point has been added - handle that case
264  if ( mRubberBand->numberOfVertices() == 2 && *mRubberBand->getPoint( 0, 0 ) == *mRubberBand->getPoint( 0, 1 ) )
265  mRubberBand->removeLastPoint();
266  mRubberBand->removeLastPoint();
267  mSnappingMatches.removeLast();
268  }
269  }
270 
271  // transform points
272  QgsPointSequence layerPoints;
273  QgsPoint lp; // in layer coords
274  for ( int i = 0; i < points.count(); ++i )
275  {
276  if ( nextPoint( QgsPoint( points[i] ), lp ) != 0 )
277  return false;
278  layerPoints << lp;
279  }
280 
281  for ( int i = 0; i < points.count(); ++i )
282  {
283  if ( i == 0 && !mCaptureCurve.isEmpty() && mCaptureCurve.endPoint() == layerPoints[0] )
284  continue; // avoid duplicate of the first vertex
285  if ( i > 0 && points[i] == points[i - 1] )
286  continue; // avoid duplicate vertices if there are any
287  mRubberBand->addPoint( points[i], i == points.count() - 1 );
288  mCaptureCurve.addVertex( layerPoints[i] );
289  mSnappingMatches.append( QgsPointLocator::Match() );
290  }
291 
292  tracer->reportError( QgsTracer::ErrNone, true ); // clear messagebar if there was any error
293  return true;
294 }
295 
297 {
298  return mRubberBand.release();
299 }
300 
301 
303 {
305  QgsPointXY point = e->mapPoint();
306 
307  mSnapIndicator->setMatch( e->mapPointMatch() );
308 
309  if ( !mTempRubberBand && mCaptureCurve.numPoints() > 0 )
310  {
311  mTempRubberBand.reset( createRubberBand( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry, true ) );
312  QgsPoint pt = mCaptureCurve.endPoint();
313  mTempRubberBand->addPoint( QgsPointXY( pt.x(), pt.y() ) );
314  mTempRubberBand->addPoint( point );
315  }
316 
317 
318  if ( mCaptureMode != CapturePoint && mTempRubberBand && mCapturing )
319  {
320  bool hasTrace = false;
321  if ( tracingEnabled() && mCaptureCurve.numPoints() != 0 )
322  {
323  hasTrace = tracingMouseMove( e );
324  }
325 
326  if ( !hasTrace )
327  {
328  if ( mCaptureCurve.numPoints() > 0 )
329  {
330  // fix temporary rubber band after tracing which may have added multiple points
331  mTempRubberBand->reset( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry );
332  if ( mCaptureMode == CapturePolygon )
333  mTempRubberBand->addPoint( *mRubberBand->getPoint( 0, 0 ), false );
334  QgsPoint pt = mCaptureCurve.endPoint();
335  QgsPointXY mapPt = toMapCoordinates( qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() ), QgsPointXY( pt.x(), pt.y() ) );
336  mTempRubberBand->addPoint( mapPt );
337  mTempRubberBand->addPoint( point );
338 
339  // fix existing rubber band after tracing - the last point may have been moved if using offset
340  if ( mRubberBand->numberOfVertices() )
341  mRubberBand->movePoint( mapPt );
342  }
343  else
344  mTempRubberBand->movePoint( point );
345  }
346  }
347 } // mouseMoveEvent
348 
349 
351 {
352  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() );
353  if ( !vlayer )
354  {
355  QgsDebugMsg( QStringLiteral( "no vector layer" ) );
356  return 1;
357  }
358  try
359  {
360  QgsPointXY mapP( mapPoint.x(), mapPoint.y() ); //#spellok
361  layerPoint = QgsPoint( toLayerCoordinates( vlayer, mapP ) ); //transform snapped point back to layer crs //#spellok
362  if ( QgsWkbTypes::hasZ( vlayer->wkbType() ) )
363  layerPoint.addZValue( defaultZValue() );
364  if ( QgsWkbTypes::hasM( vlayer->wkbType() ) )
365  layerPoint.addMValue( 0.0 );
366  }
367  catch ( QgsCsException &cse )
368  {
369  Q_UNUSED( cse )
370  QgsDebugMsg( QStringLiteral( "transformation to layer coordinate failed" ) );
371  return 2;
372  }
373 
374  return 0;
375 }
376 
378 {
379  mapPoint = QgsPoint( toMapCoordinates( p ) );
380  return nextPoint( mapPoint, layerPoint );
381 }
382 
384 {
385  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() );
386  QgsVectorLayer *sourceLayer = match.layer();
387  if ( match.isValid() && ( match.hasVertex() || ( QgsProject::instance()->topologicalEditing() && match.hasEdge() ) ) && sourceLayer &&
388  ( sourceLayer->crs() == vlayer->crs() ) )
389  {
390  QgsFeature f;
391  QgsFeatureRequest request;
392  request.setFilterFid( match.featureId() );
393  bool fetched = match.layer()->getFeatures( request ).nextFeature( f );
394  if ( fetched )
395  {
396  QgsVertexId vId;
397  if ( !f.geometry().vertexIdFromVertexNr( match.vertexIndex(), vId ) )
398  return 2;
399 
400  const QgsGeometry geom( f.geometry() );
401  if ( QgsProject::instance()->topologicalEditing() && match.hasEdge() )
402  {
403  QgsVertexId vId2;
404  if ( !f.geometry().vertexIdFromVertexNr( match.vertexIndex() + 1, vId2 ) )
405  return 2;
406  QgsLineString line( geom.constGet()->vertexAt( vId ), geom.constGet()->vertexAt( vId2 ) );
407 
408  layerPoint = QgsGeometryUtils::closestPoint( line, QgsPoint( match.point() ) );
409  }
410  else
411  {
412  layerPoint = geom.constGet()->vertexAt( vId );
413  if ( QgsWkbTypes::hasZ( vlayer->wkbType() ) && !layerPoint.is3D() )
414  layerPoint.addZValue( defaultZValue() );
415  if ( QgsWkbTypes::hasM( vlayer->wkbType() ) && !layerPoint.isMeasure() )
416  layerPoint.addMValue( 0.0 );
417  }
418 
419  // ZM support depends on the target layer
420  if ( !QgsWkbTypes::hasZ( vlayer->wkbType() ) )
421  {
422  layerPoint.dropZValue();
423  }
424 
425  if ( !QgsWkbTypes::hasM( vlayer->wkbType() ) )
426  {
427  layerPoint.dropMValue();
428  }
429 
430  return 0;
431  }
432  else
433  {
434  return 2;
435  }
436  }
437  else
438  {
439  return 1;
440  }
441 }
442 
444 {
445  return addVertex( point, QgsPointLocator::Match() );
446 }
447 
449 {
450  if ( mode() == CaptureNone )
451  {
452  QgsDebugMsg( QStringLiteral( "invalid capture mode" ) );
453  return 2;
454  }
455 
456  int res;
457  QgsPoint layerPoint;
458  res = fetchLayerPoint( match, layerPoint );
459  if ( res != 0 )
460  {
461  res = nextPoint( QgsPoint( point ), layerPoint );
462  if ( res != 0 )
463  {
464  return res;
465  }
466  }
467 
468  if ( !mRubberBand )
469  {
471  }
472 
473  if ( !mTempRubberBand )
474  {
475  mTempRubberBand.reset( createRubberBand( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry, true ) );
476  }
477  else
478  {
479  mTempRubberBand->reset( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry );
480  }
481 
482  bool traceCreated = false;
483  if ( tracingEnabled() )
484  {
485  traceCreated = tracingAddVertex( point );
486  }
487 
488  // keep new tracing start point if we created a trace. This is useful when tracing with
489  // offset so that the user stays "snapped"
490  mTracingStartPoint = traceCreated ? point : QgsPointXY();
491 
492  if ( !traceCreated )
493  {
494  // ordinary digitizing
495  mRubberBand->addPoint( point );
496  mCaptureCurve.addVertex( layerPoint );
497  mSnappingMatches.append( match );
498  }
499 
500  if ( mCaptureMode == CaptureLine )
501  {
502  mTempRubberBand->addPoint( point );
503  }
504  else if ( mCaptureMode == CapturePolygon )
505  {
506  const QgsPointXY *firstPoint = mRubberBand->getPoint( 0, 0 );
507  mTempRubberBand->addPoint( *firstPoint );
508  mTempRubberBand->movePoint( point );
509  mTempRubberBand->addPoint( point );
510  }
511 
512  validateGeometry();
513 
514  return 0;
515 }
516 
518 {
519  if ( !c )
520  {
521  return 1;
522  }
523 
524  if ( !mRubberBand )
525  {
527  }
528 
529  QgsLineString *lineString = c->curveToLine();
530  QgsPointSequence linePoints;
531  lineString->points( linePoints );
532  delete lineString;
533  QgsPointSequence::const_iterator ptIt = linePoints.constBegin();
534  for ( ; ptIt != linePoints.constEnd(); ++ptIt )
535  {
536  mRubberBand->addPoint( QgsPointXY( ptIt->x(), ptIt->y() ) );
537  }
538 
539  if ( !mTempRubberBand )
540  {
541  mTempRubberBand.reset( createRubberBand( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry, true ) );
542  }
543  else
544  {
545  mTempRubberBand->reset();
546  }
547  QgsPoint endPt = c->endPoint();
548  mTempRubberBand->addPoint( QgsPointXY( endPt.x(), endPt.y() ) ); //add last point of c
549 
550  //transform back to layer CRS in case map CRS and layer CRS are different
551  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() );
553  if ( ct.isValid() )
554  {
556  }
557  mCaptureCurve.addCurve( c );
558  for ( int i = 0; i < c->length(); ++i )
559  mSnappingMatches.append( QgsPointLocator::Match() );
560 
561  return 0;
562 }
563 
565 {
566  mCaptureCurve.clear();
567 }
568 
569 QList<QgsPointLocator::Match> QgsMapToolCapture::snappingMatches() const
570 {
571  return mSnappingMatches;
572 }
573 
574 
576 {
577  mTracingStartPoint = QgsPointXY();
578 
579  if ( mRubberBand )
580  {
581  int rubberBandSize = mRubberBand->numberOfVertices();
582  int tempRubberBandSize = mTempRubberBand->numberOfVertices();
583  int captureListSize = size();
584 
585  if ( rubberBandSize < 1 || captureListSize < 1 )
586  {
587  return;
588  }
589 
590  mRubberBand->removePoint( -1 );
591 
592  if ( rubberBandSize > 1 )
593  {
594  if ( tempRubberBandSize > 1 )
595  {
596  const QgsPointXY *point = mRubberBand->getPoint( 0, rubberBandSize - 2 );
597  mTempRubberBand->movePoint( tempRubberBandSize - 2, *point );
598  }
599  }
600  else
601  {
602  mTempRubberBand->reset( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry );
603  }
604 
605  QgsVertexId vertexToRemove;
606  vertexToRemove.part = 0;
607  vertexToRemove.ring = 0;
608  vertexToRemove.vertex = size() - 1;
609  mCaptureCurve.deleteVertex( vertexToRemove );
610  mSnappingMatches.removeAt( vertexToRemove.vertex );
611 
613 
614  validateGeometry();
615  }
616 }
617 
619 {
620  if ( e->key() == Qt::Key_Backspace || e->key() == Qt::Key_Delete )
621  {
622  undo();
623 
624  // Override default shortcut management in MapCanvas
625  e->ignore();
626  }
627  else if ( e->key() == Qt::Key_Escape )
628  {
629  stopCapturing();
630 
631  // Override default shortcut management in MapCanvas
632  e->ignore();
633  }
634 }
635 
637 {
638  mCapturing = true;
639 }
640 
642 {
643  return mCapturing;
644 }
645 
647 {
648  mRubberBand.reset();
649 
651 
652  qDeleteAll( mGeomErrorMarkers );
653  mGeomErrorMarkers.clear();
654  mGeomErrors.clear();
655 
656  mTracingStartPoint = QgsPointXY();
657 
658  mCapturing = false;
659  mCaptureCurve.clear();
660  mSnappingMatches.clear();
661  if ( currentVectorLayer() )
663 }
664 
666 {
667  mTempRubberBand.reset();
668 }
669 
671 {
672  stopCapturing();
673  clearCurve();
674 }
675 
677 {
678  mCaptureCurve.close();
679 }
680 
681 void QgsMapToolCapture::validateGeometry()
682 {
683  QgsSettings settings;
684  if ( settings.value( QStringLiteral( "qgis/digitizing/validate_geometries" ), 1 ).toInt() == 0 )
685  return;
686 
687  if ( mValidator )
688  {
689  mValidator->deleteLater();
690  mValidator = nullptr;
691  }
692 
693  mGeomErrors.clear();
694  while ( !mGeomErrorMarkers.isEmpty() )
695  {
696  delete mGeomErrorMarkers.takeFirst();
697  }
698 
699  QgsGeometry geom;
700 
701  switch ( mCaptureMode )
702  {
703  case CaptureNone:
704  case CapturePoint:
705  return;
706  case CaptureLine:
707  if ( size() < 2 )
708  return;
709  geom = QgsGeometry( mCaptureCurve.curveToLine() );
710  break;
711  case CapturePolygon:
712  if ( size() < 3 )
713  return;
714  QgsLineString *exteriorRing = mCaptureCurve.curveToLine();
715  exteriorRing->close();
716  QgsPolygon *polygon = new QgsPolygon();
717  polygon->setExteriorRing( exteriorRing );
718  geom = QgsGeometry( polygon );
719  break;
720  }
721 
722  if ( geom.isNull() )
723  return;
724 
726  if ( settings.value( QStringLiteral( "qgis/digitizing/validate_geometries" ), 1 ).toInt() == 2 )
728  mValidator = new QgsGeometryValidator( geom, nullptr, method );
729  connect( mValidator, &QgsGeometryValidator::errorFound, this, &QgsMapToolCapture::addError );
730  mValidator->start();
731  QgsDebugMsgLevel( QStringLiteral( "Validation started" ), 4 );
732 }
733 
734 void QgsMapToolCapture::addError( const QgsGeometry::Error &e )
735 {
736  mGeomErrors << e;
737  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() );
738  if ( !vlayer )
739  return;
740 
741  if ( e.hasWhere() )
742  {
744  vm->setCenter( mCanvas->mapSettings().layerToMapCoordinates( vlayer, e.where() ) );
746  vm->setPenWidth( 2 );
747  vm->setToolTip( e.what() );
748  vm->setColor( Qt::green );
749  vm->setZValue( vm->zValue() + 1 );
750  mGeomErrorMarkers << vm;
751  }
752 }
753 
755 {
756  return mCaptureCurve.numPoints();
757 }
758 
759 QVector<QgsPointXY> QgsMapToolCapture::points() const
760 {
761  QVector<QgsPointXY> pointsXY;
763 
764  return pointsXY;
765 }
766 
768 {
769  QgsPointSequence pts;
770  mCaptureCurve.points( pts );
771  return pts;
772 }
773 
774 void QgsMapToolCapture::setPoints( const QVector<QgsPointXY> &pointList )
775 {
776  QgsLineString *line = new QgsLineString( pointList );
777  mCaptureCurve.clear();
778  mCaptureCurve.addCurve( line );
779  mSnappingMatches.clear();
780  for ( int i = 0; i < line->length(); ++i )
781  mSnappingMatches.append( QgsPointLocator::Match() );
782 }
783 
785 {
786  QgsLineString *line = new QgsLineString( pointList );
787  mCaptureCurve.clear();
788  mCaptureCurve.addCurve( line );
789  mSnappingMatches.clear();
790  for ( int i = 0; i < line->length(); ++i )
791  mSnappingMatches.append( QgsPointLocator::Match() );
792 }
793 
795 {
796  QgsPoint newPoint( QgsWkbTypes::Point, point.x(), point.y() );
797 
798  // get current layer
799  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() );
800  if ( !vlayer )
801  {
802  return newPoint;
803  }
804 
805  // convert to the corresponding type for a full ZM support
806  const QgsWkbTypes::Type type = vlayer->wkbType();
807  if ( QgsWkbTypes::hasZ( type ) && !QgsWkbTypes::hasM( type ) )
808  {
809  newPoint.convertTo( QgsWkbTypes::PointZ );
810  }
811  else if ( !QgsWkbTypes::hasZ( type ) && QgsWkbTypes::hasM( type ) )
812  {
813  newPoint.convertTo( QgsWkbTypes::PointM );
814  }
815  else if ( QgsWkbTypes::hasZ( type ) && QgsWkbTypes::hasM( type ) )
816  {
817  newPoint.convertTo( QgsWkbTypes::PointZM );
818  }
819 
820  // set z value if necessary
821  if ( QgsWkbTypes::hasZ( newPoint.wkbType() ) )
822  {
823  newPoint.setZ( defaultZValue() );
824  }
825 
826  return newPoint;
827 }
828 
830 {
831  QgsPoint newPoint = mapPoint( e.mapPoint() );
832 
833  // set z value from snapped point if necessary
834  if ( QgsWkbTypes::hasZ( newPoint.wkbType() ) )
835  {
836  // if snapped, z dimension is taken from the corresponding snapped
837  // point.
838  if ( e.isSnapped() )
839  {
840  const QgsPointLocator::Match match = e.mapPointMatch();
841 
842  if ( match.layer() && QgsWkbTypes::hasZ( match.layer()->wkbType() ) )
843  {
844  const QgsFeature ft = match.layer()->getFeature( match.featureId() );
845  newPoint.setZ( ft.geometry().vertexAt( match.vertexIndex() ).z() );
846  }
847  }
848  }
849 
850  return newPoint;
851 }
bool isMeasure() const
Returns true if the geometry contains m values.
QgsPoint startPoint() const override
Returns the starting point of the curve.
Base class for all map layer types.
Definition: qgsmaplayer.h:79
double y
Definition: qgspoint.h:42
int numberOfVertices() const
Returns count of vertices in all lists of mPoint.
void points(QgsPointSequence &pt) const override
Returns a list of points within the curve.
virtual void transform(const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection d=QgsCoordinateTransform::ForwardTransform, bool transformZ=false) SIP_THROW(QgsCsException)=0
Transforms the geometry using a coordinate transform.
CaptureMode
Different capture modes.
int size()
Number of points digitized.
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...
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
Definition: qgspoint.cpp:506
QgsVectorLayer * layer() const
The vector layer where the snap occurred.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:58
void setZ(double z)
Sets the point&#39;s z-coordinate.
Definition: qgspoint.h:293
Use GEOS validation methods.
Definition: qgsgeometry.h:2007
Q_INVOKABLE QgsWkbTypes::Type wkbType() const FINAL
Returns the WKBType or WKBUnknown in case of error.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
double y
Definition: qgspointxy.h:48
QAction * actionEnableTracing() const
Access to action that user may use to toggle tracing on/off. May be nullptr if no action was associat...
A class to represent a 2D point.
Definition: qgspointxy.h:43
QgsFeature getFeature(QgsFeatureId fid) const
Queries the layer for the feature with the given id.
void setPenWidth(int width)
A QgsMapMouseEvent is the result of a user interaction with the mouse on a QgsMapCanvas.
Q_INVOKABLE QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
bool deleteVertex(QgsVertexId position) override
Deletes a vertex within the geometry.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:122
QgsPointXY toMapCoordinates(QPoint point)
transformation from screen coordinates to map coordinates
Definition: qgsmaptool.cpp:42
void activate() override
Registers this maptool with the cad dock widget.
bool isSnapped() const
Returns true if there is a snapped point cached.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
QgsAdvancedDigitizingDockWidget * mCadDockWidget
The QgsMapToolAdvancedDigitizing class is a QgsMapTool which gives event directly in map coordinates ...
void clear() override
Clears the geometry, ie reset it to a null geometry.
static bool hasZ(Type type)
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:917
const QgsPointXY * getPoint(int i, int j=0) const
Returns a vertex.
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:75
void reset(T *p=nullptr)
Will reset the managed pointer to p.
void addCurve(QgsCurve *c)
Adds a curve to the geometry (takes ownership)
QgsPoint vertexAt(QgsVertexId) const override
Returns the point corresponding to a specified vertex id.
Definition: qgspoint.cpp:485
Do not capture / determine mode from layer geometry type.
bool pointAt(int node, QgsPoint &point, QgsVertexId::VertexType &type) const override
Returns the point and vertex id of a point within the curve.
bool dropZValue() override
Drops any z-dimensions which exist in the geometry.
Definition: qgspoint.cpp:547
Extension of QgsTracer that provides extra functionality:
QgsCoordinateTransform layerTransform(const QgsMapLayer *layer) const
Returns the coordinate transform from layer&#39;s CRS to destination CRS.
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
QgsMapCanvas * mCanvas
pointer to map canvas
Definition: qgsmaptool.h:241
virtual QgsPoint endPoint() const =0
Returns the end point of the curve.
QgsFeatureRequest & setFilterFid(QgsFeatureId fid)
Sets feature ID that should be fetched.
CaptureMode mode() const
The capture mode.
virtual double length() const
Returns the planar, 2-dimensional length of the geometry.
QAction * actionEnableSnapping() const
Access to action that user may use to toggle snapping on/off.
Q_DECL_DEPRECATED void setPoints(const QVector< QgsPointXY > &pointList)
Set the points on which to work.
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:68
bool dropMValue() override
Drops any measure values which exist in the geometry.
Definition: qgspoint.cpp:558
void triggerRepaint(bool deferredUpdate=false)
Will advise the map canvas (and any other interested party) that this layer requires to be repainted...
static QgsMapCanvasTracer * tracerForCanvas(QgsMapCanvas *canvas)
Retrieve instance of this class associated with given canvas (if any).
Utility class for identifying a unique vertex within a geometry.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
Max feature count threshold was reached while reading features.
Definition: qgstracer.h:135
int addCurve(QgsCurve *c)
Adds a whole curve (e.g. circularstring) to the captured geometry. Curve must be in map CRS...
A class for drawing transient features (e.g.
Definition: qgsrubberband.h:49
void removePoint(int index=0, bool doUpdate=true, int geometryIndex=0)
Removes a vertex from the rubberband and (optionally) updates canvas.
void setCenter(const QgsPointXY &point)
void addPoint(const QgsPointXY &p, bool doUpdate=true, int geometryIndex=0)
Adds a vertex to the rubberband and update canvas.
bool isCapturing() const
Are we currently capturing?
void activate() override
Registers this maptool with the cad dock widget.
Use internal QgsGeometryValidator method.
Definition: qgsgeometry.h:2006
void movePoint(const QgsPointXY &p, int geometryIndex=0)
Moves the rubber band point specified by index.
PathError
Possible errors that may happen when calling findShortestPath()
Definition: qgstracer.h:132
This class wraps a request for features to a vector layer (or directly its vector data provider)...
void close()
Closes the line string by appending the first point to the end of the line, if it is not already clos...
A class for marking vertices of features using e.g.
Abstract base class for curved geometry type.
Definition: qgscurve.h:35
double defaultZValue() const
Returns default Z value Use for set Z coordinate to new vertex for 2.5d geometries.
~QgsMapToolCapture() override
QgsRubberBand * takeRubberBand()
Returns the rubberBand currently owned by this map tool and transfers ownership to the caller...
QgsWkbTypes::Type wkbType() const
Returns the WKB type of the geometry.
QList< QgsPointLocator::Match > snappingMatches() const
Returns a list of matches for each point on the captureCurve.
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:37
void undo()
Removes the last vertex from mRubberBand and mCaptureList.
void currentLayerChanged(QgsMapLayer *layer)
Emitted when the current layer is changed.
double x
Definition: qgspointxy.h:47
T * release()
Clears the pointer and returns it.
bool topologicalEditing() const
Convenience function to query topological editing status.
QgsMapLayer * currentLayer()
returns current layer (set by legend widget)
bool isEmpty() const override
Returns true if the geometry is empty.
QVector< QgsPoint > QgsPointSequence
QgsPoint mapPoint(const QgsMapMouseEvent &e) const
Creates a QgsPoint with ZM support if necessary (according to the WkbType of the current layer)...
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
void reportError(PathError err, bool addingVertex)
Report a path finding error to the user.
static void convertPointList(const QVector< QgsPointXY > &input, QgsPointSequence &output)
Upgrades a point list from QgsPointXY to QgsPoint.
QgsPointLocator::Match mapPointMatch() const
Returns the matching data from the most recently snapped point.
QgsVectorLayer * currentVectorLayer()
Returns the current vector layer of the map canvas or 0.
void closePolygon()
Close an open polygon.
QgsPointXY point() const
for vertex / edge match coords depending on what class returns it (geom.cache: layer coords...
void deleteTempRubberBand()
Clean a temporary rubberband.
QgsPoint vertexAt(int atVertex) const
Returns coordinates of a vertex.
QgsPointXY toLayerCoordinates(const QgsMapLayer *layer, QPoint point)
transformation from screen coordinates to layer&#39;s coordinates
Definition: qgsmaptool.cpp:54
void deactivate() override
Unregisters this maptool from the cad dock widget.
void setExteriorRing(QgsCurve *ring) override
Sets the exterior ring of the polygon.
Definition: qgspolygon.cpp:179
void startCapturing()
Start capturing.
void keyPressEvent(QKeyEvent *e) override
Intercept key events like Esc or Del to delete the last point.
Transform from destination to source CRS.
QgsPointXY mapPoint() const
mapPoint returns the point in coordinates
int vertexIndex() const
for vertex / edge match (first vertex of the edge)
void addVertex(const QgsPoint &pt)
Adds a vertex to the end of the geometry.
Q_DECL_DEPRECATED QVector< QgsPointXY > points() const
List of digitized points.
QgsRubberBand * createRubberBand(QgsWkbTypes::GeometryType geometryType=QgsWkbTypes::LineGeometry, bool alternativeBand=false)
Creates a rubber band with the color/line width from the QGIS settings.
QgsPointXY layerToMapCoordinates(const QgsMapLayer *layer, QgsPointXY point) const
transform point coordinates from layer&#39;s CRS to output CRS
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:43
ValidationMethod
Available methods for validating geometries.
Definition: qgsgeometry.h:2004
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:450
virtual QgsLineString * curveToLine(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const =0
Returns a new line string geometry corresponding to a segmentized approximation of the curve...
void points(QgsPointSequence &pts) const override
Returns a list of points within the curve.
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:739
Class that shows snapping marker on map canvas for the current snapping match.
Class for doing transforms between two map coordinate systems.
bool addMValue(double mValue=0) override
Adds a measure to the geometry, initialized to a preset value.
Definition: qgspoint.cpp:517
void clean() override
convenient method to clean members
void reset(QgsWkbTypes::GeometryType geometryType=QgsWkbTypes::LineGeometry)
Clears all the geometries in this rubberband.
No error.
Definition: qgstracer.h:134
int nextPoint(const QgsPoint &mapPoint, QgsPoint &layerPoint)
Converts a map point to layer coordinates.
virtual void cadCanvasMoveEvent(QgsMapMouseEvent *e)
Override this method when subclassing this class.
bool init()
Build the internal data structures.
Definition: qgstracer.cpp:678
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
virtual void setCursor(const QCursor &cursor)
Sets a user defined cursor.
Definition: qgsmaptool.cpp:149
static bool hasM(Type type)
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:967
QgsPointSequence pointsZM() const
List of digitized points.
static QCursor getThemeCursor(Cursor cursor)
Helper to get a theme cursor.
QgsGeometry geometry
Definition: qgsfeature.h:67
void stopCapturing()
Stop capturing.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:65
void close()
Appends first point if not already closed.
static QgsPoint closestPoint(const QgsAbstractGeometry &geometry, const QgsPoint &point)
Returns the nearest point on a segment of a geometry for the specified point.
double length() const override
Returns the planar, 2-dimensional length of the geometry.
QgsFeatureId featureId() const
The id of the feature to which the snapped geometry belongs.
bool nextFeature(QgsFeature &f)
bool isPointSnapped(const QgsPointXY &pt)
Find out whether the point is snapped to a vertex or edge (i.e. it can be used for tracing start/stop...
Definition: qgstracer.cpp:806
bool vertexIdFromVertexNr(int number, QgsVertexId &id) const
Calculates the vertex ID from a vertex number.
void cadCanvasMoveEvent(QgsMapMouseEvent *e) override
Override this method when subclassing this class.
Polygon geometry type.
Definition: qgspolygon.h:31
The QgsAdvancedDigitizingDockWidget class is a dockable widget used to handle the CAD tools on top of...
void setColor(const QColor &color)
Sets the stroke color for the marker.
Represents a vector layer which manages a vector based data sets.
void removeLastPoint(int geometryIndex=0, bool doUpdate=true)
Removes the last point.
int numPoints() const override
Returns the number of points in the curve.
void setIconType(int iconType)
void errorFound(const QgsGeometry::Error &error)
Sent when an error has been found during the validation process.
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
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...
void removePreviousPoint()
Remove previous point in the CAD point list.
void clearCurve()
Clear capture curve.
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:86
QgsPoint endPoint() const override
Returns the end point of the curve.
int addVertex(const QgsPointXY &point)
Adds a point to the rubber band (in map coordinates) and to the capture list (in layer coordinates) ...
QgsMapToolCapture(QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDockWidget, CaptureMode mode)
constructor
double x
Definition: qgspoint.h:41
void deactivate() override
Unregisters this maptool from the cad dock widget.