KWeatherCore

locationquery.cpp
1 /*
2  * SPDX-FileCopyrightText: 2020-2021 Han Young <[email protected]>
3  * SPDX-FileCopyrightText: 2020 Devin Lin <[email protected]>
4  *
5  * SPDX-License-Identifier: LGPL-2.0-or-later
6  */
7 #include "locationquery.h"
8 #include "kweathercore_p.h"
9 #include <QGeoPositionInfoSource>
10 #include <QJsonArray>
11 #include <QJsonDocument>
12 #include <QJsonObject>
13 #include <QNetworkAccessManager>
14 #include <QNetworkReply>
15 #include <QUrlQuery>
16 #include <optional>
17 namespace KWeatherCore
18 {
19 class LocationQueryPrivate : public QObject
20 {
21  Q_OBJECT
22 public:
23  LocationQueryPrivate(LocationQuery *parent);
24  void requestUpdate();
25  void query(QString name, int number);
26 Q_SIGNALS:
27  void located(const KWeatherCore::LocationQueryResult &);
28  void queryFinished(std::vector<LocationQueryResult> result);
29  void queryError();
30 private Q_SLOTS:
31  void positionUpdated(const QGeoPositionInfo &update);
32  void handleQueryResult(QNetworkReply *reply);
33 
34 private:
35  QNetworkAccessManager *manager = nullptr;
36  QGeoPositionInfoSource *locationSource = nullptr;
37 };
38 
39 LocationQueryPrivate::LocationQueryPrivate(LocationQuery *parent)
40  : QObject(parent)
41  , manager(new QNetworkAccessManager(this))
42  , locationSource(QGeoPositionInfoSource::createDefaultSource(this))
43 {
44  locationSource->stopUpdates();
45 
46  connect(locationSource, &QGeoPositionInfoSource::positionUpdated, this, &LocationQueryPrivate::positionUpdated);
47  connect(this, &LocationQueryPrivate::queryFinished, parent, &LocationQuery::queryFinished);
48  connect(this, &LocationQueryPrivate::queryError, parent, &LocationQuery::queryError);
49  connect(this, &LocationQueryPrivate::located, parent, &LocationQuery::located);
50 }
51 
52 void LocationQueryPrivate::requestUpdate()
53 {
54  locationSource->requestUpdate();
55 }
56 void LocationQueryPrivate::positionUpdated(const QGeoPositionInfo &update)
57 {
58  auto lat = self()->toFixedString(update.coordinate().latitude());
59  auto lon = self()->toFixedString(update.coordinate().longitude());
60  QUrl url(QStringLiteral("http://api.geonames.org/findNearbyJSON"));
61  QUrlQuery urlQuery;
62 
63  urlQuery.addQueryItem(QStringLiteral("lat"), lat);
64  urlQuery.addQueryItem(QStringLiteral("lng"), lon);
65  urlQuery.addQueryItem(QStringLiteral("username"), QStringLiteral("kweatherdev"));
66  url.setQuery(urlQuery);
67 
68  auto req = QNetworkRequest(url);
69 
70  qWarning() << "lat: " << lat << "lon: " << lon;
71  auto reply = manager->get(req);
72 
73  connect(reply, &QNetworkReply::finished, [this, update, reply] {
74  QJsonDocument document = QJsonDocument::fromJson(reply->readAll());
75  QJsonObject root = document.object();
76  auto array = root[QStringLiteral("geonames")].toArray();
77  if (array.size()) {
78  Q_EMIT this->located(LocationQueryResult(update.coordinate().latitude(),
79  update.coordinate().longitude(),
80  array.at(0)[QStringLiteral("toponymName")].toString(),
81  array.at(0)[QStringLiteral("name")].toString(),
82  array.at(0)[QStringLiteral("countryCode")].toString(),
83  array.at(0)[QStringLiteral("countryName")].toString(),
84  QString::number(root[QStringLiteral("geonameId")].toInt())));
85  }
86  reply->deleteLater();
87  });
88 }
90  : QObject(parent)
91  , d(new LocationQueryPrivate(this))
92 {
93 }
94 void LocationQuery::query(QString name, int number)
95 {
96  d->query(std::move(name), number);
97 }
98 
99 void LocationQueryPrivate::query(QString name, int number)
100 {
101  QUrl url(QStringLiteral("http://api.geonames.org/searchJSON"));
102  QUrlQuery urlQuery;
103 
104  urlQuery.addQueryItem(QStringLiteral("q"), name);
105  urlQuery.addQueryItem(QStringLiteral("maxRows"), QString::number(number));
106  urlQuery.addQueryItem(QStringLiteral("username"), QStringLiteral("kweatherdev"));
107  url.setQuery(urlQuery);
108 
109  auto reply = manager->get(QNetworkRequest(url));
110  connect(reply, &QNetworkReply::finished, [reply, this] {
111  this->handleQueryResult(reply);
112  });
113 }
115 {
116  d->requestUpdate();
117 }
118 static std::optional<QString> findSubdivision(const QJsonObject &json)
119 {
120  const auto adminCodeIter = json.constFind(QStringLiteral("adminCodes1"));
121  if (adminCodeIter == json.constEnd()) {
122  return std::nullopt;
123  } else {
124  return (*adminCodeIter).toObject().value(QStringLiteral("ISO3166_2")).toString();
125  }
126 }
127 void LocationQueryPrivate::handleQueryResult(QNetworkReply *reply)
128 {
129  QJsonDocument document = QJsonDocument::fromJson(reply->readAll());
130  QJsonObject root = document.object();
131  reply->deleteLater();
132 
133  auto counts = root[QStringLiteral("totalResultsCount")].toInt();
134  // if no result
135  if (!counts) {
136  Q_EMIT queryError();
137  return;
138  }
139  std::vector<LocationQueryResult> retVec;
140 
141  // if our api calls reached daily limit
142  if (root[QStringLiteral("status")].toObject()[QStringLiteral("value")].toInt() == 18) {
143  Q_EMIT queryError();
144  qWarning("API calls reached daily limit");
145  return;
146  }
147  auto geonames = root.value(QStringLiteral("geonames")).toArray();
148  // add query results
149  for (const auto &resRef : qAsConst(geonames)) {
150  auto res = resRef.toObject();
151  auto result = LocationQueryResult(res.value(QStringLiteral("lat")).toString().toFloat(),
152  res.value(QStringLiteral("lng")).toString().toFloat(),
153  res.value(QStringLiteral("toponymName")).toString(),
154  res.value(QStringLiteral("name")).toString(),
155  res.value(QStringLiteral("countryCode")).toString(),
156  res.value(QStringLiteral("countryName")).toString(),
157  QString::number(res.value(QStringLiteral("geonameId")).toInt()),
158  findSubdivision(res));
159  retVec.push_back(result);
160  }
161 
162  Q_EMIT queryFinished(retVec);
163 }
164 }
165 
166 #include "locationquery.moc"
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
Class represents location query result.
std::optional< QSqlQuery > query(const QString &queryStatement)
QJsonObject::const_iterator constEnd() const const
QJsonObject object() const const
void located(const LocationQueryResult &result)
current location has been determined
Q_SIGNALSQ_SIGNALS
void query(QString name, int number=30)
query query locations by name
void addQueryItem(const QString &key, const QString &value)
QString number(int n, int base)
QGeoCoordinate coordinate() const const
Q_OBJECTQ_OBJECT
QByteArray readAll()
void queryFinished(std::vector< LocationQueryResult > result)
the name search has completed
void deleteLater()
LocationQuery(QObject *parent=nullptr)
LocationQuery.
void locate()
locate current location
void setQuery(const QString &query, QUrl::ParsingMode mode)
void positionUpdated(const QGeoPositionInfo &update)
void queryError()
a error has encounted during query, network error or no result found
Q_SLOTSQ_SLOTS
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QJsonObject::const_iterator constFind(const QString &key) const const
Q_EMITQ_EMIT
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Sat Oct 16 2021 23:05:15 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.