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

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2009-12-01
0007  * Description : An abstract base class for tiling of markers
0008  *
0009  * SPDX-FileCopyrightText: 2009-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 "abstractmarkertiler.h"
0017 
0018 // Qt includes
0019 
0020 #include <QPair>
0021 #include <QScopedPointer>
0022 
0023 // Local includes
0024 
0025 #include "digikam_debug.h"
0026 #include "geoifacecommon.h"
0027 
0028 namespace Digikam
0029 {
0030 
0031 class Q_DECL_HIDDEN AbstractMarkerTiler::Private
0032 {
0033 public:
0034 
0035     explicit Private() = default;
0036 
0037     QScopedPointer<AbstractMarkerTiler::Tile>  rootTile;
0038     bool                                       isDirty = true;
0039 };
0040 
0041 AbstractMarkerTiler::AbstractMarkerTiler(QObject* const parent)
0042     : QObject(parent),
0043       d(new Private())
0044 {
0045 }
0046 
0047 AbstractMarkerTiler::~AbstractMarkerTiler()
0048 {
0049     delete d;
0050 }
0051 
0052 AbstractMarkerTiler::Tile* AbstractMarkerTiler::rootTile()
0053 {
0054     if (isDirty())
0055     {
0056         regenerateTiles();
0057     }
0058 
0059     return d->rootTile.data();
0060 }
0061 
0062 bool AbstractMarkerTiler::isDirty() const
0063 {
0064     return d->isDirty;
0065 }
0066 
0067 void AbstractMarkerTiler::setDirty(const bool state)
0068 {
0069     if (state && !d->isDirty)
0070     {
0071         d->isDirty = true;
0072         Q_EMIT signalTilesOrSelectionChanged();
0073     }
0074     else
0075     {
0076         d->isDirty = state;
0077     }
0078 }
0079 
0080 void AbstractMarkerTiler::resetRootTile()
0081 {
0082     d->rootTile.reset(tileNew());
0083 }
0084 
0085 void AbstractMarkerTiler::onIndicesClicked(const ClickInfo& clickInfo)
0086 {
0087     Q_UNUSED(clickInfo)
0088 }
0089 
0090 void AbstractMarkerTiler::onIndicesMoved(const TileIndex::List& tileIndicesList,
0091                                          const GeoCoordinates& targetCoordinates,
0092                                          const QPersistentModelIndex& targetSnapIndex)
0093 {
0094     Q_UNUSED(tileIndicesList);
0095     Q_UNUSED(targetCoordinates);
0096     Q_UNUSED(targetSnapIndex);
0097 }
0098 
0099 AbstractMarkerTiler::TilerFlags AbstractMarkerTiler::tilerFlags() const
0100 {
0101     return FlagNull;
0102 }
0103 
0104 // -------------------------------------------------------------------------
0105 
0106 class Q_DECL_HIDDEN AbstractMarkerTiler::NonEmptyIterator::Private
0107 {
0108 public:
0109 
0110     explicit Private()
0111         : model(nullptr),
0112           level(0),
0113           startIndex(),
0114           endIndex(),
0115           currentIndex(),
0116           atEnd(false)
0117     {
0118     }
0119 
0120     AbstractMarkerTiler*                model;
0121     int                                 level;
0122 
0123     QList<QPair<TileIndex, TileIndex> > boundsList;
0124 
0125     TileIndex                           startIndex;
0126     TileIndex                           endIndex;
0127     TileIndex                           currentIndex;
0128 
0129     bool                                atEnd;
0130 };
0131 
0132 AbstractMarkerTiler::NonEmptyIterator::~NonEmptyIterator()
0133 {
0134     delete d;
0135 }
0136 
0137 AbstractMarkerTiler::NonEmptyIterator::NonEmptyIterator(AbstractMarkerTiler* const model,
0138                                                         const int level)
0139     : d(new Private())
0140 {
0141     d->model = model;
0142     GEOIFACE_ASSERT(level <= TileIndex::MaxLevel);
0143     d->level = level;
0144 
0145     TileIndex startIndex;
0146     TileIndex endIndex;
0147 
0148     for (int i = 0 ; i <= level ; ++i)
0149     {
0150         startIndex.appendLinearIndex(0);
0151         endIndex.appendLinearIndex(TileIndex::Tiling * TileIndex::Tiling - 1);
0152     }
0153 /*
0154     qCDebug(DIGIKAM_GEOIFACE_LOG) << d->startIndexLinear << d->endIndexLinear;
0155 */
0156     d->boundsList << QPair<TileIndex, TileIndex>(startIndex, endIndex);
0157 
0158     initializeNextBounds();
0159 }
0160 
0161 AbstractMarkerTiler::NonEmptyIterator::NonEmptyIterator(AbstractMarkerTiler* const model,
0162                                                         const int level,
0163                                                         const TileIndex& startIndex,
0164                                                         const TileIndex& endIndex)
0165     : d(new Private())
0166 {
0167     d->model = model;
0168     GEOIFACE_ASSERT(level <= TileIndex::MaxLevel);
0169     d->level = level;
0170 
0171     GEOIFACE_ASSERT(startIndex.level() == level);
0172     GEOIFACE_ASSERT(endIndex.level()   == level);
0173     d->boundsList << QPair<TileIndex, TileIndex>(startIndex, endIndex);
0174 
0175     initializeNextBounds();
0176 }
0177 
0178 AbstractMarkerTiler::NonEmptyIterator::NonEmptyIterator(AbstractMarkerTiler* const model,
0179                                                         const int level,
0180                                                         const GeoCoordinates::PairList& normalizedMapBounds)
0181     : d(new Private())
0182 {
0183     d->model = model;
0184     GEOIFACE_ASSERT(level <= TileIndex::MaxLevel);
0185     d->level = level;
0186 
0187     // store the coordinates of the bounds as indices:
0188 
0189     for (int i = 0 ; i < normalizedMapBounds.count() ; ++i)
0190     {
0191         GeoCoordinates::Pair currentBounds = normalizedMapBounds.at(i);
0192         GEOIFACE_ASSERT(currentBounds.first.lat() < currentBounds.second.lat());
0193         GEOIFACE_ASSERT(currentBounds.first.lon() < currentBounds.second.lon());
0194 
0195         const TileIndex startIndex = TileIndex::fromCoordinates(currentBounds.first,  d->level);
0196         const TileIndex endIndex   = TileIndex::fromCoordinates(currentBounds.second, d->level);
0197 /*
0198          qCDebug(DIGIKAM_GEOIFACE_LOG) << currentBounds.first.geoUrl()
0199                                        << startIndex
0200                                        << currentBounds.second.geoUrl()
0201                                        << endIndex;
0202 */
0203         d->boundsList << QPair<TileIndex, TileIndex>(startIndex, endIndex);
0204     }
0205 
0206     initializeNextBounds();
0207 }
0208 
0209 bool AbstractMarkerTiler::NonEmptyIterator::initializeNextBounds()
0210 {
0211     if (d->boundsList.isEmpty())
0212     {
0213         d->atEnd = true;
0214 
0215         return false;
0216     }
0217 
0218     QPair<TileIndex, TileIndex> nextBounds = d->boundsList.takeFirst();
0219     d->startIndex                          = nextBounds.first;
0220     d->endIndex                            = nextBounds.second;
0221 
0222     GEOIFACE_ASSERT(d->startIndex.level() == d->level);
0223     GEOIFACE_ASSERT(d->endIndex.level()   == d->level);
0224 
0225     d->currentIndex.clear();
0226     d->currentIndex.appendLinearIndex(-1);
0227 
0228     // make sure the root tile is split, using linear index -1 is ok here as
0229     // this is handled in Tile::getChild
0230     d->model->getTile(d->currentIndex, true);
0231 
0232     nextIndex();
0233 
0234     return d->atEnd;
0235 }
0236 
0237 TileIndex AbstractMarkerTiler::NonEmptyIterator::nextIndex()
0238 {
0239     if (d->atEnd)
0240     {
0241         return d->currentIndex;
0242     }
0243 
0244     Q_FOREVER
0245     {
0246         const int currentLevel = d->currentIndex.level();
0247 /*
0248         qCDebug(DIGIKAM_GEOIFACE_LOG) << d->level
0249                                       << currentLevel
0250                                       << d->currentIndex;
0251 */
0252         // determine the limits in the current level:
0253         int limitLatBL   = 0;
0254         int limitLonBL   = 0;
0255         int limitLatTR   = TileIndex::Tiling-1;
0256         int limitLonTR   = TileIndex::Tiling-1;
0257 
0258         {
0259 
0260             int compareLevel = currentLevel - 1;
0261             // check limit on the left side:
0262 
0263             bool onLimit = true;
0264 
0265             for (int i = 0 ; onLimit && (i <= compareLevel) ; ++i)
0266             {
0267                 onLimit = d->currentIndex.indexLat(i) == d->startIndex.indexLat(i);
0268             }
0269 
0270             if (onLimit)
0271             {
0272                 limitLatBL = d->startIndex.indexLat(currentLevel);
0273             }
0274 
0275             // check limit on the bottom side:
0276 
0277             onLimit = true;
0278 
0279             for (int i = 0 ; onLimit && (i <= compareLevel) ; ++i)
0280             {
0281                 onLimit = d->currentIndex.indexLon(i) == d->startIndex.indexLon(i);
0282             }
0283 
0284             if (onLimit)
0285             {
0286                 limitLonBL = d->startIndex.indexLon(currentLevel);
0287             }
0288 
0289             // check limit on the right side:
0290 
0291             onLimit = true;
0292 
0293             for (int i = 0 ; onLimit && (i <= compareLevel) ; ++i)
0294             {
0295                 onLimit = d->currentIndex.indexLat(i) == d->endIndex.indexLat(i);
0296             }
0297 
0298             if (onLimit)
0299             {
0300                 limitLatTR = d->endIndex.indexLat(currentLevel);
0301             }
0302 
0303             // check limit on the top side:
0304 
0305             onLimit = true;
0306 
0307             for (int i = 0 ; onLimit && (i <= compareLevel) ; ++i)
0308             {
0309                 onLimit = d->currentIndex.indexLon(i) == d->endIndex.indexLon(i);
0310             }
0311 
0312             if (onLimit)
0313             {
0314                 limitLonTR = d->endIndex.indexLon(currentLevel);
0315             }
0316         }
0317 
0318         // get the parent tile of the current level
0319 
0320         int levelLinearIndex = d->currentIndex.lastIndex();
0321 
0322         d->currentIndex.oneUp();
0323 
0324         Tile* tile           = d->model->getTile(d->currentIndex, true);
0325 
0326         if (!tile)
0327         {
0328             // this can only happen if the model has changed during iteration. handle gracefully...
0329 
0330             d->currentIndex.oneUp();
0331 
0332             continue;
0333         }
0334         else
0335         {
0336             d->currentIndex.appendLinearIndex(levelLinearIndex);
0337         }
0338 
0339         // helper function to check if index is inside bounds of the current level
0340 
0341         auto inBounds = [&](int idx)
0342                         {
0343                             int currentLat = idx / TileIndex::Tiling;
0344                             int currentLon = idx % TileIndex::Tiling;
0345 
0346                             return (
0347                                     currentLat >= limitLatBL &&
0348                                     currentLat <= limitLatTR &&
0349                                     currentLon >= limitLonBL &&
0350                                     currentLon <= limitLonTR
0351                                    );
0352                         };
0353 
0354         // get the next linear index on the level which is in Bounds
0355 
0356         levelLinearIndex = tile->nextNonEmptyIndex(levelLinearIndex);
0357 
0358         while ((levelLinearIndex != -1) && !inBounds(levelLinearIndex))
0359         {
0360             levelLinearIndex = tile->nextNonEmptyIndex(levelLinearIndex);
0361         }
0362 
0363 
0364         if (levelLinearIndex == -1)
0365         {
0366             // no index in bound anymore on this level
0367 
0368             if (currentLevel == 0)
0369             {
0370                 // we are at the end!
0371                 // are there other bounds to iterate over?
0372 
0373                 initializeNextBounds();
0374 
0375                 // initializeNextBounds() call nextIndex which updates d->currentIndex, if possible:
0376 
0377                 return d->currentIndex;
0378             }
0379 
0380             // we need to go one level up, trim the indices:
0381 
0382             d->currentIndex.oneUp();
0383 
0384             continue;
0385         }
0386 
0387         // save the new position:
0388 
0389         d->currentIndex.oneUp();
0390         d->currentIndex.appendLinearIndex(levelLinearIndex);
0391 
0392         // are we at the target level?
0393 
0394         if (currentLevel == d->level)
0395         {
0396             // yes, return the current index:
0397 
0398             return d->currentIndex;
0399         }
0400 
0401         // go one level down:
0402 
0403         d->currentIndex.appendLinearIndex(-1);
0404 
0405         // make sure the current level is split,using linear index -1 is ok here
0406         // as this is handled in Tile::getChild
0407 
0408         d->model->getTile(d->currentIndex, true);
0409     }
0410 }
0411 
0412 TileIndex AbstractMarkerTiler::NonEmptyIterator::currentIndex() const
0413 {
0414     return d->currentIndex;
0415 }
0416 
0417 bool AbstractMarkerTiler::NonEmptyIterator::atEnd() const
0418 {
0419     return d->atEnd;
0420 }
0421 
0422 AbstractMarkerTiler* AbstractMarkerTiler::NonEmptyIterator::model() const
0423 {
0424     return d->model;
0425 }
0426 
0427 // -------------------------------------------------------------------------
0428 
0429 AbstractMarkerTiler::Tile::Tile() = default;
0430 
0431 AbstractMarkerTiler::Tile::~Tile()
0432 {
0433     for (auto* tile : children)
0434     {
0435         if (tile)
0436         {
0437             delete tile;
0438         }
0439     }
0440 }
0441 
0442 int AbstractMarkerTiler::Tile::maxChildCount()
0443 {
0444     return TileIndex::Tiling * TileIndex::Tiling;
0445 }
0446 
0447 AbstractMarkerTiler::Tile* AbstractMarkerTiler::Tile::getChild(const int linearIndex)
0448 {
0449     if (children.isEmpty())
0450     {
0451         return nullptr;
0452     }
0453 
0454     if ((linearIndex < 0) || (linearIndex >= maxChildCount()))
0455     {
0456         return nullptr;
0457     }
0458 
0459     return children.at(linearIndex);
0460 }
0461 
0462 AbstractMarkerTiler::Tile* AbstractMarkerTiler::Tile::addChild(const int linearIndex, Tile* tilePointer)
0463 {
0464     if ((tilePointer == nullptr) && children.isEmpty())
0465     {
0466         return nullptr;
0467     }
0468 
0469     prepareForChildren();
0470 
0471     GEOIFACE_ASSERT(!children[linearIndex]);
0472 
0473     if (!children[linearIndex])
0474     {
0475         nonEmptyIndices.insert(std::upper_bound(nonEmptyIndices.begin(), nonEmptyIndices.end(), linearIndex), linearIndex);
0476     }
0477 
0478     children[linearIndex] = tilePointer;
0479 
0480     return tilePointer;
0481 }
0482 
0483 
0484 void AbstractMarkerTiler::Tile::deleteChild(Tile* const childTile,
0485                                             const int knownLinearIndex)
0486 {
0487     if (children.isEmpty())
0488     {
0489         return;
0490     }
0491 
0492     int tileIndex = knownLinearIndex;
0493 
0494     if (tileIndex < 0)
0495     {
0496         tileIndex = std::distance(children.begin(), std::find(children.begin(), children.end(), childTile));
0497     }
0498 
0499     if (children[tileIndex])
0500     {
0501         nonEmptyIndices.erase(std::lower_bound(nonEmptyIndices.begin(), nonEmptyIndices.end(), tileIndex));
0502     }
0503 
0504     delete children[tileIndex];
0505     children[tileIndex] = nullptr;
0506 }
0507 
0508 bool AbstractMarkerTiler::Tile::childrenEmpty() const
0509 {
0510     return children.empty();
0511 }
0512 
0513 void AbstractMarkerTiler::Tile::prepareForChildren()
0514 {
0515     if (!children.isEmpty())
0516     {
0517         return;
0518     }
0519 
0520     children = QVector<Tile*>(maxChildCount(), nullptr);
0521 }
0522 
0523 int AbstractMarkerTiler::Tile::nextNonEmptyIndex(int linearIndex) const
0524 {
0525     auto it = std::upper_bound(nonEmptyIndices.begin(), nonEmptyIndices.end(), linearIndex);
0526 
0527     if (it != nonEmptyIndices.end())
0528     {
0529         return *it;
0530     }
0531 
0532     return -1;
0533 }
0534 
0535 } // namespace Digikam
0536 
0537 #include "moc_abstractmarkertiler.cpp"