From 7db84789f83044ec5cebb890cb5cd7bbd2ef4e28 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 28 Jan 2022 14:43:50 +1000 Subject: [PATCH] Add method to convert QgsFieldDomain to OGR field domain --- src/core/qgsogrutils.cpp | 104 +++++++++++++++++++++++++++++ src/core/qgsogrutils.h | 9 +++ tests/src/core/testqgsogrutils.cpp | 78 ++++++++++++++++++++++ 3 files changed, 191 insertions(+) diff --git a/src/core/qgsogrutils.cpp b/src/core/qgsogrutils.cpp index 200fd5bfef6..d8f060eaca1 100644 --- a/src/core/qgsogrutils.cpp +++ b/src/core/qgsogrutils.cpp @@ -2123,4 +2123,108 @@ std::unique_ptr< QgsFieldDomain > QgsOgrUtils::convertFieldDomain( OGRFieldDomai } return res; } + +OGRFieldDomainH QgsOgrUtils::convertFieldDomain( const QgsFieldDomain *domain ) +{ + if ( !domain ) + return nullptr; + + OGRFieldType domainFieldType = OFTInteger; + OGRFieldSubType domainFieldSubType = OFSTNone; + variantTypeToOgrFieldType( domain->fieldType(), domainFieldType, domainFieldSubType ); + + OGRFieldDomainH res = nullptr; + switch ( domain->type() ) + { + case Qgis::FieldDomainType::Coded: + { + std::vector< OGRCodedValue > enumeration; + const QList< QgsCodedValue> values = qgis::down_cast< const QgsCodedFieldDomain * >( domain )->values(); + enumeration.reserve( values.size() ); + for ( const QgsCodedValue &value : values ) + { + OGRCodedValue codedValue; + codedValue.pszCode = CPLStrdup( value.code().toString().toUtf8().constData() ); + codedValue.pszValue = CPLStrdup( value.value().toUtf8().constData() ); + enumeration.push_back( codedValue ); + } + OGRCodedValue last; + last.pszCode = nullptr; + last.pszValue = nullptr; + enumeration.push_back( last ); + res = OGR_CodedFldDomain_Create( + domain->name().toUtf8().constData(), + domain->description().toUtf8().constData(), + domainFieldType, + domainFieldSubType, + enumeration.data() + ); + + for ( const OGRCodedValue &value : std::as_const( enumeration ) ) + { + CPLFree( value.pszCode ); + CPLFree( value.pszValue ); + } + break; + } + + case Qgis::FieldDomainType::Range: + { + std::unique_ptr< OGRField > min = variantToOGRField( qgis::down_cast< const QgsRangeFieldDomain * >( domain )->minimum() ); + std::unique_ptr< OGRField > max = variantToOGRField( qgis::down_cast< const QgsRangeFieldDomain * >( domain )->maximum() ); + res = OGR_RangeFldDomain_Create( + domain->name().toUtf8().constData(), + domain->description().toUtf8().constData(), + domainFieldType, + domainFieldSubType, + min.get(), + qgis::down_cast< const QgsRangeFieldDomain * >( domain )->minimumIsInclusive(), + max.get(), + qgis::down_cast< const QgsRangeFieldDomain * >( domain )->maximumIsInclusive() + ); + break; + } + + case Qgis::FieldDomainType::Glob: + { + res = OGR_GlobFldDomain_Create( + domain->name().toUtf8().constData(), + domain->description().toUtf8().constData(), + domainFieldType, + domainFieldSubType, + qgis::down_cast< const QgsGlobFieldDomain * >( domain )->glob().toUtf8().constData() + ); + break; + } + } + + switch ( domain->mergePolicy() ) + { + case Qgis::FieldDomainMergePolicy::DefaultValue: + OGR_FldDomain_SetMergePolicy( res, OFDMP_DEFAULT_VALUE ); + break; + case Qgis::FieldDomainMergePolicy::GeometryWeighted: + OGR_FldDomain_SetMergePolicy( res, OFDMP_GEOMETRY_WEIGHTED ); + break; + case Qgis::FieldDomainMergePolicy::Sum: + OGR_FldDomain_SetMergePolicy( res, OFDMP_SUM ); + break; + } + + switch ( domain->splitPolicy() ) + { + case Qgis::FieldDomainSplitPolicy::DefaultValue: + OGR_FldDomain_SetSplitPolicy( res, OFDSP_DEFAULT_VALUE ); + break; + case Qgis::FieldDomainSplitPolicy::GeometryRatio: + OGR_FldDomain_SetSplitPolicy( res, OFDSP_GEOMETRY_RATIO ); + break; + case Qgis::FieldDomainSplitPolicy::Duplicate: + OGR_FldDomain_SetSplitPolicy( res, OFDSP_DUPLICATE ); + break; + } + + return res; +} + #endif diff --git a/src/core/qgsogrutils.h b/src/core/qgsogrutils.h index 40475c74bbd..2da968ef519 100644 --- a/src/core/qgsogrutils.h +++ b/src/core/qgsogrutils.h @@ -407,6 +407,15 @@ class CORE_EXPORT QgsOgrUtils * \since QGIS 3.26 */ static std::unique_ptr< QgsFieldDomain > convertFieldDomain( OGRFieldDomainH domain ); + + /** + * Converts a QGIS field domain definition to an OGR field domain equivalent. + * + * \note Requires GDAL >= 3.3 + * \note Not available in Python bindings + * \since QGIS 3.26 + */ + static OGRFieldDomainH convertFieldDomain( const QgsFieldDomain *domain ); #endif #endif }; diff --git a/tests/src/core/testqgsogrutils.cpp b/tests/src/core/testqgsogrutils.cpp index bef49079bc2..f8ff8d8e62d 100644 --- a/tests/src/core/testqgsogrutils.cpp +++ b/tests/src/core/testqgsogrutils.cpp @@ -75,6 +75,7 @@ class TestQgsOgrUtils: public QObject #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,3,0) void testConvertFieldDomain(); + void testConvertToFieldDomain(); #endif private: @@ -1053,6 +1054,83 @@ void TestQgsOgrUtils::testConvertFieldDomain() QCOMPARE( globDomain->fieldType(), QVariant::String ); OGR_FldDomain_Destroy( domain ); } + +void TestQgsOgrUtils::testConvertToFieldDomain() +{ + // test converting QgsFieldDomain to OGR field domain + QgsGlobFieldDomain globDomain( QStringLiteral( "name" ), QStringLiteral( "desc" ), QVariant::String, QStringLiteral( "*a*" ) ); + OGRFieldDomainH domain = QgsOgrUtils::convertFieldDomain( &globDomain ); + + std::unique_ptr< QgsFieldDomain > res = QgsOgrUtils::convertFieldDomain( domain ); + QCOMPARE( res->name(), QStringLiteral( "name" ) ); + QCOMPARE( res->description(), QStringLiteral( "desc" ) ); + QCOMPARE( res->splitPolicy(), Qgis::FieldDomainSplitPolicy::DefaultValue ); + QCOMPARE( res->mergePolicy(), Qgis::FieldDomainMergePolicy::DefaultValue ); + QCOMPARE( dynamic_cast< QgsGlobFieldDomain * >( res.get() )->glob(), QStringLiteral( "*a*" ) ); + OGR_FldDomain_Destroy( domain ); + + globDomain.setSplitPolicy( Qgis::FieldDomainSplitPolicy::Duplicate ); + globDomain.setMergePolicy( Qgis::FieldDomainMergePolicy::Sum ); + domain = QgsOgrUtils::convertFieldDomain( &globDomain ); + res = QgsOgrUtils::convertFieldDomain( domain ); + OGR_FldDomain_Destroy( domain ); + QCOMPARE( res->splitPolicy(), Qgis::FieldDomainSplitPolicy::Duplicate ); + QCOMPARE( res->mergePolicy(), Qgis::FieldDomainMergePolicy::Sum ); + + globDomain.setSplitPolicy( Qgis::FieldDomainSplitPolicy::GeometryRatio ); + globDomain.setMergePolicy( Qgis::FieldDomainMergePolicy::GeometryWeighted ); + domain = QgsOgrUtils::convertFieldDomain( &globDomain ); + res = QgsOgrUtils::convertFieldDomain( domain ); + OGR_FldDomain_Destroy( domain ); + QCOMPARE( res->splitPolicy(), Qgis::FieldDomainSplitPolicy::GeometryRatio ); + QCOMPARE( res->mergePolicy(), Qgis::FieldDomainMergePolicy::GeometryWeighted ); + + // range + + QgsRangeFieldDomain rangeDomain( QStringLiteral( "name" ), QStringLiteral( "desc" ), QVariant::Int, + 1, true, 5, false ); + domain = QgsOgrUtils::convertFieldDomain( &rangeDomain ); + res = QgsOgrUtils::convertFieldDomain( domain ); + OGR_FldDomain_Destroy( domain ); + QCOMPARE( res->name(), QStringLiteral( "name" ) ); + QCOMPARE( res->description(), QStringLiteral( "desc" ) ); + QCOMPARE( dynamic_cast< QgsRangeFieldDomain * >( res.get() )->minimum(), QVariant( 1 ) ); + QVERIFY( dynamic_cast< QgsRangeFieldDomain * >( res.get() )->minimumIsInclusive() ); + QCOMPARE( dynamic_cast< QgsRangeFieldDomain * >( res.get() )->maximum(), QVariant( 5 ) ); + QVERIFY( !dynamic_cast< QgsRangeFieldDomain * >( res.get() )->maximumIsInclusive() ); + + rangeDomain.setFieldType( QVariant::Double ); + rangeDomain.setMinimum( 5.5 ); + rangeDomain.setMaximum( 12.1 ); + rangeDomain.setMinimumIsInclusive( false ); + rangeDomain.setMaximumIsInclusive( true ); + domain = QgsOgrUtils::convertFieldDomain( &rangeDomain ); + res = QgsOgrUtils::convertFieldDomain( domain ); + OGR_FldDomain_Destroy( domain ); + QCOMPARE( dynamic_cast< QgsRangeFieldDomain * >( res.get() )->minimum(), QVariant( 5.5 ) ); + QVERIFY( !dynamic_cast< QgsRangeFieldDomain * >( res.get() )->minimumIsInclusive() ); + QCOMPARE( dynamic_cast< QgsRangeFieldDomain * >( res.get() )->maximum(), QVariant( 12.1 ) ); + QVERIFY( dynamic_cast< QgsRangeFieldDomain * >( res.get() )->maximumIsInclusive() ); + + // coded + QgsCodedFieldDomain codedDomain( QStringLiteral( "name" ), QStringLiteral( "desc" ), QVariant::String, + { + QgsCodedValue( "aa", "aaaa" ), + QgsCodedValue( "bb", "bbbb" ), + } ); + domain = QgsOgrUtils::convertFieldDomain( &codedDomain ); + res = QgsOgrUtils::convertFieldDomain( domain ); + OGR_FldDomain_Destroy( domain ); + QCOMPARE( res->name(), QStringLiteral( "name" ) ); + QCOMPARE( res->description(), QStringLiteral( "desc" ) ); + QList< QgsCodedValue > resValues = dynamic_cast< QgsCodedFieldDomain * >( res.get() )->values(); + QCOMPARE( resValues.size(), 2 ); + QCOMPARE( resValues.at( 0 ).code(), QVariant( "aa" ) ); + QCOMPARE( resValues.at( 0 ).value(), QStringLiteral( "aaaa" ) ); + QCOMPARE( resValues.at( 1 ).code(), QVariant( "bb" ) ); + QCOMPARE( resValues.at( 1 ).value(), QStringLiteral( "bbbb" ) ); + +} #endif QGSTEST_MAIN( TestQgsOgrUtils )