File indexing completed on 2024-05-05 16:49:23

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 "sunrisesource.h"
0008 #include "kweathercore_p.h"
0009 #include <QJsonArray>
0010 #include <QJsonDocument>
0011 #include <QJsonObject>
0012 #include <QNetworkAccessManager>
0013 #include <QNetworkReply>
0014 #include <QUrlQuery>
0015 #include <QTimeZone>
0016 
0017 namespace KWeatherCore
0018 {
0019 SunriseSource::SunriseSource(double latitude,
0020                              double longitude,
0021                              const QString &timezone,
0022                              const std::vector<Sunrise> &sunrise,
0023                              QObject *parent)
0024     : QObject(parent)
0025     , m_latitude(latitude)
0026     , m_longitude(longitude)
0027     , m_timezone(timezone)
0028     , m_sunriseVec(sunrise)
0029     , m_manager(new QNetworkAccessManager(this))
0030 {
0031     connect(m_manager,
0032             &QNetworkAccessManager::finished,
0033             this,
0034             &SunriseSource::parseResults);
0035 }
0036 void SunriseSource::requestData()
0037 {
0038     // pop older data
0039     popDay();
0040 
0041     if (m_sunriseVec.size() >= 10) // don't update if we have enough data
0042     {
0043         Q_EMIT finished();
0044         return;
0045     }
0046 
0047     auto m_offset = QDateTime::currentDateTime().toTimeZone(QTimeZone(m_timezone.toUtf8())).offsetFromUtc();
0048 
0049     QUrl url(QStringLiteral("https://api.met.no/weatherapi/sunrise/2.0/.json"));
0050     QUrlQuery query;
0051     query.addQueryItem(QStringLiteral("lat"), QString::number(m_latitude));
0052     query.addQueryItem(QStringLiteral("lon"), QString::number(m_longitude));
0053     // if we already have data, request data beyond the last day
0054     query.addQueryItem(
0055         QStringLiteral("date"),
0056         !m_sunriseVec.size()
0057             ? QDate::currentDate().toString(QStringLiteral("yyyy-MM-dd"))
0058             : QDate::currentDate()
0059                   .addDays(m_sunriseVec.size())
0060                   .toString(QStringLiteral("yyyy-MM-dd")));
0061     query.addQueryItem(QStringLiteral("days"),
0062                        !m_sunriseVec.size()
0063                            ? QString::number(10)
0064                            : QString::number(11 - m_sunriseVec.size()));
0065 
0066     // calculate offset (form example: -04:00)
0067     QString offset = m_offset < 0 ? QStringLiteral("-") : QStringLiteral("+");
0068     int hour = std::abs(m_offset) / 3600;
0069     if (hour >= 10)
0070         offset.append(QString::number(hour) + QStringLiteral(":"));
0071     else {
0072         offset.append(QStringLiteral("0") + QString::number(hour) +
0073                       QStringLiteral(":"));
0074     }
0075     int min = (std::abs(m_offset) - hour * 3600) / 60;
0076     if (min >= 10) {
0077         offset.append(QString::number(min));
0078     } else {
0079         offset.append(QStringLiteral("0") + QString::number(min));
0080     }
0081     query.addQueryItem(QStringLiteral("offset"), offset);
0082 
0083     url.setQuery(query);
0084     QNetworkRequest req(url);
0085 
0086     // see §Identification on https://api.met.no/conditions_service.html
0087     req.setHeader(QNetworkRequest::UserAgentHeader,
0088                   QString(QStringLiteral("KWeatherCore/") +
0089                           VERSION_NUMBER +
0090                           QStringLiteral(" kde-frameworks-devel@kde.org")));
0091 
0092     m_manager->get(req);
0093 }
0094 void SunriseSource::parseResults(QNetworkReply *reply)
0095 {
0096     reply->deleteLater();
0097     if (reply->error()) {
0098         qWarning() << "nmisunriseapi network error:" << reply->errorString();
0099         Q_EMIT networkError();
0100         return;
0101     }
0102 
0103     auto timezone = QTimeZone(m_timezone.toUtf8());
0104 
0105     QJsonDocument doc = QJsonDocument::fromJson(reply->readAll());
0106     QJsonArray array = doc[QStringLiteral("location")]
0107                            .toObject()[QStringLiteral("time")]
0108                            .toArray();
0109 
0110     m_sunriseVec.reserve(array.size());
0111 
0112     for (int i = 0; i <= array.count() - 2; i++) // we don't want last one
0113     {
0114         Sunrise sr;
0115         sr.setSunSet(
0116             QDateTime::fromString(array.at(i)
0117                                       .toObject()[QStringLiteral("sunset")]
0118                                       .toObject()[QStringLiteral("time")]
0119                                       .toString()
0120                                       .left(19),
0121                                   QStringLiteral("yyyy-MM-ddThh:mm:ss")).toTimeZone(timezone));
0122         sr.setSunRise(QDateTime::fromString(array.at(i)
0123                                             .toObject()[QStringLiteral("sunrise")]
0124                                             .toObject()[QStringLiteral("time")]
0125                                             .toString()
0126                                             .left(19),
0127                                         QStringLiteral("yyyy-MM-ddThh:mm:ss")).toTimeZone(timezone));
0128         sr.setMoonSet(
0129             QDateTime::fromString(array.at(i)
0130                                       .toObject()[QStringLiteral("moonset")]
0131                                       .toObject()[QStringLiteral("time")]
0132                                       .toString()
0133                                       .left(19),
0134                                   QStringLiteral("yyyy-MM-ddThh:mm:ss")).toTimeZone(timezone));
0135         sr.setMoonRise(
0136             QDateTime::fromString(array.at(i)
0137                                       .toObject()[QStringLiteral("moonrise")]
0138                                       .toObject()[QStringLiteral("time")]
0139                                       .toString()
0140                                       .left(19),
0141                                   QStringLiteral("yyyy-MM-ddThh:mm:ss")).toTimeZone(timezone));
0142         sr.setSolarMidnight(QPair<QDateTime, double>(
0143             QDateTime::fromString(
0144                 array.at(i)
0145                     .toObject()[QStringLiteral("solarmidnight")]
0146                     .toObject()[QStringLiteral("time")]
0147                     .toString()
0148                     .left(19),
0149                 QStringLiteral("yyyy-MM-ddThh:mm:ss")).toTimeZone(timezone),
0150             array.at(i)
0151                 .toObject()[QStringLiteral("solarmidnight")]
0152                 .toObject()[QStringLiteral("elevation")]
0153                 .toString()
0154                 .toDouble()));
0155         sr.setSolarNoon(QPair<QDateTime, double>(
0156             QDateTime::fromString(array.at(i)
0157                                       .toObject()[QStringLiteral("solarnoon")]
0158                                       .toObject()[QStringLiteral("time")]
0159                                       .toString()
0160                                       .left(19),
0161                                   QStringLiteral("yyyy-MM-ddThh:mm:ss")).toTimeZone(timezone),
0162             array.at(i)
0163                 .toObject()[QStringLiteral("solarnoon")]
0164                 .toObject()[QStringLiteral("elevation")]
0165                 .toString()
0166                 .toDouble()));
0167         sr.setHighMoon(QPair<QDateTime, double>(
0168             QDateTime::fromString(array.at(i)
0169                                       .toObject()[QStringLiteral("high_moon")]
0170                                       .toObject()[QStringLiteral("time")]
0171                                       .toString()
0172                                       .left(19),
0173                                   QStringLiteral("yyyy-MM-ddThh:mm:ss")).toTimeZone(timezone),
0174             array.at(i)
0175                 .toObject()[QStringLiteral("high_moon")]
0176                 .toObject()[QStringLiteral("elevation")]
0177                 .toString()
0178                 .toDouble()));
0179         sr.setLowMoon(QPair<QDateTime, double>(
0180             QDateTime::fromString(array.at(i)
0181                                       .toObject()[QStringLiteral("low_moon")]
0182                                       .toObject()[QStringLiteral("time")]
0183                                       .toString()
0184                                       .left(19),
0185                                   QStringLiteral("yyyy-MM-ddThh:mm:ss")).toTimeZone(timezone),
0186             array.at(i)
0187                 .toObject()[QStringLiteral("low_moon")]
0188                 .toObject()[QStringLiteral("elevation")]
0189                 .toString()
0190                 .toDouble()));
0191         sr.setMoonPhase(array.at(i)
0192                             .toObject()[QStringLiteral("moonposition")]
0193                             .toObject()[QStringLiteral("phase")]
0194                             .toString()
0195                             .toDouble());
0196 
0197         m_sunriseVec.emplace_back(std::move(sr));
0198     }
0199 
0200     Q_EMIT finished();
0201 }
0202 void SunriseSource::setTimezone(const QString &timezone)
0203 {
0204     m_timezone = timezone;
0205 }
0206 
0207 void SunriseSource::popDay()
0208 {
0209     auto today = QDateTime::currentDateTime();
0210     auto popIndex = 0;
0211     for (const auto &day : qAsConst(m_sunriseVec)) {
0212         if (day.sunRise().daysTo(today) > 0) {
0213             popIndex++;
0214         } else {
0215             // since vector is always sorted
0216             break;
0217         }
0218     }
0219 
0220     m_sunriseVec.erase(m_sunriseVec.begin(), m_sunriseVec.begin() + popIndex);
0221 }
0222 
0223 const std::vector<Sunrise> &SunriseSource::value() const
0224 {
0225     return m_sunriseVec;
0226 }
0227 }