File indexing completed on 2024-04-14 03:48:46

0001 // SPDX-License-Identifier: LGPL-2.1-or-later
0002 
0003 // SPDX-FileCopyrightText: 2010 Matias Kallio <matias.kallio@gmail.com>
0004 // SPDX-FileCopyrightText: 2011 Friedrich W. H. Kossebau <kossebau@kde.org>
0005 // SPDX-FileCopyrightText: 2012 Bernhard Beschow <bbeschow@cs.tu-berlin.de>
0006 
0007 #include "MarbleGlobal.h"
0008 #include "MarbleWidget.h"
0009 #include "GeoDataCoordinates.h"
0010 #include "TestUtils.h"
0011 
0012 #include <QLocale>
0013 #include <QDebug>
0014 #include <QTranslator>
0015 #include <QTemporaryFile>
0016 
0017 using namespace Marble;
0018 
0019 
0020 class TestGeoDataCoordinates : public QObject
0021 {
0022     Q_OBJECT
0023 
0024 private Q_SLOTS:
0025     void initTestCase();
0026 
0027     void testConstruction();
0028     void testSet_Degree();
0029     void testSet_Radian();
0030     void testSetLongitude_Degree();
0031     void testSetLongitude_Radian();
0032     void testSetLatitude_Degree();
0033     void testSetLatitude_Radian();
0034     void testAltitude();
0035     void testOperatorAssignment();
0036     void testDetail();
0037     void testIsPole_data();
0038     void testIsPole();
0039     void testNotation();
0040     void testNormalizeLat_data();
0041     void testNormalizeLat();
0042     void testNormalizeLon_data();
0043     void testNormalizeLon();
0044     void testNormalizeDegree_data();
0045     void testNormalizeDegree();
0046     void testNormalizeRadian_data();
0047     void testNormalizeRadian();
0048     void testFromStringDMS_data();
0049     void testFromStringDMS();
0050     void testFromStringDM_data();
0051     void testFromStringDM();
0052     void testFromStringD_data();
0053     void testFromStringD();
0054     void testFromLocaleString_data();
0055     void testFromLocaleString();
0056     void testToString_Decimal_data();
0057     void testToString_Decimal();
0058     void testToString_DMS_data();
0059     void testToString_DMS();
0060     void testToString_DM_data();
0061     void testToString_DM();
0062     void testPack_data();
0063     void testPack();
0064     void testUTM_data();
0065     void testUTM();
0066 };
0067 
0068 void TestGeoDataCoordinates::initTestCase()
0069 {
0070     QLocale::setDefault( QLocale::c() ); // needed for testing toString* conversions
0071 
0072     QTime time = QTime::currentTime();
0073     qsrand((uint)time.msec());
0074 }
0075 
0076 /*
0077  * test constructors
0078  */
0079 void TestGeoDataCoordinates::testConstruction()
0080 {
0081     GeoDataCoordinates invalid1;
0082 
0083     QVERIFY(!invalid1.isValid());
0084 
0085     GeoDataCoordinates invalid2(invalid1);
0086 
0087     QVERIFY(!invalid2.isValid());
0088     QVERIFY(!invalid1.isValid());
0089     QCOMPARE(invalid1, invalid2);
0090 
0091     const qreal lon = 164.77;
0092     const qreal lat = 55.9;
0093     const qreal alt = 400.003;
0094 
0095     GeoDataCoordinates coordinates3(lon, lat, alt, GeoDataCoordinates::Degree);
0096 
0097     QVERIFY(coordinates3.isValid());
0098     QCOMPARE(coordinates3, GeoDataCoordinates(lon, lat, alt, GeoDataCoordinates::Degree));
0099     QVERIFY(coordinates3 != invalid1);
0100     QVERIFY(coordinates3 != invalid2);
0101 
0102     QCOMPARE(coordinates3.longitude(GeoDataCoordinates::Degree), lon);
0103     QCOMPARE(coordinates3.longitude(), lon*DEG2RAD);
0104 
0105     QCOMPARE(coordinates3.latitude(GeoDataCoordinates::Degree), lat);
0106     QCOMPARE(coordinates3.latitude(), lat*DEG2RAD);
0107 
0108     QCOMPARE(coordinates3.altitude(), alt);
0109 
0110     qreal myLongitude = 0;
0111     qreal myLatitude = 0;
0112 
0113     coordinates3.geoCoordinates(myLongitude, myLatitude, GeoDataCoordinates::Degree);
0114 
0115     QCOMPARE(myLongitude, lon);
0116     QCOMPARE(myLatitude, lat);
0117 
0118     myLongitude = 0;
0119     myLatitude = 0;
0120 
0121     coordinates3.geoCoordinates(myLongitude, myLatitude);
0122 
0123     QCOMPARE(myLongitude, lon*DEG2RAD);
0124     QCOMPARE(myLatitude, lat*DEG2RAD);
0125 
0126     GeoDataCoordinates coordinates4(lon*DEG2RAD, lat*DEG2RAD, alt);
0127 
0128     QVERIFY(coordinates4.isValid());
0129     QCOMPARE(coordinates4, GeoDataCoordinates(lon, lat, alt, GeoDataCoordinates::Degree));
0130     QCOMPARE(coordinates4, coordinates3);
0131     QVERIFY(coordinates4 != invalid1);
0132     QVERIFY(coordinates4 != invalid2);
0133 
0134     GeoDataCoordinates coordinates5(coordinates3);
0135 
0136     QVERIFY(coordinates5.isValid());
0137     QCOMPARE(coordinates5, GeoDataCoordinates(lon, lat, alt, GeoDataCoordinates::Degree));
0138     QCOMPARE(coordinates5, coordinates3);
0139     QCOMPARE(coordinates5, coordinates4);
0140     QVERIFY(coordinates5 != invalid1);
0141     QVERIFY(coordinates5 != invalid2);
0142 
0143     GeoDataCoordinates coordinates6(invalid1.longitude(), invalid1.latitude(), invalid1.altitude(), GeoDataCoordinates::Radian, invalid1.detail());
0144 
0145     QVERIFY(coordinates6.isValid());  // it should be valid, even though
0146     QCOMPARE(coordinates6, invalid1); // it is equal to an invalid one
0147 }
0148 
0149 /*
0150  * test setting coordinates in degree
0151  */
0152 void TestGeoDataCoordinates::testSet_Degree()
0153 {
0154     const qreal lon = 345.8;
0155     const qreal lat = 70.3;
0156     const qreal alt = 1000.9;
0157 
0158     GeoDataCoordinates coordinates1; // invalid
0159     coordinates1.set(lon, lat, alt, GeoDataCoordinates::Degree);
0160 
0161     QVERIFY(coordinates1.isValid());
0162 
0163     GeoDataCoordinates coordinates2(coordinates1);
0164     coordinates2.set(0, 0, 0, GeoDataCoordinates::Degree);
0165 
0166     QVERIFY(coordinates2.isValid());
0167     QCOMPARE(coordinates1, GeoDataCoordinates(lon, lat, alt, GeoDataCoordinates::Degree));
0168     QCOMPARE(coordinates2, GeoDataCoordinates(0, 0, 0, GeoDataCoordinates::Degree));
0169 
0170 }
0171 
0172 /*
0173  * test setting coordinates in radian
0174  */
0175 void TestGeoDataCoordinates::testSet_Radian()
0176 {
0177     const qreal lon = 1.3;
0178     const qreal lat = 0.7;
0179     const qreal alt = 6886.44;
0180 
0181     GeoDataCoordinates coordinates1; // invalid
0182     coordinates1.set(lon, lat, alt);
0183 
0184     QVERIFY(coordinates1.isValid());
0185 
0186     GeoDataCoordinates coordinates2(coordinates1);
0187     coordinates2.set(0, 0, 0);
0188 
0189     QVERIFY(coordinates2.isValid());
0190     QCOMPARE(coordinates1, GeoDataCoordinates(lon, lat, alt));
0191     QCOMPARE(coordinates2, GeoDataCoordinates(0, 0, 0));
0192 }
0193 
0194 /*
0195  * test setLongitude() in degree
0196  */
0197 void TestGeoDataCoordinates::testSetLongitude_Degree()
0198 {
0199     const qreal lon = 143.8;
0200 
0201     GeoDataCoordinates coordinates1; // invalid
0202     coordinates1.setLongitude(lon, GeoDataCoordinates::Degree);
0203 
0204     QVERIFY(coordinates1.isValid());
0205 
0206     GeoDataCoordinates coordinates2(coordinates1);
0207     coordinates2.setLongitude(0, GeoDataCoordinates::Degree);
0208 
0209     QVERIFY(coordinates2.isValid());
0210     QCOMPARE(coordinates1, GeoDataCoordinates(lon, 0, 0, GeoDataCoordinates::Degree));
0211     QCOMPARE(coordinates2, GeoDataCoordinates(0, 0, 0, GeoDataCoordinates::Degree));
0212 }
0213 
0214 /*
0215  * test setLongitude() in radian
0216  */
0217 void TestGeoDataCoordinates::testSetLongitude_Radian()
0218 {
0219     const qreal lon = 2.5;
0220 
0221     GeoDataCoordinates coordinates1; // invalid
0222     coordinates1.setLongitude(lon);
0223 
0224     QVERIFY(coordinates1.isValid());
0225 
0226     GeoDataCoordinates coordinates2(coordinates1);
0227     coordinates2.setLongitude(0);
0228 
0229     QVERIFY(coordinates2.isValid());
0230     QCOMPARE(coordinates1, GeoDataCoordinates(lon, 0));
0231     QCOMPARE(coordinates2, GeoDataCoordinates(0, 0));
0232 }
0233 
0234 /*
0235  * test setLatitude() and latitude() in degree
0236  */
0237 void TestGeoDataCoordinates::testSetLatitude_Degree()
0238 {
0239     const qreal lat = 75.0;
0240 
0241     GeoDataCoordinates coordinates1; // invalid
0242     coordinates1.setLatitude(lat, GeoDataCoordinates::Degree);
0243 
0244     QVERIFY(coordinates1.isValid());
0245 
0246     GeoDataCoordinates coordinates2(coordinates1);
0247     coordinates2.setLatitude(0, GeoDataCoordinates::Degree);
0248 
0249     QVERIFY(coordinates2.isValid());
0250     QCOMPARE(coordinates1, GeoDataCoordinates(0, lat, 0, GeoDataCoordinates::Degree));
0251     QCOMPARE(coordinates2, GeoDataCoordinates(0, 0, 0, GeoDataCoordinates::Degree));
0252 }
0253 
0254 /*
0255  * test setLatitude() in radian
0256  */
0257 void TestGeoDataCoordinates::testSetLatitude_Radian()
0258 {
0259     const qreal lat = 1.2;
0260 
0261     GeoDataCoordinates coordinates1; // invalid
0262     coordinates1.setLatitude(lat);
0263 
0264     QVERIFY(coordinates1.isValid());
0265 
0266     GeoDataCoordinates coordinates2(coordinates1);
0267     coordinates2.setLatitude(0);
0268 
0269     QVERIFY(coordinates2.isValid());
0270     QCOMPARE(coordinates1, GeoDataCoordinates(0, lat));
0271     QCOMPARE(coordinates2, GeoDataCoordinates(0, 0));
0272 }
0273 
0274 /*
0275  * test setAltitude()
0276  */
0277 void TestGeoDataCoordinates::testAltitude()
0278 {
0279     const qreal alt = 400;
0280 
0281     GeoDataCoordinates coordinates1; // invalid
0282     coordinates1.setAltitude(alt);
0283 
0284     QVERIFY(coordinates1.isValid());
0285 
0286     GeoDataCoordinates coordinates2(coordinates1);
0287     coordinates2.setAltitude(0);
0288 
0289     QVERIFY(coordinates2.isValid());
0290     QCOMPARE(coordinates1, GeoDataCoordinates(0, 0, alt));
0291     QCOMPARE(coordinates2, GeoDataCoordinates(0, 0, 0));
0292 }
0293 
0294 void TestGeoDataCoordinates::testOperatorAssignment()
0295 {
0296     const qreal lon = 123.4;
0297     const qreal lat = 56.7;
0298     const qreal alt = 890.1;
0299 
0300     const GeoDataCoordinates coordinates1(lon, lat, alt, GeoDataCoordinates::Degree);
0301     const GeoDataCoordinates coordinates2(0, 0, 0);
0302 
0303     GeoDataCoordinates coordinates3; // invalid
0304     coordinates3 = coordinates1;
0305 
0306     QVERIFY(coordinates3.isValid());
0307     QCOMPARE(coordinates1, GeoDataCoordinates(lon, lat, alt, GeoDataCoordinates::Degree)); // stays unmodified
0308     QCOMPARE(coordinates3, coordinates1);
0309 
0310     coordinates3 = GeoDataCoordinates();
0311 
0312     QVERIFY(!coordinates3.isValid());
0313 
0314     GeoDataCoordinates coordinates4(coordinates1);
0315     coordinates4 = coordinates2;
0316 
0317     QVERIFY(coordinates4.isValid());
0318     QCOMPARE(coordinates1, GeoDataCoordinates(lon, lat, alt, GeoDataCoordinates::Degree)); // stays unmodified
0319     QCOMPARE(coordinates2, GeoDataCoordinates(0, 0, 0)); // stays unmodified
0320     QCOMPARE(coordinates4, coordinates2);
0321 }
0322 
0323 /*
0324  * test setDetail() and detail()
0325  */
0326 void TestGeoDataCoordinates::testDetail()
0327 {
0328     const quint8 detailnumber = 15;
0329 
0330     GeoDataCoordinates coordinates1;
0331     coordinates1.setDetail(detailnumber);
0332 
0333     GeoDataCoordinates coordinates2(coordinates1);
0334     coordinates2.setDetail(0);
0335 
0336     QCOMPARE(coordinates1.detail(), detailnumber);
0337 }
0338 
0339 /*
0340  * test setDefaultNotation() and defaultNotation
0341  */
0342 void TestGeoDataCoordinates::testNotation()
0343 {
0344     GeoDataCoordinates::setDefaultNotation(GeoDataCoordinates::Decimal);
0345     QVERIFY(GeoDataCoordinates::defaultNotation() == GeoDataCoordinates::Decimal);
0346 
0347     GeoDataCoordinates::setDefaultNotation(GeoDataCoordinates::DMS);
0348     QVERIFY(GeoDataCoordinates::defaultNotation() == GeoDataCoordinates::DMS);
0349 }
0350 
0351 /*
0352  * test data for testIsPole()
0353  */
0354 void TestGeoDataCoordinates::testIsPole_data()
0355 {
0356     QTest::addColumn<qreal>("lon");
0357     QTest::addColumn<qreal>("lat");
0358     QTest::addColumn<qreal>("alt");
0359     QTest::addColumn<QString>("pole");
0360 
0361     QTest::newRow("false") << qreal(50.0) << qreal(50.0) << qreal(0.0) << "false_pole";
0362     QTest::newRow("south") << qreal(0.0) << qreal(-90.0) << qreal(0.0) << "south_pole";
0363     QTest::newRow("north") << qreal(0.0) << qreal(90.0) << qreal(0.0) << "north_pole";
0364 }
0365 
0366 /*
0367  * Test isPole-method
0368  */
0369 void TestGeoDataCoordinates::testIsPole()
0370 {
0371     QFETCH(qreal, lon);
0372     QFETCH(qreal, lat);
0373     QFETCH(qreal, alt);
0374     QFETCH(QString, pole);
0375 
0376     GeoDataCoordinates coordinates1;
0377 
0378     if (pole == QLatin1String("false_pole")) {
0379         coordinates1.set(lon, lat, alt, GeoDataCoordinates::Degree);
0380         QVERIFY(coordinates1.isPole() == false);
0381     } else if (pole == QLatin1String("south_pole")) {
0382         coordinates1.set(lon, lat, alt, GeoDataCoordinates::Degree);
0383         QVERIFY(coordinates1.isPole(SouthPole));
0384     } else if (pole == QLatin1String("north_pole")) {
0385         coordinates1.set(lon, lat, alt, GeoDataCoordinates::Degree);
0386         QVERIFY(coordinates1.isPole(NorthPole));
0387     }
0388 }
0389 
0390 void TestGeoDataCoordinates::testNormalizeLat_data()
0391 {
0392     QTest::addColumn<qreal>( "latRadian" );
0393 
0394     QTest::newRow( "north pole" ) << qreal(M_PI / 2);
0395     QTest::newRow( "south pole" ) << qreal(- M_PI / 2);
0396     QTest::newRow( "somewhere" ) << qreal(1.0);
0397 }
0398 
0399 void TestGeoDataCoordinates::testNormalizeLat()
0400 {
0401     QFETCH( qreal, latRadian );
0402 
0403     qreal latDegree = RAD2DEG * latRadian;
0404     for ( int i = 1; i < 10; ++i ) {
0405         if ( ( i % 2 ) == 0 ) {
0406             QCOMPARE( GeoDataCoordinates::normalizeLat( latRadian + i * M_PI, GeoDataCoordinates::Radian ), latRadian );
0407             QCOMPARE( GeoDataCoordinates::normalizeLat( latRadian + i * M_PI ), latRadian );
0408             QCOMPARE( GeoDataCoordinates::normalizeLat( latDegree + i * 180, GeoDataCoordinates::Degree ), latDegree );
0409         }
0410         else {
0411             QCOMPARE( GeoDataCoordinates::normalizeLat( latRadian + i * M_PI, GeoDataCoordinates::Radian ), -latRadian );
0412             QCOMPARE( GeoDataCoordinates::normalizeLat( latRadian + i * M_PI ), -latRadian );
0413             QCOMPARE( GeoDataCoordinates::normalizeLat( latDegree + i * 180, GeoDataCoordinates::Degree ), -latDegree );
0414         }
0415     }
0416 }
0417 
0418 void TestGeoDataCoordinates::testNormalizeLon_data()
0419 {
0420     QTest::addColumn<qreal>( "lonRadian" );
0421 
0422     QTest::newRow( "half east" ) << qreal(M_PI / 2);
0423     QTest::newRow( "half west" ) << qreal(- M_PI / 2);
0424     QTest::newRow( "somewhere" ) << qreal(1.0);
0425     QTest::newRow( "date line east" ) << qreal(M_PI);
0426     QTest::newRow( "date line west" ) << - qreal(M_PI);
0427 
0428 }
0429 
0430 void TestGeoDataCoordinates::testNormalizeLon()
0431 {
0432     QFETCH( qreal, lonRadian );
0433 
0434     qreal lonDegree = RAD2DEG * lonRadian;
0435     for ( int i = 1; i < 10; ++i ) {
0436         if ( lonRadian == qreal(M_PI) || lonRadian == qreal(-M_PI) ) {
0437             int lonRadianLarge = qRound( lonRadian * 1000 );
0438             int lonDegreeLarge = qRound( lonDegree * 1000 );
0439             if ( qRound( GeoDataCoordinates::normalizeLon( lonRadian + i * 2 * M_PI ) * 1000 ) != lonRadianLarge
0440                  && qRound( GeoDataCoordinates::normalizeLon( lonRadian + i * 2 * M_PI ) * 1000 ) != -lonRadianLarge )
0441             {
0442                 QFAIL( "Error at M_PI/-M_PI" );
0443             }
0444             if ( qRound( GeoDataCoordinates::normalizeLon( lonRadian + i * 2 * M_PI, GeoDataCoordinates::Radian ) * 1000 ) != lonRadianLarge
0445                  && qRound( GeoDataCoordinates::normalizeLon( lonRadian + i * 2 * M_PI, GeoDataCoordinates::Radian ) * 1000 ) != -lonRadianLarge )
0446             {
0447                 QFAIL( "Error at M_PI/-M_PI" );
0448             }
0449             if ( qRound( GeoDataCoordinates::normalizeLon( lonDegree + i * 360, GeoDataCoordinates::Degree ) * 1000 ) != lonDegreeLarge
0450                  && qRound( GeoDataCoordinates::normalizeLon( lonDegree + i * 360, GeoDataCoordinates::Degree ) * 1000 ) != -lonDegreeLarge )
0451             {
0452                 QFAIL( "Error at M_PI/-M_PI" );
0453             }
0454         }
0455         else {
0456             QCOMPARE( GeoDataCoordinates::normalizeLon( lonRadian + i * 2 * M_PI, GeoDataCoordinates::Radian ), lonRadian );
0457             QCOMPARE( GeoDataCoordinates::normalizeLon( lonRadian + i * 2 * M_PI ), lonRadian );
0458             QCOMPARE( GeoDataCoordinates::normalizeLon( lonDegree + i * 360, GeoDataCoordinates::Degree ), lonDegree );
0459         }
0460     }
0461 }
0462 
0463 /*
0464  * test data for testNormalize()
0465  */
0466 void TestGeoDataCoordinates::testNormalizeDegree_data()
0467 {
0468     QTest::addColumn<qreal>("lon");
0469     QTest::addColumn<qreal>("lat");
0470 
0471     QTest::newRow("deg") << qreal(200.0) << qreal(130.0);
0472 }
0473 
0474 /*
0475  * test normalizeLon(), normalizeLat() and normalizeLonLat()
0476  */
0477 void TestGeoDataCoordinates::testNormalizeDegree()
0478 {
0479     QFETCH(qreal, lon);
0480     QFETCH(qreal, lat);
0481 
0482     QCOMPARE(GeoDataCoordinates::normalizeLon(lon, GeoDataCoordinates::Degree), qreal(-160));
0483     QCOMPARE(GeoDataCoordinates::normalizeLat(lat, GeoDataCoordinates::Degree), qreal(50));
0484 
0485     qreal normalized_lon = lon;
0486     qreal normalized_lat = lat;
0487 
0488     GeoDataCoordinates::normalizeLonLat( normalized_lon, normalized_lat, GeoDataCoordinates::Degree);
0489     QCOMPARE(normalized_lon, qreal(20));
0490     QCOMPARE(normalized_lat, qreal(50));
0491 }
0492 
0493 /*
0494  * test data for testNormalize()
0495  */
0496 void TestGeoDataCoordinates::testNormalizeRadian_data()
0497 {
0498     QTest::addColumn<qreal>("lon");
0499     QTest::addColumn<qreal>("lat");
0500 
0501     QTest::newRow("rad") << qreal(3.6) << qreal(2.7);
0502 }
0503 
0504 /*
0505  * test normalizeLon(), normalizeLat() and normalizeLonLat()
0506  */
0507 void TestGeoDataCoordinates::testNormalizeRadian()
0508 {
0509     QFETCH(qreal, lon);
0510     QFETCH(qreal, lat);
0511 
0512     // Compare up to three decimals
0513     qreal value = GeoDataCoordinates::normalizeLon(lon, GeoDataCoordinates::Radian);
0514     QCOMPARE(ceil(value * 1000) / 1000, qreal(-2.683));
0515 
0516     value = GeoDataCoordinates::normalizeLat(lat, GeoDataCoordinates::Radian);
0517     QCOMPARE(ceil(value * 1000) / 1000, qreal(0.442));
0518 
0519     qreal normalized_lon = lon;
0520     qreal normalized_lat = lat;
0521 
0522     GeoDataCoordinates::normalizeLonLat( normalized_lon, normalized_lat, GeoDataCoordinates::Radian);
0523     QCOMPARE(ceil(normalized_lon * 1000) / 1000, qreal(0.459));
0524     QCOMPARE(ceil(normalized_lat * 1000) / 1000, qreal(0.442));
0525 }
0526 
0527 enum SignType {NoSign, PositiveSign, NegativeSign};
0528 enum SphereType {PosSphere, NegSphere};
0529 enum UnitsType {NoUnits, WithUnits};
0530 enum SpacesType {NoSpaces, WithSpaces};
0531 enum LocaleType {CLocale, SystemLocale};
0532 
0533 static QString
0534 createDegreeString(SignType signType,
0535                    int degreeValue, int minutesValue, qreal secondsValue,
0536                    LocaleType locale,
0537                    UnitsType unitsType, SpacesType spacesType)
0538 {
0539     QString string;
0540 
0541     // add degree
0542     if (signType != NoSign) string.append(QLatin1Char(signType==PositiveSign?'+':'-'));
0543     string.append(QString::number(degreeValue));
0544     if (unitsType == WithUnits) string.append(QChar(0xb0));
0545 
0546     // add minutes
0547     string.append(QLatin1Char(' ') + QString::number(minutesValue));
0548     if (unitsType == WithUnits) string.append(QLatin1Char('\''));
0549 
0550     // add seconds
0551     if (locale == CLocale) {
0552         string.append(QLatin1Char(' ') + QString::number(secondsValue, 'f', 10));
0553     } else {
0554         string.append(QLatin1Char(' ') + QLocale::system().toString(secondsValue, 'f', 10));
0555     }
0556     if (unitsType == WithUnits) string.append(QLatin1Char('"'));
0557 
0558     if (spacesType == WithSpaces) string.append(QLatin1Char(' '));
0559 
0560     return string;
0561 }
0562 
0563 static QString
0564 createDegreeString(SignType signType,
0565                    int degreeValue, qreal minutesValue,
0566                    LocaleType locale,
0567                    UnitsType unitsType, SpacesType spacesType)
0568 {
0569     QString string;
0570 
0571     // add degree
0572     if (signType != NoSign) string.append(QLatin1Char(signType==PositiveSign?'+':'-'));
0573     string.append(QString::number(degreeValue));
0574     if (unitsType == WithUnits) string.append(QChar(0xb0));
0575 
0576     // add minutes
0577     if (locale == CLocale) {
0578         string.append(QLatin1Char(' ') + QString::number(minutesValue, 'f', 10));
0579     } else {
0580         string.append(QLatin1Char(' ') + QLocale::system().toString(minutesValue, 'f', 10));
0581     }
0582     if (unitsType == WithUnits) string.append(QLatin1Char('\''));
0583 
0584     if (spacesType == WithSpaces) string.append(QLatin1Char(' '));
0585 
0586     return string;
0587 }
0588 
0589 static QString
0590 createDegreeString(SignType signType,
0591                    qreal degreeValue,
0592                    LocaleType locale,
0593                    UnitsType unitsType, SpacesType spacesType)
0594 {
0595     QString string;
0596 
0597     // add degree
0598     if (signType != NoSign) string.append(QLatin1Char(signType==PositiveSign?'+':'-'));
0599     if (locale == CLocale) {
0600         string.append(QString::number(degreeValue, 'f', 10));
0601     } else {
0602         string.append(QLocale::system().toString(degreeValue, 'f', 10));
0603     }
0604     if (unitsType == WithUnits) string.append(QChar(0xb0));
0605 
0606     if (spacesType == WithSpaces) string.append(QLatin1Char(' '));
0607 
0608     return string;
0609 }
0610 
0611 /*
0612  * test data for testStringDMS()
0613  */
0614 void TestGeoDataCoordinates::testFromStringDMS_data()
0615 {
0616     QTest::addColumn<QString>("string");
0617     QTest::addColumn<qreal>("lon");
0618     QTest::addColumn<qreal>("lat");
0619 
0620     const QVector<SignType> signTypes = QVector<SignType>()
0621         << NoSign << PositiveSign << NegativeSign;
0622     const QVector<SphereType> sphereTypes = QVector<SphereType>()
0623         << PosSphere << NegSphere;
0624     const QVector<UnitsType> unitsTypes = QVector<UnitsType>()
0625         << NoUnits << WithUnits;
0626     const QVector<SpacesType> spacesTypes = QVector<SpacesType>()
0627         << NoSpaces << WithSpaces;
0628     const QVector<LocaleType> localeTypes = QVector<LocaleType>()
0629         << CLocale << SystemLocale;
0630 
0631     const QVector<uint> degreeSamples = QVector<uint>()
0632         << 0 << 140 << 180;
0633     const QVector<uint> minutesSamples = QVector<uint>()
0634         << 0 << 23 << 59;
0635     const QVector<qreal> secondsSamples = QVector<qreal>()
0636         << 0.0 << 3.14159 << 59.9999999;
0637 
0638     foreach(const UnitsType unitsType, unitsTypes) {
0639     foreach(const SpacesType spacesType, spacesTypes) {
0640     // lon
0641     foreach(const SphereType lonSphere, sphereTypes) {
0642     foreach(const SignType lonSignType, signTypes) {
0643         const bool lonIsPositive =
0644             (lonSphere==PosSphere && lonSignType!=NegativeSign) ||
0645             (lonSphere==NegSphere && lonSignType==NegativeSign);
0646     foreach(const uint lonDegree, degreeSamples) {
0647     foreach(const uint lonMinutes, minutesSamples) {
0648         if(lonDegree == 180 && lonMinutes != 0) continue;
0649     foreach(const qreal lonSeconds, secondsSamples) {
0650         if(lonDegree == 180 && lonSeconds != 0.0) continue;
0651     // lat
0652     foreach(const SphereType latSphere, sphereTypes) {
0653     foreach(const SignType latSignType, signTypes) {
0654         const bool latIsPositive =
0655             (latSphere==PosSphere && latSignType!=NegativeSign) ||
0656             (latSphere==NegSphere && latSignType==NegativeSign);
0657     foreach(const uint latDegree, degreeSamples) {
0658     foreach(const uint latMinutes, minutesSamples) {
0659         if(latDegree == 180 && latMinutes != 0) continue;
0660     foreach(const qreal latSeconds, secondsSamples) {
0661         if(latDegree == 180 && latSeconds != 0.0) continue;
0662     // locale
0663     foreach(const LocaleType locale, localeTypes) {
0664 
0665     // actual construction
0666         // Create lon & lat values
0667         qreal lon = (qreal)lonDegree + lonMinutes*MIN2HOUR + lonSeconds*SEC2HOUR;
0668         if( ! lonIsPositive )
0669             lon *= -1;
0670         qreal lat = (qreal)latDegree + latMinutes*MIN2HOUR + latSeconds*SEC2HOUR;
0671         if( ! latIsPositive )
0672             lat *= -1;
0673 
0674         // Create string
0675         QString string;
0676         string.append(createDegreeString(latSignType,
0677                                          latDegree, latMinutes, latSeconds,
0678                                          locale,
0679                                          unitsType, spacesType));
0680         string.append(QLatin1Char(latSphere==PosSphere?'N':'S'));
0681         string.append(QLatin1Char(' '));
0682         string.append(createDegreeString(lonSignType,
0683                                          lonDegree, lonMinutes, lonSeconds,
0684                                          locale,
0685                                          unitsType, spacesType));
0686         string.append(QLatin1Char(lonSphere==PosSphere?'E':'W'));
0687 
0688         // Create row title
0689         QString rowTitle;
0690         rowTitle.append(QLatin1String(spacesType==WithSpaces?"spaced dir":"unspaced dir"))
0691                 .append(QLatin1String(unitsType==WithUnits?"|units":"|no units"))
0692                 .append(QLatin1String("|lon:"))
0693                 .append(QLatin1Char(lonIsPositive?'+':'-'))
0694                 .append(QString::number(lonDegree)+QChar(0xb0))
0695                 .append(QString::number(lonMinutes)+QLatin1Char('\''))
0696                 .append(QString::number(lonSeconds, 'f', 10)+QLatin1Char('"'))
0697                 .append(QLatin1Char(lonSphere==PosSphere?'P':'N'))
0698                 .append(QLatin1String("|lat:"))
0699                 .append(QLatin1Char(latIsPositive?'+':'-'))
0700                 .append(QString::number(latDegree)+QChar(0xb0))
0701                 .append(QString::number(latMinutes)+QLatin1Char('\''))
0702                 .append(QString::number(latSeconds, 'f', 10)+QLatin1Char('"'))
0703                 .append(QLatin1Char(latSphere==PosSphere?'P':'N'))
0704                 .append(QLatin1Char('|')).append(QLatin1Char(locale==CLocale?'C':'L'))
0705                 .append(QLatin1Char('|')).append(string).append(QLatin1Char('|'));
0706         QTest::newRow(rowTitle.toLatin1().constData())
0707             << string
0708             << lon
0709             << lat;
0710     }
0711     }
0712     }
0713     }
0714     }
0715     }
0716     }
0717     }
0718     }
0719     }
0720     }
0721     }
0722     }
0723 }
0724 
0725 /*
0726  * test fromString() with DMS notation
0727  */
0728 void TestGeoDataCoordinates::testFromStringDMS()
0729 {
0730     // only run random 5% of all possible permutations
0731     if ((qreal(qrand()) / RAND_MAX) > 0.05) {
0732         QSKIP("Not picked for this run.");
0733     }
0734 
0735     QFETCH(QString, string);
0736     QFETCH(qreal, lon);
0737     QFETCH(qreal, lat);
0738 
0739     bool succeeded = false;
0740     const GeoDataCoordinates coords = GeoDataCoordinates::fromString(string, succeeded);
0741 
0742     if(! succeeded)
0743         qWarning() << "Could not parse"<<string <<"for"<<lon<<lat;
0744 
0745     QVERIFY(succeeded);
0746     QCOMPARE(coords.longitude(GeoDataCoordinates::Degree), lon);
0747     QCOMPARE(coords.latitude(GeoDataCoordinates::Degree),  lat);
0748 }
0749 
0750 /*
0751  * test data for testStringDM()
0752  */
0753 void TestGeoDataCoordinates::testFromStringDM_data()
0754 {
0755     QTest::addColumn<QString>("string");
0756     QTest::addColumn<qreal>("lon");
0757     QTest::addColumn<qreal>("lat");
0758 
0759     const QVector<SignType> signTypes = QVector<SignType>()
0760         << NoSign << PositiveSign << NegativeSign;
0761     const QVector<SphereType> sphereTypes = QVector<SphereType>()
0762         << PosSphere << NegSphere;
0763     const QVector<UnitsType> unitsTypes = QVector<UnitsType>()
0764         << NoUnits << WithUnits;
0765     const QVector<SpacesType> spacesTypes = QVector<SpacesType>()
0766         << NoSpaces << WithSpaces;
0767     const QVector<LocaleType> localeTypes = QVector<LocaleType>()
0768         << CLocale << SystemLocale;
0769 
0770     const QVector<uint> degreeSamples = QVector<uint>()
0771         << 0 << 140 << 180;
0772     const QVector<qreal> minutesSamples = QVector<qreal>()
0773         << 0.0 << 3.14159 << 59.9999999;
0774 
0775     foreach(const UnitsType unitsType, unitsTypes) {
0776     foreach(const SpacesType spacesType, spacesTypes) {
0777     // lon
0778     foreach(const SphereType lonSphere, sphereTypes) {
0779     foreach(const SignType lonSignType, signTypes) {
0780         const bool lonIsPositive =
0781             (lonSphere==PosSphere && lonSignType!=NegativeSign) ||
0782             (lonSphere==NegSphere && lonSignType==NegativeSign);
0783     foreach(const uint lonDegree, degreeSamples) {
0784     foreach(const qreal lonMinutes, minutesSamples) {
0785         if(lonDegree == 180 && lonMinutes != 0.0) continue;
0786     // lat
0787     foreach(const SphereType latSphere, sphereTypes) {
0788     foreach(const SignType latSignType, signTypes) {
0789         const bool latIsPositive =
0790             (latSphere==PosSphere && latSignType!=NegativeSign) ||
0791             (latSphere==NegSphere && latSignType==NegativeSign);
0792     foreach(const uint latDegree, degreeSamples) {
0793     foreach(const qreal latMinutes, minutesSamples) {
0794         if(latDegree == 180 && latMinutes != 0.0) continue;
0795     // locale
0796     foreach(const LocaleType locale, localeTypes) {
0797 
0798     // actual construction
0799         // Create lon & lat values
0800         qreal lon = (qreal)lonDegree + lonMinutes*MIN2HOUR;
0801         if( ! lonIsPositive )
0802             lon *= -1;
0803         qreal lat = (qreal)latDegree + latMinutes*MIN2HOUR;
0804         if( ! latIsPositive )
0805             lat *= -1;
0806 
0807         // Create string
0808         QString string;
0809         string.append(createDegreeString(latSignType,
0810                                          latDegree, latMinutes,
0811                                          locale,
0812                                          unitsType, spacesType));
0813         string.append(QLatin1Char(latSphere==PosSphere?'N':'S'));
0814         string.append(QLatin1Char(' '));
0815         string.append(createDegreeString(lonSignType,
0816                                          lonDegree, lonMinutes,
0817                                          locale,
0818                                          unitsType, spacesType));
0819         string.append(QLatin1Char(lonSphere==PosSphere?'E':'W'));
0820 
0821         // Create row title
0822         QString rowTitle;
0823         rowTitle.append(QLatin1String(spacesType==WithSpaces?"spaced dir":"unspaced dir"))
0824                 .append(QLatin1String(unitsType==WithUnits?"|units":"|no units"))
0825                 .append(QLatin1String("|lon:"))
0826                 .append(QLatin1Char(lonIsPositive?'+':'-'))
0827                 .append(QString::number(lonDegree)+QChar(0xb0))
0828                 .append(QString::number(lonMinutes, 'f', 10)+QLatin1Char('\''))
0829                 .append(QLatin1Char(lonSphere==PosSphere?'P':'N'))
0830                 .append(QLatin1String("|lat:"))
0831                 .append(QLatin1Char(latIsPositive?'+':'-'))
0832                 .append(QString::number(latDegree)+QChar(0xb0))
0833                 .append(QString::number(latMinutes, 'f', 10)+QLatin1Char('\''))
0834                 .append(QLatin1Char(latSphere==PosSphere?'P':'N'))
0835                 .append(QLatin1Char('|')).append(QLatin1Char(locale==CLocale?'C':'L'))
0836                 .append(QLatin1Char('|')).append(string).append(QLatin1Char('|'));
0837         QTest::newRow(rowTitle.toLatin1().constData())
0838             << string
0839             << lon
0840             << lat;
0841     }
0842     }
0843     }
0844     }
0845     }
0846     }
0847     }
0848     }
0849     }
0850     }
0851     }
0852 }
0853 
0854 /*
0855  * test fromString() with DM notation
0856  */
0857 void TestGeoDataCoordinates::testFromStringDM()
0858 {
0859     // only run random 5% of all possible permutations
0860     if ((qreal(qrand()) / RAND_MAX) > 0.05) {
0861         QSKIP("Not picked for this run.");
0862     }
0863 
0864 
0865     QFETCH(QString, string);
0866     QFETCH(qreal, lon);
0867     QFETCH(qreal, lat);
0868 
0869     bool succeeded = false;
0870     const GeoDataCoordinates coords = GeoDataCoordinates::fromString(string, succeeded);
0871 
0872     if(! succeeded)
0873         qWarning() << "Could not parse"<<string <<"for"<<lon<<lat;
0874 
0875     QVERIFY(succeeded);
0876     QCOMPARE(coords.longitude(GeoDataCoordinates::Degree), lon);
0877     QCOMPARE(coords.latitude(GeoDataCoordinates::Degree),  lat);
0878 }
0879 
0880 /*
0881  * test data for testStringDM()
0882  */
0883 void TestGeoDataCoordinates::testFromStringD_data()
0884 {
0885     QTest::addColumn<QString>("string");
0886     QTest::addColumn<qreal>("lon");
0887     QTest::addColumn<qreal>("lat");
0888 
0889     const QVector<SignType> signTypes = QVector<SignType>()
0890         << NoSign << PositiveSign << NegativeSign;
0891     const QVector<SphereType> sphereTypes = QVector<SphereType>()
0892         << PosSphere << NegSphere;
0893     const QVector<UnitsType> unitsTypes = QVector<UnitsType>()
0894         << NoUnits << WithUnits;
0895     const QVector<SpacesType> spacesTypes = QVector<SpacesType>()
0896         << NoSpaces << WithSpaces;
0897     const QVector<LocaleType> localeTypes = QVector<LocaleType>()
0898         << CLocale << SystemLocale;
0899 
0900     const QVector<qreal> degreeSamples = QVector<qreal>()
0901         << qreal(0.0) << qreal(3.14159) << qreal(180.0);
0902 
0903     foreach(const UnitsType unitsType, unitsTypes) {
0904     foreach(const SpacesType spacesType, spacesTypes) {
0905     // lon
0906     foreach(const SphereType lonSphere, sphereTypes) {
0907     foreach(const SignType lonSignType, signTypes) {
0908         const bool lonIsPositive =
0909             (lonSphere==PosSphere && lonSignType!=NegativeSign) ||
0910             (lonSphere==NegSphere && lonSignType==NegativeSign);
0911     foreach(const qreal lonDegree, degreeSamples) {
0912     // lat
0913     foreach(const SphereType latSphere, sphereTypes) {
0914     foreach(const SignType latSignType, signTypes) {
0915         const bool latIsPositive =
0916             (latSphere==PosSphere && latSignType!=NegativeSign) ||
0917             (latSphere==NegSphere && latSignType==NegativeSign);
0918     foreach(const qreal latDegree, degreeSamples) {
0919     // locale
0920     foreach(const LocaleType locale, localeTypes) {
0921 
0922     // actual construction
0923         // Create lon & lat values
0924         qreal lon = lonDegree;
0925         if (! lonIsPositive)
0926             lon *= -1;
0927         qreal lat = latDegree;
0928         if (! latIsPositive)
0929             lat *= -1;
0930 
0931         // Create string
0932         QString string;
0933         string.append(createDegreeString(latSignType,
0934                                          latDegree,
0935                                          locale,
0936                                          unitsType, spacesType));
0937         string.append(QLatin1Char(latSphere==PosSphere?'N':'S'));
0938         string.append(QLatin1Char(' '));
0939         string.append(createDegreeString(lonSignType,
0940                                          lonDegree,
0941                                          locale,
0942                                          unitsType, spacesType));
0943         string.append(QLatin1Char(lonSphere==PosSphere?'E':'W'));
0944 
0945         // Create row title
0946         QString rowTitle;
0947         rowTitle.append(QLatin1String(spacesType==WithSpaces?"spaced dir":"unspaced dir"))
0948                 .append(QLatin1String(unitsType==WithUnits?"|units":"|no units"))
0949                 .append(QLatin1String("|lon:"))
0950                 .append(QLatin1Char(lonIsPositive?'+':'-'))
0951                 .append(QString::number(lonDegree, 'f', 10)+QChar(0xb0))
0952                 .append(QLatin1Char(lonSphere==PosSphere?'P':'N'))
0953                 .append(QLatin1String("|lat:"))
0954                 .append(QLatin1Char(latIsPositive?'+':'-'))
0955                 .append(QString::number(latDegree, 'f', 10)+QChar(0xb0))
0956                 .append(QLatin1Char(latSphere==PosSphere?'P':'N'))
0957                 .append(QLatin1Char('|')).append(QLatin1Char(locale==CLocale?'C':'L'))
0958                 .append(QLatin1Char('|')).append(string).append(QLatin1Char('|'));
0959         QTest::newRow(rowTitle.toLatin1().constData())
0960             << string
0961             << lon
0962             << lat;
0963     }
0964     }
0965     }
0966     }
0967     }
0968     }
0969     }
0970     }
0971     }
0972 
0973     QTest::newRow("scientific notation") << "0.0,1.0e-2" << qreal(1.0e-2) << qreal(0.0);
0974     QTest::newRow("scientific notation") << "-2.4E0 1.0e-18" << qreal(1e-18) << qreal(-2.4e0);
0975     QTest::newRow("scientific notation") << "1.14e-02;1.33e+01" << qreal(1.33e1) << qreal(1.14e-2);
0976 }
0977 
0978 /*
0979  * test fromString() with DM notation
0980  */
0981 void TestGeoDataCoordinates::testFromStringD()
0982 {
0983     QFETCH(QString, string);
0984     QFETCH(qreal, lon);
0985     QFETCH(qreal, lat);
0986 
0987     bool succeeded = false;
0988     const GeoDataCoordinates coords = GeoDataCoordinates::fromString(string, succeeded);
0989 
0990     if(! succeeded)
0991         qWarning() << "Could not parse"<<string <<"for"<<lon<<lat;
0992 
0993     QVERIFY(succeeded);
0994     QCOMPARE(coords.longitude(GeoDataCoordinates::Degree), lon);
0995     QCOMPARE(coords.latitude(GeoDataCoordinates::Degree),  lat);
0996 }
0997 
0998 class FromStringRegExpTranslator : public QTranslator
0999 {
1000 public:
1001     FromStringRegExpTranslator(const QString& _degree, const QString& _minutes, const QString& _seconds,
1002                                const QString& _north, const QString& _south,
1003                                const QString& _east, const QString& _west)
1004     : QTranslator((QObject*)nullptr)
1005     , degree( _degree )
1006     , minutes( _minutes )
1007     , seconds( _seconds )
1008     , north( _north )
1009     , south( _south )
1010     , east( _east )
1011     , west( _west )
1012     {}
1013 
1014 public: // QTranslator API
1015     bool isEmpty() const override { return false; }
1016     QString translate( const char* context, const char* sourceText,
1017                                const char* disambiguation = nullptr, int n = -1 ) const override;
1018 private:
1019     const QString degree;
1020     const QString minutes;
1021     const QString seconds;
1022     const QString north;
1023     const QString south;
1024     const QString east;
1025     const QString west;
1026 };
1027 
1028 QString FromStringRegExpTranslator::translate(const char* context, const char* sourceText,
1029                                                const char* disambiguation , int n) const
1030 {
1031     Q_UNUSED(n);
1032     if (qstrcmp(context, "GeoDataCoordinates") != 0 )
1033         return QString();
1034 
1035     if (qstrcmp(sourceText, "*") != 0 )
1036         return QString();
1037 
1038     if (qstrcmp(disambiguation, "North direction terms") == 0 )
1039         return north;
1040     if (qstrcmp(disambiguation, "South direction terms") == 0 )
1041         return south;
1042     if (qstrcmp(disambiguation, "East direction terms") == 0 )
1043         return east;
1044     if (qstrcmp(disambiguation, "West direction terms") == 0 )
1045         return west;
1046     if (qstrcmp(disambiguation, "Degree symbol terms") == 0 )
1047         return degree;
1048     if (qstrcmp(disambiguation, "Minutes symbol terms") == 0 )
1049         return minutes;
1050     if (qstrcmp(disambiguation, "Seconds symbol terms") == 0 )
1051         return seconds;
1052 
1053     return QString();
1054 }
1055 
1056 class Sample
1057 {
1058 public:
1059     Sample() {}
1060     Sample(const char* _name, const char* _string, qreal _lon, qreal _lat)
1061     : name(QString::fromUtf8(_name))
1062     , string(QString::fromUtf8(_string))
1063     , lon(_lon)
1064     , lat(_lat)
1065     {}
1066     QString name;
1067     QString string;
1068     qreal lon;
1069     qreal lat;
1070 };
1071 
1072 class Language {
1073 public:
1074     Language() {}
1075     Language(const char* _name,
1076              const char* _degree, const char* _minutes, const char* _seconds,
1077              const char* _north, const char* _south, const char* _east, const char* _west,
1078              const QVector<Sample>& _samples)
1079     : name(QString::fromUtf8(_name))
1080     , degree(QString::fromUtf8(_degree))
1081     , minutes(QString::fromUtf8(_minutes))
1082     , seconds(QString::fromUtf8(_seconds))
1083     , north(QString::fromUtf8(_north))
1084     , south(QString::fromUtf8(_south))
1085     , east(QString::fromUtf8(_east))
1086     , west(QString::fromUtf8(_west))
1087     , samples(_samples)
1088     {}
1089     QString name;
1090     QString degree;
1091     QString minutes;
1092     QString seconds;
1093     QString north;
1094     QString south;
1095     QString east;
1096     QString west;
1097     QVector<Sample> samples;
1098 };
1099 
1100 void TestGeoDataCoordinates::testFromLocaleString_data()
1101 {
1102     QTest::addColumn<QString>("degree");
1103     QTest::addColumn<QString>("minutes");
1104     QTest::addColumn<QString>("seconds");
1105     QTest::addColumn<QString>("north");
1106     QTest::addColumn<QString>("south");
1107     QTest::addColumn<QString>("east");
1108     QTest::addColumn<QString>("west");
1109 
1110     QTest::addColumn<QString>("string");
1111     QTest::addColumn<qreal>("lon");
1112     QTest::addColumn<qreal>("lat");
1113 
1114     const QVector<Language> languages = QVector<Language>()
1115         << Language(
1116             "English",
1117             "*", // degree
1118             "*", // minutes
1119             "*", // seconds
1120             "*", // north
1121             "*", // south
1122             "*", // east
1123             "*", // west
1124             QVector<Sample>()
1125                 << Sample(
1126                     "London",
1127                     "N051 30.150′ W000 07.234′",
1128                     -0.12056666666666666921, 51.50249999999999772626)
1129                 << Sample(
1130                     "Ålgård",
1131                     "N58.764828 E5.855483",
1132                     5.85548300000000043752, 58.76482800000000139562))
1133 
1134         << Language(
1135             "Japanese",
1136             "度", // degree
1137             "分", // minutes
1138             "秒", // seconds
1139             "北緯", // north
1140             "南緯", // south
1141             "東経", // east
1142             "西経", // west
1143             QVector<Sample>()
1144                 << Sample(
1145                     "London",
1146                     "北緯51度30分28秒 西経0度07分41秒",
1147                     -0.12805555555555556135, 51.50777777777777544088)
1148                 << Sample(
1149                     "Sydney",
1150                     "南緯33度52分06秒 東経151度12分31秒",
1151                     151.20861111111111085847, -33.86833333333333229120))
1152         << Language(
1153             "Korean",
1154             "도", // degree
1155             "분", // minutes
1156             "초", // seconds
1157             "북위", // north
1158             "남위", // south
1159             "동경", // east
1160             "서경", // west
1161             QVector<Sample>()
1162                 << Sample(
1163                     "London",
1164                     "북위 51도 30분 26초, 서경 0도 7분 39초",
1165                     -0.12750000000000000222, 51.50722222222222512755)
1166                 << Sample(
1167                     "Sydney",
1168                     "남위 33도 31분 56초, 동경 151도 12분 40초",
1169                     151.21111111111110858474, -33.53222222222222370647))
1170 
1171 // TODO: allow test control for parsing float in given locale
1172 #if 0
1173         << Language(
1174             "Galician",
1175             "", // degree
1176             "", // minutes
1177             "", // seconds
1178             "N", //north
1179             "S", // south
1180             "L|E", // east
1181             "O|W", // west
1182             QVector<Sample>()
1183                 << Sample(
1184                     "Campamento",
1185                     "36º10,67´N 5º24,29´W",
1186                     -5.40483333333333337833, 36.17783333333333217752))
1187 #endif
1188 
1189         << Language(
1190             "German",
1191             "*", // degree
1192             "*", // minutes
1193             "*", // seconds
1194             "N", //north
1195             "S", // south
1196             "O", // east
1197             "W", // west
1198             QVector<Sample>()
1199                 << Sample(
1200                     "London",
1201                     "51° 31′ N, 0° 7′ W",
1202                     -0.11666666666666666852, 51.51666666666666571928))
1203 
1204         << Language(
1205             "Greek",
1206             "", // degree
1207             "", // minutes
1208             "", // seconds
1209             "Β", // north
1210             "Ν", // south
1211             "Α", // east
1212             "Δ", // west
1213             QVector<Sample>()
1214                 << Sample(
1215                     "Χαλκίδα",
1216                     "38° 28′ Β 23° 36′ Α",
1217                     23.6, 38.46666666666666856))
1218 
1219         << Language(
1220             "Dutch",
1221             "", // degree
1222             "", // minutes
1223             "", // seconds
1224             "N|NB", // north
1225             "Z|ZB", // south
1226             "O|OL", // east
1227             "W|WL", // west
1228             QVector<Sample>()
1229                 << Sample(
1230                     "Amersfoort",
1231                     "N 52° 8′ 32.14″ , E 5° 24′ 56.09″",
1232                     5.41558055555555561966, 52.14226111111111094942)
1233 // TODO: allow test control for parsing float in given locale
1234 #if 0
1235                 << Sample(
1236                     "London",
1237                     "51°30'00,55\" NB 0°07'34,45\" WL",
1238                     -0.12623611111111110450, 51.50015277777777811252)
1239                 << Sample(
1240                     "Amsterdam",
1241                     "52°22'12,78\" NB 4°53'42,60\" OL",
1242                     4.89516666666666644403, 52.37021666666666419587)
1243                 << Sample(
1244                     "Capetown",
1245                     "33°55'29,52\" ZB 18°25'26,60\" OL",
1246                     18.42405555555555451974, -33.92486666666666650372)
1247 #endif
1248                )
1249 
1250         << Language(
1251             "Polish",
1252             "", // degree
1253             "", // minutes
1254             "", // seconds
1255             "Pn.|Pn", // north
1256             "Płd.|Płd", // south
1257             "Wschod.|Wschod|Wsch.|Wsch|Ws.|Ws", // east
1258             "Zach.|Zach|Z", // west
1259             QVector<Sample>()
1260                 << Sample(
1261                     "Warsaw",
1262                     "52°13′56″Pn. 21°00′30″Ws.",
1263                     21.00833333333333285964, 52.23222222222221944321))
1264 
1265 // TODO: allow test control for parsing float in given locale
1266 #if 0
1267         << Language(
1268             "Esperanto",
1269             "", // degree
1270             "", // minutes
1271             "", // seconds
1272             "N", // north
1273             "S", // south
1274             "Or", // east
1275             "Ok", // west
1276             QVector<Sample>()
1277                 << Sample(
1278                     "London",
1279                     "52° 8′ 32,14″ N; 5° 24′ 56,09″ Or",
1280                     5.41558055555555561966, 52.14226111111111094942))
1281 #endif
1282 
1283         << Language(
1284             "Norwegian",
1285             "", // degree
1286             "", // minutes
1287             "", // seconds
1288             "N", // north
1289             "S", // south
1290             "Ø", // east
1291             "V", // west
1292             QVector<Sample>()
1293                 << Sample(
1294                     "London",
1295                     "51° 30′ 25” N 0° 7′ 39” V",
1296                     -0.12750000000000000222, 51.50694444444444286546)
1297                 << Sample(
1298                     "Ålgård",
1299                     "58° 45′ 53.38″ N 5° 51′ 19.74″ Ø",
1300                     5.85548333333333292927, 58.76482777777777499750))
1301 
1302         << Language(
1303             "Swedish",
1304             "", // degree
1305             "", // minutes
1306             "", // seconds
1307             "N", // north
1308             "S", // south
1309             "O", // east
1310             "V", // west
1311             QVector<Sample>()
1312                 << Sample(
1313                     "London",
1314                     "51°30′29″N 0°7′29″V",
1315                     -0.12472222222222222043, 51.50805555555555770297)
1316                 << Sample(
1317                     "Sydney",
1318                     "33°31′56″S 151°12′40″O",
1319                     151.21111111111110858474, -33.53222222222222370647))
1320 
1321         << Language(
1322             "Icelandic",
1323             "", // degree
1324             "", // minutes
1325             "", // seconds
1326             "N", //north
1327             "S", // south
1328             "A", // east
1329             "V", // west
1330 //TODO:     "breidd 51°30'26\" N, lengd 0°7'39\" V" // London
1331             QVector<Sample>()
1332                 << Sample(
1333                     "Sydney",
1334                     "33°31'56\" S, 151°12'40\" A",
1335                     151.21111111111110858474, -33.53222222222222370647))
1336 
1337         << Language(
1338             "Turkish",
1339             "", // degree
1340             "", // minutes
1341             "", // seconds
1342             "K", // north
1343             "G", // south
1344             "D", // east
1345             "B", // west
1346             QVector<Sample>()
1347                 << Sample(
1348                     "London",
1349                     "51° 30′ 28″ K, 0° 7′ 41″ B",
1350                     -0.12805555555555556135, 51.50777777777777544088))
1351 
1352         << Language(
1353             "Spanish", // (incl. Latin America)
1354             "", // degree
1355             "", // minutes
1356             "", // seconds
1357             "N", // north
1358             "S", // south
1359             "E", // east
1360             "O|W", // west
1361             QVector<Sample>()
1362                 << Sample(
1363                     "London",
1364                     "51°30′25″N 00°07′39″O",
1365                     -0.12750000000000000222, 51.50694444444444286546)
1366                 << Sample(
1367                     "Else",
1368                     "52° 8′ 32.14″ N, 5° 24′ 56.09″ W",
1369                     -5.41558055555555561966, 52.14226111111111094942)
1370                 << Sample(
1371                     "Bogotá",
1372                     "4°35’53″N 74°4’33″O",
1373                     -74.07583333333333541759, 4.59805555555555667269))
1374 
1375         << Language(
1376             "French",
1377             "", // degree
1378             "", // minutes
1379             "", // seconds
1380             "N", // north
1381             "S", // south
1382             "E", // east
1383             "O", // west
1384             QVector<Sample>()
1385                 << Sample(
1386                     "London",
1387                     "51° 30′ 18″ N 0° 04′ 43″ O",
1388                     -0.07861111111111110383, 51.50500000000000255795))
1389 
1390         << Language(
1391             "Portuguese", // incl. Brazilian Portuguese
1392             "", // degree
1393             "", // minutes
1394             "", // seconds
1395             "N", // north
1396             "S", // south
1397             "E|L", // east
1398             "O", // west
1399             QVector<Sample>()
1400                 << Sample(
1401                     "London",
1402                     "52° 8′ 32.14″ N, 5° 24′ 56.09″ E",
1403                     5.41558055555555561966, 52.14226111111111094942))
1404 
1405         << Language(
1406             "Arabic",
1407             "", // degree
1408             "", // minutes
1409             "", // seconds
1410     "شمال", // north
1411     "جنوب", // south
1412     "شرق", // east
1413     "غرب", // west
1414             QVector<Sample>()
1415                 << Sample(
1416                     "Warsaw",
1417     "52°13′56″ شمال 21°00′30″ شرق",
1418                     21.00833333333333285964, 52.23222222222221944321))
1419 
1420         << Language(
1421             "Russian",
1422             "", //"град", "градусов" // degree
1423             "", //"мин", "минут" // minutes
1424             "", //"сек", "секунд" // seconds
1425             "с. ш.", // north
1426             "ю. ш.", // south
1427             "в. д.", // east
1428             "з. д.", // west
1429             QVector<Sample>()
1430                 << Sample(
1431                     "London",
1432                     "51°30′26″ с. ш. 0°07′39″ з. д.",
1433                     -0.12750000000000000222, 51.50722222222222512755))
1434 
1435         << Language(
1436             "Ukrainian",
1437             "", // degree
1438             "", // minutes
1439             "", // seconds
1440             "пн. ш.", // north
1441             "пд. ш.", // south
1442             "сх. д.", // east
1443             "зх. д.", // west
1444             QVector<Sample>()
1445                 << Sample(
1446                     "London",
1447                     "51°30' пн. ш. 0°07' сх. д.",
1448                     0.11666666666666666852, 51.50000000000000000000)
1449                 << Sample(
1450                     "Sydney",
1451                     "33°52'10'' пд. ш. 151°12'30'' сх. д.",
1452                     151.20833333333334280724, -33.86944444444444712872)
1453                 << Sample(
1454                     "Rio de Janeiro",
1455                     "22°54'30'' пд. ш. 43°11'47'' зх. д.",
1456                     -43.19638888888889027839, -22.90833333333333499127))
1457 
1458         << Language(
1459             "Bulgarian",
1460             "", // degree
1461             "", // minutes
1462             "", // seconds
1463             "с. ш.", // north
1464             "ю. ш.", // south
1465             "и. д.", // east
1466             "и. д.", // west
1467             QVector<Sample>()
1468                 << Sample(
1469                     "London",
1470                     "51°30′26″ с. ш. 0°07′39″ и. д.",
1471                     0.12750000000000000222, 51.50722222222222512755))
1472 
1473         << Language(
1474             "Czech",
1475             "", // degree
1476             "", // minutes
1477             "", // seconds
1478             "s. š.", // north
1479             "j. š.", // south
1480             "z. d.", // east
1481             "v. d.", // west
1482             QVector<Sample>()
1483                 << Sample(
1484                     "London",
1485                     "51°30′42″ s. š., 0°02′56″ z. d.",
1486                     0.04888888888888889145, 51.51166666666666316132)
1487                 << Sample(
1488                     "Sydney",
1489                     "33° 52′ j. š., 151° 13′ v. d.",
1490                     -151.21666666666669698316, -33.86666666666666714036))
1491 
1492 
1493         << Language(
1494             "Hindi",
1495             "", // degree
1496             "", // minutes
1497             "", // seconds
1498             "उ", // north
1499             "द", // south
1500             "पू", // east
1501             "प", // west
1502             QVector<Sample>()
1503                 << Sample(
1504                     "London",
1505                     "51°30′25″उ 00°07′39″पू",
1506                     0.12750000000000000222, 51.50694444444444286546))
1507 
1508         << Language(
1509             "Tamil",
1510             "", // degree
1511             "", // minutes
1512             "", // seconds
1513             "வ", // north
1514             "தெ", // south
1515             "கி", // east
1516             "மே", // west
1517             QVector<Sample>()
1518                 << Sample(
1519                     "London",
1520                     "51°30′25″ வ 00°07′39″ கி",
1521                     0.12750000000000000222, 51.50694444444444286546))
1522         ;
1523 
1524     foreach( const Language& language, languages ) {
1525     foreach( const Sample& sample, language.samples ) {
1526         const QString rowTitle =
1527             language.name +
1528             QLatin1String("|") + sample.name +
1529             QLatin1String("|lon:") +
1530             QString::number(sample.lon, 'f', 10) +
1531             QLatin1String("|lat:") +
1532             QString::number(sample.lat, 'f', 10);
1533 
1534         QTest::newRow(rowTitle.toLatin1().constData())
1535             << language.degree
1536             << language.minutes
1537             << language.seconds
1538             << language.north
1539             << language.south
1540             << language.east
1541             << language.west
1542             << sample.string
1543             << sample.lon
1544             << sample.lat;
1545         }
1546     }
1547 }
1548 
1549 
1550 void TestGeoDataCoordinates::testFromLocaleString()
1551 {
1552     QFETCH(QString, degree);
1553     QFETCH(QString, minutes);
1554     QFETCH(QString, seconds);
1555     QFETCH(QString, north);
1556     QFETCH(QString, south);
1557     QFETCH(QString, east);
1558     QFETCH(QString, west);
1559 
1560     QFETCH(QString, string);
1561     QFETCH(qreal, lon);
1562     QFETCH(qreal, lat);
1563 
1564     FromStringRegExpTranslator translator(degree, minutes, seconds, north, south, east, west);
1565     QCoreApplication::installTranslator(&translator);
1566 
1567     bool succeeded = false;
1568     const GeoDataCoordinates coords = GeoDataCoordinates::fromString(string, succeeded);
1569 
1570     if(! succeeded)
1571         qWarning() << "Could not parse"<<string <<"for"<<lon<<lat;
1572 
1573     QVERIFY(succeeded);
1574 
1575 // Uncomment to get the lon and lat values with more precision
1576 // qWarning() << "lon"<<QString::number(coords.longitude(GeoDataCoordinates::Degree), 'f', 20)
1577 //            << "lat"<<QString::number(coords.latitude(GeoDataCoordinates::Degree), 'f', 20);
1578 
1579     QCOMPARE(coords.longitude(GeoDataCoordinates::Degree), lon);
1580     QCOMPARE(coords.latitude(GeoDataCoordinates::Degree),  lat);
1581 
1582     QCoreApplication::removeTranslator(&translator);
1583 }
1584 
1585 /*
1586  * test data for toString()
1587  */
1588 void TestGeoDataCoordinates::testToString_Decimal_data()
1589 {
1590     QTest::addColumn<qreal>("lon");
1591     QTest::addColumn<qreal>("lat");
1592     QTest::addColumn<int>("precision");
1593     QTest::addColumn<QString>("expected");
1594 
1595     addRow() << qreal(150.0) << qreal(80.0) << 0 << QString::fromUtf8( " 150°E,   80°N" );
1596     addRow() << qreal(150.0) << qreal(80.0) << 1 << QString::fromUtf8( "150.0°E,  80.0°N" );
1597     addRow() << qreal(150.0) << qreal(80.0) << 2 << QString::fromUtf8( "150.00°E,  80.00°N" );
1598     addRow() << qreal(150.0) << qreal(80.0) << 3 << QString::fromUtf8( "150.000°E,  80.000°N" );
1599     addRow() << qreal(150.0) << qreal(80.0) << 4 << QString::fromUtf8( "150.0000°E,  80.0000°N" );
1600     addRow() << qreal(150.0) << qreal(80.0) << 5 << QString::fromUtf8( "150.00000°E,  80.00000°N" );
1601 
1602     addRow() << qreal(149.6)       << qreal(79.6)       << 0 << QString::fromUtf8( " 150°E,   80°N" );
1603     addRow() << qreal(149.96)      << qreal(79.96)      << 0 << QString::fromUtf8( " 150°E,   80°N" );
1604 
1605     addRow() << qreal(149.6)       << qreal(79.6)       << 1 << QString::fromUtf8( "149.6°E,  79.6°N" );
1606     addRow() << qreal(149.96)      << qreal(79.96)      << 1 << QString::fromUtf8( "150.0°E,  80.0°N" );
1607     addRow() << qreal(149.996)     << qreal(79.996)     << 1 << QString::fromUtf8( "150.0°E,  80.0°N" );
1608 
1609     addRow() << qreal(149.96)      << qreal(79.96)      << 2 << QString::fromUtf8( "149.96°E,  79.96°N" );
1610     addRow() << qreal(149.996)     << qreal(79.996)     << 2 << QString::fromUtf8( "150.00°E,  80.00°N" );
1611     addRow() << qreal(149.9996)    << qreal(79.9996)    << 2 << QString::fromUtf8( "150.00°E,  80.00°N" );
1612 
1613     addRow() << qreal(149.996)     << qreal(79.996)     << 3 << QString::fromUtf8( "149.996°E,  79.996°N" );
1614     addRow() << qreal(149.9996)    << qreal(79.9996)    << 3 << QString::fromUtf8( "150.000°E,  80.000°N" );
1615     addRow() << qreal(149.99996)   << qreal(79.99996)   << 3 << QString::fromUtf8( "150.000°E,  80.000°N" );
1616 
1617     addRow() << qreal(149.9996)    << qreal(79.9996)    << 4 << QString::fromUtf8( "149.9996°E,  79.9996°N" );
1618     addRow() << qreal(149.99996)   << qreal(79.99996)   << 4 << QString::fromUtf8( "150.0000°E,  80.0000°N" );
1619     addRow() << qreal(149.999996)  << qreal(79.999996)  << 4 << QString::fromUtf8( "150.0000°E,  80.0000°N" );
1620 
1621     addRow() << qreal(149.99996)   << qreal(79.99996)   << 5 << QString::fromUtf8( "149.99996°E,  79.99996°N" );
1622     addRow() << qreal(149.999996)  << qreal(79.999996)  << 5 << QString::fromUtf8( "150.00000°E,  80.00000°N" );
1623     addRow() << qreal(149.9999996) << qreal(79.9999996) << 5 << QString::fromUtf8( "150.00000°E,  80.00000°N" );
1624 
1625     addRow() << qreal(149.999996)  << qreal(79.999996)  << 6 << QString::fromUtf8( "149.999996°E,  79.999996°N" );
1626     addRow() << qreal(149.9999996) << qreal(79.9999996) << 6 << QString::fromUtf8( "150.000000°E,  80.000000°N" );
1627 
1628 
1629     addRow() << qreal(150.1)       << qreal(80.1)       << 0 << QString::fromUtf8( " 150°E,   80°N" );
1630     addRow() << qreal(150.01)      << qreal(80.01)      << 0 << QString::fromUtf8( " 150°E,   80°N" );
1631 
1632     addRow() << qreal(150.1)       << qreal(80.1)       << 1 << QString::fromUtf8( "150.1°E,  80.1°N" );
1633     addRow() << qreal(150.01)      << qreal(80.01)      << 1 << QString::fromUtf8( "150.0°E,  80.0°N" );
1634     addRow() << qreal(150.001)     << qreal(80.001)     << 1 << QString::fromUtf8( "150.0°E,  80.0°N" );
1635 
1636     addRow() << qreal(150.01)      << qreal(80.01)      << 2 << QString::fromUtf8( "150.01°E,  80.01°N" );
1637     addRow() << qreal(150.001)     << qreal(80.001)     << 2 << QString::fromUtf8( "150.00°E,  80.00°N" );
1638     addRow() << qreal(150.0001)    << qreal(80.0001)    << 2 << QString::fromUtf8( "150.00°E,  80.00°N" );
1639 
1640     addRow() << qreal(150.001)     << qreal(80.001)     << 3 << QString::fromUtf8( "150.001°E,  80.001°N" );
1641     addRow() << qreal(150.0001)    << qreal(80.0001)    << 3 << QString::fromUtf8( "150.000°E,  80.000°N" );
1642     addRow() << qreal(150.00001)   << qreal(80.00001)   << 3 << QString::fromUtf8( "150.000°E,  80.000°N" );
1643 
1644     addRow() << qreal(150.0001)    << qreal(80.0001)    << 4 << QString::fromUtf8( "150.0001°E,  80.0001°N" );
1645     addRow() << qreal(150.00001)   << qreal(80.00001)   << 4 << QString::fromUtf8( "150.0000°E,  80.0000°N" );
1646     addRow() << qreal(150.000001)  << qreal(80.000001)  << 4 << QString::fromUtf8( "150.0000°E,  80.0000°N" );
1647 
1648     addRow() << qreal(150.00001)   << qreal(80.00001)   << 5 << QString::fromUtf8( "150.00001°E,  80.00001°N" );
1649     addRow() << qreal(150.000001)  << qreal(80.000001)  << 5 << QString::fromUtf8( "150.00000°E,  80.00000°N" );
1650     addRow() << qreal(150.0000001) << qreal(80.0000001) << 5 << QString::fromUtf8( "150.00000°E,  80.00000°N" );
1651 
1652     addRow() << qreal(150.000001)  << qreal(80.000001)  << 6 << QString::fromUtf8( "150.000001°E,  80.000001°N" );
1653     addRow() << qreal(150.0000001) << qreal(80.0000001) << 6 << QString::fromUtf8( "150.000000°E,  80.000000°N" );
1654 }
1655 
1656 /*
1657  * test toString()
1658  */
1659 void TestGeoDataCoordinates::testToString_Decimal()
1660 {
1661     QFETCH( qreal, lon );
1662     QFETCH( qreal, lat );
1663     QFETCH( int, precision );
1664     QFETCH( QString, expected );
1665 
1666     const GeoDataCoordinates coordinates( lon, lat, 0, GeoDataCoordinates::Degree );
1667 
1668     const QString result = coordinates.toString( GeoDataCoordinates::Decimal, precision );
1669     QCOMPARE( result, expected );
1670 }
1671 
1672 /*
1673  * test data for toString()
1674  */
1675 void TestGeoDataCoordinates::testToString_DMS_data()
1676 {
1677     QTest::addColumn<qreal>("lon");
1678     QTest::addColumn<qreal>("lat");
1679     QTest::addColumn<int>("precision");
1680     QTest::addColumn<QString>("expected");
1681 
1682     addRow() << qreal(0.)                         << qreal(0.)                        << 0 << QString::fromUtf8( "  0°E,   0°S" );
1683     addRow() << qreal(150.)                       << qreal(80.)                       << 0 << QString::fromUtf8( "150°E,  80°N" );
1684     addRow() << qreal(149. + 31./60)              << qreal(79. + 31./60)              << 0 << QString::fromUtf8( "150°E,  80°N" );
1685     addRow() << qreal(149. + 30./60 + 31./3600)   << qreal(79. + 30./60 + 31./3600)   << 0 << QString::fromUtf8( "150°E,  80°N" );
1686     addRow() << qreal(149. + 30./60 + 30.51/3600) << qreal(79. + 30./60 + 30.51/3600) << 0 << QString::fromUtf8( "150°E,  80°N" );
1687     addRow() << qreal(150. + 29./60)              << qreal(80. + 29./60)              << 0 << QString::fromUtf8( "150°E,  80°N" );
1688     addRow() << qreal(150. + 29./60 + 29./3600)   << qreal(80. + 29./60 + 29./3600)   << 0 << QString::fromUtf8( "150°E,  80°N" );
1689     addRow() << qreal(150. + 29./60 + 29.49/3600) << qreal(80. + 29./60 + 29.49/3600) << 0 << QString::fromUtf8( "150°E,  80°N" );
1690 
1691     addRow() << qreal(0.)                         << qreal(0.)                        << 1 << QString::fromUtf8( "  0° 00'E,   0° 00'S" );
1692     addRow() << qreal(150.)                       << qreal(80.)                       << 1 << QString::fromUtf8( "150° 00'E,  80° 00'N" );
1693     addRow() << qreal(149. + 59./60 + 31./3600)   << qreal(79. + 59./60 + 31./3600)   << 1 << QString::fromUtf8( "150° 00'E,  80° 00'N" );
1694     addRow() << qreal(149. + 59./60 + 30.51/3600) << qreal(79. + 59./60 + 30.51/3600) << 1 << QString::fromUtf8( "150° 00'E,  80° 00'N" );
1695     addRow() << qreal(150.          + 29./3600)   << qreal(80.          + 29./3600)   << 1 << QString::fromUtf8( "150° 00'E,  80° 00'N" );
1696     addRow() << qreal(150.          + 29.49/3600) << qreal(80.          + 29.49/3600) << 1 << QString::fromUtf8( "150° 00'E,  80° 00'N" );
1697 
1698     addRow() << qreal(0.)                         << qreal(0.)                        << 2 << QString::fromUtf8( "  0° 00'E,   0° 00'S" );
1699     addRow() << qreal(150.)                       << qreal(80.)                       << 2 << QString::fromUtf8( "150° 00'E,  80° 00'N" );
1700     addRow() << qreal(149. + 59./60 + 31./3600)   << qreal(79. + 59./60 + 31./3600)   << 2 << QString::fromUtf8( "150° 00'E,  80° 00'N" );
1701     addRow() << qreal(149. + 59./60 + 30.51/3600) << qreal(79. + 59./60 + 30.51/3600) << 2 << QString::fromUtf8( "150° 00'E,  80° 00'N" );
1702     addRow() << qreal(150.          + 29./3600)   << qreal(80.          + 29./3600)   << 2 << QString::fromUtf8( "150° 00'E,  80° 00'N" );
1703     addRow() << qreal(150.          + 29.49/3600) << qreal(80.          + 29.49/3600) << 2 << QString::fromUtf8( "150° 00'E,  80° 00'N" );
1704 
1705     addRow() << qreal(0.)                         << qreal(0.)                        << 3 << QString::fromUtf8( "  0° 00' 00\"E,   0° 00' 00\"S" );
1706     addRow() << qreal(150.)                       << qreal(80.)                       << 3 << QString::fromUtf8( "150° 00' 00\"E,  80° 00' 00\"N" );
1707     addRow() << qreal(149. + 59./60 + 59.51/3600) << qreal(79. + 59./60 + 59.51/3600) << 3 << QString::fromUtf8( "150° 00' 00\"E,  80° 00' 00\"N" );
1708     addRow() << qreal(150.          +  0.49/3600) << qreal(80.          +  0.49/3600) << 3 << QString::fromUtf8( "150° 00' 00\"E,  80° 00' 00\"N" );
1709 
1710     addRow() << qreal(0.)                         << qreal(0.)                        << 4 << QString::fromUtf8( "  0° 00' 00\"E,   0° 00' 00\"S" );
1711     addRow() << qreal(150.)                       << qreal(80.)                       << 4 << QString::fromUtf8( "150° 00' 00\"E,  80° 00' 00\"N" );
1712     addRow() << qreal(149. + 59./60 + 59.51/3600) << qreal(79. + 59./60 + 59.51/3600) << 4 << QString::fromUtf8( "150° 00' 00\"E,  80° 00' 00\"N" );
1713     addRow() << qreal(150.          +  0.49/3600) << qreal(80.          +  0.49/3600) << 4 << QString::fromUtf8( "150° 00' 00\"E,  80° 00' 00\"N" );
1714 
1715     addRow() << qreal(0.)                          << qreal(0.)                         << 5 << QString::fromUtf8( "  0° 00' 00.0\"E,   0° 00' 00.0\"S" );
1716     addRow() << qreal(150.)                        << qreal(80.)                        << 5 << QString::fromUtf8( "150° 00' 00.0\"E,  80° 00' 00.0\"N" );
1717     addRow() << qreal(149. + 59./60 + 59.951/3600) << qreal(79. + 59./60 + 59.951/3600) << 5 << QString::fromUtf8( "150° 00' 00.0\"E,  80° 00' 00.0\"N" );
1718     addRow() << qreal(150.          +  0.049/3600) << qreal(80.          +  0.049/3600) << 5 << QString::fromUtf8( "150° 00' 00.0\"E,  80° 00' 00.0\"N" );
1719 
1720     addRow() << qreal(0.)                           << qreal(0.)                          << 6 << QString::fromUtf8( "  0° 00' 00.00\"E,   0° 00' 00.00\"S" );
1721     addRow() << qreal(150.)                         << qreal(80.)                         << 6 << QString::fromUtf8( "150° 00' 00.00\"E,  80° 00' 00.00\"N" );
1722     addRow() << qreal(149. + 59./60 + 59.9951/3600) << qreal(79. + 59./60 + 59.9951/3600) << 6 << QString::fromUtf8( "150° 00' 00.00\"E,  80° 00' 00.00\"N" );
1723     addRow() << qreal(150.          +  0.0049/3600) << qreal(80.          +  0.0049/3600) << 6 << QString::fromUtf8( "150° 00' 00.00\"E,  80° 00' 00.00\"N" );
1724 }
1725 
1726 /*
1727  * test toString()
1728  */
1729 void TestGeoDataCoordinates::testToString_DMS()
1730 {
1731     QFETCH( qreal, lon );
1732     QFETCH( qreal, lat );
1733     QFETCH( int, precision );
1734     QFETCH( QString, expected );
1735 
1736     const GeoDataCoordinates coordinates( lon, lat, 0, GeoDataCoordinates::Degree );
1737 
1738     const QString result = coordinates.toString( GeoDataCoordinates::DMS, precision );
1739     QCOMPARE( result, expected );
1740 }
1741 
1742 /*
1743  * test data for toString()
1744  */
1745 void TestGeoDataCoordinates::testToString_DM_data()
1746 {
1747     QTest::addColumn<qreal>("lon");
1748     QTest::addColumn<qreal>("lat");
1749     QTest::addColumn<int>("precision");
1750     QTest::addColumn<QString>("expected");
1751 
1752     addRow() << qreal(0.)              << qreal(0.)             << 0 << QString::fromUtf8( "  0°E,   0°S" );
1753     addRow() << qreal(150.)            << qreal(80.)            << 0 << QString::fromUtf8( "150°E,  80°N" );
1754     addRow() << qreal(149. + 31./60)   << qreal(79. + 31./60)   << 0 << QString::fromUtf8( "150°E,  80°N" );
1755     addRow() << qreal(149. + 30.51/60) << qreal(79. + 30.51/60) << 0 << QString::fromUtf8( "150°E,  80°N" );
1756     addRow() << qreal(150. + 29./60)   << qreal(80. + 29./60)   << 0 << QString::fromUtf8( "150°E,  80°N" );
1757     addRow() << qreal(150. + 29.49/60) << qreal(80. + 29.49/60) << 0 << QString::fromUtf8( "150°E,  80°N" );
1758 
1759     addRow() << qreal(0.)              << qreal(0.)             << 1 << QString::fromUtf8( "  0° 00'E,   0° 00'S" );
1760     addRow() << qreal(150.)            << qreal(80.)            << 1 << QString::fromUtf8( "150° 00'E,  80° 00'N" );
1761     addRow() << qreal(149. + 59.51/60) << qreal(79. + 59.51/60) << 1 << QString::fromUtf8( "150° 00'E,  80° 00'N" );
1762     addRow() << qreal(150. +  0.49/60) << qreal(80. +  0.49/60) << 1 << QString::fromUtf8( "150° 00'E,  80° 00'N" );
1763 
1764     addRow() << qreal(0.)              << qreal(0.)             << 2 << QString::fromUtf8( "  0° 00'E,   0° 00'S" );
1765     addRow() << qreal(150.)            << qreal(80.)            << 2 << QString::fromUtf8( "150° 00'E,  80° 00'N" );
1766     addRow() << qreal(149. + 59.51/60) << qreal(79. + 59.51/60) << 2 << QString::fromUtf8( "150° 00'E,  80° 00'N" );
1767     addRow() << qreal(150. +  0.49/60) << qreal(80. +  0.49/60) << 2 << QString::fromUtf8( "150° 00'E,  80° 00'N" );
1768 
1769     addRow() << qreal(0.)               << qreal(0.)              << 3 << QString::fromUtf8( "  0° 00.0'E,   0° 00.0'S" );
1770     addRow() << qreal(150.)             << qreal(80.)             << 3 << QString::fromUtf8( "150° 00.0'E,  80° 00.0'N" );
1771     addRow() << qreal(149. + 59.951/60) << qreal(79. + 59.951/60) << 3 << QString::fromUtf8( "150° 00.0'E,  80° 00.0'N" );
1772     addRow() << qreal(150. +  0.049/60) << qreal(80. +  0.049/60) << 3 << QString::fromUtf8( "150° 00.0'E,  80° 00.0'N" );
1773 
1774     addRow() << qreal(0.)                << qreal(0.)               << 4 << QString::fromUtf8( "  0° 00.00'E,   0° 00.00'S" );
1775     addRow() << qreal(150.)              << qreal(80.)              << 4 << QString::fromUtf8( "150° 00.00'E,  80° 00.00'N" );
1776     addRow() << qreal(149. + 59.9951/60) << qreal(79. + 59.9951/60) << 4 << QString::fromUtf8( "150° 00.00'E,  80° 00.00'N" );
1777     addRow() << qreal(150. +  0.0049/60) << qreal(80. +  0.0049/60) << 4 << QString::fromUtf8( "150° 00.00'E,  80° 00.00'N" );
1778 }
1779 
1780 /*
1781  * test toString()
1782  */
1783 void TestGeoDataCoordinates::testToString_DM()
1784 {
1785     QFETCH( qreal, lon );
1786     QFETCH( qreal, lat );
1787     QFETCH( int, precision );
1788     QFETCH( QString, expected );
1789 
1790     const GeoDataCoordinates coordinates( lon, lat, 0, GeoDataCoordinates::Degree );
1791 
1792     const QString result = coordinates.toString( GeoDataCoordinates::DM, precision );
1793     QCOMPARE( result, expected );
1794 }
1795 
1796 /*
1797  * test data for testPack()
1798  */
1799 void TestGeoDataCoordinates::testPack_data()
1800 {
1801     QTest::addColumn<qreal>("lon");
1802     QTest::addColumn<qreal>("lat");
1803     QTest::addColumn<qreal>("alt");
1804 
1805     QTest::newRow("deg") << qreal(180.0) << qreal(90.0) << qreal(400.0);
1806 }
1807 
1808 /*
1809  * test pack() and unPack()
1810  */
1811 void TestGeoDataCoordinates::testPack()
1812 {
1813     QFETCH(qreal, lon);
1814     QFETCH(qreal, lat);
1815     QFETCH(qreal, alt);
1816 
1817     GeoDataCoordinates coordinates1,coordinates2;
1818     coordinates1.set(lon, lat, alt, GeoDataCoordinates::Degree);
1819 
1820     QTemporaryFile file;
1821     if(file.open()) {
1822         QDataStream out(&file);
1823         coordinates1.pack(out);
1824     }
1825     file.close();
1826 
1827     if(file.open()) {
1828         QDataStream in(&file);
1829         coordinates2.unpack(in);
1830     }
1831     file.close();
1832 
1833     QCOMPARE(coordinates1.longitude(GeoDataCoordinates::Degree), coordinates2.longitude(GeoDataCoordinates::Degree));
1834     QCOMPARE(coordinates1.latitude(GeoDataCoordinates::Degree), coordinates2.latitude(GeoDataCoordinates::Degree));
1835     QCOMPARE(coordinates1.altitude(), coordinates2.altitude());
1836 }
1837 
1838 /*
1839  * test data for testUTM()
1840  */
1841 void TestGeoDataCoordinates::testUTM_data()
1842 {
1843     QTest::addColumn<qreal>("lon");
1844     QTest::addColumn<qreal>("lat");
1845     QTest::addColumn<int>("zone");
1846     QTest::addColumn<QString>("latitudeBand");
1847     QTest::addColumn<int>("easting");
1848     QTest::addColumn<int>("northing");
1849 
1850     /* Randomly selected locations, converted to UTM with the following
1851      * tools to check their correctness:
1852      * http://home.hiwaay.net/~taylorc/toolbox/geography/geoutm.html
1853      * http://www.earthpoint.us/Convert.aspx
1854      * http://www.synnatschke.de/geo-tools/coordinate-converter.php
1855      * http://www.latlong.net/lat-long-utm.html
1856      * http://leware.net/geo/utmgoogle.htm
1857      * http://geographiclib.sourceforge.net/cgi-bin/GeoConvert
1858      */
1859 
1860     // Equator
1861     addRow() << qreal(-180.0)   << qreal(0.0)       << 1  << "N" << 16602144 << 0;
1862     addRow() << qreal(0)        << qreal(0.0)       << 31 << "N" << 16602144 << 0;
1863     addRow() << qreal(150.567)  << qreal(0.0)       << 56 << "N" << 22918607 << 0;
1864 
1865     // Zone borders
1866     int zoneNumber = 1;
1867     for ( int i = -180; i <= 180; i += 6 ){
1868         addRow() << qreal(i) << qreal(0.0) << zoneNumber << "N" << 16602144 << 0;
1869         zoneNumber++;
1870     }
1871 
1872     // Northern hemisphere
1873     addRow() << qreal(-180.0)   << qreal(15)        << 1  << "P" << 17734904 << 166051369;
1874     addRow() << qreal(0)        << qreal(60.5)      << 31 << "V" << 33523714 << 671085271;
1875     addRow() << qreal(150.567)  << qreal(75.123)    << 56 << "X" << 43029080 << 833876115;
1876 
1877     // Southern hemisphere
1878     addRow() << qreal(-3.5)     << qreal(-50)       << 30 << "F" << 46416654 << 446124952;
1879     addRow() << qreal(22.56)    << qreal(-62.456)   << 34 << "E" << 58047905 << 307404780;
1880 
1881     // Exceptions
1882 
1883     // North pole (no zone associated, so it returns 0)
1884     addRow() << qreal(-100.0)   << qreal(85.0)      << 0  << "Y" << 49026986 << 943981733;
1885     addRow() << qreal(100.0)    << qreal(85.0)      << 0  << "Z" << 50973014 << 943981733;
1886 
1887     // South pole (no zone associated, so it returns 0)
1888     addRow() << qreal(-100.0)   << qreal(-85.0)     << 0  << "A" << 49026986 << 56018267;
1889     addRow() << qreal(100.0)    << qreal(-85.0)     << 0  << "B" << 50973014 << 56018267;
1890 
1891     // Stavanger, in southwestern Norway, is in zone 32
1892     addRow() << qreal(5.73)     << qreal(58.97)     << 32 << "V" << 31201538 << 654131013;
1893     // Same longitude, at the equator, is in zone 31
1894     addRow() << qreal(5.73)     << qreal(0.0)       << 31 << "N" << 80389643 << 0;
1895 
1896     // Svalbard is in zone 33
1897     addRow() << qreal(10.55)    << qreal(78.88)     << 33 << "X" << 40427848 << 876023047;
1898     // Same longitude, at the equator, is in zone 32
1899     addRow() << qreal(10.55)    << qreal(0.0)       << 32 << "N" << 67249738 << 0;
1900 }
1901 
1902 /*
1903  * test UTM-related functions:
1904  *     - utmZone()
1905  *     - utmLatitudeBand()
1906  *     - utmEasting()
1907  *     - utmNorthing()
1908  */
1909 void TestGeoDataCoordinates::testUTM(){
1910     QFETCH(qreal, lon);
1911     QFETCH(qreal, lat);
1912     QFETCH(int, zone);
1913     QFETCH(QString, latitudeBand);
1914     QFETCH(int, easting);
1915     QFETCH(int, northing);
1916 
1917     GeoDataCoordinates coordinates;
1918     coordinates.set(lon, lat, 0, GeoDataCoordinates::Degree);
1919 
1920     QCOMPARE(coordinates.utmZone(), zone);
1921     QCOMPARE(coordinates.utmLatitudeBand(), latitudeBand);
1922 
1923     /* Comparing integers is safer than comparing qreals. As the expected
1924      * values are expressed in centimeters, the actual values are converted
1925      * to this unit.
1926      */
1927     int actualEasting = qRound( 100.0 * coordinates.utmEasting() );
1928     int actualNorthing = qRound( 100.0 * coordinates.utmNorthing() );
1929 
1930     QCOMPARE( actualEasting, easting );
1931     QCOMPARE( actualNorthing, northing );
1932 }
1933 
1934 QTEST_MAIN(TestGeoDataCoordinates)
1935 #include "TestGeoDataCoordinates.moc"
1936 
1937 
1938