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 }