From f68b2586b91b92baa7c9a79a09b942b1062f67ee Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Wed, 3 Sep 2014 20:40:24 +1000 Subject: [PATCH] [composer] Improvements to grid annotation string formatting (sponsored by NIWA, New Zealand): - Don't show directional suffix for 0 or 180 latitudes or 180 longitudes - Add padded coordinate modes - Fix precision errors causing minutes/seconds > 60 - Wraparound longitudes to restrict them to the -180 to 180 degree range --- python/core/qgspoint.sip | 14 +- src/app/composer/qgscomposermapwidget.cpp | 69 +++- src/core/composer/qgscomposermap.h | 7 +- src/core/composer/qgscomposermapgrid.cpp | 31 +- src/core/qgspoint.cpp | 190 ++++++++-- src/core/qgspoint.h | 14 +- tests/src/core/testqgspoint.cpp | 413 +++++++++++++++++++++- 7 files changed, 693 insertions(+), 45 deletions(-) diff --git a/python/core/qgspoint.sip b/python/core/qgspoint.sip index d518fd83091..05e2b354b22 100644 --- a/python/core/qgspoint.sip +++ b/python/core/qgspoint.sip @@ -57,16 +57,26 @@ class QgsPoint /** Return a string representation as degrees minutes seconds. * Its up to the calling function to ensure that this point can * be meaningfully represented in this form. + * @param thePrecision number of decimal points to use for seconds + * @param useSuffix set to true to include a direction suffix (eg 'N'), + * set to false to use a "-" prefix for west and south coordinates + * @param padded set to true to force minutes and seconds to use two decimals, + * eg, '05' instead of '5'. * @note added in QGIS 1.4 */ - QString toDegreesMinutesSeconds( int thePrecision ) const; + QString toDegreesMinutesSeconds( int thePrecision, const bool useSuffix = true, const bool padded = false ) const; /** Return a string representation as degrees minutes. * Its up to the calling function to ensure that this point can * be meaningfully represented in this form. + * @param thePrecision number of decimal points to use for minutes + * @param useSuffix set to true to include a direction suffix (eg 'N'), + * set to false to use a "-" prefix for west and south coordinates + * @param padded set to true to force minutes to use two decimals, + * eg, '05' instead of '5'. * @note added in QGIS 1.9 */ - QString toDegreesMinutes( int thePrecision ) const; + QString toDegreesMinutes( int thePrecision, const bool useSuffix = true, const bool padded = false ) const; /*! Return the well known text representation for the point. diff --git a/src/app/composer/qgscomposermapwidget.cpp b/src/app/composer/qgscomposermapwidget.cpp index 41ee4f0570b..bddad6da92d 100644 --- a/src/app/composer/qgscomposermapwidget.cpp +++ b/src/app/composer/qgscomposermapwidget.cpp @@ -66,8 +66,14 @@ QgsComposerMapWidget::QgsComposerMapWidget( QgsComposerMap* composerMap ): QgsCo mGridTypeComboBox->insertItem( 3, tr( "Frame and annotations only" ) ); mAnnotationFormatComboBox->insertItem( 0, tr( "Decimal" ) ); - mAnnotationFormatComboBox->insertItem( 1, tr( "DegreeMinute" ) ); - mAnnotationFormatComboBox->insertItem( 2, tr( "DegreeMinuteSecond" ) ); + mAnnotationFormatComboBox->insertItem( 1, tr( "Decimal with suffix" ) ); + mAnnotationFormatComboBox->insertItem( 2, tr( "Degree, minute" ) ); + mAnnotationFormatComboBox->insertItem( 3, tr( "Degree, minute with suffix" ) ); + mAnnotationFormatComboBox->insertItem( 4, tr( "Degree, minute aligned" ) ); + mAnnotationFormatComboBox->insertItem( 5, tr( "Degree, minute, second" ) ); + mAnnotationFormatComboBox->insertItem( 6, tr( "Degree, minute, second with suffix" ) ); + mAnnotationFormatComboBox->insertItem( 7, tr( "Degree, minute, second aligned" ) ); + mAnnotationFontColorButton->setColorDialogTitle( tr( "Select font color" ) ); mAnnotationFontColorButton->setColorDialogOptions( QColorDialog::ShowAlphaChannel ); @@ -1265,8 +1271,33 @@ void QgsComposerMapWidget::setGridItems( const QgsComposerMapGrid* grid ) mAnnotationFontColorButton->setColor( grid->gridAnnotationFontColor() ); //mAnnotationFormatComboBox - QgsComposerMap::GridAnnotationFormat gf = grid->gridAnnotationFormat(); - mAnnotationFormatComboBox->setCurrentIndex(( int )gf ); + switch ( grid->gridAnnotationFormat() ) + { + case QgsComposerMap::Decimal: + mAnnotationFormatComboBox->setCurrentIndex( 0 ); + break; + case QgsComposerMap::DegreeMinute: + mAnnotationFormatComboBox->setCurrentIndex( 3 ); + break; + case QgsComposerMap::DegreeMinuteSecond: + mAnnotationFormatComboBox->setCurrentIndex( 6 ); + break; + case QgsComposerMap::DecimalWithSuffix: + mAnnotationFormatComboBox->setCurrentIndex( 1 ); + break; + case QgsComposerMap::DegreeMinuteNoSuffix: + mAnnotationFormatComboBox->setCurrentIndex( 2 ); + break; + case QgsComposerMap::DegreeMinutePadded: + mAnnotationFormatComboBox->setCurrentIndex( 4 ); + break; + case QgsComposerMap::DegreeMinuteSecondNoSuffix: + mAnnotationFormatComboBox->setCurrentIndex( 5 ); + break; + case QgsComposerMap::DegreeMinuteSecondPadded: + mAnnotationFormatComboBox->setCurrentIndex( 7 ); + break; + } mDistanceToMapFrameSpinBox->setValue( grid->annotationFrameDistance() ); mCoordinatePrecisionSpinBox->setValue( grid->gridAnnotationPrecision() ); @@ -1851,7 +1882,35 @@ void QgsComposerMapWidget::on_mAnnotationFormatComboBox_currentIndexChanged( int } mComposerMap->beginCommand( tr( "Annotation format changed" ) ); - grid->setGridAnnotationFormat(( QgsComposerMap::GridAnnotationFormat )index ); + + switch ( index ) + { + case 0: + grid->setGridAnnotationFormat( QgsComposerMap::Decimal ); + break; + case 3: + grid->setGridAnnotationFormat( QgsComposerMap::DegreeMinute ); + break; + case 6: + grid->setGridAnnotationFormat( QgsComposerMap::DegreeMinuteSecond ); + break; + case 1: + grid->setGridAnnotationFormat( QgsComposerMap::DecimalWithSuffix ); + break; + case 2: + grid->setGridAnnotationFormat( QgsComposerMap::DegreeMinuteNoSuffix ); + break; + case 4: + grid->setGridAnnotationFormat( QgsComposerMap::DegreeMinutePadded ); + break; + case 5: + grid->setGridAnnotationFormat( QgsComposerMap::DegreeMinuteSecondNoSuffix ); + break; + case 7: + grid->setGridAnnotationFormat( QgsComposerMap::DegreeMinuteSecondPadded ); + break; + } + mComposerMap->updateBoundingRect(); mComposerMap->update(); mComposerMap->endCommand(); diff --git a/src/core/composer/qgscomposermap.h b/src/core/composer/qgscomposermap.h index e8745397964..805bac44f31 100644 --- a/src/core/composer/qgscomposermap.h +++ b/src/core/composer/qgscomposermap.h @@ -90,7 +90,12 @@ class CORE_EXPORT QgsComposerMap : public QgsComposerItem { Decimal = 0, DegreeMinute, - DegreeMinuteSecond + DegreeMinuteSecond, + DecimalWithSuffix, + DegreeMinuteNoSuffix, + DegreeMinutePadded, + DegreeMinuteSecondNoSuffix, + DegreeMinuteSecondPadded }; enum GridFrameStyle diff --git a/src/core/composer/qgscomposermapgrid.cpp b/src/core/composer/qgscomposermapgrid.cpp index e60882b0349..196d9911c15 100644 --- a/src/core/composer/qgscomposermapgrid.cpp +++ b/src/core/composer/qgscomposermapgrid.cpp @@ -1029,6 +1029,19 @@ QString QgsComposerMapGrid::gridAnnotationString( double value, QgsComposerMap:: { return QString::number( value, 'f', mGridAnnotationPrecision ); } + else if ( mGridAnnotationFormat == QgsComposerMap::DecimalWithSuffix ) + { + QString hemisphere; + if ( coord == QgsComposerMap::Longitude ) + { + hemisphere = value < 0 ? QObject::tr( "W" ) : QObject::tr( "E" ); + } + else + { + hemisphere = value < 0 ? QObject::tr( "S" ) : QObject::tr( "N" ); + } + return QString::number( qAbs( value ), 'f', mGridAnnotationPrecision ) + hemisphere; + } QgsPoint p; p.setX( coord == QgsComposerMap::Longitude ? value : 0 ); @@ -1039,10 +1052,26 @@ QString QgsComposerMapGrid::gridAnnotationString( double value, QgsComposerMap:: { annotationString = p.toDegreesMinutes( mGridAnnotationPrecision ); } - else //DegreeMinuteSecond + else if ( mGridAnnotationFormat == QgsComposerMap::DegreeMinuteNoSuffix ) + { + annotationString = p.toDegreesMinutes( mGridAnnotationPrecision, false ); + } + else if ( mGridAnnotationFormat == QgsComposerMap::DegreeMinutePadded ) + { + annotationString = p.toDegreesMinutes( mGridAnnotationPrecision, true, true ); + } + else if ( mGridAnnotationFormat == QgsComposerMap::DegreeMinuteSecond ) { annotationString = p.toDegreesMinutesSeconds( mGridAnnotationPrecision ); } + else if ( mGridAnnotationFormat == QgsComposerMap::DegreeMinuteSecondNoSuffix ) + { + annotationString = p.toDegreesMinutesSeconds( mGridAnnotationPrecision, false ); + } + else if ( mGridAnnotationFormat == QgsComposerMap::DegreeMinuteSecondPadded ) + { + annotationString = p.toDegreesMinutesSeconds( mGridAnnotationPrecision, true, true ); + } QStringList split = annotationString.split( "," ); if ( coord == QgsComposerMap::Longitude ) diff --git a/src/core/qgspoint.cpp b/src/core/qgspoint.cpp index 2330978505b..cf65e5b3b39 100644 --- a/src/core/qgspoint.cpp +++ b/src/core/qgspoint.cpp @@ -134,46 +134,188 @@ QString QgsPoint::toString( int thePrecision ) const return QString( "%1,%2" ).arg( x ).arg( y ); } -QString QgsPoint::toDegreesMinutesSeconds( int thePrecision ) const +QString QgsPoint::toDegreesMinutesSeconds( int thePrecision, const bool useSuffix, const bool padded ) const { - int myDegreesX = int( qAbs( m_x ) ); - float myFloatMinutesX = float(( qAbs( m_x ) - myDegreesX ) * 60 ); + //first, limit longitude to -360 to 360 degree range + double myWrappedX = fmod( m_x, 360.0 ); + //next, wrap around longitudes > 180 or < -180 degrees, so that eg "190E" -> "170W" + if ( myWrappedX > 180.0 ) + { + myWrappedX = myWrappedX - 360.0; + } + else if ( myWrappedX < -180.0 ) + { + myWrappedX = myWrappedX + 360.0; + } + + int myDegreesX = int( qAbs( myWrappedX ) ); + double myFloatMinutesX = double(( qAbs( myWrappedX ) - myDegreesX ) * 60 ); int myIntMinutesX = int( myFloatMinutesX ); - float mySecondsX = float( myFloatMinutesX - myIntMinutesX ) * 60; + double mySecondsX = double( myFloatMinutesX - myIntMinutesX ) * 60; int myDegreesY = int( qAbs( m_y ) ); - float myFloatMinutesY = float(( qAbs( m_y ) - myDegreesY ) * 60 ); + double myFloatMinutesY = double(( qAbs( m_y ) - myDegreesY ) * 60 ); int myIntMinutesY = int( myFloatMinutesY ); - float mySecondsY = float( myFloatMinutesY - myIntMinutesY ) * 60; + double mySecondsY = double( myFloatMinutesY - myIntMinutesY ) * 60; - QString myXHemisphere = m_x < 0 ? QObject::tr( "W" ) : QObject::tr( "E" ); - QString myYHemisphere = m_y < 0 ? QObject::tr( "S" ) : QObject::tr( "N" ); - QString rep = QString::number( myDegreesX ) + QChar( 176 ) + - QString::number( myIntMinutesX ) + QString( "'" ) + - QString::number( mySecondsX, 'f', thePrecision ) + QString( "\"" ) + + //make sure rounding to specified precision doesn't create seconds >= 60 + if ( qRound( mySecondsX * pow( 10, thePrecision ) ) >= 60 * pow( 10, thePrecision ) ) + { + mySecondsX = qMax( mySecondsX - 60, 0.0 ); + myIntMinutesX++; + if ( myIntMinutesX >= 60 ) + { + myIntMinutesX -= 60; + myDegreesX++; + } + } + if ( qRound( mySecondsY * pow( 10, thePrecision ) ) >= 60 * pow( 10, thePrecision ) ) + { + mySecondsY = qMax( mySecondsY - 60, 0.0 ); + myIntMinutesY++; + if ( myIntMinutesY >= 60 ) + { + myIntMinutesY -= 60; + myDegreesY++; + } + } + + QString myXHemisphere; + QString myYHemisphere; + QString myXSign; + QString myYSign; + if ( useSuffix ) + { + myXHemisphere = myWrappedX < 0 ? QObject::tr( "W" ) : QObject::tr( "E" ); + myYHemisphere = m_y < 0 ? QObject::tr( "S" ) : QObject::tr( "N" ); + } + else + { + if ( myWrappedX < 0 ) + { + myXSign = QObject::tr( "-" ); + } + if ( m_y < 0 ) + { + myYSign = QObject::tr( "-" ); + } + } + //check if coordinate is all zeros for the specified precision, and if so, + //remove the sign and hemisphere strings + if ( myDegreesX == 0 && myIntMinutesX == 0 && qRound( mySecondsX * pow( 10, thePrecision ) ) == 0 ) + { + myXSign = QString(); + myXHemisphere = QString(); + } + if ( myDegreesY == 0 && myIntMinutesY == 0 && qRound( mySecondsY * pow( 10, thePrecision ) ) == 0 ) + { + myYSign = QString(); + myYHemisphere = QString(); + } + //also remove directional prefix from 180 degree longitudes + if ( myDegreesX == 180 && myIntMinutesX == 0 && qRound( mySecondsX * pow( 10, thePrecision ) ) == 0 ) + { + myXHemisphere = QString(); + } + //pad minutes with leading digits if required + QString myMinutesX = padded ? QString( "%1" ).arg( myIntMinutesX, 2, 10, QChar( '0' ) ) : QString::number( myIntMinutesX ); + QString myMinutesY = padded ? QString( "%1" ).arg( myIntMinutesY, 2, 10, QChar( '0' ) ) : QString::number( myIntMinutesY ); + //pad seconds with leading digits if required + int digits = 2 + ( thePrecision == 0 ? 0 : 1 + thePrecision ); //1 for decimal place if required + QString myStrSecondsX = padded ? QString( "%1" ).arg( mySecondsX, digits, 'f', thePrecision, QChar( '0' ) ) : QString::number( mySecondsX, 'f', thePrecision ); + QString myStrSecondsY = padded ? QString( "%1" ).arg( mySecondsY, digits, 'f', thePrecision, QChar( '0' ) ) : QString::number( mySecondsY, 'f', thePrecision ); + + QString rep = myXSign + QString::number( myDegreesX ) + QChar( 176 ) + + myMinutesX + QString( "'" ) + + myStrSecondsX + QString( "\"" ) + myXHemisphere + QString( "," ) + - QString::number( myDegreesY ) + QChar( 176 ) + - QString::number( myIntMinutesY ) + QString( "'" ) + - QString::number( mySecondsY, 'f', thePrecision ) + QString( "\"" ) + + myYSign + QString::number( myDegreesY ) + QChar( 176 ) + + myMinutesY + QString( "'" ) + + myStrSecondsY + QString( "\"" ) + myYHemisphere; return rep; } -QString QgsPoint::toDegreesMinutes( int thePrecision ) const +QString QgsPoint::toDegreesMinutes( int thePrecision, const bool useSuffix, const bool padded ) const { - int myDegreesX = int( qAbs( m_x ) ); - float myFloatMinutesX = float(( qAbs( m_x ) - myDegreesX ) * 60 ); + //first, limit longitude to -360 to 360 degree range + double myWrappedX = fmod( m_x, 360.0 ); + //next, wrap around longitudes > 180 or < -180 degrees, so that eg "190E" -> "170W" + if ( myWrappedX > 180.0 ) + { + myWrappedX = myWrappedX - 360.0; + } + else if ( myWrappedX < -180.0 ) + { + myWrappedX = myWrappedX + 360.0; + } + + int myDegreesX = int( qAbs( myWrappedX ) ); + double myFloatMinutesX = double(( qAbs( myWrappedX ) - myDegreesX ) * 60 ); int myDegreesY = int( qAbs( m_y ) ); - float myFloatMinutesY = float(( qAbs( m_y ) - myDegreesY ) * 60 ); + double myFloatMinutesY = double(( qAbs( m_y ) - myDegreesY ) * 60 ); - QString myXHemisphere = m_x < 0 ? QObject::tr( "W" ) : QObject::tr( "E" ); - QString myYHemisphere = m_y < 0 ? QObject::tr( "S" ) : QObject::tr( "N" ); - QString rep = QString::number( myDegreesX ) + QChar( 176 ) + - QString::number( myFloatMinutesX, 'f', thePrecision ) + QString( "'" ) + + //make sure rounding to specified precision doesn't create minutes >= 60 + if ( qRound( myFloatMinutesX * pow( 10, thePrecision ) ) >= 60 * pow( 10, thePrecision ) ) + { + myFloatMinutesX = qMax( myFloatMinutesX - 60, 0.0 ); + myDegreesX++; + } + if ( qRound( myFloatMinutesY * pow( 10, thePrecision ) ) >= 60 * pow( 10, thePrecision ) ) + { + myFloatMinutesY = qMax( myFloatMinutesY - 60, 0.0 ); + myDegreesY++; + } + + QString myXHemisphere; + QString myYHemisphere; + QString myXSign; + QString myYSign; + if ( useSuffix ) + { + myXHemisphere = myWrappedX < 0 ? QObject::tr( "W" ) : QObject::tr( "E" ); + myYHemisphere = m_y < 0 ? QObject::tr( "S" ) : QObject::tr( "N" ); + } + else + { + if ( myWrappedX < 0 ) + { + myXSign = QObject::tr( "-" ); + } + if ( m_y < 0 ) + { + myYSign = QObject::tr( "-" ); + } + } + //check if coordinate is all zeros for the specified precision, and if so, + //remove the sign and hemisphere strings + if ( myDegreesX == 0 && qRound( myFloatMinutesX * pow( 10, thePrecision ) ) == 0 ) + { + myXSign = QString(); + myXHemisphere = QString(); + } + if ( myDegreesY == 0 && qRound( myFloatMinutesY * pow( 10, thePrecision ) ) == 0 ) + { + myYSign = QString(); + myYHemisphere = QString(); + } + //also remove directional prefix from 180 degree longitudes + if ( myDegreesX == 180 && qRound( myFloatMinutesX * pow( 10, thePrecision ) ) == 0 ) + { + myXHemisphere = QString(); + } + + //pad minutes with leading digits if required + int digits = 2 + ( thePrecision == 0 ? 0 : 1 + thePrecision ); //1 for decimal place if required + QString myStrMinutesX = padded ? QString( "%1" ).arg( myFloatMinutesX, digits, 'f', thePrecision, QChar( '0' ) ) : QString::number( myFloatMinutesX, 'f', thePrecision ); + QString myStrMinutesY = padded ? QString( "%1" ).arg( myFloatMinutesY, digits, 'f', thePrecision, QChar( '0' ) ) : QString::number( myFloatMinutesY, 'f', thePrecision ); + + QString rep = myXSign + QString::number( myDegreesX ) + QChar( 176 ) + + myStrMinutesX + QString( "'" ) + myXHemisphere + QString( "," ) + - QString::number( myDegreesY ) + QChar( 176 ) + - QString::number( myFloatMinutesY, 'f', thePrecision ) + QString( "'" ) + + myYSign + QString::number( myDegreesY ) + QChar( 176 ) + + myStrMinutesY + QString( "'" ) + myYHemisphere; return rep; } diff --git a/src/core/qgspoint.h b/src/core/qgspoint.h index ea2bad2866d..6e9dd7e5935 100644 --- a/src/core/qgspoint.h +++ b/src/core/qgspoint.h @@ -129,16 +129,26 @@ class CORE_EXPORT QgsPoint /** Return a string representation as degrees minutes seconds. * Its up to the calling function to ensure that this point can * be meaningfully represented in this form. + * @param thePrecision number of decimal points to use for seconds + * @param useSuffix set to true to include a direction suffix (eg 'N'), + * set to false to use a "-" prefix for west and south coordinates + * @param padded set to true to force minutes and seconds to use two decimals, + * eg, '05' instead of '5'. * @note added in QGIS 1.4 */ - QString toDegreesMinutesSeconds( int thePrecision ) const; + QString toDegreesMinutesSeconds( int thePrecision, const bool useSuffix = true, const bool padded = false ) const; /** Return a string representation as degrees minutes. * Its up to the calling function to ensure that this point can * be meaningfully represented in this form. + * @param thePrecision number of decimal points to use for minutes + * @param useSuffix set to true to include a direction suffix (eg 'N'), + * set to false to use a "-" prefix for west and south coordinates + * @param padded set to true to force minutes to use two decimals, + * eg, '05' instead of '5'. * @note added in QGIS 1.9 */ - QString toDegreesMinutes( int thePrecision ) const; + QString toDegreesMinutes( int thePrecision, const bool useSuffix = true, const bool padded = false ) const; /*! Return the well known text representation for the point. diff --git a/tests/src/core/testqgspoint.cpp b/tests/src/core/testqgspoint.cpp index 8e2296962be..a6900366119 100644 --- a/tests/src/core/testqgspoint.cpp +++ b/tests/src/core/testqgspoint.cpp @@ -38,6 +38,11 @@ class TestQgsPoint: public QObject void cleanup();// will be called after every testfunction. void toString(); void toDegreesMinutesSeconds(); + void toDegreesMinutesSecondsNoSuffix(); + void toDegreesMinutesSecondsPadded(); + void toDegreesMinutes(); + void toDegreesMinutesNoSuffix(); + void toDegreesMinutesPadded(); void wellKnownText(); void sqrDist(); void multiply(); @@ -102,15 +107,17 @@ void TestQgsPoint::toString() mReport += "

" + mPoint2.toString( 2 ) + "

"; mReport += "

" + mPoint3.toString( 2 ) + "

"; mReport += "

" + mPoint4.toString( 2 ) + "

"; - QVERIFY( mPoint1.toString( 2 ) == QString( "20.00,-20.00" ) ); -}; + QCOMPARE( mPoint1.toString( 2 ), QString( "20.00,-20.00" ) ); +} + void TestQgsPoint::toDegreesMinutesSeconds() { - mReport += "

Testing toDegreesMinutesSecods()

"; + mReport += "

Testing toDegreesMinutesSeconds()

"; mReport += "

" + mPoint1.toDegreesMinutesSeconds( 2 ) + "

"; mReport += "

" + mPoint2.toDegreesMinutesSeconds( 2 ) + "

"; mReport += "

" + mPoint3.toDegreesMinutesSeconds( 2 ) + "

"; mReport += "

" + mPoint4.toDegreesMinutesSeconds( 2 ) + "

"; + qDebug() << mPoint4.toDegreesMinutesSeconds( 2 ); QString myControlString = QString( "80" ) + QChar( 176 ) + QString( "0'0.00" ) + @@ -119,26 +126,412 @@ void TestQgsPoint::toDegreesMinutesSeconds() QString( "0'0.00" ) + QString( '"' ) + QString( "N" ); qDebug() << myControlString; - QVERIFY( mPoint4.toDegreesMinutesSeconds( 2 ) == myControlString ); + QCOMPARE( mPoint4.toDegreesMinutesSeconds( 2 ), myControlString ); + + //check if longitudes > 180 or <-180 wrap around + myControlString = QString( "10" ) + QChar( 176 ) + + QString( "0'0.00\"E" ) + + QString( ",0" ) + QChar( 176 ) + + QString( "0'0.00\"" ); + QCOMPARE( QgsPoint( 370, 0 ).toDegreesMinutesSeconds( 2 ), myControlString ); + myControlString = QString( "10" ) + QChar( 176 ) + + QString( "0'0.00\"W" ) + + QString( ",0" ) + QChar( 176 ) + + QString( "0'0.00\"" ); + QCOMPARE( QgsPoint( -370, 0 ).toDegreesMinutesSeconds( 2 ), myControlString ); + myControlString = QString( "179" ) + QChar( 176 ) + + QString( "0'0.00\"W" ) + + QString( ",0" ) + QChar( 176 ) + + QString( "0'0.00\"" ); + QCOMPARE( QgsPoint( 181, 0 ).toDegreesMinutesSeconds( 2 ), myControlString ); + myControlString = QString( "179" ) + QChar( 176 ) + + QString( "0'0.00\"E" ) + + QString( ",0" ) + QChar( 176 ) + + QString( "0'0.00\"" ); + QCOMPARE( QgsPoint( -181, 0 ).toDegreesMinutesSeconds( 2 ), myControlString ); + myControlString = QString( "1" ) + QChar( 176 ) + + QString( "0'0.00\"W" ) + + QString( ",0" ) + QChar( 176 ) + + QString( "0'0.00\"" ); + QCOMPARE( QgsPoint( 359, 0 ).toDegreesMinutesSeconds( 2 ), myControlString ); + myControlString = QString( "1" ) + QChar( 176 ) + + QString( "0'0.00\"E" ) + + QString( ",0" ) + QChar( 176 ) + + QString( "0'0.00\"" ); + QCOMPARE( QgsPoint( -359, 0 ).toDegreesMinutesSeconds( 2 ), myControlString ); + + //should be no directional suffixes for 0 degree coordinates + myControlString = QString( "0" ) + QChar( 176 ) + + QString( "0'0.00" ) + + QString( '"' ) + + QString( ",0" ) + QChar( 176 ) + + QString( "0'0.00" ) + QString( '"' ); + QCOMPARE( QgsPoint( 0, 0 ).toDegreesMinutesSeconds( 2 ), myControlString ); + //should also be no directional suffix for 0 degree coordinates within specified precision + QCOMPARE( QgsPoint( 0, 0.000001 ).toDegreesMinutesSeconds( 2 ), myControlString ); + QCOMPARE( QgsPoint( 0, -0.000001 ).toDegreesMinutesSeconds( 2 ), myControlString ); + QCOMPARE( QgsPoint( -0.000001, 0 ).toDegreesMinutesSeconds( 2 ), myControlString ); + QCOMPARE( QgsPoint( 0.000001, 0 ).toDegreesMinutesSeconds( 2 ), myControlString ); + myControlString = QString( "0" ) + QChar( 176 ) + + QString( "0'0.00000" ) + + QString( '"' ) + + QString( ",0" ) + QChar( 176 ) + + QString( "0'0.00360\"N" ); + QCOMPARE( QgsPoint( 0, 0.000001 ).toDegreesMinutesSeconds( 5 ), myControlString ); + myControlString = QString( "0" ) + QChar( 176 ) + + QString( "0'0.00000" ) + + QString( '"' ) + + QString( ",0" ) + QChar( 176 ) + + QString( "0'0.00360\"S" ); + QCOMPARE( QgsPoint( 0, -0.000001 ).toDegreesMinutesSeconds( 5 ), myControlString ); + myControlString = QString( "0" ) + QChar( 176 ) + + QString( "0'0.00360\"E" ) + + QString( ",0" ) + QChar( 176 ) + + QString( "0'0.00000" ) + QString( '"' ); + QCOMPARE( QgsPoint( 0.000001, 0 ).toDegreesMinutesSeconds( 5 ), myControlString ); + myControlString = QString( "0" ) + QChar( 176 ) + + QString( "0'0.00360\"W" ) + + QString( ",0" ) + QChar( 176 ) + + QString( "0'0.00000" ) + QString( '"' ); + QCOMPARE( QgsPoint( -0.000001, 0 ).toDegreesMinutesSeconds( 5 ), myControlString ); + + //test rounding does not create seconds >= 60 + myControlString = QString( "100" ) + QChar( 176 ) + + QString( "0'0.00\"E" ) + + QString( ",100" ) + QChar( 176 ) + + QString( "0'0.00\"N" ); + QCOMPARE( QgsPoint( 99.999999, 99.999999 ).toDegreesMinutesSeconds( 2 ), myControlString ); + + //should be no directional suffixes for 180 degree longitudes + myControlString = QString( "180" ) + QChar( 176 ) + + QString( "0'0.00" ) + + QString( '"' ) + + QString( ",0" ) + QChar( 176 ) + + QString( "0'0.00" ) + QString( '"' ); + QCOMPARE( QgsPoint( 180, 0 ).toDegreesMinutesSeconds( 2 ), myControlString ); + //should also be no directional suffix for 180 degree longitudes within specified precision + QCOMPARE( QgsPoint( 180.000001, 0 ).toDegreesMinutesSeconds( 2 ), myControlString ); + QCOMPARE( QgsPoint( 179.999999, 0 ).toDegreesMinutesSeconds( 2 ), myControlString ); + myControlString = QString( "179" ) + QChar( 176 ) + + QString( "59'59.99640\"W" ) + + QString( ",0" ) + QChar( 176 ) + + QString( "0'0.00000" ) + QString( '"' ); + QCOMPARE( QgsPoint( 180.000001, 0 ).toDegreesMinutesSeconds( 5 ), myControlString ); + myControlString = QString( "179" ) + QChar( 176 ) + + QString( "59'59.99640\"E" ) + + QString( ",0" ) + QChar( 176 ) + + QString( "0'0.00000" ) + QString( '"' ); + QCOMPARE( QgsPoint( 179.999999, 0 ).toDegreesMinutesSeconds( 5 ), myControlString ); +} + +void TestQgsPoint::toDegreesMinutesSecondsNoSuffix() +{ + QString myControlString = QString( "80" ) + QChar( 176 ) + + QString( "0'0.00" ) + + QString( '"' ) + + QString( ",20" ) + QChar( 176 ) + + QString( "0'0.00" ) + QString( '"' ); + QCOMPARE( mPoint4.toDegreesMinutesSeconds( 2, false ), myControlString ); + + //test 0 lat/long + myControlString = QString( "0" ) + QChar( 176 ) + + QString( "0'0.00" ) + + QString( '"' ) + + QString( ",0" ) + QChar( 176 ) + + QString( "0'0.00" ) + QString( '"' ); + QVERIFY( QgsPoint( 0, 0 ).toDegreesMinutesSeconds( 2, false ) == myControlString ); + //test near zero lat/long + QCOMPARE( QgsPoint( 0, 0.000001 ).toDegreesMinutesSeconds( 2, false ), myControlString ); + QCOMPARE( QgsPoint( 0.000001, 0 ).toDegreesMinutesSeconds( 2, false ), myControlString ); + //should be no "-" prefix for near-zero lat/long when rounding to 2 decimal places + QCOMPARE( QgsPoint( 0, -0.000001 ).toDegreesMinutesSeconds( 2, false ), myControlString ); + QCOMPARE( QgsPoint( -0.000001, 0 ).toDegreesMinutesSeconds( 2, false ), myControlString ); + + myControlString = QString( "0" ) + QChar( 176 ) + + QString( "0'0.00000" ) + + QString( '"' ) + + QString( ",0" ) + QChar( 176 ) + + QString( "0'0.00360" ) + QString( '"' ); + QCOMPARE( QgsPoint( 0, 0.000001 ).toDegreesMinutesSeconds( 5, false ), myControlString ); + myControlString = QString( "0" ) + QChar( 176 ) + + QString( "0'0.00000" ) + + QString( '"' ) + + QString( ",-0" ) + QChar( 176 ) + + QString( "0'0.00360" ) + QString( '"' ); + QCOMPARE( QgsPoint( 0, -0.000001 ).toDegreesMinutesSeconds( 5, false ), myControlString ); + myControlString = QString( "0" ) + QChar( 176 ) + + QString( "0'0.00360" ) + + QString( '"' ) + + QString( ",0" ) + QChar( 176 ) + + QString( "0'0.00000" ) + QString( '"' ); + QCOMPARE( QgsPoint( 0.000001, 0 ).toDegreesMinutesSeconds( 5, false ), myControlString ); + myControlString = QString( "-0" ) + QChar( 176 ) + + QString( "0'0.00360" ) + + QString( '"' ) + + QString( ",0" ) + QChar( 176 ) + + QString( "0'0.00000" ) + QString( '"' ); + QCOMPARE( QgsPoint( -0.000001, 0 ).toDegreesMinutesSeconds( 5, false ), myControlString ); +} + +void TestQgsPoint::toDegreesMinutesSecondsPadded() +{ + QString myControlString = QString( "80" ) + QChar( 176 ) + + QString( "00'00.00" ) + + QString( '"' ) + + QString( "E,20" ) + QChar( 176 ) + + QString( "00'00.00" ) + QString( '"' ) + + QString( "N" ); + qDebug() << myControlString; + QCOMPARE( mPoint4.toDegreesMinutesSeconds( 2, true, true ), myControlString ); + + //should be no directional suffixes for 0 degree coordinates + myControlString = QString( "0" ) + QChar( 176 ) + + QString( "00'00.00" ) + + QString( '"' ) + + QString( ",0" ) + QChar( 176 ) + + QString( "00'00.00" ) + QString( '"' ); + QVERIFY( QgsPoint( 0, 0 ).toDegreesMinutesSeconds( 2, true, true ) == myControlString ); + //should also be no directional suffix for 0 degree coordinates within specified precision + QCOMPARE( QgsPoint( 0, 0.000001 ).toDegreesMinutesSeconds( 2, true, true ), myControlString ); + QCOMPARE( QgsPoint( 0, -0.000001 ).toDegreesMinutesSeconds( 2, true, true ), myControlString ); + QCOMPARE( QgsPoint( -0.000001, 0 ).toDegreesMinutesSeconds( 2, true, true ), myControlString ); + QCOMPARE( QgsPoint( 0.000001, 0 ).toDegreesMinutesSeconds( 2, true, true ), myControlString ); + myControlString = QString( "0" ) + QChar( 176 ) + + QString( "00'00.00000" ) + + QString( '"' ) + + QString( ",0" ) + QChar( 176 ) + + QString( "00'00.00360\"N" ); + QCOMPARE( QgsPoint( 0, 0.000001 ).toDegreesMinutesSeconds( 5, true, true ), myControlString ); + myControlString = QString( "0" ) + QChar( 176 ) + + QString( "00'00.00000" ) + + QString( '"' ) + + QString( ",0" ) + QChar( 176 ) + + QString( "00'00.00360\"S" ); + QCOMPARE( QgsPoint( 0, -0.000001 ).toDegreesMinutesSeconds( 5, true, true ), myControlString ); + myControlString = QString( "0" ) + QChar( 176 ) + + QString( "00'00.00360\"E" ) + + QString( ",0" ) + QChar( 176 ) + + QString( "00'00.00000" ) + QString( '"' ); + QCOMPARE( QgsPoint( 0.000001, 0 ).toDegreesMinutesSeconds( 5, true, true ), myControlString ); + myControlString = QString( "0" ) + QChar( 176 ) + + QString( "00'00.00360\"W" ) + + QString( ",0" ) + QChar( 176 ) + + QString( "00'00.00000" ) + QString( '"' ); + QCOMPARE( QgsPoint( -0.000001, 0 ).toDegreesMinutesSeconds( 5, true, true ), myControlString ); +} + +void TestQgsPoint::toDegreesMinutes() +{ + mReport += "

Testing toDegreesMinutes()

"; + mReport += "

" + mPoint1.toDegreesMinutes( 2 ) + "

"; + mReport += "

" + mPoint2.toDegreesMinutes( 2 ) + "

"; + mReport += "

" + mPoint3.toDegreesMinutes( 2 ) + "

"; + mReport += "

" + mPoint4.toDegreesMinutes( 2 ) + "

"; + + qDebug() << mPoint4.toDegreesMinutes( 2 ); + QString myControlString = QString( "80" ) + QChar( 176 ) + + QString( "0.00'" ) + + QString( "E,20" ) + QChar( 176 ) + + QString( "0.00'N" ); + qDebug() << myControlString; + QCOMPARE( mPoint4.toDegreesMinutes( 2 ), myControlString ); + + //check if longitudes > 180 or <-180 wrap around + myControlString = QString( "10" ) + QChar( 176 ) + + QString( "0.00'E" ) + + QString( ",0" ) + QChar( 176 ) + + QString( "0.00'" ); + QCOMPARE( QgsPoint( 370, 0 ).toDegreesMinutes( 2 ), myControlString ); + myControlString = QString( "10" ) + QChar( 176 ) + + QString( "0.00'W" ) + + QString( ",0" ) + QChar( 176 ) + + QString( "0.00'" ); + QCOMPARE( QgsPoint( -370, 0 ).toDegreesMinutes( 2 ), myControlString ); + myControlString = QString( "179" ) + QChar( 176 ) + + QString( "0.00'W" ) + + QString( ",0" ) + QChar( 176 ) + + QString( "0.00'" ); + QCOMPARE( QgsPoint( 181, 0 ).toDegreesMinutes( 2 ), myControlString ); + myControlString = QString( "179" ) + QChar( 176 ) + + QString( "0.00'E" ) + + QString( ",0" ) + QChar( 176 ) + + QString( "0.00'" ); + QCOMPARE( QgsPoint( -181, 0 ).toDegreesMinutes( 2 ), myControlString ); + myControlString = QString( "1" ) + QChar( 176 ) + + QString( "0.00'W" ) + + QString( ",0" ) + QChar( 176 ) + + QString( "0.00'" ); + QCOMPARE( QgsPoint( 359, 0 ).toDegreesMinutes( 2 ), myControlString ); + myControlString = QString( "1" ) + QChar( 176 ) + + QString( "0.00'E" ) + + QString( ",0" ) + QChar( 176 ) + + QString( "0.00'" ); + QCOMPARE( QgsPoint( -359, 0 ).toDegreesMinutes( 2 ), myControlString ); + + //should be no directional suffixes for 0 degree coordinates + myControlString = QString( "0" ) + QChar( 176 ) + + QString( "0.00'" ) + + QString( ",0" ) + QChar( 176 ) + + QString( "0.00'" ); + QVERIFY( QgsPoint( 0, 0 ).toDegreesMinutes( 2 ) == myControlString ); + //should also be no directional suffix for 0 degree coordinates within specified precision + QCOMPARE( QgsPoint( 0, 0.000001 ).toDegreesMinutes( 2 ), myControlString ); + QCOMPARE( QgsPoint( 0, -0.000001 ).toDegreesMinutes( 2 ), myControlString ); + QCOMPARE( QgsPoint( -0.000001, 0 ).toDegreesMinutes( 2 ), myControlString ); + QCOMPARE( QgsPoint( 0.000001, 0 ).toDegreesMinutes( 2 ), myControlString ); + myControlString = QString( "0" ) + QChar( 176 ) + + QString( "0.00000'" ) + + QString( ",0" ) + QChar( 176 ) + + QString( "0.00006'N" ); + QCOMPARE( QgsPoint( 0, 0.000001 ).toDegreesMinutes( 5 ), myControlString ); + myControlString = QString( "0" ) + QChar( 176 ) + + QString( "0.00000'" ) + + QString( ",0" ) + QChar( 176 ) + + QString( "0.00006'S" ); + QCOMPARE( QgsPoint( 0, -0.000001 ).toDegreesMinutes( 5 ), myControlString ); + myControlString = QString( "0" ) + QChar( 176 ) + + QString( "0.00006'E" ) + + QString( ",0" ) + QChar( 176 ) + + QString( "0.00000'" ); + QCOMPARE( QgsPoint( 0.000001, 0 ).toDegreesMinutes( 5 ), myControlString ); + myControlString = QString( "0" ) + QChar( 176 ) + + QString( "0.00006'W" ) + + QString( ",0" ) + QChar( 176 ) + + QString( "0.00000'" ); + QCOMPARE( QgsPoint( -0.000001, 0 ).toDegreesMinutes( 5 ), myControlString ); + + //test rounding does not create minutes >= 60 + myControlString = QString( "100" ) + QChar( 176 ) + + QString( "0.00'E" ) + + QString( ",100" ) + QChar( 176 ) + + QString( "0.00'N" ); + QCOMPARE( QgsPoint( 99.999999, 99.999999 ).toDegreesMinutes( 2 ), myControlString ); + + //should be no directional suffixes for 180 degree longitudes + myControlString = QString( "180" ) + QChar( 176 ) + + QString( "0.00'" ) + + QString( ",0" ) + QChar( 176 ) + + QString( "0.00'" ); + QCOMPARE( QgsPoint( 180, 0 ).toDegreesMinutes( 2 ), myControlString ); + //should also be no directional suffix for 180 degree longitudes within specified precision + QCOMPARE( QgsPoint( 180.000001, 0 ).toDegreesMinutes( 2 ), myControlString ); + QCOMPARE( QgsPoint( 179.999999, 0 ).toDegreesMinutes( 2 ), myControlString ); + myControlString = QString( "179" ) + QChar( 176 ) + + QString( "59.99994'W" ) + + QString( ",0" ) + QChar( 176 ) + + QString( "0.00000'" ); + QCOMPARE( QgsPoint( 180.000001, 0 ).toDegreesMinutes( 5 ), myControlString ); + myControlString = QString( "179" ) + QChar( 176 ) + + QString( "59.99994'E" ) + + QString( ",0" ) + QChar( 176 ) + + QString( "0.00000'" ); + QCOMPARE( QgsPoint( 179.999999, 0 ).toDegreesMinutes( 5 ), myControlString ); +} + +void TestQgsPoint::toDegreesMinutesNoSuffix() +{ + QString myControlString = QString( "80" ) + QChar( 176 ) + + QString( "0.00'" ) + + QString( ",20" ) + QChar( 176 ) + + QString( "0.00'" ); + QCOMPARE( mPoint4.toDegreesMinutes( 2, false ), myControlString ); + + //test 0 lat/long + myControlString = QString( "0" ) + QChar( 176 ) + + QString( "0.00'" ) + + QString( ",0" ) + QChar( 176 ) + + QString( "0.00'" ); + QVERIFY( QgsPoint( 0, 0 ).toDegreesMinutes( 2, false ) == myControlString ); + //test near zero lat/long + QCOMPARE( QgsPoint( 0, 0.000001 ).toDegreesMinutes( 2, false ), myControlString ); + QCOMPARE( QgsPoint( 0.000001, 0 ).toDegreesMinutes( 2, false ), myControlString ); + //should be no "-" prefix for near-zero lat/long when rounding to 2 decimal places + QCOMPARE( QgsPoint( 0, -0.000001 ).toDegreesMinutes( 2, false ), myControlString ); + QCOMPARE( QgsPoint( -0.000001, 0 ).toDegreesMinutes( 2, false ), myControlString ); + + myControlString = QString( "0" ) + QChar( 176 ) + + QString( "0.00000'" ) + + QString( ",0" ) + QChar( 176 ) + + QString( "0.00006'" ); + QCOMPARE( QgsPoint( 0, 0.000001 ).toDegreesMinutes( 5, false ), myControlString ); + myControlString = QString( "0" ) + QChar( 176 ) + + QString( "0.00000'" ) + + QString( ",-0" ) + QChar( 176 ) + + QString( "0.00006'" ); + QCOMPARE( QgsPoint( 0, -0.000001 ).toDegreesMinutes( 5, false ), myControlString ); + myControlString = QString( "0" ) + QChar( 176 ) + + QString( "0.00006'" ) + + QString( ",0" ) + QChar( 176 ) + + QString( "0.00000'" ); + QCOMPARE( QgsPoint( 0.000001, 0 ).toDegreesMinutes( 5, false ), myControlString ); + myControlString = QString( "-0" ) + QChar( 176 ) + + QString( "0.00006'" ) + + QString( ",0" ) + QChar( 176 ) + + QString( "0.00000'" ); + QCOMPARE( QgsPoint( -0.000001, 0 ).toDegreesMinutes( 5, false ), myControlString ); +} + +void TestQgsPoint::toDegreesMinutesPadded() +{ + QString myControlString = QString( "80" ) + QChar( 176 ) + + QString( "00.00'" ) + + QString( "E,20" ) + QChar( 176 ) + + QString( "00.00'N" ); + qDebug() << myControlString; + QCOMPARE( mPoint4.toDegreesMinutes( 2, true, true ), myControlString ); + + //should be no directional suffixes for 0 degree coordinates + myControlString = QString( "0" ) + QChar( 176 ) + + QString( "00.00'" ) + + QString( ",0" ) + QChar( 176 ) + + QString( "00.00'" ); + QVERIFY( QgsPoint( 0, 0 ).toDegreesMinutes( 2, true, true ) == myControlString ); + //should also be no directional suffix for 0 degree coordinates within specified precision + QCOMPARE( QgsPoint( 0, 0.000001 ).toDegreesMinutes( 2, true, true ), myControlString ); + QCOMPARE( QgsPoint( 0, -0.000001 ).toDegreesMinutes( 2, true, true ), myControlString ); + QCOMPARE( QgsPoint( -0.000001, 0 ).toDegreesMinutes( 2, true, true ), myControlString ); + QCOMPARE( QgsPoint( 0.000001, 0 ).toDegreesMinutes( 2, true, true ), myControlString ); + myControlString = QString( "0" ) + QChar( 176 ) + + QString( "00.00000'" ) + + QString( ",0" ) + QChar( 176 ) + + QString( "00.00006'N" ); + QCOMPARE( QgsPoint( 0, 0.000001 ).toDegreesMinutes( 5, true, true ), myControlString ); + myControlString = QString( "0" ) + QChar( 176 ) + + QString( "00.00000'" ) + + QString( ",0" ) + QChar( 176 ) + + QString( "00.00006'S" ); + QCOMPARE( QgsPoint( 0, -0.000001 ).toDegreesMinutes( 5, true, true ), myControlString ); + myControlString = QString( "0" ) + QChar( 176 ) + + QString( "00.00006'E" ) + + QString( ",0" ) + QChar( 176 ) + + QString( "00.00000'" ); + QCOMPARE( QgsPoint( 0.000001, 0 ).toDegreesMinutes( 5, true, true ), myControlString ); + myControlString = QString( "0" ) + QChar( 176 ) + + QString( "00.00006'W" ) + + QString( ",0" ) + QChar( 176 ) + + QString( "00.00000'" ); + QCOMPARE( QgsPoint( -0.000001, 0 ).toDegreesMinutes( 5, true, true ), myControlString ); +} -}; void TestQgsPoint::wellKnownText() { -}; +} + void TestQgsPoint::sqrDist() { -}; +} + void TestQgsPoint::multiply() { -}; +} + void TestQgsPoint::onSegment() { -}; - +} QTEST_MAIN( TestQgsPoint ) #include "moc_testqgspoint.cxx"