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