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"