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