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