File indexing completed on 2025-03-09 03:57:14

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2010-05-12
0007  * Description : Backend for reverse geocoding using geonames.org (US-only)
0008  *
0009  * SPDX-FileCopyrightText: 2010-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0010  * SPDX-FileCopyrightText: 2010      by Michael G. Hansen <mike at mghansen dot de>
0011  * SPDX-FileCopyrightText: 2010      by Gabriel Voicu <ping dot gabi at gmail dot com>
0012  *
0013  * SPDX-License-Identifier: GPL-2.0-or-later
0014  *
0015  * ============================================================ */
0016 
0017 #include "backend-geonamesUS-rg.h"
0018 
0019 // Qt includes
0020 
0021 #include <QDomDocument>
0022 #include <QUrlQuery>
0023 #include <QTimer>
0024 
0025 // Local includes
0026 
0027 #include "digikam_debug.h"
0028 #include "networkmanager.h"
0029 #include "gpscommon.h"
0030 
0031 namespace Digikam
0032 {
0033 
0034 /**
0035  * @class BackendGeonamesUSRG
0036  *
0037  * @brief This class calls Geonames' get address service available only for USA locations.
0038  */
0039 class Q_DECL_HIDDEN GeonamesUSInternalJobs
0040 {
0041 
0042 public:
0043 
0044     GeonamesUSInternalJobs()
0045       : netReply(nullptr)
0046     {
0047     }
0048 
0049     ~GeonamesUSInternalJobs()
0050     {
0051         if (netReply)
0052         {
0053             netReply->deleteLater();
0054         }
0055     }
0056 
0057     QString            language;
0058     QList<RGInfo>      request;
0059     QByteArray         data;
0060 
0061     QNetworkReply*     netReply;
0062 };
0063 
0064 class Q_DECL_HIDDEN BackendGeonamesUSRG::Private
0065 {
0066 
0067 public:
0068 
0069     explicit Private()
0070       : itemCounter (0),
0071         itemCount   (0),
0072         mngr        (nullptr)
0073     {
0074     }
0075 
0076     int                           itemCounter;
0077     int                           itemCount;
0078     QList<GeonamesUSInternalJobs> jobs;
0079     QString                       errorMessage;
0080 
0081     QNetworkAccessManager*        mngr;
0082 };
0083 
0084 /**
0085  * Constructor
0086  * @param Parent object.
0087  */
0088 BackendGeonamesUSRG::BackendGeonamesUSRG(QObject* const parent)
0089     : RGBackend(parent),
0090       d        (new Private())
0091 {
0092     d->mngr = NetworkManager::instance()->getNetworkManager(this);
0093 
0094     connect(d->mngr, SIGNAL(finished(QNetworkReply*)),
0095             this, SLOT(slotFinished(QNetworkReply*)));
0096 }
0097 
0098 /**
0099  * Destructor
0100  */
0101 BackendGeonamesUSRG::~BackendGeonamesUSRG()
0102 {
0103     delete d;
0104 }
0105 
0106 /**
0107  * This slot calls Geonames's get address service for each image.
0108  */
0109 void BackendGeonamesUSRG::nextPhoto()
0110 {
0111     if (d->jobs.isEmpty())
0112     {
0113         return;
0114     }
0115 
0116     QUrl netUrl(QLatin1String("http://api.geonames.org/findNearestAddress"));       // krazy:exclude=insecurenet
0117 
0118     QUrlQuery q(netUrl);
0119     q.addQueryItem(QLatin1String("lat"),      d->jobs.first().request.first().coordinates.latString());
0120     q.addQueryItem(QLatin1String("lng"),      d->jobs.first().request.first().coordinates.lonString());
0121     q.addQueryItem(QLatin1String("username"), QLatin1String("digikam"));
0122 /*
0123     q.addQueryItem(QLatin1String("lang"), d->jobs.first().language);
0124 */
0125     netUrl.setQuery(q);
0126 
0127     QNetworkRequest netRequest(netUrl);
0128     netRequest.setRawHeader("User-Agent", getUserAgentName().toLatin1());
0129 
0130     d->jobs.first().netReply = d->mngr->get(netRequest);
0131 }
0132 
0133 /**
0134  * Takes the coordinate of each image and then connects to Open Street Map's reverse geocoding service.
0135  * @param rgList A list containing information needed in reverse geocoding process. At this point, it contains only coordinates.
0136  * @param language The language in which the data will be returned.
0137  */
0138 void BackendGeonamesUSRG::callRGBackend(const QList<RGInfo>& rgList, const QString& language)
0139 {
0140     d->errorMessage.clear();
0141 
0142     for (int i = 0 ; i < rgList.count() ; ++i)
0143     {
0144         bool foundIt = false;
0145 
0146         for (int j = 0 ; j < d->jobs.count() ; ++j)
0147         {
0148             if (d->jobs[j].request.first().coordinates.sameLonLatAs(rgList[i].coordinates))
0149             {
0150                 d->jobs[j].request << rgList[i];
0151                 d->jobs[j].language = language;
0152                 foundIt             = true;
0153                 break;
0154             }
0155         }
0156 
0157         if (!foundIt)
0158         {
0159             GeonamesUSInternalJobs newJob;
0160             newJob.request << rgList.at(i);
0161             newJob.language = language;
0162             d->jobs << newJob;
0163         }
0164     }
0165 
0166     nextPhoto();
0167 }
0168 
0169 /**
0170  * The data is returned from Open Street Map in a XML. This function translates the XML into a QMap.
0171  * @param xmlData The returned XML.
0172  */
0173 QMap<QString, QString> BackendGeonamesUSRG::makeQMapFromXML(const QString& xmlData)
0174 {
0175     QMap<QString, QString> mappedData;
0176     QString resultString;
0177     QDomDocument doc;
0178 
0179     doc.setContent(xmlData);
0180 
0181     QDomElement docElem = doc.documentElement();
0182     QDomNode n          = docElem.firstChild().firstChild();
0183 
0184     while (!n.isNull())
0185     {
0186         const QDomElement e = n.toElement();
0187 
0188         if (!e.isNull())
0189         {
0190             if ((e.tagName() == QLatin1String("adminName2")) ||
0191                 (e.tagName() == QLatin1String("adminName1")) ||
0192                 (e.tagName() == QLatin1String("placename")))
0193             {
0194                 mappedData.insert(e.tagName(), e.text());
0195                 resultString.append(e.tagName() + QLatin1Char(':') + e.text() + QLatin1Char('\n'));
0196             }
0197         }
0198 
0199         n = n.nextSibling();
0200 
0201     }
0202 
0203     return mappedData;
0204 }
0205 
0206 /**
0207  * @return Error message, if any.
0208  */
0209 QString BackendGeonamesUSRG::getErrorMessage()
0210 {
0211     return d->errorMessage;
0212 }
0213 
0214 /**
0215  * @return Backend name.
0216  */
0217 QString BackendGeonamesUSRG::backendName()
0218 {
0219     return QLatin1String("GeonamesUS");
0220 }
0221 
0222 void BackendGeonamesUSRG::slotFinished(QNetworkReply* reply)
0223 {
0224     for (int i = 0 ; i < d->jobs.count() ; ++i)
0225     {
0226         if (d->jobs.at(i).netReply == reply)
0227         {
0228             if (reply->error() != QNetworkReply::NoError)
0229             {
0230                 d->errorMessage = reply->errorString();
0231                 Q_EMIT signalRGReady(d->jobs.first().request);
0232                 reply->deleteLater();
0233                 d->jobs.clear();
0234 
0235                 return;
0236             }
0237 
0238             d->jobs[i].data.append(reply->readAll());
0239             break;
0240         }
0241     }
0242 
0243     for (int i = 0 ; i < d->jobs.count() ; ++i)
0244     {
0245         if (d->jobs.at(i).netReply == reply)
0246         {
0247             QString dataString;
0248             dataString = QString::fromUtf8(d->jobs[i].data.constData(),qstrlen(d->jobs[i].data.constData()));
0249             int pos    = dataString.indexOf(QLatin1String("<geonames"));
0250             dataString.remove(0,pos);
0251             dataString.chop(1);
0252 
0253             QMap<QString, QString> resultMap = makeQMapFromXML(dataString);
0254 
0255             for (int j = 0 ; j < d->jobs[i].request.count() ; ++j)
0256             {
0257                 d->jobs[i].request[j].rgData = resultMap;
0258             }
0259 
0260             Q_EMIT signalRGReady(d->jobs[i].request);
0261 
0262             d->jobs.removeAt(i);
0263 
0264             if (!d->jobs.isEmpty())
0265             {
0266                 QTimer::singleShot(500, this, SLOT(nextPhoto()));
0267             }
0268 
0269             reply->deleteLater();
0270             break;
0271         }
0272     }
0273 }
0274 
0275 void BackendGeonamesUSRG::cancelRequests()
0276 {
0277     d->jobs.clear();
0278     d->errorMessage.clear();
0279 }
0280 
0281 } // namespace Digikam
0282 
0283 #include "moc_backend-geonamesUS-rg.cpp"