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

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2011-04-30
0007  * Description : Class for geonames.org based altitude lookup
0008  *
0009  * SPDX-FileCopyrightText: 2010-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0010  * SPDX-FileCopyrightText: 2010-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 "lookupaltitudegeonames.h"
0017 
0018 // Qt includes
0019 
0020 
0021 #include <QUrlQuery>
0022 #include <QRegularExpression>
0023 
0024 // KDE includes
0025 
0026 #include <klocalizedstring.h>
0027 
0028 // Local includes
0029 
0030 #include "geoifacetypes.h"
0031 #include "networkmanager.h"
0032 
0033 namespace Digikam
0034 {
0035 
0036 class Q_DECL_HIDDEN MergedRequests
0037 {
0038 public:
0039 
0040     QList<QPair<GeoCoordinates, QIntList> > groupedRequestIndices;
0041 
0042     typedef QList<MergedRequests> List;
0043 
0044     bool addRequestIfCoordinatesAreThere(const LookupAltitude::Request& request, const int requestIndex)
0045     {
0046         for (int i = 0 ; i < groupedRequestIndices.size() ; ++i)
0047         {
0048             if (groupedRequestIndices.at(i).first.sameLonLatAs(request.coordinates))
0049             {
0050                 groupedRequestIndices[i].second << requestIndex;
0051                 return true;
0052             }
0053         }
0054 
0055         return false;
0056     }
0057 };
0058 
0059 // ------------------------------------------------------------
0060 
0061 class Q_DECL_HIDDEN LookupAltitudeGeonames::Private
0062 {
0063 public:
0064 
0065     explicit Private()
0066       : status                   (StatusSuccess),
0067         currentMergedRequestIndex(0),
0068         netReply                 (nullptr),
0069         mngr                     (nullptr)
0070 
0071     {
0072     }
0073 
0074 public:
0075 
0076     Request::List          requests;
0077     MergedRequests::List   mergedRequests;
0078     StatusAltitude         status;
0079     QString                errorMessage;
0080 
0081     int                    currentMergedRequestIndex;
0082 
0083     QNetworkReply*         netReply;
0084     QNetworkAccessManager* mngr;
0085 };
0086 
0087 // ------------------------------------------------------------
0088 
0089 LookupAltitudeGeonames::LookupAltitudeGeonames(QObject* const parent)
0090     : LookupAltitude(parent),
0091       d(new Private)
0092 {
0093     d->mngr = NetworkManager::instance()->getNetworkManager(this);
0094 
0095     connect(d->mngr, SIGNAL(finished(QNetworkReply*)),
0096             this, SLOT(slotFinished(QNetworkReply*)));
0097 }
0098 
0099 LookupAltitudeGeonames::~LookupAltitudeGeonames()
0100 {
0101 }
0102 
0103 QString LookupAltitudeGeonames::backendName() const
0104 {
0105     return QLatin1String("geonames");
0106 }
0107 
0108 QString LookupAltitudeGeonames::backendHumanName() const
0109 {
0110     return i18n("geonames.org");
0111 }
0112 
0113 void LookupAltitudeGeonames::addRequests(const Request::List& requests)
0114 {
0115     d->requests << requests;
0116 }
0117 
0118 LookupAltitude::Request::List LookupAltitudeGeonames::getRequests() const
0119 {
0120     return d->requests;
0121 }
0122 
0123 LookupAltitude::Request LookupAltitudeGeonames::getRequest(const int index) const
0124 {
0125     return d->requests.at(index);
0126 }
0127 
0128 void LookupAltitudeGeonames::startLookup()
0129 {
0130     MergedRequests currentMergedRequest;
0131 
0132     for (int i = 0 ; i < d->requests.size() ; ++i)
0133     {
0134         const Request& currentRequest = d->requests.at(i);
0135 
0136         // is there another request with the same coordinates?
0137 
0138         bool requestAdded = currentMergedRequest.addRequestIfCoordinatesAreThere(currentRequest, i);
0139 
0140         for (int j = 0 ; (!requestAdded) && j < d->mergedRequests.size() ; ++j)
0141         {
0142             requestAdded = d->mergedRequests[j].addRequestIfCoordinatesAreThere(currentRequest, i);
0143         }
0144 
0145         if (!requestAdded)
0146         {
0147             currentMergedRequest.groupedRequestIndices.append(QPair<GeoCoordinates, QIntList>(currentRequest.coordinates, QIntList()<<i));
0148 
0149             if (currentMergedRequest.groupedRequestIndices.size() >= (20-1))
0150             {
0151                 d->mergedRequests << currentMergedRequest;
0152                 currentMergedRequest = MergedRequests();
0153             }
0154         }
0155     }
0156 
0157     if (!currentMergedRequest.groupedRequestIndices.isEmpty())
0158     {
0159         d->mergedRequests << currentMergedRequest;
0160     }
0161 
0162     // all requests have been grouped into batches of 20, now start the first one
0163 
0164     d->currentMergedRequestIndex = -1;
0165     startNextRequest();
0166 }
0167 
0168 void LookupAltitudeGeonames::startNextRequest()
0169 {
0170     ++(d->currentMergedRequestIndex);
0171 
0172     if (d->currentMergedRequestIndex >= d->mergedRequests.count())
0173     {
0174         d->status = StatusSuccess;
0175 
0176         Q_EMIT signalDone();
0177 
0178         return;
0179     }
0180 
0181     const MergedRequests& currentMergedRequest = d->mergedRequests.at(d->currentMergedRequestIndex);
0182 
0183     QString latString;
0184     QString lonString;
0185 
0186     for (int i = 0 ; i < currentMergedRequest.groupedRequestIndices.count() ; ++i)
0187     {
0188         const QPair<GeoCoordinates, QIntList>& currentPair = currentMergedRequest.groupedRequestIndices.at(i);
0189         const GeoCoordinates requestCoordinates            = currentPair.first;
0190 
0191         if (!latString.isEmpty())
0192         {
0193             latString += QLatin1Char(',');
0194             lonString += QLatin1Char(',');
0195         }
0196 
0197         latString += requestCoordinates.latString();
0198         lonString += requestCoordinates.lonString();
0199     }
0200 
0201     QUrl netUrl(QLatin1String("http://api.geonames.org/srtm3"));            // krazy:exclude=insecurenet
0202 
0203     QUrlQuery q(netUrl);
0204     q.addQueryItem(QLatin1String("lats"), latString);
0205     q.addQueryItem(QLatin1String("lngs"), lonString);
0206     q.addQueryItem(QLatin1String("username"), QLatin1String("digikam"));
0207     netUrl.setQuery(q);
0208 
0209     d->netReply = d->mngr->get(QNetworkRequest(netUrl));
0210 }
0211 
0212 void LookupAltitudeGeonames::slotFinished(QNetworkReply* reply)
0213 {
0214     if (reply != d->netReply)
0215     {
0216         return;
0217     }
0218 
0219     if (reply->error() != QNetworkReply::NoError)
0220     {
0221         d->errorMessage = reply->errorString();
0222         d->status       = StatusError;
0223 
0224         // after an error, we abort:
0225 
0226         reply->deleteLater();
0227 
0228         Q_EMIT signalDone();
0229 
0230         return;
0231     }
0232 
0233     QByteArray data                            = reply->readAll();
0234     const QStringList altitudes                = QString::fromLatin1(data).split(QRegularExpression(QLatin1String("\\s+")));
0235     const MergedRequests& currentMergedRequest = d->mergedRequests.at(d->currentMergedRequestIndex);
0236     QIntList readyRequests;
0237 
0238     for (int i = 0 ; i < qMin(altitudes.count(), currentMergedRequest.groupedRequestIndices.count()) ; ++i)
0239     {
0240         const QString& altitudeString = altitudes.at(i);
0241         bool haveAltitude             = false;
0242         const qreal altitude          = altitudeString.toFloat(&haveAltitude);
0243 
0244         // -32786 means that geonames.org has no data for these coordinates
0245 
0246         if (altitude == -32768)
0247         {
0248             haveAltitude = false;
0249         }
0250 
0251         const QIntList& currentRequestIndexes = currentMergedRequest.groupedRequestIndices.at(i).second;
0252 
0253         Q_FOREACH (const int requestIndex, currentRequestIndexes)
0254         {
0255             if (haveAltitude)
0256             {
0257                 d->requests[requestIndex].coordinates.setAlt(altitude);
0258             }
0259             else
0260             {
0261                 d->requests[requestIndex].coordinates.clearAlt();
0262             }
0263 
0264             // The request has been carried out. Even if no altitude was
0265             // found, we return success.
0266 
0267             d->requests[requestIndex].success = true;
0268         }
0269 
0270         readyRequests << currentRequestIndexes;
0271     }
0272 
0273     Q_EMIT signalRequestsReady(readyRequests);
0274 
0275     reply->deleteLater();
0276 
0277     startNextRequest();
0278 }
0279 
0280 LookupAltitude::StatusAltitude LookupAltitudeGeonames::getStatus() const
0281 {
0282     return d->status;
0283 }
0284 
0285 QString LookupAltitudeGeonames::errorMessage() const
0286 {
0287     return d->errorMessage;
0288 }
0289 
0290 void LookupAltitudeGeonames::cancel()
0291 {
0292     if (d->netReply && !d->netReply->isFinished())
0293     {
0294         d->netReply->abort();
0295     }
0296 
0297     d->status = StatusCanceled;
0298 
0299     Q_EMIT signalDone();
0300 }
0301 
0302 } // namespace Digikam
0303 
0304 #include "moc_lookupaltitudegeonames.cpp"