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