QGIS API Documentation 3.39.0-Master (d85f3c2a281)
Loading...
Searching...
No Matches
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
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
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 addCurve( new QgsLineString( mapPoints ) );
320
321 resetRubberBand();
322
323 // Curves de-approximation
325 {
326#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
327 int pointBefore = mCaptureCurve.numPoints();
328#endif
329
330 // If the tool and the layer support curves
331 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() );
333 {
334 const QgsGeometry linear = QgsGeometry( mCaptureCurve.segmentize() );
335 const QgsGeometry curved = linear.convertToCurves(
338 );
340 {
341 mCaptureCurve.clear();
342 mCaptureCurve.addCurve( qgsgeometry_cast< const QgsCurve * >( curved.constGet() )->clone() );
343 }
344 else
345 {
346 mCaptureCurve = *qgsgeometry_cast<QgsCompoundCurve *>( curved.constGet() );
347 }
348 }
349
350#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
351 // sync the snapping matches list
352 const int pointAfter = mCaptureCurve.numPoints();
353
354 for ( ; pointBefore < pointAfter; ++pointBefore )
355 mSnappingMatches.append( QgsPointLocator::Match() );
356
357 for ( ; pointBefore > pointAfter; --pointBefore )
358 mSnappingMatches.removeLast();
359#else
360 mSnappingMatches.resize( mCaptureCurve.numPoints() );
361#endif
362 }
363
364 tracer->reportError( QgsTracer::ErrNone, true ); // clear messagebar if there was any error
365
366 // adjust last captured point
367 const QgsPoint lastPt = mCaptureCurve.endPoint();
368 mCaptureLastPoint = toMapCoordinates( layer(), lastPt );
369
370 return true;
371}
372
373QgsMapToolCaptureRubberBand *QgsMapToolCapture::createCurveRubberBand() const
374{
375 QgsMapToolCaptureRubberBand *rb = new QgsMapToolCaptureRubberBand( mCanvas );
376 rb->setStrokeWidth( digitizingStrokeWidth() );
377 QColor color = digitizingStrokeColor();
378
380 color.setAlphaF( color.alphaF() * alphaScale );
381 rb->setLineStyle( Qt::DotLine );
382 rb->setStrokeColor( color );
383
384 const QColor fillColor = digitizingFillColor();
385 rb->setFillColor( fillColor );
386 rb->show();
387 return rb;
388}
389
390void QgsMapToolCapture::resetRubberBand()
391{
392 if ( !mRubberBand )
393 return;
394 QgsLineString *lineString = mCaptureCurve.curveToLine();
395
396 mRubberBand->reset( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line );
397 mRubberBand->addGeometry( QgsGeometry( lineString ), layer() );
398}
399
401{
402 return mRubberBand.release();
403}
404
412
420
422{
423 if ( mCurrentCaptureTechnique == technique )
424 return;
425
426 mStartNewCurve = true;
427
428 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape && mCurrentShapeMapTool )
429 {
430 mCurrentShapeMapTool->deactivate();
431 clean();
432 }
433
434 switch ( technique )
435 {
437 mLineDigitizingType = Qgis::WkbType::LineString;
438 break;
440 mLineDigitizingType = Qgis::WkbType::CircularString;
441 break;
443 mLineDigitizingType = Qgis::WkbType::LineString;
445 break;
447 mLineDigitizingType = Qgis::WkbType::LineString;
448 break;
449
450 }
451
452 if ( mTempRubberBand )
453 mTempRubberBand->setStringType( mLineDigitizingType );
454
455 mCurrentCaptureTechnique = technique;
456
457 if ( technique == Qgis::CaptureTechnique::Shape && mCurrentShapeMapTool && isActive() )
458 {
459 clean();
460 mCurrentShapeMapTool->activate( mCaptureMode, mCaptureLastPoint );
461 }
462}
463
465{
466 if ( mCurrentShapeMapTool )
467 {
468 if ( shapeMapToolMetadata && mCurrentShapeMapTool->id() == shapeMapToolMetadata->id() )
469 return;
470 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape )
471 mCurrentShapeMapTool->deactivate();
472 mCurrentShapeMapTool->deleteLater();
473 }
474
475 mCurrentShapeMapTool.reset( shapeMapToolMetadata ? shapeMapToolMetadata->factory( this ) : nullptr );
476
477 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape && isActive() )
478 {
479 clean();
480 if ( mCurrentShapeMapTool )
481 mCurrentShapeMapTool->activate( mCaptureMode, mCaptureLastPoint );
482 }
483}
484
486{
488
489 const QgsPointXY point = e->mapPoint();
490
491 mSnapIndicator->setMatch( e->mapPointMatch() );
492
493 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape )
494 {
495 if ( !mCurrentShapeMapTool )
496 {
497 emit messageEmitted( tr( "Select an option from the Shape Digitizing Toolbar in order to capture shapes" ), Qgis::MessageLevel::Warning );
498 }
499 else
500 {
501 if ( !mTempRubberBand )
502 {
503 mTempRubberBand.reset( createCurveRubberBand() );
504 mTempRubberBand->setStringType( mLineDigitizingType );
505 mTempRubberBand->setRubberBandGeometryType( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line );
506 }
507
508 mCurrentShapeMapTool->cadCanvasMoveEvent( e, mCaptureMode );
509 return;
510 }
511 }
512 else
513 {
514 const QgsPoint mapPoint = QgsPoint( point );
515
516 if ( mCaptureMode != CapturePoint && mTempRubberBand && mCapturing )
517 {
518 bool hasTrace = false;
519
520 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Streaming )
521 {
522 if ( !mCaptureCurve.isEmpty() )
523 {
524 const QgsPoint prevPoint = mCaptureCurve.curveAt( mCaptureCurve.nCurves() - 1 )->endPoint();
525 if ( QgsPointXY( toCanvasCoordinates( toMapCoordinates( layer(), prevPoint ) ) ).distance( toCanvasCoordinates( point ) ) < mStreamingToleranceInPixels )
526 return;
527 }
528
529 mAllowAddingStreamingPoints = true;
531 mAllowAddingStreamingPoints = false;
532 }
533 else if ( tracingEnabled() && mCaptureCurve.numPoints() != 0 )
534 {
535 // Store the intermediate point for circular string to retrieve after tracing mouse move if
536 // the digitizing type is circular and the temp rubber band is effectivly circular and if this point is existing
537 // Store an empty point if the digitizing type is linear ot the point is not existing (curve not complete)
538 if ( mLineDigitizingType == Qgis::WkbType::CircularString &&
539 mTempRubberBand->stringType() == Qgis::WkbType::CircularString &&
540 mTempRubberBand->curveIsComplete() )
541 mCircularItermediatePoint = mTempRubberBand->pointFromEnd( 1 );
542 else if ( mLineDigitizingType == Qgis::WkbType::LineString ||
543 !mTempRubberBand->curveIsComplete() )
544 mCircularItermediatePoint = QgsPoint();
545
546 hasTrace = tracingMouseMove( e );
547
548 if ( !hasTrace )
549 {
550 // Restore the temp rubber band
551 mTempRubberBand->reset( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line, mLineDigitizingType, mCaptureFirstPoint );
552 mTempRubberBand->addPoint( mCaptureLastPoint );
553 if ( !mCircularItermediatePoint.isEmpty() )
554 {
555 mTempRubberBand->movePoint( mCircularItermediatePoint );
556 mTempRubberBand->addPoint( mCircularItermediatePoint );
557 }
558 }
559 }
560
561 if ( mCurrentCaptureTechnique != Qgis::CaptureTechnique::Streaming && !hasTrace )
562 {
563 if ( mCaptureCurve.numPoints() > 0 )
564 {
565 const QgsPoint mapPt = mCaptureLastPoint;
566
567 if ( mTempRubberBand )
568 {
569 mTempRubberBand->movePoint( mapPoint );
570 mTempRubberBand->movePoint( 0, mapPt );
571 }
572
573 // fix existing rubber band after tracing - the last point may have been moved if using offset
574 if ( mRubberBand->numberOfVertices() )
575 mRubberBand->movePoint( mapPt );
576 }
577 else if ( mTempRubberBand )
578 mTempRubberBand->movePoint( mapPoint );
579 }
580 }
581 }
582} // mouseMoveEvent
583
584
585int QgsMapToolCapture::nextPoint( const QgsPoint &mapPoint, QgsPoint &layerPoint )
586{
587 if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() ) )
588 {
589 try
590 {
591 const QgsPointXY mapP( mapPoint.x(), mapPoint.y() ); //#spellok
592 layerPoint = QgsPoint( toLayerCoordinates( vlayer, mapP ) ); //transform snapped point back to layer crs //#spellok
593 if ( QgsWkbTypes::hasZ( vlayer->wkbType() ) && !layerPoint.is3D() )
595 if ( QgsWkbTypes::hasM( vlayer->wkbType() ) && !layerPoint.isMeasure() )
597 }
598 catch ( QgsCsException & )
599 {
600 QgsDebugError( QStringLiteral( "transformation to layer coordinate failed" ) );
601 return 2;
602 }
603 }
604 else
605 {
606 layerPoint = QgsPoint( toLayerCoordinates( layer(), mapPoint ) );
607 }
608
609 return 0;
610}
611
612int QgsMapToolCapture::nextPoint( QPoint p, QgsPoint &layerPoint, QgsPoint &mapPoint )
613{
615 return nextPoint( mapPoint, layerPoint );
616}
617
619{
620 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() );
621 QgsVectorLayer *sourceLayer = match.layer();
623 {
625 return 0;
626 }
627 else if ( !vlayer )
628 {
629 return 1;
630 }
631
632 if ( match.isValid() && sourceLayer )
633 {
634 if ( ( match.hasVertex() || match.hasLineEndpoint() ) )
635 {
636 if ( sourceLayer->crs() != vlayer->crs() )
637 {
638 return 1;
639 }
640 QgsFeature f;
641 QgsFeatureRequest request;
642 request.setFilterFid( match.featureId() );
643 const bool fetched = match.layer()->getFeatures( request ).nextFeature( f );
644 if ( fetched )
645 {
646 QgsVertexId vId;
647 if ( !f.geometry().vertexIdFromVertexNr( match.vertexIndex(), vId ) )
648 {
649 return 2;
650 }
651 layerPoint = f.geometry().constGet()->vertexAt( vId );
652 if ( QgsWkbTypes::hasZ( vlayer->wkbType() ) && !layerPoint.is3D() )
653 layerPoint.addZValue( defaultZValue() );
654 if ( QgsWkbTypes::hasM( vlayer->wkbType() ) && !layerPoint.isMeasure() )
655 layerPoint.addMValue( defaultMValue() );
656
657 // ZM support depends on the target layer
658 if ( !QgsWkbTypes::hasZ( vlayer->wkbType() ) )
659 {
660 layerPoint.dropZValue();
661 }
662
663 if ( !QgsWkbTypes::hasM( vlayer->wkbType() ) )
664 {
665 layerPoint.dropMValue();
666 }
667
668 return 0;
669 }
670 return 2;
671 }
672 else if ( QgsProject::instance()->topologicalEditing() && ( match.hasEdge() || match.hasMiddleSegment() ) )
673 {
674 layerPoint = toLayerCoordinates( vlayer, match.interpolatedPoint( mCanvas->mapSettings().destinationCrs() ) );
675 return 0;
676 }
677 }
678 return 2;
679}
680
682{
683 return addVertex( point, QgsPointLocator::Match() );
684}
685
687{
688 if ( mode() == CaptureNone )
689 {
690 QgsDebugError( QStringLiteral( "invalid capture mode" ) );
691 return 2;
692 }
693
694 if ( mCapturing && mCurrentCaptureTechnique == Qgis::CaptureTechnique::Streaming && !mAllowAddingStreamingPoints )
695 return 0;
696
697 QgsPoint layerPoint;
698 if ( layer() )
699 {
700 int res = fetchLayerPoint( match, layerPoint );
701 if ( res != 0 )
702 {
703 res = nextPoint( QgsPoint( point ), layerPoint );
704 if ( res != 0 )
705 {
706 return res;
707 }
708 }
709 }
710 else
711 {
712 layerPoint = QgsPoint( point );
713 }
714 const QgsPoint mapPoint = toMapCoordinates( layer(), layerPoint );
715
716 if ( mCaptureMode == CapturePoint )
717 {
718 mCaptureCurve.addVertex( layerPoint );
719 mSnappingMatches.append( match );
720 }
721 else
722 {
723 if ( mCaptureFirstPoint.isEmpty() )
724 {
725 mCaptureFirstPoint = mapPoint;
726 }
727
728 if ( !mRubberBand )
730
731 if ( !mTempRubberBand )
732 {
733 mTempRubberBand.reset( createCurveRubberBand() );
734 mTempRubberBand->setStringType( mLineDigitizingType );
735 mTempRubberBand->reset( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line, mLineDigitizingType, mapPoint );
736 }
737
738 bool traceCreated = false;
739 if ( tracingEnabled() )
740 {
741 traceCreated = tracingAddVertex( mapPoint );
742 }
743
744 // keep new tracing start point if we created a trace. This is useful when tracing with
745 // offset so that the user stays "snapped"
746 mTracingStartPoint = traceCreated ? point : QgsPointXY();
747
748 if ( !traceCreated )
749 {
750 // ordinary digitizing
751 mTempRubberBand->movePoint( mapPoint ); //move the last point of the temp rubberband before operating with it
752 if ( mTempRubberBand->curveIsComplete() ) //2 points for line and 3 points for circular
753 {
754 if ( QgsCurve *curve = mTempRubberBand->curve() )
755 {
756 addCurve( curve );
757 // add curve append only invalid match to mSnappingMatches,
758 // so we need to remove them and add the one from here if it is valid
759 if ( match.isValid() && mSnappingMatches.count() > 0 && !mSnappingMatches.last().isValid() )
760 {
761 mSnappingMatches.removeLast();
762 if ( mTempRubberBand->stringType() == Qgis::WkbType::CircularString )
763 {
764 // for circular string two points are added and match for intermediate point is stored
765 mSnappingMatches.removeLast();
766 mSnappingMatches.append( mCircularIntermediateMatch );
767 }
768 mSnappingMatches.append( match );
769 }
770 }
771 mCaptureLastPoint = mapPoint;
772 mTempRubberBand->reset( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line, mLineDigitizingType, mCaptureFirstPoint );
773 }
774 else if ( mTempRubberBand->pointsCount() == 0 )
775 {
776 mCaptureLastPoint = mapPoint;
777 mCaptureCurve.addVertex( layerPoint );
778 mSnappingMatches.append( match );
779 }
780 else
781 {
782 if ( mTempRubberBand->stringType() == Qgis::WkbType::CircularString )
783 {
784 mCircularIntermediateMatch = match;
785 }
786 }
787
788 mTempRubberBand->addPoint( mapPoint );
789 }
790 else
791 {
792 mTempRubberBand->reset( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line, mLineDigitizingType, mCaptureFirstPoint );
793 mTempRubberBand->addPoint( mCaptureLastPoint );
794 }
795 }
796
797 updateExtraSnapLayer();
798 validateGeometry();
799
800 return 0;
801}
802
804{
805 if ( !c )
806 {
807 return 1;
808 }
809
810 if ( !mRubberBand )
811 {
813 }
814
815 if ( mTempRubberBand )
816 {
817 mTempRubberBand->reset( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line, mLineDigitizingType, mCaptureFirstPoint );
818 const QgsPoint endPt = c->endPoint();
819 mTempRubberBand->addPoint( endPt ); //add last point of c
820 }
821
822 const int countBefore = mCaptureCurve.vertexCount();
823 //if there is only one point, this the first digitized point that are in the this first curve added --> remove the point
824 if ( mCaptureCurve.numPoints() == 1 )
825 mCaptureCurve.removeCurve( 0 );
826
827 // Transform back to layer CRS in case map CRS and layer CRS are different
828 const QgsCoordinateTransform ct = mCanvas->mapSettings().layerTransform( layer() );
829 if ( ct.isValid() && !ct.isShortCircuited() )
830 {
831 QgsLineString *segmented = c->curveToLine();
833 // Curve geometries will be converted to segments, so we explicitly set extentPrevious to false
834 // to be able to remove the whole curve in undo
835 mCaptureCurve.addCurve( segmented, false );
836 delete c;
837 }
838 else
839 {
840 // we set the extendPrevious option to true to avoid creating compound curves with many 2 vertex linestrings -- instead we prefer
841 // to extend linestring curves so that they continue the previous linestring wherever possible...
842 mCaptureCurve.addCurve( c, !mStartNewCurve );
843 }
844
845 mStartNewCurve = false;
846
847 const int countAfter = mCaptureCurve.vertexCount();
848 const int addedPoint = countAfter - countBefore;
849
850 updateExtraSnapLayer();
851
852 for ( int i = 0; i < addedPoint; ++i )
853 mSnappingMatches.append( QgsPointLocator::Match() );
854
855 resetRubberBand();
856
857 return 0;
858}
859
861{
862 mCaptureCurve.clear();
863 updateExtraSnapLayer();
864}
865
866QList<QgsPointLocator::Match> QgsMapToolCapture::snappingMatches() const
867{
868 return mSnappingMatches;
869}
870
871void QgsMapToolCapture::undo( bool isAutoRepeat )
872{
873 mTracingStartPoint = QgsPointXY();
874
875 if ( mTempRubberBand )
876 {
877 if ( size() <= 1 && mTempRubberBand->pointsCount() != 0 )
878 return;
879
880 if ( isAutoRepeat && mIgnoreSubsequentAutoRepeatUndo )
881 return;
882 mIgnoreSubsequentAutoRepeatUndo = false;
883
884 const QgsPoint lastPoint = mTempRubberBand->lastPoint();
885
886 if ( mTempRubberBand->stringType() == Qgis::WkbType::CircularString && mTempRubberBand->pointsCount() > 2 )
887 {
888 mTempRubberBand->removeLastPoint();
889 mTempRubberBand->movePoint( lastPoint );
890 return;
891 }
892
893 QgsVertexId vertexToRemove;
894 vertexToRemove.part = 0;
895 vertexToRemove.ring = 0;
896 vertexToRemove.vertex = size() - 1;
897
898 // If the geometry was reprojected, remove the entire last curve.
899 const QgsCoordinateTransform ct = mCanvas->mapSettings().layerTransform( layer() );
900 if ( ct.isValid() && !ct.isShortCircuited() )
901 {
902 mCaptureCurve.removeCurve( mCaptureCurve.nCurves() - 1 );
903 }
904 if ( mCaptureCurve.numPoints() == 2 && mCaptureCurve.nCurves() == 1 )
905 {
906 // store the first vertex to restore if after deleting the curve
907 // because when only two vertices, removing a point remove all the curve
908 const QgsPoint fp = mCaptureCurve.startPoint();
909 mCaptureCurve.deleteVertex( vertexToRemove );
910 mCaptureCurve.addVertex( fp );
911 }
912 else
913 {
914 const int curvesBefore = mCaptureCurve.nCurves();
915 const bool lastCurveIsLineString = qgsgeometry_cast< QgsLineString * >( mCaptureCurve.curveAt( curvesBefore - 1 ) );
916
917 const int pointsCountBefore = mCaptureCurve.numPoints();
918 mCaptureCurve.deleteVertex( vertexToRemove );
919 int pointsCountAfter = mCaptureCurve.numPoints();
920 for ( ; pointsCountAfter < pointsCountBefore; pointsCountAfter++ )
921 if ( !mSnappingMatches.empty() )
922 mSnappingMatches.removeLast();
923
924 // if we have removed the last point in a linestring curve, then we "stick" here and ignore subsequent
925 // autorepeat undo actions until the user releases the undo key and holds it down again. This allows
926 // users to selectively remove portions of the geometry captured with the streaming mode by holding down
927 // the undo key, without risking accidental undo of non-streamed portions.
928 if ( mCaptureCurve.nCurves() < curvesBefore && lastCurveIsLineString )
929 mIgnoreSubsequentAutoRepeatUndo = true;
930 }
931
932 updateExtraSnapLayer();
933
934 resetRubberBand();
935
936 mTempRubberBand->reset( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line, mLineDigitizingType, mCaptureFirstPoint );
937
938 if ( mCaptureCurve.numPoints() > 0 )
939 {
940 const QgsPoint lastPt = mCaptureCurve.endPoint();
941 mCaptureLastPoint = toMapCoordinates( layer(), lastPt );
942 mTempRubberBand->addPoint( mCaptureLastPoint );
943 mTempRubberBand->movePoint( lastPoint );
944 }
945
947 validateGeometry();
948 }
949}
950
952{
953 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape && mCurrentShapeMapTool )
954 {
955 mCurrentShapeMapTool->keyPressEvent( e );
956 if ( e->isAccepted() )
957 return;
958 }
959
960 // this is backwards, but we can't change now without breaking api because
961 // forever QgsMapTools have had to explicitly mark events as ignored in order to
962 // indicate that they've consumed the event and that the default behavior should not
963 // be applied..!
964 // see QgsMapCanvas::keyPressEvent
965 e->accept();
966
967 if ( e->key() == Qt::Key_Backspace || e->key() == Qt::Key_Delete )
968 {
969 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape && mCurrentShapeMapTool )
970 {
971 if ( !e->isAutoRepeat() )
972 {
973 mCurrentShapeMapTool->undo();
974 }
975 }
976 else
977 {
978 undo( e->isAutoRepeat() );
979 }
980
981 // Override default shortcut management in MapCanvas
982 e->ignore();
983 }
984 else if ( e->key() == Qt::Key_Escape )
985 {
986 if ( mCurrentShapeMapTool )
987 mCurrentShapeMapTool->clean();
988
990
991 // Override default shortcut management in MapCanvas
992 e->ignore();
993 }
994}
995
997{
998 mCapturing = true;
999}
1000
1002{
1003 return mCapturing;
1004}
1005
1007{
1008 mRubberBand.reset();
1009
1011
1012 qDeleteAll( mGeomErrorMarkers );
1013 mGeomErrorMarkers.clear();
1014 mGeomErrors.clear();
1015
1016 mCaptureFirstPoint = QgsPoint();
1017 mCaptureLastPoint = QgsPoint();
1018
1019 mTracingStartPoint = QgsPointXY();
1020
1021 mCapturing = false;
1022 mCaptureCurve.clear();
1023 updateExtraSnapLayer();
1024 mSnappingMatches.clear();
1025 if ( auto *lCurrentVectorLayer = currentVectorLayer() )
1026 lCurrentVectorLayer->triggerRepaint();
1027}
1028
1030{
1031 mTempRubberBand.reset();
1032}
1033
1035{
1036 stopCapturing();
1037 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape && mCurrentShapeMapTool )
1038 mCurrentShapeMapTool->clean();
1039
1040 clearCurve();
1041}
1042
1044{
1045 mCaptureCurve.close();
1046 updateExtraSnapLayer();
1047}
1048
1049void QgsMapToolCapture::validateGeometry()
1050{
1053 )
1054 return;
1055
1056 if ( mValidator )
1057 {
1058 mValidator->deleteLater();
1059 mValidator = nullptr;
1060 }
1061
1062 mGeomErrors.clear();
1063 while ( !mGeomErrorMarkers.isEmpty() )
1064 {
1065 delete mGeomErrorMarkers.takeFirst();
1066 }
1067
1068 QgsGeometry geom;
1069
1070 switch ( mCaptureMode )
1071 {
1072 case CaptureNone:
1073 case CapturePoint:
1074 return;
1075 case CaptureLine:
1076 if ( size() < 2 )
1077 return;
1078 geom = QgsGeometry( mCaptureCurve.curveToLine() );
1079 break;
1080 case CapturePolygon:
1081 if ( size() < 3 )
1082 return;
1083 QgsLineString *exteriorRing = mCaptureCurve.curveToLine();
1084 exteriorRing->close();
1085 QgsPolygon *polygon = new QgsPolygon();
1086 polygon->setExteriorRing( exteriorRing );
1087 geom = QgsGeometry( polygon );
1088 break;
1089 }
1090
1091 if ( geom.isNull() )
1092 return;
1093
1097 mValidator = new QgsGeometryValidator( geom, nullptr, method );
1098 connect( mValidator, &QgsGeometryValidator::errorFound, this, &QgsMapToolCapture::addError );
1099 mValidator->start();
1100 QgsDebugMsgLevel( QStringLiteral( "Validation started" ), 4 );
1101}
1102
1103void QgsMapToolCapture::addError( const QgsGeometry::Error &e )
1104{
1105 mGeomErrors << e;
1106 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() );
1107 if ( !vlayer )
1108 return;
1109
1110 if ( e.hasWhere() )
1111 {
1113 vm->setCenter( mCanvas->mapSettings().layerToMapCoordinates( vlayer, e.where() ) );
1115 vm->setPenWidth( 2 );
1116 vm->setToolTip( e.what() );
1117 vm->setColor( Qt::green );
1118 vm->setZValue( vm->zValue() + 1 );
1119 mGeomErrorMarkers << vm;
1120 }
1121}
1122
1124{
1125 return mCaptureCurve.numPoints();
1126}
1127
1128QVector<QgsPointXY> QgsMapToolCapture::points() const
1129{
1130 QVector<QgsPointXY> pointsXY;
1132
1133 return pointsXY;
1134}
1135
1137{
1138 QgsPointSequence pts;
1139 mCaptureCurve.points( pts );
1140 return pts;
1141}
1142
1143void QgsMapToolCapture::setPoints( const QVector<QgsPointXY> &pointList )
1144{
1145 QgsLineString *line = new QgsLineString( pointList );
1146 mCaptureCurve.clear();
1147 mCaptureCurve.addCurve( line );
1148 updateExtraSnapLayer();
1149 mSnappingMatches.clear();
1150 for ( int i = 0; i < line->length(); ++i )
1151 mSnappingMatches.append( QgsPointLocator::Match() );
1152 resetRubberBand();
1153}
1154
1156{
1157 QgsLineString *line = new QgsLineString( pointList );
1158 mCaptureCurve.clear();
1159 mCaptureCurve.addCurve( line );
1160 updateExtraSnapLayer();
1161 mSnappingMatches.clear();
1162 for ( int i = 0; i < line->length(); ++i )
1163 mSnappingMatches.append( QgsPointLocator::Match() );
1164 resetRubberBand();
1165}
1166
1168{
1169 QgsPoint newPoint( Qgis::WkbType::Point, point.x(), point.y() );
1170
1171 // get current layer
1172 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() );
1173 if ( !vlayer )
1174 {
1175 return newPoint;
1176 }
1177
1178 // convert to the corresponding type for a full ZM support
1179 const Qgis::WkbType type = vlayer->wkbType();
1180 if ( QgsWkbTypes::hasZ( type ) && !QgsWkbTypes::hasM( type ) )
1181 {
1182 newPoint.convertTo( Qgis::WkbType::PointZ );
1183 }
1184 else if ( !QgsWkbTypes::hasZ( type ) && QgsWkbTypes::hasM( type ) )
1185 {
1186 newPoint.convertTo( Qgis::WkbType::PointM );
1187 }
1188 else if ( QgsWkbTypes::hasZ( type ) && QgsWkbTypes::hasM( type ) )
1189 {
1191 }
1192
1193 // set z value if necessary
1194 if ( QgsWkbTypes::hasZ( newPoint.wkbType() ) )
1195 {
1197 }
1198 // set m value if necessary
1199 if ( QgsWkbTypes::hasM( newPoint.wkbType() ) )
1200 {
1202 }
1203 return newPoint;
1204}
1205
1207{
1208 QgsPoint newPoint = mapPoint( e.mapPoint() );
1209
1210 // set z or m value from snapped point if necessary
1211 if ( QgsWkbTypes::hasZ( newPoint.wkbType() ) || QgsWkbTypes::hasM( newPoint.wkbType() ) )
1212 {
1213 // if snapped, z and m dimension are taken from the corresponding snapped
1214 // point.
1215 if ( e.isSnapped() )
1216 {
1217 const QgsPointLocator::Match match = e.mapPointMatch();
1218
1219 if ( match.layer() )
1220 {
1221 const QgsFeature ft = match.layer()->getFeature( match.featureId() );
1222 if ( QgsWkbTypes::hasZ( match.layer()->wkbType() ) )
1223 {
1224 newPoint.setZ( ft.geometry().vertexAt( match.vertexIndex() ).z() );
1225 }
1226 if ( QgsWkbTypes::hasM( match.layer()->wkbType() ) )
1227 {
1228 newPoint.setM( ft.geometry().vertexAt( match.vertexIndex() ).m() );
1229 }
1230 }
1231 }
1232 }
1233
1234 return newPoint;
1235}
1236
1237void QgsMapToolCapture::updateExtraSnapLayer()
1238{
1239 if ( !mExtraSnapLayer )
1240 return;
1241
1242 if ( canvas()->snappingUtils()->config().selfSnapping() && layer() && mCaptureCurve.numPoints() >= 2 )
1243 {
1244 // the current layer may have changed
1245 mExtraSnapLayer->setCrs( layer()->crs() );
1246 QgsGeometry geom = QgsGeometry( mCaptureCurve.clone() );
1247 // we close the curve to allow snapping on last segment
1248 if ( mCaptureMode == CapturePolygon && mCaptureCurve.numPoints() >= 3 )
1249 {
1250 qgsgeometry_cast<QgsCompoundCurve *>( geom.get() )->close();
1251 }
1252 mExtraSnapLayer->changeGeometry( mExtraSnapFeatureId, geom );
1253 }
1254 else
1255 {
1256 QgsGeometry geom;
1257 mExtraSnapLayer->changeGeometry( mExtraSnapFeatureId, geom );
1258 }
1259}
1260
1261
1263{
1264 // POINT CAPTURING
1265 if ( mode() == CapturePoint )
1266 {
1267 if ( e->button() != Qt::LeftButton )
1268 return;
1269
1270 QgsPoint savePoint; //point in layer coordinates
1271 bool isMatchPointZ = false;
1272 bool isMatchPointM = false;
1273 try
1274 {
1275 QgsPoint fetchPoint;
1276 int res = fetchLayerPoint( e->mapPointMatch(), fetchPoint );
1277 isMatchPointZ = QgsWkbTypes::hasZ( fetchPoint.wkbType() );
1278 isMatchPointM = QgsWkbTypes::hasM( fetchPoint.wkbType() );
1279
1280 if ( res == 0 )
1281 {
1283 if ( isMatchPointM && isMatchPointZ )
1284 {
1285 geomType = Qgis::WkbType::PointZM;
1286 }
1287 else if ( isMatchPointM )
1288 {
1289 geomType = Qgis::WkbType::PointM;
1290 }
1291 else if ( isMatchPointZ )
1292 {
1293 geomType = Qgis::WkbType::PointZ;
1294 }
1295 savePoint = QgsPoint( geomType, fetchPoint.x(), fetchPoint.y(), fetchPoint.z(), fetchPoint.m() );
1296 }
1297 else
1298 {
1299 QgsPointXY point = mCanvas->mapSettings().mapToLayerCoordinates( layer(), e->mapPoint() );
1300
1301 savePoint = QgsPoint( point.x(), point.y(), fetchPoint.z(), fetchPoint.m() );
1302 }
1303 }
1304 catch ( QgsCsException &cse )
1305 {
1306 Q_UNUSED( cse )
1307 emit messageEmitted( tr( "Cannot transform the point to the layer's coordinate system" ), Qgis::MessageLevel::Warning );
1308 return;
1309 }
1310
1311 QgsGeometry g( std::make_unique<QgsPoint>( savePoint ) );
1312
1313 // The snapping result needs to be added so it's available in the @snapping_results variable of default value etc. expression contexts
1314 addVertex( e->mapPoint(), e->mapPointMatch() );
1315
1316 geometryCaptured( g );
1317 pointCaptured( savePoint );
1318
1319 stopCapturing();
1320
1321 // we are done with digitizing for now so instruct advanced digitizing dock to reset its CAD points
1323 }
1324
1325 // LINE AND POLYGON CAPTURING
1326 else if ( mode() == CaptureLine || mode() == CapturePolygon )
1327 {
1328 bool digitizingFinished = false;
1329
1330 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape )
1331 {
1332 if ( !mCurrentShapeMapTool )
1333 {
1334 emit messageEmitted( tr( "Select an option from the Shape Digitizing Toolbar in order to capture shapes" ), Qgis::MessageLevel::Warning );
1335 return;
1336 }
1337 else
1338 {
1339 if ( !mTempRubberBand )
1340 {
1341 mTempRubberBand.reset( createCurveRubberBand() );
1342 mTempRubberBand->setStringType( mLineDigitizingType );
1343 mTempRubberBand->setRubberBandGeometryType( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line );
1344 }
1345
1346 digitizingFinished = mCurrentShapeMapTool->cadCanvasReleaseEvent( e, mCaptureMode );
1347 if ( digitizingFinished )
1348 mCurrentShapeMapTool->clean();
1349 }
1350 }
1351 else // i.e. not shape
1352 {
1353 //add point to list and to rubber band
1354 if ( e->button() == Qt::LeftButton )
1355 {
1356 const int error = addVertex( e->mapPoint(), e->mapPointMatch() );
1357 if ( error == 2 )
1358 {
1359 //problem with coordinate transformation
1360 emit messageEmitted( tr( "Cannot transform the point to the layers coordinate system" ), Qgis::MessageLevel::Warning );
1361 return;
1362 }
1363
1365 }
1366 else if ( e->button() == Qt::RightButton )
1367 {
1368 // End of string
1370
1371 //lines: bail out if there are not at least two vertices
1372 if ( mode() == CaptureLine && size() < 2 )
1373 {
1374 stopCapturing();
1375 return;
1376 }
1377
1378 //polygons: bail out if there are not at least two vertices
1379 if ( mode() == CapturePolygon && size() < 3 )
1380 {
1381 stopCapturing();
1382 return;
1383 }
1384
1385 if ( mode() == CapturePolygon || e->modifiers() == Qt::ShiftModifier )
1386 {
1387 closePolygon();
1388 }
1389
1390 digitizingFinished = true;
1391 }
1392 }
1393
1394 if ( digitizingFinished )
1395 {
1396 QgsGeometry g;
1397 std::unique_ptr<QgsCurve> curveToAdd( captureCurve()->clone() );
1398
1399 if ( mode() == CaptureLine )
1400 {
1401 g = QgsGeometry( curveToAdd->clone() );
1402 geometryCaptured( g );
1403 lineCaptured( curveToAdd.release() );
1404 }
1405 else
1406 {
1407
1408 //does compoundcurve contain circular strings?
1409 //does provider support circular strings?
1410 if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() ) )
1411 {
1412 const bool hasCurvedSegments = captureCurve()->hasCurvedSegments();
1413 const bool providerSupportsCurvedSegments = vlayer->dataProvider()->capabilities() & Qgis::VectorProviderCapability::CircularGeometries;
1414
1415 if ( hasCurvedSegments && providerSupportsCurvedSegments )
1416 {
1417 curveToAdd.reset( captureCurve()->clone() );
1418 }
1419 else
1420 {
1421 curveToAdd.reset( captureCurve()->curveToLine() );
1422 }
1423 }
1424 else
1425 {
1426 curveToAdd.reset( captureCurve()->clone() );
1427 }
1428 std::unique_ptr<QgsCurvePolygon> poly{new QgsCurvePolygon()};
1429 poly->setExteriorRing( curveToAdd.release() );
1430 g = QgsGeometry( poly->clone() );
1431 geometryCaptured( g );
1432 polygonCaptured( poly.get() );
1433 }
1434
1435 stopCapturing();
1436 }
1437 }
1438}
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.
The Qgis class provides global constants for use throughout the application.
Definition qgis.h:54
@ CircularGeometries
Supports circular geometry types (circularstring, compoundcurve, curvepolygon)
CaptureTechnique
Capture technique.
Definition qgis.h:376
@ 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:1931
@ QgisInternal
Use internal QgsGeometryValidator method.
@ Geos
Use GEOS validation methods.
@ Warning
Warning message.
Definition qgis.h:156
@ Polygon
Polygons.
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:256
@ 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.
@ CapturePoint
Select and capture a point or a feature.
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.
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:58
QgsFeatureId id
Definition qgsfeature.h:66
QgsGeometry geometry
Definition qgsfeature.h:69
void errorFound(const QgsGeometry::Error &error)
Sent when an error has been found during the validation process.
A geometry error.
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.
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.
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.
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.
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()
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.
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:76
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:83
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
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.
@ SupportsCurves
Supports curved geometries input.
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 =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.
QPointer< QgsMapCanvas > mCanvas
The pointer to the map canvas.
Definition qgsmaptool.h:341
QgsMapCanvas * canvas() const
returns pointer to the tool's map canvas
QgsPointXY toMapCoordinates(QPoint point)
Transforms a point from screen coordinates to map coordinates.
virtual void setCursor(const QCursor &cursor)
Sets a user defined cursor.
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.
bool isActive() const
Returns if the current map tool active on the map canvas.
A class to represent a 2D point.
Definition qgspointxy.h:60
double y
Definition qgspointxy.h:64
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:568
bool dropMValue() override
Drops any measure values which exist in the geometry.
Definition qgspoint.cpp:609
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
Definition qgspoint.cpp:557
double z
Definition qgspoint.h:54
double x
Definition qgspoint.h:52
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:626
bool isEmpty() const override
Returns true if the geometry is empty.
Definition qgspoint.cpp:737
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:598
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.
static QgsProject * instance()
Returns the QgsProject singleton instance.
void snappingConfigChanged(const QgsSnappingConfig &config)
Emitted whenever the configuration for snapping has changed.
A class for drawing transient features (e.g.
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...
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.
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.
virtual Q_INVOKABLE Qgis::VectorProviderCapabilities 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.
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.
Q_INVOKABLE QgsFeature getFeature(QgsFeatureId fid) const
Queries the layer for the feature with the given id.
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.
static bool hasM(Qgis::WkbType type)
Tests whether a WKB type contains m values.
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
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:6511
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