diff --git a/python/core/auto_generated/symbology/qgssymbollayerutils.sip.in b/python/core/auto_generated/symbology/qgssymbollayerutils.sip.in index 59e970a0bbe..e9c974695b1 100644 --- a/python/core/auto_generated/symbology/qgssymbollayerutils.sip.in +++ b/python/core/auto_generated/symbology/qgssymbollayerutils.sip.in @@ -92,6 +92,23 @@ Decodes a QSizeF from a string. .. seealso:: :py:func:`encodePoint` .. seealso:: :py:func:`decodeSize` +%End + + static QPointF toPoint( const QVariant &value, bool *ok /Out/ = 0 ); +%Docstring +Converts a ``value`` to a point. + +:param value: value to convert + +:return: - converted point + - ok: will be set to ``True`` if value was successfully converted + + +.. seealso:: :py:func:`decodePoint` + +.. seealso:: :py:func:`toSize` + +.. versionadded:: 3.10 %End static QString encodeSize( QSizeF size ); @@ -114,6 +131,23 @@ Decodes a QSizeF from a string. .. seealso:: :py:func:`decodePoint` .. versionadded:: 3.0 +%End + + static QSizeF toSize( const QVariant &value, bool *ok /Out/ = 0 ); +%Docstring +Converts a ``value`` to a size. + +:param value: value to convert + +:return: - converted size + - ok: will be set to ``True`` if value was successfully converted + + +.. seealso:: :py:func:`decodeSize` + +.. seealso:: :py:func:`toPoint` + +.. versionadded:: 3.10 %End static QString encodeMapUnitScale( const QgsMapUnitScale &mapUnitScale ); diff --git a/src/core/qgspallabeling.cpp b/src/core/qgspallabeling.cpp index 065e18a5a73..f3a989f59c0 100644 --- a/src/core/qgspallabeling.cpp +++ b/src/core/qgspallabeling.cpp @@ -1912,17 +1912,12 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::CurvedCharAngleInOut ) ) { exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::CurvedCharAngleInOut, context.expressionContext() ); - if ( exprVal.isValid() ) + bool ok = false; + const QPointF maxcharanglePt = QgsSymbolLayerUtils::toPoint( exprVal, &ok ); + if ( ok ) { - QString ptstr = exprVal.toString().trimmed(); - QgsDebugMsgLevel( QStringLiteral( "exprVal CurvedCharAngleInOut:%1" ).arg( ptstr ), 4 ); - - if ( !ptstr.isEmpty() ) - { - QPointF maxcharanglePt = QgsSymbolLayerUtils::decodePoint( ptstr ); - maxcharanglein = qBound( 20.0, static_cast< double >( maxcharanglePt.x() ), 60.0 ); - maxcharangleout = qBound( 20.0, static_cast< double >( maxcharanglePt.y() ), 95.0 ); - } + maxcharanglein = qBound( 20.0, static_cast< double >( maxcharanglePt.x() ), 60.0 ); + maxcharangleout = qBound( 20.0, static_cast< double >( maxcharanglePt.y() ), 95.0 ); } } // make sure maxcharangleout is always negative @@ -2134,15 +2129,12 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext { context.expressionContext().setOriginalValueVariable( QgsSymbolLayerUtils::encodePoint( QPointF( xOffset, yOffset ) ) ); exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::OffsetXY, context.expressionContext() ); - if ( exprVal.isValid() ) + bool ok = false; + const QPointF ddOffPt = QgsSymbolLayerUtils::toPoint( exprVal, &ok ); + if ( ok ) { - QString ptstr = exprVal.toString().trimmed(); - if ( !ptstr.isEmpty() ) - { - QPointF ddOffPt = QgsSymbolLayerUtils::decodePoint( ptstr ); - xOff = ddOffPt.x(); - yOff = ddOffPt.y(); - } + xOff = ddOffPt.x(); + yOff = ddOffPt.y(); } } @@ -2774,22 +2766,22 @@ bool QgsPalLayerSettings::dataDefinedValEval( DataDefinedValueType valType, } case DDPointF: { - QString ptstr = exprVal.toString().trimmed(); - - if ( !ptstr.isEmpty() ) + bool ok = false; + const QPointF res = QgsSymbolLayerUtils::toPoint( exprVal, &ok ); + if ( ok ) { - dataDefinedValues.insert( p, QVariant( QgsSymbolLayerUtils::decodePoint( ptstr ) ) ); + dataDefinedValues.insert( p, res ); return true; } return false; } case DDSizeF: { - QString ptstr = exprVal.toString().trimmed(); - - if ( !ptstr.isEmpty() ) + bool ok = false; + const QSizeF res = QgsSymbolLayerUtils::toSize( exprVal, &ok ); + if ( ok ) { - dataDefinedValues.insert( p, QVariant( QgsSymbolLayerUtils::decodeSize( ptstr ) ) ); + dataDefinedValues.insert( p, res ); return true; } return false; diff --git a/src/core/qgstextrenderer.cpp b/src/core/qgstextrenderer.cpp index c89c9f880de..55dd73a7453 100644 --- a/src/core/qgstextrenderer.cpp +++ b/src/core/qgstextrenderer.cpp @@ -1053,10 +1053,11 @@ void QgsTextBackgroundSettings::updateDataDefinedProperties( QgsRenderContext &c exprVal = properties.value( QgsPalLayerSettings::ShapeOffset, context.expressionContext() ); if ( exprVal.isValid() ) { - QString offset = exprVal.toString(); - if ( !offset.isEmpty() ) + bool ok = false; + const QPointF res = QgsSymbolLayerUtils::toPoint( exprVal, &ok ); + if ( ok ) { - d->offset = QgsSymbolLayerUtils::decodePoint( offset ); + d->offset = res; } } exprVal = properties.value( QgsPalLayerSettings::ShapeOffsetUnits, context.expressionContext() ); @@ -1075,10 +1076,11 @@ void QgsTextBackgroundSettings::updateDataDefinedProperties( QgsRenderContext &c exprVal = properties.value( QgsPalLayerSettings::ShapeRadii, context.expressionContext() ); if ( exprVal.isValid() ) { - QString ptstr = exprVal.toString(); - if ( !ptstr.isEmpty() ) + bool ok = false; + const QSizeF res = QgsSymbolLayerUtils::toSize( exprVal, &ok ); + if ( ok ) { - d->radii = QgsSymbolLayerUtils::decodeSize( ptstr ); + d->radii = res; } } diff --git a/src/core/symbology/qgsmarkersymbollayer.cpp b/src/core/symbology/qgsmarkersymbollayer.cpp index 2d66d0275ed..27cfed9b14a 100644 --- a/src/core/symbology/qgsmarkersymbollayer.cpp +++ b/src/core/symbology/qgsmarkersymbollayer.cpp @@ -2391,9 +2391,10 @@ bool QgsSvgMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFa if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyOffset ) ) { context.setOriginalValueVariable( QgsSymbolLayerUtils::encodePoint( mOffset ) ); - QString offsetString = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyOffset, context.renderContext().expressionContext(), QString(), &ok ); + const QVariant val = mDataDefinedProperties.value( QgsSymbolLayer::PropertyOffset, context.renderContext().expressionContext(), QString() ); + const QPointF res = QgsSymbolLayerUtils::toPoint( val, &ok ); if ( ok ) - offset = QgsSymbolLayerUtils::decodePoint( offsetString ); + offset = res; } double offsetX = offset.x(); double offsetY = offset.y(); diff --git a/src/core/symbology/qgssymbollayer.cpp b/src/core/symbology/qgssymbollayer.cpp index 154f3644b6b..1fe11203461 100644 --- a/src/core/symbology/qgssymbollayer.cpp +++ b/src/core/symbology/qgssymbollayer.cpp @@ -467,9 +467,10 @@ void QgsMarkerSymbolLayer::markerOffset( QgsSymbolRenderContext &context, double { context.setOriginalValueVariable( QgsSymbolLayerUtils::encodePoint( mOffset ) ); QVariant exprVal = mDataDefinedProperties.value( QgsSymbolLayer::PropertyOffset, context.renderContext().expressionContext() ); - if ( exprVal.isValid() ) + bool ok = false; + const QPointF offset = QgsSymbolLayerUtils::toPoint( exprVal, &ok ); + if ( ok ) { - QPointF offset = QgsSymbolLayerUtils::decodePoint( exprVal.toString() ); offsetX = offset.x(); offsetY = offset.y(); } diff --git a/src/core/symbology/qgssymbollayerutils.cpp b/src/core/symbology/qgssymbollayerutils.cpp index 3bc2bd8e2fb..e0712c9b75c 100644 --- a/src/core/symbology/qgssymbollayerutils.cpp +++ b/src/core/symbology/qgssymbollayerutils.cpp @@ -440,6 +440,57 @@ QPointF QgsSymbolLayerUtils::decodePoint( const QString &str ) return QPointF( lst[0].toDouble(), lst[1].toDouble() ); } +QPointF QgsSymbolLayerUtils::toPoint( const QVariant &value, bool *ok ) +{ + if ( ok ) + *ok = false; + + if ( value.isNull() ) + return QPoint(); + + if ( value.type() == QVariant::List ) + { + const QVariantList list = value.toList(); + if ( list.size() != 2 ) + { + return QPointF(); + } + bool convertOk = false; + double x = list.at( 0 ).toDouble( &convertOk ); + if ( convertOk ) + { + double y = list.at( 1 ).toDouble( &convertOk ); + if ( convertOk ) + { + if ( ok ) + *ok = true; + return QPointF( x, y ); + } + } + return QPointF(); + } + else + { + // can't use decodePoint here -- has no OK handling + const QStringList list = value.toString().trimmed().split( ',' ); + if ( list.count() != 2 ) + return QPointF(); + bool convertOk = false; + double x = list.at( 0 ).toDouble( &convertOk ); + if ( convertOk ) + { + double y = list.at( 1 ).toDouble( &convertOk ); + if ( convertOk ) + { + if ( ok ) + *ok = true; + return QPointF( x, y ); + } + } + return QPointF(); + } +} + QString QgsSymbolLayerUtils::encodeSize( QSizeF size ) { return QStringLiteral( "%1,%2" ).arg( qgsDoubleToString( size.width() ), qgsDoubleToString( size.height() ) ); @@ -453,6 +504,57 @@ QSizeF QgsSymbolLayerUtils::decodeSize( const QString &string ) return QSizeF( lst[0].toDouble(), lst[1].toDouble() ); } +QSizeF QgsSymbolLayerUtils::toSize( const QVariant &value, bool *ok ) +{ + if ( ok ) + *ok = false; + + if ( value.isNull() ) + return QSizeF(); + + if ( value.type() == QVariant::List ) + { + const QVariantList list = value.toList(); + if ( list.size() != 2 ) + { + return QSizeF(); + } + bool convertOk = false; + double x = list.at( 0 ).toDouble( &convertOk ); + if ( convertOk ) + { + double y = list.at( 1 ).toDouble( &convertOk ); + if ( convertOk ) + { + if ( ok ) + *ok = true; + return QSizeF( x, y ); + } + } + return QSizeF(); + } + else + { + // can't use decodePoint here -- has no OK handling + const QStringList list = value.toString().trimmed().split( ',' ); + if ( list.count() != 2 ) + return QSizeF(); + bool convertOk = false; + double x = list.at( 0 ).toDouble( &convertOk ); + if ( convertOk ) + { + double y = list.at( 1 ).toDouble( &convertOk ); + if ( convertOk ) + { + if ( ok ) + *ok = true; + return QSizeF( x, y ); + } + } + return QSizeF(); + } +} + QString QgsSymbolLayerUtils::encodeMapUnitScale( const QgsMapUnitScale &mapUnitScale ) { return QStringLiteral( "3x:%1,%2,%3,%4,%5,%6" ).arg( qgsDoubleToString( mapUnitScale.minScale ), diff --git a/src/core/symbology/qgssymbollayerutils.h b/src/core/symbology/qgssymbollayerutils.h index fbf1d6e1ebd..1d4180a1350 100644 --- a/src/core/symbology/qgssymbollayerutils.h +++ b/src/core/symbology/qgssymbollayerutils.h @@ -122,6 +122,20 @@ class CORE_EXPORT QgsSymbolLayerUtils */ static QPointF decodePoint( const QString &string ); + /** + * Converts a \a value to a point. + * + * \param value value to convert + * \param ok if specified, will be set to TRUE if value was successfully converted + * + * \returns converted point + * + * \see decodePoint() + * \see toSize() + * \since QGIS 3.10 + */ + static QPointF toPoint( const QVariant &value, bool *ok SIP_OUT = nullptr ); + /** * Encodes a QSizeF to a string. * \see decodeSize() @@ -138,6 +152,20 @@ class CORE_EXPORT QgsSymbolLayerUtils */ static QSizeF decodeSize( const QString &string ); + /** + * Converts a \a value to a size. + * + * \param value value to convert + * \param ok if specified, will be set to TRUE if value was successfully converted + * + * \returns converted size + * + * \see decodeSize() + * \see toPoint() + * \since QGIS 3.10 + */ + static QSizeF toSize( const QVariant &value, bool *ok SIP_OUT = nullptr ); + static QString encodeMapUnitScale( const QgsMapUnitScale &mapUnitScale ); static QgsMapUnitScale decodeMapUnitScale( const QString &str ); diff --git a/tests/src/python/test_qgssymbollayerutils.py b/tests/src/python/test_qgssymbollayerutils.py index 6a4f3c7424b..6a28615da85 100644 --- a/tests/src/python/test_qgssymbollayerutils.py +++ b/tests/src/python/test_qgssymbollayerutils.py @@ -38,6 +38,51 @@ class PyQgsSymbolLayerUtils(unittest.TestCase): s2 = QgsSymbolLayerUtils.decodeSize('') self.assertEqual(s2, QSizeF(0, 0)) + def testToSize(self): + s2, ok = QgsSymbolLayerUtils.toSize(None) + self.assertFalse(ok) + + s2, ok = QgsSymbolLayerUtils.toSize(4) + self.assertFalse(ok) + + s2, ok = QgsSymbolLayerUtils.toSize('4') + self.assertFalse(ok) + + # arrays + s2, ok = QgsSymbolLayerUtils.toSize([4]) + self.assertFalse(ok) + + s2, ok = QgsSymbolLayerUtils.toSize([]) + self.assertFalse(ok) + + s2, ok = QgsSymbolLayerUtils.toSize([4, 5, 6]) + self.assertFalse(ok) + + s2, ok = QgsSymbolLayerUtils.toSize([4,5]) + self.assertTrue(ok) + self.assertEqual(s2, QSizeF(4,5)) + + s2, ok = QgsSymbolLayerUtils.toSize(['4','5']) + self.assertTrue(ok) + self.assertEqual(s2, QSizeF(4,5)) + + # string values + s = QSizeF() + string = QgsSymbolLayerUtils.encodeSize(s) + s2, ok = QgsSymbolLayerUtils.toSize(string) + self.assertTrue(ok) + self.assertEqual(s2, s) + s = QSizeF(1.5, 2.5) + string = QgsSymbolLayerUtils.encodeSize(s) + s2, ok = QgsSymbolLayerUtils.toSize(string) + self.assertTrue(ok) + self.assertEqual(s2, s) + + # bad string + s2, ok = QgsSymbolLayerUtils.toSize('') + self.assertFalse(ok) + self.assertEqual(s2, QSizeF()) + def testEncodeDecodePoint(self): s = QPointF() string = QgsSymbolLayerUtils.encodePoint(s) @@ -52,6 +97,51 @@ class PyQgsSymbolLayerUtils(unittest.TestCase): s2 = QgsSymbolLayerUtils.decodePoint('') self.assertEqual(s2, QPointF()) + def testToPoint(self): + s2, ok = QgsSymbolLayerUtils.toPoint(None) + self.assertFalse(ok) + + s2, ok = QgsSymbolLayerUtils.toPoint(4) + self.assertFalse(ok) + + s2, ok = QgsSymbolLayerUtils.toPoint('4') + self.assertFalse(ok) + + # arrays + s2, ok = QgsSymbolLayerUtils.toPoint([4]) + self.assertFalse(ok) + + s2, ok = QgsSymbolLayerUtils.toPoint([]) + self.assertFalse(ok) + + s2, ok = QgsSymbolLayerUtils.toPoint([4, 5, 6]) + self.assertFalse(ok) + + s2, ok = QgsSymbolLayerUtils.toPoint([4,5]) + self.assertTrue(ok) + self.assertEqual(s2, QPointF(4,5)) + + s2, ok = QgsSymbolLayerUtils.toPoint(['4','5']) + self.assertTrue(ok) + self.assertEqual(s2, QPointF(4,5)) + + # string values + s = QPointF() + string = QgsSymbolLayerUtils.encodePoint(s) + s2, ok = QgsSymbolLayerUtils.toPoint(string) + self.assertTrue(ok) + self.assertEqual(s2, s) + s = QPointF(1.5, 2.5) + string = QgsSymbolLayerUtils.encodePoint(s) + s2, ok = QgsSymbolLayerUtils.toPoint(string) + self.assertTrue(ok) + self.assertEqual(s2, s) + + # bad string + s2, ok = QgsSymbolLayerUtils.toPoint('') + self.assertFalse(ok) + self.assertEqual(s2, QPointF()) + def testDecodeArrowHeadType(self): type, ok = QgsSymbolLayerUtils.decodeArrowHeadType(0) self.assertTrue(ok)