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