QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
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 
51 void QgsMapToolDigitizeFeature::digitized( const QgsFeature &f )
52 {
53  emit digitizingCompleted( f );
54 }
55 
57 {
58  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mLayer );
59  if ( !vlayer )
60  vlayer = currentVectorLayer();
61 
62  if ( vlayer && vlayer->geometryType() == QgsWkbTypes::NullGeometry )
63  {
64  QgsFeature f;
65  digitized( f );
66  return;
67  }
68 
69  if ( mLayer )
70  {
71  //remember current layer
72  mCurrentLayer = mCanvas->currentLayer();
73  //set the layer with the given
74  mCanvas->setCurrentLayer( mLayer );
75  }
76 
78 }
79 
81 {
83 
84  if ( mCurrentLayer )
85  //set the layer back to the one remembered
86  mCanvas->setCurrentLayer( mCurrentLayer );
87  emit digitizingFinished();
88 }
89 
91 {
92  return mCheckGeometryType;
93 }
94 
96 {
97  mCheckGeometryType = checkGeometryType;
98 }
99 
101 {
102  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mLayer );
103 
104  if ( !vlayer )
105  //if no given layer take the current from canvas
106  vlayer = currentVectorLayer();
107 
108  if ( !vlayer )
109  {
111  return;
112  }
113 
114  QgsWkbTypes::Type layerWKBType = vlayer->wkbType();
115 
116  QgsVectorDataProvider *provider = vlayer->dataProvider();
117 
118  if ( !( provider->capabilities() & QgsVectorDataProvider::AddFeatures ) )
119  {
120  emit messageEmitted( tr( "The data provider for this layer does not support the addition of features." ), Qgis::Warning );
121  return;
122  }
123 
124  if ( !vlayer->isEditable() )
125  {
127  return;
128  }
129 
130  // POINT CAPTURING
131  if ( mode() == CapturePoint )
132  {
133  if ( e->button() != Qt::LeftButton )
134  return;
135 
136  //check we only use this tool for point/multipoint layers
137  if ( vlayer->geometryType() != QgsWkbTypes::PointGeometry && mCheckGeometryType )
138  {
139  emit messageEmitted( tr( "Wrong editing tool, cannot apply the 'capture point' tool on this vector layer" ), Qgis::Warning );
140  return;
141  }
142 
143  QgsPoint savePoint; //point in layer coordinates
144  bool isMatchPointZ = false;
145  try
146  {
147  QgsPoint fetchPoint;
148  int res;
149  res = fetchLayerPoint( e->mapPointMatch(), fetchPoint );
150  if ( QgsWkbTypes::hasZ( fetchPoint.wkbType() ) )
151  isMatchPointZ = true;
152 
153  if ( res == 0 )
154  {
155  if ( isMatchPointZ )
156  savePoint = fetchPoint;
157  else
158  savePoint = QgsPoint( fetchPoint.x(), fetchPoint.y() );
159  }
160  else
161  {
162  QgsPointXY layerPoint = toLayerCoordinates( vlayer, e->mapPoint() );
163  if ( isMatchPointZ )
164  savePoint = QgsPoint( QgsWkbTypes::PointZ, layerPoint.x(), layerPoint.y(), fetchPoint.z() );
165  else
166  savePoint = QgsPoint( layerPoint.x(), layerPoint.y() );
167  }
168  }
169  catch ( QgsCsException &cse )
170  {
171  Q_UNUSED( cse )
172  emit messageEmitted( tr( "Cannot transform the point to the layers coordinate system" ), Qgis::Warning );
173  return;
174  }
175 
176  //only do the rest for provider with feature addition support
177  //note that for the grass provider, this will return false since
178  //grass provider has its own mechanism of feature addition
180  {
181  QgsFeature f( vlayer->fields() );
182 
183  QgsGeometry g;
184  if ( layerWKBType == QgsWkbTypes::Point )
185  {
186  g = QgsGeometry( qgis::make_unique<QgsPoint>( savePoint ) );
187  }
188  else if ( !QgsWkbTypes::isMultiType( layerWKBType ) && QgsWkbTypes::hasZ( layerWKBType ) )
189  {
190  g = QgsGeometry( qgis::make_unique<QgsPoint>( savePoint.x(), savePoint.y(), isMatchPointZ ? savePoint.z() : defaultZValue() ) );
191  }
192  else if ( QgsWkbTypes::isMultiType( layerWKBType ) && !QgsWkbTypes::hasZ( layerWKBType ) )
193  {
194  g = QgsGeometry::fromMultiPointXY( QgsMultiPointXY() << savePoint );
195  }
196  else if ( QgsWkbTypes::isMultiType( layerWKBType ) && QgsWkbTypes::hasZ( layerWKBType ) )
197  {
198  QgsMultiPoint *mp = new QgsMultiPoint();
199  mp->addGeometry( new QgsPoint( QgsWkbTypes::PointZ, savePoint.x(), savePoint.y(), isMatchPointZ ? savePoint.z() : defaultZValue() ) );
200  g.set( mp );
201  }
202  else
203  {
204  // if layer supports more types (mCheckGeometryType is false)
205  g = QgsGeometry( qgis::make_unique<QgsPoint>( savePoint ) );
206  }
207 
208  if ( QgsWkbTypes::hasM( layerWKBType ) )
209  {
210  g.get()->addMValue();
211  }
212 
213  f.setGeometry( g );
214  f.setValid( true );
215 
216  // The snapping result needs to be added so it's available in the @snapping_results variable of default value etc. expression contexts
217  addVertex( e->mapPoint(), e->mapPointMatch() );
218 
219  digitized( f );
220 
221  stopCapturing();
222 
223  // we are done with digitizing for now so instruct advanced digitizing dock to reset its CAD points
225  }
226  }
227 
228  // LINE AND POLYGON CAPTURING
229  else if ( mode() == CaptureLine || mode() == CapturePolygon )
230  {
231  //check we only use the line tool for line/multiline layers
232  if ( mode() == CaptureLine && vlayer->geometryType() != QgsWkbTypes::LineGeometry && mCheckGeometryType )
233  {
234  emit messageEmitted( tr( "Wrong editing tool, cannot apply the 'capture line' tool on this vector layer" ), Qgis::Warning );
235  return;
236  }
237 
238  //check we only use the polygon tool for polygon/multipolygon layers
239  if ( mode() == CapturePolygon && vlayer->geometryType() != QgsWkbTypes::PolygonGeometry && mCheckGeometryType )
240  {
241  emit messageEmitted( tr( "Wrong editing tool, cannot apply the 'capture polygon' tool on this vector layer" ), Qgis::Warning );
242  return;
243  }
244 
245  //add point to list and to rubber band
246  if ( e->button() == Qt::LeftButton )
247  {
248  int error = addVertex( e->mapPoint(), e->mapPointMatch() );
249  if ( error == 1 )
250  {
251  //current layer is not a vector layer
252  return;
253  }
254  else if ( error == 2 )
255  {
256  //problem with coordinate transformation
257  emit messageEmitted( tr( "Cannot transform the point to the layers coordinate system" ), Qgis::Warning );
258  return;
259  }
260 
261  startCapturing();
262  }
263  else if ( e->button() == Qt::RightButton )
264  {
265  // End of string
267 
268  //lines: bail out if there are not at least two vertices
269  if ( mode() == CaptureLine && size() < 2 )
270  {
271  stopCapturing();
272  return;
273  }
274 
275  //polygons: bail out if there are not at least two vertices
276  if ( mode() == CapturePolygon && size() < 3 )
277  {
278  stopCapturing();
279  return;
280  }
281 
282  if ( mode() == CapturePolygon || e->modifiers() == Qt::ShiftModifier )
283  {
284  closePolygon();
285  }
286 
287  //create QgsFeature with wkb representation
288  std::unique_ptr< QgsFeature > f( new QgsFeature( vlayer->fields(), 0 ) );
289 
290  //does compoundcurve contain circular strings?
291  //does provider support circular strings?
292  bool hasCurvedSegments = captureCurve()->hasCurvedSegments();
293  bool providerSupportsCurvedSegments = vlayer->dataProvider()->capabilities() & QgsVectorDataProvider::CircularGeometries;
294 
295  QList<QgsPointLocator::Match> snappingMatchesList;
296  QgsCurve *curveToAdd = nullptr;
297  if ( hasCurvedSegments && providerSupportsCurvedSegments )
298  {
299  curveToAdd = captureCurve()->clone();
300  }
301  else
302  {
303  curveToAdd = captureCurve()->curveToLine();
304  snappingMatchesList = snappingMatches();
305  }
306 
307  if ( mode() == CaptureLine )
308  {
309  QgsGeometry g( curveToAdd );
310  f->setGeometry( g );
311  }
312  else
313  {
314  QgsCurvePolygon *poly = nullptr;
315  if ( hasCurvedSegments && providerSupportsCurvedSegments )
316  {
317  poly = new QgsCurvePolygon();
318  }
319  else
320  {
321  poly = new QgsPolygon();
322  }
323  poly->setExteriorRing( curveToAdd );
324  QgsGeometry g( poly );
325  f->setGeometry( g );
326 
327  QList<QgsVectorLayer *> avoidIntersectionsLayers;
328  switch ( QgsProject::instance()->avoidIntersectionsMode() )
329  {
331  avoidIntersectionsLayers.append( vlayer );
332  break;
334  avoidIntersectionsLayers = QgsProject::instance()->avoidIntersectionsLayers();
335  break;
337  break;
338  }
339  if ( avoidIntersectionsLayers.size() > 0 )
340  {
341  QgsGeometry featGeom = f->geometry();
342  int avoidIntersectionsReturn = featGeom.avoidIntersections( avoidIntersectionsLayers );
343  f->setGeometry( featGeom );
344  if ( avoidIntersectionsReturn == 3 )
345  {
346  emit messageEmitted( tr( "The feature has been added, but at least one geometry intersected is invalid. These geometries must be manually repaired." ), Qgis::Warning );
347  }
348  if ( f->geometry().isEmpty() ) //avoid intersection might have removed the whole geometry
349  {
350  emit messageEmitted( tr( "The feature cannot be added because its geometry collapsed due to intersection avoidance" ), Qgis::Critical );
351  stopCapturing();
352  return;
353  }
354  }
355  }
356  f->setValid( true );
357 
358  digitized( *f );
359 
360  stopCapturing();
361  }
362  }
363 }
364 
366 {
367  mLayer = vl;
368 }
@ Warning
Definition: qgis.h:91
@ Critical
Definition: qgis.h:92
virtual bool addMValue(double mValue=0)=0
Adds a measure to the geometry, initialized to a preset value.
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 id, geometry and a list of field/values...
Definition: qgsfeature.h:56
void setValid(bool validity)
Sets the validity of the feature.
Definition: qgsfeature.cpp:195
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Definition: qgsfeature.cpp:144
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:124
static QgsGeometry fromMultiPointXY(const QgsMultiPointXY &multipoint)
Creates a new geometry from a QgsMultiPointXY object.
QgsAbstractGeometry * get()
Returns a modifiable (non-const) reference to the underlying abstract geometry primitive.
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:86
void setCurrentLayer(QgsMapLayer *layer)
QgsMapLayer * currentLayer()
returns current layer (set by legend widget)
Base class for all map layer types.
Definition: qgsmaplayer.h:85
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.
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)
@ 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.
QgsMapToolCapture::Capabilities capabilities() const override
Returns flags containing the supported capabilities.
double defaultZValue() const
Returns default Z value Use for set Z coordinate to new vertex for 2.5d geometries.
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.
QgsVectorLayer * currentVectorLayer()
Returns the current vector layer of the map canvas or 0.
QgsMapCanvas * mCanvas
The pointer to the map canvas.
Definition: qgsmaptool.h:288
QString mToolName
The translated name of the map tool.
Definition: qgsmaptool.h:306
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::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:44
double y
Definition: qgspointxy.h:48
Q_GADGET double x
Definition: qgspointxy.h:47
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:38
Q_GADGET double x
Definition: qgspoint.h:41
double z
Definition: qgspoint.h:43
double y
Definition: qgspoint.h:42
Polygon geometry type.
Definition: qgspolygon.h:34
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:501
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:111
@ 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 bool hasZ(Type type) SIP_HOLDGIL
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:1050
QVector< QgsPointXY > QgsMultiPointXY
A collection of QgsPoints that share a common collection of attributes.
Definition: qgsgeometry.h:81