QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
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#include "qgsexception.h"
18#include "qgsfeatureiterator.h"
20#include "qgslinestring.h"
21#include "qgslogger.h"
22#include "qgsmapcanvas.h"
23#include "qgsmapcanvastracer.h"
24#include "qgsmapmouseevent.h"
25#include "qgspolygon.h"
26#include "qgsrubberband.h"
27#include "qgssnapindicator.h"
28#include "qgsvectorlayer.h"
29#include "qgsvertexmarker.h"
31#include "qgsapplication.h"
32#include "qgsproject.h"
36#include "qgssnappingutils.h"
38
39#include <QAction>
40#include <QCursor>
41#include <QPixmap>
42#include <QStatusBar>
43
44
46 : QgsMapToolAdvancedDigitizing( canvas, cadDockWidget )
47 , mCaptureMode( mode )
48 , mCaptureModeFromLayer( mode == CaptureNone )
49{
50 mTempRubberBand.setParentOwner( canvas );
51
52 mSnapIndicator.reset( new QgsSnapIndicator( canvas ) );
53
54 setCursor( QgsApplication::getThemeCursor( QgsApplication::Cursor::CapturePoint ) );
55
57 this, &QgsMapToolCapture::currentLayerChanged );
58
60 layerOptions.skipCrsValidation = true;
61 layerOptions.loadDefaultStyle = false;
62 mExtraSnapLayer = new QgsVectorLayer( QStringLiteral( "LineString?crs=" ), QStringLiteral( "extra snap" ), QStringLiteral( "memory" ), layerOptions );
63 mExtraSnapLayer->startEditing();
64 QgsFeature f;
65 mExtraSnapLayer->addFeature( f );
66 mExtraSnapFeatureId = f.id();
67
69 this, &QgsMapToolCapture::updateExtraSnapLayer );
70
71 currentLayerChanged( canvas->currentLayer() );
72}
73
75{
76 // during tear down we have to clean up mExtraSnapLayer first, before
77 // we call stop capturing. Otherwise stopCapturing tries to access members
78 // from the mapcanvas, which is likely already being destroyed and triggering
79 // the deletion of this object...
80 if ( mCanvas )
81 {
82 mCanvas->snappingUtils()->removeExtraSnapLayer( mExtraSnapLayer );
83 }
84 mExtraSnapLayer->deleteLater();
85 mExtraSnapLayer = nullptr;
86
88
89 if ( mValidator )
90 {
91 mValidator->deleteLater();
92 mValidator = nullptr;
93 }
94}
95
96QgsMapToolCapture::Capabilities QgsMapToolCapture::capabilities() const
97{
99}
100
102{
103 switch ( technique )
104 {
106 return true;
110 return false;
111 }
113}
114
116{
117 if ( mTempRubberBand )
118 mTempRubberBand->show();
119
120 mCanvas->snappingUtils()->addExtraSnapLayer( mExtraSnapLayer );
122
123 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape && mCurrentShapeMapTool )
124 mCurrentShapeMapTool->activate( mCaptureMode, mCaptureLastPoint );
125}
126
128{
129 if ( mTempRubberBand )
130 mTempRubberBand->hide();
131
132 mSnapIndicator->setMatch( QgsPointLocator::Match() );
133
134 mCanvas->snappingUtils()->removeExtraSnapLayer( mExtraSnapLayer );
135
136 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape && mCurrentShapeMapTool )
137 mCurrentShapeMapTool->deactivate();
138
140}
141
142void QgsMapToolCapture::currentLayerChanged( QgsMapLayer *layer )
143{
144 if ( !mCaptureModeFromLayer )
145 return;
146
147 mCaptureMode = CaptureNone;
148
149 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
150 if ( !vlayer )
151 {
152 return;
153 }
154
155 switch ( vlayer->geometryType() )
156 {
158 mCaptureMode = CapturePoint;
159 break;
161 mCaptureMode = CaptureLine;
162 break;
164 mCaptureMode = CapturePolygon;
165 break;
166 default:
167 mCaptureMode = CaptureNone;
168 break;
169 }
170
171 if ( mTempRubberBand )
172 mTempRubberBand->setRubberBandGeometryType( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry );
173
174 resetRubberBand();
176}
177
178
179bool QgsMapToolCapture::tracingEnabled()
180{
182 return tracer && ( !tracer->actionEnableTracing() || tracer->actionEnableTracing()->isChecked() )
183 && ( !tracer->actionEnableSnapping() || tracer->actionEnableSnapping()->isChecked() );
184}
185
186
187QgsPointXY QgsMapToolCapture::tracingStartPoint()
188{
189 // if we have starting point from previous trace, then preferably use that one
190 // (useful when tracing with offset)
191 if ( mTracingStartPoint != QgsPointXY() )
192 return mTracingStartPoint;
193
194 return mCaptureLastPoint;
195}
196
197
198bool QgsMapToolCapture::tracingMouseMove( QgsMapMouseEvent *e )
199{
200 if ( !e->isSnapped() )
201 return false;
202
203 QgsPointXY pt0 = tracingStartPoint();
204 if ( pt0 == QgsPointXY() )
205 return false;
206
208 if ( !tracer )
209 return false; // this should not happen!
210
212 QVector<QgsPointXY> points = tracer->findShortestPath( pt0, e->mapPoint(), &err );
213 if ( points.isEmpty() )
214 {
215 tracer->reportError( err, false );
216 return false;
217 }
218
219 mTempRubberBand->reset( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry, QgsWkbTypes::LineString, mCaptureFirstPoint );
220 mTempRubberBand->addPoint( mCaptureLastPoint );
221
222 // if there is offset, we need to fix the rubber bands to make sure they are aligned correctly.
223 // There are two cases we need to sort out:
224 // 1. the last point of mRubberBand may need to be moved off the traced curve to respect the offset
225 // 2. first point of mTempRubberBand may be needed to be moved to the beginning of the offset trace
226 const QgsPoint lastPoint = mCaptureLastPoint;
227 QgsPointXY lastPointXY( lastPoint );
228 if ( lastPointXY == pt0 && points[0] != lastPointXY )
229 {
230 if ( mRubberBand->numberOfVertices() != 0 )
231 {
232 // if rubber band had just one point, for some strange reason it contains the point twice
233 // we only want to move the last point if there are multiple points already
234 if ( mRubberBand->numberOfVertices() > 2 || ( mRubberBand->numberOfVertices() == 2 && *mRubberBand->getPoint( 0, 0 ) != *mRubberBand->getPoint( 0, 1 ) ) )
235 mRubberBand->movePoint( points[0] );
236 }
237
238 mTempRubberBand->movePoint( 0, QgsPoint( points[0] ) );
239 }
240
241 mTempRubberBand->movePoint( QgsPoint( points[0] ) );
242
243 // update temporary rubberband
244 for ( int i = 1; i < points.count(); ++i ) //points added in the rubber band are 2D but will not be added to the capture curve
245 mTempRubberBand->addPoint( QgsPoint( points.at( i ) ), i == points.count() - 1 );
246
247
248 mTempRubberBand->addPoint( QgsPoint( points[points.size() - 1] ) );
249
250 tracer->reportError( QgsTracer::ErrNone, false ); // clear messagebar if there was any error
251 return true;
252}
253
254
255bool QgsMapToolCapture::tracingAddVertex( const QgsPointXY &point )
256{
258 if ( !tracer )
259 return false; // this should not happen!
260
261 if ( mTempRubberBand->pointsCount() == 0 )
262 {
263 if ( !tracer->init() )
264 {
266 return false;
267 }
268
269 // only accept first point if it is snapped to the graph (to vertex or edge)
270 const bool res = tracer->isPointSnapped( point );
271 if ( res )
272 {
273 mTracingStartPoint = point;
274 }
275 return false;
276 }
277
278 QgsPointXY pt0 = tracingStartPoint();
279 if ( pt0 == QgsPointXY() )
280 return false;
281
283 const QVector<QgsPointXY> tracedPointsInMapCrs = tracer->findShortestPath( pt0, point, &err );
284 if ( tracedPointsInMapCrs.isEmpty() )
285 return false; // ignore the vertex - can't find path to the end point!
286
287 // transform points
288 QgsPointSequence layerPoints;
289 layerPoints.reserve( tracedPointsInMapCrs.size() );
290 QgsPointSequence mapPoints;
291 mapPoints.reserve( tracedPointsInMapCrs.size() );
292 for ( const QgsPointXY &tracedPointMapCrs : tracedPointsInMapCrs )
293 {
294 QgsPoint mapPoint( tracedPointMapCrs );
295
296 QgsPoint lp; // in layer coords
297 if ( nextPoint( mapPoint, lp ) != 0 )
298 return false;
299
300 // copy z and m from layer point back to mapPoint, as nextPoint() call will populate these based
301 // on the context of the trace
302 if ( lp.is3D() )
303 mapPoint.addZValue( lp.z() );
304 if ( lp.isMeasure() )
305 mapPoint.addMValue( lp.m() );
306
307 mapPoints << mapPoint;
308 layerPoints << lp;
309 }
310
311 // Move the last point of the captured curve to the first point on the trace string (necessary if there is offset)
312 const QgsVertexId lastVertexId( 0, 0, mCaptureCurve.numPoints() - 1 );
313 mCaptureCurve.moveVertex( lastVertexId, layerPoints.first() );
314 mSnappingMatches.removeLast();
315 mSnappingMatches.append( QgsPointLocator::Match() );
316
317 int pointBefore = mCaptureCurve.numPoints();
318 addCurve( new QgsLineString( mapPoints ) );
319
320 resetRubberBand();
321
322 // Curves de-approximation
324 {
325 // If the tool and the layer support curves
326 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() );
327 if ( vlayer && capabilities().testFlag( QgsMapToolCapture::Capability::SupportsCurves ) && vlayer->dataProvider()->capabilities().testFlag( QgsVectorDataProvider::Capability::CircularGeometries ) )
328 {
329 const QgsGeometry linear = QgsGeometry( mCaptureCurve.segmentize() );
330 const QgsGeometry curved = linear.convertToCurves(
333 );
335 {
336 mCaptureCurve.clear();
337 mCaptureCurve.addCurve( qgsgeometry_cast< const QgsCurve * >( curved.constGet() )->clone() );
338 }
339 else
340 {
341 mCaptureCurve = *qgsgeometry_cast<QgsCompoundCurve *>( curved.constGet() );
342 }
343 }
344 }
345
346 // sync the snapping matches list
347 const int pointAfter = mCaptureCurve.numPoints();
348 for ( ; pointBefore < pointAfter; ++pointBefore )
349 mSnappingMatches.append( QgsPointLocator::Match() );
350
351 tracer->reportError( QgsTracer::ErrNone, true ); // clear messagebar if there was any error
352
353 // adjust last captured point
354 const QgsPoint lastPt = mCaptureCurve.endPoint();
355 mCaptureLastPoint = toMapCoordinates( layer(), lastPt );
356
357 return true;
358}
359
360QgsMapToolCaptureRubberBand *QgsMapToolCapture::createCurveRubberBand() const
361{
362 QgsMapToolCaptureRubberBand *rb = new QgsMapToolCaptureRubberBand( mCanvas );
363 rb->setStrokeWidth( digitizingStrokeWidth() );
364 QColor color = digitizingStrokeColor();
365
367 color.setAlphaF( color.alphaF() * alphaScale );
368 rb->setLineStyle( Qt::DotLine );
369 rb->setStrokeColor( color );
370
371 const QColor fillColor = digitizingFillColor();
372 rb->setFillColor( fillColor );
373 rb->show();
374 return rb;
375}
376
377void QgsMapToolCapture::resetRubberBand()
378{
379 if ( !mRubberBand )
380 return;
381 QgsLineString *lineString = mCaptureCurve.curveToLine();
382
384 mRubberBand->addGeometry( QgsGeometry( lineString ), layer() );
385}
386
388{
389 return mRubberBand.release();
390}
391
393{
394 if ( enable )
396 else
398}
399
401{
402 if ( enable )
404 else
406}
407
409{
410 if ( mCurrentCaptureTechnique == technique )
411 return;
412
413 mStartNewCurve = true;
414
415 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape && mCurrentShapeMapTool )
416 {
417 mCurrentShapeMapTool->deactivate();
418 clean();
419 }
420
421 switch ( technique )
422 {
424 mLineDigitizingType = QgsWkbTypes::LineString;
425 break;
427 mLineDigitizingType = QgsWkbTypes::CircularString;
428 break;
430 mLineDigitizingType = QgsWkbTypes::LineString;
432 break;
434 mLineDigitizingType = QgsWkbTypes::LineString;
435 break;
436
437 }
438
439 if ( mTempRubberBand )
440 mTempRubberBand->setStringType( mLineDigitizingType );
441
442 mCurrentCaptureTechnique = technique;
443
444 if ( technique == Qgis::CaptureTechnique::Shape && mCurrentShapeMapTool && isActive() )
445 {
446 clean();
447 mCurrentShapeMapTool->activate( mCaptureMode, mCaptureLastPoint );
448 }
449}
450
452{
453 if ( mCurrentShapeMapTool )
454 {
455 if ( shapeMapToolMetadata && mCurrentShapeMapTool->id() == shapeMapToolMetadata->id() )
456 return;
457 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape )
458 mCurrentShapeMapTool->deactivate();
459 mCurrentShapeMapTool->deleteLater();
460 }
461
462 mCurrentShapeMapTool.reset( shapeMapToolMetadata ? shapeMapToolMetadata->factory( this ) : nullptr );
463
464 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape && isActive() )
465 {
466 clean();
467 if ( mCurrentShapeMapTool )
468 mCurrentShapeMapTool->activate( mCaptureMode, mCaptureLastPoint );
469 }
470}
471
473{
475
476 const QgsPointXY point = e->mapPoint();
477
478 mSnapIndicator->setMatch( e->mapPointMatch() );
479
480 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape )
481 {
482 if ( !mCurrentShapeMapTool )
483 {
484 emit messageEmitted( tr( "Select an option from the Shape Digitizing Toolbar in order to capture shapes" ), Qgis::MessageLevel::Warning );
485 }
486 else
487 {
488 if ( !mTempRubberBand )
489 {
490 mTempRubberBand.reset( createCurveRubberBand() );
491 mTempRubberBand->setStringType( mLineDigitizingType );
492 mTempRubberBand->setRubberBandGeometryType( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry );
493 }
494
495 mCurrentShapeMapTool->cadCanvasMoveEvent( e, mCaptureMode );
496 return;
497 }
498 }
499 else
500 {
501 const QgsPoint mapPoint = QgsPoint( point );
502
503 if ( mCaptureMode != CapturePoint && mTempRubberBand && mCapturing )
504 {
505 bool hasTrace = false;
506
507 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Streaming )
508 {
509 if ( !mCaptureCurve.isEmpty() )
510 {
511 const QgsPoint prevPoint = mCaptureCurve.curveAt( mCaptureCurve.nCurves() - 1 )->endPoint();
512 if ( QgsPointXY( toCanvasCoordinates( toMapCoordinates( layer(), prevPoint ) ) ).distance( toCanvasCoordinates( point ) ) < mStreamingToleranceInPixels )
513 return;
514 }
515
516 mAllowAddingStreamingPoints = true;
518 mAllowAddingStreamingPoints = false;
519 }
520 else if ( tracingEnabled() && mCaptureCurve.numPoints() != 0 )
521 {
522 // Store the intermediate point for circular string to retrieve after tracing mouse move if
523 // the digitizing type is circular and the temp rubber band is effectivly circular and if this point is existing
524 // Store an empty point if the digitizing type is linear ot the point is not existing (curve not complete)
525 if ( mLineDigitizingType == QgsWkbTypes::CircularString &&
526 mTempRubberBand->stringType() == QgsWkbTypes::CircularString &&
527 mTempRubberBand->curveIsComplete() )
528 mCircularItermediatePoint = mTempRubberBand->pointFromEnd( 1 );
529 else if ( mLineDigitizingType == QgsWkbTypes::LineString ||
530 !mTempRubberBand->curveIsComplete() )
531 mCircularItermediatePoint = QgsPoint();
532
533 hasTrace = tracingMouseMove( e );
534
535 if ( !hasTrace )
536 {
537 // Restore the temp rubber band
538 mTempRubberBand->reset( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry, mLineDigitizingType, mCaptureFirstPoint );
539 mTempRubberBand->addPoint( mCaptureLastPoint );
540 if ( !mCircularItermediatePoint.isEmpty() )
541 {
542 mTempRubberBand->movePoint( mCircularItermediatePoint );
543 mTempRubberBand->addPoint( mCircularItermediatePoint );
544 }
545 }
546 }
547
548 if ( mCurrentCaptureTechnique != Qgis::CaptureTechnique::Streaming && !hasTrace )
549 {
550 if ( mCaptureCurve.numPoints() > 0 )
551 {
552 const QgsPoint mapPt = mCaptureLastPoint;
553
554 if ( mTempRubberBand )
555 {
556 mTempRubberBand->movePoint( mapPoint );
557 mTempRubberBand->movePoint( 0, mapPt );
558 }
559
560 // fix existing rubber band after tracing - the last point may have been moved if using offset
561 if ( mRubberBand->numberOfVertices() )
562 mRubberBand->movePoint( mapPt );
563 }
564 else if ( mTempRubberBand )
565 mTempRubberBand->movePoint( mapPoint );
566 }
567 }
568 }
569} // mouseMoveEvent
570
571
572int QgsMapToolCapture::nextPoint( const QgsPoint &mapPoint, QgsPoint &layerPoint )
573{
574 if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() ) )
575 {
576 try
577 {
578 const QgsPointXY mapP( mapPoint.x(), mapPoint.y() ); //#spellok
579 layerPoint = QgsPoint( toLayerCoordinates( vlayer, mapP ) ); //transform snapped point back to layer crs //#spellok
580 if ( QgsWkbTypes::hasZ( vlayer->wkbType() ) && !layerPoint.is3D() )
582 if ( QgsWkbTypes::hasM( vlayer->wkbType() ) && !layerPoint.isMeasure() )
584 }
585 catch ( QgsCsException & )
586 {
587 QgsDebugMsg( QStringLiteral( "transformation to layer coordinate failed" ) );
588 return 2;
589 }
590 }
591 else
592 {
593 layerPoint = QgsPoint( toLayerCoordinates( layer(), mapPoint ) );
594 }
595
596 return 0;
597}
598
599int QgsMapToolCapture::nextPoint( QPoint p, QgsPoint &layerPoint, QgsPoint &mapPoint )
600{
602 return nextPoint( mapPoint, layerPoint );
603}
604
606{
607 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() );
608 QgsVectorLayer *sourceLayer = match.layer();
610 {
612 return 0;
613 }
614 else if ( !vlayer )
615 {
616 return 1;
617 }
618
619 if ( match.isValid() && sourceLayer )
620 {
621 if ( ( match.hasVertex() || match.hasLineEndpoint() ) )
622 {
623 if ( sourceLayer->crs() != vlayer->crs() )
624 {
625 return 1;
626 }
627 QgsFeature f;
628 QgsFeatureRequest request;
629 request.setFilterFid( match.featureId() );
630 const bool fetched = match.layer()->getFeatures( request ).nextFeature( f );
631 if ( fetched )
632 {
633 QgsVertexId vId;
634 if ( !f.geometry().vertexIdFromVertexNr( match.vertexIndex(), vId ) )
635 {
636 return 2;
637 }
638 layerPoint = f.geometry().constGet()->vertexAt( vId );
639 if ( QgsWkbTypes::hasZ( vlayer->wkbType() ) && !layerPoint.is3D() )
640 layerPoint.addZValue( defaultZValue() );
641 if ( QgsWkbTypes::hasM( vlayer->wkbType() ) && !layerPoint.isMeasure() )
642 layerPoint.addMValue( defaultMValue() );
643
644 // ZM support depends on the target layer
645 if ( !QgsWkbTypes::hasZ( vlayer->wkbType() ) )
646 {
647 layerPoint.dropZValue();
648 }
649
650 if ( !QgsWkbTypes::hasM( vlayer->wkbType() ) )
651 {
652 layerPoint.dropMValue();
653 }
654
655 return 0;
656 }
657 return 2;
658 }
659 else if ( QgsProject::instance()->topologicalEditing() && ( match.hasEdge() || match.hasMiddleSegment() ) )
660 {
661 layerPoint = toLayerCoordinates( vlayer, match.interpolatedPoint( mCanvas->mapSettings().destinationCrs() ) );
662 return 0;
663 }
664 }
665 return 2;
666}
667
669{
670 return addVertex( point, QgsPointLocator::Match() );
671}
672
674{
675 if ( mode() == CaptureNone )
676 {
677 QgsDebugMsg( QStringLiteral( "invalid capture mode" ) );
678 return 2;
679 }
680
681 if ( mCapturing && mCurrentCaptureTechnique == Qgis::CaptureTechnique::Streaming && !mAllowAddingStreamingPoints )
682 return 0;
683
684 QgsPoint layerPoint;
685 if ( layer() )
686 {
687 int res = fetchLayerPoint( match, layerPoint );
688 if ( res != 0 )
689 {
690 res = nextPoint( QgsPoint( point ), layerPoint );
691 if ( res != 0 )
692 {
693 return res;
694 }
695 }
696 }
697 else
698 {
699 layerPoint = QgsPoint( point );
700 }
701 const QgsPoint mapPoint = toMapCoordinates( layer(), layerPoint );
702
703 if ( mCaptureMode == CapturePoint )
704 {
705 mCaptureCurve.addVertex( layerPoint );
706 mSnappingMatches.append( match );
707 }
708 else
709 {
710 if ( mCaptureFirstPoint.isEmpty() )
711 {
712 mCaptureFirstPoint = mapPoint;
713 }
714
715 if ( !mRubberBand )
717
718 if ( !mTempRubberBand )
719 {
720 mTempRubberBand.reset( createCurveRubberBand() );
721 mTempRubberBand->setStringType( mLineDigitizingType );
722 mTempRubberBand->reset( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry, mLineDigitizingType, mapPoint );
723 }
724
725 bool traceCreated = false;
726 if ( tracingEnabled() )
727 {
728 traceCreated = tracingAddVertex( mapPoint );
729 }
730
731 // keep new tracing start point if we created a trace. This is useful when tracing with
732 // offset so that the user stays "snapped"
733 mTracingStartPoint = traceCreated ? point : QgsPointXY();
734
735 if ( !traceCreated )
736 {
737 // ordinary digitizing
738 mTempRubberBand->movePoint( mapPoint ); //move the last point of the temp rubberband before operating with it
739 if ( mTempRubberBand->curveIsComplete() ) //2 points for line and 3 points for circular
740 {
741 if ( QgsCurve *curve = mTempRubberBand->curve() )
742 {
743 addCurve( curve );
744 // add curve append only invalid match to mSnappingMatches,
745 // so we need to remove them and add the one from here if it is valid
746 if ( match.isValid() && mSnappingMatches.count() > 0 && !mSnappingMatches.last().isValid() )
747 {
748 mSnappingMatches.removeLast();
749 if ( mTempRubberBand->stringType() == QgsWkbTypes::CircularString )
750 {
751 // for circular string two points are added and match for intermediate point is stored
752 mSnappingMatches.removeLast();
753 mSnappingMatches.append( mCircularIntermediateMatch );
754 }
755 mSnappingMatches.append( match );
756 }
757 }
758 mCaptureLastPoint = mapPoint;
759 mTempRubberBand->reset( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry, mLineDigitizingType, mCaptureFirstPoint );
760 }
761 else if ( mTempRubberBand->pointsCount() == 0 )
762 {
763 mCaptureLastPoint = mapPoint;
764 mCaptureCurve.addVertex( layerPoint );
765 mSnappingMatches.append( match );
766 }
767 else
768 {
769 if ( mTempRubberBand->stringType() == QgsWkbTypes::CircularString )
770 {
771 mCircularIntermediateMatch = match;
772 }
773 }
774
775 mTempRubberBand->addPoint( mapPoint );
776 }
777 else
778 {
779 mTempRubberBand->reset( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry, mLineDigitizingType, mCaptureFirstPoint );
780 mTempRubberBand->addPoint( mCaptureLastPoint );
781 }
782 }
783
784 updateExtraSnapLayer();
785 validateGeometry();
786
787 return 0;
788}
789
791{
792 if ( !c )
793 {
794 return 1;
795 }
796
797 if ( !mRubberBand )
798 {
800 }
801
802 if ( mTempRubberBand )
803 {
804 mTempRubberBand->reset( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry, mLineDigitizingType, mCaptureFirstPoint );
805 const QgsPoint endPt = c->endPoint();
806 mTempRubberBand->addPoint( endPt ); //add last point of c
807 }
808
809 //transform back to layer CRS in case map CRS and layer CRS are different
810 const QgsCoordinateTransform ct = mCanvas->mapSettings().layerTransform( layer() );
811 if ( ct.isValid() )
812 {
813 c->transform( ct, Qgis::TransformDirection::Reverse );
814 }
815 const int countBefore = mCaptureCurve.vertexCount();
816 //if there is only one point, this the first digitized point that are in the this first curve added --> remove the point
817 if ( mCaptureCurve.numPoints() == 1 )
818 mCaptureCurve.removeCurve( 0 );
819
820 // we set the extendPrevious option to true to avoid creating compound curves with many 2 vertex linestrings -- instead we prefer
821 // to extend linestring curves so that they continue the previous linestring wherever possible...
822 mCaptureCurve.addCurve( c, !mStartNewCurve );
823 mStartNewCurve = false;
824
825 const int countAfter = mCaptureCurve.vertexCount();
826 const int addedPoint = countAfter - countBefore;
827
828 updateExtraSnapLayer();
829
830 for ( int i = 0; i < addedPoint; ++i )
831 mSnappingMatches.append( QgsPointLocator::Match() );
832
833 resetRubberBand();
834
835 return 0;
836}
837
839{
840 mCaptureCurve.clear();
841 updateExtraSnapLayer();
842}
843
844QList<QgsPointLocator::Match> QgsMapToolCapture::snappingMatches() const
845{
846 return mSnappingMatches;
847}
848
849void QgsMapToolCapture::undo( bool isAutoRepeat )
850{
851 mTracingStartPoint = QgsPointXY();
852
853 if ( mTempRubberBand )
854 {
855 if ( size() <= 1 && mTempRubberBand->pointsCount() != 0 )
856 return;
857
858 if ( isAutoRepeat && mIgnoreSubsequentAutoRepeatUndo )
859 return;
860 mIgnoreSubsequentAutoRepeatUndo = false;
861
862 const QgsPoint lastPoint = mTempRubberBand->lastPoint();
863
864 if ( mTempRubberBand->stringType() == QgsWkbTypes::CircularString && mTempRubberBand->pointsCount() > 2 )
865 {
866 mTempRubberBand->removeLastPoint();
867 mTempRubberBand->movePoint( lastPoint );
868 return;
869 }
870
871 QgsVertexId vertexToRemove;
872 vertexToRemove.part = 0;
873 vertexToRemove.ring = 0;
874 vertexToRemove.vertex = size() - 1;
875 if ( mCaptureCurve.numPoints() == 2 && mCaptureCurve.nCurves() == 1 )
876 {
877 // store the first vertex to restore if after deleting the curve
878 // because when only two vertices, removing a point remove all the curve
879 const QgsPoint fp = mCaptureCurve.startPoint();
880 mCaptureCurve.deleteVertex( vertexToRemove );
881 mCaptureCurve.addVertex( fp );
882 }
883 else
884 {
885 const int curvesBefore = mCaptureCurve.nCurves();
886 const bool lastCurveIsLineString = qgsgeometry_cast< QgsLineString * >( mCaptureCurve.curveAt( curvesBefore - 1 ) );
887
888 const int pointsCountBefore = mCaptureCurve.numPoints();
889 mCaptureCurve.deleteVertex( vertexToRemove );
890 int pointsCountAfter = mCaptureCurve.numPoints();
891 for ( ; pointsCountAfter < pointsCountBefore; pointsCountAfter++ )
892 if ( !mSnappingMatches.empty() )
893 mSnappingMatches.removeLast();
894
895 // if we have removed the last point in a linestring curve, then we "stick" here and ignore subsequent
896 // autorepeat undo actions until the user releases the undo key and holds it down again. This allows
897 // users to selectively remove portions of the geometry captured with the streaming mode by holding down
898 // the undo key, without risking accidental undo of non-streamed portions.
899 if ( mCaptureCurve.nCurves() < curvesBefore && lastCurveIsLineString )
900 mIgnoreSubsequentAutoRepeatUndo = true;
901 }
902
903 updateExtraSnapLayer();
904
905 resetRubberBand();
906
907 mTempRubberBand->reset( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry, mLineDigitizingType, mCaptureFirstPoint );
908
909 if ( mCaptureCurve.numPoints() > 0 )
910 {
911 const QgsPoint lastPt = mCaptureCurve.endPoint();
912 mCaptureLastPoint = toMapCoordinates( layer(), lastPt );
913 mTempRubberBand->addPoint( mCaptureLastPoint );
914 mTempRubberBand->movePoint( lastPoint );
915 }
916
918 validateGeometry();
919 }
920}
921
923{
924 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape && mCurrentShapeMapTool )
925 {
926 mCurrentShapeMapTool->keyPressEvent( e );
927 if ( e->isAccepted() )
928 return;
929 }
930
931 // this is backwards, but we can't change now without breaking api because
932 // forever QgsMapTools have had to explicitly mark events as ignored in order to
933 // indicate that they've consumed the event and that the default behavior should not
934 // be applied..!
935 // see QgsMapCanvas::keyPressEvent
936 e->accept();
937
938 if ( e->key() == Qt::Key_Backspace || e->key() == Qt::Key_Delete )
939 {
940 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape && mCurrentShapeMapTool )
941 {
942 if ( !e->isAutoRepeat() )
943 {
944 mCurrentShapeMapTool->undo();
945 }
946 }
947 else
948 {
949 undo( e->isAutoRepeat() );
950 }
951
952 // Override default shortcut management in MapCanvas
953 e->ignore();
954 }
955 else if ( e->key() == Qt::Key_Escape )
956 {
957 if ( mCurrentShapeMapTool )
958 mCurrentShapeMapTool->clean();
959
961
962 // Override default shortcut management in MapCanvas
963 e->ignore();
964 }
965}
966
968{
969 mCapturing = true;
970}
971
973{
974 return mCapturing;
975}
976
978{
979 mRubberBand.reset();
980
982
983 qDeleteAll( mGeomErrorMarkers );
984 mGeomErrorMarkers.clear();
985 mGeomErrors.clear();
986
987 mCaptureFirstPoint = QgsPoint();
988 mCaptureLastPoint = QgsPoint();
989
990 mTracingStartPoint = QgsPointXY();
991
992 mCapturing = false;
993 mCaptureCurve.clear();
994 updateExtraSnapLayer();
995 mSnappingMatches.clear();
996 if ( auto *lCurrentVectorLayer = currentVectorLayer() )
997 lCurrentVectorLayer->triggerRepaint();
998}
999
1001{
1002 mTempRubberBand.reset();
1003}
1004
1006{
1007 stopCapturing();
1008 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape && mCurrentShapeMapTool )
1009 mCurrentShapeMapTool->clean();
1010
1011 clearCurve();
1012}
1013
1015{
1016 mCaptureCurve.close();
1017 updateExtraSnapLayer();
1018}
1019
1020void QgsMapToolCapture::validateGeometry()
1021{
1024 )
1025 return;
1026
1027 if ( mValidator )
1028 {
1029 mValidator->deleteLater();
1030 mValidator = nullptr;
1031 }
1032
1033 mGeomErrors.clear();
1034 while ( !mGeomErrorMarkers.isEmpty() )
1035 {
1036 delete mGeomErrorMarkers.takeFirst();
1037 }
1038
1039 QgsGeometry geom;
1040
1041 switch ( mCaptureMode )
1042 {
1043 case CaptureNone:
1044 case CapturePoint:
1045 return;
1046 case CaptureLine:
1047 if ( size() < 2 )
1048 return;
1049 geom = QgsGeometry( mCaptureCurve.curveToLine() );
1050 break;
1051 case CapturePolygon:
1052 if ( size() < 3 )
1053 return;
1054 QgsLineString *exteriorRing = mCaptureCurve.curveToLine();
1055 exteriorRing->close();
1056 QgsPolygon *polygon = new QgsPolygon();
1057 polygon->setExteriorRing( exteriorRing );
1058 geom = QgsGeometry( polygon );
1059 break;
1060 }
1061
1062 if ( geom.isNull() )
1063 return;
1064
1065 Qgis::GeometryValidationEngine method = Qgis::GeometryValidationEngine::QgisInternal;
1067 method = Qgis::GeometryValidationEngine::Geos;
1068 mValidator = new QgsGeometryValidator( geom, nullptr, method );
1069 connect( mValidator, &QgsGeometryValidator::errorFound, this, &QgsMapToolCapture::addError );
1070 mValidator->start();
1071 QgsDebugMsgLevel( QStringLiteral( "Validation started" ), 4 );
1072}
1073
1074void QgsMapToolCapture::addError( const QgsGeometry::Error &e )
1075{
1076 mGeomErrors << e;
1077 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() );
1078 if ( !vlayer )
1079 return;
1080
1081 if ( e.hasWhere() )
1082 {
1084 vm->setCenter( mCanvas->mapSettings().layerToMapCoordinates( vlayer, e.where() ) );
1086 vm->setPenWidth( 2 );
1087 vm->setToolTip( e.what() );
1088 vm->setColor( Qt::green );
1089 vm->setZValue( vm->zValue() + 1 );
1090 mGeomErrorMarkers << vm;
1091 }
1092}
1093
1095{
1096 return mCaptureCurve.numPoints();
1097}
1098
1099QVector<QgsPointXY> QgsMapToolCapture::points() const
1100{
1101 QVector<QgsPointXY> pointsXY;
1103
1104 return pointsXY;
1105}
1106
1108{
1109 QgsPointSequence pts;
1110 mCaptureCurve.points( pts );
1111 return pts;
1112}
1113
1114void QgsMapToolCapture::setPoints( const QVector<QgsPointXY> &pointList )
1115{
1116 QgsLineString *line = new QgsLineString( pointList );
1117 mCaptureCurve.clear();
1118 mCaptureCurve.addCurve( line );
1119 updateExtraSnapLayer();
1120 mSnappingMatches.clear();
1121 for ( int i = 0; i < line->length(); ++i )
1122 mSnappingMatches.append( QgsPointLocator::Match() );
1123 resetRubberBand();
1124}
1125
1127{
1128 QgsLineString *line = new QgsLineString( pointList );
1129 mCaptureCurve.clear();
1130 mCaptureCurve.addCurve( line );
1131 updateExtraSnapLayer();
1132 mSnappingMatches.clear();
1133 for ( int i = 0; i < line->length(); ++i )
1134 mSnappingMatches.append( QgsPointLocator::Match() );
1135 resetRubberBand();
1136}
1137
1139{
1140 QgsPoint newPoint( QgsWkbTypes::Point, point.x(), point.y() );
1141
1142 // get current layer
1143 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() );
1144 if ( !vlayer )
1145 {
1146 return newPoint;
1147 }
1148
1149 // convert to the corresponding type for a full ZM support
1150 const QgsWkbTypes::Type type = vlayer->wkbType();
1151 if ( QgsWkbTypes::hasZ( type ) && !QgsWkbTypes::hasM( type ) )
1152 {
1153 newPoint.convertTo( QgsWkbTypes::PointZ );
1154 }
1155 else if ( !QgsWkbTypes::hasZ( type ) && QgsWkbTypes::hasM( type ) )
1156 {
1157 newPoint.convertTo( QgsWkbTypes::PointM );
1158 }
1159 else if ( QgsWkbTypes::hasZ( type ) && QgsWkbTypes::hasM( type ) )
1160 {
1161 newPoint.convertTo( QgsWkbTypes::PointZM );
1162 }
1163
1164 // set z value if necessary
1165 if ( QgsWkbTypes::hasZ( newPoint.wkbType() ) )
1166 {
1168 }
1169 // set m value if necessary
1170 if ( QgsWkbTypes::hasM( newPoint.wkbType() ) )
1171 {
1173 }
1174 return newPoint;
1175}
1176
1178{
1179 QgsPoint newPoint = mapPoint( e.mapPoint() );
1180
1181 // set z or m value from snapped point if necessary
1182 if ( QgsWkbTypes::hasZ( newPoint.wkbType() ) || QgsWkbTypes::hasM( newPoint.wkbType() ) )
1183 {
1184 // if snapped, z and m dimension are taken from the corresponding snapped
1185 // point.
1186 if ( e.isSnapped() )
1187 {
1188 const QgsPointLocator::Match match = e.mapPointMatch();
1189
1190 if ( match.layer() )
1191 {
1192 const QgsFeature ft = match.layer()->getFeature( match.featureId() );
1193 if ( QgsWkbTypes::hasZ( match.layer()->wkbType() ) )
1194 {
1195 newPoint.setZ( ft.geometry().vertexAt( match.vertexIndex() ).z() );
1196 }
1197 if ( QgsWkbTypes::hasM( match.layer()->wkbType() ) )
1198 {
1199 newPoint.setM( ft.geometry().vertexAt( match.vertexIndex() ).m() );
1200 }
1201 }
1202 }
1203 }
1204
1205 return newPoint;
1206}
1207
1208void QgsMapToolCapture::updateExtraSnapLayer()
1209{
1210 if ( !mExtraSnapLayer )
1211 return;
1212
1213 if ( canvas()->snappingUtils()->config().selfSnapping() && layer() && mCaptureCurve.numPoints() >= 2 )
1214 {
1215 // the current layer may have changed
1216 mExtraSnapLayer->setCrs( layer()->crs() );
1217 QgsGeometry geom = QgsGeometry( mCaptureCurve.clone() );
1218 // we close the curve to allow snapping on last segment
1219 if ( mCaptureMode == CapturePolygon && mCaptureCurve.numPoints() >= 3 )
1220 {
1221 qgsgeometry_cast<QgsCompoundCurve *>( geom.get() )->close();
1222 }
1223 mExtraSnapLayer->changeGeometry( mExtraSnapFeatureId, geom );
1224 }
1225 else
1226 {
1227 QgsGeometry geom;
1228 mExtraSnapLayer->changeGeometry( mExtraSnapFeatureId, geom );
1229 }
1230}
1231
1232
1234{
1235 // POINT CAPTURING
1236 if ( mode() == CapturePoint )
1237 {
1238 if ( e->button() != Qt::LeftButton )
1239 return;
1240
1241 QgsPoint savePoint; //point in layer coordinates
1242 bool isMatchPointZ = false;
1243 bool isMatchPointM = false;
1244 try
1245 {
1246 QgsPoint fetchPoint;
1247 int res = fetchLayerPoint( e->mapPointMatch(), fetchPoint );
1248 isMatchPointZ = QgsWkbTypes::hasZ( fetchPoint.wkbType() );
1249 isMatchPointM = QgsWkbTypes::hasM( fetchPoint.wkbType() );
1250
1251 if ( res == 0 )
1252 {
1253 QgsWkbTypes::Type geomType = QgsWkbTypes::Type::Point;
1254 if ( isMatchPointM && isMatchPointZ )
1255 {
1256 geomType = QgsWkbTypes::Type::PointZM;
1257 }
1258 else if ( isMatchPointM )
1259 {
1260 geomType = QgsWkbTypes::Type::PointM;
1261 }
1262 else if ( isMatchPointZ )
1263 {
1264 geomType = QgsWkbTypes::Type::PointZ;
1265 }
1266 savePoint = QgsPoint( geomType, fetchPoint.x(), fetchPoint.y(), fetchPoint.z(), fetchPoint.m() );
1267 }
1268 else
1269 {
1270 QgsPointXY point = mCanvas->mapSettings().mapToLayerCoordinates( layer(), e->mapPoint() );
1271
1272 savePoint = QgsPoint( point.x(), point.y(), fetchPoint.z(), fetchPoint.m() );
1273 }
1274 }
1275 catch ( QgsCsException &cse )
1276 {
1277 Q_UNUSED( cse )
1278 emit messageEmitted( tr( "Cannot transform the point to the layer's coordinate system" ), Qgis::MessageLevel::Warning );
1279 return;
1280 }
1281
1282 QgsGeometry g( std::make_unique<QgsPoint>( savePoint ) );
1283
1284 // The snapping result needs to be added so it's available in the @snapping_results variable of default value etc. expression contexts
1285 addVertex( e->mapPoint(), e->mapPointMatch() );
1286
1287 geometryCaptured( g );
1288 pointCaptured( savePoint );
1289
1290 stopCapturing();
1291
1292 // we are done with digitizing for now so instruct advanced digitizing dock to reset its CAD points
1294 }
1295
1296 // LINE AND POLYGON CAPTURING
1297 else if ( mode() == CaptureLine || mode() == CapturePolygon )
1298 {
1299 bool digitizingFinished = false;
1300
1301 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape )
1302 {
1303 if ( !mCurrentShapeMapTool )
1304 {
1305 emit messageEmitted( tr( "Select an option from the Shape Digitizing Toolbar in order to capture shapes" ), Qgis::MessageLevel::Warning );
1306 return;
1307 }
1308 else
1309 {
1310 if ( !mTempRubberBand )
1311 {
1312 mTempRubberBand.reset( createCurveRubberBand() );
1313 mTempRubberBand->setStringType( mLineDigitizingType );
1314 mTempRubberBand->setRubberBandGeometryType( mCaptureMode == CapturePolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry );
1315 }
1316
1317 digitizingFinished = mCurrentShapeMapTool->cadCanvasReleaseEvent( e, mCaptureMode );
1318 if ( digitizingFinished )
1319 mCurrentShapeMapTool->clean();
1320 }
1321 }
1322 else // i.e. not shape
1323 {
1324 //add point to list and to rubber band
1325 if ( e->button() == Qt::LeftButton )
1326 {
1327 const int error = addVertex( e->mapPoint(), e->mapPointMatch() );
1328 if ( error == 2 )
1329 {
1330 //problem with coordinate transformation
1331 emit messageEmitted( tr( "Cannot transform the point to the layers coordinate system" ), Qgis::MessageLevel::Warning );
1332 return;
1333 }
1334
1336 }
1337 else if ( e->button() == Qt::RightButton )
1338 {
1339 // End of string
1341
1342 //lines: bail out if there are not at least two vertices
1343 if ( mode() == CaptureLine && size() < 2 )
1344 {
1345 stopCapturing();
1346 return;
1347 }
1348
1349 //polygons: bail out if there are not at least two vertices
1350 if ( mode() == CapturePolygon && size() < 3 )
1351 {
1352 stopCapturing();
1353 return;
1354 }
1355
1356 if ( mode() == CapturePolygon || e->modifiers() == Qt::ShiftModifier )
1357 {
1358 closePolygon();
1359 }
1360
1361 digitizingFinished = true;
1362 }
1363 }
1364
1365 if ( digitizingFinished )
1366 {
1367 QgsGeometry g;
1368 QgsCurve *curveToAdd = captureCurve()->clone();
1369
1370 if ( mode() == CaptureLine )
1371 {
1372 g = QgsGeometry( curveToAdd );
1373 geometryCaptured( g );
1374 lineCaptured( curveToAdd );
1375 }
1376 else
1377 {
1378 QgsCurvePolygon *poly = new QgsCurvePolygon();
1379 poly->setExteriorRing( curveToAdd );
1380 g = QgsGeometry( poly );
1381 geometryCaptured( g );
1382 polygonCaptured( poly );
1383 }
1384
1385 stopCapturing();
1386 }
1387 }
1388}
void setParentOwner(QObject *parent)
Sets the parent object.
void reset(T *p=nullptr)
Will reset the managed pointer to p.
void reset(T *p=nullptr)
Will reset the managed pointer to p.
T * release()
Clears the pointer and returns it.
CaptureTechnique
Capture technique.
Definition: qgis.h:153
@ Shape
Digitize shapes.
@ StraightSegments
Default capture mode - capture occurs with straight line segments.
@ CircularString
Capture in circular strings.
@ Streaming
Streaming points digitizing mode (points are automatically added as the mouse cursor moves).
GeometryValidationEngine
Available engines for validating geometries.
Definition: qgis.h:996
bool is3D() const SIP_HOLDGIL
Returns true if the geometry is 3D and contains a z-value.
virtual QgsPoint vertexAt(QgsVertexId id) const =0
Returns the point corresponding to a specified vertex id.
QgsWkbTypes::Type wkbType() const SIP_HOLDGIL
Returns the WKB type of the geometry.
bool isMeasure() const SIP_HOLDGIL
Returns true if the geometry contains m values.
The QgsAdvancedDigitizingDockWidget class is a dockable widget used to handle the CAD tools on top of...
bool cadEnabled() const
determines if CAD tools are enabled or if map tools behaves "nomally"
void switchZM()
Determines if Z or M will be enabled.
double getLineM() const
Convenient method to get the M value from the line edit wiget.
void clearPoints()
Removes all points from the CAD point list.
void removePreviousPoint()
Remove previous point in the CAD point list.
double getLineZ() const
Convenient method to get the Z value from the line edit wiget.
QgsPoint currentPointV2(bool *exists=nullptr) const
The last point.
QgsPoint currentPointLayerCoordinates(QgsMapLayer *layer) const
Returns the last CAD point, in a map layer's coordinates.
static QCursor getThemeCursor(Cursor cursor)
Helper to get a theme cursor.
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.
void close()
Appends first point if not already closed.
QgsPoint endPoint() const override SIP_HOLDGIL
Returns the end point of the curve.
const QgsCurve * curveAt(int i) const SIP_HOLDGIL
Returns the curve at the specified index.
int numPoints() const override SIP_HOLDGIL
Returns the number of points in the curve.
void removeCurve(int i)
Removes a curve from the geometry.
void addCurve(QgsCurve *c, bool extendPrevious=false)
Adds a curve to the geometry (takes ownership).
bool moveVertex(QgsVertexId position, const QgsPoint &newPos) override
Moves a vertex within the geometry.
bool deleteVertex(QgsVertexId position) override
Deletes a vertex within the geometry.
QgsCompoundCurve * clone() const override
Clones the geometry by performing a deep copy.
void points(QgsPointSequence &pts) const override
Returns a list of points within the curve.
void clear() override
Clears the geometry, ie reset it to a null geometry.
QgsPoint startPoint() const override SIP_HOLDGIL
Returns the starting point of the curve.
bool isEmpty() const override SIP_HOLDGIL
Returns true if the geometry is empty.
void addVertex(const QgsPoint &pt)
Adds a vertex to the end of the geometry.
int nCurves() const SIP_HOLDGIL
Returns the number of curves in the geometry.
Class for doing transforms between two map coordinate systems.
bool isValid() const
Returns true if the coordinate transform is valid, ie both the source and destination CRS have been s...
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:66
Curve polygon geometry type.
virtual void setExteriorRing(QgsCurve *ring)
Sets the exterior ring of the polygon.
Abstract base class for curved geometry type.
Definition: qgscurve.h:36
QgsCurve * segmentize(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const override
Returns a geometry without curves.
Definition: qgscurve.cpp:175
int vertexCount(int part=0, int ring=0) const override
Returns the number of vertices of which this geometry is built.
Definition: qgscurve.cpp:180
virtual QgsPoint endPoint() const =0
Returns the end point of the curve.
bool nextFeature(QgsFeature &f)
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFilterFid(QgsFeatureId fid)
Sets the feature ID that should be fetched.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
QgsGeometry geometry
Definition: qgsfeature.h:67
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
void errorFound(const QgsGeometry::Error &error)
Sent when an error has been found during the validation process.
A geometry error.
Definition: qgsgeometry.h:2476
bool hasWhere() const
true if the location available from
QgsPointXY where() const
The coordinates at which the error is located and should be visualized.
QString what() const
A human readable error message containing details about the error.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:164
const QgsAbstractGeometry * constGet() const SIP_HOLDGIL
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
QgsWkbTypes::Type wkbType() const SIP_HOLDGIL
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
bool vertexIdFromVertexNr(int number, QgsVertexId &id) const
Calculates the vertex ID from a vertex number.
QgsPoint vertexAt(int atVertex) const
Returns coordinates of a vertex.
Q_GADGET bool isNull
Definition: qgsgeometry.h:166
QgsAbstractGeometry * get()
Returns a modifiable (non-const) reference to the underlying abstract geometry primitive.
QgsGeometry convertToCurves(double distanceTolerance=1e-8, double angleTolerance=1e-8) const
Attempts to convert a non-curved geometry into a curved geometry type (e.g.
static void convertPointList(const QVector< QgsPointXY > &input, QgsPointSequence &output)
Upgrades a point list from QgsPointXY to QgsPoint.
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:45
double length() const override SIP_HOLDGIL
Returns the planar, 2-dimensional length of the geometry.
void close()
Closes the line string by appending the first point to the end of the line, if it is not already clos...
Extension of QgsTracer that provides extra functionality:
QAction * actionEnableSnapping() const
Access to action that user may use to toggle snapping on/off.
void reportError(PathError err, bool addingVertex)
Report a path finding error to the user.
QAction * actionEnableTracing() const
Access to action that user may use to toggle tracing on/off. May be nullptr if no action was associat...
static QgsMapCanvasTracer * tracerForCanvas(QgsMapCanvas *canvas)
Retrieve instance of this class associated with given canvas (if any).
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:90
void currentLayerChanged(QgsMapLayer *layer)
Emitted when the current layer is changed.
QgsMapLayer * currentLayer()
returns current layer (set by legend widget)
Base class for all map layer types.
Definition: qgsmaplayer.h:73
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:79
void setCrs(const QgsCoordinateReferenceSystem &srs, bool emitSignal=true)
Sets layer's spatial reference system.
A QgsMapMouseEvent is the result of a user interaction with the mouse on a QgsMapCanvas.
bool isSnapped() const
Returns true if there is a snapped point cached.
QgsPointXY mapPoint() const
mapPoint returns the point in coordinates
QgsPointLocator::Match mapPointMatch() const
Returns the matching data from the most recently snapped point.
The QgsMapToolAdvancedDigitizing class is a QgsMapTool which gives event directly in map coordinates ...
virtual void cadCanvasMoveEvent(QgsMapMouseEvent *e)
Override this method when subclassing this class.
QgsAdvancedDigitizingDockWidget * mCadDockWidget
void deactivate() override
Unregisters this maptool from the cad dock widget.
virtual QgsMapLayer * layer() const
Returns the layer associated with the map tool.
QgsAdvancedDigitizingDockWidget * cadDockWidget() const
void activate() override
Registers this maptool with the cad dock widget.
void deactivate() override
Unregisters this maptool from the cad dock widget.
void stopCapturing()
Stop capturing.
int size()
Number of points digitized.
CaptureMode mode() const
The capture mode.
QgsMapToolCapture(QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDockWidget, CaptureMode mode)
constructor
void undo(bool isAutoRepeat=false)
Removes the last vertex from mRubberBand and mCaptureList.
QgsPoint mapPoint(const QgsMapMouseEvent &e) const
Creates a QgsPoint with ZM support if necessary (according to the WkbType of the current layer).
void keyPressEvent(QKeyEvent *e) override
Intercept key events like Esc or Del to delete the last point.
void activate() override
Registers this maptool with the cad dock widget.
CaptureMode
Different capture modes.
@ CapturePolygon
Capture polygons.
@ CaptureNone
Do not capture / determine mode from layer geometry type.
@ CapturePoint
Capture points.
@ CaptureLine
Capture lines.
Q_DECL_DEPRECATED void setCircularDigitizingEnabled(bool enable)
Enable the digitizing with curve.
void deleteTempRubberBand()
Clean a temporary rubberband.
void clean() override
convenient method to clean members
~QgsMapToolCapture() override
void closePolygon()
Close an open polygon.
int addCurve(QgsCurve *c)
Adds a whole curve (e.g. circularstring) to the captured geometry. Curve must be in map CRS.
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.
QgsPointSequence pointsZM() const
List of digitized points.
Q_DECL_DEPRECATED void setPoints(const QVector< QgsPointXY > &pointList)
Set the points on which to work.
const QgsCompoundCurve * captureCurve() const
Gets the capture curve.
QList< QgsPointLocator::Match > snappingMatches() const
Returns a list of matches for each point on the captureCurve.
Q_DECL_DEPRECATED QVector< QgsPointXY > points() const
List of digitized points.
bool isCapturing() const
Are we currently capturing?
virtual bool supportsTechnique(Qgis::CaptureTechnique technique) const
Returns true if the tool supports the specified capture technique.
void setCurrentShapeMapTool(const QgsMapToolShapeMetadata *shapeMapToolMetadata)
Sets the current shape tool.
int addVertex(const QgsPointXY &point)
Adds a point to the rubber band (in map coordinates) and to the capture list (in layer coordinates)
@ ValidateGeometries
Tool supports geometry validation (since QGIS 3.22)
void setCurrentCaptureTechnique(Qgis::CaptureTechnique technique)
Sets the current capture if it is supported by the map tool.
virtual QgsMapToolCapture::Capabilities capabilities() const
Returns flags containing the supported capabilities.
void clearCurve()
Clear capture curve.
int nextPoint(const QgsPoint &mapPoint, QgsPoint &layerPoint)
Converts a map point to layer coordinates.
Q_DECL_DEPRECATED void setStreamDigitizingEnabled(bool enable)
Toggles the stream digitizing mode.
void cadCanvasMoveEvent(QgsMapMouseEvent *e) override
Override this method when subclassing this class.
void startCapturing()
Start capturing.
QgsRubberBand * takeRubberBand()
Returns the rubberBand currently owned by this map tool and transfers ownership to the caller.
void cadCanvasReleaseEvent(QgsMapMouseEvent *e) override
Override this method when subclassing this class.
static double defaultMValue()
Returns default M value.
QgsVectorLayer * currentVectorLayer()
Returns the current vector layer of the map canvas or 0.
static QColor digitizingFillColor()
Returns fill color for rubber bands (from global settings)
static double defaultZValue()
Returns default Z value.
QgsRubberBand * createRubberBand(QgsWkbTypes::GeometryType geometryType=QgsWkbTypes::LineGeometry, bool alternativeBand=false)
Creates a rubber band with the color/line width from the QGIS settings.
static QColor digitizingStrokeColor()
Returns stroke color for rubber bands (from global settings)
static int digitizingStrokeWidth()
Returns stroke width for rubber bands (from global settings)
virtual void deactivate()
Deactivates the map tool.
virtual void cadCanvasMoveEvent(QgsMapMouseEvent *e, QgsMapToolCapture::CaptureMode mode)=0
Called for a mouse move event.
QString id() const
Returns the id of the shape tool (equivalent to the one from the metadata)
virtual void undo()
Called to undo last action (last point added)
virtual void activate(QgsMapToolCapture::CaptureMode mode, const QgsPoint &lastCapturedMapPoint)
Activates the map tool with the last captured map point.
virtual void clean()
Called to clean the map tool (after canceling the operation or when the digitization has finished)
virtual bool cadCanvasReleaseEvent(QgsMapMouseEvent *e, QgsMapToolCapture::CaptureMode mode)=0
Called for a mouse release event Must return true if the digitization has ended and the geometry is c...
virtual void keyPressEvent(QKeyEvent *e)
Filters a key press event Ignores the event in default implementation.
QgsMapToolShapeMetadata is a base class for shape map tools metadata to be used in QgsMapToolShapeReg...
virtual QgsMapToolShapeAbstract * factory(QgsMapToolCapture *parentlTool) const SIP_FACTORY=0
Creates the shape map tool for the given parentTool Caller takes ownership of the returned object.
virtual QString id() const =0
Unique ID for the shape map tool.
QgsPoint toLayerCoordinates(const QgsMapLayer *layer, const QgsPoint &point)
Transforms a point from map coordinates to layer coordinates.
Definition: qgsmaptool.cpp:62
QPointer< QgsMapCanvas > mCanvas
The pointer to the map canvas.
Definition: qgsmaptool.h:338
QgsMapCanvas * canvas() const
returns pointer to the tool's map canvas
Definition: qgsmaptool.cpp:215
QgsPointXY toMapCoordinates(QPoint point)
Transforms a point from screen coordinates to map coordinates.
Definition: qgsmaptool.cpp:41
virtual void setCursor(const QCursor &cursor)
Sets a user defined cursor.
Definition: qgsmaptool.cpp:160
void messageEmitted(const QString &message, Qgis::MessageLevel=Qgis::MessageLevel::Info)
emit a message
QPoint toCanvasCoordinates(const QgsPointXY &point) const
Transforms a point from map coordinates to screen coordinates.
Definition: qgsmaptool.cpp:77
bool isActive() const
Returns if the current map tool active on the map canvas.
Definition: qgsmaptool.cpp:145
A class to represent a 2D point.
Definition: qgspointxy.h:59
double y
Definition: qgspointxy.h:63
Q_GADGET double x
Definition: qgspointxy.h:62
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
bool addMValue(double mValue=0) override
Adds a measure to the geometry, initialized to a preset value.
Definition: qgspoint.cpp:562
bool dropMValue() override
Drops any measure values which exist in the geometry.
Definition: qgspoint.cpp:603
bool isEmpty() const override SIP_HOLDGIL
Returns true if the geometry is empty.
Definition: qgspoint.cpp:767
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
Definition: qgspoint.cpp:551
Q_GADGET double x
Definition: qgspoint.h:52
bool convertTo(QgsWkbTypes::Type type) override
Converts the geometry to a specified type.
Definition: qgspoint.cpp:620
void setZ(double z) SIP_HOLDGIL
Sets the point's z-coordinate.
Definition: qgspoint.h:304
double z
Definition: qgspoint.h:54
bool dropZValue() override
Drops any z-dimensions which exist in the geometry.
Definition: qgspoint.cpp:592
double m
Definition: qgspoint.h:55
double y
Definition: qgspoint.h:53
void setM(double m) SIP_HOLDGIL
Sets the point's m-value.
Definition: qgspoint.h:319
Polygon geometry type.
Definition: qgspolygon.h:34
void setExteriorRing(QgsCurve *ring) override
Sets the exterior ring of the polygon.
Definition: qgspolygon.cpp:219
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:477
void snappingConfigChanged(const QgsSnappingConfig &config)
Emitted whenever the configuration for snapping has changed.
A class for drawing transient features (e.g.
Definition: qgsrubberband.h:52
void movePoint(const QgsPointXY &p, int geometryIndex=0, int ringIndex=0)
Moves the rubber band point specified by index.
const QgsPointXY * getPoint(int i, int j=0, int ringIndex=0) const
Returns a vertex.
void reset(QgsWkbTypes::GeometryType geometryType=QgsWkbTypes::LineGeometry)
Clears all the geometries in this rubberband.
int numberOfVertices() const
Returns count of vertices in all lists of mPoint.
void addGeometry(const QgsGeometry &geometry, QgsMapLayer *layer, bool doUpdate=true)
Adds the geometry of an existing feature to a rubberband This is useful for multi feature highlightin...
T value(const QString &dynamicKeyPart=QString()) const
Returns settings value.
static const QgsSettingsEntryBool settingsDigitizingConvertToCurve
Settings entry digitizing convert to curve.
static const QgsSettingsEntryInteger settingsDigitizingStreamTolerance
Settings entry digitizing stream tolerance.
static const QgsSettingsEntryDouble settingsDigitizingConvertToCurveDistanceTolerance
Settings entry digitizing convert to curve distance tolerance.
static const QgsSettingsEntryInteger settingsDigitizingValidateGeometries
Settings entry digitizing validate geometries.
static const QgsSettingsEntryDouble settingsDigitizingConvertToCurveAngleTolerance
Settings entry digitizing convert to curve angle tolerance.
static const QgsSettingsEntryDouble settingsDigitizingLineColorAlphaScale
Settings entry digitizing line color alpha scale.
Class that shows snapping marker on map canvas for the current snapping match.
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:803
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:736
PathError
Possible errors that may happen when calling findShortestPath()
Definition: qgstracer.h:138
@ ErrNone
No error.
Definition: qgstracer.h:139
@ ErrTooManyFeatures
Max feature count threshold was reached while reading features.
Definition: qgstracer.h:140
bool init()
Build the internal data structures.
Definition: qgstracer.cpp:675
virtual Q_INVOKABLE QgsVectorDataProvider::Capabilities capabilities() const
Returns flags containing the supported capabilities.
Represents a vector layer which manages a vector based data sets.
Q_INVOKABLE QgsWkbTypes::Type wkbType() const FINAL
Returns the WKBType or WKBUnknown in case of error.
Q_INVOKABLE QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
Q_INVOKABLE bool startEditing()
Makes the layer editable.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
QgsFeature getFeature(QgsFeatureId fid) const
Queries the layer for the feature with the given id.
bool addFeature(QgsFeature &feature, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) FINAL
Adds a single feature to the sink.
bool changeGeometry(QgsFeatureId fid, QgsGeometry &geometry, bool skipDefaultValue=false)
Changes a feature's geometry within the layer's edit buffer (but does not immediately commit the chan...
A class for marking vertices of features using e.g.
void setPenWidth(int width)
void setCenter(const QgsPointXY &point)
Sets the center point of the marker, in map coordinates.
void setIconType(int iconType)
void setColor(const QColor &color)
Sets the stroke color for the marker.
static bool hasM(Type type) SIP_HOLDGIL
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:1130
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:70
static Type flatType(Type type) SIP_HOLDGIL
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:732
static bool hasZ(Type type) SIP_HOLDGIL
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:1080
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
#define BUILTIN_UNREACHABLE
Definition: qgis.h:3148
QVector< QgsPoint > QgsPointSequence
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
const QgsCoordinateReferenceSystem & crs
QgsFeatureId featureId() const
The id of the feature to which the snapped geometry belongs.
QgsVectorLayer * layer() const
The vector layer where the snap occurred.
QgsPoint interpolatedPoint(const QgsCoordinateReferenceSystem &destinationCrs=QgsCoordinateReferenceSystem()) const
Convenient method to return a point on an edge with linear interpolation of the Z value.
bool hasEdge() const
Returns true if the Match is an edge.
bool hasLineEndpoint() const
Returns true if the Match is a line endpoint (start or end vertex).
bool hasMiddleSegment() const
Returns true if the Match is the middle of a segment.
int vertexIndex() const
for vertex / edge match (first vertex of the edge)
bool hasVertex() const
Returns true if the Match is a vertex.
Setting options for loading vector layers.
bool skipCrsValidation
Controls whether the layer is allowed to have an invalid/unknown CRS.
bool loadDefaultStyle
Set to true if the default layer style should be loaded.
Utility class for identifying a unique vertex within a geometry.
Definition: qgsvertexid.h:31
int vertex
Vertex number.
Definition: qgsvertexid.h:95
int part
Part number.
Definition: qgsvertexid.h:89
int ring
Ring number.
Definition: qgsvertexid.h:92