QGIS API Documentation  2.99.0-Master (40f86b2)
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 "qgsfeatureiterator.h"
19 #include "qgsgeometrycache.h"
21 #include "qgslinestring.h"
22 #include "qgslogger.h"
23 #include "qgspointv2.h"
24 #include "qgsgeometryfactory.h"
25 #include "qgis.h"
26 #include "qgswkbtypes.h"
27 #include "qgsvectorlayerutils.h"
28 
29 #include <limits>
30 
31 
33  : L( layer )
34 {
35 }
36 
37 bool QgsVectorLayerEditUtils::insertVertex( double x, double y, QgsFeatureId atFeatureId, int beforeVertex )
38 {
39  if ( !L->hasGeometryType() )
40  return false;
41 
42  QgsGeometry geometry;
43  if ( !cache()->geometry( atFeatureId, geometry ) )
44  {
45  // it's not in cache: let's fetch it from layer
46  QgsFeature f;
47  if ( !L->getFeatures( QgsFeatureRequest().setFilterFid( atFeatureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) || !f.hasGeometry() )
48  return false; // geometry not found
49 
50  geometry = f.geometry();
51  }
52 
53  geometry.insertVertex( x, y, beforeVertex );
54 
55  L->editBuffer()->changeGeometry( atFeatureId, geometry );
56  return true;
57 }
58 
59 bool QgsVectorLayerEditUtils::insertVertex( const QgsPointV2 &point, QgsFeatureId atFeatureId, int beforeVertex )
60 {
61  if ( !L->hasGeometryType() )
62  return false;
63 
64  QgsGeometry geometry;
65  if ( !cache()->geometry( atFeatureId, geometry ) )
66  {
67  // it's not in cache: let's fetch it from layer
68  QgsFeature f;
69  if ( !L->getFeatures( QgsFeatureRequest().setFilterFid( atFeatureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) || !f.hasGeometry() )
70  return false; // geometry not found
71 
72  geometry = f.geometry();
73  }
74 
75  geometry.insertVertex( point, beforeVertex );
76 
77  L->editBuffer()->changeGeometry( atFeatureId, geometry );
78  return true;
79 }
80 
81 bool QgsVectorLayerEditUtils::moveVertex( double x, double y, QgsFeatureId atFeatureId, int atVertex )
82 {
83  QgsPointV2 p( x, y );
84  return moveVertex( p, atFeatureId, atVertex );
85 }
86 
87 bool QgsVectorLayerEditUtils::moveVertex( const QgsPointV2 &p, QgsFeatureId atFeatureId, int atVertex )
88 {
89  if ( !L->hasGeometryType() )
90  return false;
91 
92  QgsGeometry geometry;
93  if ( !cache()->geometry( atFeatureId, geometry ) )
94  {
95  // it's not in cache: let's fetch it from layer
96  QgsFeature f;
97  if ( !L->getFeatures( QgsFeatureRequest().setFilterFid( atFeatureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) || !f.hasGeometry() )
98  return false; // geometry not found
99 
100  geometry = f.geometry();
101  }
102 
103  geometry.moveVertex( p, atVertex );
104 
105  L->editBuffer()->changeGeometry( atFeatureId, geometry );
106  return true;
107 }
108 
109 
111 {
112  if ( !L->hasGeometryType() )
114 
115  QgsGeometry geometry;
116  if ( !cache()->geometry( featureId, geometry ) )
117  {
118  // it's not in cache: let's fetch it from layer
119  QgsFeature f;
120  if ( !L->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) || !f.hasGeometry() )
121  return QgsVectorLayer::FetchFeatureFailed; // geometry not found
122 
123  geometry = f.geometry();
124  }
125 
126  if ( !geometry.deleteVertex( vertex ) )
128 
129  if ( geometry.geometry() && geometry.geometry()->nCoordinates() == 0 )
130  {
131  //last vertex deleted, set geometry to null
132  geometry.setGeometry( nullptr );
133  }
134 
135  L->editBuffer()->changeGeometry( featureId, geometry );
137 }
138 
139 int QgsVectorLayerEditUtils::addRing( const QList<QgsPoint> &ring, const QgsFeatureIds &targetFeatureIds, QgsFeatureId *modifiedFeatureId )
140 {
141  QgsLineString *ringLine = new QgsLineString();
142  QgsPointSequence ringPoints;
143  QList<QgsPoint>::const_iterator ringIt = ring.constBegin();
144  for ( ; ringIt != ring.constEnd(); ++ringIt )
145  {
146  ringPoints.append( QgsPointV2( ringIt->x(), ringIt->y() ) );
147  }
148  ringLine->setPoints( ringPoints );
149  return addRing( ringLine, targetFeatureIds, modifiedFeatureId );
150 }
151 
152 int QgsVectorLayerEditUtils::addRing( QgsCurve *ring, const QgsFeatureIds &targetFeatureIds, QgsFeatureId *modifiedFeatureId )
153 {
154  if ( !L->hasGeometryType() )
155  {
156  delete ring;
157  return 5;
158  }
159 
160  int addRingReturnCode = 5; //default: return code for 'ring not inserted'
161  QgsFeature f;
162 
163  QgsFeatureIterator fit;
164  if ( !targetFeatureIds.isEmpty() )
165  {
166  //check only specified features
167  fit = L->getFeatures( QgsFeatureRequest().setFilterFids( targetFeatureIds ) );
168  }
169  else
170  {
171  //check all intersecting features
172  QgsRectangle bBox = ring->boundingBox();
173  fit = L->getFeatures( QgsFeatureRequest().setFilterRect( bBox ).setFlags( QgsFeatureRequest::ExactIntersect ) );
174  }
175 
176  //find first valid feature we can add the ring to
177  while ( fit.nextFeature( f ) )
178  {
179  if ( !f.hasGeometry() )
180  continue;
181 
182  //add ring takes ownership of ring, and deletes it if there's an error
183  QgsGeometry g = f.geometry();
184 
185  addRingReturnCode = g.addRing( static_cast< QgsCurve * >( ring->clone() ) );
186  if ( addRingReturnCode == 0 )
187  {
188  L->editBuffer()->changeGeometry( f.id(), g );
189  if ( modifiedFeatureId )
190  *modifiedFeatureId = f.id();
191 
192  //setModified( true, true );
193  break;
194  }
195  }
196 
197  delete ring;
198  return addRingReturnCode;
199 }
200 
201 int QgsVectorLayerEditUtils::addPart( const QList<QgsPoint> &points, QgsFeatureId featureId )
202 {
204  for ( QList<QgsPoint>::const_iterator it = points.constBegin(); it != points.constEnd(); ++it )
205  {
206  l << QgsPointV2( *it );
207  }
208  return addPart( l, featureId );
209 }
210 
212 {
213  if ( !L->hasGeometryType() )
214  return 6;
215 
216  QgsGeometry geometry;
217  bool firstPart = false;
218  if ( !cache()->geometry( featureId, geometry ) ) // maybe it's in cache
219  {
220  // it's not in cache: let's fetch it from layer
221  QgsFeature f;
222  if ( !L->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) )
223  return 6; //not found
224 
225  if ( !f.hasGeometry() )
226  {
227  //no existing geometry, so adding first part to null geometry
228  firstPart = true;
229  }
230  else
231  {
232  geometry = f.geometry();
233  }
234  }
235 
236  int errorCode = geometry.addPart( points, L->geometryType() ) ;
237  if ( errorCode == 0 )
238  {
239  if ( firstPart && QgsWkbTypes::isSingleType( L->wkbType() )
241  {
242  //convert back to single part if required by layer
243  geometry.convertToSingleType();
244  }
245  L->editBuffer()->changeGeometry( featureId, geometry );
246  }
247  return errorCode;
248 }
249 
251 {
252  if ( !L->hasGeometryType() )
253  return 6;
254 
255  QgsGeometry geometry;
256  bool firstPart = false;
257  if ( !cache()->geometry( featureId, geometry ) ) // maybe it's in cache
258  {
259  // it's not in cache: let's fetch it from layer
260  QgsFeature f;
261  if ( !L->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) )
262  return 6; //not found
263 
264  if ( !f.hasGeometry() )
265  {
266  //no existing geometry, so adding first part to null geometry
267  firstPart = true;
268  }
269  else
270  {
271  geometry = f.geometry();
272  }
273  }
274 
275  int errorCode = geometry.addPart( ring, L->geometryType() ) ;
276  if ( errorCode == 0 )
277  {
278  if ( firstPart && QgsWkbTypes::isSingleType( L->wkbType() )
280  {
281  //convert back to single part if required by layer
282  geometry.convertToSingleType();
283  }
284  L->editBuffer()->changeGeometry( featureId, geometry );
285  }
286  return errorCode;
287 }
288 
289 
290 int QgsVectorLayerEditUtils::translateFeature( QgsFeatureId featureId, double dx, double dy )
291 {
292  if ( !L->hasGeometryType() )
293  return 1;
294 
295  QgsGeometry geometry;
296  if ( !cache()->geometry( featureId, geometry ) ) // maybe it's in cache
297  {
298  // it's not in cache: let's fetch it from layer
299  QgsFeature f;
300  if ( !L->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) || !f.hasGeometry() )
301  return 1; //geometry not found
302 
303  geometry = f.geometry();
304  }
305 
306  int errorCode = geometry.translate( dx, dy );
307  if ( errorCode == 0 )
308  {
309  L->editBuffer()->changeGeometry( featureId, geometry );
310  }
311  return errorCode;
312 }
313 
314 
315 int QgsVectorLayerEditUtils::splitFeatures( const QList<QgsPoint> &splitLine, bool topologicalEditing )
316 {
317  if ( !L->hasGeometryType() )
318  return 4;
319 
320  QgsFeatureList newFeatures; //store all the newly created features
321  double xMin, yMin, xMax, yMax;
322  QgsRectangle bBox; //bounding box of the split line
323  int returnCode = 0;
324  int splitFunctionReturn; //return code of QgsGeometry::splitGeometry
325  int numberOfSplitFeatures = 0;
326 
327  QgsFeatureIterator features;
328  const QgsFeatureIds selectedIds = L->selectedFeatureIds();
329 
330  if ( !selectedIds.isEmpty() ) //consider only the selected features if there is a selection
331  {
332  features = L->selectedFeaturesIterator();
333  }
334  else //else consider all the feature that intersect the bounding box of the split line
335  {
336  if ( boundingBoxFromPointList( splitLine, xMin, yMin, xMax, yMax ) == 0 )
337  {
338  bBox.setXMinimum( xMin );
339  bBox.setYMinimum( yMin );
340  bBox.setXMaximum( xMax );
341  bBox.setYMaximum( yMax );
342  }
343  else
344  {
345  return 1;
346  }
347 
348  if ( bBox.isEmpty() )
349  {
350  //if the bbox is a line, try to make a square out of it
351  if ( bBox.width() == 0.0 && bBox.height() > 0 )
352  {
353  bBox.setXMinimum( bBox.xMinimum() - bBox.height() / 2 );
354  bBox.setXMaximum( bBox.xMaximum() + bBox.height() / 2 );
355  }
356  else if ( bBox.height() == 0.0 && bBox.width() > 0 )
357  {
358  bBox.setYMinimum( bBox.yMinimum() - bBox.width() / 2 );
359  bBox.setYMaximum( bBox.yMaximum() + bBox.width() / 2 );
360  }
361  else
362  {
363  //If we have a single point, we still create a non-null box
364  double bufferDistance = 0.000001;
365  if ( L->crs().isGeographic() )
366  bufferDistance = 0.00000001;
367  bBox.setXMinimum( bBox.xMinimum() - bufferDistance );
368  bBox.setXMaximum( bBox.xMaximum() + bufferDistance );
369  bBox.setYMinimum( bBox.yMinimum() - bufferDistance );
370  bBox.setYMaximum( bBox.yMaximum() + bufferDistance );
371  }
372  }
373 
374  features = L->getFeatures( QgsFeatureRequest().setFilterRect( bBox ).setFlags( QgsFeatureRequest::ExactIntersect ) );
375  }
376 
377  QgsFeature feat;
378  while ( features.nextFeature( feat ) )
379  {
380  if ( !feat.hasGeometry() )
381  {
382  continue;
383  }
384  QList<QgsGeometry> newGeometries;
385  QList<QgsPoint> topologyTestPoints;
386  QgsGeometry featureGeom = feat.geometry();
387  splitFunctionReturn = featureGeom.splitGeometry( splitLine, newGeometries, topologicalEditing, topologyTestPoints );
388  if ( splitFunctionReturn == 0 )
389  {
390  //change this geometry
391  L->editBuffer()->changeGeometry( feat.id(), featureGeom );
392 
393  //insert new features
394  for ( int i = 0; i < newGeometries.size(); ++i )
395  {
396  QgsFeature f = QgsVectorLayerUtils::createFeature( L, newGeometries.at( i ), feat.attributes().toMap() );
397  L->editBuffer()->addFeature( f );
398  }
399 
400  if ( topologicalEditing )
401  {
402  QList<QgsPoint>::const_iterator topol_it = topologyTestPoints.constBegin();
403  for ( ; topol_it != topologyTestPoints.constEnd(); ++topol_it )
404  {
405  addTopologicalPoints( *topol_it );
406  }
407  }
408  ++numberOfSplitFeatures;
409  }
410  else if ( splitFunctionReturn > 1 ) //1 means no split but also no error
411  {
412  returnCode = splitFunctionReturn;
413  }
414  }
415 
416  if ( numberOfSplitFeatures == 0 && !selectedIds.isEmpty() )
417  {
418  //There is a selection but no feature has been split.
419  //Maybe user forgot that only the selected features are split
420  returnCode = 4;
421  }
422 
423  return returnCode;
424 }
425 
426 int QgsVectorLayerEditUtils::splitParts( const QList<QgsPoint> &splitLine, bool topologicalEditing )
427 {
428  if ( !L->hasGeometryType() )
429  return 4;
430 
431  double xMin, yMin, xMax, yMax;
432  QgsRectangle bBox; //bounding box of the split line
433  int returnCode = 0;
434  int splitFunctionReturn; //return code of QgsGeometry::splitGeometry
435  int numberOfSplitParts = 0;
436 
437  QgsFeatureIterator fit;
438 
439  if ( L->selectedFeatureCount() > 0 ) //consider only the selected features if there is a selection
440  {
441  fit = L->selectedFeaturesIterator();
442  }
443  else //else consider all the feature that intersect the bounding box of the split line
444  {
445  if ( boundingBoxFromPointList( splitLine, xMin, yMin, xMax, yMax ) == 0 )
446  {
447  bBox.setXMinimum( xMin );
448  bBox.setYMinimum( yMin );
449  bBox.setXMaximum( xMax );
450  bBox.setYMaximum( yMax );
451  }
452  else
453  {
454  return 1;
455  }
456 
457  if ( bBox.isEmpty() )
458  {
459  //if the bbox is a line, try to make a square out of it
460  if ( bBox.width() == 0.0 && bBox.height() > 0 )
461  {
462  bBox.setXMinimum( bBox.xMinimum() - bBox.height() / 2 );
463  bBox.setXMaximum( bBox.xMaximum() + bBox.height() / 2 );
464  }
465  else if ( bBox.height() == 0.0 && bBox.width() > 0 )
466  {
467  bBox.setYMinimum( bBox.yMinimum() - bBox.width() / 2 );
468  bBox.setYMaximum( bBox.yMaximum() + bBox.width() / 2 );
469  }
470  else
471  {
472  //If we have a single point, we still create a non-null box
473  double bufferDistance = 0.000001;
474  if ( L->crs().isGeographic() )
475  bufferDistance = 0.00000001;
476  bBox.setXMinimum( bBox.xMinimum() - bufferDistance );
477  bBox.setXMaximum( bBox.xMaximum() + bufferDistance );
478  bBox.setYMinimum( bBox.yMinimum() - bufferDistance );
479  bBox.setYMaximum( bBox.yMaximum() + bufferDistance );
480  }
481  }
482 
483  fit = L->getFeatures( QgsFeatureRequest().setFilterRect( bBox ).setFlags( QgsFeatureRequest::ExactIntersect ) );
484  }
485 
486  int addPartRet = 0;
487 
488  QgsFeature feat;
489  while ( fit.nextFeature( feat ) )
490  {
491  QList<QgsGeometry> newGeometries;
492  QList<QgsPoint> topologyTestPoints;
493  QgsGeometry featureGeom = feat.geometry();
494  splitFunctionReturn = featureGeom.splitGeometry( splitLine, newGeometries, topologicalEditing, topologyTestPoints );
495  if ( splitFunctionReturn == 0 )
496  {
497  //add new parts
498  if ( !newGeometries.isEmpty() )
499  featureGeom.convertToMultiType();
500 
501  for ( int i = 0; i < newGeometries.size(); ++i )
502  {
503  addPartRet = featureGeom.addPart( newGeometries.at( i ) );
504  if ( addPartRet )
505  break;
506  }
507 
508  // For test only: Exception already thrown here...
509  // feat.geometry()->asWkb();
510 
511  if ( !addPartRet )
512  {
513  L->editBuffer()->changeGeometry( feat.id(), featureGeom );
514  }
515  else
516  {
517  // Test addPartRet
518  switch ( addPartRet )
519  {
520  case 1:
521  QgsDebugMsg( "Not a multipolygon" );
522  break;
523 
524  case 2:
525  QgsDebugMsg( "Not a valid geometry" );
526  break;
527 
528  case 3:
529  QgsDebugMsg( "New polygon ring" );
530  break;
531  }
532  }
533  L->editBuffer()->changeGeometry( feat.id(), featureGeom );
534 
535  if ( topologicalEditing )
536  {
537  QList<QgsPoint>::const_iterator topol_it = topologyTestPoints.constBegin();
538  for ( ; topol_it != topologyTestPoints.constEnd(); ++topol_it )
539  {
540  addTopologicalPoints( *topol_it );
541  }
542  }
543  ++numberOfSplitParts;
544  }
545  else if ( splitFunctionReturn > 1 ) //1 means no split but also no error
546  {
547  returnCode = splitFunctionReturn;
548  }
549  }
550 
551  if ( numberOfSplitParts == 0 && L->selectedFeatureCount() > 0 && returnCode == 0 )
552  {
553  //There is a selection but no feature has been split.
554  //Maybe user forgot that only the selected features are split
555  returnCode = 4;
556  }
557 
558  return returnCode;
559 }
560 
561 
563 {
564  if ( !L->hasGeometryType() )
565  return 1;
566 
567  if ( geom.isNull() )
568  {
569  return 1;
570  }
571 
572  int returnVal = 0;
573 
574  QgsWkbTypes::Type wkbType = geom.wkbType();
575 
576  switch ( wkbType )
577  {
578  //line
581  {
582  QgsPolyline line = geom.asPolyline();
583  QgsPolyline::const_iterator line_it = line.constBegin();
584  for ( ; line_it != line.constEnd(); ++line_it )
585  {
586  if ( addTopologicalPoints( *line_it ) != 0 )
587  {
588  returnVal = 2;
589  }
590  }
591  break;
592  }
593 
594  //multiline
597  {
598  QgsMultiPolyline multiLine = geom.asMultiPolyline();
599  QgsPolyline currentPolyline;
600 
601  for ( int i = 0; i < multiLine.size(); ++i )
602  {
603  QgsPolyline::const_iterator line_it = currentPolyline.constBegin();
604  for ( ; line_it != currentPolyline.constEnd(); ++line_it )
605  {
606  if ( addTopologicalPoints( *line_it ) != 0 )
607  {
608  returnVal = 2;
609  }
610  }
611  }
612  break;
613  }
614 
615  //polygon
618  {
619  QgsPolygon polygon = geom.asPolygon();
620  QgsPolyline currentRing;
621 
622  for ( int i = 0; i < polygon.size(); ++i )
623  {
624  currentRing = polygon.at( i );
625  QgsPolyline::const_iterator line_it = currentRing.constBegin();
626  for ( ; line_it != currentRing.constEnd(); ++line_it )
627  {
628  if ( addTopologicalPoints( *line_it ) != 0 )
629  {
630  returnVal = 2;
631  }
632  }
633  }
634  break;
635  }
636 
637  //multipolygon
640  {
641  QgsMultiPolygon multiPolygon = geom.asMultiPolygon();
642  QgsPolygon currentPolygon;
643  QgsPolyline currentRing;
644 
645  for ( int i = 0; i < multiPolygon.size(); ++i )
646  {
647  currentPolygon = multiPolygon.at( i );
648  for ( int j = 0; j < currentPolygon.size(); ++j )
649  {
650  currentRing = currentPolygon.at( j );
651  QgsPolyline::const_iterator line_it = currentRing.constBegin();
652  for ( ; line_it != currentRing.constEnd(); ++line_it )
653  {
654  if ( addTopologicalPoints( *line_it ) != 0 )
655  {
656  returnVal = 2;
657  }
658  }
659  }
660  }
661  break;
662  }
663  default:
664  break;
665  }
666  return returnVal;
667 }
668 
669 
671 {
672  if ( !L->hasGeometryType() )
673  return 1;
674 
675  QMultiMap<double, QgsSnappingResult> snapResults; //results from the snapper object
676  //we also need to snap to vertex to make sure the vertex does not already exist in this geometry
677  QMultiMap<double, QgsSnappingResult> vertexSnapResults;
678 
679  QList<QgsSnappingResult> filteredSnapResults; //we filter out the results that are on existing vertices
680 
681  //work with a tolerance because coordinate projection may introduce some rounding
682  double threshold = 0.0000001;
684  {
685  threshold = 0.001;
686  }
687  else if ( L->crs().mapUnits() == QgsUnitTypes::DistanceFeet )
688  {
689  threshold = 0.0001;
690  }
691 
692 
693  if ( L->snapWithContext( p, threshold, snapResults, QgsSnapper::SnapToSegment ) != 0 )
694  {
695  return 2;
696  }
697 
698  QMultiMap<double, QgsSnappingResult>::const_iterator snap_it = snapResults.constBegin();
699  QMultiMap<double, QgsSnappingResult>::const_iterator vertex_snap_it;
700  for ( ; snap_it != snapResults.constEnd(); ++snap_it )
701  {
702  //test if p is already a vertex of this geometry. If yes, don't insert it
703  bool vertexAlreadyExists = false;
704  if ( L->snapWithContext( p, threshold, vertexSnapResults, QgsSnapper::SnapToVertex ) != 0 )
705  {
706  continue;
707  }
708 
709  vertex_snap_it = vertexSnapResults.constBegin();
710  for ( ; vertex_snap_it != vertexSnapResults.constEnd(); ++vertex_snap_it )
711  {
712  if ( snap_it.value().snappedAtGeometry == vertex_snap_it.value().snappedAtGeometry )
713  {
714  vertexAlreadyExists = true;
715  }
716  }
717 
718  if ( !vertexAlreadyExists )
719  {
720  filteredSnapResults.push_back( *snap_it );
721  }
722  }
723  insertSegmentVerticesForSnap( filteredSnapResults );
724  return 0;
725 }
726 
727 
728 int QgsVectorLayerEditUtils::insertSegmentVerticesForSnap( const QList<QgsSnappingResult> &snapResults )
729 {
730  if ( !L->hasGeometryType() )
731  return 1;
732 
733  int returnval = 0;
734  QgsPoint layerPoint;
735 
736  QList<QgsSnappingResult>::const_iterator it = snapResults.constBegin();
737  for ( ; it != snapResults.constEnd(); ++it )
738  {
739  if ( it->snappedVertexNr == -1 ) // segment snap
740  {
741  layerPoint = it->snappedVertex;
742  if ( !insertVertex( layerPoint.x(), layerPoint.y(), it->snappedAtGeometry, it->afterVertexNr ) )
743  {
744  returnval = 3;
745  }
746  }
747  }
748  return returnval;
749 }
750 
751 
752 
753 
754 int QgsVectorLayerEditUtils::boundingBoxFromPointList( const QList<QgsPoint> &list, double &xmin, double &ymin, double &xmax, double &ymax ) const
755 {
756  if ( list.size() < 1 )
757  {
758  return 1;
759  }
760 
765 
766  for ( QList<QgsPoint>::const_iterator it = list.constBegin(); it != list.constEnd(); ++it )
767  {
768  if ( it->x() < xmin )
769  {
770  xmin = it->x();
771  }
772  if ( it->x() > xmax )
773  {
774  xmax = it->x();
775  }
776  if ( it->y() < ymin )
777  {
778  ymin = it->y();
779  }
780  if ( it->y() > ymax )
781  {
782  ymax = it->y();
783  }
784  }
785 
786  return 0;
787 }
QgsPolygon asPolygon() const
Return contents of the geometry as a polygon if wkbType is WKBPolygon, otherwise an empty list...
QgsFeatureId id
Definition: qgsfeature.h:140
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.
Wrapper for iterator of features from vector data provider or vector layer.
QgsMultiPolyline asMultiPolyline() const
Return contents of the geometry as a multi linestring if wkbType is WKBMultiLineString, otherwise an empty list.
A rectangle specified with double values.
Definition: qgsrectangle.h:36
double y
Definition: qgspoint.h:42
int insertSegmentVerticesForSnap(const QList< QgsSnappingResult > &snapResults)
Inserts vertices to the snapped segments.
static QgsFeature createFeature(QgsVectorLayer *layer, const QgsGeometry &geometry=QgsGeometry(), const QgsAttributeMap &attributes=QgsAttributeMap(), QgsExpressionContext *context=nullptr)
Creates a new feature ready for insertion into a layer.
void setPoints(const QgsPointSequence &points)
Resets the line string to match the specified list of points.
bool isNull() const
Returns true if the geometry is null (ie, contains no underlying geometry accessible via geometry)...
void setXMaximum(double x)
Set the maximum x value.
Definition: qgsrectangle.h:180
Use exact geometry intersection (slower) instead of bounding boxes.
virtual bool addFeature(QgsFeature &f)
Adds a feature.
#define QgsDebugMsg(str)
Definition: qgslogger.h:36
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeature.h:358
QList< QgsFeature > QgsFeatureList
Definition: qgsfeature.h:363
QgsWkbTypes::Type wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
QVector< QgsPoint > QgsPolyline
Polyline is represented as a vector of points.
Definition: qgsgeometry.h:47
int selectedFeatureCount() const
The number of features that are selected in this layer.
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...
QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:79
Edit operation resulted in an empty geometry.
int addPart(const QList< QgsPoint > &ring, QgsFeatureId featureId)
Adds a new part polygon to a multipart feature.
int addTopologicalPoints(const QgsGeometry &geom)
Adds topological points for every vertex of the geometry.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:136
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 hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:202
QgsPolyline asPolyline() const
Return contents of the geometry as a polyline if wkbType is WKBLineString, otherwise an empty list...
int splitGeometry(const QList< QgsPoint > &splitLine, QList< QgsGeometry > &newGeometries, bool topological, QList< QgsPoint > &topologyTestPoints)
Splits this geometry according to a given line.
bool hasGeometryType() const
Returns true if this is a geometry layer and false in case of NoGeometry (table only) or UnknownGeome...
virtual bool doesStrictFeatureTypeCheck() const
Returns true if the provider is strict about the type of inserted features (e.g.
QgsWkbTypes::Type wkbType() const
Returns the WKBType or WKBUnknown in case of error.
QgsVectorLayerEditBuffer * editBuffer()
Buffer with uncommitted editing operations. Only valid after editing has been turned on...
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:65
const QgsFeatureIds & selectedFeatureIds() const
Return reference to identifiers of selected features.
bool convertToSingleType()
Converts multi type geometry into single type geometry e.g.
bool isEmpty() const
test if rectangle is empty.
int snapWithContext(const QgsPoint &startPoint, double snappingTolerance, QMultiMap< double, QgsSnappingResult > &snappingResults, QgsSnapper::SnappingType snap_to)
Snaps to segment or vertex within given tolerance.
double width() const
Width of the rectangle.
Definition: qgsrectangle.h:215
Point geometry type, with support for z-dimension and m-values.
Definition: qgspointv2.h:36
bool geometry(QgsFeatureId fid, QgsGeometry &geometry)
fetch geometry from cache, return true if successful
void setYMinimum(double y)
Set the minimum y value.
Definition: qgsrectangle.h:185
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const
Query the layer for features specified in request.
bool deleteVertex(int atVertex)
Deletes the vertex at the given position number and item (first number is index 0) Returns false if a...
virtual QgsCurve * clone() const override=0
Clones the geometry by performing a deep copy.
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)...
virtual bool changeGeometry(QgsFeatureId fid, const QgsGeometry &geom)
Change feature&#39;s geometry.
QVector< QgsPolygon > QgsMultiPolygon
A collection of QgsPolygons that share a common collection of attributes.
Definition: qgsgeometry.h:62
QList< int > QgsAttributeList
QgsCoordinateReferenceSystem crs() const
Returns the layer&#39;s spatial reference system.
QgsAttributeMap toMap() const
Returns a QgsAttributeMap of the attribute values.
Definition: qgsfeature.cpp:33
bool isGeographic() const
Returns whether the CRS is a geographic CRS (using lat/lon coordinates)
Abstract base class for curved geometry type.
Definition: qgscurve.h:33
QVector< QgsPolyline > QgsPolygon
Polygon: first item of the list is outer ring, inner rings (if any) start from second item...
Definition: qgsgeometry.h:53
Edit operation failed.
QgsGeometry geometry() const
Returns the geometry associated with this feature.
Definition: qgsfeature.cpp:113
A class to represent a point.
Definition: qgspoint.h:37
double ANALYSIS_EXPORT max(double x, double y)
Returns the maximum of two doubles or the first argument if both are equal.
Definition: MathUtils.cc:437
QgsFeatureIterator selectedFeaturesIterator(QgsFeatureRequest request=QgsFeatureRequest()) const
Get an iterator of the selected features.
int translate(double dx, double dy)
Translate this geometry by dx, dy.
double yMinimum() const
Get the y minimum value (bottom side of rectangle)
Definition: qgsrectangle.h:210
QVector< QgsPolyline > QgsMultiPolyline
A collection of QgsPolylines that share a common collection of attributes.
Definition: qgsgeometry.h:59
static bool isSingleType(Type type)
Returns true if the WKB type is a single type.
Definition: qgswkbtypes.h:540
double xMaximum() const
Get the x maximum value (right side of rectangle)
Definition: qgsrectangle.h:195
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)...
virtual QgsRectangle boundingBox() const override
Returns the minimal bounding box for the geometry.
Definition: qgscurve.cpp:108
int addRing(const QList< QgsPoint > &ring, const QgsFeatureIds &targetFeatureIds=QgsFeatureIds(), QgsFeatureId *modifiedFeatureId=nullptr)
Adds a ring to polygon/multipolygon features.
bool convertToMultiType()
Converts single type geometry into multitype geometry e.g.
void setYMaximum(double y)
Set the maximum y value.
Definition: qgsrectangle.h:190
QgsMultiPolygon asMultiPolygon() const
Return contents of the geometry as a multi polygon if wkbType is WKBMultiPolygon, otherwise an empty ...
Edit operation was successful.
void setGeometry(QgsAbstractGeometry *geometry)
Sets the underlying geometry store.
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:36
QgsVectorLayer::EditResult deleteVertex(QgsFeatureId featureId, int vertex)
Deletes a vertex from a feature.
double xMinimum() const
Get the x minimum value (left side of rectangle)
Definition: qgsrectangle.h:200
qint64 QgsFeatureId
Definition: qgsfeature.h:33
double yMaximum() const
Get the y maximum value (top side of rectangle)
Definition: qgsrectangle.h:205
Edit failed due to invalid layer.
int addRing(const QList< QgsPoint > &ring)
Adds a new ring to this geometry.
QgsVectorDataProvider * dataProvider()
Returns the data provider.
bool nextFeature(QgsFeature &f)
QList< QgsPointV2 > QgsPointSequence
virtual int nCoordinates() const
Returns the number of nodes contained in the geometry.
Represents a vector layer which manages a vector based data sets.
int splitParts(const QList< QgsPoint > &splitLine, bool topologicalEditing=false)
Splits parts cut by the given line.
EditResult
Result of an edit operation.
QgsAbstractGeometry * geometry() const
Returns the underlying geometry store.
Unable to fetch requested feature.
int addPart(const QList< QgsPoint > &points, QgsWkbTypes::GeometryType geomType=QgsWkbTypes::UnknownGeometry)
Adds a new part to a the geometry.
QgsAttributes attributes
Definition: qgsfeature.h:141
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:175
double height() const
Height of the rectangle.
Definition: qgsrectangle.h:220
QgsVectorLayerEditUtils(QgsVectorLayer *layer)
double x
Definition: qgspoint.h:41