File indexing completed on 2025-01-19 03:58:10

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2009-12-01
0007  * Description : Tile index used in the tiling classes
0008  *
0009  * SPDX-FileCopyrightText: 2010-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0010  * SPDX-FileCopyrightText: 2009-2011 by Michael G. Hansen <mike at mghansen dot de>
0011  *
0012  * SPDX-License-Identifier: GPL-2.0-or-later
0013  *
0014  * ============================================================ */
0015 
0016 #include "tileindex.h"
0017 
0018 // Local includes
0019 
0020 #include "geoifacecommon.h"
0021 
0022 namespace
0023 {
0024     static_assert (Digikam::TileIndex::Tiling == 10,
0025                    "the constants below expect 10x10 tile splits");
0026 
0027     static_assert (Digikam::TileIndex::MaxLevel == 9,
0028                    "the constants below expect 10x10 tile splits with max level 9");
0029 
0030     constexpr qint64 MaxLevelTileSplits       = 10000000000LL;
0031     constexpr double MaxLevelTileSplitsFactor = 1.0 / static_cast<double>(MaxLevelTileSplits);
0032 }
0033 
0034 
0035 namespace Digikam
0036 {
0037 
0038 TileIndex::TileIndex()
0039     : m_indicesCount(0)
0040 {
0041     for (int i = 0 ; i < MaxIndexCount ; ++i)
0042     {
0043         m_indices[i] = 0;
0044     }
0045 }
0046 
0047 TileIndex::~TileIndex()
0048 {
0049 }
0050 
0051 int TileIndex::indexCount() const
0052 {
0053     return m_indicesCount;
0054 }
0055 
0056 int TileIndex::level() const
0057 {
0058     return (m_indicesCount > 0 ? m_indicesCount - 1 : 0);
0059 }
0060 
0061 void TileIndex::clear()
0062 {
0063     m_indicesCount = 0;
0064 }
0065 
0066 void TileIndex::appendLinearIndex(const int newIndex)
0067 {
0068     GEOIFACE_ASSERT(m_indicesCount + 1 <= MaxIndexCount);
0069 
0070     m_indices[m_indicesCount] = newIndex;
0071     m_indicesCount++;
0072 }
0073 
0074 int TileIndex::linearIndex(const int getLevel) const
0075 {
0076     GEOIFACE_ASSERT(getLevel<=level());
0077 
0078     return m_indices[getLevel];
0079 }
0080 
0081 int TileIndex::at(const int getLevel) const
0082 {
0083     GEOIFACE_ASSERT(getLevel<=level());
0084 
0085     return m_indices[getLevel];
0086 }
0087 
0088 int TileIndex::lastIndex() const
0089 {
0090     GEOIFACE_ASSERT(m_indicesCount>0);
0091 
0092     return m_indices[m_indicesCount-1];
0093 }
0094 
0095 int TileIndex::indexLat(const int getLevel) const
0096 {
0097     return (linearIndex(getLevel) / Tiling);
0098 }
0099 
0100 int TileIndex::indexLon(const int getLevel) const
0101 {
0102     return (linearIndex(getLevel) % Tiling);
0103 }
0104 
0105 QPoint TileIndex::latLonIndex(const int getLevel) const
0106 {
0107     return QPoint(indexLon(getLevel), indexLat(getLevel));
0108 }
0109 
0110 void TileIndex::latLonIndex(const int getLevel, int* const latIndex, int* const lonIndex) const
0111 {
0112     GEOIFACE_ASSERT(getLevel <= level());
0113 
0114     *latIndex = indexLat(getLevel);
0115     *lonIndex = indexLon(getLevel);
0116 
0117     GEOIFACE_ASSERT(*latIndex < Tiling);
0118     GEOIFACE_ASSERT(*lonIndex < Tiling);
0119 }
0120 
0121 void TileIndex::appendLatLonIndex(const int latIndex, const int lonIndex)
0122 {
0123     appendLinearIndex(latIndex*Tiling + lonIndex);
0124 }
0125 
0126 QIntList TileIndex::toIntList() const
0127 {
0128     QIntList result;
0129 
0130     for (int i = 0 ; i < m_indicesCount ; ++i)
0131     {
0132         result << m_indices[i];
0133     }
0134 
0135     return result;
0136 }
0137 
0138 TileIndex TileIndex::fromIntList(const QIntList& intList)
0139 {
0140     TileIndex result;
0141 
0142     for (int i = 0 ; i < intList.count() ; ++i)
0143     {
0144         result.appendLinearIndex(intList.at(i));
0145     }
0146 
0147     return result;
0148 }
0149 
0150 bool TileIndex::indicesEqual(const TileIndex& a, const TileIndex& b, const int upToLevel)
0151 {
0152     GEOIFACE_ASSERT(a.level() >= upToLevel);
0153     GEOIFACE_ASSERT(b.level() >= upToLevel);
0154 
0155     for (int i = 0 ; i <= upToLevel ; ++i)
0156     {
0157         if (a.linearIndex(i) != b.linearIndex(i))
0158         {
0159             return false;
0160         }
0161     }
0162 
0163     return true;
0164 }
0165 
0166 TileIndex TileIndex::mid(const int first, const int len) const
0167 {
0168     GEOIFACE_ASSERT(first+(len-1) <= m_indicesCount);
0169 
0170     TileIndex result;
0171 
0172     for (int i = first ; i < first + len ; ++i)
0173     {
0174         result.appendLinearIndex(m_indices[i]);
0175     }
0176 
0177     return result;
0178 }
0179 
0180 void TileIndex::oneUp()
0181 {
0182     GEOIFACE_ASSERT(m_indicesCount > 0);
0183 
0184     m_indicesCount--;
0185 }
0186 
0187 QList<QIntList> TileIndex::listToIntListList(const QList<TileIndex>& tileIndexList)
0188 {
0189     QList<QIntList> result;
0190 
0191     for (int i = 0 ; i < tileIndexList.count() ; ++i)
0192     {
0193         result << tileIndexList.at(i).toIntList();
0194     }
0195 
0196     return result;
0197 }
0198 
0199 TileIndex TileIndex::fromCoordinates(const Digikam::GeoCoordinates& coordinate, const int getLevel)
0200 {
0201     GEOIFACE_ASSERT(getLevel <= MaxLevel);
0202 
0203     if (!coordinate.hasCoordinates())
0204     {
0205         return TileIndex();
0206     }
0207 
0208     qint64 tileLat, tileLon;
0209     {
0210         // this is the only place where rounding happens
0211 
0212         tileLat = static_cast<qint64>(((coordinate.lat() + 90.0)  / 180.0) * MaxLevelTileSplits);
0213         tileLon = static_cast<qint64>(((coordinate.lon() + 180.0) / 360.0) * MaxLevelTileSplits);
0214 
0215         // the very last tile includes it's upper bound
0216 
0217         tileLat = std::min(tileLat, MaxLevelTileSplits-1);
0218         tileLon = std::min(tileLon, MaxLevelTileSplits-1);
0219 
0220         // guard against bogus input
0221 
0222         tileLat = std::max(tileLat, static_cast<qint64>(0));
0223         tileLon = std::max(tileLon, static_cast<qint64>(0));
0224      }
0225 
0226     // every calculation below is on integers so no rounding issues
0227 
0228     TileIndex tileIndex;
0229 
0230     for (int i = 0 ; i <= TileIndex::MaxLevel ; ++i)
0231     {
0232         tileIndex.m_indices[TileIndex::MaxLevel-i] = (tileLat % Tiling) * Tiling + tileLon % Tiling;
0233         tileLat /= Tiling;
0234         tileLon /= Tiling;
0235     }
0236 
0237     tileIndex.m_indicesCount = getLevel + 1;
0238 
0239     return tileIndex;
0240 }
0241 
0242 GeoCoordinates TileIndex::toCoordinates() const
0243 {
0244     return toCoordinates(CornerPosition::CornerSW);
0245 }
0246 
0247 GeoCoordinates TileIndex::toCoordinates(const CornerPosition ofCorner) const
0248 {
0249     qint64 tileLat = 0;
0250     qint64 tileLon = 0;
0251 
0252     for (int l = 0 ; l <= MaxLevel ; ++l)
0253     {
0254         tileLat *= Tiling;
0255         tileLon *= Tiling;
0256 
0257         if (l < m_indicesCount)
0258         {
0259             tileLat += indexLat(l);
0260             tileLon += indexLon(l);
0261         }
0262 
0263         if ((l + 1 == m_indicesCount)               &&
0264             ((ofCorner == CornerPosition::CornerNE) ||
0265              (ofCorner == CornerPosition::CornerNW)))
0266         {
0267             tileLat += 1;
0268         }
0269 
0270         if ((l + 1 == m_indicesCount)               &&
0271             ((ofCorner == CornerPosition::CornerNE) ||
0272              (ofCorner == CornerPosition::CornerSE)))
0273         {
0274             tileLon += 1;
0275         }
0276     }
0277 
0278     return Digikam::GeoCoordinates((tileLat -  MaxLevelTileSplits / 2) * MaxLevelTileSplitsFactor * 180.0,
0279                                    (tileLon -  MaxLevelTileSplits / 2) * MaxLevelTileSplitsFactor * 360.0);
0280 }
0281 
0282 } // namespace Digikam
0283 
0284 QDebug operator<<(QDebug debug, const Digikam::TileIndex& tileIndex)
0285 {
0286     debug << tileIndex.toIntList();
0287 
0288     return debug;
0289 }