QGIS API Documentation  3.13.0-Master (740be229cb)
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 void QgsMapToolDigitizeFeature::digitized( const QgsFeature &f )
47 {
48  emit digitizingCompleted( f );
49 }
50 
52 {
53  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mLayer );
54  if ( !vlayer )
55  vlayer = currentVectorLayer();
56 
57  if ( vlayer && vlayer->geometryType() == QgsWkbTypes::NullGeometry )
58  {
59  QgsFeature f;
60  digitized( f );
61  return;
62  }
63 
64  if ( mLayer )
65  {
66  //remember current layer
67  mCurrentLayer = mCanvas->currentLayer();
68  //set the layer with the given
69  mCanvas->setCurrentLayer( mLayer );
70  }
71 
73 }
74 
76 {
78 
79  if ( mCurrentLayer )
80  //set the layer back to the one remembered
81  mCanvas->setCurrentLayer( mCurrentLayer );
82  emit digitizingFinished();
83 }
84 
86 {
87  return mCheckGeometryType;
88 }
89 
91 {
92  mCheckGeometryType = checkGeometryType;
93 }
94 
96 {
97  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mLayer );
98 
99  if ( !vlayer )
100  //if no given layer take the current from canvas
101  vlayer = currentVectorLayer();
102 
103  if ( !vlayer )
104  {
106  return;
107  }
108 
109  QgsWkbTypes::Type layerWKBType = vlayer->wkbType();
110 
111  QgsVectorDataProvider *provider = vlayer->dataProvider();
112 
113  if ( !( provider->capabilities() & QgsVectorDataProvider::AddFeatures ) )
114  {
115  emit messageEmitted( tr( "The data provider for this layer does not support the addition of features." ), Qgis::Warning );
116  return;
117  }
118 
119  if ( !vlayer->isEditable() )
120  {
122  return;
123  }
124 
125  // POINT CAPTURING
126  if ( mode() == CapturePoint )
127  {
128  if ( e->button() != Qt::LeftButton )
129  return;
130 
131  //check we only use this tool for point/multipoint layers
132  if ( vlayer->geometryType() != QgsWkbTypes::PointGeometry && mCheckGeometryType )
133  {
134  emit messageEmitted( tr( "Wrong editing tool, cannot apply the 'capture point' tool on this vector layer" ), Qgis::Warning );
135  return;
136  }
137 
138  QgsPoint savePoint; //point in layer coordinates
139  bool isMatchPointZ = false;
140  try
141  {
142  QgsPoint fetchPoint;
143  int res;
144  res = fetchLayerPoint( e->mapPointMatch(), fetchPoint );
145  if ( QgsWkbTypes::hasZ( fetchPoint.wkbType() ) )
146  isMatchPointZ = true;
147 
148  if ( res == 0 )
149  {
150  if ( isMatchPointZ )
151  savePoint = fetchPoint;
152  else
153  savePoint = QgsPoint( fetchPoint.x(), fetchPoint.y() );
154  }
155  else
156  {
157  QgsPointXY layerPoint = toLayerCoordinates( vlayer, e->mapPoint() );
158  if ( isMatchPointZ )
159  savePoint = QgsPoint( QgsWkbTypes::PointZ, layerPoint.x(), layerPoint.y(), fetchPoint.z() );
160  else
161  savePoint = QgsPoint( layerPoint.x(), layerPoint.y() );
162  }
163  }
164  catch ( QgsCsException &cse )
165  {
166  Q_UNUSED( cse )
167  emit messageEmitted( tr( "Cannot transform the point to the layers coordinate system" ), Qgis::Warning );
168  return;
169  }
170 
171  //only do the rest for provider with feature addition support
172  //note that for the grass provider, this will return false since
173  //grass provider has its own mechanism of feature addition
175  {
176  QgsFeature f( vlayer->fields(), 0 );
177 
178  QgsGeometry g;
179  if ( layerWKBType == QgsWkbTypes::Point )
180  {
181  g = QgsGeometry( qgis::make_unique<QgsPoint>( savePoint ) );
182  }
183  else if ( !QgsWkbTypes::isMultiType( layerWKBType ) && QgsWkbTypes::hasZ( layerWKBType ) )
184  {
185  g = QgsGeometry( qgis::make_unique<QgsPoint>( savePoint.x(), savePoint.y(), isMatchPointZ ? savePoint.z() : defaultZValue() ) );
186  }
187  else if ( QgsWkbTypes::isMultiType( layerWKBType ) && !QgsWkbTypes::hasZ( layerWKBType ) )
188  {
189  g = QgsGeometry::fromMultiPointXY( QgsMultiPointXY() << savePoint );
190  }
191  else if ( QgsWkbTypes::isMultiType( layerWKBType ) && QgsWkbTypes::hasZ( layerWKBType ) )
192  {
193  QgsMultiPoint *mp = new QgsMultiPoint();
194  mp->addGeometry( new QgsPoint( QgsWkbTypes::PointZ, savePoint.x(), savePoint.y(), isMatchPointZ ? savePoint.z() : defaultZValue() ) );
195  g.set( mp );
196  }
197  else
198  {
199  // if layer supports more types (mCheckGeometryType is false)
200  g = QgsGeometry( qgis::make_unique<QgsPoint>( savePoint ) );
201  }
202 
203  if ( QgsWkbTypes::hasM( layerWKBType ) )
204  {
205  g.get()->addMValue();
206  }
207 
208  f.setGeometry( g );
209  f.setValid( true );
210 
211  // The snapping result needs to be added so it's available in the @snapping_results variable of default value etc. expression contexts
212  addVertex( e->mapPoint(), e->mapPointMatch() );
213 
214  digitized( f );
215 
216  stopCapturing();
217 
218  // we are done with digitizing for now so instruct advanced digitizing dock to reset its CAD points
220  }
221  }
222 
223  // LINE AND POLYGON CAPTURING
224  else if ( mode() == CaptureLine || mode() == CapturePolygon )
225  {
226  //check we only use the line tool for line/multiline layers
227  if ( mode() == CaptureLine && vlayer->geometryType() != QgsWkbTypes::LineGeometry && mCheckGeometryType )
228  {
229  emit messageEmitted( tr( "Wrong editing tool, cannot apply the 'capture line' tool on this vector layer" ), Qgis::Warning );
230  return;
231  }
232 
233  //check we only use the polygon tool for polygon/multipolygon layers
234  if ( mode() == CapturePolygon && vlayer->geometryType() != QgsWkbTypes::PolygonGeometry && mCheckGeometryType )
235  {
236  emit messageEmitted( tr( "Wrong editing tool, cannot apply the 'capture polygon' tool on this vector layer" ), Qgis::Warning );
237  return;
238  }
239 
240  //add point to list and to rubber band
241  if ( e->button() == Qt::LeftButton )
242  {
243  int error = addVertex( e->mapPoint(), e->mapPointMatch() );
244  if ( error == 1 )
245  {
246  //current layer is not a vector layer
247  return;
248  }
249  else if ( error == 2 )
250  {
251  //problem with coordinate transformation
252  emit messageEmitted( tr( "Cannot transform the point to the layers coordinate system" ), Qgis::Warning );
253  return;
254  }
255 
256  startCapturing();
257  }
258  else if ( e->button() == Qt::RightButton )
259  {
260  // End of string
262 
263  //lines: bail out if there are not at least two vertices
264  if ( mode() == CaptureLine && size() < 2 )
265  {
266  stopCapturing();
267  return;
268  }
269 
270  //polygons: bail out if there are not at least two vertices
271  if ( mode() == CapturePolygon && size() < 3 )
272  {
273  stopCapturing();
274  return;
275  }
276 
277  if ( mode() == CapturePolygon || e->modifiers() == Qt::ShiftModifier )
278  {
279  closePolygon();
280  }
281 
282  //create QgsFeature with wkb representation
283  std::unique_ptr< QgsFeature > f( new QgsFeature( vlayer->fields(), 0 ) );
284 
285  //does compoundcurve contain circular strings?
286  //does provider support circular strings?
287  bool hasCurvedSegments = captureCurve()->hasCurvedSegments();
288  bool providerSupportsCurvedSegments = vlayer->dataProvider()->capabilities() & QgsVectorDataProvider::CircularGeometries;
289 
290  QList<QgsPointLocator::Match> snappingMatchesList;
291  QgsCurve *curveToAdd = nullptr;
292  if ( hasCurvedSegments && providerSupportsCurvedSegments )
293  {
294  curveToAdd = captureCurve()->clone();
295  }
296  else
297  {
298  curveToAdd = captureCurve()->curveToLine();
299  snappingMatchesList = snappingMatches();
300  }
301 
302  if ( mode() == CaptureLine )
303  {
304  QgsGeometry g( curveToAdd );
305  f->setGeometry( g );
306  }
307  else
308  {
309  QgsCurvePolygon *poly = nullptr;
310  if ( hasCurvedSegments && providerSupportsCurvedSegments )
311  {
312  poly = new QgsCurvePolygon();
313  }
314  else
315  {
316  poly = new QgsPolygon();
317  }
318  poly->setExteriorRing( curveToAdd );
319  QgsGeometry g( poly );
320  f->setGeometry( g );
321 
322  QgsGeometry featGeom = f->geometry();
323  int avoidIntersectionsReturn = featGeom.avoidIntersections( QgsProject::instance()->avoidIntersectionsLayers() );
324  f->setGeometry( featGeom );
325  if ( avoidIntersectionsReturn == 1 )
326  {
327  //not a polygon type. Impossible to get there
328  }
329  if ( f->geometry().isEmpty() ) //avoid intersection might have removed the whole geometry
330  {
331  emit messageEmitted( tr( "The feature cannot be added because it's geometry collapsed due to intersection avoidance" ), Qgis::Critical );
332  stopCapturing();
333  return;
334  }
335  }
336  f->setValid( true );
337 
338  digitized( *f );
339 
340  stopCapturing();
341  }
342  }
343 }
344 
346 {
347  mLayer = vl;
348 }
virtual void setExteriorRing(QgsCurve *ring)
Sets the exterior ring of the polygon.
QgsCompoundCurve * clone() const override
Clones the geometry by performing a deep copy.
Base class for all map layer types.
Definition: qgsmaplayer.h:80
double y
Definition: qgspoint.h:42
void clearPoints()
Removes all points from the CAD point list.
virtual QgsVectorDataProvider::Capabilities capabilities() const
Returns flags containing the supported capabilities.
CaptureMode
Different capture modes.
int size()
Number of points digitized.
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...
Multi point geometry collection.
Definition: qgsmultipoint.h:29
static bool isMultiType(Type type)
Returns true if the WKB type is a multi type.
Definition: qgswkbtypes.h:706
Q_INVOKABLE QgsWkbTypes::Type wkbType() const FINAL
Returns the WKBType or WKBUnknown in case of error.
void messageEmitted(const QString &message, Qgis::MessageLevel=Qgis::Info)
emit a message
double y
Definition: qgspointxy.h:48
A class to represent a 2D point.
Definition: qgspointxy.h:43
void digitizingCompleted(const QgsFeature &feature)
Emitted whenever the digitizing has been successfully completed.
void setCurrentLayer(QgsMapLayer *layer)
Curve polygon geometry type.
A QgsMapMouseEvent is the result of a user interaction with the mouse on a QgsMapCanvas.
Q_INVOKABLE QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
void setCheckGeometryType(bool checkGeometryType)
Check if CaptureMode matches layer type.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:122
QVector< QgsPointXY > QgsMultiPointXY
A collection of QgsPoints that share a common collection of attributes.
Definition: qgsgeometry.h:80
const QgsCompoundCurve * captureCurve() const
Gets the capture curve.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
static bool hasZ(Type type)
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:917
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:78
QString mToolName
translated name of the map tool
Definition: qgsmaptool.h:259
QgsMapCanvas * mCanvas
pointer to map canvas
Definition: qgsmaptool.h:241
bool isEditable() const FINAL
Returns true if the provider is in editing mode.
void notifyNotEditableLayer()
Display a timed message bar noting the active vector layer is not editable.
CaptureMode mode() const
The capture mode.
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:68
QgsFields fields() const FINAL
Returns the list of fields of this layer.
void setLayer(QgsMapLayer *vl)
Change the layer edited by the map tool.
void deactivate() override
Unregisters this maptool from the cad dock widget.
void readProject(const QDomDocument &)
Emitted when a project is being read.
bool hasCurvedSegments() const override
Returns true if the geometry contains curved segments.
void activate() override
Registers this maptool with the cad dock widget.
static QgsGeometry fromMultiPointXY(const QgsMultiPointXY &multipoint)
Creates a new geometry from a QgsMultiPointXY object.
void cadCanvasReleaseEvent(QgsMapMouseEvent *e) override
Override this method when subclassing this class.
Abstract base class for curved geometry type.
Definition: qgscurve.h:35
double defaultZValue() const
Returns default Z value Use for set Z coordinate to new vertex for 2.5d geometries.
QgsWkbTypes::Type wkbType() const
Returns the WKB type of the geometry.
QList< QgsPointLocator::Match > snappingMatches() const
Returns a list of matches for each point on the captureCurve.
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:37
double x
Definition: qgspointxy.h:47
void digitizingFinished()
Emitted whenever the digitizing has been ended without digitizing any feature.
Supports circular geometry types (circularstring, compoundcurve, curvepolygon)
QgsMapLayer * currentLayer()
returns current layer (set by legend widget)
void activate() override
Registers this maptool with the cad dock widget.
QgsPointLocator::Match mapPointMatch() const
Returns the matching data from the most recently snapped point.
bool addGeometry(QgsAbstractGeometry *g) override
Adds a geometry and takes ownership. Returns true in case of success.
void setValid(bool validity)
Sets the validity of the feature.
Definition: qgsfeature.cpp:188
QgsAdvancedDigitizingDockWidget * cadDockWidget() const
QgsVectorLayer * currentVectorLayer()
Returns the current vector layer of the map canvas or 0.
void closePolygon()
Close an open polygon.
void deleteTempRubberBand()
Clean a temporary rubberband.
QgsPointXY toLayerCoordinates(const QgsMapLayer *layer, QPoint point)
transformation from screen coordinates to layer&#39;s coordinates
Definition: qgsmaptool.cpp:54
void deactivate() override
Unregisters this maptool from the cad dock widget.
void notifyNotVectorLayer()
Display a timed message bar noting the active layer is not vector.
void startCapturing()
Start capturing.
void cleared()
Emitted when the project is cleared (and additionally when an open project is cleared just before a n...
QgsPointXY mapPoint() const
mapPoint returns the point in coordinates
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:457
void setGeometry(const QgsGeometry &geometry)
Set the feature&#39;s geometry.
Definition: qgsfeature.cpp:137
QgsMapToolDigitizeFeature(QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDockWidget, CaptureMode mode=QgsMapToolCapture::CaptureNone)
QgsMapToolDigitizeFeature is a map tool to digitize a feature geometry.
double z
Definition: qgspoint.h:43
static bool hasM(Type type)
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:967
void stopCapturing()
Stop capturing.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:65
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer&#39;s data provider, it may be nullptr.
This is the base class for vector data providers.
Polygon geometry type.
Definition: qgspolygon.h:31
The QgsAdvancedDigitizingDockWidget class is a dockable widget used to handle the CAD tools on top of...
Represents a vector layer which manages a vector based data sets.
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...
bool checkGeometryType() const
Check if CaptureMode matches layer type.
int addVertex(const QgsPointXY &point)
Adds a point to the rubber band (in map coordinates) and to the capture list (in layer coordinates) ...
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.
double x
Definition: qgspoint.h:41