QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
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#include <algorithm>
44#include <memory>
45
46
48 : QgsMapToolAdvancedDigitizing( canvas, cadDockWidget )
49 , mCaptureMode( mode )
50 , mCaptureModeFromLayer( mode == CaptureNone )
51{
52 mTempRubberBand.setParentOwner( canvas );
53
54 mSnapIndicator.reset( new QgsSnapIndicator( canvas ) );
55
56 setCursor( QgsApplication::getThemeCursor( QgsApplication::Cursor::CapturePoint ) );
57
59 this, &QgsMapToolCapture::currentLayerChanged );
60
62 layerOptions.skipCrsValidation = true;
63 layerOptions.loadDefaultStyle = false;
64 mExtraSnapLayer = new QgsVectorLayer( QStringLiteral( "LineString?crs=" ), QStringLiteral( "extra snap" ), QStringLiteral( "memory" ), layerOptions );
65 mExtraSnapLayer->startEditing();
66 QgsFeature f;
67 mExtraSnapLayer->addFeature( f );
68 mExtraSnapFeatureId = f.id();
69
71 this, &QgsMapToolCapture::updateExtraSnapLayer );
72
73 currentLayerChanged( canvas->currentLayer() );
74}
75
77{
78 // during tear down we have to clean up mExtraSnapLayer first, before
79 // we call stop capturing. Otherwise stopCapturing tries to access members
80 // from the mapcanvas, which is likely already being destroyed and triggering
81 // the deletion of this object...
82 if ( mCanvas )
83 {
84 mCanvas->snappingUtils()->removeExtraSnapLayer( mExtraSnapLayer );
85 }
86 mExtraSnapLayer->deleteLater();
87 mExtraSnapLayer = nullptr;
88
90
91 if ( mValidator )
92 {
93 mValidator->deleteLater();
94 mValidator = nullptr;
95 }
96}
97
99{
101}
102
104{
105 switch ( technique )
106 {
108 return true;
112 return false;
113 }
115}
116
118{
119 if ( mTempRubberBand )
120 mTempRubberBand->show();
121
122 mCanvas->snappingUtils()->addExtraSnapLayer( mExtraSnapLayer );
124
125 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape && mCurrentShapeMapTool )
126 mCurrentShapeMapTool->activate( mCaptureMode, mCaptureLastPoint );
127}
128
130{
131 if ( mTempRubberBand )
132 mTempRubberBand->hide();
133
134 mSnapIndicator->setMatch( QgsPointLocator::Match() );
135
136 mCanvas->snappingUtils()->removeExtraSnapLayer( mExtraSnapLayer );
137
138 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape && mCurrentShapeMapTool )
139 mCurrentShapeMapTool->deactivate();
140
142}
143
144void QgsMapToolCapture::currentLayerChanged( QgsMapLayer *layer )
145{
146 if ( !mCaptureModeFromLayer )
147 return;
148
149 mCaptureMode = CaptureNone;
150
151 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
152 if ( !vlayer )
153 {
154 return;
155 }
156
157 switch ( vlayer->geometryType() )
158 {
160 mCaptureMode = CapturePoint;
161 break;
163 mCaptureMode = CaptureLine;
164 break;
166 mCaptureMode = CapturePolygon;
167 break;
168 default:
169 mCaptureMode = CaptureNone;
170 break;
171 }
172
173 if ( mTempRubberBand )
174 mTempRubberBand->setRubberBandGeometryType( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line );
175
176 resetRubberBand();
178}
179
180
181bool QgsMapToolCapture::tracingEnabled()
182{
184 return tracer && ( !tracer->actionEnableTracing() || tracer->actionEnableTracing()->isChecked() )
185 && ( !tracer->actionEnableSnapping() || tracer->actionEnableSnapping()->isChecked() );
186}
187
188
189QgsPointXY QgsMapToolCapture::tracingStartPoint()
190{
191 // if we have starting point from previous trace, then preferably use that one
192 // (useful when tracing with offset)
193 if ( mTracingStartPoint != QgsPointXY() )
194 return mTracingStartPoint;
195
196 return mCaptureLastPoint;
197}
198
199
200bool QgsMapToolCapture::tracingMouseMove( QgsMapMouseEvent *e )
201{
202 if ( !e->isSnapped() )
203 return false;
204
205 QgsPointXY pt0 = tracingStartPoint();
206 if ( pt0 == QgsPointXY() )
207 return false;
208
210 if ( !tracer )
211 return false; // this should not happen!
212
214 QVector<QgsPointXY> points = tracer->findShortestPath( pt0, e->mapPoint(), &err );
215 if ( points.isEmpty() )
216 {
217 tracer->reportError( err, false );
218 return false;
219 }
220
221 mTempRubberBand->reset( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line, Qgis::WkbType::LineString, mCaptureFirstPoint );
222 mTempRubberBand->addPoint( mCaptureLastPoint );
223
224 // if there is offset, we need to fix the rubber bands to make sure they are aligned correctly.
225 // There are two cases we need to sort out:
226 // 1. the last point of mRubberBand may need to be moved off the traced curve to respect the offset
227 // 2. first point of mTempRubberBand may be needed to be moved to the beginning of the offset trace
228 const QgsPoint lastPoint = mCaptureLastPoint;
229 QgsPointXY lastPointXY( lastPoint );
230 if ( lastPointXY == pt0 && points[0] != lastPointXY )
231 {
232 if ( mRubberBand->numberOfVertices() != 0 )
233 {
234 // if rubber band had just one point, for some strange reason it contains the point twice
235 // we only want to move the last point if there are multiple points already
236 if ( mRubberBand->numberOfVertices() > 2 || ( mRubberBand->numberOfVertices() == 2 && *mRubberBand->getPoint( 0, 0 ) != *mRubberBand->getPoint( 0, 1 ) ) )
237 mRubberBand->movePoint( points[0] );
238 }
239
240 mTempRubberBand->movePoint( 0, QgsPoint( points[0] ) );
241 }
242
243 mTempRubberBand->movePoint( QgsPoint( points[0] ) );
244
245 // update temporary rubberband
246 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
247 mTempRubberBand->addPoint( QgsPoint( points.at( i ) ), i == points.count() - 1 );
248
249
250 mTempRubberBand->addPoint( QgsPoint( points[points.size() - 1] ) );
251
252 tracer->reportError( QgsTracer::ErrNone, false ); // clear messagebar if there was any error
253 return true;
254}
255
256
257bool QgsMapToolCapture::tracingAddVertex( const QgsPointXY &point )
258{
260 if ( !tracer )
261 return false; // this should not happen!
262
263 if ( mTempRubberBand->pointsCount() == 0 )
264 {
265 if ( !tracer->init() )
266 {
268 return false;
269 }
270
271 // only accept first point if it is snapped to the graph (to vertex or edge)
272 const bool res = tracer->isPointSnapped( point );
273 if ( res )
274 {
275 mTracingStartPoint = point;
276 }
277 return false;
278 }
279
280 QgsPointXY pt0 = tracingStartPoint();
281 if ( pt0 == QgsPointXY() )
282 return false;
283
285 const QVector<QgsPointXY> tracedPointsInMapCrs = tracer->findShortestPath( pt0, point, &err );
286 if ( tracedPointsInMapCrs.isEmpty() )
287 return false; // ignore the vertex - can't find path to the end point!
288
289 // transform points
290 QgsPointSequence layerPoints;
291 layerPoints.reserve( tracedPointsInMapCrs.size() );
292 QgsPointSequence mapPoints;
293 mapPoints.reserve( tracedPointsInMapCrs.size() );
294 for ( const QgsPointXY &tracedPointMapCrs : tracedPointsInMapCrs )
295 {
296 QgsPoint mapPoint( tracedPointMapCrs );
297
298 QgsPoint lp; // in layer coords
299 if ( nextPoint( mapPoint, lp ) != 0 )
300 return false;
301
302 // copy z and m from layer point back to mapPoint, as nextPoint() call will populate these based
303 // on the context of the trace
304 if ( lp.is3D() )
305 mapPoint.addZValue( lp.z() );
306 if ( lp.isMeasure() )
307 mapPoint.addMValue( lp.m() );
308
309 mapPoints << mapPoint;
310 layerPoints << lp;
311 }
312
313 // Move the last point of the captured curve to the first point on the trace string (necessary if there is offset)
314 const QgsVertexId lastVertexId( 0, 0, mCaptureCurve.numPoints() - 1 );
315 mCaptureCurve.moveVertex( lastVertexId, layerPoints.first() );
316 mSnappingMatches.removeLast();
317 mSnappingMatches.append( QgsPointLocator::Match() );
318
319 int pointBefore = mCaptureCurve.numPoints();
320 addCurve( new QgsLineString( mapPoints ) );
321
322 resetRubberBand();
323
324 // Curves de-approximation
326 {
327 // If the tool and the layer support curves
328 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() );
329 if ( vlayer && capabilities().testFlag( QgsMapToolCapture::Capability::SupportsCurves ) && vlayer->dataProvider()->capabilities().testFlag( QgsVectorDataProvider::Capability::CircularGeometries ) )
330 {
331 const QgsGeometry linear = QgsGeometry( mCaptureCurve.segmentize() );
332 const QgsGeometry curved = linear.convertToCurves(
335 );
337 {
338 mCaptureCurve.clear();
339 mCaptureCurve.addCurve( qgsgeometry_cast< const QgsCurve * >( curved.constGet() )->clone() );
340 }
341 else
342 {
343 mCaptureCurve = *qgsgeometry_cast<QgsCompoundCurve *>( curved.constGet() );
344 }
345 }
346 }
347
348 // sync the snapping matches list
349 const int pointAfter = mCaptureCurve.numPoints();
350 for ( ; pointBefore < pointAfter; ++pointBefore )
351 mSnappingMatches.append( QgsPointLocator::Match() );
352
353 tracer->reportError( QgsTracer::ErrNone, true ); // clear messagebar if there was any error
354
355 // adjust last captured point
356 const QgsPoint lastPt = mCaptureCurve.endPoint();
357 mCaptureLastPoint = toMapCoordinates( layer(), lastPt );
358
359 return true;
360}
361
362QgsMapToolCaptureRubberBand *QgsMapToolCapture::createCurveRubberBand() const
363{
364 QgsMapToolCaptureRubberBand *rb = new QgsMapToolCaptureRubberBand( mCanvas );
365 rb->setStrokeWidth( digitizingStrokeWidth() );
366 QColor color = digitizingStrokeColor();
367
369 color.setAlphaF( color.alphaF() * alphaScale );
370 rb->setLineStyle( Qt::DotLine );
371 rb->setStrokeColor( color );
372
373 const QColor fillColor = digitizingFillColor();
374 rb->setFillColor( fillColor );
375 rb->show();
376 return rb;
377}
378
379void QgsMapToolCapture::resetRubberBand()
380{
381 if ( !mRubberBand )
382 return;
383 QgsLineString *lineString = mCaptureCurve.curveToLine();
384
386 mRubberBand->addGeometry( QgsGeometry( lineString ), layer() );
387}
388
390{
391 return mRubberBand.release();
392}
393
395{
396 if ( enable )
398 else
400}
401
403{
404 if ( enable )
406 else
408}
409
411{
412 if ( mCurrentCaptureTechnique == technique )
413 return;
414
415 mStartNewCurve = true;
416
417 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape && mCurrentShapeMapTool )
418 {
419 mCurrentShapeMapTool->deactivate();
420 clean();
421 }
422
423 switch ( technique )
424 {
426 mLineDigitizingType = Qgis::WkbType::LineString;
427 break;
429 mLineDigitizingType = Qgis::WkbType::CircularString;
430 break;
432 mLineDigitizingType = Qgis::WkbType::LineString;
434 break;
436 mLineDigitizingType = Qgis::WkbType::LineString;
437 break;
438
439 }
440
441 if ( mTempRubberBand )
442 mTempRubberBand->setStringType( mLineDigitizingType );
443
444 mCurrentCaptureTechnique = technique;
445
446 if ( technique == Qgis::CaptureTechnique::Shape && mCurrentShapeMapTool && isActive() )
447 {
448 clean();
449 mCurrentShapeMapTool->activate( mCaptureMode, mCaptureLastPoint );
450 }
451}
452
454{
455 if ( mCurrentShapeMapTool )
456 {
457 if ( shapeMapToolMetadata && mCurrentShapeMapTool->id() == shapeMapToolMetadata->id() )
458 return;
459 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape )
460 mCurrentShapeMapTool->deactivate();
461 mCurrentShapeMapTool->deleteLater();
462 }
463
464 mCurrentShapeMapTool.reset( shapeMapToolMetadata ? shapeMapToolMetadata->factory( this ) : nullptr );
465
466 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape && isActive() )
467 {
468 clean();
469 if ( mCurrentShapeMapTool )
470 mCurrentShapeMapTool->activate( mCaptureMode, mCaptureLastPoint );
471 }
472}
473
475{
477
478 const QgsPointXY point = e->mapPoint();
479
480 mSnapIndicator->setMatch( e->mapPointMatch() );
481
482 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape )
483 {
484 if ( !mCurrentShapeMapTool )
485 {
486 emit messageEmitted( tr( "Select an option from the Shape Digitizing Toolbar in order to capture shapes" ), Qgis::MessageLevel::Warning );
487 }
488 else
489 {
490 if ( !mTempRubberBand )
491 {
492 mTempRubberBand.reset( createCurveRubberBand() );
493 mTempRubberBand->setStringType( mLineDigitizingType );
494 mTempRubberBand->setRubberBandGeometryType( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line );
495 }
496
497 mCurrentShapeMapTool->cadCanvasMoveEvent( e, mCaptureMode );
498 return;
499 }
500 }
501 else
502 {
503 const QgsPoint mapPoint = QgsPoint( point );
504
505 if ( mCaptureMode != CapturePoint && mTempRubberBand && mCapturing )
506 {
507 bool hasTrace = false;
508
509 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Streaming )
510 {
511 if ( !mCaptureCurve.isEmpty() )
512 {
513 const QgsPoint prevPoint = mCaptureCurve.curveAt( mCaptureCurve.nCurves() - 1 )->endPoint();
514 if ( QgsPointXY( toCanvasCoordinates( toMapCoordinates( layer(), prevPoint ) ) ).distance( toCanvasCoordinates( point ) ) < mStreamingToleranceInPixels )
515 return;
516 }
517
518 mAllowAddingStreamingPoints = true;
520 mAllowAddingStreamingPoints = false;
521 }
522 else if ( tracingEnabled() && mCaptureCurve.numPoints() != 0 )
523 {
524 // Store the intermediate point for circular string to retrieve after tracing mouse move if
525 // the digitizing type is circular and the temp rubber band is effectivly circular and if this point is existing
526 // Store an empty point if the digitizing type is linear ot the point is not existing (curve not complete)
527 if ( mLineDigitizingType == Qgis::WkbType::CircularString &&
528 mTempRubberBand->stringType() == Qgis::WkbType::CircularString &&
529 mTempRubberBand->curveIsComplete() )
530 mCircularItermediatePoint = mTempRubberBand->pointFromEnd( 1 );
531 else if ( mLineDigitizingType == Qgis::WkbType::LineString ||
532 !mTempRubberBand->curveIsComplete() )
533 mCircularItermediatePoint = QgsPoint();
534
535 hasTrace = tracingMouseMove( e );
536
537 if ( !hasTrace )
538 {
539 // Restore the temp rubber band
540 mTempRubberBand->reset( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line, mLineDigitizingType, mCaptureFirstPoint );
541 mTempRubberBand->addPoint( mCaptureLastPoint );
542 if ( !mCircularItermediatePoint.isEmpty() )
543 {
544 mTempRubberBand->movePoint( mCircularItermediatePoint );
545 mTempRubberBand->addPoint( mCircularItermediatePoint );
546 }
547 }
548 }
549
550 if ( mCurrentCaptureTechnique != Qgis::CaptureTechnique::Streaming && !hasTrace )
551 {
552 if ( mCaptureCurve.numPoints() > 0 )
553 {
554 const QgsPoint mapPt = mCaptureLastPoint;
555
556 if ( mTempRubberBand )
557 {
558 mTempRubberBand->movePoint( mapPoint );
559 mTempRubberBand->movePoint( 0, mapPt );
560 }
561
562 // fix existing rubber band after tracing - the last point may have been moved if using offset
563 if ( mRubberBand->numberOfVertices() )
564 mRubberBand->movePoint( mapPt );
565 }
566 else if ( mTempRubberBand )
567 mTempRubberBand->movePoint( mapPoint );
568 }
569 }
570 }
571} // mouseMoveEvent
572
573
574int QgsMapToolCapture::nextPoint( const QgsPoint &mapPoint, QgsPoint &layerPoint )
575{
576 if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() ) )
577 {
578 try
579 {
580 const QgsPointXY mapP( mapPoint.x(), mapPoint.y() ); //#spellok
581 layerPoint = QgsPoint( toLayerCoordinates( vlayer, mapP ) ); //transform snapped point back to layer crs //#spellok
582 if ( QgsWkbTypes::hasZ( vlayer->wkbType() ) && !layerPoint.is3D() )
584 if ( QgsWkbTypes::hasM( vlayer->wkbType() ) && !layerPoint.isMeasure() )
586 }
587 catch ( QgsCsException & )
588 {
589 QgsDebugError( QStringLiteral( "transformation to layer coordinate failed" ) );
590 return 2;
591 }
592 }
593 else
594 {
595 layerPoint = QgsPoint( toLayerCoordinates( layer(), mapPoint ) );
596 }
597
598 return 0;
599}
600
601int QgsMapToolCapture::nextPoint( QPoint p, QgsPoint &layerPoint, QgsPoint &mapPoint )
602{
604 return nextPoint( mapPoint, layerPoint );
605}
606
608{
609 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() );
610 QgsVectorLayer *sourceLayer = match.layer();
612 {
614 return 0;
615 }
616 else if ( !vlayer )
617 {
618 return 1;
619 }
620
621 if ( match.isValid() && sourceLayer )
622 {
623 if ( ( match.hasVertex() || match.hasLineEndpoint() ) )
624 {
625 if ( sourceLayer->crs() != vlayer->crs() )
626 {
627 return 1;
628 }
629 QgsFeature f;
630 QgsFeatureRequest request;
631 request.setFilterFid( match.featureId() );
632 const bool fetched = match.layer()->getFeatures( request ).nextFeature( f );
633 if ( fetched )
634 {
635 QgsVertexId vId;
636 if ( !f.geometry().vertexIdFromVertexNr( match.vertexIndex(), vId ) )
637 {
638 return 2;
639 }
640 layerPoint = f.geometry().constGet()->vertexAt( vId );
641 if ( QgsWkbTypes::hasZ( vlayer->wkbType() ) && !layerPoint.is3D() )
642 layerPoint.addZValue( defaultZValue() );
643 if ( QgsWkbTypes::hasM( vlayer->wkbType() ) && !layerPoint.isMeasure() )
644 layerPoint.addMValue( defaultMValue() );
645
646 // ZM support depends on the target layer
647 if ( !QgsWkbTypes::hasZ( vlayer->wkbType() ) )
648 {
649 layerPoint.dropZValue();
650 }
651
652 if ( !QgsWkbTypes::hasM( vlayer->wkbType() ) )
653 {
654 layerPoint.dropMValue();
655 }
656
657 return 0;
658 }
659 return 2;
660 }
661 else if ( QgsProject::instance()->topologicalEditing() && ( match.hasEdge() || match.hasMiddleSegment() ) )
662 {
663 layerPoint = toLayerCoordinates( vlayer, match.interpolatedPoint( mCanvas->mapSettings().destinationCrs() ) );
664 return 0;
665 }
666 }
667 return 2;
668}
669
671{
672 return addVertex( point, QgsPointLocator::Match() );
673}
674
676{
677 if ( mode() == CaptureNone )
678 {
679 QgsDebugError( QStringLiteral( "invalid capture mode" ) );
680 return 2;
681 }
682
683 if ( mCapturing && mCurrentCaptureTechnique == Qgis::CaptureTechnique::Streaming && !mAllowAddingStreamingPoints )
684 return 0;
685
686 QgsPoint layerPoint;
687 if ( layer() )
688 {
689 int res = fetchLayerPoint( match, layerPoint );
690 if ( res != 0 )
691 {
692 res = nextPoint( QgsPoint( point ), layerPoint );
693 if ( res != 0 )
694 {
695 return res;
696 }
697 }
698 }
699 else
700 {
701 layerPoint = QgsPoint( point );
702 }
703 const QgsPoint mapPoint = toMapCoordinates( layer(), layerPoint );
704
705 if ( mCaptureMode == CapturePoint )
706 {
707 mCaptureCurve.addVertex( layerPoint );
708 mSnappingMatches.append( match );
709 }
710 else
711 {
712 if ( mCaptureFirstPoint.isEmpty() )
713 {
714 mCaptureFirstPoint = mapPoint;
715 }
716
717 if ( !mRubberBand )
719
720 if ( !mTempRubberBand )
721 {
722 mTempRubberBand.reset( createCurveRubberBand() );
723 mTempRubberBand->setStringType( mLineDigitizingType );
724 mTempRubberBand->reset( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line, mLineDigitizingType, mapPoint );
725 }
726
727 bool traceCreated = false;
728 if ( tracingEnabled() )
729 {
730 traceCreated = tracingAddVertex( mapPoint );
731 }
732
733 // keep new tracing start point if we created a trace. This is useful when tracing with
734 // offset so that the user stays "snapped"
735 mTracingStartPoint = traceCreated ? point : QgsPointXY();
736
737 if ( !traceCreated )
738 {
739 // ordinary digitizing
740 mTempRubberBand->movePoint( mapPoint ); //move the last point of the temp rubberband before operating with it
741 if ( mTempRubberBand->curveIsComplete() ) //2 points for line and 3 points for circular
742 {
743 if ( QgsCurve *curve = mTempRubberBand->curve() )
744 {
745 addCurve( curve );
746 // add curve append only invalid match to mSnappingMatches,
747 // so we need to remove them and add the one from here if it is valid
748 if ( match.isValid() && mSnappingMatches.count() > 0 && !mSnappingMatches.last().isValid() )
749 {
750 mSnappingMatches.removeLast();
751 if ( mTempRubberBand->stringType() == Qgis::WkbType::CircularString )
752 {
753 // for circular string two points are added and match for intermediate point is stored
754 mSnappingMatches.removeLast();
755 mSnappingMatches.append( mCircularIntermediateMatch );
756 }
757 mSnappingMatches.append( match );
758 }
759 }
760 mCaptureLastPoint = mapPoint;
761 mTempRubberBand->reset( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line, mLineDigitizingType, mCaptureFirstPoint );
762 }
763 else if ( mTempRubberBand->pointsCount() == 0 )
764 {
765 mCaptureLastPoint = mapPoint;
766 mCaptureCurve.addVertex( layerPoint );
767 mSnappingMatches.append( match );
768 }
769 else
770 {
771 if ( mTempRubberBand->stringType() == Qgis::WkbType::CircularString )
772 {
773 mCircularIntermediateMatch = match;
774 }
775 }
776
777 mTempRubberBand->addPoint( mapPoint );
778 }
779 else
780 {
781 mTempRubberBand->reset( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line, mLineDigitizingType, mCaptureFirstPoint );
782 mTempRubberBand->addPoint( mCaptureLastPoint );
783 }
784 }
785
786 updateExtraSnapLayer();
787 validateGeometry();
788
789 return 0;
790}
791
793{
794 if ( !c )
795 {
796 return 1;
797 }
798
799 if ( !mRubberBand )
800 {
802 }
803
804 if ( mTempRubberBand )
805 {
806 mTempRubberBand->reset( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line, mLineDigitizingType, mCaptureFirstPoint );
807 const QgsPoint endPt = c->endPoint();
808 mTempRubberBand->addPoint( endPt ); //add last point of c
809 }
810
811 const int countBefore = mCaptureCurve.vertexCount();
812 //if there is only one point, this the first digitized point that are in the this first curve added --> remove the point
813 if ( mCaptureCurve.numPoints() == 1 )
814 mCaptureCurve.removeCurve( 0 );
815
816 // Transform back to layer CRS in case map CRS and layer CRS are different
817 const QgsCoordinateTransform ct = mCanvas->mapSettings().layerTransform( layer() );
818 if ( ct.isValid() && !ct.isShortCircuited() )
819 {
820 QgsLineString *segmented = c->curveToLine();
822 // Curve geometries will be converted to segments, so we explicitly set extentPrevious to false
823 // to be able to remove the whole curve in undo
824 mCaptureCurve.addCurve( segmented, false );
825 delete c;
826 }
827 else
828 {
829 // we set the extendPrevious option to true to avoid creating compound curves with many 2 vertex linestrings -- instead we prefer
830 // to extend linestring curves so that they continue the previous linestring wherever possible...
831 mCaptureCurve.addCurve( c, !mStartNewCurve );
832 }
833
834 mStartNewCurve = false;
835
836 const int countAfter = mCaptureCurve.vertexCount();
837 const int addedPoint = countAfter - countBefore;
838
839 updateExtraSnapLayer();
840
841 for ( int i = 0; i < addedPoint; ++i )
842 mSnappingMatches.append( QgsPointLocator::Match() );
843
844 resetRubberBand();
845
846 return 0;
847}
848
850{
851 mCaptureCurve.clear();
852 updateExtraSnapLayer();
853}
854
855QList<QgsPointLocator::Match> QgsMapToolCapture::snappingMatches() const
856{
857 return mSnappingMatches;
858}
859
860void QgsMapToolCapture::undo( bool isAutoRepeat )
861{
862 mTracingStartPoint = QgsPointXY();
863
864 if ( mTempRubberBand )
865 {
866 if ( size() <= 1 && mTempRubberBand->pointsCount() != 0 )
867 return;
868
869 if ( isAutoRepeat && mIgnoreSubsequentAutoRepeatUndo )
870 return;
871 mIgnoreSubsequentAutoRepeatUndo = false;
872
873 const QgsPoint lastPoint = mTempRubberBand->lastPoint();
874
875 if ( mTempRubberBand->stringType() == Qgis::WkbType::CircularString && mTempRubberBand->pointsCount() > 2 )
876 {
877 mTempRubberBand->removeLastPoint();
878 mTempRubberBand->movePoint( lastPoint );
879 return;
880 }
881
882 QgsVertexId vertexToRemove;
883 vertexToRemove.part = 0;
884 vertexToRemove.ring = 0;
885 vertexToRemove.vertex = size() - 1;
886
887 // If the geometry was reprojected, remove the entire last curve.
888 const QgsCoordinateTransform ct = mCanvas->mapSettings().layerTransform( layer() );
889 if ( ct.isValid() && !ct.isShortCircuited() )
890 {
891 mCaptureCurve.removeCurve( mCaptureCurve.nCurves() - 1 );
892 }
893 if ( mCaptureCurve.numPoints() == 2 && mCaptureCurve.nCurves() == 1 )
894 {
895 // store the first vertex to restore if after deleting the curve
896 // because when only two vertices, removing a point remove all the curve
897 const QgsPoint fp = mCaptureCurve.startPoint();
898 mCaptureCurve.deleteVertex( vertexToRemove );
899 mCaptureCurve.addVertex( fp );
900 }
901 else
902 {
903 const int curvesBefore = mCaptureCurve.nCurves();
904 const bool lastCurveIsLineString = qgsgeometry_cast< QgsLineString * >( mCaptureCurve.curveAt( curvesBefore - 1 ) );
905
906 const int pointsCountBefore = mCaptureCurve.numPoints();
907 mCaptureCurve.deleteVertex( vertexToRemove );
908 int pointsCountAfter = mCaptureCurve.numPoints();
909 for ( ; pointsCountAfter < pointsCountBefore; pointsCountAfter++ )
910 if ( !mSnappingMatches.empty() )
911 mSnappingMatches.removeLast();
912
913 // if we have removed the last point in a linestring curve, then we "stick" here and ignore subsequent
914 // autorepeat undo actions until the user releases the undo key and holds it down again. This allows
915 // users to selectively remove portions of the geometry captured with the streaming mode by holding down
916 // the undo key, without risking accidental undo of non-streamed portions.
917 if ( mCaptureCurve.nCurves() < curvesBefore && lastCurveIsLineString )
918 mIgnoreSubsequentAutoRepeatUndo = true;
919 }
920
921 updateExtraSnapLayer();
922
923 resetRubberBand();
924
925 mTempRubberBand->reset( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line, mLineDigitizingType, mCaptureFirstPoint );
926
927 if ( mCaptureCurve.numPoints() > 0 )
928 {
929 const QgsPoint lastPt = mCaptureCurve.endPoint();
930 mCaptureLastPoint = toMapCoordinates( layer(), lastPt );
931 mTempRubberBand->addPoint( mCaptureLastPoint );
932 mTempRubberBand->movePoint( lastPoint );
933 }
934
936 validateGeometry();
937 }
938}
939
941{
942 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape && mCurrentShapeMapTool )
943 {
944 mCurrentShapeMapTool->keyPressEvent( e );
945 if ( e->isAccepted() )
946 return;
947 }
948
949 // this is backwards, but we can't change now without breaking api because
950 // forever QgsMapTools have had to explicitly mark events as ignored in order to
951 // indicate that they've consumed the event and that the default behavior should not
952 // be applied..!
953 // see QgsMapCanvas::keyPressEvent
954 e->accept();
955
956 if ( e->key() == Qt::Key_Backspace || e->key() == Qt::Key_Delete )
957 {
958 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape && mCurrentShapeMapTool )
959 {
960 if ( !e->isAutoRepeat() )
961 {
962 mCurrentShapeMapTool->undo();
963 }
964 }
965 else
966 {
967 undo( e->isAutoRepeat() );
968 }
969
970 // Override default shortcut management in MapCanvas
971 e->ignore();
972 }
973 else if ( e->key() == Qt::Key_Escape )
974 {
975 if ( mCurrentShapeMapTool )
976 mCurrentShapeMapTool->clean();
977
979
980 // Override default shortcut management in MapCanvas
981 e->ignore();
982 }
983}
984
986{
987 mCapturing = true;
988}
989
991{
992 return mCapturing;
993}
994
996{
997 mRubberBand.reset();
998
1000
1001 qDeleteAll( mGeomErrorMarkers );
1002 mGeomErrorMarkers.clear();
1003 mGeomErrors.clear();
1004
1005 mCaptureFirstPoint = QgsPoint();
1006 mCaptureLastPoint = QgsPoint();
1007
1008 mTracingStartPoint = QgsPointXY();
1009
1010 mCapturing = false;
1011 mCaptureCurve.clear();
1012 updateExtraSnapLayer();
1013 mSnappingMatches.clear();
1014 if ( auto *lCurrentVectorLayer = currentVectorLayer() )
1015 lCurrentVectorLayer->triggerRepaint();
1016}
1017
1019{
1020 mTempRubberBand.reset();
1021}
1022
1024{
1025 stopCapturing();
1026 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape && mCurrentShapeMapTool )
1027 mCurrentShapeMapTool->clean();
1028
1029 clearCurve();
1030}
1031
1033{
1034 mCaptureCurve.close();
1035 updateExtraSnapLayer();
1036}
1037
1038void QgsMapToolCapture::validateGeometry()
1039{
1042 )
1043 return;
1044
1045 if ( mValidator )
1046 {
1047 mValidator->deleteLater();
1048 mValidator = nullptr;
1049 }
1050
1051 mGeomErrors.clear();
1052 while ( !mGeomErrorMarkers.isEmpty() )
1053 {
1054 delete mGeomErrorMarkers.takeFirst();
1055 }
1056
1057 QgsGeometry geom;
1058
1059 switch ( mCaptureMode )
1060 {
1061 case CaptureNone:
1062 case CapturePoint:
1063 return;
1064 case CaptureLine:
1065 if ( size() < 2 )
1066 return;
1067 geom = QgsGeometry( mCaptureCurve.curveToLine() );
1068 break;
1069 case CapturePolygon:
1070 if ( size() < 3 )
1071 return;
1072 QgsLineString *exteriorRing = mCaptureCurve.curveToLine();
1073 exteriorRing->close();
1074 QgsPolygon *polygon = new QgsPolygon();
1075 polygon->setExteriorRing( exteriorRing );
1076 geom = QgsGeometry( polygon );
1077 break;
1078 }
1079
1080 if ( geom.isNull() )
1081 return;
1082
1086 mValidator = new QgsGeometryValidator( geom, nullptr, method );
1087 connect( mValidator, &QgsGeometryValidator::errorFound, this, &QgsMapToolCapture::addError );
1088 mValidator->start();
1089 QgsDebugMsgLevel( QStringLiteral( "Validation started" ), 4 );
1090}
1091
1092void QgsMapToolCapture::addError( const QgsGeometry::Error &e )
1093{
1094 mGeomErrors << e;
1095 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() );
1096 if ( !vlayer )
1097 return;
1098
1099 if ( e.hasWhere() )
1100 {
1102 vm->setCenter( mCanvas->mapSettings().layerToMapCoordinates( vlayer, e.where() ) );
1104 vm->setPenWidth( 2 );
1105 vm->setToolTip( e.what() );
1106 vm->setColor( Qt::green );
1107 vm->setZValue( vm->zValue() + 1 );
1108 mGeomErrorMarkers << vm;
1109 }
1110}
1111
1113{
1114 return mCaptureCurve.numPoints();
1115}
1116
1117QVector<QgsPointXY> QgsMapToolCapture::points() const
1118{
1119 QVector<QgsPointXY> pointsXY;
1121
1122 return pointsXY;
1123}
1124
1126{
1127 QgsPointSequence pts;
1128 mCaptureCurve.points( pts );
1129 return pts;
1130}
1131
1132void QgsMapToolCapture::setPoints( const QVector<QgsPointXY> &pointList )
1133{
1134 QgsLineString *line = new QgsLineString( pointList );
1135 mCaptureCurve.clear();
1136 mCaptureCurve.addCurve( line );
1137 updateExtraSnapLayer();
1138 mSnappingMatches.clear();
1139 for ( int i = 0; i < line->length(); ++i )
1140 mSnappingMatches.append( QgsPointLocator::Match() );
1141 resetRubberBand();
1142}
1143
1145{
1146 QgsLineString *line = new QgsLineString( pointList );
1147 mCaptureCurve.clear();
1148 mCaptureCurve.addCurve( line );
1149 updateExtraSnapLayer();
1150 mSnappingMatches.clear();
1151 for ( int i = 0; i < line->length(); ++i )
1152 mSnappingMatches.append( QgsPointLocator::Match() );
1153 resetRubberBand();
1154}
1155
1157{
1158 QgsPoint newPoint( Qgis::WkbType::Point, point.x(), point.y() );
1159
1160 // get current layer
1161 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() );
1162 if ( !vlayer )
1163 {
1164 return newPoint;
1165 }
1166
1167 // convert to the corresponding type for a full ZM support
1168 const Qgis::WkbType type = vlayer->wkbType();
1169 if ( QgsWkbTypes::hasZ( type ) && !QgsWkbTypes::hasM( type ) )
1170 {
1171 newPoint.convertTo( Qgis::WkbType::PointZ );
1172 }
1173 else if ( !QgsWkbTypes::hasZ( type ) && QgsWkbTypes::hasM( type ) )
1174 {
1175 newPoint.convertTo( Qgis::WkbType::PointM );
1176 }
1177 else if ( QgsWkbTypes::hasZ( type ) && QgsWkbTypes::hasM( type ) )
1178 {
1180 }
1181
1182 // set z value if necessary
1183 if ( QgsWkbTypes::hasZ( newPoint.wkbType() ) )
1184 {
1186 }
1187 // set m value if necessary
1188 if ( QgsWkbTypes::hasM( newPoint.wkbType() ) )
1189 {
1191 }
1192 return newPoint;
1193}
1194
1196{
1197 QgsPoint newPoint = mapPoint( e.mapPoint() );
1198
1199 // set z or m value from snapped point if necessary
1200 if ( QgsWkbTypes::hasZ( newPoint.wkbType() ) || QgsWkbTypes::hasM( newPoint.wkbType() ) )
1201 {
1202 // if snapped, z and m dimension are taken from the corresponding snapped
1203 // point.
1204 if ( e.isSnapped() )
1205 {
1206 const QgsPointLocator::Match match = e.mapPointMatch();
1207
1208 if ( match.layer() )
1209 {
1210 const QgsFeature ft = match.layer()->getFeature( match.featureId() );
1211 if ( QgsWkbTypes::hasZ( match.layer()->wkbType() ) )
1212 {
1213 newPoint.setZ( ft.geometry().vertexAt( match.vertexIndex() ).z() );
1214 }
1215 if ( QgsWkbTypes::hasM( match.layer()->wkbType() ) )
1216 {
1217 newPoint.setM( ft.geometry().vertexAt( match.vertexIndex() ).m() );
1218 }
1219 }
1220 }
1221 }
1222
1223 return newPoint;
1224}
1225
1226void QgsMapToolCapture::updateExtraSnapLayer()
1227{
1228 if ( !mExtraSnapLayer )
1229 return;
1230
1231 if ( canvas()->snappingUtils()->config().selfSnapping() && layer() && mCaptureCurve.numPoints() >= 2 )
1232 {
1233 // the current layer may have changed
1234 mExtraSnapLayer->setCrs( layer()->crs() );
1235 QgsGeometry geom = QgsGeometry( mCaptureCurve.clone() );
1236 // we close the curve to allow snapping on last segment
1237 if ( mCaptureMode == CapturePolygon && mCaptureCurve.numPoints() >= 3 )
1238 {
1239 qgsgeometry_cast<QgsCompoundCurve *>( geom.get() )->close();
1240 }
1241 mExtraSnapLayer->changeGeometry( mExtraSnapFeatureId, geom );
1242 }
1243 else
1244 {
1245 QgsGeometry geom;
1246 mExtraSnapLayer->changeGeometry( mExtraSnapFeatureId, geom );
1247 }
1248}
1249
1250
1252{
1253 // POINT CAPTURING
1254 if ( mode() == CapturePoint )
1255 {
1256 if ( e->button() != Qt::LeftButton )
1257 return;
1258
1259 QgsPoint savePoint; //point in layer coordinates
1260 bool isMatchPointZ = false;
1261 bool isMatchPointM = false;
1262 try
1263 {
1264 QgsPoint fetchPoint;
1265 int res = fetchLayerPoint( e->mapPointMatch(), fetchPoint );
1266 isMatchPointZ = QgsWkbTypes::hasZ( fetchPoint.wkbType() );
1267 isMatchPointM = QgsWkbTypes::hasM( fetchPoint.wkbType() );
1268
1269 if ( res == 0 )
1270 {
1272 if ( isMatchPointM && isMatchPointZ )
1273 {
1274 geomType = Qgis::WkbType::PointZM;
1275 }
1276 else if ( isMatchPointM )
1277 {
1278 geomType = Qgis::WkbType::PointM;
1279 }
1280 else if ( isMatchPointZ )
1281 {
1282 geomType = Qgis::WkbType::PointZ;
1283 }
1284 savePoint = QgsPoint( geomType, fetchPoint.x(), fetchPoint.y(), fetchPoint.z(), fetchPoint.m() );
1285 }
1286 else
1287 {
1288 QgsPointXY point = mCanvas->mapSettings().mapToLayerCoordinates( layer(), e->mapPoint() );
1289
1290 savePoint = QgsPoint( point.x(), point.y(), fetchPoint.z(), fetchPoint.m() );
1291 }
1292 }
1293 catch ( QgsCsException &cse )
1294 {
1295 Q_UNUSED( cse )
1296 emit messageEmitted( tr( "Cannot transform the point to the layer's coordinate system" ), Qgis::MessageLevel::Warning );
1297 return;
1298 }
1299
1300 QgsGeometry g( std::make_unique<QgsPoint>( savePoint ) );
1301
1302 // The snapping result needs to be added so it's available in the @snapping_results variable of default value etc. expression contexts
1303 addVertex( e->mapPoint(), e->mapPointMatch() );
1304
1305 geometryCaptured( g );
1306 pointCaptured( savePoint );
1307
1308 stopCapturing();
1309
1310 // we are done with digitizing for now so instruct advanced digitizing dock to reset its CAD points
1312 }
1313
1314 // LINE AND POLYGON CAPTURING
1315 else if ( mode() == CaptureLine || mode() == CapturePolygon )
1316 {
1317 bool digitizingFinished = false;
1318
1319 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape )
1320 {
1321 if ( !mCurrentShapeMapTool )
1322 {
1323 emit messageEmitted( tr( "Select an option from the Shape Digitizing Toolbar in order to capture shapes" ), Qgis::MessageLevel::Warning );
1324 return;
1325 }
1326 else
1327 {
1328 if ( !mTempRubberBand )
1329 {
1330 mTempRubberBand.reset( createCurveRubberBand() );
1331 mTempRubberBand->setStringType( mLineDigitizingType );
1332 mTempRubberBand->setRubberBandGeometryType( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line );
1333 }
1334
1335 digitizingFinished = mCurrentShapeMapTool->cadCanvasReleaseEvent( e, mCaptureMode );
1336 if ( digitizingFinished )
1337 mCurrentShapeMapTool->clean();
1338 }
1339 }
1340 else // i.e. not shape
1341 {
1342 //add point to list and to rubber band
1343 if ( e->button() == Qt::LeftButton )
1344 {
1345 const int error = addVertex( e->mapPoint(), e->mapPointMatch() );
1346 if ( error == 2 )
1347 {
1348 //problem with coordinate transformation
1349 emit messageEmitted( tr( "Cannot transform the point to the layers coordinate system" ), Qgis::MessageLevel::Warning );
1350 return;
1351 }
1352
1354 }
1355 else if ( e->button() == Qt::RightButton )
1356 {
1357 // End of string
1359
1360 //lines: bail out if there are not at least two vertices
1361 if ( mode() == CaptureLine && size() < 2 )
1362 {
1363 stopCapturing();
1364 return;
1365 }
1366
1367 //polygons: bail out if there are not at least two vertices
1368 if ( mode() == CapturePolygon && size() < 3 )
1369 {
1370 stopCapturing();
1371 return;
1372 }
1373
1374 if ( mode() == CapturePolygon || e->modifiers() == Qt::ShiftModifier )
1375 {
1376 closePolygon();
1377 }
1378
1379 digitizingFinished = true;
1380 }
1381 }
1382
1383 if ( digitizingFinished )
1384 {
1385 QgsGeometry g;
1386 std::unique_ptr<QgsCurve> curveToAdd( captureCurve()->clone() );
1387
1388 if ( mode() == CaptureLine )
1389 {
1390 g = QgsGeometry( curveToAdd->clone() );
1391 geometryCaptured( g );
1392 lineCaptured( curveToAdd.release() );
1393 }
1394 else
1395 {
1396
1397 //does compoundcurve contain circular strings?
1398 //does provider support circular strings?
1399 if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() ) )
1400 {
1401 const bool hasCurvedSegments = captureCurve()->hasCurvedSegments();
1402 const bool providerSupportsCurvedSegments = vlayer->dataProvider()->capabilities() & QgsVectorDataProvider::CircularGeometries;
1403
1404 if ( hasCurvedSegments && providerSupportsCurvedSegments )
1405 {
1406 curveToAdd.reset( captureCurve()->clone() );
1407 }
1408 else
1409 {
1410 curveToAdd.reset( captureCurve()->curveToLine() );
1411 }
1412 }
1413 else
1414 {
1415 curveToAdd.reset( captureCurve()->clone() );
1416 }
1417 std::unique_ptr<QgsCurvePolygon> poly{new QgsCurvePolygon()};
1418 poly->setExteriorRing( curveToAdd.release() );
1419 g = QgsGeometry( poly->clone() );
1420 geometryCaptured( g );
1421 polygonCaptured( poly.get() );
1422 }
1423
1424 stopCapturing();
1425 }
1426 }
1427}
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:294
@ 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:1657
@ QgisInternal
Use internal QgsGeometryValidator method.
@ Geos
Use GEOS validation methods.
@ Polygon
Polygons.
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition: qgis.h:182
@ CompoundCurve
CompoundCurve.
@ LineString
LineString.
@ PointM
PointM.
@ CircularString
CircularString.
@ PointZ
PointZ.
@ PointZM
PointZM.
@ Reverse
Reverse/inverse transform (from destination to source)
bool isMeasure() const
Returns true if the geometry contains m values.
bool is3D() const
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.
Qgis::WkbType wkbType() const
Returns the WKB type of the geometry.
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()
Removes 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.
bool isEmpty() const override
Returns true if the geometry is empty.
int nCurves() const
Returns the number of curves in the geometry.
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.
const QgsCurve * curveAt(int i) const
Returns the curve at the specified index.
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.
bool hasCurvedSegments() const override
Returns true if the geometry contains curved segments.
QgsPoint startPoint() const override
Returns the starting point of the curve.
QgsPoint endPoint() const override
Returns the end point of the curve.
int numPoints() const override
Returns the number of points in the curve.
void addVertex(const QgsPoint &pt)
Adds a vertex to the end of the geometry.
Class for doing transforms between two map coordinate systems.
bool isShortCircuited() const
Returns true if the transform short circuits because the source and destination are equivalent.
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:67
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:35
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)
Fetch next feature and stores in f, returns true on success.
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:2694
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:162
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:164
QgsAbstractGeometry * get()
Returns a modifiable (non-const) reference to the underlying abstract geometry primitive.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (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.
Qgis::WkbType wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:45
double length() const override
Returns the planar, 2-dimensional length of the geometry.
void transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection d=Qgis::TransformDirection::Forward, bool transformZ=false) override
Transforms the geometry using a coordinate transform.
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:93
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:75
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:81
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.
QFlags< Capability > Capabilities
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.
QgsRubberBand * createRubberBand(Qgis::GeometryType geometryType=Qgis::GeometryType::Line, bool alternativeBand=false)
Creates a rubber band with the color/line width from the QGIS settings.
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.
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:341
QgsMapCanvas * canvas() const
returns pointer to the tool's map canvas
Definition: qgsmaptool.cpp:221
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:166
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:151
A class to represent a 2D point.
Definition: qgspointxy.h:60
double y
Definition: qgspointxy.h:64
Q_GADGET double x
Definition: qgspointxy.h:63
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:564
bool dropMValue() override
Drops any measure values which exist in the geometry.
Definition: qgspoint.cpp:605
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
Definition: qgspoint.cpp:553
Q_GADGET double x
Definition: qgspoint.h:52
double z
Definition: qgspoint.h:54
void setM(double m)
Sets the point's m-value.
Definition: qgspoint.h:371
bool convertTo(Qgis::WkbType type) override
Converts the geometry to a specified type.
Definition: qgspoint.cpp:622
bool isEmpty() const override
Returns true if the geometry is empty.
Definition: qgspoint.cpp:733
void setZ(double z)
Sets the point's z-coordinate.
Definition: qgspoint.h:356
bool dropZValue() override
Drops any z-dimensions which exist in the geometry.
Definition: qgspoint.cpp:594
double m
Definition: qgspoint.h:55
double y
Definition: qgspoint.h:53
Polygon geometry type.
Definition: qgspolygon.h:33
void setExteriorRing(QgsCurve *ring) override
Sets the exterior ring of the polygon.
Definition: qgspolygon.cpp:263
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:481
void snappingConfigChanged(const QgsSnappingConfig &config)
Emitted whenever the configuration for snapping has changed.
A class for drawing transient features (e.g.
Definition: qgsrubberband.h:54
void movePoint(const QgsPointXY &p, int geometryIndex=0, int ringIndex=0)
Moves the rubber band point specified by index.
void reset(Qgis::GeometryType geometryType=Qgis::GeometryType::Line)
Clears all the geometries in this rubberband.
const QgsPointXY * getPoint(int i, int j=0, int ringIndex=0) const
Returns a vertex.
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 QgsSettingsEntryInteger * settingsDigitizingStreamTolerance
Settings entry digitizing stream tolerance.
static const QgsSettingsEntryDouble * settingsDigitizingLineColorAlphaScale
Settings entry digitizing line color alpha scale.
static const QgsSettingsEntryDouble * settingsDigitizingConvertToCurveAngleTolerance
Settings entry digitizing convert to curve angle tolerance.
static const QgsSettingsEntryDouble * settingsDigitizingConvertToCurveDistanceTolerance
Settings entry digitizing convert to curve distance tolerance.
static const QgsSettingsEntryInteger * settingsDigitizingValidateGeometries
Settings entry digitizing validate geometries.
static const QgsSettingsEntryBool * settingsDigitizingConvertToCurve
Settings entry digitizing convert to curve.
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:132
@ ErrNone
No error.
Definition: qgstracer.h:133
@ ErrTooManyFeatures
Max feature count threshold was reached while reading features.
Definition: qgstracer.h:134
bool init()
Build the internal data structures.
Definition: qgstracer.cpp:675
@ CircularGeometries
Supports circular geometry types (circularstring, compoundcurve, curvepolygon)
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 bool startEditing()
Makes the layer editable.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
Q_INVOKABLE Qgis::WkbType wkbType() const FINAL
Returns the WKBType or WKBUnknown in case of error.
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.
Q_INVOKABLE Qgis::GeometryType geometryType() const
Returns point, line or polygon.
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 hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:973
static bool hasM(Qgis::WkbType type)
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:1023
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:628
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:5853
QVector< QgsPoint > QgsPointSequence
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugError(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:30
int vertex
Vertex number.
Definition: qgsvertexid.h:94
int part
Part number.
Definition: qgsvertexid.h:88
int ring
Ring number.
Definition: qgsvertexid.h:91