KWeatherCore

metnoparser.cpp
1/*
2 * SPDX-FileCopyrightText: 2020-2021 Han Young <hanyoung@protonmail.com>
3 * SPDX-FileCopyrightText: 2020 Devin Lin <espidev@gmail.com>
4 *
5 * SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7
8#include "kweathercore_p.h"
9#include "metnoparser_p.h"
10
11#include <KHolidays/SunRiseSet>
12#include <kholidays_version.h>
13
14#include <QJsonArray>
15#include <QJsonDocument>
16#include <QJsonObject>
17
18using namespace KWeatherCore;
19
20void MetNoParser::parseLocationForecast(const QByteArray &data)
21{
22 const QJsonDocument jsonDocument = QJsonDocument::fromJson(data);
23
24 if (jsonDocument.isObject()) {
25 const QJsonObject obj = jsonDocument.object();
26 const QJsonObject prop = obj[QLatin1String("properties")].toObject();
27
28 if (prop.contains(QLatin1String("timeseries"))) {
29 const QJsonArray timeseries = prop[QLatin1String("timeseries")].toArray();
30
31 // loop over all forecast data
32 for (const auto &ref : std::as_const(timeseries)) {
33 parseOneElement(ref.toObject());
34 }
35 }
36 }
37}
38
39void MetNoParser::parseOneElement(const QJsonObject &obj)
40{
41 const QJsonObject data = obj[QLatin1String("data")].toObject();
42 const QJsonObject instant = data[QLatin1String("instant")].toObject()[QLatin1String("details")].toObject();
43 // ignore last forecast, which does not have enough data
44 if (!data.contains(QLatin1String("next_6_hours")) && !data.contains(QLatin1String("next_1_hours"))) {
45 return;
46 }
47
48 // get symbolCode and precipitation amount
49 QString symbolCode;
50 double precipitationAmount = 0;
51 // some fields contain only "next_1_hours", and others may contain only
52 // "next_6_hours"
53 if (data.contains(QLatin1String("next_1_hours"))) {
54 const QJsonObject nextOneHours = data[QLatin1String("next_1_hours")].toObject();
55 symbolCode = nextOneHours[QLatin1String("summary")].toObject()[QLatin1String("symbol_code")].toString(QLatin1String("unknown"));
56 precipitationAmount = nextOneHours[QLatin1String("details")].toObject()[QLatin1String("precipitation_amount")].toDouble();
57 } else {
58 const QJsonObject nextSixHours = data[QLatin1String("next_6_hours")].toObject();
59 symbolCode = nextSixHours[QLatin1String("summary")].toObject()[QLatin1String("symbol_code")].toString(QLatin1String("unknown"));
60 precipitationAmount = nextSixHours[QLatin1String("details")].toObject()[QLatin1String("precipitation_amount")].toDouble();
61 }
62
63 symbolCode = symbolCode.split(QLatin1Char('_'))[0]; // trim _[day/night] from end -
64 // https://api.met.no/weatherapi/weathericon/2.0/legends
66 hourForecast.setNeutralWeatherIcon(KWeatherCorePrivate::resolveAPIWeatherDesc(symbolCode + QLatin1String("_neutral")).icon);
67 hourForecast.setTemperature(instant[QLatin1String("air_temperature")].toDouble());
68 hourForecast.setPressure(instant[QLatin1String("air_pressure_at_sea_level")].toDouble());
69 hourForecast.setWindDirectionDegree(instant[QLatin1String("wind_from_direction")].toDouble());
70 hourForecast.setWindSpeed(instant[QLatin1String("wind_speed")].toDouble());
71 hourForecast.setHumidity(instant[QLatin1String("relative_humidity")].toDouble());
72 hourForecast.setFog(instant[QLatin1String("fog_area_fraction")].toDouble());
73 hourForecast.setUvIndex(instant[QLatin1String("ultraviolet_index_clear_sky")].toDouble());
74 hourForecast.setPrecipitationAmount(precipitationAmount);
75 hourForecast.setSymbolCode(symbolCode);
76 hourlyForecast.push_back(std::move(hourForecast));
77}
78
79bool MetNoParser::isDayTime(const QDateTime &dt) const
80{
81 const auto sunriseTime = KHolidays::SunRiseSet::utcSunrise(dt.date(), forecast.latitude(), forecast.longitude());
82 const auto sunsetTime = KHolidays::SunRiseSet::utcSunset(dt.date(), forecast.latitude(), forecast.longitude());
83
84#if KHOLIDAYS_VERSION >= QT_VERSION_CHECK(5, 97, 0)
85 // polar day/night: there is no sunrise/sunset
86 if (!sunriseTime.isValid() || !sunsetTime.isValid()) {
87 return KHolidays::SunRiseSet::isPolarDay(dt.date(), forecast.latitude());
88 }
89#endif
90
91 auto sunrise = QDateTime(dt.date(), sunriseTime, Qt::UTC);
92 auto sunset = QDateTime(dt.date(), sunsetTime, Qt::UTC);
93
94 // sunset before sunrise means the sunset actually happens the next day
95 if (dt >= sunrise && sunset < sunrise) {
96 sunset = sunset.addDays(1);
97 } else if (dt < sunrise && sunset < sunrise) {
98 sunrise = sunrise.addDays(-1);
99 }
100
101 // 30 min threshold
102 return sunrise.addSecs(-1800) <= dt && sunset.addSecs(1800) >= dt;
103}
104
105void MetNoParser::applySunriseToForecast(const QTimeZone &timezone)
106{
107 // ************* Lambda *************** //
108 auto getSymbolCodeDescription = [](bool isDay, const QString &symbolCode) {
109 return isDay ? KWeatherCorePrivate::resolveAPIWeatherDesc(symbolCode + QStringLiteral("_day")).desc
110 : KWeatherCorePrivate::resolveAPIWeatherDesc(symbolCode + QStringLiteral("_night")).desc;
111 };
112
113 auto getSymbolCodeIcon = [](bool isDay, const QString &symbolCode) {
114 return isDay ? KWeatherCorePrivate::resolveAPIWeatherDesc(symbolCode + QStringLiteral("_day")).icon
115 : KWeatherCorePrivate::resolveAPIWeatherDesc(symbolCode + QStringLiteral("_night")).icon;
116 };
117
118 // ******* code ******** //
119 for (auto &hourForecast : hourlyForecast) {
120 hourForecast.setDate(hourForecast.date().toTimeZone(timezone));
121
122 bool isDay;
123 isDay = isDayTime(hourForecast.date());
124 hourForecast.setWeatherIcon(getSymbolCodeIcon(isDay, hourForecast.symbolCode())); // set day/night icon
125 hourForecast.setWeatherDescription(getSymbolCodeDescription(isDay, hourForecast.symbolCode()));
126 forecast += std::move(hourForecast);
127 }
128
129 // save to cache
130 QFile file(KWeatherCorePrivate::getCacheDirectory(forecast.latitude(), forecast.longitude()).path() + QStringLiteral("/cache.json"));
131
132 if (file.open(QIODevice::WriteOnly)) {
133 file.write(QJsonDocument(forecast.toJson()).toJson(QJsonDocument::Compact));
134 } else {
135 qWarning() << "write to cache failed";
136 }
137}
Class represents weatherforecast in a hour.
char * toString(const EngineQuery &query)
KHOLIDAYS_EXPORT QTime utcSunset(const QDate &date, double latitude, double longitude)
KHOLIDAYS_EXPORT QTime utcSunrise(const QDate &date, double latitude, double longitude)
KHOLIDAYS_EXPORT bool isPolarDay(const QDate &date, double latitude)
QDateTime addSecs(qint64 s) const const
QDate date() const const
QDateTime fromString(QStringView string, QStringView format, QCalendar cal)
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
bool isObject() const const
QJsonObject object() const const
QByteArray toJson(JsonFormat format) const const
bool contains(QLatin1StringView key) const const
QJsonValue value(QLatin1StringView key) const const
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
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.