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"