QGIS API Documentation 3.99.0-Master (8e76e220402)
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 if ( activated )
439 {
441 mCurrentShapeMapTool->activate( mCaptureMode, mCaptureLastPoint );
442 }
443 else
444 {
446 mCurrentShapeMapTool->deactivate();
447 }
448}
449
451{
452 return mRubberBand.release();
453}
454
462
470
472{
473 if ( mCurrentCaptureTechnique == technique )
474 return;
475
476 mStartNewCurve = true;
477
478 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape && mCurrentShapeMapTool )
479 {
480 setCurrentShapeMapToolIsActivated( false );
481 clean();
482 }
483
484 switch ( technique )
485 {
487 mLineDigitizingType = Qgis::WkbType::LineString;
488 break;
490 mLineDigitizingType = Qgis::WkbType::CircularString;
491 break;
493 mLineDigitizingType = Qgis::WkbType::LineString;
494 mStreamingToleranceInPixels = QgsSettingsRegistryCore::settingsDigitizingStreamTolerance->value();
495 break;
497 mLineDigitizingType = Qgis::WkbType::LineString;
498 break;
500 mLineDigitizingType = Qgis::WkbType::NurbsCurve;
501 break;
502 }
503
504 if ( mTempRubberBand )
505 mTempRubberBand->setStringType( mLineDigitizingType );
506
507 mCurrentCaptureTechnique = technique;
508
509 if ( technique == Qgis::CaptureTechnique::Shape && mCurrentShapeMapTool && isActive() )
510 {
511 clean();
512 setCurrentShapeMapToolIsActivated( true );
513 }
514}
515
517{
518 if ( mCurrentShapeMapTool )
519 {
520 if ( shapeMapToolMetadata && mCurrentShapeMapTool->id() == shapeMapToolMetadata->id() )
521 return;
522 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape )
523 {
524 setCurrentShapeMapToolIsActivated( false );
525 }
526 mCurrentShapeMapTool->deleteLater();
527 }
528
529 mCurrentShapeMapTool.reset( shapeMapToolMetadata ? shapeMapToolMetadata->factory( this ) : nullptr );
530
531 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape && isActive() )
532 {
533 clean();
534 if ( mCurrentShapeMapTool )
535 {
536 setCurrentShapeMapToolIsActivated( true );
537 }
538 }
539}
540
542{
543 // If we are adding a record to a non-spatial layer, just return
544 if ( mCaptureModeFromLayer && ( !canvas()->currentLayer() || !canvas()->currentLayer()->isSpatial() ) )
545 return;
546
548
549 const QgsPointXY point = e->mapPoint();
550
551 mSnapIndicator->setMatch( e->mapPointMatch() );
552
553 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape )
554 {
555 if ( !mCurrentShapeMapTool )
556 {
557 emit messageEmitted( tr( "Select an option from the Shape Digitizing Toolbar in order to capture shapes" ), Qgis::MessageLevel::Warning );
558 }
559 else
560 {
561 if ( !mTempRubberBand )
562 {
563 mTempRubberBand.reset( createCurveRubberBand() );
564 mTempRubberBand->setStringType( mLineDigitizingType );
565 mTempRubberBand->setRubberBandGeometryType( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line );
566 }
567
568 mCurrentShapeMapTool->cadCanvasMoveEvent( e, mCaptureMode );
569 return;
570 }
571 }
572 else
573 {
574 const QgsPoint mapPoint = QgsPoint( point );
575
576 QgsCoordinateReferenceSystem targetCrs = mCanvas->mapSettings().destinationCrs();
577 if ( QgsMapLayer *l = layer() )
578 {
579 // if we have a layer, then the geometry will be in the layer's CRS, not the canvas'
580 targetCrs = l->crs();
581 }
582
583 if ( mCaptureMode != CapturePoint && mTempRubberBand && mCapturing )
584 {
585 bool hasTrace = false;
586
587 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Streaming )
588 {
589 if ( !mCaptureCurve.isEmpty() )
590 {
591 const QgsPoint prevPoint = mCaptureCurve.curveAt( mCaptureCurve.nCurves() - 1 )->endPoint();
592 if ( QgsPointXY( toCanvasCoordinates( toMapCoordinates( layer(), prevPoint ) ) ).distance( toCanvasCoordinates( point ) ) < mStreamingToleranceInPixels )
593 return;
594 }
595
596 mAllowAddingStreamingPoints = true;
598 mAllowAddingStreamingPoints = false;
599
600 std::unique_ptr< QgsCompoundCurve > tempCurve( mCaptureCurve.clone() );
601 if ( mCaptureMode == CapturePolygon )
602 {
603 auto curvePolygon = std::make_unique< QgsCurvePolygon >();
604 tempCurve->close();
605 curvePolygon->setExteriorRing( tempCurve.release() );
606 emit transientGeometryChanged( QgsReferencedGeometry( QgsGeometry( std::move( curvePolygon ) ), targetCrs ) );
607 }
608 else
609 {
610 emit transientGeometryChanged( QgsReferencedGeometry( QgsGeometry( std::move( tempCurve ) ), targetCrs ) );
611 }
612 }
613 else if ( tracingEnabled() && mCaptureCurve.numPoints() != 0 )
614 {
615 // Store the intermediate point for circular string to retrieve after tracing mouse move if
616 // the digitizing type is circular and the temp rubber band is effectively circular and if this point is existing
617 // Store an empty point if the digitizing type is linear ot the point is not existing (curve not complete)
618 if ( mLineDigitizingType == Qgis::WkbType::CircularString && mTempRubberBand->stringType() == Qgis::WkbType::CircularString && mTempRubberBand->curveIsComplete() )
619 mCircularItermediatePoint = mTempRubberBand->pointFromEnd( 1 );
620 else if ( mLineDigitizingType == Qgis::WkbType::LineString || !mTempRubberBand->curveIsComplete() )
621 mCircularItermediatePoint = QgsPoint();
622
623 hasTrace = tracingMouseMove( e );
624
625 if ( !hasTrace )
626 {
627 // Restore the temp rubber band
628 mTempRubberBand->reset( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line, mLineDigitizingType, mCaptureFirstPoint );
629 mTempRubberBand->addPoint( mCaptureLastPoint );
630 if ( !mCircularItermediatePoint.isEmpty() )
631 {
632 mTempRubberBand->movePoint( mCircularItermediatePoint );
633 mTempRubberBand->addPoint( mCircularItermediatePoint );
634 }
635 }
636 }
637
638 if ( mCurrentCaptureTechnique != Qgis::CaptureTechnique::Streaming && !hasTrace )
639 {
640 if ( mCaptureCurve.numPoints() > 0 )
641 {
642 const QgsPoint mapPt = mCaptureLastPoint;
643
644 if ( mTempRubberBand )
645 {
646 mTempRubberBand->movePoint( mapPoint );
647 mTempRubberBand->movePoint( 0, mapPt );
648 }
649
650 // fix existing rubber band after tracing - the last point may have been moved if using offset
651 if ( mRubberBand->numberOfVertices() )
652 mRubberBand->movePoint( mapPt );
653
654 std::unique_ptr< QgsCompoundCurve > tempCurve( mCaptureCurve.clone() );
655
656 // add mouse hover point to current captured geometry
657 try
658 {
659 QgsPoint hoverPointTargetCrs = mapPoint;
660 hoverPointTargetCrs.transform( QgsCoordinateTransform( mCanvas->mapSettings().destinationCrs(), targetCrs, QgsProject::instance()->transformContext() ) );
661 tempCurve->addCurve( new QgsLineString( tempCurve->endPoint(), hoverPointTargetCrs ) );
662 }
663 catch ( QgsCsException &e )
664 {
665 QgsDebugError( e.what() );
666 }
667
668 if ( mCaptureMode == CapturePolygon )
669 {
670 auto curvePolygon = std::make_unique< QgsCurvePolygon >();
671 tempCurve->close();
672 curvePolygon->setExteriorRing( tempCurve.release() );
673 emit transientGeometryChanged( QgsReferencedGeometry( QgsGeometry( std::move( curvePolygon ) ), targetCrs ) );
674 }
675 else
676 {
677 emit transientGeometryChanged( QgsReferencedGeometry( QgsGeometry( std::move( tempCurve ) ), targetCrs ) );
678 }
679 }
680 else if ( mTempRubberBand )
681 mTempRubberBand->movePoint( mapPoint );
682 }
683 }
684 }
685} // mouseMoveEvent
686
687
689{
690 if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() ) )
691 {
692 try
693 {
694 QgsPointXY mapP( mapPoint.x(), mapPoint.y() ); //#spellok
695 const bool is3D = layerPoint.is3D();
696 const bool isMeasure = layerPoint.isMeasure();
697 mapP = toLayerCoordinates( vlayer, mapP ); //transform snapped point back to layer crs //#spellok
698 layerPoint = QgsPoint( layerPoint.wkbType(), mapP.x(), mapP.y(), layerPoint.z(), layerPoint.m() ); //#spellok
699 if ( QgsWkbTypes::hasZ( vlayer->wkbType() ) && !is3D )
700 layerPoint.addZValue( mCadDockWidget && mCadDockWidget->cadEnabled() ? mCadDockWidget->currentPointV2().z() : defaultZValue() );
701 if ( QgsWkbTypes::hasM( vlayer->wkbType() ) && !isMeasure )
702 layerPoint.addMValue( mCadDockWidget && mCadDockWidget->cadEnabled() ? mCadDockWidget->currentPointV2().m() : defaultMValue() );
703 }
704 catch ( QgsCsException & )
705 {
706 QgsDebugError( u"transformation to layer coordinate failed"_s );
707 return 2;
708 }
709 }
710 else
711 {
712 layerPoint = QgsPoint( toLayerCoordinates( layer(), mapPoint ) );
713 }
714
715 return 0;
716}
717
719{
721 return nextPoint( mapPoint, layerPoint );
722}
723
725{
726 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() );
727 QgsVectorLayer *sourceLayer = match.layer();
728 if ( mCadDockWidget && mCadDockWidget->cadEnabled() )
729 {
730 layerPoint = mCadDockWidget->currentPointLayerCoordinates( layer() );
731 return 0;
732 }
733 else if ( !vlayer )
734 {
735 return 1;
736 }
737
738 if ( match.isValid() && sourceLayer )
739 {
740 if ( ( match.hasVertex() || match.hasLineEndpoint() ) )
741 {
742 if ( sourceLayer->crs() != vlayer->crs() )
743 {
744 layerPoint = match.interpolatedPoint();
745 return 1;
746 }
747 QgsFeature f;
748 QgsFeatureRequest request;
749 request.setFilterFid( match.featureId() );
750 const bool fetched = match.layer()->getFeatures( request ).nextFeature( f );
751 if ( fetched )
752 {
753 QgsVertexId vId;
754 if ( !f.geometry().vertexIdFromVertexNr( match.vertexIndex(), vId ) )
755 {
756 return 2;
757 }
758 layerPoint = f.geometry().constGet()->vertexAt( vId );
759 if ( QgsWkbTypes::hasZ( vlayer->wkbType() ) && !layerPoint.is3D() )
760 layerPoint.addZValue( defaultZValue() );
761 if ( QgsWkbTypes::hasM( vlayer->wkbType() ) && !layerPoint.isMeasure() )
762 layerPoint.addMValue( defaultMValue() );
763
764 // ZM support depends on the target layer
765 if ( !QgsWkbTypes::hasZ( vlayer->wkbType() ) )
766 {
767 layerPoint.dropZValue();
768 }
769
770 if ( !QgsWkbTypes::hasM( vlayer->wkbType() ) )
771 {
772 layerPoint.dropMValue();
773 }
774
775 return 0;
776 }
777 return 2;
778 }
779 else if ( QgsProject::instance()->topologicalEditing() && ( match.hasEdge() || match.hasMiddleSegment() ) )
780 {
781 layerPoint = toLayerCoordinates( vlayer, match.interpolatedPoint( mCanvas->mapSettings().destinationCrs() ) );
782 return 0;
783 }
784 }
785 return 2;
786}
787
789{
790 return addVertex( point, QgsPointLocator::Match() );
791}
792
794{
795 if ( mode() == CaptureNone )
796 {
797 QgsDebugError( u"invalid capture mode"_s );
798 return 2;
799 }
800
801 if ( mCapturing && mCurrentCaptureTechnique == Qgis::CaptureTechnique::Streaming && !mAllowAddingStreamingPoints )
802 return 0;
803
804 QgsPoint layerPoint;
805 if ( layer() )
806 {
807 int res = fetchLayerPoint( match, layerPoint );
808 if ( res != 0 )
809 {
810 res = nextPoint( QgsPoint( point ), layerPoint );
811 if ( res != 0 )
812 {
813 return res;
814 }
815 }
816 }
817 else
818 {
819 layerPoint = QgsPoint( point );
820 }
821 const QgsPoint mapPoint = toMapCoordinates( layer(), layerPoint );
822
823 if ( mCaptureMode == CapturePoint )
824 {
825 mCaptureCurve.addVertex( layerPoint );
826 mSnappingMatches.append( match );
827 }
828 else
829 {
830 if ( mCaptureFirstPoint.isEmpty() )
831 {
832 mCaptureFirstPoint = mapPoint;
833 }
834
835 if ( !mRubberBand )
837
838 if ( !mTempRubberBand )
839 {
840 mTempRubberBand.reset( createCurveRubberBand() );
841 mTempRubberBand->setStringType( mLineDigitizingType );
842 mTempRubberBand->reset( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line, mLineDigitizingType, mapPoint );
843 }
844
845 bool traceCreated = false;
846 if ( tracingEnabled() )
847 {
848 traceCreated = tracingAddVertex( mapPoint );
849 }
850
851 // keep new tracing start point if we created a trace. This is useful when tracing with
852 // offset so that the user stays "snapped"
853 mTracingStartPoint = traceCreated ? point : QgsPointXY();
854
855 if ( !traceCreated )
856 {
857 // ordinary digitizing
858 mTempRubberBand->movePoint( mapPoint ); //move the last point of the temp rubberband before operating with it
859 if ( mTempRubberBand->curveIsComplete() ) //2 points for line and 3 points for circular
860 {
861 if ( QgsCurve *curve = mTempRubberBand->curve() )
862 {
863 addCurve( curve );
864 // add curve append only invalid match to mSnappingMatches,
865 // so we need to remove them and add the one from here if it is valid
866 if ( match.isValid() && mSnappingMatches.count() > 0 && !mSnappingMatches.last().isValid() )
867 {
868 mSnappingMatches.removeLast();
869 if ( mTempRubberBand->stringType() == Qgis::WkbType::CircularString )
870 {
871 // for circular string two points are added and match for intermediate point is stored
872 mSnappingMatches.removeLast();
873 mSnappingMatches.append( mCircularIntermediateMatch );
874 }
875 mSnappingMatches.append( match );
876 }
877 }
878 mCaptureLastPoint = mapPoint;
879 mTempRubberBand->reset( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line, mLineDigitizingType, mCaptureFirstPoint );
880 }
881 else if ( mTempRubberBand->pointsCount() == 0 )
882 {
883 mCaptureLastPoint = mapPoint;
884 mCaptureCurve.addVertex( layerPoint );
885 mSnappingMatches.append( match );
886 }
887 else
888 {
889 if ( mTempRubberBand->stringType() == Qgis::WkbType::CircularString )
890 {
891 mCircularIntermediateMatch = match;
892 }
893 }
894
895 mTempRubberBand->addPoint( mapPoint );
896 }
897 else
898 {
899 mTempRubberBand->reset( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line, mLineDigitizingType, mCaptureFirstPoint );
900 mTempRubberBand->addPoint( mCaptureLastPoint );
901 }
902 }
903
904 updateExtraSnapLayer();
905 validateGeometry();
906
907 return 0;
908}
909
911{
912 if ( !c )
913 {
914 return 1;
915 }
916
917 if ( !mRubberBand )
918 {
920 }
921
922 if ( mTempRubberBand )
923 {
924 mTempRubberBand->reset( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line, mLineDigitizingType, mCaptureFirstPoint );
925 const QgsPoint endPt = c->endPoint();
926 mTempRubberBand->addPoint( endPt ); //add last point of c
927 }
928
929 const int countBefore = mCaptureCurve.vertexCount();
930 //if there is only one point, this the first digitized point that are in the this first curve added --> remove the point
931 if ( mCaptureCurve.numPoints() == 1 )
932 mCaptureCurve.removeCurve( 0 );
933
934 // Transform back to layer CRS in case map CRS and layer CRS are different
935 const QgsCoordinateTransform ct = mCanvas->mapSettings().layerTransform( layer() );
936 if ( ct.isValid() && !ct.isShortCircuited() )
937 {
938 QgsLineString *segmented = c->curveToLine();
940 // Curve geometries will be converted to segments, so we explicitly set extentPrevious to false
941 // to be able to remove the whole curve in undo
942 mCaptureCurve.addCurve( segmented, false );
943 delete c;
944 }
945 else
946 {
947 // we set the extendPrevious option to true to avoid creating compound curves with many 2 vertex linestrings -- instead we prefer
948 // to extend linestring curves so that they continue the previous linestring wherever possible...
949 mCaptureCurve.addCurve( c, !mStartNewCurve );
950 }
951
952 mStartNewCurve = false;
953
954 const int countAfter = mCaptureCurve.vertexCount();
955 const int addedPoint = countAfter - countBefore;
956
957 updateExtraSnapLayer();
958
959 for ( int i = 0; i < addedPoint; ++i )
960 mSnappingMatches.append( QgsPointLocator::Match() );
961
962 resetRubberBand();
963
964 return 0;
965}
966
968{
969 mCaptureCurve.clear();
970 updateExtraSnapLayer();
971}
972
973QList<QgsPointLocator::Match> QgsMapToolCapture::snappingMatches() const
974{
975 return mSnappingMatches;
976}
977
978void QgsMapToolCapture::undo( bool isAutoRepeat )
979{
980 mTracingStartPoint = QgsPointXY();
981
982 if ( mTempRubberBand )
983 {
984 if ( size() <= 1 && mTempRubberBand->pointsCount() != 0 )
985 return;
986
987 if ( isAutoRepeat && mIgnoreSubsequentAutoRepeatUndo )
988 return;
989 mIgnoreSubsequentAutoRepeatUndo = false;
990
991 const QgsPoint lastPoint = mTempRubberBand->lastPoint();
992
993 if ( mTempRubberBand->stringType() == Qgis::WkbType::CircularString && mTempRubberBand->pointsCount() > 2 )
994 {
995 mTempRubberBand->removeLastPoint();
996 mTempRubberBand->movePoint( lastPoint );
997 return;
998 }
999
1000 // Handle NURBS ControlPoints mode: remove last control point
1001 if ( QgsWkbTypes::isNurbsType( mTempRubberBand->stringType() ) && mTempRubberBand->pointsCount() > 1 )
1002 {
1003 mTempRubberBand->removeLastPoint();
1004 mTempRubberBand->movePoint( lastPoint );
1005 mCadDockWidget->removePreviousPoint();
1006 return;
1007 }
1008
1009 QgsVertexId vertexToRemove;
1010 vertexToRemove.part = 0;
1011 vertexToRemove.ring = 0;
1012 vertexToRemove.vertex = size() - 1;
1013
1014 // If the geometry was reprojected, remove the entire last curve.
1015 const QgsCoordinateTransform ct = mCanvas->mapSettings().layerTransform( layer() );
1016 if ( ct.isValid() && !ct.isShortCircuited() )
1017 {
1018 mCaptureCurve.removeCurve( mCaptureCurve.nCurves() - 1 );
1019 }
1020 if ( mCaptureCurve.numPoints() == 2 && mCaptureCurve.nCurves() == 1 )
1021 {
1022 // store the first vertex to restore if after deleting the curve
1023 // because when only two vertices, removing a point remove all the curve
1024 const QgsPoint fp = mCaptureCurve.startPoint();
1025 mCaptureCurve.deleteVertex( vertexToRemove );
1026 mCaptureCurve.addVertex( fp );
1027 }
1028 else
1029 {
1030 const int curvesBefore = mCaptureCurve.nCurves();
1031 const bool lastCurveIsLineString = qgsgeometry_cast<const QgsLineString *>( mCaptureCurve.curveAt( curvesBefore - 1 ) );
1032
1033 const int pointsCountBefore = mCaptureCurve.numPoints();
1034 mCaptureCurve.deleteVertex( vertexToRemove );
1035 int pointsCountAfter = mCaptureCurve.numPoints();
1036 for ( ; pointsCountAfter < pointsCountBefore; pointsCountAfter++ )
1037 if ( !mSnappingMatches.empty() )
1038 mSnappingMatches.removeLast();
1039
1040 // if we have removed the last point in a linestring curve, then we "stick" here and ignore subsequent
1041 // autorepeat undo actions until the user releases the undo key and holds it down again. This allows
1042 // users to selectively remove portions of the geometry captured with the streaming mode by holding down
1043 // the undo key, without risking accidental undo of non-streamed portions.
1044 if ( mCaptureCurve.nCurves() < curvesBefore && lastCurveIsLineString )
1045 mIgnoreSubsequentAutoRepeatUndo = true;
1046 }
1047
1048 updateExtraSnapLayer();
1049
1050 resetRubberBand();
1051
1052 mTempRubberBand->reset( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line, mLineDigitizingType, mCaptureFirstPoint );
1053
1054 if ( mCaptureCurve.numPoints() > 0 )
1055 {
1056 const QgsPoint lastPt = mCaptureCurve.endPoint();
1057 mCaptureLastPoint = toMapCoordinates( layer(), lastPt );
1058 mTempRubberBand->addPoint( mCaptureLastPoint );
1059 mTempRubberBand->movePoint( lastPoint );
1060 }
1061
1062 mCadDockWidget->removePreviousPoint();
1063 validateGeometry();
1064 }
1065}
1066
1068{
1069 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape && mCurrentShapeMapTool )
1070 {
1071 mCurrentShapeMapTool->keyPressEvent( e );
1072 if ( e->isAccepted() )
1073 return;
1074 }
1075
1076 // this is backwards, but we can't change now without breaking api because
1077 // forever QgsMapTools have had to explicitly mark events as ignored in order to
1078 // indicate that they've consumed the event and that the default behavior should not
1079 // be applied..!
1080 // see QgsMapCanvas::keyPressEvent
1081 e->accept();
1082
1083 if ( e->key() == Qt::Key_Backspace || e->key() == Qt::Key_Delete )
1084 {
1085 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape && mCurrentShapeMapTool )
1086 {
1087 if ( !e->isAutoRepeat() )
1088 {
1089 mCurrentShapeMapTool->undo();
1090 }
1091 }
1092 else
1093 {
1094 undo( e->isAutoRepeat() );
1095 }
1096
1097 // Override default shortcut management in MapCanvas
1098 e->ignore();
1099 }
1100 else if ( e->key() == Qt::Key_Escape )
1101 {
1102 if ( mCurrentShapeMapTool )
1103 mCurrentShapeMapTool->clean();
1104
1105 stopCapturing();
1106
1107 // Override default shortcut management in MapCanvas
1108 e->ignore();
1109 }
1110 else if ( e->key() == Qt::Key_W && !e->isAutoRepeat() )
1111 {
1112 // Enable NURBS weight editing mode when W is pressed
1113 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::NurbsCurve && mTempRubberBand && mTempRubberBand->pointsCount() >= 2 )
1114 {
1115 mWeightEditMode = true;
1116 // Edit the last control point by default (the one being digitized)
1117 mWeightEditControlPointIndex = mTempRubberBand->pointsCount() - 2; // -2 because last point is the cursor position
1118
1119 // Enable and update weight via CAD dock widget (which will notify the floater)
1120 if ( cadDockWidget() )
1121 {
1122 cadDockWidget()->setWeight( QString::number( mTempRubberBand->weight( mWeightEditControlPointIndex ), 'f', 2 ), true );
1123 }
1124 e->ignore();
1125 }
1126 }
1127}
1128
1130{
1131 if ( e->key() == Qt::Key_W && !e->isAutoRepeat() )
1132 {
1133 if ( mWeightEditMode )
1134 {
1135 mWeightEditMode = false;
1136 mWeightEditControlPointIndex = -1;
1137
1138 // Disable weight editing via CAD dock widget
1139 if ( cadDockWidget() )
1140 {
1141 cadDockWidget()->setWeight( QString(), false );
1142 }
1143
1144 e->accept();
1145 return;
1146 }
1147 }
1148
1150}
1151
1152void QgsMapToolCapture::wheelEvent( QWheelEvent *e )
1153{
1154 if ( mWeightEditMode )
1155 {
1156 // Adjust weight with mouse wheel
1157 // Base adjustment: 0.1 per wheel step
1158 // Ctrl modifier: fine adjustment (0.01 per step)
1159 // Shift modifier: coarse adjustment (1.0 per step)
1160 double adjustment = e->angleDelta().y() > 0 ? 0.1 : -0.1;
1161 if ( e->modifiers() & Qt::ControlModifier )
1162 adjustment *= 0.1;
1163 else if ( e->modifiers() & Qt::ShiftModifier )
1164 adjustment *= 10.0;
1165
1166 const double currentWeight = mTempRubberBand->weight( mWeightEditControlPointIndex );
1167 const double newWeight = std::max( 0.01, currentWeight + adjustment );
1168
1169 if ( mTempRubberBand->setWeight( mWeightEditControlPointIndex, newWeight ) )
1170 {
1171 if ( cadDockWidget() )
1172 {
1173 cadDockWidget()->setWeight( QString::number( newWeight, 'f', 2 ), true );
1174 }
1175 }
1176
1177 e->accept();
1178 return;
1179 }
1180
1182}
1183
1185{
1186 mCapturing = true;
1187}
1188
1190{
1191 return mCapturing;
1192}
1193
1195{
1196 mRubberBand.reset();
1197
1199
1200 // Reset weight editing mode when stopping capture
1201 if ( mWeightEditMode )
1202 {
1203 mWeightEditMode = false;
1204 mWeightEditControlPointIndex = -1;
1205 if ( cadDockWidget() )
1206 {
1207 cadDockWidget()->setWeight( QString(), false );
1208 }
1209 }
1210
1211 qDeleteAll( mGeomErrorMarkers );
1212 mGeomErrorMarkers.clear();
1213 mGeomErrors.clear();
1214
1215 mCaptureFirstPoint = QgsPoint();
1216 mCaptureLastPoint = QgsPoint();
1217
1218 mTracingStartPoint = QgsPointXY();
1219
1220 mCapturing = false;
1221 mCaptureCurve.clear();
1222 updateExtraSnapLayer();
1223 mSnappingMatches.clear();
1224 if ( auto *lCurrentVectorLayer = currentVectorLayer() )
1225 lCurrentVectorLayer->triggerRepaint();
1226
1228}
1229
1231{
1232 mTempRubberBand.reset();
1233}
1234
1236{
1237 stopCapturing();
1238 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape && mCurrentShapeMapTool )
1239 mCurrentShapeMapTool->clean();
1240
1241 clearCurve();
1242}
1243
1245{
1246 mCaptureCurve.close();
1247 updateExtraSnapLayer();
1248}
1249
1250void QgsMapToolCapture::validateGeometry()
1251{
1253 || !( capabilities() & ValidateGeometries ) )
1254 return;
1255
1256 if ( mValidator )
1257 {
1258 mValidator->deleteLater();
1259 mValidator = nullptr;
1260 }
1261
1262 mGeomErrors.clear();
1263 while ( !mGeomErrorMarkers.isEmpty() )
1264 {
1265 delete mGeomErrorMarkers.takeFirst();
1266 }
1267
1268 QgsGeometry geom;
1269
1270 switch ( mCaptureMode )
1271 {
1272 case CaptureNone:
1273 case CapturePoint:
1274 return;
1275 case CaptureLine:
1276 if ( size() < 2 )
1277 return;
1278 geom = QgsGeometry( mCaptureCurve.curveToLine() );
1279 break;
1280 case CapturePolygon:
1281 if ( size() < 3 )
1282 return;
1283 QgsLineString *exteriorRing = mCaptureCurve.curveToLine();
1284 exteriorRing->close();
1285 QgsPolygon *polygon = new QgsPolygon();
1286 polygon->setExteriorRing( exteriorRing );
1287 geom = QgsGeometry( polygon );
1288 break;
1289 }
1290
1291 if ( geom.isNull() )
1292 return;
1293
1297 mValidator = new QgsGeometryValidator( geom, nullptr, method );
1298 connect( mValidator, &QgsGeometryValidator::errorFound, this, &QgsMapToolCapture::addError );
1299 mValidator->start();
1300 QgsDebugMsgLevel( u"Validation started"_s, 4 );
1301}
1302
1303void QgsMapToolCapture::addError( const QgsGeometry::Error &e )
1304{
1305 mGeomErrors << e;
1306 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() );
1307 if ( !vlayer )
1308 return;
1309
1310 if ( e.hasWhere() )
1311 {
1312 QgsVertexMarker *vm = new QgsVertexMarker( mCanvas );
1313 vm->setCenter( mCanvas->mapSettings().layerToMapCoordinates( vlayer, e.where() ) );
1315 vm->setPenWidth( 2 );
1316 vm->setToolTip( e.what() );
1317 vm->setColor( Qt::green );
1318 vm->setZValue( vm->zValue() + 1 );
1319 mGeomErrorMarkers << vm;
1320 }
1321}
1322
1324{
1325 return mCaptureCurve.numPoints();
1326}
1327
1328QVector<QgsPointXY> QgsMapToolCapture::points() const
1329{
1330 QVector<QgsPointXY> pointsXY;
1332
1333 return pointsXY;
1334}
1335
1337{
1338 QgsPointSequence pts;
1339 mCaptureCurve.points( pts );
1340 return pts;
1341}
1342
1343void QgsMapToolCapture::setPoints( const QVector<QgsPointXY> &pointList )
1344{
1345 QgsLineString *line = new QgsLineString( pointList );
1346 mCaptureCurve.clear();
1347 mCaptureCurve.addCurve( line );
1348 updateExtraSnapLayer();
1349 mSnappingMatches.clear();
1350 for ( int i = 0; i < line->length(); ++i )
1351 mSnappingMatches.append( QgsPointLocator::Match() );
1352 resetRubberBand();
1353}
1354
1356{
1357 QgsLineString *line = new QgsLineString( pointList );
1358 mCaptureCurve.clear();
1359 mCaptureCurve.addCurve( line );
1360 updateExtraSnapLayer();
1361 mSnappingMatches.clear();
1362 for ( int i = 0; i < line->length(); ++i )
1363 mSnappingMatches.append( QgsPointLocator::Match() );
1364 resetRubberBand();
1365}
1366
1368{
1369 QgsPoint newPoint( Qgis::WkbType::Point, point.x(), point.y() );
1370
1371 // get current layer
1372 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() );
1373 if ( !vlayer )
1374 {
1375 return newPoint;
1376 }
1377
1378 // convert to the corresponding type for a full ZM support
1379 const Qgis::WkbType type = vlayer->wkbType();
1380 if ( QgsWkbTypes::hasZ( type ) && !QgsWkbTypes::hasM( type ) )
1381 {
1382 newPoint.convertTo( Qgis::WkbType::PointZ );
1383 }
1384 else if ( !QgsWkbTypes::hasZ( type ) && QgsWkbTypes::hasM( type ) )
1385 {
1386 newPoint.convertTo( Qgis::WkbType::PointM );
1387 }
1388 else if ( QgsWkbTypes::hasZ( type ) && QgsWkbTypes::hasM( type ) )
1389 {
1391 }
1392
1393 // set z value if necessary
1394 if ( QgsWkbTypes::hasZ( newPoint.wkbType() ) )
1395 {
1396 newPoint.setZ( mCadDockWidget && mCadDockWidget->cadEnabled() ? mCadDockWidget->getLineZ() : defaultZValue() );
1397 }
1398 // set m value if necessary
1399 if ( QgsWkbTypes::hasM( newPoint.wkbType() ) )
1400 {
1401 newPoint.setM( mCadDockWidget && mCadDockWidget->cadEnabled() ? mCadDockWidget->getLineM() : defaultMValue() );
1402 }
1403 return newPoint;
1404}
1405
1407{
1408 QgsPoint newPoint = mapPoint( e.mapPoint() );
1409
1410 // set z or m value from snapped point if necessary
1411 if ( QgsWkbTypes::hasZ( newPoint.wkbType() ) || QgsWkbTypes::hasM( newPoint.wkbType() ) )
1412 {
1413 // if snapped, z and m dimension are taken from the corresponding snapped
1414 // point.
1415 if ( e.isSnapped() )
1416 {
1417 const QgsPointLocator::Match match = e.mapPointMatch();
1418
1419 if ( match.layer() )
1420 {
1421 const QgsFeature ft = match.layer()->getFeature( match.featureId() );
1422 if ( QgsWkbTypes::hasZ( match.layer()->wkbType() ) )
1423 {
1424 newPoint.setZ( ft.geometry().vertexAt( match.vertexIndex() ).z() );
1425 }
1426 if ( QgsWkbTypes::hasM( match.layer()->wkbType() ) )
1427 {
1428 newPoint.setM( ft.geometry().vertexAt( match.vertexIndex() ).m() );
1429 }
1430 }
1431 }
1432 }
1433
1434 return newPoint;
1435}
1436
1437void QgsMapToolCapture::updateExtraSnapLayer()
1438{
1439 if ( !mExtraSnapLayer )
1440 return;
1441
1442 if ( canvas()->snappingUtils()->config().selfSnapping() && layer() && mCaptureCurve.numPoints() >= 2 )
1443 {
1444 // the current layer may have changed
1445 mExtraSnapLayer->setCrs( layer()->crs() );
1446 QgsGeometry geom = QgsGeometry( mCaptureCurve.clone() );
1447 // we close the curve to allow snapping on last segment
1448 if ( mCaptureMode == CapturePolygon && mCaptureCurve.numPoints() >= 3 )
1449 {
1450 qgsgeometry_cast<QgsCompoundCurve *>( geom.get() )->close();
1451 }
1452 mExtraSnapLayer->changeGeometry( mExtraSnapFeatureId, geom );
1453 }
1454 else
1455 {
1456 QgsGeometry geom;
1457 mExtraSnapLayer->changeGeometry( mExtraSnapFeatureId, geom );
1458 }
1459}
1460
1461
1463{
1464 // POINT CAPTURING
1465 if ( mode() == CapturePoint )
1466 {
1467 if ( e->button() != Qt::LeftButton )
1468 return;
1469
1470 QgsPoint savePoint; //point in layer coordinates
1471 bool isMatchPointZ = false;
1472 bool isMatchPointM = false;
1473 try
1474 {
1475 QgsPoint fetchPoint;
1476 int res = fetchLayerPoint( e->mapPointMatch(), fetchPoint );
1477 isMatchPointZ = QgsWkbTypes::hasZ( fetchPoint.wkbType() );
1478 isMatchPointM = QgsWkbTypes::hasM( fetchPoint.wkbType() );
1479
1480 if ( res == 0 )
1481 {
1483 if ( isMatchPointM && isMatchPointZ )
1484 {
1485 geomType = Qgis::WkbType::PointZM;
1486 }
1487 else if ( isMatchPointM )
1488 {
1489 geomType = Qgis::WkbType::PointM;
1490 }
1491 else if ( isMatchPointZ )
1492 {
1493 geomType = Qgis::WkbType::PointZ;
1494 }
1495 savePoint = QgsPoint( geomType, fetchPoint.x(), fetchPoint.y(), fetchPoint.z(), fetchPoint.m() );
1496 }
1497 else
1498 {
1499 QgsPointXY point = mCanvas->mapSettings().mapToLayerCoordinates( layer(), e->mapPoint() );
1500
1501 savePoint = QgsPoint( point.x(), point.y(), fetchPoint.z(), fetchPoint.m() );
1502 }
1503 }
1504 catch ( QgsCsException &cse )
1505 {
1506 Q_UNUSED( cse )
1507 emit messageEmitted( tr( "Cannot transform the point to the layer's coordinate system" ), Qgis::MessageLevel::Warning );
1508 return;
1509 }
1510
1511 QgsGeometry g( std::make_unique<QgsPoint>( savePoint ) );
1512
1513 // The snapping result needs to be added so it's available in the @snapping_results variable of default value etc. expression contexts
1514 addVertex( e->mapPoint(), e->mapPointMatch() );
1515
1516 geometryCaptured( g );
1517 pointCaptured( savePoint );
1518
1519 stopCapturing();
1520
1521 // we are done with digitizing for now so instruct advanced digitizing dock to reset its CAD points
1523 }
1524
1525 // LINE AND POLYGON CAPTURING
1526 else if ( mode() == CaptureLine || mode() == CapturePolygon )
1527 {
1528 bool digitizingFinished = false;
1529 QgsPointSequence nurbsControlPoints;
1530 QVector<double> nurbsWeights;
1531
1532 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::Shape )
1533 {
1534 if ( !mCurrentShapeMapTool )
1535 {
1536 emit messageEmitted( tr( "Select an option from the Shape Digitizing Toolbar in order to capture shapes" ), Qgis::MessageLevel::Warning );
1537 return;
1538 }
1539 else
1540 {
1541 if ( !mTempRubberBand )
1542 {
1543 mTempRubberBand.reset( createCurveRubberBand() );
1544 mTempRubberBand->setStringType( mLineDigitizingType );
1545 mTempRubberBand->setRubberBandGeometryType( mCaptureMode == CapturePolygon ? Qgis::GeometryType::Polygon : Qgis::GeometryType::Line );
1546 }
1547
1548 digitizingFinished = mCurrentShapeMapTool->cadCanvasReleaseEvent( e, mCaptureMode );
1549 if ( digitizingFinished )
1550 mCurrentShapeMapTool->clean();
1551 }
1552 }
1553 else // i.e. not shape
1554 {
1555 //add point to list and to rubber band
1556 if ( e->button() == Qt::LeftButton )
1557 {
1558 const int error = addVertex( e->mapPoint(), e->mapPointMatch() );
1559 if ( error == 2 )
1560 {
1561 //problem with coordinate transformation
1562 emit messageEmitted( tr( "Cannot transform the point to the layers coordinate system" ), Qgis::MessageLevel::Warning );
1563 return;
1564 }
1565
1567 }
1568 else if ( e->button() == Qt::RightButton )
1569 {
1570 // End of string
1571
1572 // Extract NURBS control points and weights from the rubberband before deleting it
1573 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::NurbsCurve && mTempRubberBand )
1574 {
1575 const int rbPointCount = mTempRubberBand->pointsCount();
1576 if ( rbPointCount > 1 )
1577 {
1578 // Exclude the last point (cursor position)
1579 for ( int i = 0; i < rbPointCount - 1; ++i )
1580 {
1581 nurbsControlPoints.append( mTempRubberBand->pointFromEnd( rbPointCount - 1 - i ) );
1582 }
1583 // Also extract weights (in correct order)
1584 const QVector<double> &rbWeights = mTempRubberBand->weights();
1585 for ( int i = 0; i < rbPointCount - 1; ++i )
1586 {
1587 if ( i < rbWeights.size() )
1588 nurbsWeights.append( rbWeights[i] );
1589 else
1590 nurbsWeights.append( 1.0 );
1591 }
1592 }
1593 }
1594
1596
1597 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::NurbsCurve )
1598 {
1599 // Minimum 4 control points required for degree 3 NURBS
1600 if ( mode() == CaptureLine && nurbsControlPoints.count() < 4 )
1601 {
1602 stopCapturing();
1603 return;
1604 }
1605 if ( mode() == CapturePolygon && nurbsControlPoints.count() < 4 )
1606 {
1607 stopCapturing();
1608 return;
1609 }
1610 }
1611 else
1612 {
1613 //lines: bail out if there are not at least two vertices
1614 if ( mode() == CaptureLine && size() < 2 )
1615 {
1616 stopCapturing();
1617 return;
1618 }
1619
1620 //polygons: bail out if there are not at least two vertices
1621 if ( mode() == CapturePolygon && size() < 3 )
1622 {
1623 stopCapturing();
1624 return;
1625 }
1626 }
1627
1628 if ( mode() == CapturePolygon || e->modifiers() == Qt::ShiftModifier )
1629 {
1630 // Close NURBS curve by adding first control point at the end
1631 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::NurbsCurve && !nurbsControlPoints.isEmpty() )
1632 {
1633 nurbsControlPoints.append( nurbsControlPoints.first() );
1634 if ( !nurbsWeights.isEmpty() )
1635 nurbsWeights.append( nurbsWeights.first() );
1636 }
1637 else
1638 {
1639 closePolygon();
1640 }
1641 }
1642
1643 digitizingFinished = true;
1644 }
1645 }
1646
1647 if ( digitizingFinished )
1648 {
1649 QgsGeometry g;
1650 std::unique_ptr<QgsCurve> curveToAdd;
1651
1652 // Create a single NurbsCurve from all control points
1653 if ( mCurrentCaptureTechnique == Qgis::CaptureTechnique::NurbsCurve )
1654 {
1655 // Get degree from settings
1657 const int n = nurbsControlPoints.size();
1658
1659 // Adapt degree if not enough control points
1660 if ( n < degree + 1 )
1661 {
1662 degree = std::max( 1, n - 1 );
1663 if ( n < 2 )
1664 {
1665 curveToAdd = std::make_unique<QgsLineString>( nurbsControlPoints );
1666 }
1667 }
1668
1669 if ( !curveToAdd )
1670 {
1671 // Generate uniform clamped knot vector (size = n + degree + 1)
1672 const int knotCount = n + degree + 1;
1673 QVector<double> knots( knotCount );
1674
1675 // First (degree + 1) knots are 0
1676 for ( int i = 0; i <= degree; ++i )
1677 knots[i] = 0.0;
1678
1679 // Last (degree + 1) knots are 1
1680 for ( int i = knotCount - degree - 1; i < knotCount; ++i )
1681 knots[i] = 1.0;
1682
1683 // Middle knots are uniformly spaced
1684 const int numMiddleKnots = n - degree - 1;
1685 for ( int i = 0; i < numMiddleKnots; ++i )
1686 {
1687 knots[degree + 1 + i] = static_cast<double>( i + 1 ) / ( numMiddleKnots + 1 );
1688 }
1689
1690 // Ensure we have the right number of weights
1691 QVector<double> weights = nurbsWeights;
1692 while ( weights.size() < n )
1693 weights.append( 1.0 );
1694 weights.resize( n );
1695
1696 curveToAdd = std::make_unique<QgsNurbsCurve>( nurbsControlPoints, degree, knots, weights );
1697 }
1698 }
1699 else
1700 {
1701 curveToAdd.reset( captureCurve()->clone() );
1702 }
1703
1704 if ( mode() == CaptureLine )
1705 {
1706 g = QgsGeometry( curveToAdd->clone() );
1707 geometryCaptured( g );
1708 lineCaptured( curveToAdd.release() );
1709 }
1710 else
1711 {
1712 // For NURBS curves, keep the already-created curve
1713 // For other curves, check provider support for curved segments
1714 if ( mCurrentCaptureTechnique != Qgis::CaptureTechnique::NurbsCurve )
1715 {
1716 //does compoundcurve contain circular strings?
1717 //does provider support circular strings?
1718 if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer() ) )
1719 {
1720 const bool hasCurvedSegments = captureCurve()->hasCurvedSegments();
1721 const bool providerSupportsCurvedSegments = vlayer->dataProvider()->capabilities() & Qgis::VectorProviderCapability::CircularGeometries;
1722
1723 if ( hasCurvedSegments && providerSupportsCurvedSegments )
1724 {
1725 curveToAdd.reset( captureCurve()->clone() );
1726 }
1727 else
1728 {
1729 curveToAdd.reset( captureCurve()->curveToLine() );
1730 }
1731 }
1732 else
1733 {
1734 curveToAdd.reset( captureCurve()->clone() );
1735 }
1736 }
1737 std::unique_ptr<QgsCurvePolygon> poly { new QgsCurvePolygon() };
1738 poly->setExteriorRing( curveToAdd.release() );
1739 g = QgsGeometry( poly->clone() );
1740 geometryCaptured( g );
1741 polygonCaptured( poly.get() );
1742 }
1743
1744 stopCapturing();
1745 }
1746 }
1747}
@ 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:369
friend class QgsMapCanvas
Definition qgsmaptool.h:389
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:593
bool dropMValue() override
Drops any measure values which exist in the geometry.
Definition qgspoint.cpp:634
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
Definition qgspoint.cpp:582
bool deleteVertex(QgsVertexId position) override
Deletes a vertex within the geometry.
Definition qgspoint.cpp:485
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:398
bool convertTo(Qgis::WkbType type) override
Converts the geometry to a specified type.
Definition qgspoint.cpp:651
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:408
void setZ(double z)
Sets the point's z-coordinate.
Definition qgspoint.h:383
bool dropZValue() override
Drops any z-dimensions which exist in the geometry.
Definition qgspoint.cpp:623
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:120
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:7524
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