QGIS API Documentation 3.28.14-Firenze (exported)
Loading...
Searching...
No Matches
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
18#include "qgsfeatureiterator.h"
20#include "qgslinestring.h"
21#include "qgslogger.h"
22#include "qgspoint.h"
23#include "qgsgeometryfactory.h"
24#include "qgis.h"
25#include "qgswkbtypes.h"
26#include "qgsvectorlayerutils.h"
27#include "qgsvectorlayer.h"
28#include "qgsgeometryoptions.h"
29#include "qgsabstractgeometry.h"
30
31#include <limits>
32
33
38
39bool QgsVectorLayerEditUtils::insertVertex( double x, double y, QgsFeatureId atFeatureId, int beforeVertex )
40{
41 if ( !mLayer->isSpatial() )
42 return false;
43
44 QgsFeature f;
45 if ( !mLayer->getFeatures( QgsFeatureRequest().setFilterFid( atFeatureId ).setNoAttributes() ).nextFeature( f ) || !f.hasGeometry() )
46 return false; // geometry not found
47
48 QgsGeometry geometry = f.geometry();
49
50 geometry.insertVertex( x, y, beforeVertex );
51
52 mLayer->changeGeometry( atFeatureId, geometry );
53 return true;
54}
55
56bool QgsVectorLayerEditUtils::insertVertex( const QgsPoint &point, QgsFeatureId atFeatureId, int beforeVertex )
57{
58 if ( !mLayer->isSpatial() )
59 return false;
60
61 QgsFeature f;
62 if ( !mLayer->getFeatures( QgsFeatureRequest().setFilterFid( atFeatureId ).setNoAttributes() ).nextFeature( f ) || !f.hasGeometry() )
63 return false; // geometry not found
64
65 QgsGeometry geometry = f.geometry();
66
67 geometry.insertVertex( point, beforeVertex );
68
69 mLayer->changeGeometry( atFeatureId, geometry );
70 return true;
71}
72
73bool QgsVectorLayerEditUtils::moveVertex( double x, double y, QgsFeatureId atFeatureId, int atVertex )
74{
75 QgsPoint p( x, y );
76 return moveVertex( p, atFeatureId, atVertex );
77}
78
79bool QgsVectorLayerEditUtils::moveVertex( const QgsPoint &p, QgsFeatureId atFeatureId, int atVertex )
80{
81 if ( !mLayer->isSpatial() )
82 return false;
83
84 QgsFeature f;
85 if ( !mLayer->getFeatures( QgsFeatureRequest().setFilterFid( atFeatureId ).setNoAttributes() ).nextFeature( f ) || !f.hasGeometry() )
86 return false; // geometry not found
87
88 QgsGeometry geometry = f.geometry();
89
90 geometry.moveVertex( p, atVertex );
91
92 mLayer->changeGeometry( atFeatureId, geometry );
93 return true;
94}
95
96
98{
99 if ( !mLayer->isSpatial() )
101
102 QgsFeature f;
103 if ( !mLayer->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setNoAttributes() ).nextFeature( f ) || !f.hasGeometry() )
104 return Qgis::VectorEditResult::FetchFeatureFailed; // geometry not found
105
106 QgsGeometry geometry = f.geometry();
107
108 if ( !geometry.deleteVertex( vertex ) )
110
111 if ( geometry.constGet() && geometry.constGet()->nCoordinates() == 0 )
112 {
113 //last vertex deleted, set geometry to null
114 geometry.set( nullptr );
115 }
116
117 mLayer->changeGeometry( featureId, geometry );
119}
120
121
122static
123Qgis::GeometryOperationResult staticAddRing( QgsVectorLayer *layer, std::unique_ptr< QgsCurve > &ring, const QgsFeatureIds &targetFeatureIds, QgsFeatureIds *modifiedFeatureIds, bool firstOne = true )
124{
125
126 if ( !layer || !layer->isSpatial() )
127 {
129 }
130
131 if ( !ring )
132 {
134 }
135
136 if ( !ring->isClosed() )
137 {
139 }
140
141 if ( !layer->isValid() || !layer->editBuffer() || !layer->dataProvider() )
142 {
144 }
145
146 Qgis::GeometryOperationResult addRingReturnCode = Qgis::GeometryOperationResult::AddRingNotInExistingFeature; //default: return code for 'ring not inserted'
147 QgsFeature f;
148
150 if ( !targetFeatureIds.isEmpty() )
151 {
152 //check only specified features
153 fit = layer->getFeatures( QgsFeatureRequest().setFilterFids( targetFeatureIds ) );
154 }
155 else
156 {
157 //check all intersecting features
158 QgsRectangle bBox = ring->boundingBox();
159 fit = layer->getFeatures( QgsFeatureRequest().setFilterRect( bBox ).setFlags( QgsFeatureRequest::ExactIntersect ) );
160 }
161
162 //find first valid feature we can add the ring to
163 while ( fit.nextFeature( f ) )
164 {
165 if ( !f.hasGeometry() )
166 continue;
167
168 //add ring takes ownership of ring, and deletes it if there's an error
169 QgsGeometry g = f.geometry();
170
171 addRingReturnCode = g.addRing( static_cast< QgsCurve * >( ring->clone() ) );
172 if ( addRingReturnCode == Qgis::GeometryOperationResult::Success )
173 {
174 layer->changeGeometry( f.id(), g );
175 if ( modifiedFeatureIds )
176 {
177 modifiedFeatureIds->insert( f.id() );
178 if ( firstOne )
179 {
180 break;
181 }
182 }
183
184 }
185 }
186
187 return addRingReturnCode;
188}
189
190Qgis::GeometryOperationResult QgsVectorLayerEditUtils::addRing( const QVector<QgsPointXY> &ring, const QgsFeatureIds &targetFeatureIds, QgsFeatureId *modifiedFeatureId )
191{
193 for ( QVector<QgsPointXY>::const_iterator it = ring.constBegin(); it != ring.constEnd(); ++it )
194 {
195 l << QgsPoint( *it );
196 }
197 return addRing( l, targetFeatureIds, modifiedFeatureId );
198}
199
201{
202 QgsLineString *ringLine = new QgsLineString( ring );
203 return addRing( ringLine, targetFeatureIds, modifiedFeatureId );
204}
205
207{
208 std::unique_ptr<QgsCurve> uniquePtrRing( ring );
209 if ( modifiedFeatureId )
210 {
211 QgsFeatureIds *modifiedFeatureIds = new QgsFeatureIds;
212 Qgis::GeometryOperationResult result = staticAddRing( mLayer, uniquePtrRing, targetFeatureIds, modifiedFeatureIds, true );
213 *modifiedFeatureId = *modifiedFeatureIds->begin();
214 return result;
215 }
216 return staticAddRing( mLayer, uniquePtrRing, targetFeatureIds, nullptr, true );
217}
218
220{
221
222 std::unique_ptr<QgsCurve> uniquePtrRing( ring );
223 return staticAddRing( mLayer, uniquePtrRing, targetFeatureIds, modifiedFeatureIds, false );
224}
225
226
227
229{
231 for ( QVector<QgsPointXY>::const_iterator it = points.constBegin(); it != points.constEnd(); ++it )
232 {
233 l << QgsPoint( *it );
234 }
235 return addPart( l, featureId );
236}
237
239{
240 if ( !mLayer->isSpatial() )
242
243 QgsGeometry geometry;
244 bool firstPart = false;
245 QgsFeature f;
246 if ( !mLayer->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setNoAttributes() ).nextFeature( f ) )
248
249 if ( !f.hasGeometry() )
250 {
251 //no existing geometry, so adding first part to null geometry
252 firstPart = true;
253 }
254 else
255 {
256 geometry = f.geometry();
257 }
258
259 Qgis::GeometryOperationResult errorCode = geometry.addPart( points, mLayer->geometryType() );
261 {
262 if ( firstPart && QgsWkbTypes::isSingleType( mLayer->wkbType() )
264 {
265 //convert back to single part if required by layer
266 geometry.convertToSingleType();
267 }
268 mLayer->changeGeometry( featureId, geometry );
269 }
270 return errorCode;
271}
272
274{
275 if ( !mLayer->isSpatial() )
277
278 QgsGeometry geometry;
279 bool firstPart = false;
280 QgsFeature f;
281 if ( !mLayer->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setNoAttributes() ).nextFeature( f ) )
283
284 if ( !f.hasGeometry() )
285 {
286 //no existing geometry, so adding first part to null geometry
287 firstPart = true;
288 }
289 else
290 {
291 geometry = f.geometry();
292 }
293
294 Qgis::GeometryOperationResult errorCode = geometry.addPart( ring, mLayer->geometryType() );
296 {
297 if ( firstPart && QgsWkbTypes::isSingleType( mLayer->wkbType() )
299 {
300 //convert back to single part if required by layer
301 geometry.convertToSingleType();
302 }
303 mLayer->changeGeometry( featureId, geometry );
304 }
305 return errorCode;
306}
307
308// TODO QGIS 4.0 -- this should return Qgis::GeometryOperationResult
309int QgsVectorLayerEditUtils::translateFeature( QgsFeatureId featureId, double dx, double dy )
310{
311 if ( !mLayer->isSpatial() )
312 return 1;
313
314 QgsFeature f;
315 if ( !mLayer->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setNoAttributes() ).nextFeature( f ) || !f.hasGeometry() )
316 return 1; //geometry not found
317
318 QgsGeometry geometry = f.geometry();
319
320 Qgis::GeometryOperationResult errorCode = geometry.translate( dx, dy );
322 {
323 mLayer->changeGeometry( featureId, geometry );
324 }
325 return errorCode == Qgis::GeometryOperationResult::Success ? 0 : 1;
326}
327
328Qgis::GeometryOperationResult QgsVectorLayerEditUtils::splitFeatures( const QVector<QgsPointXY> &splitLine, bool topologicalEditing )
329{
331 for ( QVector<QgsPointXY>::const_iterator it = splitLine.constBegin(); it != splitLine.constEnd(); ++it )
332 {
333 l << QgsPoint( *it );
334 }
335 return splitFeatures( l, topologicalEditing );
336}
337
339{
340 QgsLineString lineString( splitLine );
341 QgsPointSequence topologyTestPoints;
342 bool preserveCircular = false;
343 return splitFeatures( &lineString, topologyTestPoints, preserveCircular, topologicalEditing );
344}
345
346Qgis::GeometryOperationResult QgsVectorLayerEditUtils::splitFeatures( const QgsCurve *curve, QgsPointSequence &topologyTestPoints, bool preserveCircular, bool topologicalEditing )
347{
348 if ( !mLayer->isSpatial() )
350
351 QgsRectangle bBox; //bounding box of the split line
353 Qgis::GeometryOperationResult splitFunctionReturn; //return code of QgsGeometry::splitGeometry
354 int numberOfSplitFeatures = 0;
355
356 QgsFeatureIterator features;
357 const QgsFeatureIds selectedIds = mLayer->selectedFeatureIds();
358
359 // deactivate preserving circular if the curve contains only straight segments to avoid transforming Polygon to CurvePolygon
360 preserveCircular &= curve->hasCurvedSegments();
361
362 if ( !selectedIds.isEmpty() ) //consider only the selected features if there is a selection
363 {
364 features = mLayer->getSelectedFeatures();
365 }
366 else //else consider all the feature that intersect the bounding box of the split line
367 {
368
369 bBox = curve->boundingBox();
370
371 if ( bBox.isEmpty() )
372 {
373 //if the bbox is a line, try to make a square out of it
374 if ( bBox.width() == 0.0 && bBox.height() > 0 )
375 {
376 bBox.setXMinimum( bBox.xMinimum() - bBox.height() / 2 );
377 bBox.setXMaximum( bBox.xMaximum() + bBox.height() / 2 );
378 }
379 else if ( bBox.height() == 0.0 && bBox.width() > 0 )
380 {
381 bBox.setYMinimum( bBox.yMinimum() - bBox.width() / 2 );
382 bBox.setYMaximum( bBox.yMaximum() + bBox.width() / 2 );
383 }
384 else
385 {
386 //If we have a single point, we still create a non-null box
387 double bufferDistance = 0.000001;
388 if ( mLayer->crs().isGeographic() )
389 bufferDistance = 0.00000001;
390 bBox.setXMinimum( bBox.xMinimum() - bufferDistance );
391 bBox.setXMaximum( bBox.xMaximum() + bufferDistance );
392 bBox.setYMinimum( bBox.yMinimum() - bufferDistance );
393 bBox.setYMaximum( bBox.yMaximum() + bufferDistance );
394 }
395 }
396
397 features = mLayer->getFeatures( QgsFeatureRequest().setFilterRect( bBox ).setFlags( QgsFeatureRequest::ExactIntersect ) );
398 }
399
401
402 QgsFeature feat;
403 while ( features.nextFeature( feat ) )
404 {
405 if ( !feat.hasGeometry() )
406 {
407 continue;
408 }
409 QVector<QgsGeometry> newGeometries;
410 QgsPointSequence featureTopologyTestPoints;
411 QgsGeometry featureGeom = feat.geometry();
412 splitFunctionReturn = featureGeom.splitGeometry( curve, newGeometries, preserveCircular, topologicalEditing, featureTopologyTestPoints );
413 topologyTestPoints.append( featureTopologyTestPoints );
414 if ( splitFunctionReturn == Qgis::GeometryOperationResult::Success )
415 {
416 //change this geometry
417 mLayer->changeGeometry( feat.id(), featureGeom );
418
419 //insert new features
420 QgsAttributeMap attributeMap = feat.attributes().toMap();
421 for ( const QgsGeometry &geom : std::as_const( newGeometries ) )
422 {
423 featuresDataToAdd << QgsVectorLayerUtils::QgsFeatureData( geom, attributeMap );
424 }
425
426 if ( topologicalEditing )
427 {
428 QgsPointSequence::const_iterator topol_it = featureTopologyTestPoints.constBegin();
429 for ( ; topol_it != featureTopologyTestPoints.constEnd(); ++topol_it )
430 {
431 addTopologicalPoints( *topol_it );
432 }
433 }
434 ++numberOfSplitFeatures;
435 }
436 else if ( splitFunctionReturn != Qgis::GeometryOperationResult::Success && splitFunctionReturn != Qgis::GeometryOperationResult::NothingHappened ) // i.e. no split but no error occurred
437 {
438 returnCode = splitFunctionReturn;
439 }
440 }
441
442 if ( !featuresDataToAdd.isEmpty() )
443 {
444 // finally create and add all bits of geometries cut off the original geometries
445 // (this is much faster than creating features one by one)
446 QgsFeatureList featuresListToAdd = QgsVectorLayerUtils::createFeatures( mLayer, featuresDataToAdd );
447 mLayer->addFeatures( featuresListToAdd );
448 }
449
450 if ( numberOfSplitFeatures == 0 )
451 {
453 }
454
455 return returnCode;
456}
457
458Qgis::GeometryOperationResult QgsVectorLayerEditUtils::splitParts( const QVector<QgsPointXY> &splitLine, bool topologicalEditing )
459{
461 for ( QVector<QgsPointXY>::const_iterator it = splitLine.constBegin(); it != splitLine.constEnd(); ++it )
462 {
463 l << QgsPoint( *it );
464 }
465 return splitParts( l, topologicalEditing );
466}
467
469{
470 if ( !mLayer->isSpatial() )
472
473 double xMin, yMin, xMax, yMax;
474 QgsRectangle bBox; //bounding box of the split line
475 int numberOfSplitParts = 0;
476
478
479 if ( mLayer->selectedFeatureCount() > 0 ) //consider only the selected features if there is a selection
480 {
481 fit = mLayer->getSelectedFeatures();
482 }
483 else //else consider all the feature that intersect the bounding box of the split line
484 {
485 if ( boundingBoxFromPointList( splitLine, xMin, yMin, xMax, yMax ) )
486 {
487 bBox.setXMinimum( xMin );
488 bBox.setYMinimum( yMin );
489 bBox.setXMaximum( xMax );
490 bBox.setYMaximum( yMax );
491 }
492 else
493 {
495 }
496
497 if ( bBox.isEmpty() )
498 {
499 //if the bbox is a line, try to make a square out of it
500 if ( bBox.width() == 0.0 && bBox.height() > 0 )
501 {
502 bBox.setXMinimum( bBox.xMinimum() - bBox.height() / 2 );
503 bBox.setXMaximum( bBox.xMaximum() + bBox.height() / 2 );
504 }
505 else if ( bBox.height() == 0.0 && bBox.width() > 0 )
506 {
507 bBox.setYMinimum( bBox.yMinimum() - bBox.width() / 2 );
508 bBox.setYMaximum( bBox.yMaximum() + bBox.width() / 2 );
509 }
510 else
511 {
512 //If we have a single point, we still create a non-null box
513 double bufferDistance = 0.000001;
514 if ( mLayer->crs().isGeographic() )
515 bufferDistance = 0.00000001;
516 bBox.setXMinimum( bBox.xMinimum() - bufferDistance );
517 bBox.setXMaximum( bBox.xMaximum() + bufferDistance );
518 bBox.setYMinimum( bBox.yMinimum() - bufferDistance );
519 bBox.setYMaximum( bBox.yMaximum() + bufferDistance );
520 }
521 }
522
523 fit = mLayer->getFeatures( QgsFeatureRequest().setFilterRect( bBox ).setFlags( QgsFeatureRequest::ExactIntersect ) );
524 }
525
526 QgsFeature feat;
527 while ( fit.nextFeature( feat ) )
528 {
529 QgsGeometry featureGeom = feat.geometry();
530
531 const QVector<QgsGeometry> geomCollection = featureGeom.asGeometryCollection();
532 QVector<QgsGeometry> resultCollection;
533 QgsPointSequence topologyTestPoints;
534 for ( QgsGeometry part : geomCollection )
535 {
536 QVector<QgsGeometry> newGeometries;
537 QgsPointSequence partTopologyTestPoints;
538
539 const Qgis::GeometryOperationResult splitFunctionReturn = part.splitGeometry( splitLine, newGeometries, topologicalEditing, partTopologyTestPoints, false );
540
541 if ( splitFunctionReturn == Qgis::GeometryOperationResult::Success && !newGeometries.isEmpty() )
542 {
543 for ( int i = 0; i < newGeometries.size(); ++i )
544 {
545 resultCollection.append( newGeometries.at( i ).asGeometryCollection() );
546 }
547
548 topologyTestPoints.append( partTopologyTestPoints );
549
550 ++numberOfSplitParts;
551 }
552 // Note: For multilinestring layers, when the split line does not intersect the feature part,
553 // QgsGeometry::splitGeometry returns InvalidBaseGeometry instead of NothingHappened
554 else if ( splitFunctionReturn == Qgis::GeometryOperationResult::NothingHappened ||
556 {
557 // Add part as is
558 resultCollection.append( part );
559 }
560 else if ( splitFunctionReturn != Qgis::GeometryOperationResult::Success )
561 {
562 return splitFunctionReturn;
563 }
564 }
565
566 QgsGeometry newGeom = QgsGeometry::collectGeometry( resultCollection );
567 mLayer->changeGeometry( feat.id(), newGeom ) ;
568
569 if ( topologicalEditing )
570 {
571 QgsPointSequence::const_iterator topol_it = topologyTestPoints.constBegin();
572 for ( ; topol_it != topologyTestPoints.constEnd(); ++topol_it )
573 {
574 addTopologicalPoints( *topol_it );
575 }
576 }
577
578 }
579 if ( numberOfSplitParts == 0 && mLayer->selectedFeatureCount() > 0 )
580 {
581 //There is a selection but no feature has been split.
582 //Maybe user forgot that only the selected features are split
584 }
585
587}
588
589
591{
592 if ( !mLayer->isSpatial() )
593 return 1;
594
595 if ( geom.isNull() )
596 {
597 return 1;
598 }
599
600 bool pointsAdded = false;
601
603 while ( it != geom.vertices_end() )
604 {
605 if ( addTopologicalPoints( *it ) == 0 )
606 {
607 pointsAdded = true;
608 }
609 ++it;
610 }
611
612 return pointsAdded ? 0 : 2;
613}
614
616{
617 if ( !mLayer->isSpatial() )
618 return 1;
619
620 double segmentSearchEpsilon = mLayer->crs().isGeographic() ? 1e-12 : 1e-8;
621
622 //work with a tolerance because coordinate projection may introduce some rounding
623 double threshold = mLayer->geometryOptions()->geometryPrecision();
624
625 if ( qgsDoubleNear( threshold, 0.0 ) )
626 {
627 threshold = 0.0000001;
628
629 if ( mLayer->crs().mapUnits() == QgsUnitTypes::DistanceMeters )
630 {
631 threshold = 0.001;
632 }
633 else if ( mLayer->crs().mapUnits() == QgsUnitTypes::DistanceFeet )
634 {
635 threshold = 0.0001;
636 }
637 }
638
639 QgsRectangle searchRect( p.x() - threshold, p.y() - threshold,
640 p.x() + threshold, p.y() + threshold );
641 double sqrSnappingTolerance = threshold * threshold;
642
643 QgsFeature f;
645 .setFilterRect( searchRect )
647 .setNoAttributes() );
648
649 QMap<QgsFeatureId, QgsGeometry> features;
650 QMap<QgsFeatureId, int> segments;
651
652 while ( fit.nextFeature( f ) )
653 {
654 int afterVertex;
655 QgsPointXY snappedPoint;
656 double sqrDistSegmentSnap = f.geometry().closestSegmentWithContext( p, snappedPoint, afterVertex, nullptr, segmentSearchEpsilon );
657 if ( sqrDistSegmentSnap < sqrSnappingTolerance )
658 {
659 segments[f.id()] = afterVertex;
660 features[f.id()] = f.geometry();
661 }
662 }
663
664 if ( segments.isEmpty() )
665 return 2;
666
667 bool pointsAdded = false;
668 for ( QMap<QgsFeatureId, int>::const_iterator it = segments.constBegin(); it != segments.constEnd(); ++it )
669 {
670 QgsFeatureId fid = it.key();
671 int segmentAfterVertex = it.value();
672 QgsGeometry geom = features[fid];
673
674 int atVertex, beforeVertex, afterVertex;
675 double sqrDistVertexSnap;
676 geom.closestVertex( p, atVertex, beforeVertex, afterVertex, sqrDistVertexSnap );
677
678 if ( sqrDistVertexSnap < sqrSnappingTolerance )
679 continue; // the vertex already exists - do not insert it
680
681 if ( !mLayer->insertVertex( p, fid, segmentAfterVertex ) )
682 {
683 QgsDebugMsg( QStringLiteral( "failed to insert topo point" ) );
684 }
685 else
686 {
687 pointsAdded = true;
688 }
689 }
690
691 return pointsAdded ? 0 : 2;
692}
693
695{
696 if ( !mLayer->isSpatial() )
697 return 1;
698
699 if ( ps.isEmpty() )
700 {
701 return 1;
702 }
703
704 bool pointsAdded = false;
705
706 QgsPointSequence::const_iterator it = ps.constBegin();
707 while ( it != ps.constEnd() )
708 {
709 if ( addTopologicalPoints( *it ) == 0 )
710 {
711 pointsAdded = true;
712 }
713 ++it;
714 }
715
716 return pointsAdded ? 0 : 2;
717}
718
723
724bool QgsVectorLayerEditUtils::mergeFeatures( const QgsFeatureId &targetFeatureId, const QgsFeatureIds &mergeFeatureIds, const QgsAttributes &mergeAttributes, const QgsGeometry &unionGeometry, QString &errorMessage )
725{
726 errorMessage.clear();
727
728 if ( mergeFeatureIds.isEmpty() )
729 {
730 errorMessage = QObject::tr( "List of features to merge is empty" );
731 return false;
732 }
733
734 QgsAttributeMap newAttributes;
735 for ( int i = 0; i < mergeAttributes.count(); ++i )
736 {
737 QVariant val = mergeAttributes.at( i );
738
739 bool isDefaultValue = mLayer->fields().fieldOrigin( i ) == QgsFields::OriginProvider &&
740 mLayer->dataProvider() &&
741 mLayer->dataProvider()->defaultValueClause( mLayer->fields().fieldOriginIndex( i ) ) == val;
742
743 // convert to destination data type
744 QString errorMessageConvertCompatible;
745 if ( !isDefaultValue && !mLayer->fields().at( i ).convertCompatible( val, &errorMessageConvertCompatible ) )
746 {
747 if ( errorMessage.isEmpty() )
748 errorMessage = QObject::tr( "Could not store value '%1' in field of type %2: %3" ).arg( mergeAttributes.at( i ).toString(), mLayer->fields().at( i ).typeName(), errorMessageConvertCompatible );
749 }
750 newAttributes[ i ] = val;
751 }
752
753 mLayer->beginEditCommand( QObject::tr( "Merged features" ) );
754
755 // Delete other features but the target feature
756 QgsFeatureIds::const_iterator feature_it = mergeFeatureIds.constBegin();
757 for ( ; feature_it != mergeFeatureIds.constEnd(); ++feature_it )
758 {
759 if ( *feature_it != targetFeatureId )
760 mLayer->deleteFeature( *feature_it );
761 }
762
763 // Modify target feature or create a new one if invalid
764 QgsGeometry mergeGeometry = unionGeometry;
765 if ( targetFeatureId == FID_NULL )
766 {
767 QgsFeature mergeFeature = QgsVectorLayerUtils::createFeature( mLayer, mergeGeometry, newAttributes );
768 mLayer->addFeature( mergeFeature );
769 }
770 else
771 {
772 mLayer->changeGeometry( targetFeatureId, mergeGeometry );
773 mLayer->changeAttributeValues( targetFeatureId, newAttributes );
774 }
775
776 mLayer->endEditCommand();
777
778 mLayer->triggerRepaint();
779
780 return true;
781}
782
783bool QgsVectorLayerEditUtils::boundingBoxFromPointList( const QgsPointSequence &list, double &xmin, double &ymin, double &xmax, double &ymax ) const
784{
785 if ( list.empty() )
786 {
787 return false;
788 }
789
790 xmin = std::numeric_limits<double>::max();
791 xmax = -std::numeric_limits<double>::max();
792 ymin = std::numeric_limits<double>::max();
793 ymax = -std::numeric_limits<double>::max();
794
795 for ( QgsPointSequence::const_iterator it = list.constBegin(); it != list.constEnd(); ++it )
796 {
797 if ( it->x() < xmin )
798 {
799 xmin = it->x();
800 }
801 if ( it->x() > xmax )
802 {
803 xmax = it->x();
804 }
805 if ( it->y() < ymin )
806 {
807 ymin = it->y();
808 }
809 if ( it->y() > ymax )
810 {
811 ymax = it->y();
812 }
813 }
814
815 return true;
816}
GeometryOperationResult
Success or failure of a geometry operation.
Definition qgis.h:955
@ AddPartSelectedGeometryNotFound
The selected geometry cannot be found.
@ InvalidInputGeometryType
The input geometry (ring, part, split line, etc.) has not the correct geometry type.
@ Success
Operation succeeded.
@ AddRingNotInExistingFeature
The input ring doesn't have any existing ring to fit into.
@ AddRingNotClosed
The input ring is not closed.
@ NothingHappened
Nothing happened, without any error.
@ InvalidBaseGeometry
The base geometry on which the operation is done is invalid or empty.
@ LayerNotEditable
Cannot edit layer.
VectorEditResult
Flags which control feature selection behavior.
Definition qgis.h:845
@ EmptyGeometry
Edit operation resulted in an empty geometry.
@ Success
Edit operation was successful.
@ FetchFeatureFailed
Unable to fetch requested feature.
@ EditFailed
Edit operation failed.
@ InvalidLayer
Edit failed due to invalid layer.
The vertex_iterator class provides STL-style iterator for vertices.
virtual int nCoordinates() const
Returns the number of nodes contained in the geometry.
virtual bool hasCurvedSegments() const
Returns true if the geometry contains curved segments.
A vector of attributes.
CORE_EXPORT QgsAttributeMap toMap() const
Returns a QgsAttributeMap of the attribute values.
Abstract base class for curved geometry type.
Definition qgscurve.h:36
QgsRectangle boundingBox() const override
Returns the minimal bounding box for the geometry.
Definition qgscurve.cpp:238
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
This class wraps a request for features to a vector layer (or directly its vector data provider).
@ ExactIntersect
Use exact geometry intersection (slower) instead of bounding boxes.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:56
QgsAttributes attributes
Definition qgsfeature.h:65
QgsFeatureId id
Definition qgsfeature.h:64
QgsGeometry geometry
Definition qgsfeature.h:67
bool hasGeometry() const
Returns true if the feature has an associated geometry.
QString typeName() const
Gets the field type.
Definition qgsfield.cpp:152
bool convertCompatible(QVariant &v, QString *errorMessage=nullptr) const
Converts the provided variant to a compatible format.
Definition qgsfield.cpp:424
@ OriginProvider
Field comes from the underlying data provider of the vector layer (originIndex = index in provider's ...
Definition qgsfields.h:51
FieldOrigin fieldOrigin(int fieldIdx) const
Returns the field's origin (value from an enumeration).
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
int fieldOriginIndex(int fieldIdx) const
Returns the field's origin index (its meaning is specific to each type of origin).
double geometryPrecision() const
The precision in which geometries on this layer should be saved.
A geometry is the spatial representation of a feature.
bool deleteVertex(int atVertex)
Deletes the vertex at the given position number and item (first number is index 0)
static QgsGeometry collectGeometry(const QVector< QgsGeometry > &geometries)
Creates a new multipart geometry from a list of QgsGeometry objects.
QgsPointXY closestVertex(const QgsPointXY &point, int &closestVertexIndex, int &previousVertexIndex, int &nextVertexIndex, double &sqrDist) const
Returns the vertex closest to the given point, the corresponding vertex index, squared distance snap ...
Qgis::GeometryOperationResult addPart(const QVector< QgsPointXY > &points, QgsWkbTypes::GeometryType geomType=QgsWkbTypes::UnknownGeometry)
Adds a new part to a the geometry.
QVector< QgsGeometry > asGeometryCollection() const
Returns contents of the geometry as a list of geometries.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
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 convertToSingleType()
Converts multi type geometry into single type geometry e.g.
Qgis::GeometryOperationResult addRing(const QVector< QgsPointXY > &ring)
Adds a new ring to this geometry.
void set(QgsAbstractGeometry *geometry)
Sets the underlying geometry store.
QgsAbstractGeometry::vertex_iterator vertices_begin() const
Returns STL-style iterator pointing to the first vertex of the geometry.
Qgis::GeometryOperationResult translate(double dx, double dy, double dz=0.0, double dm=0.0)
Translates this geometry by dx, dy, dz and dm.
Q_DECL_DEPRECATED Qgis::GeometryOperationResult splitGeometry(const QVector< QgsPointXY > &splitLine, QVector< QgsGeometry > &newGeometries, bool topological, QVector< QgsPointXY > &topologyTestPoints, bool splitFeature=true)
Splits this geometry according to a given line.
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...
double closestSegmentWithContext(const QgsPointXY &point, QgsPointXY &minDistPoint, int &nextVertexIndex, int *leftOrRightOfSegment=nullptr, double epsilon=DEFAULT_SEGMENT_EPSILON) const
Searches for the closest segment of geometry to the given point.
QgsAbstractGeometry::vertex_iterator vertices_end() const
Returns STL-style iterator pointing to the imaginary vertex after the last vertex of the geometry.
Line string geometry type, with support for z-dimension and m-values.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:79
void triggerRepaint(bool deferredUpdate=false)
Will advise the map canvas (and any other interested party) that this layer requires to be repainted.
A class to represent a 2D point.
Definition qgspointxy.h:59
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
double x
Definition qgspoint.h:52
double y
Definition qgspoint.h:53
A rectangle specified with double values.
double xMinimum() const
Returns the x minimum value (left side of rectangle).
void setYMinimum(double y)
Set the minimum y value.
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
void setXMinimum(double x)
Set the minimum x value.
double width() const
Returns the width of the rectangle.
double xMaximum() const
Returns the x maximum value (right side of rectangle).
double yMaximum() const
Returns the y maximum value (top side of rectangle).
void setYMaximum(double y)
Set the maximum y value.
void setXMaximum(double x)
Set the maximum x value.
bool isEmpty() const
Returns true if the rectangle is empty.
double height() const
Returns the height of the rectangle.
@ DistanceMeters
Meters.
@ DistanceFeet
Imperial feet.
virtual bool doesStrictFeatureTypeCheck() const
Returns true if the provider is strict about the type of inserted features (e.g.
virtual QString defaultValueClause(int fieldIndex) const
Returns any default value clauses which are present at the provider for a specified field index.
int translateFeature(QgsFeatureId featureId, double dx, double dy)
Translates feature by dx, dy.
bool mergeFeatures(const QgsFeatureId &targetFeatureId, const QgsFeatureIds &mergeFeatureIds, const QgsAttributes &mergeAttributes, const QgsGeometry &unionGeometry, QString &errorMessage)
Merge features into a single one.
QgsVectorLayerEditUtils(QgsVectorLayer *layer)
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)...
Q_DECL_DEPRECATED Qgis::GeometryOperationResult addPart(const QVector< QgsPointXY > &ring, QgsFeatureId featureId)
Adds a new part polygon to a multipart feature.
Qgis::VectorEditResult deleteVertex(QgsFeatureId featureId, int vertex)
Deletes a vertex from a feature.
Qgis::GeometryOperationResult addRingV2(QgsCurve *ring, const QgsFeatureIds &targetFeatureIds=QgsFeatureIds(), QgsFeatureIds *modifiedFeatureIds=nullptr)
Adds a ring to polygon/multipolygon features.
int addTopologicalPoints(const QgsGeometry &geom)
Adds topological points for every vertex of the geometry.
Q_DECL_DEPRECATED Qgis::GeometryOperationResult splitParts(const QVector< QgsPointXY > &splitLine, bool topologicalEditing=false)
Splits parts cut by the given line.
Q_DECL_DEPRECATED Qgis::GeometryOperationResult splitFeatures(const QVector< QgsPointXY > &splitLine, bool topologicalEditing=false)
Splits features cut by the given line.
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),...
Q_DECL_DEPRECATED Qgis::GeometryOperationResult addRing(const QVector< QgsPointXY > &ring, const QgsFeatureIds &targetFeatureIds=QgsFeatureIds(), QgsFeatureId *modifiedFeatureId=nullptr)
Adds a ring to polygon/multipolygon features.
Encapsulate geometry and attributes for new features, to be passed to createFeatures.
QList< QgsVectorLayerUtils::QgsFeatureData > QgsFeaturesDataList
Alias for list of QgsFeatureData.
static QgsFeature createFeature(const QgsVectorLayer *layer, const QgsGeometry &geometry=QgsGeometry(), const QgsAttributeMap &attributes=QgsAttributeMap(), QgsExpressionContext *context=nullptr)
Creates a new feature ready for insertion into a layer.
static QgsFeatureList createFeatures(const QgsVectorLayer *layer, const QgsFeaturesDataList &featuresData, QgsExpressionContext *context=nullptr)
Creates a set of new features ready for insertion into a layer.
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.
bool isSpatial() const FINAL
Returns true if this is a geometry layer and false in case of NoGeometry (table only) or UnknownGeome...
bool deleteFeature(QgsFeatureId fid, DeleteContext *context=nullptr)
Deletes a feature from the layer (but does not commit it).
bool insertVertex(double x, double y, QgsFeatureId atFeatureId, int beforeVertex)
Inserts a new vertex before the given vertex number, in the given ring, item (first number is index 0...
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
bool addFeatures(QgsFeatureList &features, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) FINAL
Adds a list of features to the sink.
int selectedFeatureCount() const
Returns the number of features that are selected in this layer.
void endEditCommand()
Finish edit command and add it to undo/redo stack.
Q_INVOKABLE const QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
bool changeAttributeValues(QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues=QgsAttributeMap(), bool skipDefaultValues=false)
Changes attributes' values for a feature (but does not immediately commit the changes).
QgsGeometryOptions * geometryOptions() const
Configuration and logic to apply automatically on any edit happening on this layer.
Q_INVOKABLE QgsVectorLayerEditBuffer * editBuffer()
Buffer with uncommitted editing operations. Only valid after editing has been turned on.
QgsFeatureIterator getSelectedFeatures(QgsFeatureRequest request=QgsFeatureRequest()) const
Returns an iterator of the selected features.
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
bool addFeature(QgsFeature &feature, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) FINAL
Adds a single feature to the sink.
void beginEditCommand(const QString &text)
Create edit command for undo/redo operations.
bool changeGeometry(QgsFeatureId fid, QgsGeometry &geometry, bool skipDefaultValue=false)
Changes a feature's geometry within the layer's edit buffer (but does not immediately commit the chan...
static bool isSingleType(Type type)
Returns true if the WKB type is a single type.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:2527
QVector< QgsPoint > QgsPointSequence
QMap< int, QVariant > QgsAttributeMap
QList< QgsFeature > QgsFeatureList
Definition qgsfeature.h:922
#define FID_NULL
QSet< QgsFeatureId > QgsFeatureIds
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
#define QgsDebugMsg(str)
Definition qgslogger.h:38