QGIS API Documentation  3.0.2-Girona (307d082)
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 
34 #include <QAction>
35 #include <QCursor>
36 #include <QPixmap>
37 #include <QMouseEvent>
38 #include <QStatusBar>
39 
40 
42  : QgsMapToolAdvancedDigitizing( canvas, cadDockWidget )
43  , mCaptureMode( mode )
44 {
45  mCaptureModeFromLayer = mode == CaptureNone;
46  mCapturing = false;
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 
57 {
58  stopCapturing();
59 
60  if ( mValidator )
61  {
62  mValidator->deleteLater();
63  mValidator = nullptr;
64  }
65 }
66 
68 {
69  if ( mTempRubberBand )
70  mTempRubberBand->show();
71 
73 }
74 
76 {
77  if ( mTempRubberBand )
78  mTempRubberBand->hide();
79 
80  mSnapIndicator->setMatch( QgsPointLocator::Match() );
81 
83 }
84 
85 void QgsMapToolCapture::validationFinished()
86 {
87  emit messageDiscarded();
88  QString msgFinished = tr( "Validation finished" );
89  if ( !mValidationWarnings.isEmpty() )
90  {
91  emit messageEmitted( mValidationWarnings.join( QStringLiteral( "\n" ) ).append( "\n" ).append( msgFinished ), Qgis::Warning );
92  }
93 }
94 
95 void QgsMapToolCapture::currentLayerChanged( QgsMapLayer *layer )
96 {
97  if ( !mCaptureModeFromLayer )
98  return;
99 
100  mCaptureMode = CaptureNone;
101 
102  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
103  if ( !vlayer )
104  {
105  return;
106  }
107 
108  switch ( vlayer->geometryType() )
109  {
111  mCaptureMode = CapturePoint;
112  break;
114  mCaptureMode = CaptureLine;
115  break;
117  mCaptureMode = CapturePolygon;
118  break;
119  default:
120  mCaptureMode = CaptureNone;
121  break;
122  }
123 }
124 
125 
126 bool QgsMapToolCapture::tracingEnabled()
127 {
129  return tracer && ( !tracer->actionEnableTracing() || tracer->actionEnableTracing()->isChecked() )
130  && ( !tracer->actionEnableSnapping() || tracer->actionEnableSnapping()->isChecked() );
131 }
132 
133 
134 QgsPointXY QgsMapToolCapture::tracingStartPoint()
135 {
136  try
137  {
138  QgsMapLayer *layer = mCanvas->currentLayer();
139  if ( !layer )
140  return QgsPointXY();
141 
142  // if we have starting point from previous trace, then preferably use that one
143  // (useful when tracing with offset)
144  if ( mTracingStartPoint != QgsPointXY() )
145  return mTracingStartPoint;
146 
147  QgsPoint v = mCaptureCurve.endPoint();
148  return toMapCoordinates( layer, QgsPointXY( v.x(), v.y() ) );
149  }
150  catch ( QgsCsException & )
151  {
152  QgsDebugMsg( "transformation to layer coordinate failed" );
153  return QgsPointXY();
154  }
155 }
156 
157 
158 bool QgsMapToolCapture::tracingMouseMove( QgsMapMouseEvent *e )
159 {
160  if ( !e->isSnapped() )
161  return false;
162 
163  QgsPointXY pt0 = tracingStartPoint();
164  if ( pt0 == QgsPointXY() )
165  return false;
166 
168  if ( !tracer )
169  return false; // this should not happen!
170 
171  mTempRubberBand->reset( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry );
172 
174  QVector<QgsPointXY> points = tracer->findShortestPath( pt0, e->mapPoint(), &err );
175  if ( points.isEmpty() )
176  {
177  tracer->reportError( err, false );
178  return false;
179  }
180 
181  if ( mCaptureMode == CapturePolygon )
182  mTempRubberBand->addPoint( *mRubberBand->getPoint( 0, 0 ), false );
183 
184  // if there is offset, we need to fix the rubber bands to make sure they are aligned correctly.
185  // There are two cases we need to sort out:
186  // 1. the last point of mRubberBand may need to be moved off the traced curve to respect the offset
187  // 2. extra first point of mTempRubberBand may be needed if there is gap between where mRubberBand ends and trace starts
188  if ( mRubberBand->numberOfVertices() != 0 )
189  {
190  QgsPointXY lastPoint = *mRubberBand->getPoint( 0, mRubberBand->numberOfVertices() - 1 );
191  if ( lastPoint == pt0 && points[0] != lastPoint )
192  {
193  // if rubber band had just one point, for some strange reason it contains the point twice
194  // we only want to move the last point if there are multiple points already
195  if ( mRubberBand->numberOfVertices() > 2 || ( mRubberBand->numberOfVertices() == 2 && *mRubberBand->getPoint( 0, 0 ) != *mRubberBand->getPoint( 0, 1 ) ) )
196  mRubberBand->movePoint( points[0] );
197  }
198  else
199  {
200  mTempRubberBand->addPoint( lastPoint, false );
201  }
202  }
203 
204  // update rubberband
205  for ( int i = 0; i < points.count(); ++i )
206  mTempRubberBand->addPoint( points.at( i ), i == points.count() - 1 );
207 
208  tracer->reportError( QgsTracer::ErrNone, false ); // clear messagebar if there was any error
209  return true;
210 }
211 
212 
213 bool QgsMapToolCapture::tracingAddVertex( const QgsPointXY &point )
214 {
216  if ( !tracer )
217  return false; // this should not happen!
218 
219  if ( mCaptureCurve.numPoints() == 0 )
220  {
221  if ( !tracer->init() )
222  {
224  return false;
225  }
226 
227  // only accept first point if it is snapped to the graph (to vertex or edge)
228  bool res = tracer->isPointSnapped( point );
229  if ( res )
230  {
231  QgsPoint layerPoint;
232  nextPoint( QgsPoint( point ), layerPoint ); // assuming the transform went fine earlier
233 
234  mRubberBand->addPoint( point );
235  mCaptureCurve.addVertex( layerPoint );
236  mSnappingMatches.append( QgsPointLocator::Match() );
237  }
238  return res;
239  }
240 
241  QgsPointXY pt0 = tracingStartPoint();
242  if ( pt0 == QgsPointXY() )
243  return false;
244 
246  QVector<QgsPointXY> points = tracer->findShortestPath( pt0, point, &err );
247  if ( points.isEmpty() )
248  return false; // ignore the vertex - can't find path to the end point!
249 
250  if ( !mCaptureCurve.isEmpty() )
251  {
252  QgsPoint lp; // in layer coords
253  if ( nextPoint( QgsPoint( pt0 ), lp ) != 0 )
254  return false;
255  QgsPoint last;
257  mCaptureCurve.pointAt( mCaptureCurve.numPoints() - 1, last, type );
258  if ( last == lp )
259  {
260  // remove the last point in the curve if it is the same as our first point
261  if ( mCaptureCurve.numPoints() != 2 )
262  mCaptureCurve.deleteVertex( QgsVertexId( 0, 0, mCaptureCurve.numPoints() - 1 ) );
263  else
264  {
265  // there is a strange behavior in deleteVertex() that with just two points
266  // the whole curve is cleared - so we need to do this little dance to work it around
267  QgsPoint first = mCaptureCurve.startPoint();
268  mCaptureCurve.clear();
269  mCaptureCurve.addVertex( first );
270  }
271  // for unknown reasons, rubber band has 2 points even if only one point has been added - handle that case
272  if ( mRubberBand->numberOfVertices() == 2 && *mRubberBand->getPoint( 0, 0 ) == *mRubberBand->getPoint( 0, 1 ) )
273  mRubberBand->removeLastPoint();
274  mRubberBand->removeLastPoint();
275  mSnappingMatches.removeLast();
276  }
277  }
278 
279  // transform points
280  QgsPointSequence layerPoints;
281  QgsPoint lp; // in layer coords
282  for ( int i = 0; i < points.count(); ++i )
283  {
284  if ( nextPoint( QgsPoint( points[i] ), lp ) != 0 )
285  return false;
286  layerPoints << lp;
287  }
288 
289  for ( int i = 0; i < points.count(); ++i )
290  {
291  if ( i == 0 && !mCaptureCurve.isEmpty() && mCaptureCurve.endPoint() == layerPoints[0] )
292  continue; // avoid duplicate of the first vertex
293  if ( i > 0 && points[i] == points[i - 1] )
294  continue; // avoid duplicate vertices if there are any
295  mRubberBand->addPoint( points[i], i == points.count() - 1 );
296  mCaptureCurve.addVertex( layerPoints[i] );
297  mSnappingMatches.append( QgsPointLocator::Match() );
298  }
299 
300  tracer->reportError( QgsTracer::ErrNone, true ); // clear messagebar if there was any error
301  return true;
302 }
303 
304 
306 {
308  QgsPointXY point = e->mapPoint();
309 
310  mSnapIndicator->setMatch( e->mapPointMatch() );
311 
312  if ( !mTempRubberBand && mCaptureCurve.numPoints() > 0 )
313  {
314  mTempRubberBand = createRubberBand( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry, true );
315  QgsPoint pt = mCaptureCurve.endPoint();
316  mTempRubberBand->addPoint( QgsPointXY( pt.x(), pt.y() ) );
317  mTempRubberBand->addPoint( point );
318  }
319 
320 
321  if ( mCaptureMode != CapturePoint && mTempRubberBand && mCapturing )
322  {
323  bool hasTrace = false;
324  if ( tracingEnabled() && mCaptureCurve.numPoints() != 0 )
325  {
326  hasTrace = tracingMouseMove( e );
327  }
328 
329  if ( !hasTrace )
330  {
331  if ( mCaptureCurve.numPoints() > 0 )
332  {
333  // fix temporary rubber band after tracing which may have added multiple points
334  mTempRubberBand->reset( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry );
335  if ( mCaptureMode == CapturePolygon )
336  mTempRubberBand->addPoint( *mRubberBand->getPoint( 0, 0 ), false );
337  QgsPoint pt = mCaptureCurve.endPoint();
338  QgsPointXY mapPt = toMapCoordinates( qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() ), QgsPointXY( pt.x(), pt.y() ) );
339  mTempRubberBand->addPoint( mapPt );
340  mTempRubberBand->addPoint( point );
341 
342  // fix existing rubber band after tracing - the last point may have been moved if using offset
343  if ( mRubberBand->numberOfVertices() )
344  mRubberBand->movePoint( mapPt );
345  }
346  else
347  mTempRubberBand->movePoint( point );
348  }
349  }
350 } // mouseMoveEvent
351 
352 
354 {
355  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() );
356  if ( !vlayer )
357  {
358  QgsDebugMsg( "no vector layer" );
359  return 1;
360  }
361  try
362  {
363  QgsPointXY mapP( mapPoint.x(), mapPoint.y() ); //#spellok
364  layerPoint = QgsPoint( toLayerCoordinates( vlayer, mapP ) ); //transform snapped point back to layer crs //#spellok
365  if ( QgsWkbTypes::hasZ( vlayer->wkbType() ) )
366  layerPoint.addZValue( defaultZValue() );
367  if ( QgsWkbTypes::hasM( vlayer->wkbType() ) )
368  layerPoint.addMValue( 0.0 );
369  }
370  catch ( QgsCsException &cse )
371  {
372  Q_UNUSED( cse );
373  QgsDebugMsg( "transformation to layer coordinate failed" );
374  return 2;
375  }
376 
377  return 0;
378 }
379 
381 {
382  mapPoint = QgsPoint( toMapCoordinates( p ) );
383  return nextPoint( mapPoint, layerPoint );
384 }
385 
387 {
388  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() );
389  QgsVectorLayer *sourceLayer = match.layer();
390  if ( match.isValid() && match.hasVertex() && sourceLayer &&
391  ( sourceLayer->crs() == vlayer->crs() ) )
392  {
393  QgsFeature f;
394  QgsFeatureRequest request;
395  request.setFilterFid( match.featureId() );
396  bool fetched = match.layer()->getFeatures( request ).nextFeature( f );
397  if ( fetched )
398  {
399  QgsVertexId vId;
400  if ( !f.geometry().vertexIdFromVertexNr( match.vertexIndex(), vId ) )
401  return 2;
402  layerPoint = f.geometry().constGet()->vertexAt( vId );
403  return 0;
404  }
405  else
406  {
407  return 2;
408  }
409  }
410  else
411  {
412  return 1;
413  }
414 }
415 
417 {
418  return addVertex( point, QgsPointLocator::Match() );
419 }
420 
422 {
423  if ( mode() == CaptureNone )
424  {
425  QgsDebugMsg( "invalid capture mode" );
426  return 2;
427  }
428 
429  int res;
430  QgsPoint layerPoint;
431  res = fetchLayerPoint( match, layerPoint );
432  if ( res != 0 )
433  {
434  res = nextPoint( QgsPoint( point ), layerPoint );
435  if ( res != 0 )
436  {
437  return res;
438  }
439  }
440 
441  if ( !mRubberBand )
442  {
444  }
445 
446  if ( !mTempRubberBand )
447  {
448  mTempRubberBand = createRubberBand( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry, true );
449  }
450  else
451  {
452  mTempRubberBand->reset( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry );
453  }
454 
455  bool traceCreated = false;
456  if ( tracingEnabled() )
457  {
458  traceCreated = tracingAddVertex( point );
459  }
460 
461  // keep new tracing start point if we created a trace. This is useful when tracing with
462  // offset so that the user stays "snapped"
463  mTracingStartPoint = traceCreated ? point : QgsPointXY();
464 
465  if ( !traceCreated )
466  {
467  // ordinary digitizing
468  mRubberBand->addPoint( point );
469  mCaptureCurve.addVertex( layerPoint );
470  mSnappingMatches.append( match );
471  }
472 
473  if ( mCaptureMode == CaptureLine )
474  {
475  mTempRubberBand->addPoint( point );
476  }
477  else if ( mCaptureMode == CapturePolygon )
478  {
479  const QgsPointXY *firstPoint = mRubberBand->getPoint( 0, 0 );
480  mTempRubberBand->addPoint( *firstPoint );
481  mTempRubberBand->movePoint( point );
482  mTempRubberBand->addPoint( point );
483  }
484 
485  validateGeometry();
486 
487  return 0;
488 }
489 
491 {
492  if ( !c )
493  {
494  return 1;
495  }
496 
497  if ( !mRubberBand )
498  {
500  }
501 
502  QgsLineString *lineString = c->curveToLine();
503  QgsPointSequence linePoints;
504  lineString->points( linePoints );
505  delete lineString;
506  QgsPointSequence::const_iterator ptIt = linePoints.constBegin();
507  for ( ; ptIt != linePoints.constEnd(); ++ptIt )
508  {
509  mRubberBand->addPoint( QgsPointXY( ptIt->x(), ptIt->y() ) );
510  }
511 
512  if ( !mTempRubberBand )
513  {
514  mTempRubberBand = createRubberBand( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry, true );
515  }
516  else
517  {
518  mTempRubberBand->reset();
519  }
520  QgsPoint endPt = c->endPoint();
521  mTempRubberBand->addPoint( QgsPointXY( endPt.x(), endPt.y() ) ); //add last point of c
522 
523  //transform back to layer CRS in case map CRS and layer CRS are different
524  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() );
526  if ( ct.isValid() )
527  {
529  }
530  mCaptureCurve.addCurve( c );
531  for ( int i = 0; i < c->length(); ++i )
532  mSnappingMatches.append( QgsPointLocator::Match() );
533 
534  return 0;
535 }
536 
538 {
539  mCaptureCurve.clear();
540 }
541 
542 QList<QgsPointLocator::Match> QgsMapToolCapture::snappingMatches() const
543 {
544  return mSnappingMatches;
545 }
546 
547 
549 {
550  mTracingStartPoint = QgsPointXY();
551 
552  if ( mRubberBand )
553  {
554  int rubberBandSize = mRubberBand->numberOfVertices();
555  int tempRubberBandSize = mTempRubberBand->numberOfVertices();
556  int captureListSize = size();
557 
558  if ( rubberBandSize < 1 || captureListSize < 1 )
559  {
560  return;
561  }
562 
563  mRubberBand->removePoint( -1 );
564 
565  if ( rubberBandSize > 1 )
566  {
567  if ( tempRubberBandSize > 1 )
568  {
569  const QgsPointXY *point = mRubberBand->getPoint( 0, rubberBandSize - 2 );
570  mTempRubberBand->movePoint( tempRubberBandSize - 2, *point );
571  }
572  }
573  else
574  {
575  mTempRubberBand->reset( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry );
576  }
577 
578  QgsVertexId vertexToRemove;
579  vertexToRemove.part = 0;
580  vertexToRemove.ring = 0;
581  vertexToRemove.vertex = size() - 1;
582  mCaptureCurve.deleteVertex( vertexToRemove );
583  mSnappingMatches.removeAt( vertexToRemove.vertex );
584 
585  validateGeometry();
586  }
587 }
588 
590 {
591  if ( e->key() == Qt::Key_Backspace || e->key() == Qt::Key_Delete )
592  {
593  undo();
594 
595  // Override default shortcut management in MapCanvas
596  e->ignore();
597  }
598  else if ( e->key() == Qt::Key_Escape )
599  {
600  stopCapturing();
601 
602  // Override default shortcut management in MapCanvas
603  e->ignore();
604  }
605 }
606 
608 {
609  mCapturing = true;
610 }
611 
613 {
614  return mCapturing;
615 }
616 
618 {
619  if ( mRubberBand )
620  {
621  delete mRubberBand;
622  mRubberBand = nullptr;
623  }
624 
625  if ( mTempRubberBand )
626  {
627  delete mTempRubberBand;
628  mTempRubberBand = nullptr;
629  }
630 
631  while ( !mGeomErrorMarkers.isEmpty() )
632  {
633  delete mGeomErrorMarkers.takeFirst();
634  }
635 
636  mGeomErrors.clear();
637 
638  mTracingStartPoint = QgsPointXY();
639 
640  mCapturing = false;
641  mCaptureCurve.clear();
642  mSnappingMatches.clear();
643  if ( currentVectorLayer() )
645 }
646 
648 {
649  if ( mTempRubberBand )
650  {
651  delete mTempRubberBand;
652  mTempRubberBand = nullptr;
653  }
654 }
655 
657 {
658  stopCapturing();
659  clearCurve();
660 }
661 
663 {
664  mCaptureCurve.close();
665 }
666 
667 void QgsMapToolCapture::validateGeometry()
668 {
669  QgsSettings settings;
670  if ( settings.value( QStringLiteral( "qgis/digitizing/validate_geometries" ), 1 ).toInt() == 0 )
671  return;
672 
673  if ( mValidator )
674  {
675  mValidator->deleteLater();
676  mValidator = nullptr;
677  }
678 
679  mValidationWarnings.clear();
680  mGeomErrors.clear();
681  while ( !mGeomErrorMarkers.isEmpty() )
682  {
683  delete mGeomErrorMarkers.takeFirst();
684  }
685 
686  QgsGeometry geom;
687 
688  switch ( mCaptureMode )
689  {
690  case CaptureNone:
691  case CapturePoint:
692  return;
693  case CaptureLine:
694  if ( size() < 2 )
695  return;
696  geom = QgsGeometry( mCaptureCurve.curveToLine() );
697  break;
698  case CapturePolygon:
699  if ( size() < 3 )
700  return;
701  QgsLineString *exteriorRing = mCaptureCurve.curveToLine();
702  exteriorRing->close();
703  QgsPolygon *polygon = new QgsPolygon();
704  polygon->setExteriorRing( exteriorRing );
705  geom = QgsGeometry( polygon );
706  break;
707  }
708 
709  if ( !geom )
710  return;
711 
713  if ( settings.value( QStringLiteral( "qgis/digitizing/validate_geometries" ), 1 ).toInt() == 2 )
715  mValidator = new QgsGeometryValidator( geom, nullptr, method );
716  connect( mValidator, &QgsGeometryValidator::errorFound, this, &QgsMapToolCapture::addError );
717  connect( mValidator, &QThread::finished, this, &QgsMapToolCapture::validationFinished );
718  mValidator->start();
719  QgsDebugMsgLevel( "Validation started", 4 );
720 }
721 
722 void QgsMapToolCapture::addError( QgsGeometry::Error e )
723 {
724  mGeomErrors << e;
725  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() );
726  if ( !vlayer )
727  return;
728 
729  mValidationWarnings << e.what();
730 
731  if ( e.hasWhere() )
732  {
734  vm->setCenter( mCanvas->mapSettings().layerToMapCoordinates( vlayer, e.where() ) );
736  vm->setPenWidth( 2 );
737  vm->setToolTip( e.what() );
738  vm->setColor( Qt::green );
739  vm->setZValue( vm->zValue() + 1 );
740  mGeomErrorMarkers << vm;
741  }
742 
743  emit messageDiscarded();
744  emit messageEmitted( mValidationWarnings.join( QStringLiteral( "\n" ) ), Qgis::Warning );
745 }
746 
748 {
749  return mCaptureCurve.numPoints();
750 }
751 
752 QVector<QgsPointXY> QgsMapToolCapture::points() const
753 {
754  QgsPointSequence pts;
755  QVector<QgsPointXY> points;
756  mCaptureCurve.points( pts );
757  QgsGeometry::convertPointList( pts, points );
758  return points;
759 }
760 
761 void QgsMapToolCapture::setPoints( const QVector<QgsPointXY> &pointList )
762 {
763  QgsLineString *line = new QgsLineString( pointList );
764  mCaptureCurve.clear();
765  mCaptureCurve.addCurve( line );
766  mSnappingMatches.clear();
767  for ( int i = 0; i < line->length(); ++i )
768  mSnappingMatches.append( QgsPointLocator::Match() );
769 }
770 
772 {
773  QgsPoint newPoint( QgsWkbTypes::Point, point.x(), point.y() );
774 
775  // get current layer
776  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() );
777  if ( !vlayer )
778  {
779  return newPoint;
780  }
781 
782  // convert to the corresponding type for a full ZM support
783  const QgsWkbTypes::Type type = vlayer->wkbType();
784  if ( QgsWkbTypes::hasZ( type ) && !QgsWkbTypes::hasM( type ) )
785  {
786  newPoint.convertTo( QgsWkbTypes::PointZ );
787  }
788  else if ( !QgsWkbTypes::hasZ( type ) && QgsWkbTypes::hasM( type ) )
789  {
790  newPoint.convertTo( QgsWkbTypes::PointM );
791  }
792  else if ( QgsWkbTypes::hasZ( type ) && QgsWkbTypes::hasM( type ) )
793  {
794  newPoint.convertTo( QgsWkbTypes::PointZM );
795  }
796 
797  // set z value if necessary
798  if ( QgsWkbTypes::hasZ( newPoint.wkbType() ) )
799  {
800  newPoint.setZ( defaultZValue() );
801  }
802 
803  return newPoint;
804 }
805 
807 {
808  QgsPoint newPoint = mapPoint( e.mapPoint() );
809 
810  // set z value from snapped point if necessary
811  if ( QgsWkbTypes::hasZ( newPoint.wkbType() ) )
812  {
813  // if snapped, z dimension is taken from the corresponding snapped
814  // point.
815  if ( e.isSnapped() )
816  {
817  const QgsPointLocator::Match match = e.mapPointMatch();
818  const QgsWkbTypes::Type snappedType = match.layer()->wkbType();
819 
820  if ( QgsWkbTypes::hasZ( snappedType ) )
821  {
822  const QgsFeature ft = match.layer()->getFeature( match.featureId() );
823  newPoint.setZ( ft.geometry().vertexAt( match.vertexIndex() ).z() );
824  }
825  }
826  }
827 
828  return newPoint;
829 }
QgsPoint startPoint() const override
Returns the starting point of the curve.
Base class for all map layer types.
Definition: qgsmaplayer.h:56
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.
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...
QgsVectorLayer * layer() const
The vector layer where the snap occurred.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:57
void setZ(double z)
Sets the point&#39;s z-coordinate.
Definition: qgspoint.h:216
Use GEOS validation methods.
Definition: qgsgeometry.h:1398
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
void messageEmitted(const QString &message, Qgis::MessageLevel=Qgis::Info)
emit a message
double y
Definition: qgspointxy.h:48
QAction * actionEnableTracing() const
Access to action that user may use to toggle tracing on/off. May be null if no action was associated...
A class to represent a 2D point.
Definition: qgspointxy.h:43
QgsFeature getFeature(QgsFeatureId fid) const
Query 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.
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:111
QgsPointXY toMapCoordinates(QPoint point)
transformation from screen coordinates to map coordinates
Definition: qgsmaptool.cpp:40
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:62
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.
virtual void transform(const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection d=QgsCoordinateTransform::ForwardTransform, bool transformZ=false)=0
Transforms the geometry using a coordinate transform.
static bool hasZ(Type type)
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:768
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:74
void addCurve(QgsCurve *c)
Adds a curve to the geometry (takes ownership)
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.
Extension of QgsTracer that provides extra functionality:
QgsCoordinateTransform layerTransform(const QgsMapLayer *layer) const
Return coordinate transform from layer&#39;s CRS to destination CRS.
QgsMapCanvas * mCanvas
pointer to map canvas
Definition: qgsmaptool.h:236
virtual QgsPoint endPoint() const =0
Returns the end point of the curve.
QgsFeatureRequest & setFilterFid(QgsFeatureId fid)
Set feature ID that should be fetched.
CaptureMode mode() const
The capture mode.
virtual double length() const
Returns the length of the geometry.
QAction * actionEnableSnapping() const
Access to action that user may use to toggle snapping on/off.
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:67
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:126
int addCurve(QgsCurve *c)
Adds a whole curve (e.g. circularstring) to the captured geometry. Curve must be in map CRS...
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?
QgsWkbTypes::Type wkbType() const override
Returns the WKBType or WKBUnknown in case of error.
void activate() override
Registers this maptool with the cad dock widget.
Use internal QgsGeometryValidator method.
Definition: qgsgeometry.h:1397
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:123
This class wraps a request for features to a vector layer (or directly its vector data provider)...
QgsCoordinateReferenceSystem crs() const
Returns the layer&#39;s spatial reference system.
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
Return default Z value Use for set Z coordinate to new vertex for 2.5d geometries.
QgsGeometry geometry() const
Returns the geometry associated with this feature.
Definition: qgsfeature.cpp:101
~QgsMapToolCapture() override
void errorFound(const QgsGeometry::Error &)
QgsWkbTypes::Type wkbType() const
Returns the WKB type of the geometry.
QList< QgsPointLocator::Match > snappingMatches() const
Return 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
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const override
Query the layer for features specified in request.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
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
Get 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.
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:52
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.
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.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), const Section section=NoSection) const
Returns the value for setting key.
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:41
ValidationMethod
Available methods for validating geometries.
Definition: qgsgeometry.h:1395
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:660
Class that shows snapping marker on map canvas for the current snapping match.
Class for doing transforms between two map coordinate systems.
void messageDiscarded()
emit signal to clear previous message
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:125
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:617
virtual void setCursor(const QCursor &cursor)
Set a user defined cursor.
Definition: qgsmaptool.cpp:142
static bool hasM(Type type)
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:818
static QCursor getThemeCursor(Cursor cursor)
Helper to get a theme cursor.
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.
double length() const override
Returns the 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:727
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
virtual QgsPoint vertexAt(QgsVertexId id) const =0
Returns the point corresponding to a specified vertex id.
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)
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 clearCurve()
Clear capture curve.
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.