37using namespace Qt::StringLiterals;
46 if ( !mLayer->isSpatial() )
57 mLayer->changeGeometry( atFeatureId, geometry );
63 if ( !mLayer->isSpatial() )
74 mLayer->changeGeometry( atFeatureId, geometry );
86 if ( !mLayer->isSpatial() )
112 return mLayer->changeGeometry( atFeatureId, geometry );
117 if ( !mLayer->isSpatial() )
132 geometry.
set(
nullptr );
135 mLayer->changeGeometry( featureId, geometry );
154 if ( !ring->isClosed() )
168 if ( !targetFeatureIds.isEmpty() )
181 bool success =
false;
192 addRingReturnCode = g.
addRing(
static_cast< QgsCurve *
>( ring->clone() ) );
196 addRingReturnCode = g.
addRing(
static_cast< QgsCurve *
>( ring->reversed() ) );
202 if ( modifiedFeatureIds )
204 modifiedFeatureIds->insert( f.
id() );
218double QgsVectorLayerEditUtils::getTopologicalSearchRadius(
const QgsVectorLayer *layer )
238void QgsVectorLayerEditUtils::addTopologicalPointsToLayers(
const QgsGeometry &geom,
QgsVectorLayer *vlayer,
const QList<QgsMapLayer *> &layers,
const QString &toolName )
243 for ( QgsMapLayer *layer : layers )
245 QgsVectorLayer *vectorLayer = qobject_cast<QgsVectorLayer *>( layer );
250 QgsCoordinateTransform ct;
251 if ( vectorLayer->
crs() != vlayer->
crs() )
258 catch ( QgsCsException & )
260 QgsDebugError( u
"Bounding box transformation failed, skipping topological points for layer %1"_s.arg( vlayer->
id() ) );
264 bbox.
grow( getTopologicalSearchRadius( vectorLayer ) );
271 vectorLayer->
beginEditCommand( QObject::tr(
"Topological points added by '%1'" ).arg( toolName ) );
274 if ( vectorLayer->
crs() != vlayer->
crs() )
279 QgsGeometry transformedGeom( geom );
280 transformedGeom.transform( ct );
283 catch ( QgsCsException & )
285 QgsDebugError( u
"transformation to vectorLayer coordinate failed"_s );
293 if ( returnValue == 0 )
310 for ( QVector<QgsPointXY>::const_iterator it = ring.constBegin(); it != ring.constEnd(); ++it )
314 return addRing( l, targetFeatureIds, modifiedFeatureId );
320 return addRing( ringLine, targetFeatureIds, modifiedFeatureId );
325 std::unique_ptr<QgsCurve> uniquePtrRing( ring );
326 if ( modifiedFeatureId )
330 if ( modifiedFeatureId && !modifiedFeatureIds.empty() )
331 *modifiedFeatureId = *modifiedFeatureIds.begin();
334 return staticAddRing( mLayer, uniquePtrRing, targetFeatureIds,
nullptr,
true );
340 std::unique_ptr<QgsCurve> uniquePtrRing( ring );
341 return staticAddRing( mLayer, uniquePtrRing, targetFeatureIds, modifiedFeatureIds,
false );
349 for ( QVector<QgsPointXY>::const_iterator it = points.constBegin(); it != points.constEnd(); ++it )
353 return addPart( l, featureId );
358 if ( !mLayer->isSpatial() )
362 bool firstPart =
false;
364 if ( !mLayer->getFeatures(
QgsFeatureRequest().setFilterFid( featureId ).setNoAttributes() ).nextFeature( f ) )
381 && mLayer->dataProvider()->doesStrictFeatureTypeCheck() )
386 mLayer->changeGeometry( featureId, geometry );
394 if ( !mLayer->isSpatial() )
398 bool firstPart =
false;
400 if ( !mLayer->getFeatures(
QgsFeatureRequest().setFilterFid( featureId ).setNoAttributes() ).nextFeature( f ) )
421 && mLayer->dataProvider()->doesStrictFeatureTypeCheck() )
426 mLayer->changeGeometry( featureId, geometry );
434 if ( !mLayer->isSpatial() )
446 mLayer->changeGeometry( featureId, geometry );
455 for ( QVector<QgsPointXY>::const_iterator it = splitLine.constBegin(); it != splitLine.constEnd(); ++it )
466 bool preserveCircular =
false;
467 return splitFeatures( &lineString, topologyTestPoints, preserveCircular, topologicalEditing );
472 if ( !mLayer->isSpatial() )
478 int numberOfSplitFeatures = 0;
481 const QgsFeatureIds selectedIds = mLayer->selectedFeatureIds();
486 if ( !selectedIds.isEmpty() )
488 features = mLayer->getSelectedFeatures();
503 else if ( bBox.
height() == 0.0 && bBox.
width() > 0 )
511 double bufferDistance = 0.000001;
512 if ( mLayer->crs().isGeographic() )
513 bufferDistance = 0.00000001;
526 const int fieldCount = mLayer->fields().count();
535 QVector<QgsGeometry> newGeometries;
539 splitFunctionReturn = featureGeom.
splitGeometry( curve, newGeometries, preserveCircular, topologicalEditing, featureTopologyTestPoints );
540 topologyTestPoints.append( featureTopologyTestPoints );
545 double featureGeomSize = size( featureGeom );
547 QVector<QgsGeometry>::iterator largestNewFeature = std::max_element( newGeometries.begin(), newGeometries.end(), [ &size ](
const QgsGeometry & a,
const QgsGeometry & b ) ->
bool
549 return size( a ) < size( b );
552 if ( size( *largestNewFeature ) > featureGeomSize )
555 *largestNewFeature = featureGeom;
560 mLayer->changeGeometry( feat.
id(), featureGeom );
564 for (
int fieldIdx = 0; fieldIdx < fieldCount; ++fieldIdx )
566 const QgsField field = mLayer->fields().at( fieldIdx );
578 const double originalValue = feat.
attribute( fieldIdx ).toDouble();
580 double originalSize = 0;
582 switch ( originalGeom.
type() )
590 originalSize = originalGeom.
length();
593 originalSize = originalGeom.
area();
598 switch ( featureGeom.
type() )
606 newSize = featureGeom.
length();
609 newSize = featureGeom.
area();
613 attributeMap.insert( fieldIdx, originalSize > 0 ? ( originalValue * newSize / originalSize ) : originalValue );
620 if ( !attributeMap.isEmpty() )
622 mLayer->changeAttributeValues( feat.
id(), attributeMap );
626 for (
const QgsGeometry &geom : std::as_const( newGeometries ) )
629 for (
int fieldIdx = 0; fieldIdx < fieldCount; ++fieldIdx )
631 const QgsField field = mLayer->fields().at( fieldIdx );
640 attributeMap.insert( fieldIdx, feat.
attribute( fieldIdx ) );
647 attributeMap.insert( fieldIdx, feat.
attribute( fieldIdx ) );
651 const double originalValue = feat.
attribute( fieldIdx ).toDouble();
653 double originalSize = 0;
655 switch ( originalGeom.
type() )
663 originalSize = originalGeom.
length();
666 originalSize = originalGeom.
area();
671 switch ( geom.
type() )
682 newSize = geom.
area();
686 attributeMap.insert( fieldIdx, originalSize > 0 ? ( originalValue * newSize / originalSize ) : originalValue );
700 if ( topologicalEditing )
702 QgsPointSequence::const_iterator topol_it = featureTopologyTestPoints.constBegin();
703 for ( ; topol_it != featureTopologyTestPoints.constEnd(); ++topol_it )
708 ++numberOfSplitFeatures;
712 returnCode = splitFunctionReturn;
716 if ( !featuresDataToAdd.isEmpty() )
721 mLayer->addFeatures( featuresListToAdd );
724 if ( numberOfSplitFeatures == 0 )
735 for ( QVector<QgsPointXY>::const_iterator it = splitLine.constBegin(); it != splitLine.constEnd(); ++it )
744 if ( !mLayer->isSpatial() )
747 double xMin, yMin, xMax, yMax;
749 int numberOfSplitParts = 0;
753 if ( mLayer->selectedFeatureCount() > 0 )
755 fit = mLayer->getSelectedFeatures();
759 if ( boundingBoxFromPointList( splitLine, xMin, yMin, xMax, yMax ) )
779 else if ( bBox.
height() == 0.0 && bBox.
width() > 0 )
787 double bufferDistance = 0.000001;
788 if ( mLayer->crs().isGeographic() )
789 bufferDistance = 0.00000001;
806 QVector<QgsGeometry> resultCollection;
810 QVector<QgsGeometry> newGeometries;
813 const Qgis::GeometryOperationResult splitFunctionReturn = part.splitGeometry( splitLine, newGeometries, topologicalEditing, partTopologyTestPoints,
false );
817 for (
int i = 0; i < newGeometries.size(); ++i )
819 resultCollection.append( newGeometries.at( i ).asGeometryCollection() );
822 topologyTestPoints.append( partTopologyTestPoints );
824 ++numberOfSplitParts;
832 resultCollection.append( part );
836 return splitFunctionReturn;
841 mLayer->changeGeometry( feat.
id(), newGeom ) ;
843 if ( topologicalEditing )
845 QgsPointSequence::const_iterator topol_it = topologyTestPoints.constBegin();
846 for ( ; topol_it != topologyTestPoints.constEnd(); ++topol_it )
853 if ( numberOfSplitParts == 0 && mLayer->selectedFeatureCount() > 0 )
866 if ( !mLayer->isSpatial() )
874 bool pointsAdded =
false;
886 return pointsAdded ? 0 : 2;
891 if ( !mLayer->isSpatial() )
894 double segmentSearchEpsilon = mLayer->crs().isGeographic() ? 1e-12 : 1e-8;
897 double threshold = getTopologicalSearchRadius( mLayer );
900 searchRect.
grow( threshold );
904 .setFilterRect( searchRect )
906 .setNoAttributes() );
908 bool pointsAdded =
false;
915 mLayer->changeGeometry( f.
id(), geom );
919 return pointsAdded ? 0 : 2;
924 if ( !mLayer->isSpatial() )
932 bool pointsAdded =
false;
934 QgsPointSequence::const_iterator it = ps.constBegin();
935 while ( it != ps.constEnd() )
944 return pointsAdded ? 0 : 2;
954 errorMessage.clear();
956 if ( mergeFeatureIds.isEmpty() )
958 errorMessage = QObject::tr(
"List of features to merge is empty" );
963 for (
int i = 0; i < mergeAttributes.count(); ++i )
965 QVariant val = mergeAttributes.at( i );
968 mLayer->dataProvider() &&
969 mLayer->dataProvider()->defaultValueClause( mLayer->fields().fieldOriginIndex( i ) ) == val;
972 QString errorMessageConvertCompatible;
973 if ( !isDefaultValue && !mLayer->fields().at( i ).convertCompatible( val, &errorMessageConvertCompatible ) )
975 if ( errorMessage.isEmpty() )
976 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 );
978 newAttributes[ i ] = val;
981 mLayer->beginEditCommand( QObject::tr(
"Merged features" ) );
984 QgsFeatureIds::const_iterator feature_it = mergeFeatureIds.constBegin();
985 for ( ; feature_it != mergeFeatureIds.constEnd(); ++feature_it )
987 if ( *feature_it != targetFeatureId )
988 mLayer->deleteFeature( *feature_it );
996 mLayer->addFeature( mergeFeature );
1000 mLayer->changeGeometry( targetFeatureId, mergeGeometry );
1001 mLayer->changeAttributeValues( targetFeatureId, newAttributes );
1004 mLayer->endEditCommand();
1006 mLayer->triggerRepaint();
1011bool QgsVectorLayerEditUtils::boundingBoxFromPointList(
const QgsPointSequence &list,
double &xmin,
double &ymin,
double &xmax,
double &ymax )
const
1018 xmin = std::numeric_limits<double>::max();
1019 xmax = -std::numeric_limits<double>::max();
1020 ymin = std::numeric_limits<double>::max();
1021 ymax = -std::numeric_limits<double>::max();
1023 for ( QgsPointSequence::const_iterator it = list.constBegin(); it != list.constEnd(); ++it )
1025 if ( it->x() < xmin )
1029 if ( it->x() > xmax )
1033 if ( it->y() < ymin )
1037 if ( it->y() > ymax )
GeometryOperationResult
Success or failure of a geometry operation.
@ 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.
@ ExactIntersect
Use exact geometry intersection (slower) instead of bounding boxes.
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
@ GeometryRatio
New values are computed by the ratio of their area/length compared to the area/length of the original...
@ UnsetField
Clears the field value so that the data provider backend will populate using any backend triggers or ...
@ DefaultValue
Use default field value.
@ Duplicate
Duplicate original value.
@ Provider
Field originates from the underlying data provider of the vector layer.
VectorEditResult
Specifies the result of a vector layer edit operation.
@ 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 an STL-style iterator for vertices.
virtual bool addZValue(double zValue=0)=0
Adds a z-dimension to the geometry, initialized to a preset value.
bool isMeasure() const
Returns true if the geometry contains m values.
virtual QgsRectangle boundingBox() const
Returns the minimal bounding box for the geometry.
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
virtual int nCoordinates() const
Returns the number of nodes contained in the geometry.
virtual bool addMValue(double mValue=0)=0
Adds a measure to the geometry, initialized to a preset value.
Qgis::WkbType wkbType() const
Returns the WKB type of the geometry.
virtual bool hasCurvedSegments() const
Returns true if the geometry contains curved segments.
Qgis::DistanceUnit mapUnits
Abstract base class for curved geometry type.
Qgis::AngularDirection orientation() const
Returns the curve's orientation, e.g.
virtual QgsCurve * reversed() const =0
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
Wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Q_INVOKABLE QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
Encapsulate a field in an attribute table or data source.
Qgis::FieldDomainSplitPolicy splitPolicy() const
Returns the field's split policy, which indicates how field values should be handled during a split o...
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).
double length() const
Returns the planar, 2-dimensional length of geometry.
bool addTopologicalPoint(const QgsPoint &point, double snappingTolerance=1e-8, double segmentSearchEpsilon=1e-12)
Adds a vertex to the segment which intersect point but don't already have a vertex there.
static QgsGeometry collectGeometry(const QVector< QgsGeometry > &geometries)
Creates a new multipart geometry from a list of QgsGeometry objects.
QVector< QgsGeometry > asGeometryCollection() const
Returns contents of the geometry as a list of geometries.
QgsAbstractGeometry * get()
Returns a modifiable (non-const) reference to the underlying abstract geometry primitive.
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.
double area() const
Returns the planar, 2-dimensional area of the geometry.
Qgis::AngularDirection polygonOrientation() const
Returns the orientation of the polygon.
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.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
Qgis::GeometryOperationResult addPartV2(const QVector< QgsPointXY > &points, Qgis::WkbType wkbType=Qgis::WkbType::Unknown)
Adds a new part to a 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...
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
QgsCoordinateTransformContext transformContext() const
Returns the layer data provider coordinate transform context or a default transform context if the la...
Point geometry type, with support for z-dimension and m-values.
A rectangle specified with double values.
void setYMinimum(double y)
Set the minimum y value.
void setXMinimum(double x)
Set the minimum x value.
void setYMaximum(double y)
Set the maximum y value.
void setXMaximum(double x)
Set the maximum x value.
void grow(double delta)
Grows the rectangle in place by the specified amount.
static const QgsSettingsEntryDouble * settingsDigitizingDefaultMValue
Settings entry digitizing default m value.
static const QgsSettingsEntryDouble * settingsDigitizingDefaultZValue
Settings entry digitizing default z value.
Represents a default, "not-specified" value for a feature attribute.
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 dataset.
bool isEditable() const final
Returns true if the provider is in editing mode.
bool isSpatial() const final
Returns true if this is a geometry layer and false in case of NoGeometry (table only) or UnknownGeome...
void endEditCommand()
Finish edit command and add it to undo/redo stack.
void destroyEditCommand()
Destroy active command and reverts all changes in it.
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 getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const final
Queries the layer for features specified in request.
Q_INVOKABLE Qgis::GeometryType geometryType() const
Returns point, line or polygon.
void beginEditCommand(const QString &text)
Create edit command for undo/redo operations.
int addTopologicalPoints(const QgsGeometry &geom)
Adds topological points for every vertex of the geometry.
QgsVectorDataProvider * dataProvider() final
Returns the layer's data provider, it may be nullptr.
Q_INVOKABLE 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 Q_INVOKABLE bool isSingleType(Qgis::WkbType type)
Returns true if the WKB type is a single type.
static Q_INVOKABLE bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
static Q_INVOKABLE bool hasM(Qgis::WkbType type)
Tests whether a WKB type contains m values.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
QVector< QgsPoint > QgsPointSequence
QMap< int, QVariant > QgsAttributeMap
QList< QgsFeature > QgsFeatureList
QSet< QgsFeatureId > QgsFeatureIds
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
#define QgsDebugError(str)