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