Skip to content

Commit

Permalink
[FEATURE] Add a separate unit choice for milliradians (SI definition)…
Browse files Browse the repository at this point in the history
… vs mil (NATO definition)

Allows angular measurements in either of these units
  • Loading branch information
nyalldawson committed Jul 18, 2019
1 parent 009354a commit 83168d3
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 27 deletions.
3 changes: 2 additions & 1 deletion python/core/auto_generated/qgsunittypes.sip.in
Expand Up @@ -79,7 +79,8 @@ Helper functions for various unit types.
AngleMinutesOfArc,
AngleSecondsOfArc,
AngleTurn,
AngleMil,
AngleMilliradiansSI,
AngleMilNATO,
AngleUnknownUnit,
};

Expand Down
3 changes: 2 additions & 1 deletion src/app/qgsoptions.cpp
Expand Up @@ -535,7 +535,8 @@ QgsOptions::QgsOptions( QWidget *parent, Qt::WindowFlags fl, const QList<QgsOpti
mAngleUnitsComboBox->addItem( tr( "Minutes of arc" ), QgsUnitTypes::AngleMinutesOfArc );
mAngleUnitsComboBox->addItem( tr( "Seconds of arc" ), QgsUnitTypes::AngleSecondsOfArc );
mAngleUnitsComboBox->addItem( tr( "Turns/revolutions" ), QgsUnitTypes::AngleTurn );
mAngleUnitsComboBox->addItem( tr( "Milliradians (mil)" ), QgsUnitTypes::AngleMil );
mAngleUnitsComboBox->addItem( tr( "Milliradians (SI definition)" ), QgsUnitTypes::AngleMilliradiansSI );
mAngleUnitsComboBox->addItem( tr( "Mil (NATO/military definition)" ), QgsUnitTypes::AngleMilNATO );

QgsUnitTypes::AngleUnit unit = QgsUnitTypes::decodeAngleUnit( mSettings->value( QStringLiteral( "/qgis/measure/angleunits" ), QgsUnitTypes::encodeUnit( QgsUnitTypes::AngleDegrees ) ).toString() );
mAngleUnitsComboBox->setCurrentIndex( mAngleUnitsComboBox->findData( unit ) );
Expand Down
77 changes: 63 additions & 14 deletions src/core/qgsunittypes.cpp
Expand Up @@ -1180,7 +1180,9 @@ QString QgsUnitTypes::encodeUnit( QgsUnitTypes::AngleUnit unit )
return QStringLiteral( "soa" );
case AngleTurn:
return QStringLiteral( "tr" );
case AngleMil:
case AngleMilliradiansSI:
return QStringLiteral( "milliradians" );
case AngleMilNATO:
return QStringLiteral( "mil" );
case AngleUnknownUnit:
return QStringLiteral( "<unknown>" );
Expand All @@ -1207,8 +1209,10 @@ QgsUnitTypes::AngleUnit QgsUnitTypes::decodeAngleUnit( const QString &string, bo
return AngleSecondsOfArc;
if ( normalized == encodeUnit( AngleTurn ) )
return AngleTurn;
if ( normalized == encodeUnit( AngleMil ) )
return AngleMil;
if ( normalized == encodeUnit( AngleMilliradiansSI ) )
return AngleMilliradiansSI;
if ( normalized == encodeUnit( AngleMilNATO ) )
return AngleMilNATO;
if ( normalized == encodeUnit( AngleUnknownUnit ) )
return AngleUnknownUnit;
if ( ok )
Expand All @@ -1233,7 +1237,9 @@ QString QgsUnitTypes::toString( QgsUnitTypes::AngleUnit unit )
return QObject::tr( "seconds of arc", "angle" );
case AngleTurn:
return QObject::tr( "turns", "angle" );
case AngleMil:
case AngleMilliradiansSI:
return QObject::tr( "milliradians", "angle" );
case AngleMilNATO:
return QObject::tr( "mil", "angle" );
case AngleUnknownUnit:
return QObject::tr( "<unknown>", "angle" );
Expand Down Expand Up @@ -1262,8 +1268,10 @@ double QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::AngleUnit fromUnit, Qgs
return 3600;
case AngleTurn:
return 1.0 / 360.0;
case AngleMil:
case AngleMilliradiansSI:
return M_PI / 180.0 * 1000;
case AngleMilNATO:
return 3200.0 / 180;
case AngleUnknownUnit:
break;
}
Expand All @@ -1285,8 +1293,10 @@ double QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::AngleUnit fromUnit, Qgs
return 3600 * 180.0 / M_PI;
case AngleTurn:
return 0.5 / M_PI;
case AngleMil:
case AngleMilliradiansSI:
return 1000;
case AngleMilNATO:
return 3200.0 / M_PI;
case AngleUnknownUnit:
break;
}
Expand All @@ -1308,8 +1318,10 @@ double QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::AngleUnit fromUnit, Qgs
return 3600 * 360.0 / 400.0;
case AngleTurn:
return 1.0 / 400.0;
case AngleMil:
case AngleMilliradiansSI:
return M_PI / 200.0 * 1000;
case AngleMilNATO:
return 3200.0 / 200.0;
case AngleUnknownUnit:
break;
}
Expand All @@ -1331,8 +1343,10 @@ double QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::AngleUnit fromUnit, Qgs
return 60.0;
case AngleTurn:
return 1.0 / 360.0 / 60.0;
case AngleMil:
case AngleMilliradiansSI:
return M_PI / 180.0 / 60.0 * 1000;
case AngleMilNATO:
return 3200.0 / 180.0 / 60.0;
case AngleUnknownUnit:
break;
}
Expand All @@ -1354,8 +1368,10 @@ double QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::AngleUnit fromUnit, Qgs
return 1.0;
case AngleTurn:
return 1.0 / 360.0 / 3600.0;
case AngleMil:
case AngleMilliradiansSI:
return M_PI / 180.0 / 3600.0 * 1000;
case AngleMilNATO:
return 3200.0 / 180.0 / 3600.0;
case AngleUnknownUnit:
break;
}
Expand All @@ -1377,14 +1393,16 @@ double QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::AngleUnit fromUnit, Qgs
return 360.0 * 3600.0;
case AngleTurn:
return 1.0;
case AngleMil:
case AngleMilliradiansSI:
return 2 * M_PI * 1000;
case AngleMilNATO:
return 2 * 3200;
case AngleUnknownUnit:
break;
}
break;
}
case AngleMil:
case AngleMilliradiansSI:
{
switch ( toUnit )
{
Expand All @@ -1400,7 +1418,35 @@ double QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::AngleUnit fromUnit, Qgs
return 180.0 * 3600.0 / M_PI / 1000;
case AngleTurn:
return M_PI / 2 / 1000;
case AngleMil:
case AngleMilliradiansSI:
return 1.0;
case AngleMilNATO:
return 3200.0 / 1000.0 / M_PI;
case AngleUnknownUnit:
break;
}
break;
}

case AngleMilNATO:
{
switch ( toUnit )
{
case AngleDegrees:
return 180.0 / 3200;
case AngleRadians:
return M_PI / 3200;
case AngleGon:
return 200.0 / 3200;
case AngleMinutesOfArc:
return 60 * 180.0 / 3200;
case AngleSecondsOfArc:
return 3600.0 * 180 / 3200;
case AngleTurn:
return 1.0 / ( 2 * 32000 );
case AngleMilliradiansSI:
return 1000.0 * M_PI / 3200.0;
case AngleMilNATO:
return 1.0;
case AngleUnknownUnit:
break;
Expand Down Expand Up @@ -1438,8 +1484,11 @@ QString QgsUnitTypes::formatAngle( double angle, int decimals, QgsUnitTypes::Ang
case AngleTurn:
unitLabel = QObject::tr( " tr", "angle turn" );
break;
case AngleMil:
unitLabel = QObject::tr( " mil", "angular mil" );
case AngleMilliradiansSI:
unitLabel = QObject::tr( " millirad", "angular mil SI" );
break;
case AngleMilNATO:
unitLabel = QObject::tr( " mil", "angular mil NATO" );
break;
case AngleUnknownUnit:
break;
Expand Down
3 changes: 2 additions & 1 deletion src/core/qgsunittypes.h
Expand Up @@ -103,7 +103,8 @@ class CORE_EXPORT QgsUnitTypes
AngleMinutesOfArc, //!< Minutes of arc
AngleSecondsOfArc, //!< Seconds of arc
AngleTurn, //!< Turn/revolutions
AngleMil, //!< Angular mil
AngleMilliradiansSI, //!< Angular milliradians (SI definition, 1/1000 of radian)
AngleMilNATO, //!< Angular mil (NATO definition, 6400 mil = 2PI radians)
AngleUnknownUnit, //!< Unknown angle unit
};
Q_ENUM( AngleUnit )
Expand Down
31 changes: 21 additions & 10 deletions tests/src/python/test_qgsunittypes.py
Expand Up @@ -558,7 +558,8 @@ def testEncodeDecodeAngleUnits(self):
QgsUnitTypes.AngleMinutesOfArc,
QgsUnitTypes.AngleSecondsOfArc,
QgsUnitTypes.AngleTurn,
QgsUnitTypes.AngleMil,
QgsUnitTypes.AngleMilliradiansSI,
QgsUnitTypes.AngleMilNATO,
QgsUnitTypes.AngleUnknownUnit]

for u in units:
Expand All @@ -584,7 +585,8 @@ def testAngleToString(self):
QgsUnitTypes.AngleMinutesOfArc,
QgsUnitTypes.AngleSecondsOfArc,
QgsUnitTypes.AngleTurn,
QgsUnitTypes.AngleMil,
QgsUnitTypes.AngleMilliradiansSI,
QgsUnitTypes.AngleMilNATO,
QgsUnitTypes.AngleUnknownUnit]

dupes = set()
Expand All @@ -599,13 +601,21 @@ def testAngleToString(self):
def testAngleFromUnitToUnitFactor(self):
"""Test calculation of conversion factor between angular units"""

expected = {QgsUnitTypes.AngleDegrees: {QgsUnitTypes.AngleDegrees: 1.0, QgsUnitTypes.AngleRadians: 0.0174533, QgsUnitTypes.AngleGon: 1.1111111, QgsUnitTypes.AngleMinutesOfArc: 60, QgsUnitTypes.AngleSecondsOfArc: 3600, QgsUnitTypes.AngleTurn: 0.00277777777778, QgsUnitTypes.AngleMil: 17.453292519943297},
QgsUnitTypes.AngleRadians: {QgsUnitTypes.AngleDegrees: 57.2957795, QgsUnitTypes.AngleRadians: 1.0, QgsUnitTypes.AngleGon: 63.6619772, QgsUnitTypes.AngleMinutesOfArc: 3437.7467708, QgsUnitTypes.AngleSecondsOfArc: 206264.8062471, QgsUnitTypes.AngleTurn: 0.159154943092, QgsUnitTypes.AngleMil: 1000.0},
QgsUnitTypes.AngleGon: {QgsUnitTypes.AngleDegrees: 0.9000000, QgsUnitTypes.AngleRadians: 0.015707968623450838802, QgsUnitTypes.AngleGon: 1.0, QgsUnitTypes.AngleMinutesOfArc: 54.0000000, QgsUnitTypes.AngleSecondsOfArc: 3240.0000000, QgsUnitTypes.AngleTurn: 0.0025, QgsUnitTypes.AngleMil: 15.707963267948967},
QgsUnitTypes.AngleMinutesOfArc: {QgsUnitTypes.AngleDegrees: 0.016666672633390722247, QgsUnitTypes.AngleRadians: 0.00029088831280398030638, QgsUnitTypes.AngleGon: 0.018518525464057963154, QgsUnitTypes.AngleMinutesOfArc: 1.0, QgsUnitTypes.AngleSecondsOfArc: 60.0, QgsUnitTypes.AngleTurn: 4.62962962962963e-05, QgsUnitTypes.AngleMil: 0.29088820866572157},
QgsUnitTypes.AngleSecondsOfArc: {QgsUnitTypes.AngleDegrees: 0.00027777787722304257169, QgsUnitTypes.AngleRadians: 4.848138546730629518e-6, QgsUnitTypes.AngleGon: 0.0003086420910674814405, QgsUnitTypes.AngleMinutesOfArc: 0.016666672633325253783, QgsUnitTypes.AngleSecondsOfArc: 1.0, QgsUnitTypes.AngleTurn: 7.71604938271605e-07, QgsUnitTypes.AngleMil: 0.0048481482527009582897},
QgsUnitTypes.AngleTurn: {QgsUnitTypes.AngleDegrees: 360.0, QgsUnitTypes.AngleRadians: 6.2831853071795, QgsUnitTypes.AngleGon: 400.0, QgsUnitTypes.AngleMinutesOfArc: 21600, QgsUnitTypes.AngleSecondsOfArc: 1296000, QgsUnitTypes.AngleTurn: 1, QgsUnitTypes.AngleMil: 6283.185307179586},
QgsUnitTypes.AngleMil: {QgsUnitTypes.AngleDegrees: 0.057295779513082325, QgsUnitTypes.AngleRadians: 0.001, QgsUnitTypes.AngleGon: 0.06366197723675814, QgsUnitTypes.AngleMinutesOfArc: 3.4377467707849396, QgsUnitTypes.AngleSecondsOfArc: 206.26480624709637, QgsUnitTypes.AngleTurn: 0.0015707963267948967, QgsUnitTypes.AngleMil: 1.0}
expected = {QgsUnitTypes.AngleDegrees: {QgsUnitTypes.AngleDegrees: 1.0, QgsUnitTypes.AngleRadians: 0.0174533, QgsUnitTypes.AngleGon: 1.1111111, QgsUnitTypes.AngleMinutesOfArc: 60, QgsUnitTypes.AngleSecondsOfArc: 3600, QgsUnitTypes.AngleTurn: 0.00277777777778, QgsUnitTypes.AngleMilliradiansSI: 17.453292519943297, QgsUnitTypes.AngleMilNATO: 17.77777777777778},
QgsUnitTypes.AngleRadians: {QgsUnitTypes.AngleDegrees: 57.2957795, QgsUnitTypes.AngleRadians: 1.0, QgsUnitTypes.AngleGon: 63.6619772, QgsUnitTypes.AngleMinutesOfArc: 3437.7467708, QgsUnitTypes.AngleSecondsOfArc: 206264.8062471, QgsUnitTypes.AngleTurn: 0.159154943092, QgsUnitTypes.AngleMilliradiansSI: 1000.0, QgsUnitTypes.AngleMilNATO: 1018.5916357881301},
QgsUnitTypes.AngleGon: {QgsUnitTypes.AngleDegrees: 0.9000000, QgsUnitTypes.AngleRadians: 0.015707968623450838802, QgsUnitTypes.AngleGon: 1.0, QgsUnitTypes.AngleMinutesOfArc: 54.0000000, QgsUnitTypes.AngleSecondsOfArc: 3240.0000000, QgsUnitTypes.AngleTurn: 0.0025, QgsUnitTypes.AngleMilliradiansSI: 15.707963267948967, QgsUnitTypes.AngleMilNATO: 16},
QgsUnitTypes.AngleMinutesOfArc: {QgsUnitTypes.AngleDegrees: 0.016666672633390722247, QgsUnitTypes.AngleRadians: 0.00029088831280398030638, QgsUnitTypes.AngleGon: 0.018518525464057963154, QgsUnitTypes.AngleMinutesOfArc: 1.0, QgsUnitTypes.AngleSecondsOfArc: 60.0, QgsUnitTypes.AngleTurn: 4.62962962962963e-05, QgsUnitTypes.AngleMilliradiansSI: 0.29088820866572157, QgsUnitTypes.AngleMilNATO: 0.29629629629629634},
QgsUnitTypes.AngleSecondsOfArc: {QgsUnitTypes.AngleDegrees: 0.00027777787722304257169, QgsUnitTypes.AngleRadians: 4.848138546730629518e-6, QgsUnitTypes.AngleGon: 0.0003086420910674814405, QgsUnitTypes.AngleMinutesOfArc: 0.016666672633325253783, QgsUnitTypes.AngleSecondsOfArc: 1.0, QgsUnitTypes.AngleTurn: 7.71604938271605e-07, QgsUnitTypes.AngleMilliradiansSI: 0.0048481482527009582897, QgsUnitTypes.AngleMilNATO: 0.0049382716049382715},
QgsUnitTypes.AngleTurn: {QgsUnitTypes.AngleDegrees: 360.0, QgsUnitTypes.AngleRadians: 6.2831853071795, QgsUnitTypes.AngleGon: 400.0, QgsUnitTypes.AngleMinutesOfArc: 21600, QgsUnitTypes.AngleSecondsOfArc: 1296000, QgsUnitTypes.AngleTurn: 1, QgsUnitTypes.AngleMilliradiansSI: 6283.185307179586, QgsUnitTypes.AngleMilNATO: 6400},
QgsUnitTypes.AngleMilliradiansSI: {QgsUnitTypes.AngleDegrees: 0.057295779513082325, QgsUnitTypes.AngleRadians: 0.001, QgsUnitTypes.AngleGon: 0.06366197723675814, QgsUnitTypes.AngleMinutesOfArc: 3.4377467707849396, QgsUnitTypes.AngleSecondsOfArc: 206.26480624709637, QgsUnitTypes.AngleTurn: 0.0015707963267948967, QgsUnitTypes.AngleMilliradiansSI: 1.0, QgsUnitTypes.AngleMilNATO: 1.0185916357881302},
QgsUnitTypes.AngleMilNATO: {QgsUnitTypes.AngleDegrees: 0.05625,
QgsUnitTypes.AngleRadians: 0.0009817477042468104,
QgsUnitTypes.AngleGon: 0.0625,
QgsUnitTypes.AngleMinutesOfArc: 3.375,
QgsUnitTypes.AngleSecondsOfArc: 202.5,
QgsUnitTypes.AngleTurn: 0.000015625,
QgsUnitTypes.AngleMilliradiansSI: 0.9817477042468102,
QgsUnitTypes.AngleMilNATO: 1.0}
}

for from_unit in list(expected.keys()):
Expand All @@ -632,7 +642,8 @@ def testFormatAngle(self):
self.assertEqual(QgsUnitTypes.formatAngle(1.11111111, 4, QgsUnitTypes.AngleMinutesOfArc), '1.1111′')
self.assertEqual(QgsUnitTypes.formatAngle(1.99999999, 2, QgsUnitTypes.AngleSecondsOfArc), '2.00″')
self.assertEqual(QgsUnitTypes.formatAngle(1, 2, QgsUnitTypes.AngleTurn), '1.00 tr')
self.assertEqual(QgsUnitTypes.formatAngle(1, 2, QgsUnitTypes.AngleMil), '1.00 mil')
self.assertEqual(QgsUnitTypes.formatAngle(1, 2, QgsUnitTypes.AngleMilliradiansSI), '1.00 millirad')
self.assertEqual(QgsUnitTypes.formatAngle(1, 2, QgsUnitTypes.AngleMilNATO), '1.00 mil')
self.assertEqual(QgsUnitTypes.formatAngle(1, 2, QgsUnitTypes.AngleUnknownUnit), '1.00')

def testEncodeDecodeLayoutUnits(self):
Expand Down

0 comments on commit 83168d3

Please sign in to comment.