File indexing completed on 2025-02-02 05:02:32

0001 /*
0002     SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "mapdownloadmanager.h"
0008 #include "reservationmanager.h"
0009 
0010 #include "solidextras/networkstatus.h"
0011 
0012 #include <KOSMIndoorMap/MapLoader>
0013 
0014 #include <KItinerary/LocationUtil>
0015 #include <KItinerary/Place>
0016 #include <KItinerary/SortUtil>
0017 
0018 #include <QDebug>
0019 #include <QTimer>
0020 
0021 #include <cmath>
0022 
0023 using namespace KItinerary;
0024 using SolidExtras::NetworkStatus;
0025 
0026 MapDownloadManager::MapDownloadManager(QObject* parent)
0027     : QObject(parent)
0028     , m_netStatus(new NetworkStatus(this))
0029 {
0030     connect(m_netStatus, &NetworkStatus::connectivityChanged, this, &MapDownloadManager::networkStatusChanged);
0031     connect(m_netStatus, &NetworkStatus::meteredChanged, this, &MapDownloadManager::networkStatusChanged);
0032 }
0033 
0034 MapDownloadManager::~MapDownloadManager() = default;
0035 
0036 void MapDownloadManager::setReservationManager(ReservationManager *resMgr)
0037 {
0038     m_resMgr = resMgr;
0039 
0040     connect(m_resMgr, &ReservationManager::batchAdded, this, &MapDownloadManager::addAutomaticRequestForBatch);
0041     connect(m_resMgr, &ReservationManager::batchChanged, this, &MapDownloadManager::addAutomaticRequestForBatch);
0042     connect(m_resMgr, &ReservationManager::batchContentChanged, this, &MapDownloadManager::addAutomaticRequestForBatch);
0043 }
0044 
0045 void MapDownloadManager::setAutomaticDownloadEnabled(bool enable)
0046 {
0047     m_autoDownloadEnabled = enable;
0048     if (canAutoDownload()) {
0049         download();
0050     }
0051 }
0052 
0053 static bool isRelevantTime(const QDateTime &dt)
0054 {
0055     const auto now = QDateTime::currentDateTime();
0056     return dt > now && dt < now.addDays(14);
0057 }
0058 
0059 void MapDownloadManager::download()
0060 {
0061     for (const auto &batchId : m_resMgr->batches()) {
0062         addRequestForBatch(batchId);
0063     }
0064 
0065     qDebug() << m_pendingRequests.size() << "pending download requests" << m_netStatus->connectivity() << m_netStatus->metered();
0066     downloadNext();
0067 }
0068 
0069 bool MapDownloadManager::canAutoDownload() const
0070 {
0071     return m_autoDownloadEnabled && m_netStatus->connectivity() != NetworkStatus::No && m_netStatus->metered() == NetworkStatus::No;
0072 }
0073 
0074 void MapDownloadManager::addAutomaticRequestForBatch(const QString& batchId)
0075 {
0076     addRequestForBatch(batchId);
0077     if (canAutoDownload()) {
0078         QTimer::singleShot(std::chrono::seconds(5), this, &MapDownloadManager::downloadNext);
0079     }
0080 }
0081 
0082 void MapDownloadManager::addRequestForBatch(const QString &batchId)
0083 {
0084     const auto res = m_resMgr->reservation(batchId);
0085     if (!LocationUtil::isLocationChange(res)) {
0086         return;
0087     }
0088     const auto arrTime = SortUtil::endDateTime(res);
0089     if (isRelevantTime(arrTime)) {
0090         const auto arr = LocationUtil::arrivalLocation(res);
0091         const auto arrGeo = LocationUtil::geo(arr);
0092         qDebug() << LocationUtil::name(arr) << arrGeo.latitude() << arrGeo.longitude();
0093         addRequest(arrGeo.latitude(), arrGeo.longitude(), arrTime);
0094     }
0095 
0096     const auto depTime = SortUtil::startDateTime(res);
0097     if (isRelevantTime(depTime)) {
0098         const auto dep = LocationUtil::departureLocation(res);
0099         const auto depGeo = LocationUtil::geo(dep);
0100         qDebug() << LocationUtil::name(dep) << depGeo.latitude() << depGeo.longitude();
0101         addRequest(depGeo.latitude(), depGeo.longitude(), depTime);
0102     }
0103 }
0104 
0105 void MapDownloadManager::addRequest(double lat, double lon, const QDateTime &cacheUntil)
0106 {
0107     if (std::isnan(lat) || std::isnan(lon)) {
0108         return;
0109     }
0110 
0111     // check if we already have this cached
0112     for (auto it = m_cachedRequests.begin(); it != m_cachedRequests.end(); ++it) {
0113         if (LocationUtil::distance(lat, lon, (*it).lat, (*it).lon) > 10.0) {
0114             continue;
0115         }
0116         if ((*it).cacheUntil >= cacheUntil) {
0117             return;
0118         }
0119         m_cachedRequests.erase(it);
0120         break;
0121     }
0122 
0123     // check if there is a pending request that would cover this
0124     for (auto &req : m_pendingRequests) {
0125         if (LocationUtil::distance(req.lat, req.lon, lat, lon) < 10.0) {
0126             req.cacheUntil = std::max(req.cacheUntil, cacheUntil);
0127             return;
0128         }
0129     }
0130 
0131     m_pendingRequests.push_back({ lat, lon, cacheUntil });
0132 }
0133 
0134 void MapDownloadManager::downloadNext()
0135 {
0136     if (m_loader || m_pendingRequests.empty()) {
0137         return;
0138     }
0139 
0140     m_currentRequest = std::move(m_pendingRequests.back());
0141     m_pendingRequests.pop_back();
0142 
0143     m_loader = new KOSMIndoorMap::MapLoader(this);
0144     connect(m_loader, &KOSMIndoorMap::MapLoader::done, this, &MapDownloadManager::downloadFinished);
0145     m_loader->loadForCoordinate(m_currentRequest.lat, m_currentRequest.lon, m_currentRequest.cacheUntil);
0146 }
0147 
0148 void MapDownloadManager::downloadFinished()
0149 {
0150     m_loader->deleteLater();
0151     m_loader = nullptr;
0152     m_cachedRequests.push_back(std::move(m_currentRequest));
0153 
0154     if (m_pendingRequests.empty()) {
0155         Q_EMIT finished();
0156     } else {
0157         downloadNext();
0158     }
0159 }
0160 
0161 void MapDownloadManager::networkStatusChanged()
0162 {
0163     if (canAutoDownload()) {
0164         downloadNext();
0165     }
0166 }
0167 
0168 #include "moc_mapdownloadmanager.cpp"