File indexing completed on 2024-05-05 16:49:22
0001 /* 0002 * SPDX-FileCopyrightText: 2020-2021 Han Young <hanyoung@protonmail.com> 0003 * SPDX-FileCopyrightText: 2020 Devin Lin <espidev@gmail.com> 0004 * 0005 * SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 #include "locationquery.h" 0008 #include "kweathercore_p.h" 0009 #include <QGeoPositionInfoSource> 0010 #include <QJsonArray> 0011 #include <QJsonDocument> 0012 #include <QJsonObject> 0013 #include <QNetworkAccessManager> 0014 #include <QNetworkReply> 0015 #include <QUrlQuery> 0016 namespace KWeatherCore 0017 { 0018 class LocationQueryPrivate : public QObject 0019 { 0020 Q_OBJECT 0021 public: 0022 LocationQueryPrivate(LocationQuery *parent); 0023 void requestUpdate(); 0024 void query(QString name, int number); 0025 Q_SIGNALS: 0026 void located(const KWeatherCore::LocationQueryResult &); 0027 void queryFinished(std::vector<LocationQueryResult> result); 0028 void queryError(); 0029 private Q_SLOTS: 0030 void positionUpdated(const QGeoPositionInfo &update); 0031 void handleQueryResult(QNetworkReply *reply); 0032 0033 private: 0034 QNetworkAccessManager *manager = nullptr; 0035 QGeoPositionInfoSource *locationSource = nullptr; 0036 }; 0037 0038 LocationQueryPrivate::LocationQueryPrivate(LocationQuery *parent) 0039 : QObject(parent) 0040 , manager(new QNetworkAccessManager(this)) 0041 , locationSource(QGeoPositionInfoSource::createDefaultSource(this)) 0042 { 0043 locationSource->stopUpdates(); 0044 0045 connect(locationSource, 0046 &QGeoPositionInfoSource::positionUpdated, 0047 this, 0048 &LocationQueryPrivate::positionUpdated); 0049 connect(this, 0050 &LocationQueryPrivate::queryFinished, 0051 parent, 0052 &LocationQuery::queryFinished); 0053 connect(this, 0054 &LocationQueryPrivate::queryError, 0055 parent, 0056 &LocationQuery::queryError); 0057 connect( 0058 this, &LocationQueryPrivate::located, parent, &LocationQuery::located); 0059 } 0060 0061 void LocationQueryPrivate::requestUpdate() 0062 { 0063 locationSource->requestUpdate(); 0064 } 0065 void LocationQueryPrivate::positionUpdated(const QGeoPositionInfo &update) 0066 { 0067 auto lat = toFixedString(update.coordinate().latitude()); 0068 auto lon = toFixedString(update.coordinate().longitude()); 0069 QUrl url(QStringLiteral("http://api.geonames.org/findNearbyJSON")); 0070 QUrlQuery urlQuery; 0071 0072 urlQuery.addQueryItem(QStringLiteral("lat"), lat); 0073 urlQuery.addQueryItem(QStringLiteral("lng"), lon); 0074 urlQuery.addQueryItem(QStringLiteral("username"), 0075 QStringLiteral("kweatherdev")); 0076 url.setQuery(urlQuery); 0077 0078 auto req = QNetworkRequest(url); 0079 0080 qWarning() << "lat: " << lat << "lon: " << lon; 0081 auto reply = manager->get(req); 0082 0083 connect(reply, &QNetworkReply::finished, [this, update, reply] { 0084 QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); 0085 QJsonObject root = document.object(); 0086 auto array = root[QStringLiteral("geonames")].toArray(); 0087 if (array.size()) { 0088 Q_EMIT this->located(LocationQueryResult( 0089 update.coordinate().latitude(), 0090 update.coordinate().longitude(), 0091 array.at(0)[QStringLiteral("toponymName")].toString(), 0092 array.at(0)[QStringLiteral("name")].toString(), 0093 array.at(0)[QStringLiteral("countryCode")].toString(), 0094 array.at(0)[QStringLiteral("countryName")].toString(), 0095 QString::number(root[QStringLiteral("geonameId")].toInt()))); 0096 } 0097 reply->deleteLater(); 0098 }); 0099 } 0100 LocationQuery::LocationQuery(QObject *parent) 0101 : QObject(parent) 0102 , d(new LocationQueryPrivate(this)) 0103 { 0104 } 0105 void LocationQuery::query(QString name, int number) 0106 { 0107 d->query(std::move(name), number); 0108 } 0109 0110 void LocationQueryPrivate::query(QString name, int number) 0111 { 0112 QUrl url(QStringLiteral("http://api.geonames.org/searchJSON")); 0113 QUrlQuery urlQuery; 0114 0115 urlQuery.addQueryItem(QStringLiteral("q"), name); 0116 urlQuery.addQueryItem(QStringLiteral("maxRows"), QString::number(number)); 0117 urlQuery.addQueryItem(QStringLiteral("username"), 0118 QStringLiteral("kweatherdev")); 0119 url.setQuery(urlQuery); 0120 0121 auto reply = manager->get(QNetworkRequest(url)); 0122 connect(reply, &QNetworkReply::finished, [reply, this] { 0123 this->handleQueryResult(reply); 0124 }); 0125 } 0126 void LocationQuery::locate() 0127 { 0128 d->requestUpdate(); 0129 } 0130 void LocationQueryPrivate::handleQueryResult(QNetworkReply *reply) 0131 { 0132 QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); 0133 QJsonObject root = document.object(); 0134 reply->deleteLater(); 0135 0136 auto counts = root[QStringLiteral("totalResultsCount")].toInt(); 0137 // if no result 0138 if (!counts) { 0139 Q_EMIT queryError(); 0140 return; 0141 } 0142 std::vector<LocationQueryResult> retVec; 0143 0144 // if our api calls reached daily limit 0145 if (root[QStringLiteral("status")] 0146 .toObject()[QStringLiteral("value")] 0147 .toInt() == 18) { 0148 Q_EMIT queryError(); 0149 qWarning("API calls reached daily limit"); 0150 return; 0151 } 0152 auto geonames = root.value(QStringLiteral("geonames")).toArray(); 0153 // add query results 0154 for (const auto &resRef : qAsConst(geonames)) { 0155 auto res = resRef.toObject(); 0156 auto result = LocationQueryResult( 0157 res.value(QStringLiteral("lat")).toString().toFloat(), 0158 res.value(QStringLiteral("lng")).toString().toFloat(), 0159 res.value(QStringLiteral("toponymName")).toString(), 0160 res.value(QStringLiteral("name")).toString(), 0161 res.value(QStringLiteral("countryCode")).toString(), 0162 res.value(QStringLiteral("countryName")).toString(), 0163 QString::number(res.value(QStringLiteral("geonameId")).toInt())); 0164 retVec.push_back(result); 0165 } 0166 0167 Q_EMIT queryFinished(retVec); 0168 } 0169 } 0170 0171 #include "locationquery.moc"