QGIS API Documentation  3.2.0-Bonn (bc43194)
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 
403  layerPoint = f.geometry().constGet()->vertexAt( vId );
404 
405  // ZM support depends on the target layer
406  if ( !QgsWkbTypes::hasZ( vlayer->wkbType() ) )
407  {
408  layerPoint.dropZValue();
409  }
410 
411  if ( !QgsWkbTypes::hasM( vlayer->wkbType() ) )
412  {
413  layerPoint.dropMValue();
414  }
415 
416  return 0;
417  }
418  else
419  {
420  return 2;
421  }
422  }
423  else
424  {
425  return 1;
426  }
427 }
428 
430 {
431  return addVertex( point, QgsPointLocator::Match() );
432 }
433 
435 {
436  if ( mode() == CaptureNone )
437  {
438  QgsDebugMsg( "invalid capture mode" );
439  return 2;
440  }
441 
442  int res;
443  QgsPoint layerPoint;
444  res = fetchLayerPoint( match, layerPoint );
445  if ( res != 0 )
446  {
447  res = nextPoint( QgsPoint( point ), layerPoint );
448  if ( res != 0 )
449  {
450  return res;
451  }
452  }
453 
454  if ( !mRubberBand )
455  {
457  }
458 
459  if ( !mTempRubberBand )
460  {
461  mTempRubberBand = createRubberBand( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry, true );
462  }
463  else
464  {
465  mTempRubberBand->reset( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry );
466  }
467 
468  bool traceCreated = false;
469  if ( tracingEnabled() )
470  {
471  traceCreated = tracingAddVertex( point );
472  }
473 
474  // keep new tracing start point if we created a trace. This is useful when tracing with
475  // offset so that the user stays "snapped"
476  mTracingStartPoint = traceCreated ? point : QgsPointXY();
477 
478  if ( !traceCreated )
479  {
480  // ordinary digitizing
481  mRubberBand->addPoint( point );
482  mCaptureCurve.addVertex( layerPoint );
483  mSnappingMatches.append( match );
484  }
485 
486  if ( mCaptureMode == CaptureLine )
487  {
488  mTempRubberBand->addPoint( point );
489  }
490  else if ( mCaptureMode == CapturePolygon )
491  {
492  const QgsPointXY *firstPoint = mRubberBand->getPoint( 0, 0 );
493  mTempRubberBand->addPoint( *firstPoint );
494  mTempRubberBand->movePoint( point );
495  mTempRubberBand->addPoint( point );
496  }
497 
498  validateGeometry();
499 
500  return 0;
501 }
502 
504 {
505  if ( !c )
506  {
507  return 1;
508  }
509 
510  if ( !mRubberBand )
511  {
513  }
514 
515  QgsLineString *lineString = c->curveToLine();
516  QgsPointSequence linePoints;
517  lineString->points( linePoints );
518  delete lineString;
519  QgsPointSequence::const_iterator ptIt = linePoints.constBegin();
520  for ( ; ptIt != linePoints.constEnd(); ++ptIt )
521  {
522  mRubberBand->addPoint( QgsPointXY( ptIt->x(), ptIt->y() ) );
523  }
524 
525  if ( !mTempRubberBand )
526  {
527  mTempRubberBand = createRubberBand( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry, true );
528  }
529  else
530  {
531  mTempRubberBand->reset();
532  }
533  QgsPoint endPt = c->endPoint();
534  mTempRubberBand->addPoint( QgsPointXY( endPt.x(), endPt.y() ) ); //add last point of c
535 
536  //transform back to layer CRS in case map CRS and layer CRS are different
537  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() );
539  if ( ct.isValid() )
540  {
542  }
543  mCaptureCurve.addCurve( c );
544  for ( int i = 0; i < c->length(); ++i )
545  mSnappingMatches.append( QgsPointLocator::Match() );
546 
547  return 0;
548 }
549 
551 {
552  mCaptureCurve.clear();
553 }
554 
555 QList<QgsPointLocator::Match> QgsMapToolCapture::snappingMatches() const
556 {
557  return mSnappingMatches;
558 }
559 
560 
562 {
563  mTracingStartPoint = QgsPointXY();
564 
565  if ( mRubberBand )
566  {
567  int rubberBandSize = mRubberBand->numberOfVertices();
568  int tempRubberBandSize = mTempRubberBand->numberOfVertices();
569  int captureListSize = size();
570 
571  if ( rubberBandSize < 1 || captureListSize < 1 )
572  {
573  return;
574  }
575 
576  mRubberBand->removePoint( -1 );
577 
578  if ( rubberBandSize > 1 )
579  {
580  if ( tempRubberBandSize > 1 )
581  {
582  const QgsPointXY *point = mRubberBand->getPoint( 0, rubberBandSize - 2 );
583  mTempRubberBand->movePoint( tempRubberBandSize - 2, *point );
584  }
585  }
586  else
587  {
588  mTempRubberBand->reset( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry );
589  }
590 
591  QgsVertexId vertexToRemove;
592  vertexToRemove.part = 0;
593  vertexToRemove.ring = 0;
594  vertexToRemove.vertex = size() - 1;
595  mCaptureCurve.deleteVertex( vertexToRemove );
596  mSnappingMatches.removeAt( vertexToRemove.vertex );
597 
598  validateGeometry();
599  }
600 }
601 
603 {
604  if ( e->key() == Qt::Key_Backspace || e->key() == Qt::Key_Delete )
605  {
606  undo();
607 
608  // Override default shortcut management in MapCanvas
609  e->ignore();
610  }
611  else if ( e->key() == Qt::Key_Escape )
612  {
613  stopCapturing();
614 
615  // Override default shortcut management in MapCanvas
616  e->ignore();
617  }
618 }
619 
621 {
622  mCapturing = true;
623 }
624 
626 {
627  return mCapturing;
628 }
629 
631 {
632  if ( mRubberBand )
633  {
634  delete mRubberBand;
635  mRubberBand = nullptr;
636  }
637 
638  if ( mTempRubberBand )
639  {
640  delete mTempRubberBand;
641  mTempRubberBand = nullptr;
642  }
643 
644  while ( !mGeomErrorMarkers.isEmpty() )
645  {
646  delete mGeomErrorMarkers.takeFirst();
647  }
648 
649  mGeomErrors.clear();
650 
651  mTracingStartPoint = QgsPointXY();
652 
653  mCapturing = false;
654  mCaptureCurve.clear();
655  mSnappingMatches.clear();
656  if ( currentVectorLayer() )
658 }
659 
661 {
662  if ( mTempRubberBand )
663  {
664  delete mTempRubberBand;
665  mTempRubberBand = nullptr;
666  }
667 }
668 
670 {
671  stopCapturing();
672  clearCurve();
673 }
674 
676 {
677  mCaptureCurve.close();
678 }
679 
680 void QgsMapToolCapture::validateGeometry()
681 {
682  QgsSettings settings;
683  if ( settings.value( QStringLiteral( "qgis/digitizing/validate_geometries" ), 1 ).toInt() == 0 )
684  return;
685 
686  if ( mValidator )
687  {
688  mValidator->deleteLater();
689  mValidator = nullptr;
690  }
691 
692  mValidationWarnings.clear();
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 )
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  connect( mValidator, &QThread::finished, this, &QgsMapToolCapture::validationFinished );
731  mValidator->start();
732  QgsDebugMsgLevel( "Validation started", 4 );
733 }
734 
735 void QgsMapToolCapture::addError( QgsGeometry::Error e )
736 {
737  mGeomErrors << e;
738  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() );
739  if ( !vlayer )
740  return;
741 
742  mValidationWarnings << e.what();
743 
744  if ( e.hasWhere() )
745  {
747  vm->setCenter( mCanvas->mapSettings().layerToMapCoordinates( vlayer, e.where() ) );
749  vm->setPenWidth( 2 );
750  vm->setToolTip( e.what() );
751  vm->setColor( Qt::green );
752  vm->setZValue( vm->zValue() + 1 );
753  mGeomErrorMarkers << vm;
754  }
755 
756  emit messageDiscarded();
757  emit messageEmitted( mValidationWarnings.join( QStringLiteral( "\n" ) ), Qgis::Warning );
758 }
759 
761 {
762  return mCaptureCurve.numPoints();
763 }
764 
765 QVector<QgsPointXY> QgsMapToolCapture::points() const
766 {
767  QgsPointSequence pts;
768  QVector<QgsPointXY> points;
769  mCaptureCurve.points( pts );
770  QgsGeometry::convertPointList( pts, points );
771  return points;
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  QgsPoint newPoint( QgsWkbTypes::Point, point.x(), point.y() );
787 
788  // get current layer
789  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() );
790  if ( !vlayer )
791  {
792  return newPoint;
793  }
794 
795  // convert to the corresponding type for a full ZM support
796  const QgsWkbTypes::Type type = vlayer->wkbType();
797  if ( QgsWkbTypes::hasZ( type ) && !QgsWkbTypes::hasM( type ) )
798  {
799  newPoint.convertTo( QgsWkbTypes::PointZ );
800  }
801  else if ( !QgsWkbTypes::hasZ( type ) && QgsWkbTypes::hasM( type ) )
802  {
803  newPoint.convertTo( QgsWkbTypes::PointM );
804  }
805  else if ( QgsWkbTypes::hasZ( type ) && QgsWkbTypes::hasM( type ) )
806  {
807  newPoint.convertTo( QgsWkbTypes::PointZM );
808  }
809 
810  // set z value if necessary
811  if ( QgsWkbTypes::hasZ( newPoint.wkbType() ) )
812  {
813  newPoint.setZ( defaultZValue() );
814  }
815 
816  return newPoint;
817 }
818 
820 {
821  QgsPoint newPoint = mapPoint( e.mapPoint() );
822 
823  // set z value from snapped point if necessary
824  if ( QgsWkbTypes::hasZ( newPoint.wkbType() ) )
825  {
826  // if snapped, z dimension is taken from the corresponding snapped
827  // point.
828  if ( e.isSnapped() )
829  {
830  const QgsPointLocator::Match match = e.mapPointMatch();
831  const QgsWkbTypes::Type snappedType = match.layer()->wkbType();
832 
833  if ( QgsWkbTypes::hasZ( snappedType ) )
834  {
835  const QgsFeature ft = match.layer()->getFeature( match.featureId() );
836  newPoint.setZ( ft.geometry().vertexAt( match.vertexIndex() ).z() );
837  }
838  }
839  }
840 
841  return newPoint;
842 }
QgsPoint startPoint() const override
Returns the starting point of the curve.
Base class for all map layer types.
Definition: qgsmaplayer.h:61
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...
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:237
Use GEOS validation methods.
Definition: qgsgeometry.h:1447
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
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:104
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.
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.
bool dropZValue() override
Drops any z-dimensions which exist in the geometry.
Definition: qgspoint.cpp:510
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:236
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 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
bool dropMValue() override
Drops any measure values which exist in the geometry.
Definition: qgspoint.cpp:521
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:1446
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
Returns 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
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
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
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.
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.
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:1444
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:659
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:616
virtual void setCursor(const QCursor &cursor)
Sets 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:726
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.