QGIS API Documentation 3.40.0-Bratislava (b56115d8743)
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 if ( vlayer->isSpatial() )
158 {
160 }
161 else
162 {
163 setCursor( QCursor( Qt::ArrowCursor ) );
164 mCanvas->mapTool()->clean();
165 }
166
167 switch ( vlayer->geometryType() )
168 {
170 mCaptureMode = CapturePoint;
171 break;
173 mCaptureMode = CaptureLine;
174 break;
176 mCaptureMode = CapturePolygon;
177 break;
178 default:
179 mCaptureMode = CaptureNone;
180 break;
181 }
182
183 if ( mTempRubberBand )
184 mTempRubberBand->setRubberBandGeometryType( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line );
185
186 resetRubberBand();
188}
189
190
191bool QgsMapToolCapture::tracingEnabled()
192{
194 return tracer && ( !tracer->actionEnableTracing() || tracer->actionEnableTracing()->isChecked() )
195 && ( !tracer->actionEnableSnapping() || tracer->actionEnableSnapping()->isChecked() );
196}
197
198
199QgsPointXY QgsMapToolCapture::tracingStartPoint()
200{
201 // if we have starting point from previous trace, then preferably use that one
202 // (useful when tracing with offset)
203 if ( mTracingStartPoint != QgsPointXY() )
204 return mTracingStartPoint;
205
206 return mCaptureLastPoint;
207}
208
209
210bool QgsMapToolCapture::tracingMouseMove( QgsMapMouseEvent *e )
211{
212 if ( !e->isSnapped() )
213 return false;
214
215 QgsPointXY pt0 = tracingStartPoint();
216 if ( pt0 == QgsPointXY() )
217 return false;
218
220 if ( !tracer )
221 return false; // this should not happen!
222
224 QVector<QgsPointXY> points = tracer->findShortestPath( pt0, e->mapPoint(), &err );
225 if ( points.isEmpty() )
226 {
227 tracer->reportError( err, false );
228 return false;
229 }
230
231 mTempRubberBand->reset( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line, Qgis::WkbType::LineString, mCaptureFirstPoint );
232 mTempRubberBand->addPoint( mCaptureLastPoint );
233
234 // if there is offset, we need to fix the rubber bands to make sure they are aligned correctly.
235 // There are two cases we need to sort out:
236 // 1. the last point of mRubberBand may need to be moved off the traced curve to respect the offset
237 // 2. first point of mTempRubberBand may be needed to be moved to the beginning of the offset trace
238 const QgsPoint lastPoint = mCaptureLastPoint;
239 QgsPointXY lastPointXY( lastPoint );
240 if ( lastPointXY == pt0 && points[0] != lastPointXY )
241 {
242 if ( mRubberBand->numberOfVertices() != 0 )
243 {
244 // if rubber band had just one point, for some strange reason it contains the point twice
245 // we only want to move the last point if there are multiple points already
246 if ( mRubberBand->numberOfVertices() > 2 || ( mRubberBand->numberOfVertices() == 2 && *mRubberBand->getPoint( 0, 0 ) != *mRubberBand->getPoint( 0, 1 ) ) )
247 mRubberBand->movePoint( points[0] );
248 }
249
250 mTempRubberBand->movePoint( 0, QgsPoint( points[0] ) );
251 }
252
253 mTempRubberBand->movePoint( QgsPoint( points[0] ) );
254
255 // update temporary rubberband
256 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
257 mTempRubberBand->addPoint( QgsPoint( points.at( i ) ), i == points.count() - 1 );
258
259
260 mTempRubberBand->addPoint( QgsPoint( points[points.size() - 1] ) );
261
262 tracer->reportError( QgsTracer::ErrNone, false ); // clear messagebar if there was any error
263 return true;
264}
265
266
267bool QgsMapToolCapture::tracingAddVertex( const QgsPointXY &point )
268{
270 if ( !tracer )
271 return false; // this should not happen!
272
273 if ( mTempRubberBand->pointsCount() == 0 )
274 {
275 if ( !tracer->init() )
276 {
278 return false;
279 }
280
281 // only accept first point if it is snapped to the graph (to vertex or edge)
282 const bool res = tracer->isPointSnapped( point );
283 if ( res )
284 {
285 mTracingStartPoint = point;
286 }
287 return false;
288 }
289
290 QgsPointXY pt0 = tracingStartPoint();
291 if ( pt0 == QgsPointXY() )
292 return false;
293
295 const QVector<QgsPointXY> tracedPointsInMapCrs = tracer->findShortestPath( pt0, point, &err );
296 if ( tracedPointsInMapCrs.isEmpty() )
297 return false; // ignore the vertex - can't find path to the end point!
298
299 // transform points
300 QgsPointSequence layerPoints;
301 layerPoints.reserve( tracedPointsInMapCrs.size() );
302 QgsPointSequence mapPoints;
303 mapPoints.reserve( tracedPointsInMapCrs.size() );
304 for ( const QgsPointXY &tracedPointMapCrs : tracedPointsInMapCrs )
305 {
306 QgsPoint mapPoint( tracedPointMapCrs );
307
308 QgsPoint lp; // in layer coords
309 if ( nextPoint( mapPoint, lp ) != 0 )
310 return false;
311
312 // copy z and m from layer point back to mapPoint, as nextPoint() call will populate these based
313 // on the context of the trace
314 if ( lp.is3D() )
315 mapPoint.addZValue( lp.z() );
316 if ( lp.isMeasure() )
317 mapPoint.addMValue( lp.m() );
318
319 mapPoints << mapPoint;
320 layerPoints << lp;
321 }
322
323 // Move the last point of the captured curve to the first point on the trace string (necessary if there is offset)
324 const QgsVertexId lastVertexId( 0, 0, mCaptureCurve.numPoints() - 1 );
325 mCaptureCurve.moveVertex( lastVertexId, layerPoints.first() );
326 mSnappingMatches.removeLast();
327 mSnappingMatches.append( QgsPointLocator::Match() );
328
329 addCurve( new QgsLineString( mapPoints ) );
330
331 resetRubberBand();
332
333 // Curves de-approximation
335 {
336#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
337 int pointBefore = mCaptureCurve.numPoints();
338#endif
339
340 // If the tool and the layer support curves
341 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() );
343 {
344 const QgsGeometry linear = QgsGeometry( mCaptureCurve.segmentize() );
345 const QgsGeometry curved = linear.convertToCurves(
348 );
350 {
351 mCaptureCurve.clear();
352 mCaptureCurve.addCurve( qgsgeometry_cast< const QgsCurve * >( curved.constGet() )->clone() );
353 }
354 else
355 {
356 mCaptureCurve = *qgsgeometry_cast<QgsCompoundCurve *>( curved.constGet() );
357 }
358 }
359
360#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
361 // sync the snapping matches list
362 const int pointAfter = mCaptureCurve.numPoints();
363
364 for ( ; pointBefore < pointAfter; ++pointBefore )
365 mSnappingMatches.append( QgsPointLocator::Match() );
366
367 for ( ; pointBefore > pointAfter; --pointBefore )
368 mSnappingMatches.removeLast();
369#else
370 mSnappingMatches.resize( mCaptureCurve.numPoints() );
371#endif
372 }
373
374 tracer->reportError( QgsTracer::ErrNone, true ); // clear messagebar if there was any error
375
376 // adjust last captured point
377 const QgsPoint lastPt = mCaptureCurve.endPoint();
378 mCaptureLastPoint = toMapCoordinates( layer(), lastPt );
379
380 return true;
381}
382
383QgsMapToolCaptureRubberBand *QgsMapToolCapture::createCurveRubberBand() const
384{
385 QgsMapToolCaptureRubberBand *rb = new QgsMapToolCaptureRubberBand( mCanvas );
386 rb->setStrokeWidth( digitizingStrokeWidth() );
387 QColor color = digitizingStrokeColor();
388
390 color.setAlphaF( color.alphaF() * alphaScale );
391 rb->setLineStyle( Qt::DotLine );
392 rb->setStrokeColor( color );
393
394 const QColor fillColor = digitizingFillColor();
395 rb->setFillColor( fillColor );
396 rb->show();
397 return rb;
398}
399
400void QgsMapToolCapture::resetRubberBand()
401{
402 if ( !mRubberBand )
403 return;
404 QgsLineString *lineString = mCaptureCurve.curveToLine();
405
406 mRubberBand->reset( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line );
407 mRubberBand->addGeometry( QgsGeometry( lineString ), layer() );
408}
409
411{
412 return mRubberBand.release();
413}
414
422
430
432{
433 if ( mCurrentCaptureTechnique == technique )
434 return;
435
436 mStartNewCurve = true;
437
438 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape && mCurrentShapeMapTool )
439 {
440 mCurrentShapeMapTool->deactivate();
441 clean();
442 }
443
444 switch ( technique )
445 {
447 mLineDigitizingType = Qgis::WkbType::LineString;
448 break;
450 mLineDigitizingType = Qgis::WkbType::CircularString;
451 break;
453 mLineDigitizingType = Qgis::WkbType::LineString;
455 break;
457 mLineDigitizingType = Qgis::WkbType::LineString;
458 break;
459
460 }
461
462 if ( mTempRubberBand )
463 mTempRubberBand->setStringType( mLineDigitizingType );
464
465 mCurrentCaptureTechnique = technique;
466
467 if ( technique == Qgis::CaptureTechnique::Shape && mCurrentShapeMapTool && isActive() )
468 {
469 clean();
470 mCurrentShapeMapTool->activate( mCaptureMode, mCaptureLastPoint );
471 }
472}
473
475{
476 if ( mCurrentShapeMapTool )
477 {
478 if ( shapeMapToolMetadata && mCurrentShapeMapTool->id() == shapeMapToolMetadata->id() )
479 return;
480 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape )
481 mCurrentShapeMapTool->deactivate();
482 mCurrentShapeMapTool->deleteLater();
483 }
484
485 mCurrentShapeMapTool.reset( shapeMapToolMetadata ? shapeMapToolMetadata->factory( this ) : nullptr );
486
487 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape && isActive() )
488 {
489 clean();
490 if ( mCurrentShapeMapTool )
491 mCurrentShapeMapTool->activate( mCaptureMode, mCaptureLastPoint );
492 }
493}
494
496{
498
499 const QgsPointXY point = e->mapPoint();
500 if ( canvas()->currentLayer() && canvas()->currentLayer()->isSpatial() )
501 mSnapIndicator->setMatch( e->mapPointMatch() );
502
503 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape )
504 {
505 if ( !mCurrentShapeMapTool )
506 {
507 emit messageEmitted( tr( "Select an option from the Shape Digitizing Toolbar in order to capture shapes" ), Qgis::MessageLevel::Warning );
508 }
509 else
510 {
511 if ( !mTempRubberBand )
512 {
513 mTempRubberBand.reset( createCurveRubberBand() );
514 mTempRubberBand->setStringType( mLineDigitizingType );
515 mTempRubberBand->setRubberBandGeometryType( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line );
516 }
517
518 mCurrentShapeMapTool->cadCanvasMoveEvent( e, mCaptureMode );
519 return;
520 }
521 }
522 else
523 {
524 const QgsPoint mapPoint = QgsPoint( point );
525
526 if ( mCaptureMode != CapturePoint && mTempRubberBand && mCapturing )
527 {
528 bool hasTrace = false;
529
530 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Streaming )
531 {
532 if ( !mCaptureCurve.isEmpty() )
533 {
534 const QgsPoint prevPoint = mCaptureCurve.curveAt( mCaptureCurve.nCurves() - 1 )->endPoint();
535 if ( QgsPointXY( toCanvasCoordinates( toMapCoordinates( layer(), prevPoint ) ) ).distance( toCanvasCoordinates( point ) ) < mStreamingToleranceInPixels )
536 return;
537 }
538
539 mAllowAddingStreamingPoints = true;
541 mAllowAddingStreamingPoints = false;
542 }
543 else if ( tracingEnabled() && mCaptureCurve.numPoints() != 0 )
544 {
545 // Store the intermediate point for circular string to retrieve after tracing mouse move if
546 // the digitizing type is circular and the temp rubber band is effectivly circular and if this point is existing
547 // Store an empty point if the digitizing type is linear ot the point is not existing (curve not complete)
548 if ( mLineDigitizingType == Qgis::WkbType::CircularString &&
549 mTempRubberBand->stringType() == Qgis::WkbType::CircularString &&
550 mTempRubberBand->curveIsComplete() )
551 mCircularItermediatePoint = mTempRubberBand->pointFromEnd( 1 );
552 else if ( mLineDigitizingType == Qgis::WkbType::LineString ||
553 !mTempRubberBand->curveIsComplete() )
554 mCircularItermediatePoint = QgsPoint();
555
556 hasTrace = tracingMouseMove( e );
557
558 if ( !hasTrace )
559 {
560 // Restore the temp rubber band
561 mTempRubberBand->reset( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line, mLineDigitizingType, mCaptureFirstPoint );
562 mTempRubberBand->addPoint( mCaptureLastPoint );
563 if ( !mCircularItermediatePoint.isEmpty() )
564 {
565 mTempRubberBand->movePoint( mCircularItermediatePoint );
566 mTempRubberBand->addPoint( mCircularItermediatePoint );
567 }
568 }
569 }
570
571 if ( mCurrentCaptureTechnique != Qgis::CaptureTechnique::Streaming && !hasTrace )
572 {
573 if ( mCaptureCurve.numPoints() > 0 )
574 {
575 const QgsPoint mapPt = mCaptureLastPoint;
576
577 if ( mTempRubberBand )
578 {
579 mTempRubberBand->movePoint( mapPoint );
580 mTempRubberBand->movePoint( 0, mapPt );
581 }
582
583 // fix existing rubber band after tracing - the last point may have been moved if using offset
584 if ( mRubberBand->numberOfVertices() )
585 mRubberBand->movePoint( mapPt );
586 }
587 else if ( mTempRubberBand )
588 mTempRubberBand->movePoint( mapPoint );
589 }
590 }
591 }
592} // mouseMoveEvent
593
594
595int QgsMapToolCapture::nextPoint( const QgsPoint &mapPoint, QgsPoint &layerPoint )
596{
597 if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() ) )
598 {
599 try
600 {
601 const QgsPointXY mapP( mapPoint.x(), mapPoint.y() ); //#spellok
602 layerPoint = QgsPoint( toLayerCoordinates( vlayer, mapP ) ); //transform snapped point back to layer crs //#spellok
603 if ( QgsWkbTypes::hasZ( vlayer->wkbType() ) && !layerPoint.is3D() )
605 if ( QgsWkbTypes::hasM( vlayer->wkbType() ) && !layerPoint.isMeasure() )
607 }
608 catch ( QgsCsException & )
609 {
610 QgsDebugError( QStringLiteral( "transformation to layer coordinate failed" ) );
611 return 2;
612 }
613 }
614 else
615 {
616 layerPoint = QgsPoint( toLayerCoordinates( layer(), mapPoint ) );
617 }
618
619 return 0;
620}
621
622int QgsMapToolCapture::nextPoint( QPoint p, QgsPoint &layerPoint, QgsPoint &mapPoint )
623{
625 return nextPoint( mapPoint, layerPoint );
626}
627
629{
630 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() );
631 QgsVectorLayer *sourceLayer = match.layer();
633 {
635 return 0;
636 }
637 else if ( !vlayer )
638 {
639 return 1;
640 }
641
642 if ( match.isValid() && sourceLayer )
643 {
644 if ( ( match.hasVertex() || match.hasLineEndpoint() ) )
645 {
646 if ( sourceLayer->crs() != vlayer->crs() )
647 {
648 return 1;
649 }
650 QgsFeature f;
651 QgsFeatureRequest request;
652 request.setFilterFid( match.featureId() );
653 const bool fetched = match.layer()->getFeatures( request ).nextFeature( f );
654 if ( fetched )
655 {
656 QgsVertexId vId;
657 if ( !f.geometry().vertexIdFromVertexNr( match.vertexIndex(), vId ) )
658 {
659 return 2;
660 }
661 layerPoint = f.geometry().constGet()->vertexAt( vId );
662 if ( QgsWkbTypes::hasZ( vlayer->wkbType() ) && !layerPoint.is3D() )
663 layerPoint.addZValue( defaultZValue() );
664 if ( QgsWkbTypes::hasM( vlayer->wkbType() ) && !layerPoint.isMeasure() )
665 layerPoint.addMValue( defaultMValue() );
666
667 // ZM support depends on the target layer
668 if ( !QgsWkbTypes::hasZ( vlayer->wkbType() ) )
669 {
670 layerPoint.dropZValue();
671 }
672
673 if ( !QgsWkbTypes::hasM( vlayer->wkbType() ) )
674 {
675 layerPoint.dropMValue();
676 }
677
678 return 0;
679 }
680 return 2;
681 }
682 else if ( QgsProject::instance()->topologicalEditing() && ( match.hasEdge() || match.hasMiddleSegment() ) )
683 {
684 layerPoint = toLayerCoordinates( vlayer, match.interpolatedPoint( mCanvas->mapSettings().destinationCrs() ) );
685 return 0;
686 }
687 }
688 return 2;
689}
690
692{
693 return addVertex( point, QgsPointLocator::Match() );
694}
695
697{
698 if ( mode() == CaptureNone )
699 {
700 QgsDebugError( QStringLiteral( "invalid capture mode" ) );
701 return 2;
702 }
703
704 if ( mCapturing && mCurrentCaptureTechnique == Qgis::CaptureTechnique::Streaming && !mAllowAddingStreamingPoints )
705 return 0;
706
707 QgsPoint layerPoint;
708 if ( layer() )
709 {
710 int res = fetchLayerPoint( match, layerPoint );
711 if ( res != 0 )
712 {
713 res = nextPoint( QgsPoint( point ), layerPoint );
714 if ( res != 0 )
715 {
716 return res;
717 }
718 }
719 }
720 else
721 {
722 layerPoint = QgsPoint( point );
723 }
724 const QgsPoint mapPoint = toMapCoordinates( layer(), layerPoint );
725
726 if ( mCaptureMode == CapturePoint )
727 {
728 mCaptureCurve.addVertex( layerPoint );
729 mSnappingMatches.append( match );
730 }
731 else
732 {
733 if ( mCaptureFirstPoint.isEmpty() )
734 {
735 mCaptureFirstPoint = mapPoint;
736 }
737
738 if ( !mRubberBand )
740
741 if ( !mTempRubberBand )
742 {
743 mTempRubberBand.reset( createCurveRubberBand() );
744 mTempRubberBand->setStringType( mLineDigitizingType );
745 mTempRubberBand->reset( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line, mLineDigitizingType, mapPoint );
746 }
747
748 bool traceCreated = false;
749 if ( tracingEnabled() )
750 {
751 traceCreated = tracingAddVertex( mapPoint );
752 }
753
754 // keep new tracing start point if we created a trace. This is useful when tracing with
755 // offset so that the user stays "snapped"
756 mTracingStartPoint = traceCreated ? point : QgsPointXY();
757
758 if ( !traceCreated )
759 {
760 // ordinary digitizing
761 mTempRubberBand->movePoint( mapPoint ); //move the last point of the temp rubberband before operating with it
762 if ( mTempRubberBand->curveIsComplete() ) //2 points for line and 3 points for circular
763 {
764 if ( QgsCurve *curve = mTempRubberBand->curve() )
765 {
766 addCurve( curve );
767 // add curve append only invalid match to mSnappingMatches,
768 // so we need to remove them and add the one from here if it is valid
769 if ( match.isValid() && mSnappingMatches.count() > 0 && !mSnappingMatches.last().isValid() )
770 {
771 mSnappingMatches.removeLast();
772 if ( mTempRubberBand->stringType() == Qgis::WkbType::CircularString )
773 {
774 // for circular string two points are added and match for intermediate point is stored
775 mSnappingMatches.removeLast();
776 mSnappingMatches.append( mCircularIntermediateMatch );
777 }
778 mSnappingMatches.append( match );
779 }
780 }
781 mCaptureLastPoint = mapPoint;
782 mTempRubberBand->reset( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line, mLineDigitizingType, mCaptureFirstPoint );
783 }
784 else if ( mTempRubberBand->pointsCount() == 0 )
785 {
786 mCaptureLastPoint = mapPoint;
787 mCaptureCurve.addVertex( layerPoint );
788 mSnappingMatches.append( match );
789 }
790 else
791 {
792 if ( mTempRubberBand->stringType() == Qgis::WkbType::CircularString )
793 {
794 mCircularIntermediateMatch = match;
795 }
796 }
797
798 mTempRubberBand->addPoint( mapPoint );
799 }
800 else
801 {
802 mTempRubberBand->reset( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line, mLineDigitizingType, mCaptureFirstPoint );
803 mTempRubberBand->addPoint( mCaptureLastPoint );
804 }
805 }
806
807 updateExtraSnapLayer();
808 validateGeometry();
809
810 return 0;
811}
812
814{
815 if ( !c )
816 {
817 return 1;
818 }
819
820 if ( !mRubberBand )
821 {
823 }
824
825 if ( mTempRubberBand )
826 {
827 mTempRubberBand->reset( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line, mLineDigitizingType, mCaptureFirstPoint );
828 const QgsPoint endPt = c->endPoint();
829 mTempRubberBand->addPoint( endPt ); //add last point of c
830 }
831
832 const int countBefore = mCaptureCurve.vertexCount();
833 //if there is only one point, this the first digitized point that are in the this first curve added --> remove the point
834 if ( mCaptureCurve.numPoints() == 1 )
835 mCaptureCurve.removeCurve( 0 );
836
837 // Transform back to layer CRS in case map CRS and layer CRS are different
838 const QgsCoordinateTransform ct = mCanvas->mapSettings().layerTransform( layer() );
839 if ( ct.isValid() && !ct.isShortCircuited() )
840 {
841 QgsLineString *segmented = c->curveToLine();
843 // Curve geometries will be converted to segments, so we explicitly set extentPrevious to false
844 // to be able to remove the whole curve in undo
845 mCaptureCurve.addCurve( segmented, false );
846 delete c;
847 }
848 else
849 {
850 // we set the extendPrevious option to true to avoid creating compound curves with many 2 vertex linestrings -- instead we prefer
851 // to extend linestring curves so that they continue the previous linestring wherever possible...
852 mCaptureCurve.addCurve( c, !mStartNewCurve );
853 }
854
855 mStartNewCurve = false;
856
857 const int countAfter = mCaptureCurve.vertexCount();
858 const int addedPoint = countAfter - countBefore;
859
860 updateExtraSnapLayer();
861
862 for ( int i = 0; i < addedPoint; ++i )
863 mSnappingMatches.append( QgsPointLocator::Match() );
864
865 resetRubberBand();
866
867 return 0;
868}
869
871{
872 mCaptureCurve.clear();
873 updateExtraSnapLayer();
874}
875
876QList<QgsPointLocator::Match> QgsMapToolCapture::snappingMatches() const
877{
878 return mSnappingMatches;
879}
880
881void QgsMapToolCapture::undo( bool isAutoRepeat )
882{
883 mTracingStartPoint = QgsPointXY();
884
885 if ( mTempRubberBand )
886 {
887 if ( size() <= 1 && mTempRubberBand->pointsCount() != 0 )
888 return;
889
890 if ( isAutoRepeat && mIgnoreSubsequentAutoRepeatUndo )
891 return;
892 mIgnoreSubsequentAutoRepeatUndo = false;
893
894 const QgsPoint lastPoint = mTempRubberBand->lastPoint();
895
896 if ( mTempRubberBand->stringType() == Qgis::WkbType::CircularString && mTempRubberBand->pointsCount() > 2 )
897 {
898 mTempRubberBand->removeLastPoint();
899 mTempRubberBand->movePoint( lastPoint );
900 return;
901 }
902
903 QgsVertexId vertexToRemove;
904 vertexToRemove.part = 0;
905 vertexToRemove.ring = 0;
906 vertexToRemove.vertex = size() - 1;
907
908 // If the geometry was reprojected, remove the entire last curve.
909 const QgsCoordinateTransform ct = mCanvas->mapSettings().layerTransform( layer() );
910 if ( ct.isValid() && !ct.isShortCircuited() )
911 {
912 mCaptureCurve.removeCurve( mCaptureCurve.nCurves() - 1 );
913 }
914 if ( mCaptureCurve.numPoints() == 2 && mCaptureCurve.nCurves() == 1 )
915 {
916 // store the first vertex to restore if after deleting the curve
917 // because when only two vertices, removing a point remove all the curve
918 const QgsPoint fp = mCaptureCurve.startPoint();
919 mCaptureCurve.deleteVertex( vertexToRemove );
920 mCaptureCurve.addVertex( fp );
921 }
922 else
923 {
924 const int curvesBefore = mCaptureCurve.nCurves();
925 const bool lastCurveIsLineString = qgsgeometry_cast< QgsLineString * >( mCaptureCurve.curveAt( curvesBefore - 1 ) );
926
927 const int pointsCountBefore = mCaptureCurve.numPoints();
928 mCaptureCurve.deleteVertex( vertexToRemove );
929 int pointsCountAfter = mCaptureCurve.numPoints();
930 for ( ; pointsCountAfter < pointsCountBefore; pointsCountAfter++ )
931 if ( !mSnappingMatches.empty() )
932 mSnappingMatches.removeLast();
933
934 // if we have removed the last point in a linestring curve, then we "stick" here and ignore subsequent
935 // autorepeat undo actions until the user releases the undo key and holds it down again. This allows
936 // users to selectively remove portions of the geometry captured with the streaming mode by holding down
937 // the undo key, without risking accidental undo of non-streamed portions.
938 if ( mCaptureCurve.nCurves() < curvesBefore && lastCurveIsLineString )
939 mIgnoreSubsequentAutoRepeatUndo = true;
940 }
941
942 updateExtraSnapLayer();
943
944 resetRubberBand();
945
946 mTempRubberBand->reset( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line, mLineDigitizingType, mCaptureFirstPoint );
947
948 if ( mCaptureCurve.numPoints() > 0 )
949 {
950 const QgsPoint lastPt = mCaptureCurve.endPoint();
951 mCaptureLastPoint = toMapCoordinates( layer(), lastPt );
952 mTempRubberBand->addPoint( mCaptureLastPoint );
953 mTempRubberBand->movePoint( lastPoint );
954 }
955
957 validateGeometry();
958 }
959}
960
962{
963 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape && mCurrentShapeMapTool )
964 {
965 mCurrentShapeMapTool->keyPressEvent( e );
966 if ( e->isAccepted() )
967 return;
968 }
969
970 // this is backwards, but we can't change now without breaking api because
971 // forever QgsMapTools have had to explicitly mark events as ignored in order to
972 // indicate that they've consumed the event and that the default behavior should not
973 // be applied..!
974 // see QgsMapCanvas::keyPressEvent
975 e->accept();
976
977 if ( e->key() == Qt::Key_Backspace || e->key() == Qt::Key_Delete )
978 {
979 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape && mCurrentShapeMapTool )
980 {
981 if ( !e->isAutoRepeat() )
982 {
983 mCurrentShapeMapTool->undo();
984 }
985 }
986 else
987 {
988 undo( e->isAutoRepeat() );
989 }
990
991 // Override default shortcut management in MapCanvas
992 e->ignore();
993 }
994 else if ( e->key() == Qt::Key_Escape )
995 {
996 if ( mCurrentShapeMapTool )
997 mCurrentShapeMapTool->clean();
998
1000
1001 // Override default shortcut management in MapCanvas
1002 e->ignore();
1003 }
1004}
1005
1007{
1008 mCapturing = true;
1009}
1010
1012{
1013 return mCapturing;
1014}
1015
1017{
1018 mRubberBand.reset();
1019
1021
1022 qDeleteAll( mGeomErrorMarkers );
1023 mGeomErrorMarkers.clear();
1024 mGeomErrors.clear();
1025
1026 mCaptureFirstPoint = QgsPoint();
1027 mCaptureLastPoint = QgsPoint();
1028
1029 mTracingStartPoint = QgsPointXY();
1030
1031 mCapturing = false;
1032 mCaptureCurve.clear();
1033 updateExtraSnapLayer();
1034 mSnappingMatches.clear();
1035 if ( auto *lCurrentVectorLayer = currentVectorLayer() )
1036 lCurrentVectorLayer->triggerRepaint();
1037}
1038
1040{
1041 mTempRubberBand.reset();
1042}
1043
1045{
1046 stopCapturing();
1047 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape && mCurrentShapeMapTool )
1048 mCurrentShapeMapTool->clean();
1049
1050 clearCurve();
1051}
1052
1054{
1055 mCaptureCurve.close();
1056 updateExtraSnapLayer();
1057}
1058
1059void QgsMapToolCapture::validateGeometry()
1060{
1063 )
1064 return;
1065
1066 if ( mValidator )
1067 {
1068 mValidator->deleteLater();
1069 mValidator = nullptr;
1070 }
1071
1072 mGeomErrors.clear();
1073 while ( !mGeomErrorMarkers.isEmpty() )
1074 {
1075 delete mGeomErrorMarkers.takeFirst();
1076 }
1077
1078 QgsGeometry geom;
1079
1080 switch ( mCaptureMode )
1081 {
1082 case CaptureNone:
1083 case CapturePoint:
1084 return;
1085 case CaptureLine:
1086 if ( size() < 2 )
1087 return;
1088 geom = QgsGeometry( mCaptureCurve.curveToLine() );
1089 break;
1090 case CapturePolygon:
1091 if ( size() < 3 )
1092 return;
1093 QgsLineString *exteriorRing = mCaptureCurve.curveToLine();
1094 exteriorRing->close();
1095 QgsPolygon *polygon = new QgsPolygon();
1096 polygon->setExteriorRing( exteriorRing );
1097 geom = QgsGeometry( polygon );
1098 break;
1099 }
1100
1101 if ( geom.isNull() )
1102 return;
1103
1107 mValidator = new QgsGeometryValidator( geom, nullptr, method );
1108 connect( mValidator, &QgsGeometryValidator::errorFound, this, &QgsMapToolCapture::addError );
1109 mValidator->start();
1110 QgsDebugMsgLevel( QStringLiteral( "Validation started" ), 4 );
1111}
1112
1113void QgsMapToolCapture::addError( const QgsGeometry::Error &e )
1114{
1115 mGeomErrors << e;
1116 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() );
1117 if ( !vlayer )
1118 return;
1119
1120 if ( e.hasWhere() )
1121 {
1123 vm->setCenter( mCanvas->mapSettings().layerToMapCoordinates( vlayer, e.where() ) );
1125 vm->setPenWidth( 2 );
1126 vm->setToolTip( e.what() );
1127 vm->setColor( Qt::green );
1128 vm->setZValue( vm->zValue() + 1 );
1129 mGeomErrorMarkers << vm;
1130 }
1131}
1132
1134{
1135 return mCaptureCurve.numPoints();
1136}
1137
1138QVector<QgsPointXY> QgsMapToolCapture::points() const
1139{
1140 QVector<QgsPointXY> pointsXY;
1142
1143 return pointsXY;
1144}
1145
1147{
1148 QgsPointSequence pts;
1149 mCaptureCurve.points( pts );
1150 return pts;
1151}
1152
1153void QgsMapToolCapture::setPoints( const QVector<QgsPointXY> &pointList )
1154{
1155 QgsLineString *line = new QgsLineString( pointList );
1156 mCaptureCurve.clear();
1157 mCaptureCurve.addCurve( line );
1158 updateExtraSnapLayer();
1159 mSnappingMatches.clear();
1160 for ( int i = 0; i < line->length(); ++i )
1161 mSnappingMatches.append( QgsPointLocator::Match() );
1162 resetRubberBand();
1163}
1164
1166{
1167 QgsLineString *line = new QgsLineString( pointList );
1168 mCaptureCurve.clear();
1169 mCaptureCurve.addCurve( line );
1170 updateExtraSnapLayer();
1171 mSnappingMatches.clear();
1172 for ( int i = 0; i < line->length(); ++i )
1173 mSnappingMatches.append( QgsPointLocator::Match() );
1174 resetRubberBand();
1175}
1176
1178{
1179 QgsPoint newPoint( Qgis::WkbType::Point, point.x(), point.y() );
1180
1181 // get current layer
1182 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() );
1183 if ( !vlayer )
1184 {
1185 return newPoint;
1186 }
1187
1188 // convert to the corresponding type for a full ZM support
1189 const Qgis::WkbType type = vlayer->wkbType();
1190 if ( QgsWkbTypes::hasZ( type ) && !QgsWkbTypes::hasM( type ) )
1191 {
1192 newPoint.convertTo( Qgis::WkbType::PointZ );
1193 }
1194 else if ( !QgsWkbTypes::hasZ( type ) && QgsWkbTypes::hasM( type ) )
1195 {
1196 newPoint.convertTo( Qgis::WkbType::PointM );
1197 }
1198 else if ( QgsWkbTypes::hasZ( type ) && QgsWkbTypes::hasM( type ) )
1199 {
1201 }
1202
1203 // set z value if necessary
1204 if ( QgsWkbTypes::hasZ( newPoint.wkbType() ) )
1205 {
1207 }
1208 // set m value if necessary
1209 if ( QgsWkbTypes::hasM( newPoint.wkbType() ) )
1210 {
1212 }
1213 return newPoint;
1214}
1215
1217{
1218 QgsPoint newPoint = mapPoint( e.mapPoint() );
1219
1220 // set z or m value from snapped point if necessary
1221 if ( QgsWkbTypes::hasZ( newPoint.wkbType() ) || QgsWkbTypes::hasM( newPoint.wkbType() ) )
1222 {
1223 // if snapped, z and m dimension are taken from the corresponding snapped
1224 // point.
1225 if ( e.isSnapped() )
1226 {
1227 const QgsPointLocator::Match match = e.mapPointMatch();
1228
1229 if ( match.layer() )
1230 {
1231 const QgsFeature ft = match.layer()->getFeature( match.featureId() );
1232 if ( QgsWkbTypes::hasZ( match.layer()->wkbType() ) )
1233 {
1234 newPoint.setZ( ft.geometry().vertexAt( match.vertexIndex() ).z() );
1235 }
1236 if ( QgsWkbTypes::hasM( match.layer()->wkbType() ) )
1237 {
1238 newPoint.setM( ft.geometry().vertexAt( match.vertexIndex() ).m() );
1239 }
1240 }
1241 }
1242 }
1243
1244 return newPoint;
1245}
1246
1247void QgsMapToolCapture::updateExtraSnapLayer()
1248{
1249 if ( !mExtraSnapLayer )
1250 return;
1251
1252 if ( canvas()->snappingUtils()->config().selfSnapping() && layer() && mCaptureCurve.numPoints() >= 2 )
1253 {
1254 // the current layer may have changed
1255 mExtraSnapLayer->setCrs( layer()->crs() );
1256 QgsGeometry geom = QgsGeometry( mCaptureCurve.clone() );
1257 // we close the curve to allow snapping on last segment
1258 if ( mCaptureMode == CapturePolygon && mCaptureCurve.numPoints() >= 3 )
1259 {
1260 qgsgeometry_cast<QgsCompoundCurve *>( geom.get() )->close();
1261 }
1262 mExtraSnapLayer->changeGeometry( mExtraSnapFeatureId, geom );
1263 }
1264 else
1265 {
1266 QgsGeometry geom;
1267 mExtraSnapLayer->changeGeometry( mExtraSnapFeatureId, geom );
1268 }
1269}
1270
1271
1273{
1274 // POINT CAPTURING
1275 if ( mode() == CapturePoint )
1276 {
1277 if ( e->button() != Qt::LeftButton )
1278 return;
1279
1280 QgsPoint savePoint; //point in layer coordinates
1281 bool isMatchPointZ = false;
1282 bool isMatchPointM = false;
1283 try
1284 {
1285 QgsPoint fetchPoint;
1286 int res = fetchLayerPoint( e->mapPointMatch(), fetchPoint );
1287 isMatchPointZ = QgsWkbTypes::hasZ( fetchPoint.wkbType() );
1288 isMatchPointM = QgsWkbTypes::hasM( fetchPoint.wkbType() );
1289
1290 if ( res == 0 )
1291 {
1293 if ( isMatchPointM && isMatchPointZ )
1294 {
1295 geomType = Qgis::WkbType::PointZM;
1296 }
1297 else if ( isMatchPointM )
1298 {
1299 geomType = Qgis::WkbType::PointM;
1300 }
1301 else if ( isMatchPointZ )
1302 {
1303 geomType = Qgis::WkbType::PointZ;
1304 }
1305 savePoint = QgsPoint( geomType, fetchPoint.x(), fetchPoint.y(), fetchPoint.z(), fetchPoint.m() );
1306 }
1307 else
1308 {
1309 QgsPointXY point = mCanvas->mapSettings().mapToLayerCoordinates( layer(), e->mapPoint() );
1310
1311 savePoint = QgsPoint( point.x(), point.y(), fetchPoint.z(), fetchPoint.m() );
1312 }
1313 }
1314 catch ( QgsCsException &cse )
1315 {
1316 Q_UNUSED( cse )
1317 emit messageEmitted( tr( "Cannot transform the point to the layer's coordinate system" ), Qgis::MessageLevel::Warning );
1318 return;
1319 }
1320
1321 QgsGeometry g( std::make_unique<QgsPoint>( savePoint ) );
1322
1323 // The snapping result needs to be added so it's available in the @snapping_results variable of default value etc. expression contexts
1324 addVertex( e->mapPoint(), e->mapPointMatch() );
1325
1326 geometryCaptured( g );
1327 pointCaptured( savePoint );
1328
1329 stopCapturing();
1330
1331 // we are done with digitizing for now so instruct advanced digitizing dock to reset its CAD points
1333 }
1334
1335 // LINE AND POLYGON CAPTURING
1336 else if ( mode() == CaptureLine || mode() == CapturePolygon )
1337 {
1338 bool digitizingFinished = false;
1339
1340 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape )
1341 {
1342 if ( !mCurrentShapeMapTool )
1343 {
1344 emit messageEmitted( tr( "Select an option from the Shape Digitizing Toolbar in order to capture shapes" ), Qgis::MessageLevel::Warning );
1345 return;
1346 }
1347 else
1348 {
1349 if ( !mTempRubberBand )
1350 {
1351 mTempRubberBand.reset( createCurveRubberBand() );
1352 mTempRubberBand->setStringType( mLineDigitizingType );
1353 mTempRubberBand->setRubberBandGeometryType( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line );
1354 }
1355
1356 digitizingFinished = mCurrentShapeMapTool->cadCanvasReleaseEvent( e, mCaptureMode );
1357 if ( digitizingFinished )
1358 mCurrentShapeMapTool->clean();
1359 }
1360 }
1361 else // i.e. not shape
1362 {
1363 //add point to list and to rubber band
1364 if ( e->button() == Qt::LeftButton )
1365 {
1366 const int error = addVertex( e->mapPoint(), e->mapPointMatch() );
1367 if ( error == 2 )
1368 {
1369 //problem with coordinate transformation
1370 emit messageEmitted( tr( "Cannot transform the point to the layers coordinate system" ), Qgis::MessageLevel::Warning );
1371 return;
1372 }
1373
1375 }
1376 else if ( e->button() == Qt::RightButton )
1377 {
1378 // End of string
1380
1381 //lines: bail out if there are not at least two vertices
1382 if ( mode() == CaptureLine && size() < 2 )
1383 {
1384 stopCapturing();
1385 return;
1386 }
1387
1388 //polygons: bail out if there are not at least two vertices
1389 if ( mode() == CapturePolygon && size() < 3 )
1390 {
1391 stopCapturing();
1392 return;
1393 }
1394
1395 if ( mode() == CapturePolygon || e->modifiers() == Qt::ShiftModifier )
1396 {
1397 closePolygon();
1398 }
1399
1400 digitizingFinished = true;
1401 }
1402 }
1403
1404 if ( digitizingFinished )
1405 {
1406 QgsGeometry g;
1407 std::unique_ptr<QgsCurve> curveToAdd( captureCurve()->clone() );
1408
1409 if ( mode() == CaptureLine )
1410 {
1411 g = QgsGeometry( curveToAdd->clone() );
1412 geometryCaptured( g );
1413 lineCaptured( curveToAdd.release() );
1414 }
1415 else
1416 {
1417
1418 //does compoundcurve contain circular strings?
1419 //does provider support circular strings?
1420 if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() ) )
1421 {
1422 const bool hasCurvedSegments = captureCurve()->hasCurvedSegments();
1423 const bool providerSupportsCurvedSegments = vlayer->dataProvider()->capabilities() & Qgis::VectorProviderCapability::CircularGeometries;
1424
1425 if ( hasCurvedSegments && providerSupportsCurvedSegments )
1426 {
1427 curveToAdd.reset( captureCurve()->clone() );
1428 }
1429 else
1430 {
1431 curveToAdd.reset( captureCurve()->curveToLine() );
1432 }
1433 }
1434 else
1435 {
1436 curveToAdd.reset( captureCurve()->clone() );
1437 }
1438 std::unique_ptr<QgsCurvePolygon> poly{new QgsCurvePolygon()};
1439 poly->setExteriorRing( curveToAdd.release() );
1440 g = QgsGeometry( poly->clone() );
1441 geometryCaptured( g );
1442 polygonCaptured( poly.get() );
1443 }
1444
1445 stopCapturing();
1446 }
1447 }
1448}
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.
bool isSpatial() const FINAL
Returns true if this is a geometry layer and false in case of NoGeometry (table only) or UnknownGeome...
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:6571
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