QGIS API Documentation  2.0.1-Dufour
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
qgsvectorlayereditutils.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsvectorlayereditutils.cpp
3  ---------------------
4  begin : Dezember 2012
5  copyright : (C) 2012 by Martin Dobias
6  email : wonder dot 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  ***************************************************************************/
16 
17 #include "qgsvectordataprovider.h"
18 #include "qgsgeometrycache.h"
20 
21 #include <limits>
22 
23 
25  : L( layer )
26 {
27 }
28 
29 bool QgsVectorLayerEditUtils::insertVertex( double x, double y, QgsFeatureId atFeatureId, int beforeVertex )
30 {
31  if ( !L->hasGeometryType() )
32  return false;
33 
34  QgsGeometry geometry;
35  if ( !cache()->geometry( atFeatureId, geometry ) )
36  return false; // TODO: support also uncached geometries
37 
38  geometry.insertVertex( x, y, beforeVertex );
39 
40  L->editBuffer()->changeGeometry( atFeatureId, &geometry );
41  return true;
42 }
43 
44 
45 bool QgsVectorLayerEditUtils::moveVertex( double x, double y, QgsFeatureId atFeatureId, int atVertex )
46 {
47  if ( !L->hasGeometryType() )
48  return false;
49 
50  QgsGeometry geometry;
51  if ( !cache()->geometry( atFeatureId, geometry ) )
52  return false; // TODO: support also uncached geometries
53 
54  geometry.moveVertex( x, y, atVertex );
55 
56  L->editBuffer()->changeGeometry( atFeatureId, &geometry );
57  return true;
58 }
59 
60 
61 bool QgsVectorLayerEditUtils::deleteVertex( QgsFeatureId atFeatureId, int atVertex )
62 {
63  if ( !L->hasGeometryType() )
64  return false;
65 
66  QgsGeometry geometry;
67  if ( !cache()->geometry( atFeatureId, geometry ) )
68  return false; // TODO: support also uncached geometries
69 
70  if ( !geometry.deleteVertex( atVertex ) )
71  return false;
72 
73  L->editBuffer()->changeGeometry( atFeatureId, &geometry );
74  return true;
75 }
76 
77 
78 int QgsVectorLayerEditUtils::addRing( const QList<QgsPoint>& ring )
79 {
80  if ( !L->hasGeometryType() )
81  return 5;
82 
83  int addRingReturnCode = 5; //default: return code for 'ring not inserted'
84  double xMin, yMin, xMax, yMax;
85  QgsRectangle bBox;
86 
87  if ( boundingBoxFromPointList( ring, xMin, yMin, xMax, yMax ) == 0 )
88  {
89  bBox.setXMinimum( xMin ); bBox.setYMinimum( yMin );
90  bBox.setXMaximum( xMax ); bBox.setYMaximum( yMax );
91  }
92  else
93  {
94  return 3; //ring not valid
95  }
96 
97  QgsFeatureIterator fit = L->getFeatures( QgsFeatureRequest().setFilterRect( bBox ).setFlags( QgsFeatureRequest::ExactIntersect ) );
98 
99  QgsFeature f;
100  while ( fit.nextFeature( f ) )
101  {
102  addRingReturnCode = f.geometry()->addRing( ring );
103  if ( addRingReturnCode == 0 )
104  {
105  L->editBuffer()->changeGeometry( f.id(), f.geometry() );
106 
107  //setModified( true, true );
108  break;
109  }
110  }
111 
112  return addRingReturnCode;
113 }
114 
115 
116 int QgsVectorLayerEditUtils::addPart( const QList<QgsPoint> &points, QgsFeatureId featureId )
117 {
118  if ( !L->hasGeometryType() )
119  return 6;
120 
121  QgsGeometry geometry;
122  if ( !cache()->geometry( featureId, geometry ) ) // maybe it's in cache
123  {
124  // it's not in cache: let's fetch it from layer
125  QgsFeature f;
126  if ( !L->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) || !f.geometry() )
127  return 6; //geometry not found
128 
129  geometry = *f.geometry();
130  }
131 
132  int errorCode = geometry.addPart( points );
133  if ( errorCode == 0 )
134  {
135  L->editBuffer()->changeGeometry( featureId, &geometry );
136  }
137  return errorCode;
138 }
139 
140 
141 
142 int QgsVectorLayerEditUtils::translateFeature( QgsFeatureId featureId, double dx, double dy )
143 {
144  if ( !L->hasGeometryType() )
145  return 1;
146 
147  QgsGeometry geometry;
148  if ( !cache()->geometry( featureId, geometry ) ) // maybe it's in cache
149  {
150  // it's not in cache: let's fetch it from layer
151  QgsFeature f;
152  if ( !L->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) || !f.geometry() )
153  return 1; //geometry not found
154 
155  geometry = *f.geometry();
156  }
157 
158  int errorCode = geometry.translate( dx, dy );
159  if ( errorCode == 0 )
160  {
161  L->editBuffer()->changeGeometry( featureId, &geometry );
162  }
163  return errorCode;
164 }
165 
166 
167 int QgsVectorLayerEditUtils::splitFeatures( const QList<QgsPoint>& splitLine, bool topologicalEditing )
168 {
169  if ( !L->hasGeometryType() )
170  return 4;
171 
172  QgsFeatureList newFeatures; //store all the newly created features
173  double xMin, yMin, xMax, yMax;
174  QgsRectangle bBox; //bounding box of the split line
175  int returnCode = 0;
176  int splitFunctionReturn; //return code of QgsGeometry::splitGeometry
177  int numberOfSplittedFeatures = 0;
178 
179  QgsFeatureList featureList;
180  const QgsFeatureIds selectedIds = L->selectedFeaturesIds();
181 
182  if ( selectedIds.size() > 0 ) //consider only the selected features if there is a selection
183  {
184  featureList = L->selectedFeatures();
185  }
186  else //else consider all the feature that intersect the bounding box of the split line
187  {
188  if ( boundingBoxFromPointList( splitLine, xMin, yMin, xMax, yMax ) == 0 )
189  {
190  bBox.setXMinimum( xMin ); bBox.setYMinimum( yMin );
191  bBox.setXMaximum( xMax ); bBox.setYMaximum( yMax );
192  }
193  else
194  {
195  return 1;
196  }
197 
198  if ( bBox.isEmpty() )
199  {
200  //if the bbox is a line, try to make a square out of it
201  if ( bBox.width() == 0.0 && bBox.height() > 0 )
202  {
203  bBox.setXMinimum( bBox.xMinimum() - bBox.height() / 2 );
204  bBox.setXMaximum( bBox.xMaximum() + bBox.height() / 2 );
205  }
206  else if ( bBox.height() == 0.0 && bBox.width() > 0 )
207  {
208  bBox.setYMinimum( bBox.yMinimum() - bBox.width() / 2 );
209  bBox.setYMaximum( bBox.yMaximum() + bBox.width() / 2 );
210  }
211  else
212  {
213  return 2;
214  }
215  }
216 
217  QgsFeatureIterator fit = L->getFeatures( QgsFeatureRequest().setFilterRect( bBox ).setFlags( QgsFeatureRequest::ExactIntersect ) );
218 
219  QgsFeature f;
220  while ( fit.nextFeature( f ) )
221  featureList << QgsFeature( f );
222  }
223 
224  QgsFeatureList::iterator select_it = featureList.begin();
225  for ( ; select_it != featureList.end(); ++select_it )
226  {
227  if ( !select_it->geometry() )
228  {
229  continue;
230  }
231  QList<QgsGeometry*> newGeometries;
232  QList<QgsPoint> topologyTestPoints;
233  QgsGeometry* newGeometry = 0;
234  splitFunctionReturn = select_it->geometry()->splitGeometry( splitLine, newGeometries, topologicalEditing, topologyTestPoints );
235  if ( splitFunctionReturn == 0 )
236  {
237  //change this geometry
238  L->editBuffer()->changeGeometry( select_it->id(), select_it->geometry() );
239 
240  //insert new features
241  for ( int i = 0; i < newGeometries.size(); ++i )
242  {
243  newGeometry = newGeometries.at( i );
244  QgsFeature newFeature;
245  newFeature.setGeometry( newGeometry );
246 
247  //use default value where possible for primary key (e.g. autoincrement),
248  //and use the value from the original (split) feature if not primary key
249  QgsAttributes newAttributes = select_it->attributes();
250  foreach ( int pkIdx, L->dataProvider()->pkAttributeIndexes() )
251  {
252  const QVariant defaultValue = L->dataProvider()->defaultValue( pkIdx );
253  if ( !defaultValue.isNull() )
254  {
255  newAttributes[ pkIdx ] = defaultValue;
256  }
257  else //try with NULL
258  {
259  newAttributes[ pkIdx ] = QVariant();
260  }
261  }
262 
263  newFeature.setAttributes( newAttributes );
264 
265  newFeatures.append( newFeature );
266  }
267 
268  if ( topologicalEditing )
269  {
270  QList<QgsPoint>::const_iterator topol_it = topologyTestPoints.constBegin();
271  for ( ; topol_it != topologyTestPoints.constEnd(); ++topol_it )
272  {
273  addTopologicalPoints( *topol_it );
274  }
275  }
276  ++numberOfSplittedFeatures;
277  }
278  else if ( splitFunctionReturn > 1 ) //1 means no split but also no error
279  {
280  returnCode = splitFunctionReturn;
281  }
282  }
283 
284  if ( numberOfSplittedFeatures == 0 && selectedIds.size() > 0 )
285  {
286  //There is a selection but no feature has been split.
287  //Maybe user forgot that only the selected features are split
288  returnCode = 4;
289  }
290 
291 
292  //now add the new features to this vectorlayer
293  L->editBuffer()->addFeatures( newFeatures );
294 
295  return returnCode;
296 }
297 
298 
299 
301 {
302  if ( !L->hasGeometryType() )
303  return 1;
304 
305  if ( !geom )
306  {
307  return 1;
308  }
309 
310  int returnVal = 0;
311 
312  QGis::WkbType wkbType = geom->wkbType();
313 
314  switch ( wkbType )
315  {
316  //line
318  case QGis::WKBLineString:
319  {
320  QgsPolyline theLine = geom->asPolyline();
321  QgsPolyline::const_iterator line_it = theLine.constBegin();
322  for ( ; line_it != theLine.constEnd(); ++line_it )
323  {
324  if ( addTopologicalPoints( *line_it ) != 0 )
325  {
326  returnVal = 2;
327  }
328  }
329  break;
330  }
331 
332  //multiline
335  {
336  QgsMultiPolyline theMultiLine = geom->asMultiPolyline();
337  QgsPolyline currentPolyline;
338 
339  for ( int i = 0; i < theMultiLine.size(); ++i )
340  {
341  QgsPolyline::const_iterator line_it = currentPolyline.constBegin();
342  for ( ; line_it != currentPolyline.constEnd(); ++line_it )
343  {
344  if ( addTopologicalPoints( *line_it ) != 0 )
345  {
346  returnVal = 2;
347  }
348  }
349  }
350  break;
351  }
352 
353  //polygon
354  case QGis::WKBPolygon25D:
355  case QGis::WKBPolygon:
356  {
357  QgsPolygon thePolygon = geom->asPolygon();
358  QgsPolyline currentRing;
359 
360  for ( int i = 0; i < thePolygon.size(); ++i )
361  {
362  currentRing = thePolygon.at( i );
363  QgsPolyline::const_iterator line_it = currentRing.constBegin();
364  for ( ; line_it != currentRing.constEnd(); ++line_it )
365  {
366  if ( addTopologicalPoints( *line_it ) != 0 )
367  {
368  returnVal = 2;
369  }
370  }
371  }
372  break;
373  }
374 
375  //multipolygon
378  {
379  QgsMultiPolygon theMultiPolygon = geom->asMultiPolygon();
380  QgsPolygon currentPolygon;
381  QgsPolyline currentRing;
382 
383  for ( int i = 0; i < theMultiPolygon.size(); ++i )
384  {
385  currentPolygon = theMultiPolygon.at( i );
386  for ( int j = 0; j < currentPolygon.size(); ++j )
387  {
388  currentRing = currentPolygon.at( j );
389  QgsPolyline::const_iterator line_it = currentRing.constBegin();
390  for ( ; line_it != currentRing.constEnd(); ++line_it )
391  {
392  if ( addTopologicalPoints( *line_it ) != 0 )
393  {
394  returnVal = 2;
395  }
396  }
397  }
398  }
399  break;
400  }
401  default:
402  break;
403  }
404  return returnVal;
405 }
406 
407 
409 {
410  if ( !L->hasGeometryType() )
411  return 1;
412 
413  QMultiMap<double, QgsSnappingResult> snapResults; //results from the snapper object
414  //we also need to snap to vertex to make sure the vertex does not already exist in this geometry
415  QMultiMap<double, QgsSnappingResult> vertexSnapResults;
416 
417  QList<QgsSnappingResult> filteredSnapResults; //we filter out the results that are on existing vertices
418 
419  //work with a tolerance because coordinate projection may introduce some rounding
420  double threshold = 0.0000001;
421  if ( L->crs().mapUnits() == QGis::Meters )
422  {
423  threshold = 0.001;
424  }
425  else if ( L->crs().mapUnits() == QGis::Feet )
426  {
427  threshold = 0.0001;
428  }
429 
430 
431  if ( L->snapWithContext( p, threshold, snapResults, QgsSnapper::SnapToSegment ) != 0 )
432  {
433  return 2;
434  }
435 
436  QMultiMap<double, QgsSnappingResult>::const_iterator snap_it = snapResults.constBegin();
437  QMultiMap<double, QgsSnappingResult>::const_iterator vertex_snap_it;
438  for ( ; snap_it != snapResults.constEnd(); ++snap_it )
439  {
440  //test if p is already a vertex of this geometry. If yes, don't insert it
441  bool vertexAlreadyExists = false;
442  if ( L->snapWithContext( p, threshold, vertexSnapResults, QgsSnapper::SnapToVertex ) != 0 )
443  {
444  continue;
445  }
446 
447  vertex_snap_it = vertexSnapResults.constBegin();
448  for ( ; vertex_snap_it != vertexSnapResults.constEnd(); ++vertex_snap_it )
449  {
450  if ( snap_it.value().snappedAtGeometry == vertex_snap_it.value().snappedAtGeometry )
451  {
452  vertexAlreadyExists = true;
453  }
454  }
455 
456  if ( !vertexAlreadyExists )
457  {
458  filteredSnapResults.push_back( *snap_it );
459  }
460  }
461  insertSegmentVerticesForSnap( filteredSnapResults );
462  return 0;
463 }
464 
465 
466 int QgsVectorLayerEditUtils::insertSegmentVerticesForSnap( const QList<QgsSnappingResult>& snapResults )
467 {
468  if ( !L->hasGeometryType() )
469  return 1;
470 
471  int returnval = 0;
472  QgsPoint layerPoint;
473 
474  QList<QgsSnappingResult>::const_iterator it = snapResults.constBegin();
475  for ( ; it != snapResults.constEnd(); ++it )
476  {
477  if ( it->snappedVertexNr == -1 ) // segment snap
478  {
479  layerPoint = it->snappedVertex;
480  if ( !insertVertex( layerPoint.x(), layerPoint.y(), it->snappedAtGeometry, it->afterVertexNr ) )
481  {
482  returnval = 3;
483  }
484  }
485  }
486  return returnval;
487 }
488 
489 
490 
491 
492 int QgsVectorLayerEditUtils::boundingBoxFromPointList( const QList<QgsPoint>& list, double& xmin, double& ymin, double& xmax, double& ymax ) const
493 {
494  if ( list.size() < 1 )
495  {
496  return 1;
497  }
498 
503 
504  for ( QList<QgsPoint>::const_iterator it = list.constBegin(); it != list.constEnd(); ++it )
505  {
506  if ( it->x() < xmin )
507  {
508  xmin = it->x();
509  }
510  if ( it->x() > xmax )
511  {
512  xmax = it->x();
513  }
514  if ( it->y() < ymin )
515  {
516  ymin = it->y();
517  }
518  if ( it->y() > ymax )
519  {
520  ymax = it->y();
521  }
522  }
523 
524  return 0;
525 }
QgsFeatureId id() const
Get the feature id for this feature.
Definition: qgsfeature.cpp:101
Wrapper for iterator of features from vector data provider or vector layer.
A rectangle specified with double values.
Definition: qgsrectangle.h:35
bool isEmpty() const
test if rectangle is empty
int insertSegmentVerticesForSnap(const QList< QgsSnappingResult > &snapResults)
Inserts vertices to the snapped segments.
QgsFeatureList selectedFeatures()
Get a copy of the user-selected features.
void setXMaximum(double x)
Set the maximum x value.
Definition: qgsrectangle.h:160
bool addFeatures(QgsFeatureList &features)
Insert a copy of the given features into the layer (but does not commit it)
Use exact geometry intersection (slower) instead of bounding boxes.
double yMaximum() const
Get the y maximum value (top side of rectangle)
Definition: qgsrectangle.h:185
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeature.h:321
QList< QgsFeature > QgsFeatureList
Definition: qgsfeature.h:326
QgsMultiPolyline asMultiPolyline() const
return contents of the geometry as a multi linestring if wkbType is WKBMultiLineString, otherwise an empty list
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest())
Query the provider for features specified in request.
QVector< QgsPoint > QgsPolyline
polyline is represented as a vector of points
Definition: qgsgeometry.h:38
QgsGeometry * geometry() const
Get the geometry object associated with this feature.
Definition: qgsfeature.cpp:113
QgsPolygon asPolygon() const
return contents of the geometry as a polygon if wkbType is WKBPolygon, otherwise an empty list ...
bool moveVertex(double x, double y, int atVertex)
Moves the vertex at the given position number and item (first number is index 0) to the given coordin...
int addPart(const QList< QgsPoint > &points)
Adds a new island polygon to a multipolygon feature.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:72
void setAttributes(const QgsAttributes &attrs)
Definition: qgsfeature.h:145
WkbType
Used for symbology operations.
Definition: qgis.h:53
int addPart(const QList< QgsPoint > &ring, QgsFeatureId featureId)
Adds a new part polygon to a multipart feature.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:114
virtual QgsAttributeList pkAttributeIndexes()
Return list of indexes of fields that make up the primary key.
bool insertVertex(double x, double y, int beforeVertex)
Insert a new vertex before the given vertex index, ring and item (first number is index 0) If the req...
bool deleteVertex(QgsFeatureId atFeatureId, int atVertex)
Deletes a vertex from a feature.
double x() const
Definition: qgspoint.h:110
int boundingBoxFromPointList(const QList< QgsPoint > &list, double &xmin, double &ymin, double &xmax, double &ymax) const
Little helper function that gives bounding box from a list of points.
QgsMultiPolygon asMultiPolygon() const
return contents of the geometry as a multi polygon if wkbType is WKBMultiPolygon, otherwise an empty ...
double ANALYSIS_EXPORT max(double x, double y)
returns the maximum of two doubles or the first argument if both are equal
int splitGeometry(const QList< QgsPoint > &splitLine, QList< QgsGeometry * > &newGeometries, bool topological, QList< QgsPoint > &topologyTestPoints)
Splits this geometry according to a given line.
void setGeometry(const QgsGeometry &geom)
Set this feature's geometry from another QgsGeometry object (deep copy)
Definition: qgsfeature.cpp:135
QgsVectorLayerEditBuffer * editBuffer()
Buffer with uncommitted editing operations. Only valid after editing has been turned on...
double yMinimum() const
Get the y minimum value (bottom side of rectangle)
Definition: qgsrectangle.h:190
double xMaximum() const
Get the x maximum value (right side of rectangle)
Definition: qgsrectangle.h:175
const QgsFeatureIds & selectedFeaturesIds() const
Return reference to identifiers of selected features.
int snapWithContext(const QgsPoint &startPoint, double snappingTolerance, QMultiMap< double, QgsSnappingResult > &snappingResults, QgsSnapper::SnappingType snap_to)
Snaps to segment or vertex within given tolerance.
bool geometry(QgsFeatureId fid, QgsGeometry &geometry)
fetch geometry from cache, return true if successful
int addTopologicalPoints(QgsGeometry *geom)
Adds topological points for every vertex of the geometry.
void setYMinimum(double y)
Set the minimum y value.
Definition: qgsrectangle.h:165
bool deleteVertex(int atVertex)
Deletes the vertex at the given position number and item (first number is index 0) Returns false if a...
int translateFeature(QgsFeatureId featureId, double dx, double dy)
Translates feature by dx, dy.
bool moveVertex(double x, double y, QgsFeatureId atFeatureId, int atVertex)
Moves the vertex at the given position number, ring and item (first number is index 0)...
This class wraps a request for features to a vector layer (or directly its vector data provider)...
QVector< QgsPolygon > QgsMultiPolygon
a collection of QgsPolygons that share a common collection of attributes
Definition: qgsgeometry.h:53
QList< int > QgsAttributeList
QGis::WkbType wkbType() const
Returns type of wkb (point / linestring / polygon etc.)
QVector< QgsPolyline > QgsPolygon
polygon: first item of the list is outer ring, inner rings (if any) start from second item ...
Definition: qgsgeometry.h:44
A class to represent a point geometry.
Definition: qgspoint.h:63
int translate(double dx, double dy)
Translate this geometry by dx, dy.
QVector< QgsPolyline > QgsMultiPolyline
a collection of QgsPolylines that share a common collection of attributes
Definition: qgsgeometry.h:50
QgsPolyline asPolyline() const
return contents of the geometry as a polyline if wkbType is WKBLineString, otherwise an empty list ...
bool insertVertex(double x, double y, QgsFeatureId atFeatureId, int beforeVertex)
Insert a new vertex before the given vertex number, in the given ring, item (first number is index 0)...
bool hasGeometryType() const
Returns true if this is a geometry layer and false in case of NoGeometry (table only) or UnknownGeome...
int addRing(const QList< QgsPoint > &ring)
Adds a ring to polygon/multipolygon features.
virtual QVariant defaultValue(int fieldId)
Returns the default value for field specified by fieldId.
QVector< QVariant > QgsAttributes
Definition: qgsfeature.h:100
void setYMaximum(double y)
Set the maximum y value.
Definition: qgsrectangle.h:170
qint64 QgsFeatureId
Definition: qgsfeature.h:30
double y() const
Definition: qgspoint.h:118
const QgsCoordinateReferenceSystem & crs() const
Returns layer's spatial reference system.
int addRing(const QList< QgsPoint > &ring)
Adds a new ring to this geometry.
QgsVectorDataProvider * dataProvider()
Returns the data provider.
bool nextFeature(QgsFeature &f)
double width() const
Width of the rectangle.
Definition: qgsrectangle.h:195
Represents a vector layer which manages a vector based data sets.
double xMinimum() const
Get the x minimum value (left side of rectangle)
Definition: qgsrectangle.h:180
bool changeGeometry(QgsFeatureId fid, QgsGeometry *geom)
change feature's geometry
int splitFeatures(const QList< QgsPoint > &splitLine, bool topologicalEditing=false)
Splits features cut by the given line.
void setXMinimum(double x)
Set the minimum x value.
Definition: qgsrectangle.h:155
double height() const
Height of the rectangle.
Definition: qgsrectangle.h:200
QgsVectorLayerEditUtils(QgsVectorLayer *layer)