From: Sami Rämö Date: Wed, 14 Jul 2010 14:34:57 +0000 (+0300) Subject: Implemented GeoCoordinate::convertFrom, iterated unit tests X-Git-Tag: v2.0b-1~155 X-Git-Url: http://git.maemo.org/git/?a=commitdiff_plain;h=3098db5b50f527c9caa4a83d257dc60e7cab550e;p=situare Implemented GeoCoordinate::convertFrom, iterated unit tests - implemented GeoCoordinate::convertFrom(SceneCoordinate) and unit tests for it - modified SceneCoordinate::convertFrom(GeoCoordinate); removed assertions and implemented normalizing the x value to be always inside the map - iterated/fixed unit tests for SceneCoordinate; added formatted print for failing double values, added more test data into conversion tests, comparing only integer part of the result --- diff --git a/src/coordinates/geocoordinate.cpp b/src/coordinates/geocoordinate.cpp index 1fcdb31..f58697a 100644 --- a/src/coordinates/geocoordinate.cpp +++ b/src/coordinates/geocoordinate.cpp @@ -21,6 +21,10 @@ #include +/// @todo remove mapcommon.h include after refactored the convertFrom() +#include "map/mapcommon.h" +#include "scenecoordinate.h" + #include "geocoordinate.h" GeoCoordinate::GeoCoordinate() : @@ -37,6 +41,45 @@ GeoCoordinate::GeoCoordinate(double latitude, double longitude) : qDebug() << __PRETTY_FUNCTION__; } +GeoCoordinate::GeoCoordinate(SceneCoordinate &coordinate) +{ + qDebug() << __PRETTY_FUNCTION__; + + convertFrom(coordinate); +} + +void GeoCoordinate::convertFrom(const SceneCoordinate &coordinate) +{ + qDebug() << __PRETTY_FUNCTION__; + + const int zoomLevel = 18; // replacing the parameter temporarily + + double tileFactor = 1 << (MAX_MAP_ZOOM_LEVEL - zoomLevel); + double xFactor = (coordinate.x() / (TILE_SIZE_X*tileFactor)); + double yFactor = (coordinate.y() / (TILE_SIZE_Y*tileFactor)); + + tileFactor = 1 << zoomLevel; + double longitude = xFactor / tileFactor * 360.0 - 180; + + double n = M_PI - 2.0 * M_PI * yFactor / tileFactor; + double latitude = 180.0 / M_PI * atan(0.5 * (exp(n) - exp(-n))); + +// return QPointF(longitude, latitude); + setLatitude(latitude); + setLongitude(longitude); +} + +//double tilex2long(int x, int z) +//{ +// return x / pow(2.0, z) * 360.0 - 180; +//} + +//double tiley2lat(int y, int z) +//{ +// double n = M_PI - 2.0 * M_PI * y / pow(2.0, z); +// return 180.0 / M_PI * atan(0.5 * (exp(n) - exp(-n))); +//} + bool GeoCoordinate::isNull() const { qDebug() << __PRETTY_FUNCTION__; diff --git a/src/coordinates/geocoordinate.h b/src/coordinates/geocoordinate.h index c31f9bd..52a6562 100644 --- a/src/coordinates/geocoordinate.h +++ b/src/coordinates/geocoordinate.h @@ -26,6 +26,8 @@ #include #include +class SceneCoordinate; + /** * @brief Geographic coordinate * @@ -48,6 +50,17 @@ public: GeoCoordinate(double latitude, double longitude); /** + * @brief Constructs a coordinate with values converted from the given SceneCoordinate + * + * @param coordinate Scene coordinate + */ + GeoCoordinate(SceneCoordinate &coordinate); + +/******************************************************************************* + * MEMBER FUNCTIONS AND SLOTS + ******************************************************************************/ +public: + /** * @brief Check if coordinate is (0.0, 0.0) * * @returns True if both latitude and longitude are 0.0, otherwise false @@ -83,6 +96,17 @@ public: void setLongitude(double longitude); private: + /** + * @brief Convert values from SceneCoordinate + * + * @param coordinate Scene coordinate + */ + void convertFrom(const SceneCoordinate &coordinate); + +/******************************************************************************* + * DATA MEMBERS + ******************************************************************************/ +private: double m_latitude; ///< Latitude value double m_longitude; ///< Longitude value }; diff --git a/src/coordinates/scenecoordinate.cpp b/src/coordinates/scenecoordinate.cpp index 48f5df7..923ad90 100644 --- a/src/coordinates/scenecoordinate.cpp +++ b/src/coordinates/scenecoordinate.cpp @@ -51,22 +51,15 @@ void SceneCoordinate::convertFrom(const GeoCoordinate &coordinate) { qDebug() << __PRETTY_FUNCTION__; - Q_ASSERT(coordinate.longitude() >= MIN_LONGITUDE); - Q_ASSERT(coordinate.longitude() <= MAX_LONGITUDE); - Q_ASSERT(coordinate.latitude() >= MIN_LATITUDE); - Q_ASSERT(coordinate.latitude() <= MAX_LATITUDE); - - // calculate x & y positions in the map (0..1) double worldX = static_cast((coordinate.longitude() + 180.0) / 360.0); double worldY = static_cast((1.0 - log(tan(coordinate.latitude() * M_PI / 180.0) + 1.0 / cos(coordinate.latitude() * M_PI / 180.0)) / M_PI) / 2.0); - setX(worldX * MAX_TILES_PER_SIDE * TILE_SIZE_X); - setY(worldY * MAX_TILES_PER_SIDE * TILE_SIZE_Y); + m_x = worldX * MAX_TILES_PER_SIDE * TILE_SIZE_X; + m_y = worldY * MAX_TILES_PER_SIDE * TILE_SIZE_Y; - Q_ASSERT((x() >= MAP_MIN_PIXEL_X) && (x() <= MAP_MAX_PIXEL_X)); - Q_ASSERT((y() >= MAP_MIN_PIXEL_Y) && (y() <= MAP_MAX_PIXEL_Y)); + normalize(m_x, MAP_MIN_PIXEL_X, MAP_MAX_PIXEL_X); } bool SceneCoordinate::isNull() const @@ -79,6 +72,18 @@ bool SceneCoordinate::isNull() const return false; } +void SceneCoordinate::normalize(double &value, int min, int max) +{ + qDebug() << __PRETTY_FUNCTION__; + Q_ASSERT_X(max >= min, "parameters", "max can't be smaller than min"); + + while (int(value) < min) + value += max - min + 1; + + while (int(value) > max) + value -= max - min + 1; +} + double SceneCoordinate::x() const { qDebug() << __PRETTY_FUNCTION__; diff --git a/src/coordinates/scenecoordinate.h b/src/coordinates/scenecoordinate.h index 7c172fd..3d06728 100644 --- a/src/coordinates/scenecoordinate.h +++ b/src/coordinates/scenecoordinate.h @@ -52,6 +52,8 @@ public: /** * @brief Constructs a coordinate with values converted from the given GeoCoordinate * + * Uses convertFrom() method. + * * @param coordinate Geological coordinate */ SceneCoordinate(GeoCoordinate &coordinate); @@ -99,10 +101,33 @@ private: /** * @brief Convert values from GeoCoordinate * + * Does run normalize() for the x value after the conversion to make sure that the result + * is inside the allowed map pixel values. + * + * In horizontal direction: + * -180º equals scene pixel 0 (first scene pixel) + * +180º equals -180º + * + * scene has 2^18 * 256 - 1 = 67108864 pixels per side + * one pixel width is 360º / 67108864 = 0.00000536441802978516º + * so the last scene pixel is 180º - 0.00000536441802978516º = 179.99999463558197021484º * + * * @param coordinate Geological coordinate */ void convertFrom(const GeoCoordinate &coordinate); + /** + * @brief Translate integer part of the given value between min and max + * + * If given value is not inside the given range (min <= value <= max), then the allowed range + * is adder or subtracted until the value does fit in the range. Only integer part is compared. + * + * @param value Value to be normalized + * @param min Minimum allowed value + * @param max Maximum allowed value + */ + void normalize(double &value, int min, int max); + /******************************************************************************* * DATA MEMBERS ******************************************************************************/ diff --git a/tests/coordinates/geocoordinate/geocoordinate.pro b/tests/coordinates/geocoordinate/geocoordinate.pro index 93043a9..ab0685c 100644 --- a/tests/coordinates/geocoordinate/geocoordinate.pro +++ b/tests/coordinates/geocoordinate/geocoordinate.pro @@ -15,13 +15,16 @@ TEMPLATE = app SOURCES += testgeocoordinate.cpp \ - ../../../src/coordinates/geocoordinate.cpp + ../../../src/coordinates/geocoordinate.cpp \ + ../../../src/coordinates/scenecoordinate.cpp DEFINES += SRCDIR=\\\"$$PWD/\\\" INCLUDEPATH += . \ ../../../src/ HEADERS += \ - ../../../src/coordinates/geocoordinate.h + ../../../src/coordinates/geocoordinate.h \ + ../../../src/coordinates/scenecoordinate.h \ + ../../../src/map/mapcommon.h DEFINES += QT_NO_DEBUG_OUTPUT diff --git a/tests/coordinates/geocoordinate/testgeocoordinate.cpp b/tests/coordinates/geocoordinate/testgeocoordinate.cpp index efeb59e..0c95012 100644 --- a/tests/coordinates/geocoordinate/testgeocoordinate.cpp +++ b/tests/coordinates/geocoordinate/testgeocoordinate.cpp @@ -22,26 +22,37 @@ #include #include +#include "coordinates/scenecoordinate.h" +#include "map/mapcommon.h" + #include "coordinates/geocoordinate.h" const double LATITUDE = 12.345678; const double LONGITUDE = -89.765432; +const double ONE_SCENE_PIXEL_WIDTH_IN_DEGREES = 0.00000536441802978516; + class TestGeoCoordinate : public QObject { Q_OBJECT -public: - TestGeoCoordinate(); - private Q_SLOTS: void constructors(); void isNull(); + void conversion(); + void conversion_data(); void settersAndGetters(); }; -TestGeoCoordinate::TestGeoCoordinate() -{ +// for formatting the output of double valuest into the test log +namespace QTest { + template<> + char *toString(const double &number) + { + QByteArray ba; + ba += QByteArray::number(number, 'f', 9); + return qstrdup(ba.data()); + } } void TestGeoCoordinate::constructors() @@ -52,6 +63,37 @@ void TestGeoCoordinate::constructors() GeoCoordinate coordinate2(LATITUDE, LONGITUDE); QCOMPARE(coordinate2.latitude(), LATITUDE); QCOMPARE(coordinate2.longitude(), LONGITUDE); + + // NOTE: constructor with conversion from GeoCoordinate is tested in conversion() test slot +} + +void TestGeoCoordinate::conversion() +{ + // allow rounding error of one tenth of one scene pixel + const double MAX_ERROR = ONE_SCENE_PIXEL_WIDTH_IN_DEGREES * 0.1; + + QFETCH(SceneCoordinate, sceneCoordinate); + QFETCH(GeoCoordinate, result); + + GeoCoordinate geoCoordinate(sceneCoordinate); + + QVERIFY(qAbs(geoCoordinate.latitude() - result.latitude()) < MAX_ERROR); + QVERIFY(qAbs(geoCoordinate.longitude() - result.longitude()) < MAX_ERROR); +} + +void TestGeoCoordinate::conversion_data() +{ + QTest::addColumn("sceneCoordinate"); + QTest::addColumn("result"); + + QTest::newRow("top left") << SceneCoordinate(MAP_MIN_PIXEL_X, MAP_MIN_PIXEL_Y) + << GeoCoordinate(MAX_LATITUDE, MIN_LONGITUDE); + + const double LAST_SCENE_HORIZONTAL_PIXEL_LONGITUDE = MAX_LONGITUDE + - ONE_SCENE_PIXEL_WIDTH_IN_DEGREES; + + QTest::newRow("bottom right") << SceneCoordinate(MAP_MAX_PIXEL_X, MAP_MAX_PIXEL_Y) + << GeoCoordinate(MIN_LATITUDE, LAST_SCENE_HORIZONTAL_PIXEL_LONGITUDE); } void TestGeoCoordinate::isNull() diff --git a/tests/coordinates/scenecoordinate/scenecoordinate.pro b/tests/coordinates/scenecoordinate/scenecoordinate.pro index 8c8257e..06a39ab 100644 --- a/tests/coordinates/scenecoordinate/scenecoordinate.pro +++ b/tests/coordinates/scenecoordinate/scenecoordinate.pro @@ -21,7 +21,8 @@ DEFINES += SRCDIR=\\\"$$PWD/\\\" HEADERS += \ ../../../src/coordinates/scenecoordinate.h \ - ../../../src/coordinates/geocoordinate.h + ../../../src/coordinates/geocoordinate.h \ + ../../../src/map/mapcommon.h INCLUDEPATH += . \ ../../../src/ diff --git a/tests/coordinates/scenecoordinate/testscenecoordinate.cpp b/tests/coordinates/scenecoordinate/testscenecoordinate.cpp index a607c96..6767e25 100644 --- a/tests/coordinates/scenecoordinate/testscenecoordinate.cpp +++ b/tests/coordinates/scenecoordinate/testscenecoordinate.cpp @@ -42,6 +42,17 @@ private Q_SLOTS: void settersAndGetters(); }; +// for formatting the output of double valuest into the test log +namespace QTest { + template<> + char *toString(const double &number) + { + QByteArray ba; + ba += QByteArray::number(number, 'f', 9); + return qstrdup(ba.data()); + } +} + void TestSceneCoordinate::constructors() { SceneCoordinate coordinate; @@ -61,8 +72,12 @@ void TestSceneCoordinate::conversion() SceneCoordinate sceneCoordinate(geoCoordinate); - QCOMPARE(sceneCoordinate.x(), result.x()); - QCOMPARE(sceneCoordinate.y(), result.y()); + // Comparison is done using only the integer parts because data type of the scene coordinate + // values is double so the result is not exact pixel value but is one containing a fractional + // part. Also the rounding errors below one pixel are insignificant. + + QCOMPARE(int(sceneCoordinate.x()), int(result.x())); + QCOMPARE(int(sceneCoordinate.y()), int(result.y())); } void TestSceneCoordinate::conversion_data() @@ -70,13 +85,23 @@ void TestSceneCoordinate::conversion_data() QTest::addColumn("geoCoordinate"); QTest::addColumn("result"); - QTest::newRow("top left") << GeoCoordinate(MAX_LATITUDE, MIN_LONGITUDE) - << SceneCoordinate(0, 0); + QTest::newRow("top left pixel") << GeoCoordinate(MAX_LATITUDE, MIN_LONGITUDE) + << SceneCoordinate(0, 0); + + const double ONE_SCENE_PIXEL_WIDTH_IN_DEGREES = 0.00000536441802978516; + const double LAST_SCENE_HORIZONTAL_PIXEL_LONGITUDE = MAX_LONGITUDE + - ONE_SCENE_PIXEL_WIDTH_IN_DEGREES; + QTest::newRow("bottom right pixel") + << GeoCoordinate(MIN_LATITUDE, LAST_SCENE_HORIZONTAL_PIXEL_LONGITUDE) + << SceneCoordinate(MAP_MAX_PIXEL_X, MAP_MAX_PIXEL_Y); + + QTest::newRow("southeast corner with 180 degrees longitude") + << GeoCoordinate(MIN_LATITUDE, MAX_LONGITUDE) + << SceneCoordinate(MAP_MIN_PIXEL_X, MAP_MAX_PIXEL_Y); - int x = (1 << MAX_MAP_ZOOM_LEVEL) * TILE_SIZE_X; - int y = (1 << MAX_MAP_ZOOM_LEVEL) * TILE_SIZE_Y; - QTest::newRow("bottom right") << GeoCoordinate(MIN_LATITUDE, MAX_LONGITUDE) - << SceneCoordinate(x, y); + QTest::newRow("southeast corner just little over west edge of the map") + << GeoCoordinate(MIN_LATITUDE, MIN_LONGITUDE - ONE_SCENE_PIXEL_WIDTH_IN_DEGREES) + << SceneCoordinate(MAP_MAX_PIXEL_X, MAP_MAX_PIXEL_Y); } void TestSceneCoordinate::isNull()