QGIS API Documentation  3.21.0-Master (5b68dc587e)
qgsmaptooldigitizefeature.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmaptooldigitizefeature.cpp
3 
4  ---------------------
5  begin : 7.12.2017
6  copyright : (C) 2017 by David Signer
7  email : [email protected]
8  ***************************************************************************
9  * *
10  * This program is free software; you can redistribute it and/or modify *
11  * it under the terms of the GNU General Public License as published by *
12  * the Free Software Foundation; either version 2 of the License, or *
13  * (at your option) any later version. *
14  * *
15  ***************************************************************************/
16 
19 #include "qgsapplication.h"
20 #include "qgsattributedialog.h"
21 #include "qgsexception.h"
22 #include "qgscurvepolygon.h"
23 #include "qgsfields.h"
24 #include "qgsgeometry.h"
25 #include "qgslinestring.h"
26 #include "qgsmultipoint.h"
27 #include "qgsmapcanvas.h"
28 #include "qgsmapmouseevent.h"
29 #include "qgspolygon.h"
30 #include "qgsproject.h"
31 #include "qgsvectordataprovider.h"
32 #include "qgsvectorlayer.h"
33 #include "qgslogger.h"
34 
35 #include <QSettings>
36 
38  : QgsMapToolCapture( canvas, cadDockWidget, mode )
39  , mCheckGeometryType( true )
40 {
41  mToolName = tr( "Digitize feature" );
44 }
45 
46 QgsMapToolCapture::Capabilities QgsMapToolDigitizeFeature::capabilities() const
47 {
49 }
50 
52 {
53  switch ( technique )
54  {
56  return true;
60  }
61  return false;
62 }
63 
64 void QgsMapToolDigitizeFeature::digitized( const QgsFeature &f )
65 {
66  emit digitizingCompleted( f );
67 }
68 
70 {
71  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mLayer );
72  if ( !vlayer )
73  vlayer = currentVectorLayer();
74 
75  if ( vlayer && vlayer->geometryType() == QgsWkbTypes::NullGeometry )
76  {
77  const QgsFeature f;
78  digitized( f );
79  return;
80  }
81 
82  if ( mLayer )
83  {
84  //remember current layer
85  mCurrentLayer = mCanvas->currentLayer();
86  //set the layer with the given
87  mCanvas->setCurrentLayer( mLayer );
88  }
89 
91 }
92 
94 {
96 
97  if ( mCurrentLayer )
98  //set the layer back to the one remembered
99  mCanvas->setCurrentLayer( mCurrentLayer );
100  emit digitizingFinished();
101 }
102 
104 {
105  return mCheckGeometryType;
106 }
107 
109 {
110  mCheckGeometryType = checkGeometryType;
111 }
112 
114 {
115  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mLayer );
116 
117  if ( !vlayer )
118  //if no given layer take the current from canvas
119  vlayer = currentVectorLayer();
120 
121  if ( !vlayer )
122  {
124  return;
125  }
126 
127  const QgsWkbTypes::Type layerWKBType = vlayer->wkbType();
128 
129  QgsVectorDataProvider *provider = vlayer->dataProvider();
130 
131  if ( !( provider->capabilities() & QgsVectorDataProvider::AddFeatures ) )
132  {
133  emit messageEmitted( tr( "The data provider for this layer does not support the addition of features." ), Qgis::MessageLevel::Warning );
134  return;
135  }
136 
137  if ( !vlayer->isEditable() )
138  {
140  return;
141  }
142 
143  // POINT CAPTURING
144  if ( mode() == CapturePoint )
145  {
146  if ( e->button() != Qt::LeftButton )
147  return;
148 
149  //check we only use this tool for point/multipoint layers
150  if ( vlayer->geometryType() != QgsWkbTypes::PointGeometry && mCheckGeometryType )
151  {
152  emit messageEmitted( tr( "Wrong editing tool, cannot apply the 'capture point' tool on this vector layer" ), Qgis::MessageLevel::Warning );
153  return;
154  }
155 
156  QgsPoint savePoint; //point in layer coordinates
157  bool isMatchPointZ = false;
158  bool isMatchPointM = false;
159  try
160  {
161  QgsPoint fetchPoint;
162  int res;
163  res = fetchLayerPoint( e->mapPointMatch(), fetchPoint );
164  isMatchPointZ = QgsWkbTypes::hasZ( fetchPoint.wkbType() );
165  isMatchPointM = QgsWkbTypes::hasM( fetchPoint.wkbType() );
166 
167  if ( res == 0 )
168  {
169  savePoint = QgsPoint( QgsWkbTypes::singleType( layerWKBType ), fetchPoint.x(), fetchPoint.y(), fetchPoint.z(), fetchPoint.m() );
170  }
171  else
172  {
173  const QgsPointXY layerPoint = toLayerCoordinates( vlayer, e->mapPoint() );
174  savePoint = QgsPoint( QgsWkbTypes::singleType( layerWKBType ), layerPoint.x(), layerPoint.y(), fetchPoint.z(), fetchPoint.m() );
175  }
176  }
177  catch ( QgsCsException &cse )
178  {
179  Q_UNUSED( cse )
180  emit messageEmitted( tr( "Cannot transform the point to the layers coordinate system" ), Qgis::MessageLevel::Warning );
181  return;
182  }
183 
184  //only do the rest for provider with feature addition support
185  //note that for the grass provider, this will return false since
186  //grass provider has its own mechanism of feature addition
188  {
189  QgsFeature f( vlayer->fields() );
190 
191  QgsGeometry g;
192  const QgsPoint result( QgsWkbTypes::singleType( layerWKBType ), savePoint.x(), savePoint.y(), isMatchPointZ ? savePoint.z() : defaultZValue(), isMatchPointM ? savePoint.m() : defaultMValue() );
193  if ( mCheckGeometryType == false )
194  {
195  // if layer supports more types (mCheckGeometryType is false)
196  g = QgsGeometry( std::make_unique<QgsPoint>( savePoint ) );
197  }
198  else
199  {
200  if ( !QgsWkbTypes::isMultiType( layerWKBType ) )
201  {
202  g = QgsGeometry( std::make_unique<QgsPoint>( result ) );
203  }
204  else
205  {
206  QgsMultiPoint *mp = new QgsMultiPoint();
207  mp->addGeometry( new QgsPoint( result ) );
208  g.set( mp );
209  }
210  }
211 
212  f.setGeometry( g );
213  f.setValid( true );
214 
215  // The snapping result needs to be added so it's available in the @snapping_results variable of default value etc. expression contexts
216  addVertex( e->mapPoint(), e->mapPointMatch() );
217 
218  digitized( f );
219 
220  stopCapturing();
221 
222  // we are done with digitizing for now so instruct advanced digitizing dock to reset its CAD points
224  }
225  }
226 
227  // LINE AND POLYGON CAPTURING
228  else if ( mode() == CaptureLine || mode() == CapturePolygon )
229  {
230  //check we only use the line tool for line/multiline layers
231  if ( mode() == CaptureLine && vlayer->geometryType() != QgsWkbTypes::LineGeometry && mCheckGeometryType )
232  {
233  emit messageEmitted( tr( "Wrong editing tool, cannot apply the 'capture line' tool on this vector layer" ), Qgis::MessageLevel::Warning );
234  return;
235  }
236 
237  //check we only use the polygon tool for polygon/multipolygon layers
238  if ( mode() == CapturePolygon && vlayer->geometryType() != QgsWkbTypes::PolygonGeometry && mCheckGeometryType )
239  {
240  emit messageEmitted( tr( "Wrong editing tool, cannot apply the 'capture polygon' tool on this vector layer" ), Qgis::MessageLevel::Warning );
241  return;
242  }
243 
244  //add point to list and to rubber band
245  if ( e->button() == Qt::LeftButton )
246  {
247  const int error = addVertex( e->mapPoint(), e->mapPointMatch() );
248  if ( error == 2 )
249  {
250  //problem with coordinate transformation
251  emit messageEmitted( tr( "Cannot transform the point to the layers coordinate system" ), Qgis::MessageLevel::Warning );
252  return;
253  }
254 
255  startCapturing();
256  }
257  else if ( e->button() == Qt::RightButton )
258  {
259  // End of string
261 
262  //lines: bail out if there are not at least two vertices
263  if ( mode() == CaptureLine && size() < 2 )
264  {
265  stopCapturing();
266  return;
267  }
268 
269  //polygons: bail out if there are not at least two vertices
270  if ( mode() == CapturePolygon && size() < 3 )
271  {
272  stopCapturing();
273  return;
274  }
275 
276  if ( mode() == CapturePolygon || e->modifiers() == Qt::ShiftModifier )
277  {
278  closePolygon();
279  }
280 
281  //create QgsFeature with wkb representation
282  std::unique_ptr< QgsFeature > f( new QgsFeature( vlayer->fields(), 0 ) );
283 
284  //does compoundcurve contain circular strings?
285  //does provider support circular strings?
286  const bool hasCurvedSegments = captureCurve()->hasCurvedSegments();
287  const bool providerSupportsCurvedSegments = vlayer->dataProvider()->capabilities() & QgsVectorDataProvider::CircularGeometries;
288 
289  QList<QgsPointLocator::Match> snappingMatchesList;
290  QgsCurve *curveToAdd = nullptr;
291  if ( hasCurvedSegments && providerSupportsCurvedSegments )
292  {
293  curveToAdd = captureCurve()->clone();
294  }
295  else
296  {
297  curveToAdd = captureCurve()->curveToLine();
298  snappingMatchesList = snappingMatches();
299  }
300 
301  if ( mode() == CaptureLine )
302  {
303  const QgsGeometry g( curveToAdd );
304  f->setGeometry( g );
305  }
306  else
307  {
308  QgsCurvePolygon *poly = nullptr;
309  if ( hasCurvedSegments && providerSupportsCurvedSegments )
310  {
311  poly = new QgsCurvePolygon();
312  }
313  else
314  {
315  poly = new QgsPolygon();
316  }
317  poly->setExteriorRing( curveToAdd );
318  const QgsGeometry g( poly );
319  f->setGeometry( g );
320 
321  QList<QgsVectorLayer *> avoidIntersectionsLayers;
322  switch ( QgsProject::instance()->avoidIntersectionsMode() )
323  {
325  avoidIntersectionsLayers.append( vlayer );
326  break;
328  avoidIntersectionsLayers = QgsProject::instance()->avoidIntersectionsLayers();
329  break;
331  break;
332  }
333  if ( avoidIntersectionsLayers.size() > 0 )
334  {
335  QgsGeometry featGeom = f->geometry();
336  const int avoidIntersectionsReturn = featGeom.avoidIntersections( avoidIntersectionsLayers );
337  f->setGeometry( featGeom );
338  if ( avoidIntersectionsReturn == 3 )
339  {
340  emit messageEmitted( tr( "The feature has been added, but at least one geometry intersected is invalid. These geometries must be manually repaired." ), Qgis::MessageLevel::Warning );
341  }
342  if ( f->geometry().isEmpty() ) //avoid intersection might have removed the whole geometry
343  {
344  emit messageEmitted( tr( "The feature cannot be added because its geometry collapsed due to intersection avoidance" ), Qgis::MessageLevel::Critical );
345  stopCapturing();
346  return;
347  }
348  }
349  }
350  f->setValid( true );
351 
352  digitized( *f );
353 
354  stopCapturing();
355  }
356  }
357 }
358 
360 {
361  mLayer = vl;
362 }
QgsWkbTypes::Type wkbType() const SIP_HOLDGIL
Returns the WKB type of the geometry.
The QgsAdvancedDigitizingDockWidget class is a dockable widget used to handle the CAD tools on top of...
void clearPoints()
Removes all points from the CAD point list.
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.
QgsCompoundCurve * clone() const override
Clones the geometry by performing a deep copy.
bool hasCurvedSegments() const override
Returns true if the geometry contains curved segments.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:66
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:36
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
void setValid(bool validity)
Sets the validity of the feature.
Definition: qgsfeature.cpp:196
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Definition: qgsfeature.cpp:145
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:124
void set(QgsAbstractGeometry *geometry)
Sets the underlying geometry store.
int avoidIntersections(const QList< QgsVectorLayer * > &avoidIntersectionsLayers, const QHash< QgsVectorLayer *, QSet< QgsFeatureId > > &ignoreFeatures=(QHash< QgsVectorLayer *, QSet< QgsFeatureId > >()))
Modifies geometry to avoid intersections with the layers specified in project properties.
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:88
void setCurrentLayer(QgsMapLayer *layer)
QgsMapLayer * currentLayer()
returns current layer (set by legend widget)
Base class for all map layer types.
Definition: qgsmaplayer.h:73
A QgsMapMouseEvent is the result of a user interaction with the mouse on a QgsMapCanvas.
QgsPointXY mapPoint() const
mapPoint returns the point in coordinates
QgsPointLocator::Match mapPointMatch() const
Returns the matching data from the most recently snapped point.
QgsAdvancedDigitizingDockWidget * cadDockWidget() const
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 activate() override
Registers this maptool with the cad dock widget.
CaptureMode
Different capture modes.
@ CapturePolygon
Capture polygons.
@ CapturePoint
Capture points.
@ CaptureLine
Capture lines.
void deleteTempRubberBand()
Clean a temporary rubberband.
QList< QgsPointLocator::Match > snappingMatches() const
Returns a list of matches for each point on the captureCurve.
CaptureTechnique
Capture technique.
@ Streaming
Streaming points digitizing mode (points are automatically added as the mouse cursor moves)....
@ StraightSegments
Default capture mode - capture occurs with straight line segments.
@ CircularString
Capture in circular strings.
void closePolygon()
Close an open polygon.
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.
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 (since QGIS 3.22)
@ SupportsCurves
Supports curved geometries input.
const QgsCompoundCurve * captureCurve() const
Gets the capture curve.
void startCapturing()
Start capturing.
void cadCanvasReleaseEvent(QgsMapMouseEvent *e) override
Override this method when subclassing this class.
void setCheckGeometryType(bool checkGeometryType)
Check if CaptureMode matches layer type.
void digitizingCompleted(const QgsFeature &feature)
Emitted whenever the digitizing has been successfully completed.
bool checkGeometryType() const
Check if CaptureMode matches layer type.
void digitizingFinished()
Emitted whenever the digitizing has been ended without digitizing any feature.
void activate() override
Registers this maptool with the cad dock widget.
void setLayer(QgsMapLayer *vl)
Change the layer edited by the map tool.
void deactivate() override
Unregisters this maptool from the cad dock widget.
QgsMapToolDigitizeFeature(QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDockWidget, CaptureMode mode=QgsMapToolCapture::CaptureNone)
QgsMapToolDigitizeFeature is a map tool to digitize a feature geometry.
bool supportsTechnique(CaptureTechnique technique) const override
Returns true if the tool supports the specified capture technique.
QgsMapToolCapture::Capabilities capabilities() const override
Returns flags containing the supported capabilities.
double defaultZValue() const
Returns default Z value.
void notifyNotVectorLayer()
Display a timed message bar noting the active layer is not vector.
void notifyNotEditableLayer()
Display a timed message bar noting the active vector layer is not editable.
double defaultMValue() const
Returns default M value.
QgsVectorLayer * currentVectorLayer()
Returns the current vector layer of the map canvas or 0.
QgsMapCanvas * mCanvas
The pointer to the map canvas.
Definition: qgsmaptool.h:304
QString mToolName
The translated name of the map tool.
Definition: qgsmaptool.h:322
QgsPointXY toLayerCoordinates(const QgsMapLayer *layer, QPoint point)
Transforms a point from screen coordinates to layer coordinates.
Definition: qgsmaptool.cpp:51
void messageEmitted(const QString &message, Qgis::MessageLevel=Qgis::MessageLevel::Info)
emit a message
Multi point geometry collection.
Definition: qgsmultipoint.h:30
bool addGeometry(QgsAbstractGeometry *g) override
Adds a geometry and takes ownership. Returns true in case of success.
A class to represent a 2D point.
Definition: qgspointxy.h:59
double y
Definition: qgspointxy.h:63
Q_GADGET double x
Definition: qgspointxy.h:62
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
Q_GADGET double x
Definition: qgspoint.h:52
double z
Definition: qgspoint.h:54
double m
Definition: qgspoint.h:55
double y
Definition: qgspoint.h:53
Polygon geometry type.
Definition: qgspolygon.h:34
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:467
void cleared()
Emitted when the project is cleared (and additionally when an open project is cleared just before a n...
QList< QgsVectorLayer * > avoidIntersectionsLayers
Definition: qgsproject.h:113
@ AvoidIntersectionsLayers
Overlap with features from a specified list of layers when digitizing new features not allowed.
@ AllowIntersections
Overlap with any feature allowed when digitizing new features.
@ AvoidIntersectionsCurrentLayer
Overlap with features from the active layer when digitizing new features not allowed.
void readProject(const QDomDocument &)
Emitted when a project is being read.
This is the base class for vector data providers.
@ CircularGeometries
Supports circular geometry types (circularstring, compoundcurve, curvepolygon)
@ AddFeatures
Allows adding features.
virtual Q_INVOKABLE QgsVectorDataProvider::Capabilities capabilities() const
Returns flags containing the supported capabilities.
Represents a vector layer which manages a vector based data sets.
Q_INVOKABLE QgsWkbTypes::Type wkbType() const FINAL
Returns the WKBType or WKBUnknown in case of error.
Q_INVOKABLE QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
bool isEditable() const FINAL
Returns true if the provider is in editing mode.
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
static bool isMultiType(Type type) SIP_HOLDGIL
Returns true if the WKB type is a multi type.
Definition: qgswkbtypes.h:832
static bool hasM(Type type) SIP_HOLDGIL
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:1100
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:70
static Type singleType(Type type) SIP_HOLDGIL
Returns the single type for a WKB type.
Definition: qgswkbtypes.h:157
static bool hasZ(Type type) SIP_HOLDGIL
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:1050