KWeatherCore

locationqueryreply.cpp
1/*
2 * SPDX-FileCopyrightText: 2020-2021 Han Young <hanyoung@protonmail.com>
3 * SPDX-FileCopyrightText: 2020 Devin Lin <espidev@gmail.com>
4 * SPDX-FileCopyrightText: 2022 Volker Krause <vkrause@kde.org>
5 * SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7
8#include "locationqueryreply.h"
9#include "kweathercore_p.h"
10#include "locationquery.h"
11#include "locationqueryresult.h"
12#include "reply_p.h"
13
14#include <QCoreApplication>
15#include <QGeoPositionInfo>
16#include <QGeoPositionInfoSource>
17#include <QJsonArray>
18#include <QJsonDocument>
19#include <QJsonObject>
20#include <QNetworkAccessManager>
21#include <QNetworkReply>
22#include <QUrlQuery>
23
24#if QT_CONFIG(permissions)
25#include <QPermissions>
26#endif
27
28using namespace KWeatherCore;
29
30class KWeatherCore::LocationQueryReplyPrivate : public ReplyPrivate
31{
32public:
33 void requestPosition(LocationQueryReply *q, QGeoPositionInfoSource *source, QNetworkAccessManager *nam);
34 std::vector<LocationQueryResult> m_result;
35};
36
37static std::optional<QString> findSubdivision(const QJsonObject &json)
38{
39 const auto adminCodeIter = json.constFind(QLatin1String("adminCodes1"));
40 if (adminCodeIter == json.constEnd()) {
41 return std::nullopt;
42 } else {
43 return (*adminCodeIter).toObject().value(QLatin1String("ISO3166_2")).toString();
44 }
45}
46
47LocationQueryReply::LocationQueryReply(const QString &name, int number, QNetworkAccessManager *nam, QObject *parent)
48 : Reply(new LocationQueryReplyPrivate, parent)
49{
50 QUrl url(QStringLiteral("http://api.geonames.org/searchJSON"));
51 QUrlQuery urlQuery;
52
53 urlQuery.addQueryItem(QStringLiteral("q"), name);
54 urlQuery.addQueryItem(QStringLiteral("maxRows"), QString::number(number));
55 urlQuery.addQueryItem(QStringLiteral("username"), QStringLiteral("kweatherdev"));
56 url.setQuery(urlQuery);
57
58 auto reply = nam->get(QNetworkRequest(url));
59 QObject::connect(reply, &QNetworkReply::finished, this, [reply, this]() {
61 reply->deleteLater();
62 if (reply->error() != QNetworkReply::NoError) {
63 d->setError(Reply::NetworkError, reply->errorString());
64 Q_EMIT finished();
65 return;
66 }
67
68 const QJsonDocument document = QJsonDocument::fromJson(reply->readAll());
69 const QJsonObject root = document.object();
70
71 auto counts = root[QLatin1String("totalResultsCount")].toInt();
72 // if no result
73 if (!counts) {
74 d->setError(Reply::NotFound);
75 Q_EMIT finished();
76 return;
77 }
78
79 // if our api calls reached daily limit
80 if (root[QLatin1String("status")].toObject()[QLatin1String("value")].toInt() == 18) {
81 d->setError(Reply::RateLimitExceeded);
82 qWarning("API calls reached daily limit");
83 Q_EMIT finished();
84 return;
85 }
86
87 const auto geonames = root.value(QLatin1String("geonames")).toArray();
88 // add query results
89 for (const auto &resRef : std::as_const(geonames)) {
90 const auto res = resRef.toObject();
91 const auto result = LocationQueryResult(res.value(QLatin1String("lat")).toString().toFloat(),
92 res.value(QLatin1String("lng")).toString().toFloat(),
93 res.value(QLatin1String("toponymName")).toString(),
94 res.value(QLatin1String("name")).toString(),
95 res.value(QLatin1String("countryCode")).toString(),
96 res.value(QLatin1String("countryName")).toString(),
97 QString::number(res.value(QLatin1String("geonameId")).toInt()),
98 findSubdivision(res));
99 d->m_result.push_back(result);
100 }
101
102 Q_EMIT finished();
103 });
104}
105
106LocationQueryReply::LocationQueryReply(QGeoPositionInfoSource *source, QNetworkAccessManager *nam, QObject *parent)
107 : Reply(new LocationQueryReplyPrivate, parent)
108{
110 if (!source) {
111 d->setError(LocationQueryReply::NoService);
112 QMetaObject::invokeMethod(this, &LocationQueryReply::finished, Qt::QueuedConnection);
113 return;
114 }
115
116#if QT_CONFIG(permissions)
117 QLocationPermission permission;
120 switch (QCoreApplication::instance()->checkPermission(permission)) {
121 case Qt::PermissionStatus::Undetermined:
122 QCoreApplication::instance()->requestPermission(permission, this, [this, nam, source](const auto &permission) {
124 if (permission.status() == Qt::PermissionStatus::Granted) {
125 d->requestPosition(this, source, nam);
126 } else {
127 d->setError(LocationQueryReply::NoService);
128 Q_EMIT finished();
129 }
130 });
131 return;
132 case Qt::PermissionStatus::Denied:
133 d->setError(LocationQueryReply::NoService);
134 QMetaObject::invokeMethod(this, &LocationQueryReply::finished, Qt::QueuedConnection);
135 return;
136 case Qt::PermissionStatus::Granted:
137 d->requestPosition(this, source, nam);
138 break;
139 }
140#else
141 d->requestPosition(this, source, nam);
142#endif
143}
144
145LocationQueryReply::~LocationQueryReply() = default;
146
147void LocationQueryReplyPrivate::requestPosition(LocationQueryReply *q, QGeoPositionInfoSource *source, QNetworkAccessManager *nam)
148{
149 QObject::connect(source, &QGeoPositionInfoSource::positionUpdated, q, [this, q, nam](const QGeoPositionInfo &pos) {
150 const auto lat = pos.coordinate().latitude();
151 const auto lon = pos.coordinate().longitude();
152 QUrl url(QStringLiteral("http://api.geonames.org/findNearbyJSON"));
153 QUrlQuery urlQuery;
154
155 urlQuery.addQueryItem(QStringLiteral("lat"), KWeatherCorePrivate::toFixedString(lat));
156 urlQuery.addQueryItem(QStringLiteral("lng"), KWeatherCorePrivate::toFixedString(lon));
157 urlQuery.addQueryItem(QStringLiteral("username"), QStringLiteral("kweatherdev"));
158 url.setQuery(urlQuery);
159
160 auto req = QNetworkRequest(url);
161
162 qWarning() << "lat: " << lat << "lon: " << lon;
163 auto reply = nam->get(req);
164
165 QObject::connect(reply, &QNetworkReply::finished, q, [this, q, lat, lon, reply] {
166 reply->deleteLater();
167 const QJsonDocument document = QJsonDocument::fromJson(reply->readAll());
168 const QJsonObject root = document.object();
169 const auto array = root[QLatin1String("geonames")].toArray();
170 if (array.size()) {
171 m_result.push_back(LocationQueryResult(lat,
172 lon,
173 array.at(0)[QLatin1String("toponymName")].toString(),
174 array.at(0)[QLatin1String("name")].toString(),
175 array.at(0)[QLatin1String("countryCode")].toString(),
176 array.at(0)[QLatin1String("countryName")].toString(),
177 QString::number(root[QLatin1String("geonameId")].toInt())));
178 } else {
179 setError(Reply::NotFound);
180 }
181
182 Q_EMIT q->finished();
183 });
184 });
185
186 source->requestUpdate();
187}
188
189const std::vector<LocationQueryResult> &LocationQueryReply::result() const
190{
191 Q_D(const LocationQueryReply);
192 return d->m_result;
193}
Asynchronous reply for a location query.
const std::vector< LocationQueryResult > & result() const
Result of the location query.
Class represents location query result.
Base class for all asynchronous jobs.
Definition reply.h:24
void finished()
Emitted once the job has been finished, either successfully or with an error.
@ NetworkError
Network operation failed.
Definition reply.h:32
@ RateLimitExceeded
Remote API rate limited exceeded.
Definition reply.h:33
@ NotFound
The queried information could not be found by the backend (e.g. unknown location).
Definition reply.h:34
char * toString(const EngineQuery &query)
KNOTIFICATIONS_EXPORT Qt::PermissionStatus checkPermission()
QCoreApplication * instance()
void requestPermission(const QPermission &permission, Functor &&functor)
QGeoCoordinate coordinate() const const
void positionUpdated(const QGeoPositionInfo &update)
virtual void requestUpdate(int timeout)=0
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
QJsonObject object() const const
const_iterator constEnd() const const
const_iterator constFind(QLatin1StringView key) const const
QJsonValue value(QLatin1StringView key) const const
QJsonArray toArray() const const
void setAccuracy(Accuracy accuracy)
void setAvailability(Availability availability)
bool invokeMethod(QObject *context, Functor &&function, FunctorReturnType *ret)
QNetworkReply * get(const QNetworkRequest &request)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QString number(double n, char format, int precision)
QueuedConnection
void addQueryItem(const QString &key, const QString &value)
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:20:42 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.