File indexing completed on 2025-01-19 03:51:19
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2010-06-01 0007 * Description : A simple backend to search OSM and Geonames.org. 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 "searchresultbackend.h" 0017 0018 // Qt includes 0019 0020 #include <QDomDocument> 0021 #include <QUrlQuery> 0022 0023 // KDE includes 0024 0025 #include <klocalizedstring.h> 0026 0027 // Local includes 0028 0029 #include "gpscommon.h" 0030 #include "digikam_debug.h" 0031 #include "networkmanager.h" 0032 0033 namespace DigikamGenericGeolocationEditPlugin 0034 { 0035 0036 class Q_DECL_HIDDEN SearchResultBackend::Private 0037 { 0038 public: 0039 0040 explicit Private() 0041 : netReply(nullptr), 0042 mngr (nullptr) 0043 { 0044 } 0045 0046 SearchResultBackend::SearchResult::List results; 0047 QString runningBackend; 0048 QByteArray searchData; 0049 QString errorMessage; 0050 0051 QNetworkReply* netReply; 0052 QNetworkAccessManager* mngr; 0053 }; 0054 0055 SearchResultBackend::SearchResultBackend(QObject* const parent) 0056 : QObject(parent), 0057 d (new Private()) 0058 { 0059 d->mngr = NetworkManager::instance()->getNetworkManager(this); 0060 0061 connect(d->mngr, SIGNAL(finished(QNetworkReply*)), 0062 this, SLOT(slotFinished(QNetworkReply*))); 0063 } 0064 0065 SearchResultBackend::~SearchResultBackend() 0066 { 0067 delete d; 0068 } 0069 0070 bool SearchResultBackend::search(const QString& backendName, const QString& searchTerm) 0071 { 0072 d->searchData.clear(); 0073 d->errorMessage.clear(); 0074 d->results.clear(); 0075 0076 if (backendName == QLatin1String("osm")) 0077 { 0078 d->runningBackend = backendName; 0079 0080 QUrl netUrl(QLatin1String("https://nominatim.openstreetmap.org/search")); 0081 0082 QUrlQuery q(netUrl); 0083 q.addQueryItem(QLatin1String("format"), QLatin1String("xml")); 0084 q.addQueryItem(QLatin1String("q"), searchTerm); 0085 netUrl.setQuery(q); 0086 0087 QNetworkRequest netRequest(netUrl); 0088 netRequest.setRawHeader("User-Agent", getUserAgentName().toLatin1()); 0089 0090 d->netReply = d->mngr->get(netRequest); 0091 0092 return true; 0093 } 0094 0095 if (backendName == QLatin1String("geonames.org")) 0096 { 0097 d->runningBackend = backendName; 0098 0099 // documentation: https://www.geonames.org/export/geonames-search.html 0100 0101 QUrl netUrl(QLatin1String("http://api.geonames.org/search")); // krazy:exclude=insecurenet 0102 0103 QUrlQuery q(netUrl); 0104 q.addQueryItem(QLatin1String("type"), QLatin1String("xml")); 0105 q.addQueryItem(QLatin1String("q"), searchTerm); 0106 q.addQueryItem(QLatin1String("username"), QLatin1String("digikam")); 0107 netUrl.setQuery(q); 0108 0109 QNetworkRequest netRequest(netUrl); 0110 netRequest.setRawHeader("User-Agent", getUserAgentName().toLatin1()); 0111 0112 d->netReply = d->mngr->get(netRequest); 0113 0114 return true; 0115 } 0116 0117 return false; 0118 } 0119 0120 void SearchResultBackend::slotFinished(QNetworkReply* reply) 0121 { 0122 if (reply != d->netReply) 0123 { 0124 return; 0125 } 0126 0127 if (reply->error() != QNetworkReply::NoError) 0128 { 0129 d->errorMessage = reply->errorString(); 0130 Q_EMIT signalSearchCompleted(); 0131 reply->deleteLater(); 0132 return; 0133 } 0134 0135 d->searchData.append(reply->readAll()); 0136 0137 const QString resultString = QString::fromUtf8(d->searchData.constData(), d->searchData.count()); 0138 0139 if (d->runningBackend == QLatin1String("osm")) 0140 { 0141 QDomDocument doc; 0142 doc.setContent(resultString); // error-handling 0143 QDomElement docElement = doc.documentElement(); // error-handling 0144 0145 for (QDomNode resultNode = docElement.firstChild(); !resultNode.isNull(); resultNode = resultNode.nextSibling()) 0146 { 0147 QDomElement resultElement = resultNode.toElement(); 0148 0149 if (resultElement.isNull()) 0150 { 0151 continue; 0152 } 0153 0154 if (resultElement.tagName() != QLatin1String("place")) 0155 { 0156 continue; 0157 } 0158 0159 const QString boundingBoxString = resultElement.attribute(QLatin1String("boundingbox")); 0160 const QString latString = resultElement.attribute(QLatin1String("lat")); 0161 const QString lonString = resultElement.attribute(QLatin1String("lon")); 0162 const QString displayName = resultElement.attribute(QLatin1String("display_name")); 0163 const QString placeId = resultElement.attribute(QLatin1String("place_id")); 0164 0165 if (latString.isEmpty() || lonString.isEmpty() || displayName.isEmpty()) 0166 { 0167 continue; 0168 } 0169 0170 // now parse the strings: 0171 0172 qreal lat; 0173 qreal lon; 0174 bool okay = false; 0175 lat = latString.toDouble(&okay); 0176 0177 if (okay) 0178 { 0179 lon = lonString.toDouble(&okay); 0180 } 0181 0182 if (!okay) 0183 { 0184 continue; 0185 } 0186 0187 SearchResult result; 0188 result.coordinates = GeoCoordinates(lat, lon); 0189 result.name = displayName; 0190 0191 if (!placeId.isEmpty()) 0192 { 0193 result.internalId = QLatin1String("osm-") + placeId; 0194 } 0195 0196 // TODO: parse bounding box 0197 0198 d->results << result; 0199 } 0200 } 0201 else if (d->runningBackend == QLatin1String("geonames.org")) 0202 { 0203 QDomDocument doc; 0204 doc.setContent(resultString); // error-handling 0205 QDomElement docElement = doc.documentElement(); // error-handling 0206 qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG)<<docElement.toElement().tagName(); 0207 0208 for (QDomNode resultNode = docElement.firstChild() ; !resultNode.isNull() ; resultNode = resultNode.nextSibling()) 0209 { 0210 QDomElement resultElement = resultNode.toElement(); 0211 qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << resultElement.tagName(); 0212 0213 if (resultElement.isNull()) 0214 { 0215 continue; 0216 } 0217 0218 if (resultElement.tagName() != QLatin1String("geoname")) 0219 { 0220 continue; 0221 } 0222 0223 QString latString; 0224 QString lonString; 0225 QString displayName; 0226 QString geoNameId; 0227 0228 for (QDomNode resultSubNode = resultElement.firstChild(); !resultSubNode.isNull(); resultSubNode = resultSubNode.nextSibling()) 0229 { 0230 QDomElement resultSubElement = resultSubNode.toElement(); 0231 0232 if (resultSubElement.isNull()) 0233 { 0234 continue; 0235 } 0236 0237 if (resultSubElement.tagName() == QLatin1String("lat")) 0238 { 0239 latString = resultSubElement.text(); 0240 } 0241 else if (resultSubElement.tagName() == QLatin1String("lng")) 0242 { 0243 lonString = resultSubElement.text(); 0244 } 0245 else if (resultSubElement.tagName() == QLatin1String("name")) 0246 { 0247 displayName = resultSubElement.text(); 0248 } 0249 else if (resultSubElement.tagName() == QLatin1String("geonameId")) 0250 { 0251 geoNameId = resultSubElement.text(); 0252 } 0253 } 0254 0255 if (latString.isEmpty() || lonString.isEmpty() || displayName.isEmpty()) 0256 { 0257 continue; 0258 } 0259 0260 // now parse the strings: 0261 0262 qreal lat; 0263 qreal lon; 0264 bool okay = false; 0265 lat = latString.toDouble(&okay); 0266 0267 if (okay) 0268 { 0269 lon = lonString.toDouble(&okay); 0270 } 0271 0272 if (!okay) 0273 { 0274 continue; 0275 } 0276 0277 SearchResult result; 0278 result.coordinates = GeoCoordinates(lat, lon); 0279 result.name = displayName; 0280 0281 if (!geoNameId.isEmpty()) 0282 { 0283 result.internalId = QLatin1String("geonames.org-") + geoNameId; 0284 } 0285 0286 d->results << result; 0287 } 0288 } 0289 0290 Q_EMIT signalSearchCompleted(); 0291 0292 reply->deleteLater(); 0293 } 0294 0295 SearchResultBackend::SearchResult::List SearchResultBackend::getResults() const 0296 { 0297 return d->results; 0298 } 0299 0300 QString SearchResultBackend::getErrorMessage() const 0301 { 0302 return d->errorMessage; 0303 } 0304 0305 QList<QPair<QString, QString> > SearchResultBackend::getBackends() const 0306 { 0307 QList<QPair<QString, QString> > resultList; 0308 resultList << QPair<QString, QString>(i18n("GeoNames"), QLatin1String("geonames.org")); 0309 resultList << QPair<QString, QString>(i18n("OSM"), QLatin1String("osm")); 0310 0311 return resultList; 0312 } 0313 0314 } // namespace DigikamGenericGeolocationEditPlugin 0315 0316 #include "moc_searchresultbackend.cpp"