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 QString::number(res.value(QLatin1String("geonameId")).toInt()),
97 findSubdivision(res));
98 d->m_result.push_back(result);
99 }
100
101 Q_EMIT finished();
102 });
103}
104
105LocationQueryReply::LocationQueryReply(QGeoPositionInfoSource *source, QNetworkAccessManager *nam, QObject *parent)
106 : Reply(new LocationQueryReplyPrivate, parent)
107{
109 if (!source) {
110 d->setError(LocationQueryReply::NoService);
111 QMetaObject::invokeMethod(this, &LocationQueryReply::finished, Qt::QueuedConnection);
112 return;
113 }
114
115#if QT_CONFIG(permissions)
116 QLocationPermission permission;
119 switch (QCoreApplication::instance()->checkPermission(permission)) {
120 case Qt::PermissionStatus::Undetermined:
121 QCoreApplication::instance()->requestPermission(permission, this, [this, nam, source](const auto &permission) {
123 if (permission.status() == Qt::PermissionStatus::Granted) {
124 d->requestPosition(this, source, nam);
125 } else {
126 d->setError(LocationQueryReply::NoService);
127 Q_EMIT finished();
128 }
129 });
130 return;
131 case Qt::PermissionStatus::Denied:
132 d->setError(LocationQueryReply::NoService);
133 QMetaObject::invokeMethod(this, &LocationQueryReply::finished, Qt::QueuedConnection);
134 return;
135 case Qt::PermissionStatus::Granted:
136 d->requestPosition(this, source, nam);
137 break;
138 }
139#else
140 d->requestPosition(this, source, nam);
141#endif
142}
143
144LocationQueryReply::~LocationQueryReply() = default;
145
146void LocationQueryReplyPrivate::requestPosition(LocationQueryReply *q, QGeoPositionInfoSource *source, QNetworkAccessManager *nam)
147{
148 QObject::connect(source, &QGeoPositionInfoSource::positionUpdated, q, [this, q, nam](const QGeoPositionInfo &pos) {
149 const auto lat = pos.coordinate().latitude();
150 const auto lon = pos.coordinate().longitude();
151 QUrl url(QStringLiteral("http://api.geonames.org/findNearbyJSON"));
152 QUrlQuery urlQuery;
153
154 urlQuery.addQueryItem(QStringLiteral("lat"), KWeatherCorePrivate::toFixedString(lat));
155 urlQuery.addQueryItem(QStringLiteral("lng"), KWeatherCorePrivate::toFixedString(lon));
156 urlQuery.addQueryItem(QStringLiteral("username"), QStringLiteral("kweatherdev"));
157 url.setQuery(urlQuery);
158
159 auto req = QNetworkRequest(url);
160
161 qWarning() << "lat: " << lat << "lon: " << lon;
162 auto reply = nam->get(req);
163
164 QObject::connect(reply, &QNetworkReply::finished, q, [this, q, lat, lon, reply] {
165 reply->deleteLater();
166 const QJsonDocument document = QJsonDocument::fromJson(reply->readAll());
167 const QJsonObject root = document.object();
168 const auto array = root[QLatin1String("geonames")].toArray();
169 if (array.size()) {
170 m_result.push_back(LocationQueryResult(lat,
171 lon,
172 array.at(0)[QLatin1String("toponymName")].toString(),
173 array.at(0)[QLatin1String("name")].toString(),
174 array.at(0)[QLatin1String("countryCode")].toString(),
175 array.at(0)[QLatin1String("countryName")].toString(),
176 QString::number(root[QLatin1String("geonameId")].toInt())));
177 } else {
178 setError(Reply::NotFound);
179 }
180
181 Q_EMIT q->finished();
182 });
183 });
184
185 source->requestUpdate();
186}
187
188const std::vector<LocationQueryResult> &LocationQueryReply::result() const
189{
190 Q_D(const LocationQueryReply);
191 return d->m_result;
192}
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 Fri May 24 2024 12:02:06 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.