#include <QDebug>
+/// @todo remove mapcommon.h include after refactored the convertFrom()
+#include "map/mapcommon.h"
+#include "scenecoordinate.h"
+
#include "geocoordinate.h"
GeoCoordinate::GeoCoordinate() :
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__;
#include <QDebug>
#include <QMetaType>
+class SceneCoordinate;
+
/**
* @brief Geographic coordinate
*
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
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
};
{
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<double>((coordinate.longitude() + 180.0) / 360.0);
double worldY = static_cast<double>((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
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__;
/**
* @brief Constructs a coordinate with values converted from the given GeoCoordinate
*
+ * Uses convertFrom() method.
+ *
* @param coordinate Geological coordinate
*/
SceneCoordinate(GeoCoordinate &coordinate);
/**
* @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
******************************************************************************/
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
#include <QtCore/QString>
#include <QtTest/QtTest>
+#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()
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>("sceneCoordinate");
+ QTest::addColumn<GeoCoordinate>("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()
HEADERS += \
../../../src/coordinates/scenecoordinate.h \
- ../../../src/coordinates/geocoordinate.h
+ ../../../src/coordinates/geocoordinate.h \
+ ../../../src/map/mapcommon.h
INCLUDEPATH += . \
../../../src/
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;
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()
QTest::addColumn<GeoCoordinate>("geoCoordinate");
QTest::addColumn<SceneCoordinate>("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()