mirror of
https://github.com/qgis/QGIS.git
synced 2025-10-06 00:07:29 -04:00
Ensure that field split policies are correctly respected when
splitting features
This commit is contained in:
parent
edfb9764cb
commit
9595596e04
@ -14,6 +14,7 @@
|
|||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
#include "qgsvectorlayereditutils.h"
|
#include "qgsvectorlayereditutils.h"
|
||||||
|
|
||||||
|
#include "qgsunsetattributevalue.h"
|
||||||
#include "qgsvectordataprovider.h"
|
#include "qgsvectordataprovider.h"
|
||||||
#include "qgsfeatureiterator.h"
|
#include "qgsfeatureiterator.h"
|
||||||
#include "qgsvectorlayereditbuffer.h"
|
#include "qgsvectorlayereditbuffer.h"
|
||||||
@ -413,6 +414,8 @@ Qgis::GeometryOperationResult QgsVectorLayerEditUtils::splitFeatures( const QgsC
|
|||||||
|
|
||||||
QgsVectorLayerUtils::QgsFeaturesDataList featuresDataToAdd;
|
QgsVectorLayerUtils::QgsFeaturesDataList featuresDataToAdd;
|
||||||
|
|
||||||
|
const int fieldCount = mLayer->fields().count();
|
||||||
|
|
||||||
QgsFeature feat;
|
QgsFeature feat;
|
||||||
while ( features.nextFeature( feat ) )
|
while ( features.nextFeature( feat ) )
|
||||||
{
|
{
|
||||||
@ -422,7 +425,8 @@ Qgis::GeometryOperationResult QgsVectorLayerEditUtils::splitFeatures( const QgsC
|
|||||||
}
|
}
|
||||||
QVector<QgsGeometry> newGeometries;
|
QVector<QgsGeometry> newGeometries;
|
||||||
QgsPointSequence featureTopologyTestPoints;
|
QgsPointSequence featureTopologyTestPoints;
|
||||||
QgsGeometry featureGeom = feat.geometry();
|
const QgsGeometry originalGeom = feat.geometry();
|
||||||
|
QgsGeometry featureGeom = originalGeom;
|
||||||
splitFunctionReturn = featureGeom.splitGeometry( curve, newGeometries, preserveCircular, topologicalEditing, featureTopologyTestPoints );
|
splitFunctionReturn = featureGeom.splitGeometry( curve, newGeometries, preserveCircular, topologicalEditing, featureTopologyTestPoints );
|
||||||
topologyTestPoints.append( featureTopologyTestPoints );
|
topologyTestPoints.append( featureTopologyTestPoints );
|
||||||
if ( splitFunctionReturn == Qgis::GeometryOperationResult::Success )
|
if ( splitFunctionReturn == Qgis::GeometryOperationResult::Success )
|
||||||
@ -430,10 +434,142 @@ Qgis::GeometryOperationResult QgsVectorLayerEditUtils::splitFeatures( const QgsC
|
|||||||
//change this geometry
|
//change this geometry
|
||||||
mLayer->changeGeometry( feat.id(), featureGeom );
|
mLayer->changeGeometry( feat.id(), featureGeom );
|
||||||
|
|
||||||
|
//update any attributes for original feature which are set to GeometryRatio split policy
|
||||||
|
QgsAttributeMap attributeMap;
|
||||||
|
for ( int fieldIdx = 0; fieldIdx < fieldCount; ++fieldIdx )
|
||||||
|
{
|
||||||
|
const QgsField field = mLayer->fields().at( fieldIdx );
|
||||||
|
switch ( field.splitPolicy() )
|
||||||
|
{
|
||||||
|
case Qgis::FieldDomainSplitPolicy::DefaultValue:
|
||||||
|
case Qgis::FieldDomainSplitPolicy::Duplicate:
|
||||||
|
case Qgis::FieldDomainSplitPolicy::UnsetField:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Qgis::FieldDomainSplitPolicy::GeometryRatio:
|
||||||
|
{
|
||||||
|
if ( field.isNumeric() )
|
||||||
|
{
|
||||||
|
const double originalValue = feat.attribute( fieldIdx ).toDouble();
|
||||||
|
|
||||||
|
double originalSize = 0;
|
||||||
|
|
||||||
|
switch ( originalGeom.type() )
|
||||||
|
{
|
||||||
|
case Qgis::GeometryType::Point:
|
||||||
|
case Qgis::GeometryType::Unknown:
|
||||||
|
case Qgis::GeometryType::Null:
|
||||||
|
originalSize = 0;
|
||||||
|
break;
|
||||||
|
case Qgis::GeometryType::Line:
|
||||||
|
originalSize = originalGeom.length();
|
||||||
|
break;
|
||||||
|
case Qgis::GeometryType::Polygon:
|
||||||
|
originalSize = originalGeom.area();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
double newSize = 0;
|
||||||
|
switch ( featureGeom.type() )
|
||||||
|
{
|
||||||
|
case Qgis::GeometryType::Point:
|
||||||
|
case Qgis::GeometryType::Unknown:
|
||||||
|
case Qgis::GeometryType::Null:
|
||||||
|
newSize = 0;
|
||||||
|
break;
|
||||||
|
case Qgis::GeometryType::Line:
|
||||||
|
newSize = featureGeom.length();
|
||||||
|
break;
|
||||||
|
case Qgis::GeometryType::Polygon:
|
||||||
|
newSize = featureGeom.area();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeMap.insert( fieldIdx, originalSize > 0 ? ( originalValue * newSize / originalSize ) : originalValue );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !attributeMap.isEmpty() )
|
||||||
|
{
|
||||||
|
mLayer->changeAttributeValues( feat.id(), attributeMap );
|
||||||
|
}
|
||||||
|
|
||||||
//insert new features
|
//insert new features
|
||||||
QgsAttributeMap attributeMap = feat.attributes().toMap();
|
|
||||||
for ( const QgsGeometry &geom : std::as_const( newGeometries ) )
|
for ( const QgsGeometry &geom : std::as_const( newGeometries ) )
|
||||||
{
|
{
|
||||||
|
QgsAttributeMap attributeMap;
|
||||||
|
for ( int fieldIdx = 0; fieldIdx < fieldCount; ++fieldIdx )
|
||||||
|
{
|
||||||
|
const QgsField field = mLayer->fields().at( fieldIdx );
|
||||||
|
// respect field split policy
|
||||||
|
switch ( field.splitPolicy() )
|
||||||
|
{
|
||||||
|
case Qgis::FieldDomainSplitPolicy::DefaultValue:
|
||||||
|
// TODO!!!
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Qgis::FieldDomainSplitPolicy::Duplicate:
|
||||||
|
attributeMap.insert( fieldIdx, feat.attribute( fieldIdx ) );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Qgis::FieldDomainSplitPolicy::GeometryRatio:
|
||||||
|
{
|
||||||
|
if ( !field.isNumeric() )
|
||||||
|
{
|
||||||
|
attributeMap.insert( fieldIdx, feat.attribute( fieldIdx ) );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const double originalValue = feat.attribute( fieldIdx ).toDouble();
|
||||||
|
|
||||||
|
double originalSize = 0;
|
||||||
|
|
||||||
|
switch ( originalGeom.type() )
|
||||||
|
{
|
||||||
|
case Qgis::GeometryType::Point:
|
||||||
|
case Qgis::GeometryType::Unknown:
|
||||||
|
case Qgis::GeometryType::Null:
|
||||||
|
originalSize = 0;
|
||||||
|
break;
|
||||||
|
case Qgis::GeometryType::Line:
|
||||||
|
originalSize = originalGeom.length();
|
||||||
|
break;
|
||||||
|
case Qgis::GeometryType::Polygon:
|
||||||
|
originalSize = originalGeom.area();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
double newSize = 0;
|
||||||
|
switch ( geom.type() )
|
||||||
|
{
|
||||||
|
case Qgis::GeometryType::Point:
|
||||||
|
case Qgis::GeometryType::Unknown:
|
||||||
|
case Qgis::GeometryType::Null:
|
||||||
|
newSize = 0;
|
||||||
|
break;
|
||||||
|
case Qgis::GeometryType::Line:
|
||||||
|
newSize = geom.length();
|
||||||
|
break;
|
||||||
|
case Qgis::GeometryType::Polygon:
|
||||||
|
newSize = geom.area();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeMap.insert( fieldIdx, originalSize > 0 ? ( originalValue * newSize / originalSize ) : originalValue );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Qgis::FieldDomainSplitPolicy::UnsetField:
|
||||||
|
attributeMap.insert( fieldIdx, QgsUnsetAttributeValue() );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
featuresDataToAdd << QgsVectorLayerUtils::QgsFeatureData( geom, attributeMap );
|
featuresDataToAdd << QgsVectorLayerUtils::QgsFeatureData( geom, attributeMap );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,6 +34,9 @@ from qgis.core import (
|
|||||||
QgsVectorLayer,
|
QgsVectorLayer,
|
||||||
QgsVectorLayerEditUtils,
|
QgsVectorLayerEditUtils,
|
||||||
QgsWkbTypes,
|
QgsWkbTypes,
|
||||||
|
QgsDefaultValue,
|
||||||
|
QgsVectorLayerUtils,
|
||||||
|
QgsUnsetAttributeValue
|
||||||
)
|
)
|
||||||
from qgis.testing import start_app, unittest
|
from qgis.testing import start_app, unittest
|
||||||
|
|
||||||
@ -571,6 +574,114 @@ class TestQgsVectorLayerEditUtils(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
self.assertEqual(mergedFeature.attribute('name'), 'tre')
|
self.assertEqual(mergedFeature.attribute('name'), 'tre')
|
||||||
|
|
||||||
|
def test_split_policy_lines(self):
|
||||||
|
temp_layer = QgsVectorLayer("LineString?crs=epsg:3111&field=pk:int&field=field_default:real&field=field_dupe:real&field=field_unset:real&field=field_ratio:real&field=apply_default:real",
|
||||||
|
"vl", "memory")
|
||||||
|
self.assertTrue(temp_layer.isValid())
|
||||||
|
|
||||||
|
temp_layer.setDefaultValueDefinition(1,
|
||||||
|
QgsDefaultValue('301'))
|
||||||
|
temp_layer.setDefaultValueDefinition(2,
|
||||||
|
QgsDefaultValue('302'))
|
||||||
|
temp_layer.setDefaultValueDefinition(3,
|
||||||
|
QgsDefaultValue('303'))
|
||||||
|
temp_layer.setDefaultValueDefinition(4,
|
||||||
|
QgsDefaultValue('304'))
|
||||||
|
temp_layer.setDefaultValueDefinition(5,
|
||||||
|
QgsDefaultValue('305', True))
|
||||||
|
|
||||||
|
temp_layer.setFieldSplitPolicy(1,
|
||||||
|
Qgis.FieldDomainSplitPolicy.DefaultValue)
|
||||||
|
temp_layer.setFieldSplitPolicy(2,
|
||||||
|
Qgis.FieldDomainSplitPolicy.Duplicate)
|
||||||
|
temp_layer.setFieldSplitPolicy(3,
|
||||||
|
Qgis.FieldDomainSplitPolicy.UnsetField)
|
||||||
|
temp_layer.setFieldSplitPolicy(4,
|
||||||
|
Qgis.FieldDomainSplitPolicy.GeometryRatio)
|
||||||
|
# this will be ignored -- the apply on update default will override it
|
||||||
|
temp_layer.setFieldSplitPolicy(5,
|
||||||
|
Qgis.FieldDomainSplitPolicy.GeometryRatio)
|
||||||
|
|
||||||
|
temp_layer.startEditing()
|
||||||
|
|
||||||
|
feature = QgsVectorLayerUtils.createFeature(temp_layer,
|
||||||
|
QgsGeometry.fromWkt('LineString( 0 0, 10 0)'))
|
||||||
|
feature[1] = 3301
|
||||||
|
feature[5] = 3305
|
||||||
|
self.assertTrue(temp_layer.addFeature(feature))
|
||||||
|
|
||||||
|
temp_layer.commitChanges()
|
||||||
|
|
||||||
|
original_feature = next(temp_layer.getFeatures())
|
||||||
|
self.assertEqual(original_feature.attributes(),
|
||||||
|
[None, 3301.0, 302.0, 303.0, 304.0, 3305.0])
|
||||||
|
|
||||||
|
temp_layer.startEditing()
|
||||||
|
|
||||||
|
split_curve = QgsGeometry.fromWkt('LineString (0.3 0.2, 0.27 -0.2, 0.86 -0.24, 0.88 0.22)')
|
||||||
|
temp_layer.splitFeatures(split_curve.constGet(), preserveCircular=False)
|
||||||
|
|
||||||
|
features = list(temp_layer.getFeatures())
|
||||||
|
attributes = [f.attributes() for f in features]
|
||||||
|
self.assertCountEqual(attributes,
|
||||||
|
[[None, 3301.0, 302.0, 303.0, 8.664000000000001, 305.0],
|
||||||
|
[None, 301, 302.0, QgsUnsetAttributeValue(), 17.797217391304347, 305.0],
|
||||||
|
[None, 301, 302.0, QgsUnsetAttributeValue(), 277.53878260869567, 305.0]
|
||||||
|
])
|
||||||
|
|
||||||
|
temp_layer.rollBack()
|
||||||
|
|
||||||
|
def test_split_policy_polygon(self):
|
||||||
|
temp_layer = QgsVectorLayer("Polygon?crs=epsg:3111&field=pk:int&field=field_default:real&field=field_dupe:real&field=field_unset:real&field=field_ratio:real",
|
||||||
|
"vl", "memory")
|
||||||
|
self.assertTrue(temp_layer.isValid())
|
||||||
|
|
||||||
|
temp_layer.setDefaultValueDefinition(1,
|
||||||
|
QgsDefaultValue('301'))
|
||||||
|
temp_layer.setDefaultValueDefinition(2,
|
||||||
|
QgsDefaultValue('302'))
|
||||||
|
temp_layer.setDefaultValueDefinition(3,
|
||||||
|
QgsDefaultValue('303'))
|
||||||
|
temp_layer.setDefaultValueDefinition(4,
|
||||||
|
QgsDefaultValue('304'))
|
||||||
|
|
||||||
|
temp_layer.setFieldSplitPolicy(1,
|
||||||
|
Qgis.FieldDomainSplitPolicy.DefaultValue)
|
||||||
|
temp_layer.setFieldSplitPolicy(2,
|
||||||
|
Qgis.FieldDomainSplitPolicy.Duplicate)
|
||||||
|
temp_layer.setFieldSplitPolicy(3,
|
||||||
|
Qgis.FieldDomainSplitPolicy.UnsetField)
|
||||||
|
temp_layer.setFieldSplitPolicy(4,
|
||||||
|
Qgis.FieldDomainSplitPolicy.GeometryRatio)
|
||||||
|
|
||||||
|
temp_layer.startEditing()
|
||||||
|
|
||||||
|
feature = QgsVectorLayerUtils.createFeature(temp_layer,
|
||||||
|
QgsGeometry.fromWkt('Polygon(( 0 0, 10 0, 10 10, 0 10, 0 0))'))
|
||||||
|
feature[1] = 3301
|
||||||
|
self.assertTrue(temp_layer.addFeature(feature))
|
||||||
|
|
||||||
|
temp_layer.commitChanges()
|
||||||
|
|
||||||
|
original_feature = next(temp_layer.getFeatures())
|
||||||
|
self.assertEqual(original_feature.attributes(),
|
||||||
|
[None, 3301.0, 302.0, 303.0, 304.0])
|
||||||
|
|
||||||
|
temp_layer.startEditing()
|
||||||
|
|
||||||
|
split_curve = QgsGeometry.fromWkt('LineString (-2.7 4.3, 5.5 11.8, 5.28 -5.57)')
|
||||||
|
temp_layer.splitFeatures(split_curve.constGet(), preserveCircular=False)
|
||||||
|
|
||||||
|
features = list(temp_layer.getFeatures())
|
||||||
|
attributes = [f.attributes() for f in features]
|
||||||
|
self.assertCountEqual(attributes,
|
||||||
|
[[None, 3301.0, 302.0, 303.0, 147.23845863746018],
|
||||||
|
[None, 301, 302.0, QgsUnsetAttributeValue(), 17.343326048780483],
|
||||||
|
[None, 301, 302.0, QgsUnsetAttributeValue(), 139.41821531375936]
|
||||||
|
])
|
||||||
|
|
||||||
|
temp_layer.rollBack()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user