From 6dc0b6910f3a9d41260ca4c3f81f64585d3be662 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Mon, 15 Feb 2016 14:09:40 +1100 Subject: [PATCH] Move handling of angular units to QgsUnitTypes, swap angle and distance radio buttons to combo boxes in options dialog for consistency with project properties --- python/core/qgsunittypes.sip | 48 +++++ src/app/qgsdisplayangle.cpp | 21 +-- src/app/qgsoptions.cpp | 87 +++------ src/core/qgsunittypes.cpp | 243 ++++++++++++++++++++++++++ src/core/qgsunittypes.h | 49 ++++++ src/ui/qgsoptionsbase.ui | 181 +++++++------------ tests/src/python/test_qgsunittypes.py | 80 +++++++++ 7 files changed, 514 insertions(+), 195 deletions(-) diff --git a/python/core/qgsunittypes.sip b/python/core/qgsunittypes.sip index b5f778e8a36..4774daf4a1a 100644 --- a/python/core/qgsunittypes.sip +++ b/python/core/qgsunittypes.sip @@ -36,6 +36,18 @@ class QgsUnitTypes UnknownAreaUnit, /*!< unknown areal unit */ }; + //! Units of angles + enum AngleUnit + { + AngleDegrees = 0, /*!< degrees */ + Radians, /*!< square kilometers */ + Gon, /*!< gon/gradian */ + MinutesOfArc, /*!< minutes of arc */ + SecondsOfArc, /*!< seconds of arc */ + Turn, /*!< turn/revolutions */ + UnknownAngleUnit, /*!< unknown angle unit */ + }; + /** Returns the type for a distance unit. */ static DistanceUnitType unitType( QGis::UnitType unit ); @@ -120,5 +132,41 @@ class QgsUnitTypes */ static AreaUnit distanceToAreaUnit( QGis::UnitType distanceUnit ); + // ANGULAR UNITS + + /** Encodes an angular unit to a string. + * @param unit unit to encode + * @returns encoded string + * @see decodeAngleUnit() + */ + static QString encodeUnit( AngleUnit unit ); + + /** Decodes an angular unit from a string. + * @param string string to decode + * @param ok optional boolean, will be set to true if string was converted successfully + * @returns decoded units + * @see encodeUnit() + */ + static AngleUnit decodeAngleUnit( const QString& string, bool *ok = 0 ); + + /** Returns a translated string representing an angular unit. + * @param unit unit to convert to string + */ + static QString toString( AngleUnit unit ); + + /** Returns the conversion factor between the specified angular units. + * @param fromUnit angle unit to convert from + * @param toUnit angle unit to convert to + * @returns multiplication factor to convert between units + */ + static double fromUnitToUnitFactor( AngleUnit fromUnit, AngleUnit toUnit ); + + /** Returns an angle formatted as a friendly string. + * @param angle angle to format + * @param decimals number of decimal places to show + * @param unit unit of angle + * @returns formatted angle string + */ + static QString formatAngle( double angle, int decimals, AngleUnit unit ); }; diff --git a/src/app/qgsdisplayangle.cpp b/src/app/qgsdisplayangle.cpp index be54b70bdf9..600aff2f6bc 100644 --- a/src/app/qgsdisplayangle.cpp +++ b/src/app/qgsdisplayangle.cpp @@ -16,6 +16,7 @@ #include "qgsdisplayangle.h" #include "qgsmapcanvas.h" #include "qgslogger.h" +#include "qgsunittypes.h" #include #include @@ -41,23 +42,7 @@ void QgsDisplayAngle::setValueInRadians( double value ) void QgsDisplayAngle::updateUi() { QSettings settings; - QString unitString = settings.value( "/qgis/measure/angleunits", "degrees" ).toString(); + QgsUnitTypes::AngleUnit unit = QgsUnitTypes::decodeAngleUnit( settings.value( "/qgis/measure/angleunits", QgsUnitTypes::encodeUnit( QgsUnitTypes::AngleDegrees ) ).toString() ); int decimals = settings.value( "/qgis/measure/decimalplaces", "3" ).toInt(); - - if ( unitString == "degrees" ) - { - mAngleLineEdit->setText( tr( "%1 degrees" ).arg( QLocale::system().toString( mValue * 180 / M_PI ), - 'f', decimals ) ); - } - else if ( unitString == "radians" ) - { - mAngleLineEdit->setText( tr( "%1 radians" ).arg( QLocale::system().toString( mValue ), - 'f', decimals ) ); - - } - else if ( unitString == "gon" ) - { - mAngleLineEdit->setText( tr( "%1 gon" ).arg( QLocale::system().toString( mValue / M_PI * 200 ), - 'f', decimals ) ); - } + mAngleLineEdit->setText( QgsUnitTypes::formatAngle( mValue * QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::Radians, unit ), decimals, unit ) ); } diff --git a/src/app/qgsoptions.cpp b/src/app/qgsoptions.cpp index 797cf4f5280..678a32e2f18 100644 --- a/src/app/qgsoptions.cpp +++ b/src/app/qgsoptions.cpp @@ -460,27 +460,17 @@ QgsOptions::QgsOptions( QWidget *parent, Qt::WindowFlags fl ) : } // Set the units for measuring - bool ok = false; - QGis::UnitType myDisplayUnits = QgsUnitTypes::decodeDistanceUnit( mSettings->value( "/qgis/measure/displayunits" ).toString(), &ok ); - if ( !ok ) - myDisplayUnits = QGis::Meters; + mDistanceUnitsComboBox->addItem( tr( "Meters" ), QGis::Meters ); + mDistanceUnitsComboBox->addItem( tr( "Feet" ), QGis::Feet ); + mDistanceUnitsComboBox->addItem( tr( "Nautical miles" ), QGis::NauticalMiles ); + mDistanceUnitsComboBox->addItem( tr( "Degrees" ), QGis::Degrees ); + mDistanceUnitsComboBox->addItem( tr( "Map units" ), QGis::UnknownUnit ); - if ( myDisplayUnits == QGis::Feet ) - { - radFeet->setChecked( true ); - } - else if ( myDisplayUnits == QGis::NauticalMiles ) - { - radNautical->setChecked( true ); - } - else if ( myDisplayUnits == QGis::Degrees ) - { - radDegrees->setChecked( true ); - } - else - { - radMeters->setChecked( true ); - } + bool ok = false; + QGis::UnitType distanceUnits = QgsUnitTypes::decodeDistanceUnit( mSettings->value( "/qgis/measure/displayunits" ).toString(), &ok ); + if ( !ok ) + distanceUnits = QGis::Meters; + mDistanceUnitsComboBox->setCurrentIndex( mDistanceUnitsComboBox->findData( distanceUnits ) ); mAreaUnitsComboBox->addItem( tr( "Square meters" ), QgsUnitTypes::SquareMeters ); mAreaUnitsComboBox->addItem( tr( "Square kilometers" ), QgsUnitTypes::SquareKilometers ); @@ -498,24 +488,15 @@ QgsOptions::QgsOptions( QWidget *parent, Qt::WindowFlags fl ) : areaUnits = QgsUnitTypes::SquareMeters; mAreaUnitsComboBox->setCurrentIndex( mAreaUnitsComboBox->findData( areaUnits ) ); - QButtonGroup* angleButtonGroup = new QButtonGroup( this ); - angleButtonGroup->addButton( mDegreesRadioButton ); - angleButtonGroup->addButton( mRadiansRadioButton ); - angleButtonGroup->addButton( mGonRadioButton ); + mAngleUnitsComboBox->addItem( tr( "Degrees" ), QgsUnitTypes::AngleDegrees ); + mAngleUnitsComboBox->addItem( tr( "Radians" ), QgsUnitTypes::Radians ); + mAngleUnitsComboBox->addItem( tr( "Gon/gradians" ), QgsUnitTypes::Gon ); + mAngleUnitsComboBox->addItem( tr( "Minutes of arc" ), QgsUnitTypes::MinutesOfArc ); + mAngleUnitsComboBox->addItem( tr( "Seconds of arc" ), QgsUnitTypes::SecondsOfArc ); + mAngleUnitsComboBox->addItem( tr( "Turns/revolutions" ), QgsUnitTypes::Turn ); - QString myAngleUnitsTxt = mSettings->value( "/qgis/measure/angleunits", "degrees" ).toString(); - if ( myAngleUnitsTxt == "gon" ) - { - mGonRadioButton->setChecked( true ); - } - else if ( myAngleUnitsTxt == "radians" ) - { - mRadiansRadioButton->setChecked( true ); - } - else //degrees - { - mDegreesRadioButton->setChecked( true ); - } + QgsUnitTypes::AngleUnit unit = QgsUnitTypes::decodeAngleUnit( mSettings->value( "/qgis/measure/angleunits", QgsUnitTypes::encodeUnit( QgsUnitTypes::AngleDegrees ) ).toString() ); + mAngleUnitsComboBox->setCurrentIndex( mAngleUnitsComboBox->findData( unit ) ); // set decimal places of the measure tool int decimalPlaces = mSettings->value( "/qgis/measure/decimalplaces", "3" ).toInt(); @@ -1262,36 +1243,16 @@ void QgsOptions::saveOptions() mSettings->setValue( "/Projections/showDatumTransformDialog", chkShowDatumTransformDialog->isChecked() ); - if ( radFeet->isChecked() ) - { - mSettings->setValue( "/qgis/measure/displayunits", QgsUnitTypes::encodeUnit( QGis::Feet ) ); - } - else if ( radNautical->isChecked() ) - { - mSettings->setValue( "/qgis/measure/displayunits", QgsUnitTypes::encodeUnit( QGis::NauticalMiles ) ); - } - else if ( radDegrees->isChecked() ) - { - mSettings->setValue( "/qgis/measure/displayunits", QgsUnitTypes::encodeUnit( QGis::Degrees ) ); - } - else - { - mSettings->setValue( "/qgis/measure/displayunits", QgsUnitTypes::encodeUnit( QGis::Meters ) ); - } + //measurement settings + + QGis::UnitType distanceUnit = static_cast< QGis::UnitType >( mDistanceUnitsComboBox->itemData( mDistanceUnitsComboBox->currentIndex() ).toInt() ); + mSettings->setValue( "/qgis/measure/displayunits", QgsUnitTypes::encodeUnit( distanceUnit ) ); QgsUnitTypes::AreaUnit areaUnit = static_cast< QgsUnitTypes::AreaUnit >( mAreaUnitsComboBox->itemData( mAreaUnitsComboBox->currentIndex() ).toInt() ); mSettings->setValue( "/qgis/measure/areaunits", QgsUnitTypes::encodeUnit( areaUnit ) ); - QString angleUnitString = "degrees"; - if ( mRadiansRadioButton->isChecked() ) - { - angleUnitString = "radians"; - } - else if ( mGonRadioButton->isChecked() ) - { - angleUnitString = "gon"; - } - mSettings->setValue( "/qgis/measure/angleunits", angleUnitString ); + QgsUnitTypes::AngleUnit angleUnit = static_cast< QgsUnitTypes::AngleUnit >( mAngleUnitsComboBox->itemData( mAngleUnitsComboBox->currentIndex() ).toInt() ); + mSettings->setValue( "/qgis/measure/angleunits", QgsUnitTypes::encodeUnit( angleUnit ) ); int decimalPlaces = mDecimalPlacesSpinBox->value(); mSettings->setValue( "/qgis/measure/decimalplaces", decimalPlaces ); diff --git a/src/core/qgsunittypes.cpp b/src/core/qgsunittypes.cpp index 671b5d2fa4e..11c54134820 100644 --- a/src/core/qgsunittypes.cpp +++ b/src/core/qgsunittypes.cpp @@ -16,6 +16,7 @@ #include "qgsunittypes.h" #include +#include /*************************************************************************** * This class is considered CRITICAL and any change MUST be accompanied with @@ -698,6 +699,248 @@ QgsUnitTypes::AreaUnit QgsUnitTypes::distanceToAreaUnit( QGis::UnitType distance return UnknownAreaUnit; } +QString QgsUnitTypes::encodeUnit( QgsUnitTypes::AngleUnit unit ) +{ + switch ( unit ) + { + case AngleDegrees: + return "degrees"; + case Radians: + return "radians"; + case Gon: + return "gon"; + case MinutesOfArc: + return "moa"; + case SecondsOfArc: + return "soa"; + case Turn: + return "tr"; + case UnknownAngleUnit: + return ""; + } + return QString(); +} + +QgsUnitTypes::AngleUnit QgsUnitTypes::decodeAngleUnit( const QString& string, bool* ok ) +{ + QString normalized = string.trimmed().toLower(); + + if ( ok ) + *ok = true; + + if ( normalized == encodeUnit( AngleDegrees ) ) + return AngleDegrees; + if ( normalized == encodeUnit( Radians ) ) + return Radians; + if ( normalized == encodeUnit( Gon ) ) + return Gon; + if ( normalized == encodeUnit( MinutesOfArc ) ) + return MinutesOfArc; + if ( normalized == encodeUnit( SecondsOfArc ) ) + return SecondsOfArc; + if ( normalized == encodeUnit( Turn ) ) + return Turn; + if ( normalized == encodeUnit( UnknownAngleUnit ) ) + return UnknownAngleUnit; + if ( ok ) + *ok = false; + + return UnknownAngleUnit; +} + +QString QgsUnitTypes::toString( QgsUnitTypes::AngleUnit unit ) +{ + switch ( unit ) + { + case AngleDegrees: + return QCoreApplication::translate( "QgsUnitTypes::AngleUnit", "degrees" ); + case Radians: + return QCoreApplication::translate( "QgsUnitTypes::AngleUnit", "radians" ); + case Gon: + return QCoreApplication::translate( "QgsUnitTypes::AngleUnit", "gon" ); + case MinutesOfArc: + return QCoreApplication::translate( "QgsUnitTypes::AngleUnit", "minutes of arc" ); + case SecondsOfArc: + return QCoreApplication::translate( "QgsUnitTypes::AngleUnit", "seconds of arc" ); + case Turn: + return QCoreApplication::translate( "QgsUnitTypes::AngleUnit", "turns" ); + case UnknownAngleUnit: + return QCoreApplication::translate( "QgsUnitTypes::AngleUnit", "" ); + } + return QString(); +} + +double QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::AngleUnit fromUnit, QgsUnitTypes::AngleUnit toUnit ) +{ + // Calculate the conversion factor between the specified units + if ( fromUnit != toUnit ) + { + switch ( fromUnit ) + { + case AngleDegrees: + { + switch ( toUnit ) + { + case AngleDegrees: + return 1.0; + case Radians: + return M_PI / 180.0; + case Gon: + return 400.0 / 360.0; + case MinutesOfArc: + return 60; + case SecondsOfArc: + return 3600; + case Turn: + return 1.0 / 360.0; + case UnknownAngleUnit: + break; + } + break; + } + case Radians: + { + switch ( toUnit ) + { + case AngleDegrees: + return 180.0 / M_PI; + case Radians: + return 1.0; + case Gon: + return 200.0 / M_PI; + case MinutesOfArc: + return 60 * 180.0 / M_PI; + case SecondsOfArc: + return 3600 * 180.0 / M_PI; + case Turn: + return 0.5 / M_PI; + case UnknownAngleUnit: + break; + } + break; + } + case Gon: + { + switch ( toUnit ) + { + case AngleDegrees: + return 360.0 / 400.0; + case Radians: + return M_PI / 200.0; + case Gon: + return 1.0; + case MinutesOfArc: + return 60 * 360.0 / 400.0; + case SecondsOfArc: + return 3600 * 360.0 / 400.0; + case Turn: + return 1.0 / 400.0; + case UnknownAngleUnit: + break; + } + break; + } + case MinutesOfArc: + { + switch ( toUnit ) + { + case AngleDegrees: + return 1 / 60.0; + case Radians: + return M_PI / 180.0 / 60.0; + case Gon: + return 400.0 / 360.0 / 60.0; + case MinutesOfArc: + return 1.0; + case SecondsOfArc: + return 60.0; + case Turn: + return 1.0 / 360.0 / 60.0; + case UnknownAngleUnit: + break; + } + break; + } + case SecondsOfArc: + { + switch ( toUnit ) + { + case AngleDegrees: + return 1 / 3600.0; + case Radians: + return M_PI / 180.0 / 3600.0; + case Gon: + return 400.0 / 360.0 / 3600.0; + case MinutesOfArc: + return 1.0 / 60.0; + case SecondsOfArc: + return 1.0; + case Turn: + return 1.0 / 360.0 / 3600.0; + case UnknownAngleUnit: + break; + } + break; + } + case Turn: + { + switch ( toUnit ) + { + case AngleDegrees: + return 360.0; + case Radians: + return 2 * M_PI; + case Gon: + return 400.0; + case MinutesOfArc: + return 360.0 * 60.0; + case SecondsOfArc: + return 360.0 * 3600.0; + case Turn: + return 1.0; + case UnknownAngleUnit: + break; + } + break; + } + case UnknownAngleUnit: + break; + } + } + return 1.0; +} + +QString QgsUnitTypes::formatAngle( double angle, int decimals, QgsUnitTypes::AngleUnit unit ) +{ + QString unitLabel; + + switch ( unit ) + { + case AngleDegrees: + unitLabel = QObject::trUtf8( "°" ); + break; + case Radians: + unitLabel = QObject::trUtf8( " rad" ); + break; + case Gon: + unitLabel = QObject::trUtf8( " gon" ); + break; + case MinutesOfArc: + unitLabel = QObject::trUtf8( "′" ); + break; + case SecondsOfArc: + unitLabel = QObject::trUtf8( "″" ); + break; + case Turn: + unitLabel = QObject::trUtf8( " tr" ); + break; + case UnknownAngleUnit: + break; + } + + return QLocale::system().toString( angle, 'f', decimals ) + unitLabel; +} + // enable for QGIS 3.0 #if 0 diff --git a/src/core/qgsunittypes.h b/src/core/qgsunittypes.h index 4e20cf239bf..f790d868072 100644 --- a/src/core/qgsunittypes.h +++ b/src/core/qgsunittypes.h @@ -60,6 +60,18 @@ class CORE_EXPORT QgsUnitTypes UnknownAreaUnit, /*!< unknown areal unit */ }; + //! Units of angles + enum AngleUnit + { + AngleDegrees = 0, /*!< degrees */ + Radians, /*!< square kilometers */ + Gon, /*!< gon/gradian */ + MinutesOfArc, /*!< minutes of arc */ + SecondsOfArc, /*!< seconds of arc */ + Turn, /*!< turn/revolutions */ + UnknownAngleUnit, /*!< unknown angle unit */ + }; + // DISTANCE UNITS /** Returns the type for a distance unit. @@ -148,6 +160,43 @@ class CORE_EXPORT QgsUnitTypes */ static AreaUnit distanceToAreaUnit( QGis::UnitType distanceUnit ); + // ANGULAR UNITS + + /** Encodes an angular unit to a string. + * @param unit unit to encode + * @returns encoded string + * @see decodeAngleUnit() + */ + static QString encodeUnit( AngleUnit unit ); + + /** Decodes an angular unit from a string. + * @param string string to decode + * @param ok optional boolean, will be set to true if string was converted successfully + * @returns decoded units + * @see encodeUnit() + */ + static AngleUnit decodeAngleUnit( const QString& string, bool *ok = 0 ); + + /** Returns a translated string representing an angular unit. + * @param unit unit to convert to string + */ + static QString toString( AngleUnit unit ); + + /** Returns the conversion factor between the specified angular units. + * @param fromUnit angle unit to convert from + * @param toUnit angle unit to convert to + * @returns multiplication factor to convert between units + */ + static double fromUnitToUnitFactor( AngleUnit fromUnit, AngleUnit toUnit ); + + /** Returns an angle formatted as a friendly string. + * @param angle angle to format + * @param decimals number of decimal places to show + * @param unit unit of angle + * @returns formatted angle string + */ + static QString formatAngle( double angle, int decimals, AngleUnit unit ); + //TODO QGIS 3.0 - enable and move symbol units here! Otherwise creates circular dependancies... #if 0 // SYMBOL UNITS diff --git a/src/ui/qgsoptionsbase.ui b/src/ui/qgsoptionsbase.ui index 11e352ef196..720578aae50 100644 --- a/src/ui/qgsoptionsbase.ui +++ b/src/ui/qgsoptionsbase.ui @@ -337,7 +337,7 @@ 0 0 - 949 + 723 670 @@ -1010,7 +1010,7 @@ 0 0 - 949 + 663 1057 @@ -1447,7 +1447,7 @@ 0 0 - 949 + 581 691 @@ -1826,7 +1826,7 @@ 0 0 - 949 + 747 802 @@ -2546,8 +2546,8 @@ 0 0 - 965 - 578 + 171 + 258 @@ -2651,8 +2651,8 @@ 0 0 - 965 - 578 + 528 + 327 @@ -2990,7 +2990,7 @@ 0 0 949 - 635 + 645 @@ -3141,45 +3141,84 @@ Measure tool - - + + - Nautical Miles + Preferred distance units - - + + + + + + + + + + If unchecked large numbers will be converted from m. to km. and from ft. to miles + - Degrees + Keep base unit - - + + + + + - Degrees + Decimal places - - + + - Radians + Rubberband color - + + + + Qt::Horizontal + + + + 191 + 20 + + + + + + + + Preferred area units + + + + + + + + + + + Preferred angle units - - + + - + @@ -3204,88 +3243,6 @@ - - - - &Meters - - - - - - - Feet - - - - - - - If unchecked large numbers will be converted from m. to km. and from ft. to miles - - - Keep base unit - - - - - - - Gon - - - - - - - Rubberband color - - - - - - - Preferred measurements units - - - - - - - Qt::Horizontal - - - - 191 - 20 - - - - - - - - Decimal places - - - - - - - - - - - - - - Preferred area units - - - - - - @@ -5376,14 +5333,9 @@ pbnMeasureColor mDecimalPlacesSpinBox mKeepBaseUnitCheckBox - radMeters - radFeet - radNautical - radDegrees + mDistanceUnitsComboBox mAreaUnitsComboBox - mDegreesRadioButton - mRadiansRadioButton - mGonRadioButton + mAngleUnitsComboBox cmbWheelAction spinZoomFactor mListGlobalScales @@ -5463,6 +5415,7 @@ mAddUrlPushButton mRemoveUrlPushButton mAdvancedSettingsEnableButton + cbxCheckVersion diff --git a/tests/src/python/test_qgsunittypes.py b/tests/src/python/test_qgsunittypes.py index d1a18ac562c..58690363501 100644 --- a/tests/src/python/test_qgsunittypes.py +++ b/tests/src/python/test_qgsunittypes.py @@ -247,6 +247,86 @@ class TestQgsUnitTypes(unittest.TestCase): for t in expected.keys(): self.assertEqual(QgsUnitTypes.distanceToAreaUnit(t), expected[t]) + def testEncodeDecodeAngleUnits(self): + """Test encoding and decoding angle units""" + units = [QgsUnitTypes.AngleDegrees, + QgsUnitTypes.Radians, + QgsUnitTypes.Gon, + QgsUnitTypes.MinutesOfArc, + QgsUnitTypes.SecondsOfArc, + QgsUnitTypes.Turn, + QgsUnitTypes.UnknownAngleUnit] + + for u in units: + res, ok = QgsUnitTypes.decodeAngleUnit(QgsUnitTypes.encodeUnit(u)) + assert ok, 'could not decode unit {}'.format(QgsUnitTypes.toString(u)) + self.assertEqual(res, u) + + # Test decoding bad units + res, ok = QgsUnitTypes.decodeAngleUnit('bad') + self.assertFalse(ok) + self.assertEqual(res, QgsUnitTypes.UnknownAngleUnit) + + # Test that string is cleaned before decoding + res, ok = QgsUnitTypes.decodeAngleUnit(' MoA ') + assert ok + self.assertEqual(res, QgsUnitTypes.MinutesOfArc) + + def testAngleToString(self): + """Test converting angle unit to string""" + units = [QgsUnitTypes.AngleDegrees, + QgsUnitTypes.Radians, + QgsUnitTypes.Gon, + QgsUnitTypes.MinutesOfArc, + QgsUnitTypes.SecondsOfArc, + QgsUnitTypes.Turn, + QgsUnitTypes.UnknownAngleUnit] + + dupes = set() + + # can't test result as it may be translated, so make sure it's non-empty and not a duplicate + for u in units: + s = QgsUnitTypes.toString(u) + assert len(s) > 0 + self.assertFalse(s in dupes) + dupes.add(s) + + def testAngleFromUnitToUnitFactor(self): + """Test calculation of conversion factor between angular units""" + + expected = {QgsUnitTypes.AngleDegrees: {QgsUnitTypes.AngleDegrees: 1.0, QgsUnitTypes.Radians: 0.0174533, QgsUnitTypes.Gon: 1.1111111, QgsUnitTypes.MinutesOfArc: 60, QgsUnitTypes.SecondsOfArc: 3600, QgsUnitTypes.Turn: 0.00277777777778}, + QgsUnitTypes.Radians: {QgsUnitTypes.AngleDegrees: 57.2957795, QgsUnitTypes.Radians: 1.0, QgsUnitTypes.Gon: 63.6619772, QgsUnitTypes.MinutesOfArc: 3437.7467708, QgsUnitTypes.SecondsOfArc: 206264.8062471, QgsUnitTypes.Turn: 0.159154943092}, + QgsUnitTypes.Gon: {QgsUnitTypes.AngleDegrees: 0.9000000, QgsUnitTypes.Radians: 0.015707968623450838802, QgsUnitTypes.Gon: 1.0, QgsUnitTypes.MinutesOfArc: 54.0000000, QgsUnitTypes.SecondsOfArc: 3240.0000000, QgsUnitTypes.Turn: 0.0025}, + QgsUnitTypes.MinutesOfArc: {QgsUnitTypes.AngleDegrees: 0.016666672633390722247, QgsUnitTypes.Radians: 0.00029088831280398030638, QgsUnitTypes.Gon: 0.018518525464057963154, QgsUnitTypes.MinutesOfArc: 1.0, QgsUnitTypes.SecondsOfArc: 60.0, QgsUnitTypes.Turn: 4.62962962962963e-05}, + QgsUnitTypes.SecondsOfArc: {QgsUnitTypes.AngleDegrees: 0.00027777787722304257169, QgsUnitTypes.Radians: 4.848138546730629518e-6, QgsUnitTypes.Gon: 0.0003086420910674814405, QgsUnitTypes.MinutesOfArc: 0.016666672633325253783, QgsUnitTypes.SecondsOfArc: 1.0, QgsUnitTypes.Turn: 7.71604938271605e-07}, + QgsUnitTypes.Turn: {QgsUnitTypes.AngleDegrees: 360.0, QgsUnitTypes.Radians: 6.2831853071795, QgsUnitTypes.Gon: 400.0, QgsUnitTypes.MinutesOfArc: 21600, QgsUnitTypes.SecondsOfArc: 1296000, QgsUnitTypes.Turn: 1} + } + + for from_unit in expected.keys(): + for to_unit in expected[from_unit].keys(): + expected_factor = expected[from_unit][to_unit] + res = QgsUnitTypes.fromUnitToUnitFactor(from_unit, to_unit) + self.assertAlmostEqual(res, + expected_factor, + msg='got {:.7f}, expected {:.7f} when converting from {} to {}'.format(res, expected_factor, + QgsUnitTypes.toString(from_unit), + QgsUnitTypes.toString(to_unit))) + #test conversion to unknown units + res = QgsUnitTypes.fromUnitToUnitFactor(from_unit, QgsUnitTypes.UnknownAngleUnit) + self.assertAlmostEqual(res, + 1.0, + msg='got {:.7f}, expected 1.0 when converting from {} to unknown units'.format(res, expected_factor, + QgsUnitTypes.toString(from_unit))) + + def testFormatAngle(self): + """Test formatting angles""" + self.assertEqual(QgsUnitTypes.formatAngle(45, 3, QgsUnitTypes.AngleDegrees), u'45.000°') + self.assertEqual(QgsUnitTypes.formatAngle(1, 2, QgsUnitTypes.Radians), '1.00 rad') + self.assertEqual(QgsUnitTypes.formatAngle(1, 0, QgsUnitTypes.Gon), u'1 gon') + self.assertEqual(QgsUnitTypes.formatAngle(1.11111111, 4, QgsUnitTypes.MinutesOfArc), u'1.1111′') + self.assertEqual(QgsUnitTypes.formatAngle(1.99999999, 2, QgsUnitTypes.SecondsOfArc), u'2.00″') + self.assertEqual(QgsUnitTypes.formatAngle(1, 2, QgsUnitTypes.Turn), u'1.00 tr') + self.assertEqual(QgsUnitTypes.formatAngle(1, 2, QgsUnitTypes.UnknownAngleUnit), u'1.00') if __name__ == "__main__": unittest.main()