QGIS API Documentation 3.41.0-Master (d2aaa9c6e02)
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{
495 // If we are adding a record to a non-spatial layer, just return
496 if ( mCaptureModeFromLayer && !canvas()->currentLayer()->isSpatial() )
497 return;
498
500
501 const QgsPointXY point = e->mapPoint();
502
503 mSnapIndicator->setMatch( e->mapPointMatch() );
504
505 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape )
506 {
507 if ( !mCurrentShapeMapTool )
508 {
509 emit messageEmitted( tr( "Select an option from the Shape Digitizing Toolbar in order to capture shapes" ), Qgis::MessageLevel::Warning );
510 }
511 else
512 {
513 if ( !mTempRubberBand )
514 {
515 mTempRubberBand.reset( createCurveRubberBand() );
516 mTempRubberBand->setStringType( mLineDigitizingType );
517 mTempRubberBand->setRubberBandGeometryType( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line );
518 }
519
520 mCurrentShapeMapTool->cadCanvasMoveEvent( e, mCaptureMode );
521 return;
522 }
523 }
524 else
525 {
526 const QgsPoint mapPoint = QgsPoint( point );
527
528 if ( mCaptureMode != CapturePoint && mTempRubberBand && mCapturing )
529 {
530 bool hasTrace = false;
531
532 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Streaming )
533 {
534 if ( !mCaptureCurve.isEmpty() )
535 {
536 const QgsPoint prevPoint = mCaptureCurve.curveAt( mCaptureCurve.nCurves() - 1 )->endPoint();
537 if ( QgsPointXY( toCanvasCoordinates( toMapCoordinates( layer(), prevPoint ) ) ).distance( toCanvasCoordinates( point ) ) < mStreamingToleranceInPixels )
538 return;
539 }
540
541 mAllowAddingStreamingPoints = true;
543 mAllowAddingStreamingPoints = false;
544 }
545 else if ( tracingEnabled() && mCaptureCurve.numPoints() != 0 )
546 {
547 // Store the intermediate point for circular string to retrieve after tracing mouse move if
548 // the digitizing type is circular and the temp rubber band is effectivly circular and if this point is existing
549 // Store an empty point if the digitizing type is linear ot the point is not existing (curve not complete)
550 if ( mLineDigitizingType == Qgis::WkbType::CircularString && mTempRubberBand->stringType() == Qgis::WkbType::CircularString && mTempRubberBand->curveIsComplete() )
551 mCircularItermediatePoint = mTempRubberBand->pointFromEnd( 1 );
552 else if ( mLineDigitizingType == Qgis::WkbType::LineString || !mTempRubberBand->curveIsComplete() )
553 mCircularItermediatePoint = QgsPoint();
554
555 hasTrace = tracingMouseMove( e );
556
557 if ( !hasTrace )
558 {
559 // Restore the temp rubber band
560 mTempRubberBand->reset( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line, mLineDigitizingType, mCaptureFirstPoint );
561 mTempRubberBand->addPoint( mCaptureLastPoint );
562 if ( !mCircularItermediatePoint.isEmpty() )
563 {
564 mTempRubberBand->movePoint( mCircularItermediatePoint );
565 mTempRubberBand->addPoint( mCircularItermediatePoint );
566 }
567 }
568 }
569
570 if ( mCurrentCaptureTechnique != Qgis::CaptureTechnique::Streaming && !hasTrace )
571 {
572 if ( mCaptureCurve.numPoints() > 0 )
573 {
574 const QgsPoint mapPt = mCaptureLastPoint;
575
576 if ( mTempRubberBand )
577 {
578 mTempRubberBand->movePoint( mapPoint );
579 mTempRubberBand->movePoint( 0, mapPt );
580 }
581
582 // fix existing rubber band after tracing - the last point may have been moved if using offset
583 if ( mRubberBand->numberOfVertices() )
584 mRubberBand->movePoint( mapPt );
585 }
586 else if ( mTempRubberBand )
587 mTempRubberBand->movePoint( mapPoint );
588 }
589 }
590 }
591} // mouseMoveEvent
592
593
594int QgsMapToolCapture::nextPoint( const QgsPoint &mapPoint, QgsPoint &layerPoint )
595{
596 if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() ) )
597 {
598 try
599 {
600 const QgsPointXY mapP( mapPoint.x(), mapPoint.y() ); //#spellok
601 layerPoint = QgsPoint( toLayerCoordinates( vlayer, mapP ) ); //transform snapped point back to layer crs //#spellok
602 if ( QgsWkbTypes::hasZ( vlayer->wkbType() ) && !layerPoint.is3D() )
604 if ( QgsWkbTypes::hasM( vlayer->wkbType() ) && !layerPoint.isMeasure() )
606 }
607 catch ( QgsCsException & )
608 {
609 QgsDebugError( QStringLiteral( "transformation to layer coordinate failed" ) );
610 return 2;
611 }
612 }
613 else
614 {
615 layerPoint = QgsPoint( toLayerCoordinates( layer(), mapPoint ) );
616 }
617
618 return 0;
619}
620
621int QgsMapToolCapture::nextPoint( QPoint p, QgsPoint &layerPoint, QgsPoint &mapPoint )
622{
624 return nextPoint( mapPoint, layerPoint );
625}
626
628{
629 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() );
630 QgsVectorLayer *sourceLayer = match.layer();
632 {
634 return 0;
635 }
636 else if ( !vlayer )
637 {
638 return 1;
639 }
640
641 if ( match.isValid() && sourceLayer )
642 {
643 if ( ( match.hasVertex() || match.hasLineEndpoint() ) )
644 {
645 if ( sourceLayer->crs() != vlayer->crs() )
646 {
647 return 1;
648 }
649 QgsFeature f;
650 QgsFeatureRequest request;
651 request.setFilterFid( match.featureId() );
652 const bool fetched = match.layer()->getFeatures( request ).nextFeature( f );
653 if ( fetched )
654 {
655 QgsVertexId vId;
656 if ( !f.geometry().vertexIdFromVertexNr( match.vertexIndex(), vId ) )
657 {
658 return 2;
659 }
660 layerPoint = f.geometry().constGet()->vertexAt( vId );
661 if ( QgsWkbTypes::hasZ( vlayer->wkbType() ) && !layerPoint.is3D() )
662 layerPoint.addZValue( defaultZValue() );
663 if ( QgsWkbTypes::hasM( vlayer->wkbType() ) && !layerPoint.isMeasure() )
664 layerPoint.addMValue( defaultMValue() );
665
666 // ZM support depends on the target layer
667 if ( !QgsWkbTypes::hasZ( vlayer->wkbType() ) )
668 {
669 layerPoint.dropZValue();
670 }
671
672 if ( !QgsWkbTypes::hasM( vlayer->wkbType() ) )
673 {
674 layerPoint.dropMValue();
675 }
676
677 return 0;
678 }
679 return 2;
680 }
681 else if ( QgsProject::instance()->topologicalEditing() && ( match.hasEdge() || match.hasMiddleSegment() ) )
682 {
683 layerPoint = toLayerCoordinates( vlayer, match.interpolatedPoint( mCanvas->mapSettings().destinationCrs() ) );
684 return 0;
685 }
686 }
687 return 2;
688}
689
691{
692 return addVertex( point, QgsPointLocator::Match() );
693}
694
696{
697 if ( mode() == CaptureNone )
698 {
699 QgsDebugError( QStringLiteral( "invalid capture mode" ) );
700 return 2;
701 }
702
703 if ( mCapturing && mCurrentCaptureTechnique == Qgis::CaptureTechnique::Streaming && !mAllowAddingStreamingPoints )
704 return 0;
705
706 QgsPoint layerPoint;
707 if ( layer() )
708 {
709 int res = fetchLayerPoint( match, layerPoint );
710 if ( res != 0 )
711 {
712 res = nextPoint( QgsPoint( point ), layerPoint );
713 if ( res != 0 )
714 {
715 return res;
716 }
717 }
718 }
719 else
720 {
721 layerPoint = QgsPoint( point );
722 }
723 const QgsPoint mapPoint = toMapCoordinates( layer(), layerPoint );
724
725 if ( mCaptureMode == CapturePoint )
726 {
727 mCaptureCurve.addVertex( layerPoint );
728 mSnappingMatches.append( match );
729 }
730 else
731 {
732 if ( mCaptureFirstPoint.isEmpty() )
733 {
734 mCaptureFirstPoint = mapPoint;
735 }
736
737 if ( !mRubberBand )
739
740 if ( !mTempRubberBand )
741 {
742 mTempRubberBand.reset( createCurveRubberBand() );
743 mTempRubberBand->setStringType( mLineDigitizingType );
744 mTempRubberBand->reset( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line, mLineDigitizingType, mapPoint );
745 }
746
747 bool traceCreated = false;
748 if ( tracingEnabled() )
749 {
750 traceCreated = tracingAddVertex( mapPoint );
751 }
752
753 // keep new tracing start point if we created a trace. This is useful when tracing with
754 // offset so that the user stays "snapped"
755 mTracingStartPoint = traceCreated ? point : QgsPointXY();
756
757 if ( !traceCreated )
758 {
759 // ordinary digitizing
760 mTempRubberBand->movePoint( mapPoint ); //move the last point of the temp rubberband before operating with it
761 if ( mTempRubberBand->curveIsComplete() ) //2 points for line and 3 points for circular
762 {
763 if ( QgsCurve *curve = mTempRubberBand->curve() )
764 {
765 addCurve( curve );
766 // add curve append only invalid match to mSnappingMatches,
767 // so we need to remove them and add the one from here if it is valid
768 if ( match.isValid() && mSnappingMatches.count() > 0 && !mSnappingMatches.last().isValid() )
769 {
770 mSnappingMatches.removeLast();
771 if ( mTempRubberBand->stringType() == Qgis::WkbType::CircularString )
772 {
773 // for circular string two points are added and match for intermediate point is stored
774 mSnappingMatches.removeLast();
775 mSnappingMatches.append( mCircularIntermediateMatch );
776 }
777 mSnappingMatches.append( match );
778 }
779 }
780 mCaptureLastPoint = mapPoint;
781 mTempRubberBand->reset( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line, mLineDigitizingType, mCaptureFirstPoint );
782 }
783 else if ( mTempRubberBand->pointsCount() == 0 )
784 {
785 mCaptureLastPoint = mapPoint;
786 mCaptureCurve.addVertex( layerPoint );
787 mSnappingMatches.append( match );
788 }
789 else
790 {
791 if ( mTempRubberBand->stringType() == Qgis::WkbType::CircularString )
792 {
793 mCircularIntermediateMatch = match;
794 }
795 }
796
797 mTempRubberBand->addPoint( mapPoint );
798 }
799 else
800 {
801 mTempRubberBand->reset( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line, mLineDigitizingType, mCaptureFirstPoint );
802 mTempRubberBand->addPoint( mCaptureLastPoint );
803 }
804 }
805
806 updateExtraSnapLayer();
807 validateGeometry();
808
809 return 0;
810}
811
813{
814 if ( !c )
815 {
816 return 1;
817 }
818
819 if ( !mRubberBand )
820 {
822 }
823
824 if ( mTempRubberBand )
825 {
826 mTempRubberBand->reset( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line, mLineDigitizingType, mCaptureFirstPoint );
827 const QgsPoint endPt = c->endPoint();
828 mTempRubberBand->addPoint( endPt ); //add last point of c
829 }
830
831 const int countBefore = mCaptureCurve.vertexCount();
832 //if there is only one point, this the first digitized point that are in the this first curve added --> remove the point
833 if ( mCaptureCurve.numPoints() == 1 )
834 mCaptureCurve.removeCurve( 0 );
835
836 // Transform back to layer CRS in case map CRS and layer CRS are different
837 const QgsCoordinateTransform ct = mCanvas->mapSettings().layerTransform( layer() );
838 if ( ct.isValid() && !ct.isShortCircuited() )
839 {
840 QgsLineString *segmented = c->curveToLine();
842 // Curve geometries will be converted to segments, so we explicitly set extentPrevious to false
843 // to be able to remove the whole curve in undo
844 mCaptureCurve.addCurve( segmented, false );
845 delete c;
846 }
847 else
848 {
849 // we set the extendPrevious option to true to avoid creating compound curves with many 2 vertex linestrings -- instead we prefer
850 // to extend linestring curves so that they continue the previous linestring wherever possible...
851 mCaptureCurve.addCurve( c, !mStartNewCurve );
852 }
853
854 mStartNewCurve = false;
855
856 const int countAfter = mCaptureCurve.vertexCount();
857 const int addedPoint = countAfter - countBefore;
858
859 updateExtraSnapLayer();
860
861 for ( int i = 0; i < addedPoint; ++i )
862 mSnappingMatches.append( QgsPointLocator::Match() );
863
864 resetRubberBand();
865
866 return 0;
867}
868
870{
871 mCaptureCurve.clear();
872 updateExtraSnapLayer();
873}
874
875QList<QgsPointLocator::Match> QgsMapToolCapture::snappingMatches() const
876{
877 return mSnappingMatches;
878}
879
880void QgsMapToolCapture::undo( bool isAutoRepeat )
881{
882 mTracingStartPoint = QgsPointXY();
883
884 if ( mTempRubberBand )
885 {
886 if ( size() <= 1 && mTempRubberBand->pointsCount() != 0 )
887 return;
888
889 if ( isAutoRepeat && mIgnoreSubsequentAutoRepeatUndo )
890 return;
891 mIgnoreSubsequentAutoRepeatUndo = false;
892
893 const QgsPoint lastPoint = mTempRubberBand->lastPoint();
894
895 if ( mTempRubberBand->stringType() == Qgis::WkbType::CircularString && mTempRubberBand->pointsCount() > 2 )
896 {
897 mTempRubberBand->removeLastPoint();
898 mTempRubberBand->movePoint( lastPoint );
899 return;
900 }
901
902 QgsVertexId vertexToRemove;
903 vertexToRemove.part = 0;
904 vertexToRemove.ring = 0;
905 vertexToRemove.vertex = size() - 1;
906
907 // If the geometry was reprojected, remove the entire last curve.
908 const QgsCoordinateTransform ct = mCanvas->mapSettings().layerTransform( layer() );
909 if ( ct.isValid() && !ct.isShortCircuited() )
910 {
911 mCaptureCurve.removeCurve( mCaptureCurve.nCurves() - 1 );
912 }
913 if ( mCaptureCurve.numPoints() == 2 && mCaptureCurve.nCurves() == 1 )
914 {
915 // store the first vertex to restore if after deleting the curve
916 // because when only two vertices, removing a point remove all the curve
917 const QgsPoint fp = mCaptureCurve.startPoint();
918 mCaptureCurve.deleteVertex( vertexToRemove );
919 mCaptureCurve.addVertex( fp );
920 }
921 else
922 {
923 const int curvesBefore = mCaptureCurve.nCurves();
924 const bool lastCurveIsLineString = qgsgeometry_cast<QgsLineString *>( mCaptureCurve.curveAt( curvesBefore - 1 ) );
925
926 const int pointsCountBefore = mCaptureCurve.numPoints();
927 mCaptureCurve.deleteVertex( vertexToRemove );
928 int pointsCountAfter = mCaptureCurve.numPoints();
929 for ( ; pointsCountAfter < pointsCountBefore; pointsCountAfter++ )
930 if ( !mSnappingMatches.empty() )
931 mSnappingMatches.removeLast();
932
933 // if we have removed the last point in a linestring curve, then we "stick" here and ignore subsequent
934 // autorepeat undo actions until the user releases the undo key and holds it down again. This allows
935 // users to selectively remove portions of the geometry captured with the streaming mode by holding down
936 // the undo key, without risking accidental undo of non-streamed portions.
937 if ( mCaptureCurve.nCurves() < curvesBefore && lastCurveIsLineString )
938 mIgnoreSubsequentAutoRepeatUndo = true;
939 }
940
941 updateExtraSnapLayer();
942
943 resetRubberBand();
944
945 mTempRubberBand->reset( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line, mLineDigitizingType, mCaptureFirstPoint );
946
947 if ( mCaptureCurve.numPoints() > 0 )
948 {
949 const QgsPoint lastPt = mCaptureCurve.endPoint();
950 mCaptureLastPoint = toMapCoordinates( layer(), lastPt );
951 mTempRubberBand->addPoint( mCaptureLastPoint );
952 mTempRubberBand->movePoint( lastPoint );
953 }
954
956 validateGeometry();
957 }
958}
959
961{
962 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape && mCurrentShapeMapTool )
963 {
964 mCurrentShapeMapTool->keyPressEvent( e );
965 if ( e->isAccepted() )
966 return;
967 }
968
969 // this is backwards, but we can't change now without breaking api because
970 // forever QgsMapTools have had to explicitly mark events as ignored in order to
971 // indicate that they've consumed the event and that the default behavior should not
972 // be applied..!
973 // see QgsMapCanvas::keyPressEvent
974 e->accept();
975
976 if ( e->key() == Qt::Key_Backspace || e->key() == Qt::Key_Delete )
977 {
978 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape && mCurrentShapeMapTool )
979 {
980 if ( !e->isAutoRepeat() )
981 {
982 mCurrentShapeMapTool->undo();
983 }
984 }
985 else
986 {
987 undo( e->isAutoRepeat() );
988 }
989
990 // Override default shortcut management in MapCanvas
991 e->ignore();
992 }
993 else if ( e->key() == Qt::Key_Escape )
994 {
995 if ( mCurrentShapeMapTool )
996 mCurrentShapeMapTool->clean();
997
999
1000 // Override default shortcut management in MapCanvas
1001 e->ignore();
1002 }
1003}
1004
1006{
1007 mCapturing = true;
1008}
1009
1011{
1012 return mCapturing;
1013}
1014
1016{
1017 mRubberBand.reset();
1018
1020
1021 qDeleteAll( mGeomErrorMarkers );
1022 mGeomErrorMarkers.clear();
1023 mGeomErrors.clear();
1024
1025 mCaptureFirstPoint = QgsPoint();
1026 mCaptureLastPoint = QgsPoint();
1027
1028 mTracingStartPoint = QgsPointXY();
1029
1030 mCapturing = false;
1031 mCaptureCurve.clear();
1032 updateExtraSnapLayer();
1033 mSnappingMatches.clear();
1034 if ( auto *lCurrentVectorLayer = currentVectorLayer() )
1035 lCurrentVectorLayer->triggerRepaint();
1036}
1037
1039{
1040 mTempRubberBand.reset();
1041}
1042
1044{
1045 stopCapturing();
1046 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape && mCurrentShapeMapTool )
1047 mCurrentShapeMapTool->clean();
1048
1049 clearCurve();
1050}
1051
1053{
1054 mCaptureCurve.close();
1055 updateExtraSnapLayer();
1056}
1057
1058void QgsMapToolCapture::validateGeometry()
1059{
1061 || !( capabilities() & ValidateGeometries ) )
1062 return;
1063
1064 if ( mValidator )
1065 {
1066 mValidator->deleteLater();
1067 mValidator = nullptr;
1068 }
1069
1070 mGeomErrors.clear();
1071 while ( !mGeomErrorMarkers.isEmpty() )
1072 {
1073 delete mGeomErrorMarkers.takeFirst();
1074 }
1075
1076 QgsGeometry geom;
1077
1078 switch ( mCaptureMode )
1079 {
1080 case CaptureNone:
1081 case CapturePoint:
1082 return;
1083 case CaptureLine:
1084 if ( size() < 2 )
1085 return;
1086 geom = QgsGeometry( mCaptureCurve.curveToLine() );
1087 break;
1088 case CapturePolygon:
1089 if ( size() < 3 )
1090 return;
1091 QgsLineString *exteriorRing = mCaptureCurve.curveToLine();
1092 exteriorRing->close();
1093 QgsPolygon *polygon = new QgsPolygon();
1094 polygon->setExteriorRing( exteriorRing );
1095 geom = QgsGeometry( polygon );
1096 break;
1097 }
1098
1099 if ( geom.isNull() )
1100 return;
1101
1105 mValidator = new QgsGeometryValidator( geom, nullptr, method );
1106 connect( mValidator, &QgsGeometryValidator::errorFound, this, &QgsMapToolCapture::addError );
1107 mValidator->start();
1108 QgsDebugMsgLevel( QStringLiteral( "Validation started" ), 4 );
1109}
1110
1111void QgsMapToolCapture::addError( const QgsGeometry::Error &e )
1112{
1113 mGeomErrors << e;
1114 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() );
1115 if ( !vlayer )
1116 return;
1117
1118 if ( e.hasWhere() )
1119 {
1121 vm->setCenter( mCanvas->mapSettings().layerToMapCoordinates( vlayer, e.where() ) );
1123 vm->setPenWidth( 2 );
1124 vm->setToolTip( e.what() );
1125 vm->setColor( Qt::green );
1126 vm->setZValue( vm->zValue() + 1 );
1127 mGeomErrorMarkers << vm;
1128 }
1129}
1130
1132{
1133 return mCaptureCurve.numPoints();
1134}
1135
1136QVector<QgsPointXY> QgsMapToolCapture::points() const
1137{
1138 QVector<QgsPointXY> pointsXY;
1140
1141 return pointsXY;
1142}
1143
1145{
1146 QgsPointSequence pts;
1147 mCaptureCurve.points( pts );
1148 return pts;
1149}
1150
1151void QgsMapToolCapture::setPoints( const QVector<QgsPointXY> &pointList )
1152{
1153 QgsLineString *line = new QgsLineString( pointList );
1154 mCaptureCurve.clear();
1155 mCaptureCurve.addCurve( line );
1156 updateExtraSnapLayer();
1157 mSnappingMatches.clear();
1158 for ( int i = 0; i < line->length(); ++i )
1159 mSnappingMatches.append( QgsPointLocator::Match() );
1160 resetRubberBand();
1161}
1162
1164{
1165 QgsLineString *line = new QgsLineString( pointList );
1166 mCaptureCurve.clear();
1167 mCaptureCurve.addCurve( line );
1168 updateExtraSnapLayer();
1169 mSnappingMatches.clear();
1170 for ( int i = 0; i < line->length(); ++i )
1171 mSnappingMatches.append( QgsPointLocator::Match() );
1172 resetRubberBand();
1173}
1174
1176{
1177 QgsPoint newPoint( Qgis::WkbType::Point, point.x(), point.y() );
1178
1179 // get current layer
1180 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() );
1181 if ( !vlayer )
1182 {
1183 return newPoint;
1184 }
1185
1186 // convert to the corresponding type for a full ZM support
1187 const Qgis::WkbType type = vlayer->wkbType();
1188 if ( QgsWkbTypes::hasZ( type ) && !QgsWkbTypes::hasM( type ) )
1189 {
1190 newPoint.convertTo( Qgis::WkbType::PointZ );
1191 }
1192 else if ( !QgsWkbTypes::hasZ( type ) && QgsWkbTypes::hasM( type ) )
1193 {
1194 newPoint.convertTo( Qgis::WkbType::PointM );
1195 }
1196 else if ( QgsWkbTypes::hasZ( type ) && QgsWkbTypes::hasM( type ) )
1197 {
1199 }
1200
1201 // set z value if necessary
1202 if ( QgsWkbTypes::hasZ( newPoint.wkbType() ) )
1203 {
1205 }
1206 // set m value if necessary
1207 if ( QgsWkbTypes::hasM( newPoint.wkbType() ) )
1208 {
1210 }
1211 return newPoint;
1212}
1213
1215{
1216 QgsPoint newPoint = mapPoint( e.mapPoint() );
1217
1218 // set z or m value from snapped point if necessary
1219 if ( QgsWkbTypes::hasZ( newPoint.wkbType() ) || QgsWkbTypes::hasM( newPoint.wkbType() ) )
1220 {
1221 // if snapped, z and m dimension are taken from the corresponding snapped
1222 // point.
1223 if ( e.isSnapped() )
1224 {
1225 const QgsPointLocator::Match match = e.mapPointMatch();
1226
1227 if ( match.layer() )
1228 {
1229 const QgsFeature ft = match.layer()->getFeature( match.featureId() );
1230 if ( QgsWkbTypes::hasZ( match.layer()->wkbType() ) )
1231 {
1232 newPoint.setZ( ft.geometry().vertexAt( match.vertexIndex() ).z() );
1233 }
1234 if ( QgsWkbTypes::hasM( match.layer()->wkbType() ) )
1235 {
1236 newPoint.setM( ft.geometry().vertexAt( match.vertexIndex() ).m() );
1237 }
1238 }
1239 }
1240 }
1241
1242 return newPoint;
1243}
1244
1245void QgsMapToolCapture::updateExtraSnapLayer()
1246{
1247 if ( !mExtraSnapLayer )
1248 return;
1249
1250 if ( canvas()->snappingUtils()->config().selfSnapping() && layer() && mCaptureCurve.numPoints() >= 2 )
1251 {
1252 // the current layer may have changed
1253 mExtraSnapLayer->setCrs( layer()->crs() );
1254 QgsGeometry geom = QgsGeometry( mCaptureCurve.clone() );
1255 // we close the curve to allow snapping on last segment
1256 if ( mCaptureMode == CapturePolygon && mCaptureCurve.numPoints() >= 3 )
1257 {
1258 qgsgeometry_cast<QgsCompoundCurve *>( geom.get() )->close();
1259 }
1260 mExtraSnapLayer->changeGeometry( mExtraSnapFeatureId, geom );
1261 }
1262 else
1263 {
1264 QgsGeometry geom;
1265 mExtraSnapLayer->changeGeometry( mExtraSnapFeatureId, geom );
1266 }
1267}
1268
1269
1271{
1272 // POINT CAPTURING
1273 if ( mode() == CapturePoint )
1274 {
1275 if ( e->button() != Qt::LeftButton )
1276 return;
1277
1278 QgsPoint savePoint; //point in layer coordinates
1279 bool isMatchPointZ = false;
1280 bool isMatchPointM = false;
1281 try
1282 {
1283 QgsPoint fetchPoint;
1284 int res = fetchLayerPoint( e->mapPointMatch(), fetchPoint );
1285 isMatchPointZ = QgsWkbTypes::hasZ( fetchPoint.wkbType() );
1286 isMatchPointM = QgsWkbTypes::hasM( fetchPoint.wkbType() );
1287
1288 if ( res == 0 )
1289 {
1291 if ( isMatchPointM && isMatchPointZ )
1292 {
1293 geomType = Qgis::WkbType::PointZM;
1294 }
1295 else if ( isMatchPointM )
1296 {
1297 geomType = Qgis::WkbType::PointM;
1298 }
1299 else if ( isMatchPointZ )
1300 {
1301 geomType = Qgis::WkbType::PointZ;
1302 }
1303 savePoint = QgsPoint( geomType, fetchPoint.x(), fetchPoint.y(), fetchPoint.z(), fetchPoint.m() );
1304 }
1305 else
1306 {
1307 QgsPointXY point = mCanvas->mapSettings().mapToLayerCoordinates( layer(), e->mapPoint() );
1308
1309 savePoint = QgsPoint( point.x(), point.y(), fetchPoint.z(), fetchPoint.m() );
1310 }
1311 }
1312 catch ( QgsCsException &cse )
1313 {
1314 Q_UNUSED( cse )
1315 emit messageEmitted( tr( "Cannot transform the point to the layer's coordinate system" ), Qgis::MessageLevel::Warning );
1316 return;
1317 }
1318
1319 QgsGeometry g( std::make_unique<QgsPoint>( savePoint ) );
1320
1321 // The snapping result needs to be added so it's available in the @snapping_results variable of default value etc. expression contexts
1322 addVertex( e->mapPoint(), e->mapPointMatch() );
1323
1324 geometryCaptured( g );
1325 pointCaptured( savePoint );
1326
1327 stopCapturing();
1328
1329 // we are done with digitizing for now so instruct advanced digitizing dock to reset its CAD points
1331 }
1332
1333 // LINE AND POLYGON CAPTURING
1334 else if ( mode() == CaptureLine || mode() == CapturePolygon )
1335 {
1336 bool digitizingFinished = false;
1337
1338 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape )
1339 {
1340 if ( !mCurrentShapeMapTool )
1341 {
1342 emit messageEmitted( tr( "Select an option from the Shape Digitizing Toolbar in order to capture shapes" ), Qgis::MessageLevel::Warning );
1343 return;
1344 }
1345 else
1346 {
1347 if ( !mTempRubberBand )
1348 {
1349 mTempRubberBand.reset( createCurveRubberBand() );
1350 mTempRubberBand->setStringType( mLineDigitizingType );
1351 mTempRubberBand->setRubberBandGeometryType( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line );
1352 }
1353
1354 digitizingFinished = mCurrentShapeMapTool->cadCanvasReleaseEvent( e, mCaptureMode );
1355 if ( digitizingFinished )
1356 mCurrentShapeMapTool->clean();
1357 }
1358 }
1359 else // i.e. not shape
1360 {
1361 //add point to list and to rubber band
1362 if ( e->button() == Qt::LeftButton )
1363 {
1364 const int error = addVertex( e->mapPoint(), e->mapPointMatch() );
1365 if ( error == 2 )
1366 {
1367 //problem with coordinate transformation
1368 emit messageEmitted( tr( "Cannot transform the point to the layers coordinate system" ), Qgis::MessageLevel::Warning );
1369 return;
1370 }
1371
1373 }
1374 else if ( e->button() == Qt::RightButton )
1375 {
1376 // End of string
1378
1379 //lines: bail out if there are not at least two vertices
1380 if ( mode() == CaptureLine && size() < 2 )
1381 {
1382 stopCapturing();
1383 return;
1384 }
1385
1386 //polygons: bail out if there are not at least two vertices
1387 if ( mode() == CapturePolygon && size() < 3 )
1388 {
1389 stopCapturing();
1390 return;
1391 }
1392
1393 if ( mode() == CapturePolygon || e->modifiers() == Qt::ShiftModifier )
1394 {
1395 closePolygon();
1396 }
1397
1398 digitizingFinished = true;
1399 }
1400 }
1401
1402 if ( digitizingFinished )
1403 {
1404 QgsGeometry g;
1405 std::unique_ptr<QgsCurve> curveToAdd( captureCurve()->clone() );
1406
1407 if ( mode() == CaptureLine )
1408 {
1409 g = QgsGeometry( curveToAdd->clone() );
1410 geometryCaptured( g );
1411 lineCaptured( curveToAdd.release() );
1412 }
1413 else
1414 {
1415 //does compoundcurve contain circular strings?
1416 //does provider support circular strings?
1417 if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() ) )
1418 {
1419 const bool hasCurvedSegments = captureCurve()->hasCurvedSegments();
1420 const bool providerSupportsCurvedSegments = vlayer->dataProvider()->capabilities() & Qgis::VectorProviderCapability::CircularGeometries;
1421
1422 if ( hasCurvedSegments && providerSupportsCurvedSegments )
1423 {
1424 curveToAdd.reset( captureCurve()->clone() );
1425 }
1426 else
1427 {
1428 curveToAdd.reset( captureCurve()->curveToLine() );
1429 }
1430 }
1431 else
1432 {
1433 curveToAdd.reset( captureCurve()->clone() );
1434 }
1435 std::unique_ptr<QgsCurvePolygon> poly { new QgsCurvePolygon() };
1436 poly->setExteriorRing( curveToAdd.release() );
1437 g = QgsGeometry( poly->clone() );
1438 geometryCaptured( g );
1439 polygonCaptured( poly.get() );
1440 }
1441
1442 stopCapturing();
1443 }
1444 }
1445}
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:6745
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