File indexing completed on 2024-04-28 04:42:42
0001 /* 0002 * SPDX-FileCopyrightText: 2020-2021 Han Young <hanyoung@protonmail.com> 0003 * SPDX-FileCopyrightText: 2020-2021 Devin Lin <espidev@gmail.com> 0004 * 0005 * SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "kweathercore_p.h" 0009 0010 #include <KLazyLocalizedString> 0011 0012 #include <QStandardPaths> 0013 0014 #include <cstring> 0015 #include <iomanip> 0016 #include <sstream> 0017 0018 namespace KWeatherCore 0019 { 0020 CAPPolygon KWeatherCorePrivate::stringToPolygon(const QString &str) 0021 { 0022 CAPPolygon res; 0023 #if QT_VERSION > QT_VERSION_CHECK(6, 0, 0) 0024 const auto pairList = QStringView(str).split(QLatin1Char(' '), Qt::SkipEmptyParts); 0025 #else 0026 const auto pairList = str.splitRef(QLatin1Char(' '), Qt::SkipEmptyParts); 0027 #endif 0028 0029 res.reserve(pairList.size()); 0030 for (auto &pair : pairList) { 0031 auto coordinate = pair.split(QLatin1Char(',')); 0032 if (coordinate.size() != 2) { 0033 continue; 0034 } 0035 bool latOk = false, lonOk = false; 0036 res.push_back({coordinate.front().toFloat(&latOk), coordinate.back().toFloat(&lonOk)}); 0037 if (!latOk || !lonOk) { 0038 res.pop_back(); 0039 } 0040 } 0041 return res; 0042 } 0043 0044 QString KWeatherCorePrivate::toFixedString(double num) 0045 { 0046 std::ostringstream oss; 0047 oss << std::fixed << std::setprecision(2) << num; 0048 return QString::fromStdString(oss.str()); 0049 } 0050 0051 QDir KWeatherCorePrivate::getCacheDirectory(double latitude, double longitude) 0052 { 0053 #ifdef Q_OS_ANDROID 0054 QDir dir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QStringLiteral("/cache/") + toFixedString(latitude) + QStringLiteral("/") 0055 + toFixedString(longitude)); 0056 if (!dir.exists()) 0057 dir.mkpath(QStringLiteral(".")); 0058 return dir; 0059 #else 0060 QDir dir(QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QStringLiteral("/kweather/cache/") + toFixedString(latitude) 0061 + QStringLiteral("/") + toFixedString(longitude)); 0062 if (!dir.exists()) { 0063 dir.mkpath(QStringLiteral(".")); 0064 } 0065 return dir; 0066 #endif 0067 } 0068 0069 CAPAlertInfo::Urgency KWeatherCorePrivate::urgencyStringToEnum(const QString &str) 0070 { 0071 if (str == QLatin1String("Immediate")) { 0072 return CAPAlertInfo::Urgency::Immediate; 0073 } else if (str == QLatin1String("Expected")) { 0074 return CAPAlertInfo::Urgency::Expected; 0075 } else if (str == QLatin1String("Future")) { 0076 return CAPAlertInfo::Urgency::Future; 0077 } else if (str == QLatin1String("Past")) { 0078 return CAPAlertInfo::Urgency::Past; 0079 } else { 0080 return CAPAlertInfo::Urgency::UnknownUrgency; 0081 } 0082 } 0083 0084 CAPAlertInfo::Severity KWeatherCorePrivate::severityStringToEnum(const QString &str) 0085 { 0086 if (str == QLatin1String("Extreme")) { 0087 return CAPAlertInfo::Severity::Extreme; 0088 } else if (str == QLatin1String("Severe")) { 0089 return CAPAlertInfo::Severity::Severe; 0090 } else if (str == QLatin1String("Moderate")) { 0091 return CAPAlertInfo::Severity::Moderate; 0092 } else if (str == QLatin1String("Minor")) { 0093 return CAPAlertInfo::Severity::Minor; 0094 } else { 0095 return CAPAlertInfo::Severity::UnknownSeverity; 0096 } 0097 } 0098 0099 CAPAlertInfo::Certainty KWeatherCorePrivate::certaintyStringToEnum(const QString &str) 0100 { 0101 if (str == QLatin1String("Observed")) { 0102 return CAPAlertInfo::Certainty::Observed; 0103 } else if (str == QLatin1String("Likely")) { 0104 return CAPAlertInfo::Certainty::Likely; 0105 } else if (str == QLatin1String("Possible")) { 0106 return CAPAlertInfo::Certainty::Possible; 0107 } else if (str == QLatin1String("Unlikely")) { 0108 return CAPAlertInfo::Certainty::Unlikely; 0109 } else { 0110 return CAPAlertInfo::Certainty::UnknownCertainty; 0111 } 0112 } 0113 0114 QString KWeatherCorePrivate::urgencyToString(CAPAlertInfo::Urgency urgency) 0115 { 0116 QString res; 0117 switch (urgency) { 0118 case CAPAlertInfo::Urgency::Immediate: 0119 res = i18n("Immediate"); 0120 break; 0121 case CAPAlertInfo::Urgency::Expected: 0122 res = i18n("Expected"); 0123 break; 0124 case CAPAlertInfo::Urgency::Future: 0125 res = i18n("Future"); 0126 break; 0127 case CAPAlertInfo::Urgency::Past: 0128 res = i18n("Past"); 0129 break; 0130 case CAPAlertInfo::Urgency::UnknownUrgency: 0131 res = i18n("Unknown"); 0132 } 0133 return res; 0134 } 0135 0136 QString KWeatherCorePrivate::severityToString(CAPAlertInfo::Severity severity) 0137 { 0138 QString res; 0139 switch (severity) { 0140 case CAPAlertInfo::Severity::Extreme: 0141 res = i18n("Extreme"); 0142 break; 0143 case CAPAlertInfo::Severity::Severe: 0144 res = i18n("Severe"); 0145 break; 0146 case CAPAlertInfo::Severity::Moderate: 0147 res = i18n("Moderate"); 0148 break; 0149 case CAPAlertInfo::Severity::Minor: 0150 res = i18n("Minor"); 0151 break; 0152 case CAPAlertInfo::Severity::UnknownSeverity: 0153 res = i18n("Unknown"); 0154 } 0155 return res; 0156 } 0157 0158 QString KWeatherCorePrivate::certaintyToString(CAPAlertInfo::Certainty certainty) 0159 { 0160 QString res; 0161 switch (certainty) { 0162 case CAPAlertInfo::Certainty::Observed: 0163 res = i18n("Observed"); 0164 break; 0165 case CAPAlertInfo::Certainty::Likely: 0166 res = i18n("Likely"); 0167 break; 0168 case CAPAlertInfo::Certainty::Possible: 0169 res = i18n("Possible"); 0170 break; 0171 case CAPAlertInfo::Certainty::Unlikely: 0172 res = i18n("Unlikely"); 0173 break; 0174 case CAPAlertInfo::Certainty::UnknownCertainty: 0175 res = i18n("Unknown"); 0176 } 0177 return res; 0178 } 0179 0180 // rank weather (for what best describes the day overall) 0181 // only need neutral icons 0182 // ### needs to be alphabetically sorted by icon name! 0183 struct { 0184 const char *iconName; 0185 int rank; 0186 } static constexpr const WEATHER_ICON_PRIORITY_RANK[] = { 0187 {"weather-clear", 0}, 0188 {"weather-clouds", 2}, 0189 {"weather-few-clouds", 1}, 0190 {"weather-fog", 3}, 0191 {"weather-freezing-rain", 6}, 0192 {"weather-freezing-scattered-rain", 6}, 0193 {"weather-freezing-scattered-rain-storm", 7}, 0194 {"weather-freezing-storm", 6}, 0195 {"weather-hail", 5}, 0196 {"weather-mist", 3}, 0197 {"weather-none-available", -1}, 0198 {"weather-showers", 5}, 0199 {"weather-showers-scattered", 4}, 0200 {"weather-showers-scattered-storm", 7}, 0201 {"weather-snow", 5}, 0202 {"weather-snow-rain", 6}, 0203 {"weather-snow-scattered", 4}, 0204 {"weather-snow-scattered-storm", 7}, 0205 {"weather-snow-storm", 7}, 0206 {"weather-storm", 7}, 0207 }; 0208 0209 int KWeatherCorePrivate::weatherIconPriorityRank(const QString &icon) 0210 { 0211 const auto it = std::lower_bound(std::begin(WEATHER_ICON_PRIORITY_RANK), std::end(WEATHER_ICON_PRIORITY_RANK), icon, [](const auto &lhs, const auto &rhs) { 0212 return QLatin1String(lhs.iconName) < rhs; 0213 }); 0214 if (it != std::end(WEATHER_ICON_PRIORITY_RANK) && QLatin1String((*it).iconName) == icon) { 0215 return (*it).rank; 0216 } 0217 0218 return 0; 0219 } 0220 0221 // https://api.met.no/weatherapi/weathericon/2.0/legends 0222 // ### needs to be alphabetically sorted by key! 0223 struct { 0224 const char *key; 0225 const char *icon; 0226 const KLazyLocalizedString desc; 0227 } static constexpr const WEATHER_API_DESC_MAP[] = { 0228 {"clearsky_day", "weather-clear", kli18n("Clear")}, 0229 {"clearsky_neutral", "weather-clear", kli18n("Clear")}, 0230 {"clearsky_night", "weather-clear-night", kli18n("Clear")}, 0231 {"cloudy_day", "weather-clouds", kli18n("Cloudy")}, 0232 {"cloudy_neutral", "weather-clouds", kli18n("Cloudy")}, 0233 {"cloudy_night", "weather-clouds-night", kli18n("Cloudy")}, 0234 {"fair_day", "weather-few-clouds", kli18n("Partly Sunny")}, 0235 {"fair_neutral", "weather-few-clouds", kli18n("Light Clouds")}, 0236 {"fair_night", "weather-few-clouds-night", kli18n("Light Clouds")}, 0237 {"fog_day", "weather-fog", kli18n("Fog")}, 0238 {"fog_neutral", "weather-fog", kli18n("Fog")}, 0239 {"fog_night", "weather-fog", kli18n("Fog")}, 0240 0241 {"heavyrain_day", "weather-showers-day", kli18n("Heavy Rain")}, 0242 {"heavyrain_neutral", "weather-showers", kli18n("Heavy Rain")}, 0243 {"heavyrain_night", "weather-showers-night", kli18n("Heavy Rain")}, 0244 {"heavyrainandthunder_day", "weather-storm-day", kli18n("Thunderstorm with Heavy Rain")}, 0245 {"heavyrainandthunder_neutral", "weather-storm", kli18n("Thunderstorm with Heavy Rain")}, 0246 {"heavyrainandthunder_night", "weather-storm-night", kli18n("Thunderstorm with Heavy Rain")}, 0247 {"heavyrainshowers_day", "weather-showers-day", kli18n("Heavy Rain Shower")}, 0248 {"heavyrainshowers_neutral", "weather-showers", kli18n("Heavy Rain Shower")}, 0249 {"heavyrainshowers_night", "weather-showers-night", kli18n("Heavy Rain Shower")}, 0250 {"heavyrainshowersandthunder_day", "weather-storm-day", kli18n("Thunderstorm with Heavy Rain Shower")}, 0251 {"heavyrainshowersandthunder_neutral", "weather-storm", kli18n("Thunderstorm with Heavy Rain Shower")}, 0252 {"heavyrainshowersandthunder_night", "weather-storm-night", kli18n("Thunderstorm with Heavy Rain Shower")}, 0253 0254 {"heavysleet_day", "weather-freezing-rain", kli18n("Heavy Sleet")}, 0255 {"heavysleet_neutral", "weather-freezing-rain", kli18n("Heavy Sleet")}, 0256 {"heavysleet_night", "weather-freezing-rain", kli18n("Heavy Sleet")}, 0257 {"heavysleetandthunder_day", "weather-freezing-storm", kli18n("Thunderstorm with Heavy Sleet")}, 0258 {"heavysleetandthunder_neutral", "weather-freezing-storm", kli18n("Thunderstorm with Heavy Sleet")}, 0259 {"heavysleetandthunder_night", "weather-freezing-storm", kli18n("Thunderstorm with Heavy Sleet")}, 0260 {"heavysleetshowers_day", "weather-freezing-rain-day", kli18n("Heavy Sleet Shower")}, 0261 {"heavysleetshowers_neutral", "weather-freezing-rain", kli18n("Heavy Sleet Shower")}, 0262 {"heavysleetshowers_night", "weather-freezing-rain-night", kli18n("Heavy Sleet Shower")}, 0263 {"heavysleetshowersandthunder_day", "weather-freezing-storm-day", kli18n("Thunderstorm with Heavy Sleet Shower")}, 0264 {"heavysleetshowersandthunder_neutral", "weather-freezing-storm", kli18n("Thunderstorm with Heavy Sleet Shower")}, 0265 {"heavysleetshowersandthunder_night", "weather-freezing-storm-night", kli18n("Thunderstorm with Heavy Sleet Shower")}, 0266 0267 {"heavysnow_day", "weather-snow", kli18n("Heavy Snow")}, 0268 {"heavysnow_neutral", "weather-snow", kli18n("Heavy Snow")}, 0269 {"heavysnow_night", "weather-snow", kli18n("Heavy Snow")}, 0270 {"heavysnowshowers_day", "weather-snow-day", kli18n("Heavy Snow Shower")}, 0271 {"heavysnowshowers_neutral", "weather-snow", kli18n("Heavy Snow Shower")}, 0272 {"heavysnowshowers_night", "weather-snow-night", kli18n("Heavy Snow Shower")}, 0273 {"heavysnowshowersandthunder_day", "weather-snow-storm-day", kli18n("Thunderstorm with Heavy Snow Shower")}, 0274 {"heavysnowshowersandthunder_neutral", "weather-snow-storm", kli18n("Thunderstorm with Heavy Snow Shower")}, 0275 {"heavysnowshowersandthunder_night", "weather-snow-storm-night", kli18n("Thunderstorm with Heavy Snow Shower")}, 0276 0277 {"lightrain_day", "weather-showers-scattered", kli18n("Light Rain")}, 0278 {"lightrain_neutral", "weather-showers-scattered", kli18n("Light Rain")}, 0279 {"lightrain_night", "weather-showers-scattered", kli18n("Light Rain")}, 0280 {"lightrainandthunder_day", "weather-showers-scattered-storm", kli18n("Thunderstorm with Light Rain")}, 0281 {"lightrainandthunder_neutral", "weather-showers-scattered-storm", kli18n("Thunderstorm with Light Rain")}, 0282 {"lightrainandthunder_night", "weather-showers-scattered-storm", kli18n("Thunderstorm with Light Rain")}, 0283 {"lightrainshowers_day", "weather-showers-scattered-day", kli18n("Light Rain Shower")}, 0284 {"lightrainshowers_neutral", "weather-showers-scattered", kli18n("Light Rain Shower")}, 0285 {"lightrainshowers_night", "weather-showers-scattered-night", kli18n("Light Rain Shower")}, 0286 {"lightrainshowersandthunder_day", "weather-showers-scattered-storm-day", kli18n("Thunderstorm with Light Rain Shower")}, 0287 {"lightrainshowersandthunder_neutral", "weather-showers-scattered-storm", kli18n("Thunderstorm with Light Rain Shower")}, 0288 {"lightrainshowersandthunder_night", "weather-showers-scattered-storm-night", kli18n("Thunderstorm with Light Rain Shower")}, 0289 0290 {"lightsleet_day", "weather-showers-scattered", kli18n("Light Sleet")}, 0291 {"lightsleet_neutral", "weather-showers-scattered", kli18n("Light Sleet")}, 0292 {"lightsleet_night", "weather-showers-scattered", kli18n("Light Sleet")}, 0293 {"lightsleetandthunder_day", "weather-freezing-scattered-rain-storm", kli18n("Thunderstorm with Light Sleet")}, 0294 {"lightsleetandthunder_neutral", "weather-freezing-scattered-rain-storm", kli18n("Thunderstorm with Light Sleet")}, 0295 {"lightsleetandthunder_night", "weather-freezing-scattered-rain-storm", kli18n("Thunderstorm with Light Sleet")}, 0296 {"lightsleetshowers_day", "weather-freezing-scattered-rain-day", kli18n("Light Sleet Shower")}, 0297 {"lightsleetshowers_neutral", "weather-freezing-scattered-rain", kli18n("Light Sleet Shower")}, 0298 {"lightsleetshowers_night", "weather-freezing-scattered-rain-night", kli18n("Light Sleet Shower")}, 0299 0300 {"lightsnow_day", "weather-snow-scattered", kli18n("Light Snow")}, 0301 {"lightsnow_neutral", "weather-snow-scattered", kli18n("Light Snow")}, 0302 {"lightsnow_night", "weather-snow-scattered", kli18n("Light Snow")}, 0303 {"lightsnowandthunder_day", "weather-snow-scattered-storm", kli18n("Thunderstorm with Light Snow")}, 0304 {"lightsnowandthunder_neutral", "weather-snow-scattered-storm", kli18n("Thunderstorm with Light Snow")}, 0305 {"lightsnowandthunder_night", "weather-snow-scattered-storm", kli18n("Thunderstorm with Light Snow")}, 0306 {"lightsnowshowers_day", "weather-snow-scattered-day", kli18n("Light Snow Shower")}, 0307 {"lightsnowshowers_neutral", "weather-snow-scattered", kli18n("Light Snow Shower")}, 0308 {"lightsnowshowers_night", "weather-snow-scattered-night", kli18n("Light Snow Shower")}, 0309 0310 {"lightssleetshowersandthunder_day", "weather-freezing-scattered-rain-storm-day", kli18n("Thunderstorm with Light Sleet Shower")}, 0311 {"lightssleetshowersandthunder_neutral", "weather-freezing-scattered-rain-storm", kli18n("Thunderstorm with Light Sleet Shower")}, 0312 {"lightssleetshowersandthunder_night", "weather-freezing-scattered-rain-storm-night", kli18n("Thunderstorm with Light Sleet Shower")}, 0313 {"lightssnowshowersandthunder_day", "weather-snow-scattered-storm-day", kli18n("Thunderstorm with Light Snow Shower")}, 0314 {"lightssnowshowersandthunder_neutral", "weather-snow-scattered-storm", kli18n("Thunderstorm with Light Snow Shower")}, 0315 {"lightssnowshowersandthunder_night", "weather-snow-scattered-storm-night", kli18n("Thunderstorm with Light Snow Shower")}, 0316 0317 {"partlycloudy_day", "weather-clouds", kli18n("Partly Cloudy")}, 0318 {"partlycloudy_neutral", "weather-clouds", kli18n("Partly Cloudy")}, 0319 {"partlycloudy_night", "weather-clouds-night", kli18n("Partly Cloudy")}, 0320 0321 {"rain_day", "weather-showers", kli18n("Rain")}, 0322 {"rain_neutral", "weather-showers", kli18n("Rain")}, 0323 {"rain_night", "weather-showers", kli18n("Rain")}, 0324 {"rainandthunder_day", "weather-storm", kli18n("Thunderstorm with Rain")}, 0325 {"rainandthunder_neutral", "weather-storm", kli18n("Thunderstorm with Rain")}, 0326 {"rainandthunder_night", "weather-storm", kli18n("Thunderstorm with Rain")}, 0327 {"rainshowers_day", "weather-showers-day", kli18n("Rain Shower")}, 0328 {"rainshowers_neutral", "weather-showers", kli18n("Rain Shower")}, 0329 {"rainshowers_night", "weather-showers-night", kli18n("Rain Shower")}, 0330 {"rainshowersandthunder_day", "weather-storm-day", kli18n("Thunderstorm with Rain Shower")}, 0331 {"rainshowersandthunder_neutral", "weather-storm", kli18n("Thunderstorm with Rain Shower")}, 0332 {"rainshowersandthunder_night", "weather-storm-night", kli18n("Thunderstorm with Rain Shower")}, 0333 0334 {"sleet_day", "weather-freezing-rain", kli18n("Sleet")}, 0335 {"sleet_neutral", "weather-freezing-rain", kli18n("Sleet")}, 0336 {"sleet_night", "weather-freezing-rain", kli18n("Sleet")}, 0337 {"sleetandthunder_day", "weather-storm", kli18n("Thunderstorm with Sleet")}, 0338 {"sleetandthunder_neutral", "weather-storm", kli18n("Thunderstorm with Sleet")}, 0339 {"sleetandthunder_night", "weather-storm", kli18n("Thunderstorm with Sleet")}, 0340 {"sleetshowers_day", "weather-freezing-rain-day", kli18n("Sleet Shower")}, 0341 {"sleetshowers_neutral", "weather-freezing-rain", kli18n("Sleet Shower")}, 0342 {"sleetshowers_night", "weather-freezing-rain-night", kli18n("Sleet Shower")}, 0343 {"sleetshowersandthunder_day", "weather-freezing-storm-day", kli18n("Thunderstorm with Sleet Shower")}, 0344 {"sleetshowersandthunder_neutral", "weather-freezing-storm", kli18n("Thunderstorm with Sleet Shower")}, 0345 {"sleetshowersandthunder_night", "weather-freezing-storm-night", kli18n("Thunderstorm with Sleet Shower")}, 0346 0347 {"snow_day", "weather-snow", kli18n("Snow")}, 0348 {"snow_neutral", "weather-snow", kli18n("Snow")}, 0349 {"snow_night", "weather-snow", kli18n("Snow")}, 0350 {"snowandthunder_day", "weather-snow-storm", kli18n("Thunderstorm with Snow")}, 0351 {"snowandthunder_neutral", "weather-snow-storm", kli18n("Thunderstorm with Snow")}, 0352 {"snowandthunder_night", "weather-snow-storm", kli18n("Thunderstorm with Snow")}, 0353 {"snowshowers_day", "weather-snow-day", kli18n("Snow Shower")}, 0354 {"snowshowers_neutral", "weather-snow", kli18n("Snow Shower")}, 0355 {"snowshowers_night", "weather-snow-night", kli18n("Snow Shower")}, 0356 {"snowshowersandthunder_day", "weather-snow-storm-day", kli18n("Thunderstorm with Snow Shower")}, 0357 {"snowshowersandthunder_neutral", "weather-snow-storm", kli18n("Thunderstorm with Snow Shower")}, 0358 {"snowshowersandthunder_night", "weather-snow-storm-night", kli18n("Thunderstorm with Snow Shower")}, 0359 0360 }; 0361 0362 ResolvedWeatherDesc KWeatherCorePrivate::resolveAPIWeatherDesc(const QString &desc) 0363 { 0364 const auto key = desc.toUtf8(); 0365 const auto it = std::lower_bound(std::begin(WEATHER_API_DESC_MAP), std::end(WEATHER_API_DESC_MAP), key, [](const auto &lhs, const auto &rhs) { 0366 return std::strcmp(lhs.key, rhs.constData()) < 0; 0367 }); 0368 if (it != std::end(WEATHER_API_DESC_MAP) && std::strcmp((*it).key, key.constData()) == 0) { 0369 return ResolvedWeatherDesc(QLatin1String((*it).icon), (*it).desc.toString()); 0370 } 0371 0372 return {}; 0373 } 0374 0375 }