diff --git a/src/lib/marble/geodata/scene/GeoSceneAbstractTileProjection.cpp b/src/lib/marble/geodata/scene/GeoSceneAbstractTileProjection.cpp index 2f68b7045..642b6b00a 100644 --- a/src/lib/marble/geodata/scene/GeoSceneAbstractTileProjection.cpp +++ b/src/lib/marble/geodata/scene/GeoSceneAbstractTileProjection.cpp @@ -1,90 +1,80 @@ /* Copyright 2016 Friedrich W. H. Kossebau This file is part of the KDE project This library is free software you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License aint with this library see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "GeoSceneAbstractTileProjection.h" #include "GeoDataLatLonBox.h" #include namespace Marble { class GeoSceneAbstractTileProjectionPrivate { public: GeoSceneAbstractTileProjectionPrivate(); public: int levelZeroColumns; int levelZeroRows; }; GeoSceneAbstractTileProjectionPrivate::GeoSceneAbstractTileProjectionPrivate() : levelZeroColumns(1) , levelZeroRows(1) { } GeoSceneAbstractTileProjection::GeoSceneAbstractTileProjection() : d_ptr(new GeoSceneAbstractTileProjectionPrivate()) { } GeoSceneAbstractTileProjection::~GeoSceneAbstractTileProjection() { } int GeoSceneAbstractTileProjection::levelZeroColumns() const { return d_ptr->levelZeroColumns; } void GeoSceneAbstractTileProjection::setLevelZeroColumns(int levelZeroColumns) { d_ptr->levelZeroColumns = levelZeroColumns; } int GeoSceneAbstractTileProjection::levelZeroRows() const { return d_ptr->levelZeroRows; } void GeoSceneAbstractTileProjection::setLevelZeroRows(int levelZeroRows) { d_ptr->levelZeroRows = levelZeroRows; } -GeoDataLatLonBox GeoSceneAbstractTileProjection::geoCoordinates(int zoomLevel, int x, int y) const -{ - qreal west, north, east, south; - - geoCoordinates(zoomLevel, x, y, west, north); - geoCoordinates(zoomLevel, x + 1, y + 1, east, south); - - return GeoDataLatLonBox(north, south, east, west); -} - GeoDataLatLonBox GeoSceneAbstractTileProjection::geoCoordinates(const TileId &tileId) const { return geoCoordinates(tileId.zoomLevel(), tileId.x(), tileId.y()); } } diff --git a/src/lib/marble/geodata/scene/GeoSceneAbstractTileProjection.h b/src/lib/marble/geodata/scene/GeoSceneAbstractTileProjection.h index fd7fa8800..b45b4355e 100644 --- a/src/lib/marble/geodata/scene/GeoSceneAbstractTileProjection.h +++ b/src/lib/marble/geodata/scene/GeoSceneAbstractTileProjection.h @@ -1,139 +1,125 @@ /* Copyright 2016 Friedrich W. H. Kossebau This file is part of the KDE project This library is free software you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License aint with this library see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef MARBLE_GEOSCENEABSTRACTTILEPROJECTION_H #define MARBLE_GEOSCENEABSTRACTTILEPROJECTION_H #include "geodata_export.h" #include #include namespace Marble { class GeoSceneAbstractTileProjectionPrivate; class GeoDataLatLonBox; class TileId; /** * @short A base class for projections between tile indizes and geo coordinates in Marble. * * For map tiling with indizes in x and y dimensions and 1 or multiple zoomlevels. * The lowest zoomlevel is 0. */ class GEODATA_EXPORT GeoSceneAbstractTileProjection { public: enum Type { Equirectangular, Mercator }; /** * @brief Construct a new GeoSceneAbstractTileProjection. */ GeoSceneAbstractTileProjection(); virtual ~GeoSceneAbstractTileProjection(); public: virtual GeoSceneAbstractTileProjection::Type type() const = 0; /** * @return the number of tiles on level 0 in x dimension */ int levelZeroColumns() const; /** * @brief Sets the number of tiles on level 0 in x dimension * * @param levelZeroColumns new number of tiles on level 0 in x dimension * * Default value of the levelZeroColumns property is 1. */ void setLevelZeroColumns(int levelZeroColumns); /** * @return the number of tiles on level 0 in y dimension */ int levelZeroRows() const; /** * @brief Sets the number of tiles on level 0 in y dimension * * @param levelZeroColumns new number of tiles on level 0 in y dimension * * Default value of the levelZeroRows property is 1. */ void setLevelZeroRows(int levelZeroRows); /** * @brief Get the tile indexes which cover the given geographical box. * If @p latLonBox or @p zoomLevel have values out-of-bounds, the behaviour is undefined. * * @param latLonBox the geo coordinates of the requested tiles * @param zoomLevel the zoomlevel of the requested tiles * * @return range of tile indexes covering given geographical box at given zoom level */ virtual QRect tileIndexes(const GeoDataLatLonBox &latLonBox, int zoomLevel) const = 0; - /** - * @brief Get the north-west geo coordinates corresponding to a tile. - * If @p x, @p y or @p zoomLevel have values out-of-bounds, the behaviour is undefined. - * - * @param zoomLevel the zoomlevel of the tile - * @param x the x index of the tile - * @param y the y index of the tile - * @param westernTileEdgeLon the longitude angle in radians of the western tile edge tis returned through this parameter - * @param northernTileEdgeLat the latitude angle in radians of the northern tile edge is returned through this parameter - */ - virtual void geoCoordinates(int zoomLevel, - int x, int y, - qreal& westernTileEdgeLon, qreal& northernTileEdgeLat) const = 0; - /** * @brief Get the boundary geo coordinates corresponding to a tile. * If @p x, @p y or @p zoomLevel have values out-of-bounds, the behaviour is undefined. * * @param zoomLevel the zoomlevel of the tile * @param x the x index of the tile * @param y the y index of the tile * * @return geographic bounding box covered by the given tile */ - GeoDataLatLonBox geoCoordinates(int zoomLevel, int x, int y) const; + virtual GeoDataLatLonBox geoCoordinates(int zoomLevel, int x, int y) const = 0; /** * @brief Get the boundary geo coordinates corresponding to a tile. * If @p tildId has values out-of-bounds, the behaviour is undefined. * * @param tileId the id of the tile * * @return geographic bounding box covered by the given tile */ GeoDataLatLonBox geoCoordinates(const TileId &tileId) const; private: Q_DISABLE_COPY(GeoSceneAbstractTileProjection) const QScopedPointer d_ptr; }; } #endif diff --git a/src/lib/marble/geodata/scene/GeoSceneEquirectTileProjection.cpp b/src/lib/marble/geodata/scene/GeoSceneEquirectTileProjection.cpp index 74d1386f5..96a4abc43 100644 --- a/src/lib/marble/geodata/scene/GeoSceneEquirectTileProjection.cpp +++ b/src/lib/marble/geodata/scene/GeoSceneEquirectTileProjection.cpp @@ -1,145 +1,146 @@ /* Copyright 2016 Friedrich W. H. Kossebau This file is part of the KDE project This library is free software you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License aint with this library see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "GeoSceneEquirectTileProjection.h" #include #include namespace Marble { GeoSceneEquirectTileProjection::GeoSceneEquirectTileProjection() { } GeoSceneEquirectTileProjection::~GeoSceneEquirectTileProjection() { } GeoSceneAbstractTileProjection::Type GeoSceneEquirectTileProjection::type() const { return Equirectangular; } static inline unsigned int lowerBoundTileIndex(qreal baseTileIndex) { const qreal floorBaseTileIndex = floor(baseTileIndex); unsigned int tileIndex = static_cast(floorBaseTileIndex); return (baseTileIndex == floorBaseTileIndex) ? tileIndex-1 : tileIndex; } static inline unsigned int upperBoundTileIndex(qreal baseTileIndex) { return (unsigned int)floor(baseTileIndex); } static inline qreal baseTileXFromLon(qreal lon, unsigned int tileCount) { return 0.5 * (lon / M_PI + 1.0) * tileCount; } static inline qreal baseTileYFromLat(qreal lat, unsigned int tileCount) { return (0.5 - lat / M_PI) * tileCount; } // on tile borders selects the tile to the east static inline unsigned int eastBoundTileXFromLon(qreal lon, unsigned int tileCount) { // special casing tile-map end if (lon == M_PI) { return 0; } return upperBoundTileIndex(baseTileXFromLon(lon, tileCount)); } // on tile borders selects the tile to the west static inline unsigned int westBoundTileXFromLon(qreal lon, unsigned int tileCount) { // special casing tile-map end if (lon == -M_PI) { return tileCount-1; } return lowerBoundTileIndex(baseTileXFromLon(lon, tileCount)); } // on tile borders selects the tile to the south static inline unsigned int southBoundTileYFromLat(qreal lat, unsigned int tileCount) { // special casing tile-map end if (lat == -M_PI*0.5) { return 0; } return upperBoundTileIndex(baseTileYFromLat(lat, tileCount)); } // on tile borders selects the tile to the north static inline unsigned int northBoundTileYFromLat(qreal lat, unsigned int tileCount) { // special casing tile-map end if (lat == M_PI*0.5) { return tileCount-1; } return lowerBoundTileIndex(baseTileYFromLat(lat, tileCount)); } QRect GeoSceneEquirectTileProjection::tileIndexes(const GeoDataLatLonBox &latLonBox, int zoomLevel) const { const unsigned int xTileCount = (1 << zoomLevel) * levelZeroColumns(); const int westX = eastBoundTileXFromLon(latLonBox.west(), xTileCount); const int eastX = westBoundTileXFromLon(latLonBox.east(), xTileCount); const unsigned int yTileCount = (1 << zoomLevel) * levelZeroRows(); const int northY = southBoundTileYFromLat(latLonBox.north(), yTileCount); const int southY = northBoundTileYFromLat(latLonBox.south(), yTileCount); return QRect(QPoint(westX, northY), QPoint(eastX, southY)); } -void GeoSceneEquirectTileProjection::geoCoordinates(int zoomLevel, - int x, int y, - qreal& westernTileEdgeLon, qreal& northernTileEdgeLat) const +GeoDataLatLonBox GeoSceneEquirectTileProjection::geoCoordinates(int zoomLevel, int x, int y) const { - qreal radius = (1 << zoomLevel) * levelZeroColumns() / 2.0; + const qreal radiusX = (1 << zoomLevel) * levelZeroColumns() / 2.0; + const qreal radiusY = (1 << zoomLevel) * levelZeroRows() / 2.0; - westernTileEdgeLon = (x - radius ) / radius * M_PI; + const qreal west = (x - radiusX) / radiusX * M_PI; + const qreal north = (radiusY - y) / radiusY * M_PI / 2.0; - radius = (1 << zoomLevel) * levelZeroRows() / 2.0; + const qreal east = ((x + 1) - radiusX) / radiusX * M_PI; + const qreal south = (radiusY - (y + 1)) / radiusY * M_PI / 2.0; - northernTileEdgeLat = (radius - y) / radius * M_PI / 2.0; + return GeoDataLatLonBox(north, south, east, west); } } diff --git a/src/lib/marble/geodata/scene/GeoSceneEquirectTileProjection.h b/src/lib/marble/geodata/scene/GeoSceneEquirectTileProjection.h index ea600298c..187da69bf 100644 --- a/src/lib/marble/geodata/scene/GeoSceneEquirectTileProjection.h +++ b/src/lib/marble/geodata/scene/GeoSceneEquirectTileProjection.h @@ -1,77 +1,75 @@ /* Copyright 2016 Friedrich W. H. Kossebau This file is part of the KDE project This library is free software you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License aint with this library see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef MARBLE_GEOSCENEEQUIRECTTILEPROJECTION_H #define MARBLE_GEOSCENEEQUIRECTTILEPROJECTION_H #include "GeoSceneAbstractTileProjection.h" namespace Marble { /** * Convertes the x and y indizes of tiles to and from geo coordinates. * For tiles of maps in Equirectangular projection. * * Tiles do have the same width and the same height per zoomlevel. * The number of tiles per dimension is twice that of the previous lower zoomlevel. * The indexing is done in x dimension eastwards, with the first tiles beginning at -180 degree * and an x value of 0 and the last tiles ending at +180 degree, * in y dimension southwards with the first tiles beginning at +90 degree and a y value of 0 * and the last tiles ending at -90 degree. */ class GEODATA_EXPORT GeoSceneEquirectTileProjection : public GeoSceneAbstractTileProjection { public: /** * @brief Construct a new GeoSceneEquirectTileProjection. */ GeoSceneEquirectTileProjection(); ~GeoSceneEquirectTileProjection() override; public: /** * @copydoc */ GeoSceneAbstractTileProjection::Type type() const override; /** * @copydoc */ QRect tileIndexes(const GeoDataLatLonBox &latLonBox, int zoomLevel) const override; /** * @copydoc */ - void geoCoordinates(int zoomLevel, - int x, int y, - qreal& westernTileEdgeLon, qreal& northernTileEdgeLat) const override; + GeoDataLatLonBox geoCoordinates(int zoomLevel, int x, int y) const override; using GeoSceneAbstractTileProjection::geoCoordinates; private: Q_DISABLE_COPY(GeoSceneEquirectTileProjection) }; } #endif diff --git a/src/lib/marble/geodata/scene/GeoSceneMercatorTileProjection.cpp b/src/lib/marble/geodata/scene/GeoSceneMercatorTileProjection.cpp index 1203e883d..9522807c1 100644 --- a/src/lib/marble/geodata/scene/GeoSceneMercatorTileProjection.cpp +++ b/src/lib/marble/geodata/scene/GeoSceneMercatorTileProjection.cpp @@ -1,165 +1,169 @@ /* Copyright 2016 Friedrich W. H. Kossebau This file is part of the KDE project This library is free software you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License aint with this library see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "GeoSceneMercatorTileProjection.h" #include #include namespace Marble { GeoSceneMercatorTileProjection::GeoSceneMercatorTileProjection() { } GeoSceneMercatorTileProjection::~GeoSceneMercatorTileProjection() { } GeoSceneAbstractTileProjection::Type GeoSceneMercatorTileProjection::type() const { return Mercator; } static inline unsigned int lowerBoundTileIndex(qreal baseTileIndex) { const qreal floorBaseTileIndex = floor(baseTileIndex); unsigned int tileIndex = static_cast(floorBaseTileIndex); return (baseTileIndex == floorBaseTileIndex) ? tileIndex-1 : tileIndex; } static inline unsigned int upperBoundTileIndex(qreal baseTileIndex) { return (unsigned int)floor(baseTileIndex); } static inline qreal baseTileXFromLon(qreal lon, unsigned int tileCount) { return 0.5 * (lon / M_PI + 1.0) * tileCount; } static inline qreal baseTileYFromLat(qreal latitude, unsigned int tileCount) { // We need to calculate the tile position from the latitude // projected using the Mercator projection. This requires the inverse Gudermannian // function which is only defined between -85°S and 85°N. Therefore in order to // prevent undefined results we need to restrict our calculation. // Using 85.0 instead of some more correct 85.05113, to avoid running into NaN issues. qreal maxAbsLat = 85.0 * DEG2RAD; qreal lat = (qAbs(latitude) > maxAbsLat) ? latitude/qAbs(latitude) * maxAbsLat : latitude; return (0.5 * (1.0 - gdInv(lat) / M_PI) * tileCount); } // on tile borders selects the tile to the east static inline unsigned int eastBoundTileXFromLon(qreal lon, unsigned int tileCount) { // special casing tile-map end if (lon == M_PI) { return 0; } return upperBoundTileIndex(baseTileXFromLon(lon, tileCount)); } // on tile borders selects the tile to the west static inline unsigned int westBoundTileXFromLon(qreal lon, unsigned int tileCount) { // special casing tile-map end if (lon == -M_PI) { return tileCount-1; } return lowerBoundTileIndex(baseTileXFromLon(lon, tileCount)); } // on tile borders selects the tile to the south static inline unsigned int southBoundTileYFromLat(qreal lat, unsigned int tileCount) { // special casing tile-map end if (lat == -M_PI*0.5) { // calculate with normal lat value lat = M_PI * 0.5; } return upperBoundTileIndex(baseTileYFromLat(lat, tileCount)); } // on tile borders selects the tile to the north static inline unsigned int northBoundTileYFromLat(qreal lat, unsigned int tileCount) { // special casing tile-map end if (lat == M_PI*0.5) { // calculate with normal lat value lat = - M_PI * 0.5; } return lowerBoundTileIndex(baseTileYFromLat(lat, tileCount)); } static inline qreal lonFromTileX(unsigned int x, unsigned int tileCount) { return ( (2*M_PI * x) / tileCount - M_PI ); } static inline qreal latFromTileY(unsigned int y, unsigned int tileCount) { return gd(M_PI * (1.0 - (2.0 * y) / tileCount)); } QRect GeoSceneMercatorTileProjection::tileIndexes(const GeoDataLatLonBox &latLonBox, int zoomLevel) const { const unsigned int xTileCount = (1 << zoomLevel) * levelZeroColumns(); const int westX = eastBoundTileXFromLon(latLonBox.west(), xTileCount); const int eastX = westBoundTileXFromLon(latLonBox.east(), xTileCount); const unsigned int yTileCount = (1 << zoomLevel) * levelZeroRows(); const int northY = southBoundTileYFromLat(latLonBox.north(), yTileCount); const int southY = northBoundTileYFromLat(latLonBox.south(), yTileCount); return QRect(QPoint(westX, northY), QPoint(eastX, southY)); } -void GeoSceneMercatorTileProjection::geoCoordinates(int zoomLevel, - int x, int y, - qreal& westernTileEdgeLon, qreal& northernTileEdgeLat) const + +GeoDataLatLonBox GeoSceneMercatorTileProjection::geoCoordinates(int zoomLevel, int x, int y) const { const unsigned int xTileCount = (1 << zoomLevel) * levelZeroColumns(); - westernTileEdgeLon = lonFromTileX(x, xTileCount); - const unsigned int yTileCount = (1 << zoomLevel) * levelZeroRows(); - northernTileEdgeLat = latFromTileY(y, yTileCount); + + const qreal west = lonFromTileX(x, xTileCount); + const qreal north = latFromTileY(y, yTileCount); + + const qreal east = lonFromTileX(x + 1, xTileCount); + const qreal south = latFromTileY(y + 1, yTileCount); + + return GeoDataLatLonBox(north, south, east, west); } } diff --git a/src/lib/marble/geodata/scene/GeoSceneMercatorTileProjection.h b/src/lib/marble/geodata/scene/GeoSceneMercatorTileProjection.h index faaafe016..232759f91 100644 --- a/src/lib/marble/geodata/scene/GeoSceneMercatorTileProjection.h +++ b/src/lib/marble/geodata/scene/GeoSceneMercatorTileProjection.h @@ -1,81 +1,79 @@ /* Copyright 2016 Friedrich W. H. Kossebau This file is part of the KDE project This library is free software you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License aint with this library see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef MARBLE_GEOSCENEMERCATORTILEPROJECTION_H #define MARBLE_GEOSCENEMERCATORTILEPROJECTION_H #include "GeoSceneAbstractTileProjection.h" namespace Marble { /** * Convertes the x and y indizes of tiles to and from geo coordinates. * For tiles of maps in Mercator projection. * * Tiles do have the same width and the same height per zoomlevel. * The number of tiles per dimension is twice that of the previous lower zoomlevel. * The indexing is done in x dimension eastwards, with the first tiles beginning at -180 degree * and an x value of 0 and the last tiles ending at +180 degree, * in y dimension southwards with the first tiles beginning at +85.05113 degree and a y value of 0 * and the last tiles ending at -85.05113 degree. * * NOTE: The method @c tileIndexes() handles any latitude value >= +85.0 degree as * exactly +85.0 degree and any latitude value <= -85.0 as exactly -85.0 degree. * So for higher zoomlevels the outermost tiles will be masked by that and not included in any results. */ class GEODATA_EXPORT GeoSceneMercatorTileProjection : public GeoSceneAbstractTileProjection { public: /** * @brief Construct a new GeoSceneMercatorTileProjection. */ GeoSceneMercatorTileProjection(); ~GeoSceneMercatorTileProjection() override; public: /** * @copydoc */ GeoSceneAbstractTileProjection::Type type() const override; /** * @copydoc */ QRect tileIndexes(const GeoDataLatLonBox &latLonBox, int zoomLevel) const override; /** * @copydoc */ - void geoCoordinates(int zoomLevel, - int x, int y, - qreal& westernTileEdgeLon, qreal& northernTileEdgeLat) const override; + GeoDataLatLonBox geoCoordinates(int zoomLevel, int x, int y) const override; using GeoSceneAbstractTileProjection::geoCoordinates; private: Q_DISABLE_COPY(GeoSceneMercatorTileProjection) }; } #endif diff --git a/tests/TestTileProjection.cpp b/tests/TestTileProjection.cpp index b9c14cd4b..8a134d974 100644 --- a/tests/TestTileProjection.cpp +++ b/tests/TestTileProjection.cpp @@ -1,522 +1,504 @@ /* Copyright 2016 Friedrich W. H. Kossebau This file is part of the KDE project This library is free software you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License aint with this library see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "TestUtils.h" #include #include #include #include namespace Marble { class TileProjectionTest : public QObject { Q_OBJECT private Q_SLOTS: void testTypeEquirect(); void testTypeMercator(); void testLevelZeroColumnsRowsEquirect(); void testLevelZeroColumnsRowsMercator(); void testTileIndexesEquirect_data(); void testTileIndexesEquirect(); void testTileIndexesMercator_data(); void testTileIndexesMercator(); void testGeoCoordinatesEquirect_data(); void testGeoCoordinatesEquirect(); void testGeoCoordinatesMercator_data(); void testGeoCoordinatesMercator(); private: void testLevelZeroColumnsRows(GeoSceneAbstractTileProjection& projection); }; void TileProjectionTest::testLevelZeroColumnsRows(GeoSceneAbstractTileProjection& projection) { // test default QCOMPARE(projection.levelZeroColumns(), 1); QCOMPARE(projection.levelZeroRows(), 1); // test setting a different value const int levelZeroColumns = 4; const int levelZeroRows = 6; projection.setLevelZeroColumns(levelZeroColumns); projection.setLevelZeroRows(levelZeroRows); QCOMPARE(projection.levelZeroColumns(), levelZeroColumns); QCOMPARE(projection.levelZeroRows(), levelZeroRows); } void TileProjectionTest::testLevelZeroColumnsRowsEquirect() { GeoSceneEquirectTileProjection projection; testLevelZeroColumnsRows(projection); } void TileProjectionTest::testLevelZeroColumnsRowsMercator() { GeoSceneMercatorTileProjection projection; testLevelZeroColumnsRows(projection); } void TileProjectionTest::testTypeEquirect() { GeoSceneEquirectTileProjection projection; QCOMPARE(projection.type(), GeoSceneAbstractTileProjection::Equirectangular); } void TileProjectionTest::testTypeMercator() { GeoSceneMercatorTileProjection projection; QCOMPARE(projection.type(), GeoSceneAbstractTileProjection::Mercator); } void TileProjectionTest::testTileIndexesEquirect_data() { QTest::addColumn("westLon"); QTest::addColumn("northLat"); QTest::addColumn("eastLon"); QTest::addColumn("southLat"); QTest::addColumn("zoomLevel"); QTest::addColumn("expectedTileXWest"); QTest::addColumn("expectedTileYNorth"); QTest::addColumn("expectedTileXEast"); QTest::addColumn("expectedTileYSouth"); // zoomlevel zero: 1 tile // bounds matching the tile map addRow() << qreal(-M_PI) << qreal(+M_PI * 0.5) << qreal(+M_PI) << qreal(-M_PI * 0.5) << 0 << 0 << 0 << 0 << 0; // bounds inside the 1 tile addRow() << qreal(-M_PI*0.5) << qreal(+M_PI * 0.25) << qreal(+M_PI*0.5) << qreal(-M_PI * 0.25) << 0 << 0 << 0 << 0 << 0; // bounds west and north on tile map borders, with normal border values addRow() << qreal(-M_PI) << qreal(+M_PI * 0.5) << qreal(+M_PI*0.5) << qreal(-M_PI * 0.25) << 0 << 0 << 0 << 0 << 0; // bounds west and north on tile map borders, with border values from other map border sides addRow() << qreal(+M_PI) << qreal(-M_PI * 0.5) << qreal(+M_PI*0.5) << qreal(-M_PI * 0.25) << 0 << 0 << 0 << 0 << 0; // zoomlevel 1: 2 tiles per dimension // bounds matching the tile map addRow() << qreal(-M_PI) << qreal(+M_PI * 0.5) << qreal(+M_PI) << qreal(-M_PI * 0.5) << 1 << 0 << 0 << 1 << 1; // bounds inside the 4 tiles addRow() << qreal(-M_PI*0.5) << qreal(+M_PI * 0.25) << qreal(+M_PI*0.5) << qreal(-M_PI * 0.25) << 1 << 0 << 0 << 1 << 1; // bounds matching the most north-west tile, with normal border values addRow() << qreal(-M_PI) << qreal(+M_PI * 0.5) << qreal(0) << qreal(0) << 1 << 0 << 0 << 0 << 0; // bounds matching the most north-west tile, with border values from other map border sides addRow() << qreal(+M_PI) << qreal(-M_PI * 0.5) << qreal(0) << qreal(0) << 1 << 0 << 0 << 0 << 0; // bounds matching the most south-east tile, with normal border values addRow() << qreal(0) << qreal(0) << qreal(+M_PI) << qreal(-M_PI * 0.5) << 1 << 1 << 1 << 1 << 1; // bounds matching the most south-east tile, with border values from other map border sides addRow() << qreal(0) << qreal(0) << qreal(-M_PI) << qreal(+M_PI * 0.5) << 1 << 1 << 1 << 1 << 1; // zoomlevel 9: 2^8==512 tiles per dimension // bounds matching the tile map addRow() << qreal(-M_PI) << qreal(+M_PI * 0.5) << qreal(+M_PI) << qreal(-M_PI * 0.5) << 9 << 0 << 0 << 511 << 511; // bounds inside the outer tiles addRow() << qreal(-M_PI*(511/512.0)) << qreal(+M_PI * 0.5 * (511/512.0)) << qreal(+M_PI*(511/512.0)) << qreal(-M_PI * 0.5 * (511/512.0)) << 9 << 0 << 0 << 511 << 511; // bounds matching the most north-west tile, with normal border values addRow() << qreal(-M_PI) << qreal(+M_PI * 0.5) << qreal(-M_PI*(255/256.0)) << qreal(+M_PI * 0.5 *(255/256.0)) << 9 << 0 << 0 << 0 << 0; // bounds matching the most north-west tile, with border values from other map border sides addRow() << qreal(+M_PI) << qreal(-M_PI * 0.5) << qreal(-M_PI*(255/256.0)) << qreal(+M_PI * 0.5 *(255/256.0)) << 9 << 0 << 0 << 0 << 0; // bounds matching the most south-east tile, with normal border values addRow() << qreal(+M_PI*(255/256.0)) << qreal(-M_PI * 0.5 *(255/256.0)) << qreal(+M_PI) << qreal(-M_PI * 0.5) << 9 << 511 << 511 << 511 << 511; // bounds matching the most south-east tile, with border values from other map border sides addRow() << qreal(+M_PI*(255/256.0)) << qreal(-M_PI * 0.5 *(255/256.0)) << qreal(-M_PI) << qreal(+M_PI * 0.5) << 9 << 511 << 511 << 511 << 511; } void TileProjectionTest::testTileIndexesEquirect() { QFETCH(qreal, westLon); QFETCH(qreal, northLat); QFETCH(qreal, eastLon); QFETCH(qreal, southLat); QFETCH(int, zoomLevel); QFETCH(int, expectedTileXWest); QFETCH(int, expectedTileYNorth); QFETCH(int, expectedTileXEast); QFETCH(int, expectedTileYSouth); GeoDataLatLonBox latLonBox(northLat, southLat, eastLon, westLon); const GeoSceneEquirectTileProjection projection; const QRect rect = projection.tileIndexes(latLonBox, zoomLevel); QCOMPARE(rect.left(), expectedTileXWest); QCOMPARE(rect.top(), expectedTileYNorth); QCOMPARE(rect.right(), expectedTileXEast); QCOMPARE(rect.bottom(), expectedTileYSouth); } void TileProjectionTest::testTileIndexesMercator_data() { QTest::addColumn("westLon"); QTest::addColumn("northLat"); QTest::addColumn("eastLon"); QTest::addColumn("southLat"); QTest::addColumn("zoomLevel"); QTest::addColumn("expectedTileXWest"); QTest::addColumn("expectedTileYNorth"); QTest::addColumn("expectedTileXEast"); QTest::addColumn("expectedTileYSouth"); // zoomlevel zero: 1 tile // bounds matching the tile map up to 90 degree latitude addRow() << qreal(-M_PI) << qreal(+M_PI * 0.5) << qreal(+M_PI) << qreal(-M_PI * 0.5) << 0 << 0 << 0 << 0 << 0; // bounds matching the tile map with 85 degree latitude limit addRow() << qreal(-M_PI) << qreal(85.0 * DEG2RAD) << qreal(+M_PI) << qreal(-85.0 * DEG2RAD) << 0 << 0 << 0 << 0 << 0; // bounds inside the 1 tile addRow() << qreal(-M_PI*0.5) << qreal(+M_PI * 0.25) << qreal(+M_PI*0.5) << qreal(-M_PI * 0.25) << 0 << 0 << 0 << 0 << 0; // bounds west and north on tile map borders, with normal border values addRow() << qreal(-M_PI) << qreal(+M_PI * 0.5) << qreal(+M_PI*0.5) << qreal(-M_PI * 0.25) << 0 << 0 << 0 << 0 << 0; // bounds west and north on tile map borders, with border values from other map border sides addRow() << qreal(+M_PI) << qreal(-M_PI * 0.5) << qreal(+M_PI*0.5) << qreal(-M_PI * 0.25) << 0 << 0 << 0 << 0 << 0; // zoomlevel 1: 2 tiles per dimension // bounds matching the tile map up to 90 degree latitude addRow() << qreal(-M_PI) << qreal(+M_PI * 0.5) << qreal(+M_PI) << qreal(-M_PI * 0.5) << 1 << 0 << 0 << 1 << 1; // bounds matching the tile map with 85 degree latitude limit addRow() << qreal(-M_PI) << qreal(85.0 * DEG2RAD) << qreal(+M_PI) << qreal(-85.0 * DEG2RAD) << 1 << 0 << 0 << 1 << 1; // bounds inside the 4 tiles addRow() << qreal(-M_PI*0.5) << qreal(+M_PI * 0.25) << qreal(+M_PI*0.5) << qreal(-M_PI * 0.25) << 1 << 0 << 0 << 1 << 1; // bounds matching the most north-west tile, with normal border values addRow() << qreal(-M_PI) << qreal(+M_PI * 0.5) << qreal(0) << qreal(0) << 1 << 0 << 0 << 0 << 0; // bounds matching the most north-west tile, with border values from other map border sides addRow() << qreal(+M_PI) << qreal(-M_PI * 0.5) << qreal(0) << qreal(0) << 1 << 0 << 0 << 0 << 0; // bounds matching the most south-east tile, with normal border values addRow() << qreal(0) << qreal(0) << qreal(+M_PI) << qreal(-M_PI * 0.5) << 1 << 1 << 1 << 1 << 1; // bounds matching the most south-east tile, with border values from other map border sides addRow() << qreal(0) << qreal(0) << qreal(-M_PI) << qreal(+M_PI * 0.5) << 1 << 1 << 1 << 1 << 1; // zoomlevel 9: 2^8==512 tiles per dimension // GeoSceneMercatorTileProjection bounds latitude value at +/- 85.0 degree (so not at 85.05113), // which results in some tiles missed at the outer sides. // bounds matching the tile map up to 90 degree latitude addRow() << qreal(-M_PI) << qreal(+M_PI * 0.5) << qreal(+M_PI) << qreal(-M_PI * 0.5) << 9 << 0 << 5 << 511 << 506; // bounds matching the tile map with 85 degree latitude limit addRow() << qreal(-M_PI) << qreal(85.0 * DEG2RAD) << qreal(+M_PI) << qreal(-85.0 * DEG2RAD) << 9 << 0 << 5 << 511 << 506; // bounds inside the outer tiles addRow() << qreal(-M_PI*(511/512.0)) << qreal(+M_PI * 0.5 * (511/512.0)) << qreal(+M_PI*(511/512.0)) << qreal(-M_PI * 0.5 * (511/512.0)) << 9 << 0 << 5 << 511 << 506; // bounds matching the most north-west tile, with normal border values addRow() << qreal(-M_PI) << qreal(+M_PI * 0.5) << qreal(-M_PI*(255/256.0)) << qreal(+M_PI * 0.5 *(255/256.0)) << 9 << 0 << 5 << 0 << 5; // bounds matching the most north-west tile, with border values from other map border sides addRow() << qreal(+M_PI) << qreal(-M_PI * 0.5) << qreal(-M_PI*(255/256.0)) << qreal(+M_PI * 0.5 *(255/256.0)) << 9 << 0 << 5 << 0 << 5; // bounds matching the most south-east tile, with normal border values addRow() << qreal(+M_PI*(255/256.0)) << qreal(-M_PI * 0.5 *(255/256.0)) << qreal(+M_PI) << qreal(-M_PI * 0.5) << 9 << 511 << 506 << 511 << 506; // bounds matching the most south-east tile, with border values from other map border sides addRow() << qreal(+M_PI*(255/256.0)) << qreal(-M_PI * 0.5 *(255/256.0)) << qreal(-M_PI) << qreal(+M_PI * 0.5) << 9 << 511 << 506 << 511 << 506; } void TileProjectionTest::testTileIndexesMercator() { QFETCH(qreal, westLon); QFETCH(qreal, northLat); QFETCH(qreal, eastLon); QFETCH(qreal, southLat); QFETCH(int, zoomLevel); QFETCH(int, expectedTileXWest); QFETCH(int, expectedTileYNorth); QFETCH(int, expectedTileXEast); QFETCH(int, expectedTileYSouth); GeoDataLatLonBox latLonBox(northLat, southLat, eastLon, westLon); const GeoSceneMercatorTileProjection projection; const QRect rect = projection.tileIndexes(latLonBox, zoomLevel); QCOMPARE(rect.left(), expectedTileXWest); QCOMPARE(rect.top(), expectedTileYNorth); QCOMPARE(rect.right(), expectedTileXEast); QCOMPARE(rect.bottom(), expectedTileYSouth); } void TileProjectionTest::testGeoCoordinatesEquirect_data() { QTest::addColumn("tileX"); QTest::addColumn("tileY"); QTest::addColumn("zoomLevel"); QTest::addColumn("expectedWesternTileEdgeLon"); QTest::addColumn("expectedNorthernTileEdgeLat"); QTest::addColumn("expectedEasternTileEdgeLon"); QTest::addColumn("expectedSouthernTileEdgeLat"); // zoomlevel zero: 1 tile addRow() << 0 << 0 << 0 << qreal(-M_PI) << qreal(+M_PI * 0.5) << qreal(+M_PI) << qreal(-M_PI * 0.5); // zoomlevel 1: 2 tiles per dimension addRow() << 0 << 0 << 1 << qreal(-M_PI) << qreal(+M_PI * 0.5) << qreal(0) << qreal(0); addRow() << 0 << 1 << 1 << qreal(-M_PI) << qreal(0) << qreal(0) << qreal(-M_PI * 0.5); addRow() << 1 << 0 << 1 << qreal(0) << qreal(+M_PI * 0.5) << qreal(+M_PI) << qreal(0); addRow() << 1 << 1 << 1 << qreal(0) << qreal(0) << qreal(+M_PI) << qreal(-M_PI * 0.5); // zoomlevel 9: 2^8==512 tiles per dimension addRow() << 0 << 0 << 9 << qreal(-M_PI) << qreal(+M_PI * 0.5) << qreal(-M_PI * (255/256.0)) << qreal(+M_PI * 0.5 * (255/256.0)); addRow() << 0 << 256 << 9 << qreal(-M_PI) << qreal(0) << qreal(-M_PI * (255/256.0)) << qreal(-M_PI * 0.5 * (1/256.0)); addRow() << 256 << 0 << 9 << qreal(0) << qreal(+M_PI * 0.5) << qreal(M_PI * (1/256.0)) << qreal(+M_PI * 0.5 * (255/256.0)); addRow() << 511 << 511 << 9 << qreal(M_PI * (255/256.0)) << qreal(-M_PI * 0.5 * (255/256.0)) << qreal(+M_PI) << qreal(-M_PI * 0.5); } void TileProjectionTest::testGeoCoordinatesEquirect() { QFETCH(int, tileX); QFETCH(int, tileY); QFETCH(int, zoomLevel); QFETCH(qreal, expectedWesternTileEdgeLon); QFETCH(qreal, expectedNorthernTileEdgeLat); QFETCH(qreal, expectedEasternTileEdgeLon); QFETCH(qreal, expectedSouthernTileEdgeLat); - GeoSceneEquirectTileProjection projection; - - qreal westernTileEdgeLon; - qreal northernTileEdgeLat; - - // method variant with zoomLevel, tileX, tileY - projection.geoCoordinates(zoomLevel, tileX, tileY, westernTileEdgeLon, northernTileEdgeLat); - - QCOMPARE(westernTileEdgeLon, expectedWesternTileEdgeLon); - QCOMPARE(northernTileEdgeLat, expectedNorthernTileEdgeLat); + const GeoSceneEquirectTileProjection projection; // method variants with GeoDataLatLonBox const GeoDataLatLonBox latLonBox = projection.geoCoordinates(zoomLevel, tileX, tileY); QCOMPARE(latLonBox.west(), expectedWesternTileEdgeLon); QCOMPARE(latLonBox.north(), expectedNorthernTileEdgeLat); QCOMPARE(latLonBox.east(), expectedEasternTileEdgeLon); QCOMPARE(latLonBox.south(), expectedSouthernTileEdgeLat); TileId tileId(QStringLiteral("testmap"), zoomLevel, tileX, tileY); const GeoDataLatLonBox latLonBox2 = projection.geoCoordinates(tileId); QCOMPARE(latLonBox2.west(), expectedWesternTileEdgeLon); QCOMPARE(latLonBox2.north(), expectedNorthernTileEdgeLat); QCOMPARE(latLonBox2.east(), expectedEasternTileEdgeLon); QCOMPARE(latLonBox2.south(), expectedSouthernTileEdgeLat); } void TileProjectionTest::testGeoCoordinatesMercator_data() { QTest::addColumn("tileX"); QTest::addColumn("tileY"); QTest::addColumn("zoomLevel"); QTest::addColumn("expectedWesternTileEdgeLon"); QTest::addColumn("expectedNorthernTileEdgeLat"); QTest::addColumn("expectedEasternTileEdgeLon"); QTest::addColumn("expectedSouthernTileEdgeLat"); const qreal absMaxLat = DEG2RAD * 85.05113; // zoomlevel zero: 1 tile addRow() << 0 << 0 << 0 << qreal(-M_PI) << qreal(+absMaxLat) << qreal(+M_PI) << qreal(-absMaxLat); // zoomlevel 1: 2 tiles per dimension addRow() << 0 << 0 << 1 << qreal(-M_PI) << qreal(+absMaxLat) << qreal(0) << qreal(0); addRow() << 0 << 1 << 1 << qreal(-M_PI) << qreal(0) << qreal(0) << qreal(-absMaxLat); addRow() << 1 << 0 << 1 << qreal(0) << qreal(+absMaxLat) << qreal(+M_PI) << qreal(0); addRow() << 1 << 1 << 1 << qreal(0) << qreal(0) << qreal(+M_PI) << qreal(-absMaxLat); // zoomlevel 9: 2^8==512 tiles per dimension addRow() << 0 << 0 << 9 << qreal(-M_PI) << qreal(+absMaxLat) << qreal(-M_PI * (255/256.0)) << qreal(+1.48336); addRow() << 0 << 256 << 9 << qreal(-M_PI) << qreal(0) << qreal(-M_PI * (255/256.0)) << qreal(-0.0122715); addRow() << 256 << 0 << 9 << qreal(0) << qreal(+absMaxLat) << qreal(M_PI * (1/256.0)) << qreal(+1.48336); addRow() << 511 << 511 << 9 << qreal(M_PI * (255/256.0)) << qreal(-1.48336) << qreal(+M_PI) << qreal(-absMaxLat); } void TileProjectionTest::testGeoCoordinatesMercator() { QFETCH(int, tileX); QFETCH(int, tileY); QFETCH(int, zoomLevel); QFETCH(qreal, expectedWesternTileEdgeLon); QFETCH(qreal, expectedNorthernTileEdgeLat); QFETCH(qreal, expectedEasternTileEdgeLon); QFETCH(qreal, expectedSouthernTileEdgeLat); - GeoSceneMercatorTileProjection projection; - - qreal westernTileEdgeLon; - qreal northernTileEdgeLat; - - // method variant with zoomLevel, tileX, tileY - projection.geoCoordinates(zoomLevel, tileX, tileY, westernTileEdgeLon, northernTileEdgeLat); - - QCOMPARE(westernTileEdgeLon, expectedWesternTileEdgeLon); - QFUZZYCOMPARE(northernTileEdgeLat, expectedNorthernTileEdgeLat, 0.00001); + const GeoSceneMercatorTileProjection projection; // method variants with GeoDataLatLonBox const GeoDataLatLonBox latLonBox = projection.geoCoordinates(zoomLevel, tileX, tileY); QCOMPARE(latLonBox.west(), expectedWesternTileEdgeLon); QFUZZYCOMPARE(latLonBox.north(), expectedNorthernTileEdgeLat, 0.00001); QCOMPARE(latLonBox.east(), expectedEasternTileEdgeLon); QFUZZYCOMPARE(latLonBox.south(), expectedSouthernTileEdgeLat, 0.00001); TileId tileId(QStringLiteral("testmap"), zoomLevel, tileX, tileY); const GeoDataLatLonBox latLonBox2 = projection.geoCoordinates(tileId); QCOMPARE(latLonBox2.west(), expectedWesternTileEdgeLon); QFUZZYCOMPARE(latLonBox2.north(), expectedNorthernTileEdgeLat, 0.00001); QCOMPARE(latLonBox2.east(), expectedEasternTileEdgeLon); QFUZZYCOMPARE(latLonBox2.south(), expectedSouthernTileEdgeLat, 0.00001); } } // namespace Marble QTEST_MAIN(Marble::TileProjectionTest) #include "TestTileProjection.moc"