QGIS API Documentation 3.99.0-Master (d270888f95f)
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
18#include <algorithm>
19#include <memory>
20
23#include "qgsapplication.h"
24#include "qgsexception.h"
25#include "qgsfeatureiterator.h"
27#include "qgslinestring.h"
28#include "qgslogger.h"
29#include "qgsmapcanvas.h"
30#include "qgsmapcanvastracer.h"
31#include "qgsmapmouseevent.h"
35#include "qgsnurbscurve.h"
36#include "qgspolygon.h"
37#include "qgsproject.h"
38#include "qgsrubberband.h"
40#include "qgssnapindicator.h"
41#include "qgssnappingutils.h"
42#include "qgsvectorlayer.h"
43#include "qgsvertexmarker.h"
44
45#include <QAction>
46#include <QCursor>
47#include <QPixmap>
48#include <QStatusBar>
49#include <QString>
50
51#include "moc_qgsmaptoolcapture.cpp"
52
53using namespace Qt::StringLiterals;
54
57 , mCaptureMode( mode )
58 , mCaptureModeFromLayer( mode == CaptureNone )
59{
60 mTempRubberBand.setParentOwner( canvas );
61
62 mSnapIndicator = std::make_unique<QgsSnapIndicator>( canvas );
63
65
66 connect( canvas, &QgsMapCanvas::currentLayerChanged, this, &QgsMapToolCapture::currentLayerChanged );
67
69 layerOptions.skipCrsValidation = true;
70 layerOptions.loadDefaultStyle = false;
71 mExtraSnapLayer = new QgsVectorLayer( u"LineString?crs="_s, u"extra snap"_s, u"memory"_s, layerOptions );
72 mExtraSnapLayer->startEditing();
73 QgsFeature f;
74 mExtraSnapLayer->addFeature( f );
75 mExtraSnapFeatureId = f.id();
76
77 connect( QgsProject::instance(), &QgsProject::snappingConfigChanged, this, &QgsMapToolCapture::updateExtraSnapLayer );
78
79 currentLayerChanged( canvas->currentLayer() );
80}
81
83{
84 // during tear down we have to clean up mExtraSnapLayer first, before
85 // we call stop capturing. Otherwise stopCapturing tries to access members
86 // from the mapcanvas, which is likely already being destroyed and triggering
87 // the deletion of this object...
88 if ( mCanvas )
89 {
90 mCanvas->snappingUtils()->removeExtraSnapLayer( mExtraSnapLayer );
91 }
92 mExtraSnapLayer->deleteLater();
93 mExtraSnapLayer = nullptr;
94
96
97 if ( mValidator )
98 {
99 mValidator->deleteLater();
100 mValidator = nullptr;
101 }
102}
103
108
123
125{
126 if ( mTempRubberBand )
127 mTempRubberBand->show();
128
129 mCanvas->snappingUtils()->addExtraSnapLayer( mExtraSnapLayer );
131
132 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape && mCurrentShapeMapTool )
133 {
134 setCurrentShapeMapToolIsActivated( true );
135 }
136}
137
139{
140 if ( mTempRubberBand )
141 mTempRubberBand->hide();
142
143 mSnapIndicator->setMatch( QgsPointLocator::Match() );
144
145 mCanvas->snappingUtils()->removeExtraSnapLayer( mExtraSnapLayer );
146
147 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape && mCurrentShapeMapTool )
148 {
149 setCurrentShapeMapToolIsActivated( false );
150 }
151
153}
154
155void QgsMapToolCapture::currentLayerChanged( QgsMapLayer *layer )
156{
157 if ( !mCaptureModeFromLayer )
158 return;
159
160 mCaptureMode = CaptureNone;
161
162 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
163 if ( !vlayer )
164 {
165 return;
166 }
167
168 if ( vlayer->isSpatial() )
169 {
171 }
172 else
173 {
174 setCursor( QCursor( Qt::ArrowCursor ) );
175 }
176
177 switch ( vlayer->geometryType() )
178 {
180 mCaptureMode = CapturePoint;
181 break;
183 mCaptureMode = CaptureLine;
184 break;
186 mCaptureMode = CapturePolygon;
187 break;
188 default:
189 mCaptureMode = CaptureNone;
190 break;
191 }
192
193 if ( mTempRubberBand )
194 mTempRubberBand->setRubberBandGeometryType( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line );
195
196 resetRubberBand();
198}
199
200
201bool QgsMapToolCapture::tracingEnabled()
202{
203 QgsMapCanvasTracer *tracer = QgsMapCanvasTracer::tracerForCanvas( mCanvas );
204 return tracer && ( !tracer->actionEnableTracing() || tracer->actionEnableTracing()->isChecked() )
205 && ( !tracer->actionEnableSnapping() || tracer->actionEnableSnapping()->isChecked() );
206}
207
208
209QgsPointXY QgsMapToolCapture::tracingStartPoint()
210{
211 // if we have starting point from previous trace, then preferably use that one
212 // (useful when tracing with offset)
213 if ( mTracingStartPoint != QgsPointXY() )
214 return mTracingStartPoint;
215
216 return mCaptureLastPoint;
217}
218
219
220bool QgsMapToolCapture::tracingMouseMove( QgsMapMouseEvent *e )
221{
222 if ( !e->isSnapped() )
223 return false;
224
225 QgsPointXY pt0 = tracingStartPoint();
226 if ( pt0 == QgsPointXY() )
227 return false;
228
229 QgsMapCanvasTracer *tracer = QgsMapCanvasTracer::tracerForCanvas( mCanvas );
230 if ( !tracer )
231 return false; // this should not happen!
232
234 QVector<QgsPointXY> points = tracer->findShortestPath( pt0, e->mapPoint(), &err );
235 if ( points.isEmpty() )
236 {
237 tracer->reportError( err, false );
238 return false;
239 }
240
241 mTempRubberBand->reset( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line, Qgis::WkbType::LineString, mCaptureFirstPoint );
242 mTempRubberBand->addPoint( mCaptureLastPoint );
243
244 // if there is offset, we need to fix the rubber bands to make sure they are aligned correctly.
245 // There are two cases we need to sort out:
246 // 1. the last point of mRubberBand may need to be moved off the traced curve to respect the offset
247 // 2. first point of mTempRubberBand may be needed to be moved to the beginning of the offset trace
248 const QgsPoint lastPoint = mCaptureLastPoint;
249 QgsPointXY lastPointXY( lastPoint );
250 if ( lastPointXY == pt0 && points[0] != lastPointXY )
251 {
252 if ( mRubberBand->numberOfVertices() != 0 )
253 {
254 // if rubber band had just one point, for some strange reason it contains the point twice
255 // we only want to move the last point if there are multiple points already
256 if ( mRubberBand->numberOfVertices() > 2 || ( mRubberBand->numberOfVertices() == 2 && *mRubberBand->getPoint( 0, 0 ) != *mRubberBand->getPoint( 0, 1 ) ) )
257 mRubberBand->movePoint( points[0] );
258 }
259
260 mTempRubberBand->movePoint( 0, QgsPoint( points[0] ) );
261 }
262
263 mTempRubberBand->movePoint( QgsPoint( points[0] ) );
264
265 // update temporary rubberband
266 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
267 mTempRubberBand->addPoint( QgsPoint( points.at( i ) ), i == points.count() - 1 );
268
269
270 mTempRubberBand->addPoint( QgsPoint( points[points.size() - 1] ) );
271
272 tracer->reportError( QgsTracer::ErrNone, false ); // clear messagebar if there was any error
273
274 QgsCoordinateReferenceSystem targetCrs = mCanvas->mapSettings().destinationCrs();
275 if ( QgsMapLayer *l = layer() )
276 {
277 // if we have a layer, then the geometry will be in the layer's CRS, not the canvas'
278 targetCrs = l->crs();
279 }
280
281 std::unique_ptr< QgsCompoundCurve > tempCurve( mCaptureCurve.clone() );
282 try
283 {
284 std::unique_ptr< QgsCurve > tracedCurve( mTempRubberBand->curve() );
285 tracedCurve->transform( QgsCoordinateTransform( mCanvas->mapSettings().destinationCrs(), targetCrs, QgsProject::instance()->transformContext() ) );
286 tempCurve->addCurve( tracedCurve.release() );
287 if ( mCaptureMode == CapturePolygon )
288 {
289 tempCurve->close();
290 auto curvePolygon = std::make_unique< QgsCurvePolygon >();
291 curvePolygon->setExteriorRing( tempCurve.release() );
292 emit transientGeometryChanged( QgsReferencedGeometry( QgsGeometry( std::move( curvePolygon ) ), targetCrs ) );
293 }
294 else
295 {
296 emit transientGeometryChanged( QgsReferencedGeometry( QgsGeometry( std::move( tempCurve ) ), targetCrs ) );
297 }
298 }
299 catch ( QgsCsException &e )
300 {
301 QgsDebugError( e.what() );
302 }
303
304 return true;
305}
306
307
308bool QgsMapToolCapture::tracingAddVertex( const QgsPointXY &point )
309{
310 QgsMapCanvasTracer *tracer = QgsMapCanvasTracer::tracerForCanvas( mCanvas );
311 if ( !tracer )
312 return false; // this should not happen!
313
314 if ( mTempRubberBand->pointsCount() == 0 )
315 {
316 if ( !tracer->init() )
317 {
319 return false;
320 }
321
322 // only accept first point if it is snapped to the graph (to vertex or edge)
323 const bool res = tracer->isPointSnapped( point );
324 if ( res )
325 {
326 mTracingStartPoint = point;
327 }
328 return false;
329 }
330
331 QgsPointXY pt0 = tracingStartPoint();
332 if ( pt0 == QgsPointXY() )
333 return false;
334
336 const QVector<QgsPointXY> tracedPointsInMapCrs = tracer->findShortestPath( pt0, point, &err );
337 if ( tracedPointsInMapCrs.isEmpty() )
338 return false; // ignore the vertex - can't find path to the end point!
339
340 // transform points
341 QgsPointSequence layerPoints;
342 layerPoints.reserve( tracedPointsInMapCrs.size() );
343 QgsPointSequence mapPoints;
344 mapPoints.reserve( tracedPointsInMapCrs.size() );
345 for ( const QgsPointXY &tracedPointMapCrs : tracedPointsInMapCrs )
346 {
347 QgsPoint mapPoint( tracedPointMapCrs );
348
349 QgsPoint lp; // in layer coords
350 if ( nextPoint( mapPoint, lp ) != 0 )
351 return false;
352
353 // copy z and m from layer point back to mapPoint, as nextPoint() call will populate these based
354 // on the context of the trace
355 if ( lp.is3D() )
356 mapPoint.addZValue( lp.z() );
357 if ( lp.isMeasure() )
358 mapPoint.addMValue( lp.m() );
359
360 mapPoints << mapPoint;
361 layerPoints << lp;
362 }
363
364 // Move the last point of the captured curve to the first point on the trace string (necessary if there is offset)
365 const QgsVertexId lastVertexId( 0, 0, mCaptureCurve.numPoints() - 1 );
366 mCaptureCurve.moveVertex( lastVertexId, layerPoints.first() );
367 mSnappingMatches.removeLast();
368 mSnappingMatches.append( QgsPointLocator::Match() );
369
370 addCurve( new QgsLineString( mapPoints ) );
371
372 resetRubberBand();
373
374 // Curves de-approximation
376 {
377 // If the tool and the layer support curves
378 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() );
380 {
381 const QgsGeometry linear = QgsGeometry( mCaptureCurve.segmentize() );
382 const QgsGeometry curved = linear.convertToCurves(
385 );
387 {
388 mCaptureCurve.clear();
389 mCaptureCurve.addCurve( qgsgeometry_cast<const QgsCurve *>( curved.constGet() )->clone() );
390 }
391 else
392 {
393 mCaptureCurve = *qgsgeometry_cast<const QgsCompoundCurve *>( curved.constGet() );
394 }
395 }
396
397 mSnappingMatches.resize( mCaptureCurve.numPoints() );
398 }
399
400 tracer->reportError( QgsTracer::ErrNone, true ); // clear messagebar if there was any error
401
402 // adjust last captured point
403 const QgsPoint lastPt = mCaptureCurve.endPoint();
404 mCaptureLastPoint = toMapCoordinates( layer(), lastPt );
405
406 return true;
407}
408
409QgsMapToolCaptureRubberBand *QgsMapToolCapture::createCurveRubberBand() const
410{
411 QgsMapToolCaptureRubberBand *rb = new QgsMapToolCaptureRubberBand( mCanvas );
412 rb->setStrokeWidth( digitizingStrokeWidth() );
413 QColor color = digitizingStrokeColor();
414
416 color.setAlphaF( color.alphaF() * alphaScale );
417 rb->setLineStyle( Qt::DotLine );
418 rb->setStrokeColor( color );
419
420 const QColor fillColor = digitizingFillColor();
421 rb->setFillColor( fillColor );
422 rb->show();
423 return rb;
424}
425
426void QgsMapToolCapture::resetRubberBand()
427{
428 if ( !mRubberBand )
429 return;
430 QgsLineString *lineString = mCaptureCurve.curveToLine();
431
432 mRubberBand->reset( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line );
433 mRubberBand->addGeometry( QgsGeometry( lineString ), layer() );
434}
435
436void QgsMapToolCapture::setCurrentShapeMapToolIsActivated( bool activated )
437{
438 // suppress false positive clang tidy warning
439 // NOLINTBEGIN(bugprone-branch-clone)
440 if ( activated )
441 {
443 mCurrentShapeMapTool->activate( mCaptureMode, mCaptureLastPoint );
444 }
445 else
446 {
448 mCurrentShapeMapTool->deactivate();
449 }
450 // NOLINTEND(bugprone-branch-clone)
451}
452
454{
455 return mRubberBand.release();
456}
457
465
473
475{
476 if ( mCurrentCaptureTechnique == technique )
477 return;
478
479 mStartNewCurve = true;
480
481 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape && mCurrentShapeMapTool )
482 {
483 setCurrentShapeMapToolIsActivated( false );
484 clean();
485 }
486
487 switch ( technique )
488 {
490 mLineDigitizingType = Qgis::WkbType::LineString;
491 break;
493 mLineDigitizingType = Qgis::WkbType::CircularString;
494 break;
496 mLineDigitizingType = Qgis::WkbType::LineString;
497 mStreamingToleranceInPixels = QgsSettingsRegistryCore::settingsDigitizingStreamTolerance->value();
498 break;
500 mLineDigitizingType = Qgis::WkbType::LineString;
501 break;
503 mLineDigitizingType = Qgis::WkbType::NurbsCurve;
504 break;
505 }
506
507 if ( mTempRubberBand )
508 mTempRubberBand->setStringType( mLineDigitizingType );
509
510 mCurrentCaptureTechnique = technique;
511
512 if ( technique == Qgis::CaptureTechnique::Shape && mCurrentShapeMapTool && isActive() )
513 {
514 clean();
515 setCurrentShapeMapToolIsActivated( true );
516 }
517}
518
520{
521 if ( mCurrentShapeMapTool )
522 {
523 if ( shapeMapToolMetadata && mCurrentShapeMapTool->id() == shapeMapToolMetadata->id() )
524 return;
525 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape )
526 {
527 setCurrentShapeMapToolIsActivated( false );
528 }
529 mCurrentShapeMapTool->deleteLater();
530 }
531
532 mCurrentShapeMapTool.reset( shapeMapToolMetadata ? shapeMapToolMetadata->factory( this ) : nullptr );
533
534 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape && isActive() )
535 {
536 clean();
537 if ( mCurrentShapeMapTool )
538 {
539 setCurrentShapeMapToolIsActivated( true );
540 }
541 }
542}
543
545{
546 // If we are adding a record to a non-spatial layer, just return
547 if ( mCaptureModeFromLayer && ( !canvas()->currentLayer() || !canvas()->currentLayer()->isSpatial() ) )
548 return;
549
551
552 const QgsPointXY point = e->mapPoint();
553
554 mSnapIndicator->setMatch( e->mapPointMatch() );
555
556 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape )
557 {
558 if ( !mCurrentShapeMapTool )
559 {
560 emit messageEmitted( tr( "Select an option from the Shape Digitizing Toolbar in order to capture shapes" ), Qgis::MessageLevel::Warning );
561 }
562 else
563 {
564 if ( !mTempRubberBand )
565 {
566 mTempRubberBand.reset( createCurveRubberBand() );
567 mTempRubberBand->setStringType( mLineDigitizingType );
568 mTempRubberBand->setRubberBandGeometryType( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line );
569 }
570
571 mCurrentShapeMapTool->cadCanvasMoveEvent( e, mCaptureMode );
572 return;
573 }
574 }
575 else
576 {
577 const QgsPoint mapPoint = QgsPoint( point );
578
579 QgsCoordinateReferenceSystem targetCrs = mCanvas->mapSettings().destinationCrs();
580 if ( QgsMapLayer *l = layer() )
581 {
582 // if we have a layer, then the geometry will be in the layer's CRS, not the canvas'
583 targetCrs = l->crs();
584 }
585
586 if ( mCaptureMode != CapturePoint && mTempRubberBand && mCapturing )
587 {
588 bool hasTrace = false;
589
590 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Streaming )
591 {
592 if ( !mCaptureCurve.isEmpty() )
593 {
594 const QgsPoint prevPoint = mCaptureCurve.curveAt( mCaptureCurve.nCurves() - 1 )->endPoint();
595 if ( QgsPointXY( toCanvasCoordinates( toMapCoordinates( layer(), prevPoint ) ) ).distance( toCanvasCoordinates( point ) ) < mStreamingToleranceInPixels )
596 return;
597 }
598
599 mAllowAddingStreamingPoints = true;
601 mAllowAddingStreamingPoints = false;
602
603 std::unique_ptr< QgsCompoundCurve > tempCurve( mCaptureCurve.clone() );
604 if ( mCaptureMode == CapturePolygon )
605 {
606 auto curvePolygon = std::make_unique< QgsCurvePolygon >();
607 tempCurve->close();
608 curvePolygon->setExteriorRing( tempCurve.release() );
609 emit transientGeometryChanged( QgsReferencedGeometry( QgsGeometry( std::move( curvePolygon ) ), targetCrs ) );
610 }
611 else
612 {
613 emit transientGeometryChanged( QgsReferencedGeometry( QgsGeometry( std::move( tempCurve ) ), targetCrs ) );
614 }
615 }
616 else if ( tracingEnabled() && mCaptureCurve.numPoints() != 0 )
617 {
618 // Store the intermediate point for circular string to retrieve after tracing mouse move if
619 // the digitizing type is circular and the temp rubber band is effectively circular and if this point is existing
620 // Store an empty point if the digitizing type is linear ot the point is not existing (curve not complete)
621 if ( mLineDigitizingType == Qgis::WkbType::CircularString && mTempRubberBand->stringType() == Qgis::WkbType::CircularString && mTempRubberBand->curveIsComplete() )
622 mCircularItermediatePoint = mTempRubberBand->pointFromEnd( 1 );
623 else if ( mLineDigitizingType == Qgis::WkbType::LineString || !mTempRubberBand->curveIsComplete() )
624 mCircularItermediatePoint = QgsPoint();
625
626 hasTrace = tracingMouseMove( e );
627
628 if ( !hasTrace )
629 {
630 // Restore the temp rubber band
631 mTempRubberBand->reset( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line, mLineDigitizingType, mCaptureFirstPoint );
632 mTempRubberBand->addPoint( mCaptureLastPoint );
633 if ( !mCircularItermediatePoint.isEmpty() )
634 {
635 mTempRubberBand->movePoint( mCircularItermediatePoint );
636 mTempRubberBand->addPoint( mCircularItermediatePoint );
637 }
638 }
639 }
640
641 if ( mCurrentCaptureTechnique != Qgis::CaptureTechnique::Streaming && !hasTrace )
642 {
643 if ( mCaptureCurve.numPoints() > 0 )
644 {
645 const QgsPoint mapPt = mCaptureLastPoint;
646
647 if ( mTempRubberBand )
648 {
649 mTempRubberBand->movePoint( mapPoint );
650 mTempRubberBand->movePoint( 0, mapPt );
651 }
652
653 // fix existing rubber band after tracing - the last point may have been moved if using offset
654 if ( mRubberBand->numberOfVertices() )
655 mRubberBand->movePoint( mapPt );
656
657 std::unique_ptr< QgsCompoundCurve > tempCurve( mCaptureCurve.clone() );
658
659 // add mouse hover point to current captured geometry
660 try
661 {
662 QgsPoint hoverPointTargetCrs = mapPoint;
663 hoverPointTargetCrs.transform( QgsCoordinateTransform( mCanvas->mapSettings().destinationCrs(), targetCrs, QgsProject::instance()->transformContext() ) );
664 tempCurve->addCurve( new QgsLineString( tempCurve->endPoint(), hoverPointTargetCrs ) );
665 }
666 catch ( QgsCsException &e )
667 {
668 QgsDebugError( e.what() );
669 }
670
671 if ( mCaptureMode == CapturePolygon )
672 {
673 auto curvePolygon = std::make_unique< QgsCurvePolygon >();
674 tempCurve->close();
675 curvePolygon->setExteriorRing( tempCurve.release() );
676 emit transientGeometryChanged( QgsReferencedGeometry( QgsGeometry( std::move( curvePolygon ) ), targetCrs ) );
677 }
678 else
679 {
680 emit transientGeometryChanged( QgsReferencedGeometry( QgsGeometry( std::move( tempCurve ) ), targetCrs ) );
681 }
682 }
683 else if ( mTempRubberBand )
684 mTempRubberBand->movePoint( mapPoint );
685 }
686 }
687 }
688} // mouseMoveEvent
689
690
692{
693 if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() ) )
694 {
695 try
696 {
697 QgsPointXY mapP( mapPoint.x(), mapPoint.y() ); //#spellok
698 const bool is3D = layerPoint.is3D();
699 const bool isMeasure = layerPoint.isMeasure();
700 mapP = toLayerCoordinates( vlayer, mapP ); //transform snapped point back to layer crs //#spellok
701 layerPoint = QgsPoint( layerPoint.wkbType(), mapP.x(), mapP.y(), layerPoint.z(), layerPoint.m() ); //#spellok
702 if ( QgsWkbTypes::hasZ( vlayer->wkbType() ) && !is3D )
703 layerPoint.addZValue( mCadDockWidget && mCadDockWidget->cadEnabled() ? mCadDockWidget->currentPointV2().z() : defaultZValue() );
704 if ( QgsWkbTypes::hasM( vlayer->wkbType() ) && !isMeasure )
705 layerPoint.addMValue( mCadDockWidget && mCadDockWidget->cadEnabled() ? mCadDockWidget->currentPointV2().m() : defaultMValue() );
706 }
707 catch ( QgsCsException & )
708 {
709 QgsDebugError( u"transformation to layer coordinate failed"_s );
710 return 2;
711 }
712 }
713 else
714 {
715 layerPoint = QgsPoint( toLayerCoordinates( layer(), mapPoint ) );
716 }
717
718 return 0;
719}
720
722{
724 return nextPoint( mapPoint, layerPoint );
725}
726
728{
729 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() );
730 QgsVectorLayer *sourceLayer = match.layer();
731 if ( mCadDockWidget && mCadDockWidget->cadEnabled() )
732 {
733 layerPoint = mCadDockWidget->currentPointLayerCoordinates( layer() );
734 return 0;
735 }
736 else if ( !vlayer )
737 {
738 return 1;
739 }
740
741 if ( match.isValid() && sourceLayer )
742 {
743 if ( ( match.hasVertex() || match.hasLineEndpoint() ) )
744 {
745 if ( sourceLayer->crs() != vlayer->crs() )
746 {
747 layerPoint = match.interpolatedPoint();
748 return 1;
749 }
750 QgsFeature f;
751 QgsFeatureRequest request;
752 request.setFilterFid( match.featureId() );
753 const bool fetched = match.layer()->getFeatures( request ).nextFeature( f );
754 if ( fetched )
755 {
756 QgsVertexId vId;
757 if ( !f.geometry().vertexIdFromVertexNr( match.vertexIndex(), vId ) )
758 {
759 return 2;
760 }
761 layerPoint = f.geometry().constGet()->vertexAt( vId );
762 if ( QgsWkbTypes::hasZ( vlayer->wkbType() ) && !layerPoint.is3D() )
763 layerPoint.addZValue( defaultZValue() );
764 if ( QgsWkbTypes::hasM( vlayer->wkbType() ) && !layerPoint.isMeasure() )
765 layerPoint.addMValue( defaultMValue() );
766
767 // ZM support depends on the target layer
768 if ( !QgsWkbTypes::hasZ( vlayer->wkbType() ) )
769 {
770 layerPoint.dropZValue();
771 }
772
773 if ( !QgsWkbTypes::hasM( vlayer->wkbType() ) )
774 {
775 layerPoint.dropMValue();
776 }
777
778 return 0;
779 }
780 return 2;
781 }
782 else if ( QgsProject::instance()->topologicalEditing() && ( match.hasEdge() || match.hasMiddleSegment() ) )
783 {
784 layerPoint = toLayerCoordinates( vlayer, match.interpolatedPoint( mCanvas->mapSettings().destinationCrs() ) );
785 return 0;
786 }
787 }
788 return 2;
789}
790
792{
793 return addVertex( point, QgsPointLocator::Match() );
794}
795
797{
798 if ( mode() == CaptureNone )
799 {
800 QgsDebugError( u"invalid capture mode"_s );
801 return 2;
802 }
803
804 if ( mCapturing && mCurrentCaptureTechnique == Qgis::CaptureTechnique::Streaming && !mAllowAddingStreamingPoints )
805 return 0;
806
807 QgsPoint layerPoint;
808 if ( layer() )
809 {
810 int res = fetchLayerPoint( match, layerPoint );
811 if ( res != 0 )
812 {
813 res = nextPoint( QgsPoint( point ), layerPoint );
814 if ( res != 0 )
815 {
816 return res;
817 }
818 }
819 }
820 else
821 {
822 layerPoint = QgsPoint( point );
823 }
824 const QgsPoint mapPoint = toMapCoordinates( layer(), layerPoint );
825
826 if ( mCaptureMode == CapturePoint )
827 {
828 mCaptureCurve.addVertex( layerPoint );
829 mSnappingMatches.append( match );
830 }
831 else
832 {
833 if ( mCaptureFirstPoint.isEmpty() )
834 {
835 mCaptureFirstPoint = mapPoint;
836 }
837
838 if ( !mRubberBand )
840
841 if ( !mTempRubberBand )
842 {
843 mTempRubberBand.reset( createCurveRubberBand() );
844 mTempRubberBand->setStringType( mLineDigitizingType );
845 mTempRubberBand->reset( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line, mLineDigitizingType, mapPoint );
846 }
847
848 bool traceCreated = false;
849 if ( tracingEnabled() )
850 {
851 traceCreated = tracingAddVertex( mapPoint );
852 }
853
854 // keep new tracing start point if we created a trace. This is useful when tracing with
855 // offset so that the user stays "snapped"
856 mTracingStartPoint = traceCreated ? point : QgsPointXY();
857
858 if ( !traceCreated )
859 {
860 // ordinary digitizing
861 mTempRubberBand->movePoint( mapPoint ); //move the last point of the temp rubberband before operating with it
862 if ( mTempRubberBand->curveIsComplete() ) //2 points for line and 3 points for circular
863 {
864 if ( QgsCurve *curve = mTempRubberBand->curve() )
865 {
866 addCurve( curve );
867 // add curve append only invalid match to mSnappingMatches,
868 // so we need to remove them and add the one from here if it is valid
869 if ( match.isValid() && mSnappingMatches.count() > 0 && !mSnappingMatches.last().isValid() )
870 {
871 mSnappingMatches.removeLast();
872 if ( mTempRubberBand->stringType() == Qgis::WkbType::CircularString )
873 {
874 // for circular string two points are added and match for intermediate point is stored
875 mSnappingMatches.removeLast();
876 mSnappingMatches.append( mCircularIntermediateMatch );
877 }
878 mSnappingMatches.append( match );
879 }
880 }
881 mCaptureLastPoint = mapPoint;
882 mTempRubberBand->reset( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line, mLineDigitizingType, mCaptureFirstPoint );
883 }
884 else if ( mTempRubberBand->pointsCount() == 0 )
885 {
886 mCaptureLastPoint = mapPoint;
887 mCaptureCurve.addVertex( layerPoint );
888 mSnappingMatches.append( match );
889 }
890 else
891 {
892 if ( mTempRubberBand->stringType() == Qgis::WkbType::CircularString )
893 {
894 mCircularIntermediateMatch = match;
895 }
896 }
897
898 mTempRubberBand->addPoint( mapPoint );
899 }
900 else
901 {
902 mTempRubberBand->reset( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line, mLineDigitizingType, mCaptureFirstPoint );
903 mTempRubberBand->addPoint( mCaptureLastPoint );
904 }
905 }
906
907 updateExtraSnapLayer();
908 validateGeometry();
909
910 return 0;
911}
912
914{
915 if ( !c )
916 {
917 return 1;
918 }
919
920 if ( !mRubberBand )
921 {
923 }
924
925 if ( mTempRubberBand )
926 {
927 mTempRubberBand->reset( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line, mLineDigitizingType, mCaptureFirstPoint );
928 const QgsPoint endPt = c->endPoint();
929 mTempRubberBand->addPoint( endPt ); //add last point of c
930 }
931
932 const int countBefore = mCaptureCurve.vertexCount();
933 //if there is only one point, this the first digitized point that are in the this first curve added --> remove the point
934 if ( mCaptureCurve.numPoints() == 1 )
935 mCaptureCurve.removeCurve( 0 );
936
937 // Transform back to layer CRS in case map CRS and layer CRS are different
938 const QgsCoordinateTransform ct = mCanvas->mapSettings().layerTransform( layer() );
939 if ( ct.isValid() && !ct.isShortCircuited() )
940 {
941 QgsLineString *segmented = c->curveToLine();
943 // Curve geometries will be converted to segments, so we explicitly set extentPrevious to false
944 // to be able to remove the whole curve in undo
945 mCaptureCurve.addCurve( segmented, false );
946 delete c;
947 }
948 else
949 {
950 // we set the extendPrevious option to true to avoid creating compound curves with many 2 vertex linestrings -- instead we prefer
951 // to extend linestring curves so that they continue the previous linestring wherever possible...
952 mCaptureCurve.addCurve( c, !mStartNewCurve );
953 }
954
955 mStartNewCurve = false;
956
957 const int countAfter = mCaptureCurve.vertexCount();
958 const int addedPoint = countAfter - countBefore;
959
960 updateExtraSnapLayer();
961
962 for ( int i = 0; i < addedPoint; ++i )
963 mSnappingMatches.append( QgsPointLocator::Match() );
964
965 resetRubberBand();
966
967 return 0;
968}
969
971{
972 mCaptureCurve.clear();
973 updateExtraSnapLayer();
974}
975
976QList<QgsPointLocator::Match> QgsMapToolCapture::snappingMatches() const
977{
978 return mSnappingMatches;
979}
980
981void QgsMapToolCapture::undo( bool isAutoRepeat )
982{
983 mTracingStartPoint = QgsPointXY();
984
985 if ( mTempRubberBand )
986 {
987 if ( size() <= 1 && mTempRubberBand->pointsCount() != 0 )
988 return;
989
990 if ( isAutoRepeat && mIgnoreSubsequentAutoRepeatUndo )
991 return;
992 mIgnoreSubsequentAutoRepeatUndo = false;
993
994 const QgsPoint lastPoint = mTempRubberBand->lastPoint();
995
996 if ( mTempRubberBand->stringType() == Qgis::WkbType::CircularString && mTempRubberBand->pointsCount() > 2 )
997 {
998 mTempRubberBand->removeLastPoint();
999 mTempRubberBand->movePoint( lastPoint );
1000 return;
1001 }
1002
1003 // Handle NURBS ControlPoints mode: remove last control point
1004 if ( QgsWkbTypes::isNurbsType( mTempRubberBand->stringType() ) && mTempRubberBand->pointsCount() > 1 )
1005 {
1006 mTempRubberBand->removeLastPoint();
1007 mTempRubberBand->movePoint( lastPoint );
1008 mCadDockWidget->removePreviousPoint();
1009 return;
1010 }
1011
1012 QgsVertexId vertexToRemove;
1013 vertexToRemove.part = 0;
1014 vertexToRemove.ring = 0;
1015 vertexToRemove.vertex = size() - 1;
1016
1017 // If the geometry was reprojected, remove the entire last curve.
1018 const QgsCoordinateTransform ct = mCanvas->mapSettings().layerTransform( layer() );
1019 if ( ct.isValid() && !ct.isShortCircuited() )
1020 {
1021 mCaptureCurve.removeCurve( mCaptureCurve.nCurves() - 1 );
1022 }
1023 if ( mCaptureCurve.numPoints() == 2 && mCaptureCurve.nCurves() == 1 )
1024 {
1025 // store the first vertex to restore if after deleting the curve
1026 // because when only two vertices, removing a point remove all the curve
1027 const QgsPoint fp = mCaptureCurve.startPoint();
1028 mCaptureCurve.deleteVertex( vertexToRemove );
1029 mCaptureCurve.addVertex( fp );
1030 }
1031 else
1032 {
1033 const int curvesBefore = mCaptureCurve.nCurves();
1034 const bool lastCurveIsLineString = qgsgeometry_cast<const QgsLineString *>( mCaptureCurve.curveAt( curvesBefore - 1 ) );
1035
1036 const int pointsCountBefore = mCaptureCurve.numPoints();
1037 mCaptureCurve.deleteVertex( vertexToRemove );
1038 int pointsCountAfter = mCaptureCurve.numPoints();
1039 for ( ; pointsCountAfter < pointsCountBefore; pointsCountAfter++ )
1040 if ( !mSnappingMatches.empty() )
1041 mSnappingMatches.removeLast();
1042
1043 // if we have removed the last point in a linestring curve, then we "stick" here and ignore subsequent
1044 // autorepeat undo actions until the user releases the undo key and holds it down again. This allows
1045 // users to selectively remove portions of the geometry captured with the streaming mode by holding down
1046 // the undo key, without risking accidental undo of non-streamed portions.
1047 if ( mCaptureCurve.nCurves() < curvesBefore && lastCurveIsLineString )
1048 mIgnoreSubsequentAutoRepeatUndo = true;
1049 }
1050
1051 updateExtraSnapLayer();
1052
1053 resetRubberBand();
1054
1055 mTempRubberBand->reset( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line, mLineDigitizingType, mCaptureFirstPoint );
1056
1057 if ( mCaptureCurve.numPoints() > 0 )
1058 {
1059 const QgsPoint lastPt = mCaptureCurve.endPoint();
1060 mCaptureLastPoint = toMapCoordinates( layer(), lastPt );
1061 mTempRubberBand->addPoint( mCaptureLastPoint );
1062 mTempRubberBand->movePoint( lastPoint );
1063 }
1064
1065 mCadDockWidget->removePreviousPoint();
1066 validateGeometry();
1067 }
1068}
1069
1071{
1072 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape && mCurrentShapeMapTool )
1073 {
1074 mCurrentShapeMapTool->keyPressEvent( e );
1075 if ( e->isAccepted() )
1076 return;
1077 }
1078
1079 // this is backwards, but we can't change now without breaking api because
1080 // forever QgsMapTools have had to explicitly mark events as ignored in order to
1081 // indicate that they've consumed the event and that the default behavior should not
1082 // be applied..!
1083 // see QgsMapCanvas::keyPressEvent
1084 e->accept();
1085
1086 if ( e->key() == Qt::Key_Backspace || e->key() == Qt::Key_Delete )
1087 {
1088 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape && mCurrentShapeMapTool )
1089 {
1090 if ( !e->isAutoRepeat() )
1091 {
1092 mCurrentShapeMapTool->undo();
1093 }
1094 }
1095 else
1096 {
1097 undo( e->isAutoRepeat() );
1098 }
1099
1100 // Override default shortcut management in MapCanvas
1101 e->ignore();
1102 }
1103 else if ( e->key() == Qt::Key_Escape )
1104 {
1105 if ( mCurrentShapeMapTool )
1106 mCurrentShapeMapTool->clean();
1107
1108 stopCapturing();
1109
1110 // Override default shortcut management in MapCanvas
1111 e->ignore();
1112 }
1113 else if ( e->key() == Qt::Key_W && !e->isAutoRepeat() )
1114 {
1115 // Enable NURBS weight editing mode when W is pressed
1116 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::NurbsCurve && mTempRubberBand && mTempRubberBand->pointsCount() >= 2 )
1117 {
1118 mWeightEditMode = true;
1119 // Edit the last control point by default (the one being digitized)
1120 mWeightEditControlPointIndex = mTempRubberBand->pointsCount() - 2; // -2 because last point is the cursor position
1121
1122 // Enable and update weight via CAD dock widget (which will notify the floater)
1123 if ( cadDockWidget() )
1124 {
1125 cadDockWidget()->setWeight( QString::number( mTempRubberBand->weight( mWeightEditControlPointIndex ), 'f', 2 ), true );
1126 }
1127 e->ignore();
1128 }
1129 }
1130}
1131
1133{
1134 if ( e->key() == Qt::Key_W && !e->isAutoRepeat() )
1135 {
1136 if ( mWeightEditMode )
1137 {
1138 mWeightEditMode = false;
1139 mWeightEditControlPointIndex = -1;
1140
1141 // Disable weight editing via CAD dock widget
1142 if ( cadDockWidget() )
1143 {
1144 cadDockWidget()->setWeight( QString(), false );
1145 }
1146
1147 e->accept();
1148 return;
1149 }
1150 }
1151
1153}
1154
1155void QgsMapToolCapture::wheelEvent( QWheelEvent *e )
1156{
1157 if ( mWeightEditMode )
1158 {
1159 // Adjust weight with mouse wheel
1160 // Base adjustment: 0.1 per wheel step
1161 // Ctrl modifier: fine adjustment (0.01 per step)
1162 // Shift modifier: coarse adjustment (1.0 per step)
1163 double adjustment = e->angleDelta().y() > 0 ? 0.1 : -0.1;
1164 if ( e->modifiers() & Qt::ControlModifier )
1165 adjustment *= 0.1;
1166 else if ( e->modifiers() & Qt::ShiftModifier )
1167 adjustment *= 10.0;
1168
1169 const double currentWeight = mTempRubberBand->weight( mWeightEditControlPointIndex );
1170 const double newWeight = std::max( 0.01, currentWeight + adjustment );
1171
1172 if ( mTempRubberBand->setWeight( mWeightEditControlPointIndex, newWeight ) )
1173 {
1174 if ( cadDockWidget() )
1175 {
1176 cadDockWidget()->setWeight( QString::number( newWeight, 'f', 2 ), true );
1177 }
1178 }
1179
1180 e->accept();
1181 return;
1182 }
1183
1185}
1186
1188{
1189 mCapturing = true;
1190}
1191
1193{
1194 return mCapturing;
1195}
1196
1198{
1199 mRubberBand.reset();
1200
1202
1203 // Reset weight editing mode when stopping capture
1204 if ( mWeightEditMode )
1205 {
1206 mWeightEditMode = false;
1207 mWeightEditControlPointIndex = -1;
1208 if ( cadDockWidget() )
1209 {
1210 cadDockWidget()->setWeight( QString(), false );
1211 }
1212 }
1213
1214 qDeleteAll( mGeomErrorMarkers );
1215 mGeomErrorMarkers.clear();
1216 mGeomErrors.clear();
1217
1218 mCaptureFirstPoint = QgsPoint();
1219 mCaptureLastPoint = QgsPoint();
1220
1221 mTracingStartPoint = QgsPointXY();
1222
1223 mCapturing = false;
1224 mCaptureCurve.clear();
1225 updateExtraSnapLayer();
1226 mSnappingMatches.clear();
1227 if ( auto *lCurrentVectorLayer = currentVectorLayer() )
1228 lCurrentVectorLayer->triggerRepaint();
1229}
1230
1232{
1233 mTempRubberBand.reset();
1234}
1235
1237{
1238 stopCapturing();
1239 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape && mCurrentShapeMapTool )
1240 mCurrentShapeMapTool->clean();
1241
1242 clearCurve();
1243}
1244
1246{
1247 mCaptureCurve.close();
1248 updateExtraSnapLayer();
1249}
1250
1251void QgsMapToolCapture::validateGeometry()
1252{
1254 || !( capabilities() & ValidateGeometries ) )
1255 return;
1256
1257 if ( mValidator )
1258 {
1259 mValidator->deleteLater();
1260 mValidator = nullptr;
1261 }
1262
1263 mGeomErrors.clear();
1264 while ( !mGeomErrorMarkers.isEmpty() )
1265 {
1266 delete mGeomErrorMarkers.takeFirst();
1267 }
1268
1269 QgsGeometry geom;
1270
1271 switch ( mCaptureMode )
1272 {
1273 case CaptureNone:
1274 case CapturePoint:
1275 return;
1276 case CaptureLine:
1277 if ( size() < 2 )
1278 return;
1279 geom = QgsGeometry( mCaptureCurve.curveToLine() );
1280 break;
1281 case CapturePolygon:
1282 if ( size() < 3 )
1283 return;
1284 QgsLineString *exteriorRing = mCaptureCurve.curveToLine();
1285 exteriorRing->close();
1286 QgsPolygon *polygon = new QgsPolygon();
1287 polygon->setExteriorRing( exteriorRing );
1288 geom = QgsGeometry( polygon );
1289 break;
1290 }
1291
1292 if ( geom.isNull() )
1293 return;
1294
1298 mValidator = new QgsGeometryValidator( geom, nullptr, method );
1299 connect( mValidator, &QgsGeometryValidator::errorFound, this, &QgsMapToolCapture::addError );
1300 mValidator->start();
1301 QgsDebugMsgLevel( u"Validation started"_s, 4 );
1302}
1303
1304void QgsMapToolCapture::addError( const QgsGeometry::Error &e )
1305{
1306 mGeomErrors << e;
1307 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() );
1308 if ( !vlayer )
1309 return;
1310
1311 if ( e.hasWhere() )
1312 {
1313 QgsVertexMarker *vm = new QgsVertexMarker( mCanvas );
1314 vm->setCenter( mCanvas->mapSettings().layerToMapCoordinates( vlayer, e.where() ) );
1316 vm->setPenWidth( 2 );
1317 vm->setToolTip( e.what() );
1318 vm->setColor( Qt::green );
1319 vm->setZValue( vm->zValue() + 1 );
1320 mGeomErrorMarkers << vm;
1321 }
1322}
1323
1325{
1326 return mCaptureCurve.numPoints();
1327}
1328
1329QVector<QgsPointXY> QgsMapToolCapture::points() const
1330{
1331 QVector<QgsPointXY> pointsXY;
1333
1334 return pointsXY;
1335}
1336
1338{
1339 QgsPointSequence pts;
1340 mCaptureCurve.points( pts );
1341 return pts;
1342}
1343
1344void QgsMapToolCapture::setPoints( const QVector<QgsPointXY> &pointList )
1345{
1346 QgsLineString *line = new QgsLineString( pointList );
1347 mCaptureCurve.clear();
1348 mCaptureCurve.addCurve( line );
1349 updateExtraSnapLayer();
1350 mSnappingMatches.clear();
1351 for ( int i = 0; i < line->length(); ++i )
1352 mSnappingMatches.append( QgsPointLocator::Match() );
1353 resetRubberBand();
1354}
1355
1357{
1358 QgsLineString *line = new QgsLineString( pointList );
1359 mCaptureCurve.clear();
1360 mCaptureCurve.addCurve( line );
1361 updateExtraSnapLayer();
1362 mSnappingMatches.clear();
1363 for ( int i = 0; i < line->length(); ++i )
1364 mSnappingMatches.append( QgsPointLocator::Match() );
1365 resetRubberBand();
1366}
1367
1369{
1370 QgsPoint newPoint( Qgis::WkbType::Point, point.x(), point.y() );
1371
1372 // get current layer
1373 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() );
1374 if ( !vlayer )
1375 {
1376 return newPoint;
1377 }
1378
1379 // convert to the corresponding type for a full ZM support
1380 const Qgis::WkbType type = vlayer->wkbType();
1381 if ( QgsWkbTypes::hasZ( type ) && !QgsWkbTypes::hasM( type ) )
1382 {
1383 newPoint.convertTo( Qgis::WkbType::PointZ );
1384 }
1385 else if ( !QgsWkbTypes::hasZ( type ) && QgsWkbTypes::hasM( type ) )
1386 {
1387 newPoint.convertTo( Qgis::WkbType::PointM );
1388 }
1389 else if ( QgsWkbTypes::hasZ( type ) && QgsWkbTypes::hasM( type ) )
1390 {
1392 }
1393
1394 // set z value if necessary
1395 if ( QgsWkbTypes::hasZ( newPoint.wkbType() ) )
1396 {
1397 newPoint.setZ( mCadDockWidget && mCadDockWidget->cadEnabled() ? mCadDockWidget->getLineZ() : defaultZValue() );
1398 }
1399 // set m value if necessary
1400 if ( QgsWkbTypes::hasM( newPoint.wkbType() ) )
1401 {
1402 newPoint.setM( mCadDockWidget && mCadDockWidget->cadEnabled() ? mCadDockWidget->getLineM() : defaultMValue() );
1403 }
1404 return newPoint;
1405}
1406
1408{
1409 QgsPoint newPoint = mapPoint( e.mapPoint() );
1410
1411 // set z or m value from snapped point if necessary
1412 if ( QgsWkbTypes::hasZ( newPoint.wkbType() ) || QgsWkbTypes::hasM( newPoint.wkbType() ) )
1413 {
1414 // if snapped, z and m dimension are taken from the corresponding snapped
1415 // point.
1416 if ( e.isSnapped() )
1417 {
1418 const QgsPointLocator::Match match = e.mapPointMatch();
1419
1420 if ( match.layer() )
1421 {
1422 const QgsFeature ft = match.layer()->getFeature( match.featureId() );
1423 if ( QgsWkbTypes::hasZ( match.layer()->wkbType() ) )
1424 {
1425 newPoint.setZ( ft.geometry().vertexAt( match.vertexIndex() ).z() );
1426 }
1427 if ( QgsWkbTypes::hasM( match.layer()->wkbType() ) )
1428 {
1429 newPoint.setM( ft.geometry().vertexAt( match.vertexIndex() ).m() );
1430 }
1431 }
1432 }
1433 }
1434
1435 return newPoint;
1436}
1437
1438void QgsMapToolCapture::updateExtraSnapLayer()
1439{
1440 if ( !mExtraSnapLayer )
1441 return;
1442
1443 if ( canvas()->snappingUtils()->config().selfSnapping() && layer() && mCaptureCurve.numPoints() >= 2 )
1444 {
1445 // the current layer may have changed
1446 mExtraSnapLayer->setCrs( layer()->crs() );
1447 QgsGeometry geom = QgsGeometry( mCaptureCurve.clone() );
1448 // we close the curve to allow snapping on last segment
1449 if ( mCaptureMode == CapturePolygon && mCaptureCurve.numPoints() >= 3 )
1450 {
1451 qgsgeometry_cast<QgsCompoundCurve *>( geom.get() )->close();
1452 }
1453 mExtraSnapLayer->changeGeometry( mExtraSnapFeatureId, geom );
1454 }
1455 else
1456 {
1457 QgsGeometry geom;
1458 mExtraSnapLayer->changeGeometry( mExtraSnapFeatureId, geom );
1459 }
1460}
1461
1462
1464{
1465 // POINT CAPTURING
1466 if ( mode() == CapturePoint )
1467 {
1468 if ( e->button() != Qt::LeftButton )
1469 return;
1470
1471 QgsPoint savePoint; //point in layer coordinates
1472 bool isMatchPointZ = false;
1473 bool isMatchPointM = false;
1474 try
1475 {
1476 QgsPoint fetchPoint;
1477 int res = fetchLayerPoint( e->mapPointMatch(), fetchPoint );
1478 isMatchPointZ = QgsWkbTypes::hasZ( fetchPoint.wkbType() );
1479 isMatchPointM = QgsWkbTypes::hasM( fetchPoint.wkbType() );
1480
1481 if ( res == 0 )
1482 {
1484 if ( isMatchPointM && isMatchPointZ )
1485 {
1486 geomType = Qgis::WkbType::PointZM;
1487 }
1488 else if ( isMatchPointM )
1489 {
1490 geomType = Qgis::WkbType::PointM;
1491 }
1492 else if ( isMatchPointZ )
1493 {
1494 geomType = Qgis::WkbType::PointZ;
1495 }
1496 savePoint = QgsPoint( geomType, fetchPoint.x(), fetchPoint.y(), fetchPoint.z(), fetchPoint.m() );
1497 }
1498 else
1499 {
1500 QgsPointXY point = mCanvas->mapSettings().mapToLayerCoordinates( layer(), e->mapPoint() );
1501
1502 savePoint = QgsPoint( point.x(), point.y(), fetchPoint.z(), fetchPoint.m() );
1503 }
1504 }
1505 catch ( QgsCsException &cse )
1506 {
1507 Q_UNUSED( cse )
1508 emit messageEmitted( tr( "Cannot transform the point to the layer's coordinate system" ), Qgis::MessageLevel::Warning );
1509 return;
1510 }
1511
1512 QgsGeometry g( std::make_unique<QgsPoint>( savePoint ) );
1513
1514 // The snapping result needs to be added so it's available in the @snapping_results variable of default value etc. expression contexts
1515 addVertex( e->mapPoint(), e->mapPointMatch() );
1516
1517 geometryCaptured( g );
1518 pointCaptured( savePoint );
1519
1520 stopCapturing();
1521
1522 // we are done with digitizing for now so instruct advanced digitizing dock to reset its CAD points
1524 }
1525
1526 // LINE AND POLYGON CAPTURING
1527 else if ( mode() == CaptureLine || mode() == CapturePolygon )
1528 {
1529 bool digitizingFinished = false;
1530 QgsPointSequence nurbsControlPoints;
1531 QVector<double> nurbsWeights;
1532
1533 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape )
1534 {
1535 if ( !mCurrentShapeMapTool )
1536 {
1537 emit messageEmitted( tr( "Select an option from the Shape Digitizing Toolbar in order to capture shapes" ), Qgis::MessageLevel::Warning );
1538 return;
1539 }
1540 else
1541 {
1542 if ( !mTempRubberBand )
1543 {
1544 mTempRubberBand.reset( createCurveRubberBand() );
1545 mTempRubberBand->setStringType( mLineDigitizingType );
1546 mTempRubberBand->setRubberBandGeometryType( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line );
1547 }
1548
1549 digitizingFinished = mCurrentShapeMapTool->cadCanvasReleaseEvent( e, mCaptureMode );
1550 if ( digitizingFinished )
1551 mCurrentShapeMapTool->clean();
1552 }
1553 }
1554 else // i.e. not shape
1555 {
1556 //add point to list and to rubber band
1557 if ( e->button() == Qt::LeftButton )
1558 {
1559 const int error = addVertex( e->mapPoint(), e->mapPointMatch() );
1560 if ( error == 2 )
1561 {
1562 //problem with coordinate transformation
1563 emit messageEmitted( tr( "Cannot transform the point to the layers coordinate system" ), Qgis::MessageLevel::Warning );
1564 return;
1565 }
1566
1568 }
1569 else if ( e->button() == Qt::RightButton )
1570 {
1571 // End of string
1572
1573 // Extract NURBS control points and weights from the rubberband before deleting it
1574 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::NurbsCurve && mTempRubberBand )
1575 {
1576 const int rbPointCount = mTempRubberBand->pointsCount();
1577 if ( rbPointCount > 1 )
1578 {
1579 // Exclude the last point (cursor position)
1580 for ( int i = 0; i < rbPointCount - 1; ++i )
1581 {
1582 nurbsControlPoints.append( mTempRubberBand->pointFromEnd( rbPointCount - 1 - i ) );
1583 }
1584 // Also extract weights (in correct order)
1585 const QVector<double> &rbWeights = mTempRubberBand->weights();
1586 for ( int i = 0; i < rbPointCount - 1; ++i )
1587 {
1588 if ( i < rbWeights.size() )
1589 nurbsWeights.append( rbWeights[i] );
1590 else
1591 nurbsWeights.append( 1.0 );
1592 }
1593 }
1594 }
1595
1597
1598 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::NurbsCurve )
1599 {
1600 // Minimum 4 control points required for degree 3 NURBS
1601 if ( mode() == CaptureLine && nurbsControlPoints.count() < 4 )
1602 {
1603 stopCapturing();
1604 return;
1605 }
1606 if ( mode() == CapturePolygon && nurbsControlPoints.count() < 4 )
1607 {
1608 stopCapturing();
1609 return;
1610 }
1611 }
1612 else
1613 {
1614 //lines: bail out if there are not at least two vertices
1615 if ( mode() == CaptureLine && size() < 2 )
1616 {
1617 stopCapturing();
1618 return;
1619 }
1620
1621 //polygons: bail out if there are not at least two vertices
1622 if ( mode() == CapturePolygon && size() < 3 )
1623 {
1624 stopCapturing();
1625 return;
1626 }
1627 }
1628
1629 if ( mode() == CapturePolygon || e->modifiers() == Qt::ShiftModifier )
1630 {
1631 // Close NURBS curve by adding first control point at the end
1632 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::NurbsCurve && !nurbsControlPoints.isEmpty() )
1633 {
1634 nurbsControlPoints.append( nurbsControlPoints.first() );
1635 if ( !nurbsWeights.isEmpty() )
1636 nurbsWeights.append( nurbsWeights.first() );
1637 }
1638 else
1639 {
1640 closePolygon();
1641 }
1642 }
1643
1644 digitizingFinished = true;
1645 }
1646 }
1647
1648 if ( digitizingFinished )
1649 {
1650 QgsGeometry g;
1651 std::unique_ptr<QgsCurve> curveToAdd;
1652
1653 // Create a single NurbsCurve from all control points
1654 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::NurbsCurve )
1655 {
1656 // Get degree from settings
1658 const int n = nurbsControlPoints.size();
1659
1660 // Adapt degree if not enough control points
1661 if ( n < degree + 1 )
1662 {
1663 degree = std::max( 1, n - 1 );
1664 if ( n < 2 )
1665 {
1666 curveToAdd = std::make_unique<QgsLineString>( nurbsControlPoints );
1667 }
1668 }
1669
1670 if ( !curveToAdd )
1671 {
1672 // Generate uniform clamped knot vector (size = n + degree + 1)
1673 const int knotCount = n + degree + 1;
1674 QVector<double> knots( knotCount );
1675
1676 // First (degree + 1) knots are 0
1677 for ( int i = 0; i <= degree; ++i )
1678 knots[i] = 0.0;
1679
1680 // Last (degree + 1) knots are 1
1681 for ( int i = knotCount - degree - 1; i < knotCount; ++i )
1682 knots[i] = 1.0;
1683
1684 // Middle knots are uniformly spaced
1685 const int numMiddleKnots = n - degree - 1;
1686 for ( int i = 0; i < numMiddleKnots; ++i )
1687 {
1688 knots[degree + 1 + i] = static_cast<double>( i + 1 ) / ( numMiddleKnots + 1 );
1689 }
1690
1691 // Ensure we have the right number of weights
1692 QVector<double> weights = nurbsWeights;
1693 while ( weights.size() < n )
1694 weights.append( 1.0 );
1695 weights.resize( n );
1696
1697 curveToAdd = std::make_unique<QgsNurbsCurve>( nurbsControlPoints, degree, knots, weights );
1698 }
1699 }
1700 else
1701 {
1702 curveToAdd.reset( captureCurve()->clone() );
1703 }
1704
1705 if ( mode() == CaptureLine )
1706 {
1707 g = QgsGeometry( curveToAdd->clone() );
1708 geometryCaptured( g );
1709 lineCaptured( curveToAdd.release() );
1710 }
1711 else
1712 {
1713 // For NURBS curves, keep the already-created curve
1714 // For other curves, check provider support for curved segments
1715 if ( mCurrentCaptureTechnique != Qgis::CaptureTechnique::NurbsCurve )
1716 {
1717 //does compoundcurve contain circular strings?
1718 //does provider support circular strings?
1719 if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() ) )
1720 {
1721 const bool hasCurvedSegments = captureCurve()->hasCurvedSegments();
1722 const bool providerSupportsCurvedSegments = vlayer->dataProvider()->capabilities() & Qgis::VectorProviderCapability::CircularGeometries;
1723
1724 if ( hasCurvedSegments && providerSupportsCurvedSegments )
1725 {
1726 curveToAdd.reset( captureCurve()->clone() );
1727 }
1728 else
1729 {
1730 curveToAdd.reset( captureCurve()->curveToLine() );
1731 }
1732 }
1733 else
1734 {
1735 curveToAdd.reset( captureCurve()->clone() );
1736 }
1737 }
1738 std::unique_ptr<QgsCurvePolygon> poly { new QgsCurvePolygon() };
1739 poly->setExteriorRing( curveToAdd.release() );
1740 g = QgsGeometry( poly->clone() );
1741 geometryCaptured( g );
1742 polygonCaptured( poly.get() );
1743 }
1744
1745 stopCapturing();
1746 }
1747 }
1748}
@ CircularGeometries
Supports circular geometry types (circularstring, compoundcurve, curvepolygon).
Definition qgis.h:533
CaptureTechnique
Capture technique.
Definition qgis.h:404
@ NurbsCurve
Digitizes NURBS curves with control points.
Definition qgis.h:409
@ Shape
Digitize shapes.
Definition qgis.h:408
@ StraightSegments
Default capture mode - capture occurs with straight line segments.
Definition qgis.h:405
@ CircularString
Capture in circular strings.
Definition qgis.h:406
@ Streaming
Streaming points digitizing mode (points are automatically added as the mouse cursor moves).
Definition qgis.h:407
GeometryValidationEngine
Available engines for validating geometries.
Definition qgis.h:2142
@ QgisInternal
Use internal QgsGeometryValidator method.
Definition qgis.h:2143
@ Geos
Use GEOS validation methods.
Definition qgis.h:2144
@ Warning
Warning message.
Definition qgis.h:161
@ Point
Points.
Definition qgis.h:366
@ Line
Lines.
Definition qgis.h:367
@ Polygon
Polygons.
Definition qgis.h:368
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:280
@ CompoundCurve
CompoundCurve.
Definition qgis.h:291
@ Point
Point.
Definition qgis.h:282
@ LineString
LineString.
Definition qgis.h:283
@ NurbsCurve
NurbsCurve.
Definition qgis.h:297
@ PointM
PointM.
Definition qgis.h:315
@ CircularString
CircularString.
Definition qgis.h:290
@ PointZ
PointZ.
Definition qgis.h:299
@ PointZM
PointZM.
Definition qgis.h:331
@ Reverse
Reverse/inverse transform (from destination to source).
Definition qgis.h:2731
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.
A dockable widget used to handle the CAD tools on top of a selection of map tools.
void switchZM()
Determines if Z or M will be enabled.
void setWeight(const QString &value, bool enabled)
Set the weight value for NURBS curves.
void clearPoints()
Removes all points from the CAD point list.
static QCursor getThemeCursor(Cursor cursor)
Helper to get a theme cursor.
@ CapturePoint
Select and capture a point or a feature.
QgsCompoundCurve * clone() const override
Clones the geometry by performing a deep copy.
bool hasCurvedSegments() const override
Returns true if the geometry contains curved segments.
int numPoints() const override
Returns the number of points in the curve.
Represents a coordinate reference system (CRS).
Handles coordinate transforms between two 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.
Abstract base class for curved geometry type.
Definition qgscurve.h:36
QString what() const
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
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:60
QgsFeatureId id
Definition qgsfeature.h:68
QgsGeometry geometry
Definition qgsfeature.h:71
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.
void close()
Closes the line string by appending the first point to the end of the line, if it is not already clos...
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).
void currentLayerChanged(QgsMapLayer *layer)
Emitted when the current layer is changed.
Base class for all map layer types.
Definition qgsmaplayer.h:83
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:90
void setCrs(const QgsCoordinateReferenceSystem &srs, bool emitSignal=true)
Sets layer's spatial reference system.
A mouse event which is the result of a user interaction with 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.
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
QgsMapToolAdvancedDigitizing(QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDockWidget)
Creates an advanced digitizing maptool.
void activate() override
Registers this maptool with the cad dock widget.
void transientGeometryChanged(const QgsReferencedGeometry &geometry)
Emitted whenever the geometry associated with the tool is changed, including transient (i....
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.
void wheelEvent(QWheelEvent *e) override
Handles wheel events for NURBS weight editing.
QgsMapToolCapture(QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDockWidget, CaptureMode mode)
constructor
virtual void geometryCaptured(const QgsGeometry &geometry)
Called when the geometry is captured.
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
virtual void polygonCaptured(const QgsCurvePolygon *polygon)
Called when a polygon is captured.
void closePolygon()
Close an open polygon.
virtual void pointCaptured(const QgsPoint &point)
Called when a point is captured.
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.
void keyReleaseEvent(QKeyEvent *e) override
Handles key release events for NURBS weight editing mode.
QList< QgsPointLocator::Match > snappingMatches() const
Returns a list of matches for each point on the captureCurve.
virtual void lineCaptured(const QgsCurve *line)
Called when a line is captured.
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).
void transientGeometryChanged(const QgsReferencedGeometry &geometry)
Emitted whenever the geometry associated with the tool is changed, including transient (i....
Base class for shape map tools metadata to be used in QgsMapToolShapeRegistry.
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:360
friend class QgsMapCanvas
Definition qgsmaptool.h:380
void messageEmitted(const QString &message, Qgis::MessageLevel level=Qgis::MessageLevel::Info)
Emitted when a message should be shown to the user in the application message bar.
void activated()
Emitted when the map tool is activated.
virtual void keyReleaseEvent(QKeyEvent *e)
Key event for overriding. Default implementation does nothing.
QPoint toCanvasCoordinates(const QgsPointXY &point) const
Transforms a point from map coordinates to screen coordinates.
virtual void wheelEvent(QWheelEvent *e)
Mouse wheel event for overriding. Default implementation does nothing.
bool isActive() const
Returns if the current map tool active on the map canvas.
Represents a 2D point.
Definition qgspointxy.h:62
double y
Definition qgspointxy.h:66
double x
Definition qgspointxy.h:65
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:53
bool addMValue(double mValue=0) override
Adds a measure to the geometry, initialized to a preset value.
Definition qgspoint.cpp:575
bool dropMValue() override
Drops any measure values which exist in the geometry.
Definition qgspoint.cpp:616
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
Definition qgspoint.cpp:564
bool deleteVertex(QgsVertexId position) override
Deletes a vertex within the geometry.
Definition qgspoint.cpp:467
double z
Definition qgspoint.h:58
double x
Definition qgspoint.h:56
void setM(double m)
Sets the point's m-value.
Definition qgspoint.h:369
bool convertTo(Qgis::WkbType type) override
Converts the geometry to a specified type.
Definition qgspoint.cpp:633
void transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection d=Qgis::TransformDirection::Forward, bool transformZ=false) override
Transforms the geometry using a coordinate transform.
Definition qgspoint.cpp:390
void setZ(double z)
Sets the point's z-coordinate.
Definition qgspoint.h:354
bool dropZValue() override
Drops any z-dimensions which exist in the geometry.
Definition qgspoint.cpp:605
double m
Definition qgspoint.h:59
double y
Definition qgspoint.h:57
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.
QgsCoordinateTransformContext transformContext
Definition qgsproject.h:119
A QgsGeometry with associated coordinate reference system.
Responsible for drawing transient features (e.g.
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 QgsSettingsEntryInteger * settingsDigitizingNurbsDegree
Settings entry digitizing NURBS curve degree.
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.
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:133
@ ErrNone
No error.
Definition qgstracer.h:134
@ ErrTooManyFeatures
Max feature count threshold was reached while reading features.
Definition qgstracer.h:135
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 dataset.
bool isSpatial() const final
Returns true if this is a geometry layer and false in case of NoGeometry (table only) or UnknownGeome...
Q_INVOKABLE Qgis::WkbType wkbType() const final
Returns the WKBType or WKBUnknown in case of error.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const final
Queries the layer for features specified in request.
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.
QgsVectorDataProvider * dataProvider() final
Returns the layer's data provider, it may be nullptr.
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 Q_INVOKABLE bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
static Q_INVOKABLE bool hasM(Qgis::WkbType type)
Tests whether a WKB type contains m values.
static Q_INVOKABLE bool isNurbsType(Qgis::WkbType type)
Returns true if the WKB type is a NURBS curve type.
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:7489
T qgsgeometry_cast(QgsAbstractGeometry *geom)
QVector< QgsPoint > QgsPointSequence
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:63
#define QgsDebugError(str)
Definition qgslogger.h:59
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:34
int vertex
Vertex number.
Definition qgsvertexid.h:98
int part
Part number.
Definition qgsvertexid.h:92
int ring
Ring number.
Definition qgsvertexid.h:95