File indexing completed on 2024-05-19 03:51:50

0001 /*
0002     SPDX-FileCopyrightText: 2016 Friedrich W. H. Kossebau <kossebau@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "GeoSceneMercatorTileProjection.h"
0008 
0009 #include <GeoDataLatLonBox.h>
0010 #include <MarbleMath.h>
0011 
0012 
0013 namespace Marble
0014 {
0015 
0016 GeoSceneMercatorTileProjection::GeoSceneMercatorTileProjection()
0017 {
0018 }
0019 
0020 
0021 GeoSceneMercatorTileProjection::~GeoSceneMercatorTileProjection()
0022 {
0023 }
0024 
0025 GeoSceneAbstractTileProjection::Type GeoSceneMercatorTileProjection::type() const
0026 {
0027     return Mercator;
0028 }
0029 
0030 
0031 static inline
0032 unsigned int lowerBoundTileIndex(qreal baseTileIndex)
0033 {
0034     const qreal floorBaseTileIndex = floor(baseTileIndex);
0035     unsigned int tileIndex = static_cast<unsigned int>(floorBaseTileIndex);
0036     return (baseTileIndex == floorBaseTileIndex) ? tileIndex-1 : tileIndex;
0037 }
0038 
0039 static inline
0040 unsigned int upperBoundTileIndex(qreal baseTileIndex)
0041 {
0042     return (unsigned int)floor(baseTileIndex);
0043 }
0044 
0045 static inline
0046 qreal baseTileXFromLon(qreal lon, unsigned int tileCount)
0047 {
0048     return 0.5 * (lon / M_PI + 1.0) * tileCount;
0049 }
0050 
0051 static inline
0052 qreal baseTileYFromLat(qreal latitude, unsigned int tileCount)
0053 {
0054     // We need to calculate the tile position from the latitude
0055     // projected using the Mercator projection. This requires the inverse Gudermannian
0056     // function which is only defined between -85°S and 85°N. Therefore in order to
0057     // prevent undefined results we need to restrict our calculation.
0058     // Using 85.0 instead of some more correct 85.05113, to avoid running into NaN issues.
0059     qreal maxAbsLat = 85.0 * DEG2RAD;
0060     qreal lat = (qAbs(latitude) > maxAbsLat) ? latitude/qAbs(latitude) * maxAbsLat : latitude;
0061     return (0.5 * (1.0 - gdInv(lat) / M_PI) * tileCount);
0062 }
0063 
0064 // on tile borders selects the tile to the east
0065 static inline
0066 unsigned int eastBoundTileXFromLon(qreal lon, unsigned int tileCount)
0067 {
0068     // special casing tile-map end
0069     if (lon == M_PI) {
0070         return 0;
0071     }
0072     return upperBoundTileIndex(baseTileXFromLon(lon, tileCount));
0073 }
0074 
0075 // on tile borders selects the tile to the west
0076 static inline
0077 unsigned int westBoundTileXFromLon(qreal lon, unsigned int tileCount)
0078 {
0079     // special casing tile-map end
0080     if (lon == -M_PI) {
0081         return tileCount-1;
0082     }
0083     return lowerBoundTileIndex(baseTileXFromLon(lon, tileCount));
0084 }
0085 
0086 // on tile borders selects the tile to the south
0087 static inline
0088 unsigned int southBoundTileYFromLat(qreal lat, unsigned int tileCount)
0089 {
0090     // special casing tile-map end
0091     if (lat == -M_PI*0.5) {
0092         // calculate with normal lat value
0093         lat = M_PI * 0.5;
0094     }
0095     return upperBoundTileIndex(baseTileYFromLat(lat, tileCount));
0096 }
0097 
0098 // on tile borders selects the tile to the north
0099 static inline
0100 unsigned int northBoundTileYFromLat(qreal lat, unsigned int tileCount)
0101 {
0102     // special casing tile-map end
0103     if (lat == M_PI*0.5) {
0104         // calculate with normal lat value
0105         lat = - M_PI * 0.5;
0106     }
0107     return lowerBoundTileIndex(baseTileYFromLat(lat, tileCount));
0108 }
0109 
0110 
0111 static inline
0112 qreal lonFromTileX(unsigned int x, unsigned int tileCount)
0113 {
0114     return ( (2*M_PI * x) / tileCount - M_PI );
0115 }
0116 
0117 static inline
0118 qreal latFromTileY(unsigned int y, unsigned int tileCount)
0119 {
0120     return gd(M_PI * (1.0 - (2.0 * y) / tileCount));
0121 }
0122 
0123 
0124 QRect GeoSceneMercatorTileProjection::tileIndexes(const GeoDataLatLonBox &latLonBox, int zoomLevel) const
0125 {
0126     const unsigned int xTileCount = (1 << zoomLevel) * levelZeroColumns();
0127 
0128     const int westX =   eastBoundTileXFromLon(latLonBox.west(),  xTileCount);
0129     const int eastX =   westBoundTileXFromLon(latLonBox.east(),  xTileCount);
0130 
0131     const unsigned int yTileCount = (1 << zoomLevel) * levelZeroRows();
0132 
0133     const int northY = southBoundTileYFromLat(latLonBox.north(), yTileCount);
0134     const int southY = northBoundTileYFromLat(latLonBox.south(), yTileCount);
0135 
0136     return QRect(QPoint(westX, northY), QPoint(eastX, southY));
0137 }
0138 
0139 
0140 GeoDataLatLonBox GeoSceneMercatorTileProjection::geoCoordinates(int zoomLevel, int x, int y) const
0141 {
0142     const unsigned int xTileCount = (1 << zoomLevel) * levelZeroColumns();
0143     const unsigned int yTileCount = (1 << zoomLevel) * levelZeroRows();
0144 
0145     const qreal west = lonFromTileX(x, xTileCount);
0146     const qreal north = latFromTileY(y, yTileCount);
0147 
0148     const qreal east = lonFromTileX(x + 1, xTileCount);
0149     const qreal south = latFromTileY(y + 1, yTileCount);
0150 
0151     return GeoDataLatLonBox(north, south, east, west);
0152 }
0153 
0154 }