File indexing completed on 2025-01-12 05:01:50
0001 /* 0002 SPDX-FileCopyrightText: 2007-2011, 2019 Shawn Starr <shawn.starr@rogers.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 /* Ion for Environment Canada XML data */ 0008 0009 #include "ion_envcan.h" 0010 0011 // #include "ion_envcandebug.h" 0012 0013 #include <KIO/TransferJob> 0014 #include <KLocalizedString> 0015 #include <KUnitConversion/Converter> 0016 0017 #include <QRegularExpression> 0018 #include <QTimeZone> 0019 0020 WeatherData::WeatherData() 0021 : stationLatitude(qQNaN()) 0022 , stationLongitude(qQNaN()) 0023 , temperature(qQNaN()) 0024 , dewpoint(qQNaN()) 0025 , windchill(qQNaN()) 0026 , pressure(qQNaN()) 0027 , visibility(qQNaN()) 0028 , humidity(qQNaN()) 0029 , windSpeed(qQNaN()) 0030 , windGust(qQNaN()) 0031 , normalHigh(qQNaN()) 0032 , normalLow(qQNaN()) 0033 , prevHigh(qQNaN()) 0034 , prevLow(qQNaN()) 0035 , recordHigh(qQNaN()) 0036 , recordLow(qQNaN()) 0037 , recordRain(qQNaN()) 0038 , recordSnow(qQNaN()) 0039 { 0040 } 0041 0042 WeatherData::ForecastInfo::ForecastInfo() 0043 : tempHigh(qQNaN()) 0044 , tempLow(qQNaN()) 0045 , popPrecent(qQNaN()) 0046 { 0047 } 0048 0049 // ctor, dtor 0050 EnvCanadaIon::EnvCanadaIon(QObject *parent) 0051 : IonInterface(parent) 0052 { 0053 // Get the real city XML URL so we can parse this 0054 getXMLSetup(); 0055 } 0056 0057 void EnvCanadaIon::deleteForecasts() 0058 { 0059 QMutableHashIterator<QString, WeatherData> it(m_weatherData); 0060 while (it.hasNext()) { 0061 it.next(); 0062 WeatherData &item = it.value(); 0063 qDeleteAll(item.warnings); 0064 item.warnings.clear(); 0065 0066 qDeleteAll(item.forecasts); 0067 item.forecasts.clear(); 0068 } 0069 } 0070 0071 void EnvCanadaIon::reset() 0072 { 0073 deleteForecasts(); 0074 emitWhenSetup = true; 0075 m_sourcesToReset = sources(); 0076 getXMLSetup(); 0077 } 0078 0079 EnvCanadaIon::~EnvCanadaIon() 0080 { 0081 // Destroy each warning stored in a QList 0082 deleteForecasts(); 0083 } 0084 0085 QMap<QString, IonInterface::ConditionIcons> EnvCanadaIon::setupConditionIconMappings() const 0086 { 0087 return QMap<QString, ConditionIcons>{ 0088 0089 // Explicit periods 0090 {QStringLiteral("mainly sunny"), FewCloudsDay}, 0091 {QStringLiteral("mainly clear"), FewCloudsNight}, 0092 {QStringLiteral("sunny"), ClearDay}, 0093 {QStringLiteral("clear"), ClearNight}, 0094 0095 // Available conditions 0096 {QStringLiteral("blowing snow"), Snow}, 0097 {QStringLiteral("cloudy"), Overcast}, 0098 {QStringLiteral("distant precipitation"), LightRain}, 0099 {QStringLiteral("drifting snow"), Flurries}, 0100 {QStringLiteral("drizzle"), LightRain}, 0101 {QStringLiteral("dust"), NotAvailable}, 0102 {QStringLiteral("dust devils"), NotAvailable}, 0103 {QStringLiteral("fog"), Mist}, 0104 {QStringLiteral("fog bank near station"), Mist}, 0105 {QStringLiteral("fog depositing ice"), Mist}, 0106 {QStringLiteral("fog patches"), Mist}, 0107 {QStringLiteral("freezing drizzle"), FreezingDrizzle}, 0108 {QStringLiteral("freezing rain"), FreezingRain}, 0109 {QStringLiteral("funnel cloud"), NotAvailable}, 0110 {QStringLiteral("hail"), Hail}, 0111 {QStringLiteral("haze"), Haze}, 0112 {QStringLiteral("heavy blowing snow"), Snow}, 0113 {QStringLiteral("heavy drifting snow"), Snow}, 0114 {QStringLiteral("heavy drizzle"), LightRain}, 0115 {QStringLiteral("heavy hail"), Hail}, 0116 {QStringLiteral("heavy mixed rain and drizzle"), LightRain}, 0117 {QStringLiteral("heavy mixed rain and snow shower"), RainSnow}, 0118 {QStringLiteral("heavy rain"), Rain}, 0119 {QStringLiteral("heavy rain and snow"), RainSnow}, 0120 {QStringLiteral("heavy rainshower"), Rain}, 0121 {QStringLiteral("heavy snow"), Snow}, 0122 {QStringLiteral("heavy snow pellets"), Snow}, 0123 {QStringLiteral("heavy snowshower"), Snow}, 0124 {QStringLiteral("heavy thunderstorm with hail"), Thunderstorm}, 0125 {QStringLiteral("heavy thunderstorm with rain"), Thunderstorm}, 0126 {QStringLiteral("ice crystals"), Flurries}, 0127 {QStringLiteral("ice pellets"), Hail}, 0128 {QStringLiteral("increasing cloud"), Overcast}, 0129 {QStringLiteral("light drizzle"), LightRain}, 0130 {QStringLiteral("light freezing drizzle"), FreezingRain}, 0131 {QStringLiteral("light freezing rain"), FreezingRain}, 0132 {QStringLiteral("light rain"), LightRain}, 0133 {QStringLiteral("light rainshower"), LightRain}, 0134 {QStringLiteral("light snow"), LightSnow}, 0135 {QStringLiteral("light snow pellets"), LightSnow}, 0136 {QStringLiteral("light snowshower"), Flurries}, 0137 {QStringLiteral("lightning visible"), Thunderstorm}, 0138 {QStringLiteral("mist"), Mist}, 0139 {QStringLiteral("mixed rain and drizzle"), LightRain}, 0140 {QStringLiteral("mixed rain and snow shower"), RainSnow}, 0141 {QStringLiteral("not reported"), NotAvailable}, 0142 {QStringLiteral("rain"), Rain}, 0143 {QStringLiteral("rain and snow"), RainSnow}, 0144 {QStringLiteral("rainshower"), LightRain}, 0145 {QStringLiteral("recent drizzle"), LightRain}, 0146 {QStringLiteral("recent dust or sand storm"), NotAvailable}, 0147 {QStringLiteral("recent fog"), Mist}, 0148 {QStringLiteral("recent freezing precipitation"), FreezingDrizzle}, 0149 {QStringLiteral("recent hail"), Hail}, 0150 {QStringLiteral("recent rain"), Rain}, 0151 {QStringLiteral("recent rain and snow"), RainSnow}, 0152 {QStringLiteral("recent rainshower"), Rain}, 0153 {QStringLiteral("recent snow"), Snow}, 0154 {QStringLiteral("recent snowshower"), Flurries}, 0155 {QStringLiteral("recent thunderstorm"), Thunderstorm}, 0156 {QStringLiteral("recent thunderstorm with hail"), Thunderstorm}, 0157 {QStringLiteral("recent thunderstorm with heavy hail"), Thunderstorm}, 0158 {QStringLiteral("recent thunderstorm with heavy rain"), Thunderstorm}, 0159 {QStringLiteral("recent thunderstorm with rain"), Thunderstorm}, 0160 {QStringLiteral("sand or dust storm"), NotAvailable}, 0161 {QStringLiteral("severe sand or dust storm"), NotAvailable}, 0162 {QStringLiteral("shallow fog"), Mist}, 0163 {QStringLiteral("smoke"), NotAvailable}, 0164 {QStringLiteral("snow"), Snow}, 0165 {QStringLiteral("snow crystals"), Flurries}, 0166 {QStringLiteral("snow grains"), Flurries}, 0167 {QStringLiteral("squalls"), Snow}, 0168 {QStringLiteral("thunderstorm"), Thunderstorm}, 0169 {QStringLiteral("thunderstorm with hail"), Thunderstorm}, 0170 {QStringLiteral("thunderstorm with rain"), Thunderstorm}, 0171 {QStringLiteral("thunderstorm with light rainshowers"), Thunderstorm}, 0172 {QStringLiteral("thunderstorm with heavy rainshowers"), Thunderstorm}, 0173 {QStringLiteral("thunderstorm with sand or dust storm"), Thunderstorm}, 0174 {QStringLiteral("thunderstorm without precipitation"), Thunderstorm}, 0175 {QStringLiteral("tornado"), NotAvailable}, 0176 }; 0177 } 0178 0179 QMap<QString, IonInterface::ConditionIcons> EnvCanadaIon::setupForecastIconMappings() const 0180 { 0181 return QMap<QString, ConditionIcons>{ 0182 0183 // Abbreviated forecast descriptions 0184 {QStringLiteral("a few flurries"), Flurries}, 0185 {QStringLiteral("a few flurries mixed with ice pellets"), RainSnow}, 0186 {QStringLiteral("a few flurries or rain showers"), RainSnow}, 0187 {QStringLiteral("a few flurries or thundershowers"), RainSnow}, 0188 {QStringLiteral("a few rain showers or flurries"), RainSnow}, 0189 {QStringLiteral("a few rain showers or wet flurries"), RainSnow}, 0190 {QStringLiteral("a few showers"), LightRain}, 0191 {QStringLiteral("a few showers or drizzle"), LightRain}, 0192 {QStringLiteral("a few showers or thundershowers"), Thunderstorm}, 0193 {QStringLiteral("a few showers or thunderstorms"), Thunderstorm}, 0194 {QStringLiteral("a few thundershowers"), Thunderstorm}, 0195 {QStringLiteral("a few thunderstorms"), Thunderstorm}, 0196 {QStringLiteral("a few wet flurries"), RainSnow}, 0197 {QStringLiteral("a few wet flurries or rain showers"), RainSnow}, 0198 {QStringLiteral("a mix of sun and cloud"), PartlyCloudyDay}, 0199 {QStringLiteral("cloudy with sunny periods"), PartlyCloudyDay}, 0200 {QStringLiteral("partly cloudy"), PartlyCloudyDay}, 0201 {QStringLiteral("mainly cloudy"), PartlyCloudyDay}, 0202 {QStringLiteral("mainly sunny"), FewCloudsDay}, 0203 {QStringLiteral("sunny"), ClearDay}, 0204 {QStringLiteral("blizzard"), Snow}, 0205 {QStringLiteral("clear"), ClearNight}, 0206 {QStringLiteral("cloudy"), Overcast}, 0207 {QStringLiteral("drizzle"), LightRain}, 0208 {QStringLiteral("drizzle mixed with freezing drizzle"), FreezingDrizzle}, 0209 {QStringLiteral("drizzle mixed with rain"), LightRain}, 0210 {QStringLiteral("drizzle or freezing drizzle"), LightRain}, 0211 {QStringLiteral("drizzle or rain"), LightRain}, 0212 {QStringLiteral("flurries"), Flurries}, 0213 {QStringLiteral("flurries at times heavy"), Flurries}, 0214 {QStringLiteral("flurries at times heavy or rain snowers"), RainSnow}, 0215 {QStringLiteral("flurries mixed with ice pellets"), FreezingRain}, 0216 {QStringLiteral("flurries or ice pellets"), FreezingRain}, 0217 {QStringLiteral("flurries or rain showers"), RainSnow}, 0218 {QStringLiteral("flurries or thundershowers"), Flurries}, 0219 {QStringLiteral("fog"), Mist}, 0220 {QStringLiteral("fog developing"), Mist}, 0221 {QStringLiteral("fog dissipating"), Mist}, 0222 {QStringLiteral("fog patches"), Mist}, 0223 {QStringLiteral("freezing drizzle"), FreezingDrizzle}, 0224 {QStringLiteral("freezing rain"), FreezingRain}, 0225 {QStringLiteral("freezing rain mixed with rain"), FreezingRain}, 0226 {QStringLiteral("freezing rain mixed with snow"), FreezingRain}, 0227 {QStringLiteral("freezing rain or ice pellets"), FreezingRain}, 0228 {QStringLiteral("freezing rain or rain"), FreezingRain}, 0229 {QStringLiteral("freezing rain or snow"), FreezingRain}, 0230 {QStringLiteral("ice fog"), Mist}, 0231 {QStringLiteral("ice fog developing"), Mist}, 0232 {QStringLiteral("ice fog dissipating"), Mist}, 0233 {QStringLiteral("ice pellets"), Hail}, 0234 {QStringLiteral("ice pellets mixed with freezing rain"), Hail}, 0235 {QStringLiteral("ice pellets mixed with snow"), Hail}, 0236 {QStringLiteral("ice pellets or snow"), RainSnow}, 0237 {QStringLiteral("light snow"), LightSnow}, 0238 {QStringLiteral("light snow and blizzard"), LightSnow}, 0239 {QStringLiteral("light snow and blizzard and blowing snow"), Snow}, 0240 {QStringLiteral("light snow and blowing snow"), LightSnow}, 0241 {QStringLiteral("light snow mixed with freezing drizzle"), FreezingDrizzle}, 0242 {QStringLiteral("light snow mixed with freezing rain"), FreezingRain}, 0243 {QStringLiteral("light snow or ice pellets"), LightSnow}, 0244 {QStringLiteral("light snow or rain"), RainSnow}, 0245 {QStringLiteral("light wet snow"), RainSnow}, 0246 {QStringLiteral("light wet snow or rain"), RainSnow}, 0247 {QStringLiteral("local snow squalls"), Snow}, 0248 {QStringLiteral("near blizzard"), Snow}, 0249 {QStringLiteral("overcast"), Overcast}, 0250 {QStringLiteral("increasing cloudiness"), Overcast}, 0251 {QStringLiteral("increasing clouds"), Overcast}, 0252 {QStringLiteral("periods of drizzle"), LightRain}, 0253 {QStringLiteral("periods of drizzle mixed with freezing drizzle"), FreezingDrizzle}, 0254 {QStringLiteral("periods of drizzle mixed with rain"), LightRain}, 0255 {QStringLiteral("periods of drizzle or freezing drizzle"), FreezingDrizzle}, 0256 {QStringLiteral("periods of drizzle or rain"), LightRain}, 0257 {QStringLiteral("periods of freezing drizzle"), FreezingDrizzle}, 0258 {QStringLiteral("periods of freezing drizzle or drizzle"), FreezingDrizzle}, 0259 {QStringLiteral("periods of freezing drizzle or rain"), FreezingDrizzle}, 0260 {QStringLiteral("periods of freezing rain"), FreezingRain}, 0261 {QStringLiteral("periods of freezing rain mixed with ice pellets"), FreezingRain}, 0262 {QStringLiteral("periods of freezing rain mixed with rain"), FreezingRain}, 0263 {QStringLiteral("periods of freezing rain mixed with snow"), FreezingRain}, 0264 {QStringLiteral("periods of freezing rain mixed with freezing drizzle"), FreezingRain}, 0265 {QStringLiteral("periods of freezing rain or ice pellets"), FreezingRain}, 0266 {QStringLiteral("periods of freezing rain or rain"), FreezingRain}, 0267 {QStringLiteral("periods of freezing rain or snow"), FreezingRain}, 0268 {QStringLiteral("periods of ice pellets"), Hail}, 0269 {QStringLiteral("periods of ice pellets mixed with freezing rain"), Hail}, 0270 {QStringLiteral("periods of ice pellets mixed with snow"), Hail}, 0271 {QStringLiteral("periods of ice pellets or freezing rain"), Hail}, 0272 {QStringLiteral("periods of ice pellets or snow"), Hail}, 0273 {QStringLiteral("periods of light snow"), LightSnow}, 0274 {QStringLiteral("periods of light snow and blizzard"), Snow}, 0275 {QStringLiteral("periods of light snow and blizzard and blowing snow"), Snow}, 0276 {QStringLiteral("periods of light snow and blowing snow"), LightSnow}, 0277 {QStringLiteral("periods of light snow mixed with freezing drizzle"), RainSnow}, 0278 {QStringLiteral("periods of light snow mixed with freezing rain"), RainSnow}, 0279 {QStringLiteral("periods of light snow mixed with ice pellets"), LightSnow}, 0280 {QStringLiteral("periods of light snow mixed with rain"), RainSnow}, 0281 {QStringLiteral("periods of light snow or freezing drizzle"), RainSnow}, 0282 {QStringLiteral("periods of light snow or freezing rain"), RainSnow}, 0283 {QStringLiteral("periods of light snow or ice pellets"), LightSnow}, 0284 {QStringLiteral("periods of light snow or rain"), RainSnow}, 0285 {QStringLiteral("periods of light wet snow"), LightSnow}, 0286 {QStringLiteral("periods of light wet snow mixed with rain"), RainSnow}, 0287 {QStringLiteral("periods of light wet snow or rain"), RainSnow}, 0288 {QStringLiteral("periods of rain"), Rain}, 0289 {QStringLiteral("periods of rain mixed with freezing rain"), Rain}, 0290 {QStringLiteral("periods of rain mixed with snow"), RainSnow}, 0291 {QStringLiteral("periods of rain or drizzle"), Rain}, 0292 {QStringLiteral("periods of rain or freezing rain"), Rain}, 0293 {QStringLiteral("periods of rain or thundershowers"), Showers}, 0294 {QStringLiteral("periods of rain or thunderstorms"), Thunderstorm}, 0295 {QStringLiteral("periods of rain or snow"), RainSnow}, 0296 {QStringLiteral("periods of snow"), Snow}, 0297 {QStringLiteral("periods of snow and blizzard"), Snow}, 0298 {QStringLiteral("periods of snow and blizzard and blowing snow"), Snow}, 0299 {QStringLiteral("periods of snow and blowing snow"), Snow}, 0300 {QStringLiteral("periods of snow mixed with freezing drizzle"), RainSnow}, 0301 {QStringLiteral("periods of snow mixed with freezing rain"), RainSnow}, 0302 {QStringLiteral("periods of snow mixed with ice pellets"), Snow}, 0303 {QStringLiteral("periods of snow mixed with rain"), RainSnow}, 0304 {QStringLiteral("periods of snow or freezing drizzle"), RainSnow}, 0305 {QStringLiteral("periods of snow or freezing rain"), RainSnow}, 0306 {QStringLiteral("periods of snow or ice pellets"), Snow}, 0307 {QStringLiteral("periods of snow or rain"), RainSnow}, 0308 {QStringLiteral("periods of rain or snow"), RainSnow}, 0309 {QStringLiteral("periods of wet snow"), Snow}, 0310 {QStringLiteral("periods of wet snow mixed with rain"), RainSnow}, 0311 {QStringLiteral("periods of wet snow or rain"), RainSnow}, 0312 {QStringLiteral("rain"), Rain}, 0313 {QStringLiteral("rain at times heavy"), Rain}, 0314 {QStringLiteral("rain at times heavy mixed with freezing rain"), FreezingRain}, 0315 {QStringLiteral("rain at times heavy mixed with snow"), RainSnow}, 0316 {QStringLiteral("rain at times heavy or drizzle"), Rain}, 0317 {QStringLiteral("rain at times heavy or freezing rain"), Rain}, 0318 {QStringLiteral("rain at times heavy or snow"), RainSnow}, 0319 {QStringLiteral("rain at times heavy or thundershowers"), Showers}, 0320 {QStringLiteral("rain at times heavy or thunderstorms"), Thunderstorm}, 0321 {QStringLiteral("rain mixed with freezing rain"), FreezingRain}, 0322 {QStringLiteral("rain mixed with snow"), RainSnow}, 0323 {QStringLiteral("rain or drizzle"), Rain}, 0324 {QStringLiteral("rain or freezing rain"), Rain}, 0325 {QStringLiteral("rain or snow"), RainSnow}, 0326 {QStringLiteral("rain or thundershowers"), Showers}, 0327 {QStringLiteral("rain or thunderstorms"), Thunderstorm}, 0328 {QStringLiteral("rain showers or flurries"), RainSnow}, 0329 {QStringLiteral("rain showers or wet flurries"), RainSnow}, 0330 {QStringLiteral("showers"), Showers}, 0331 {QStringLiteral("showers at times heavy"), Showers}, 0332 {QStringLiteral("showers at times heavy or thundershowers"), Showers}, 0333 {QStringLiteral("showers at times heavy or thunderstorms"), Thunderstorm}, 0334 {QStringLiteral("showers or drizzle"), Showers}, 0335 {QStringLiteral("showers or thundershowers"), Thunderstorm}, 0336 {QStringLiteral("showers or thunderstorms"), Thunderstorm}, 0337 {QStringLiteral("smoke"), NotAvailable}, 0338 {QStringLiteral("snow"), Snow}, 0339 {QStringLiteral("snow and blizzard"), Snow}, 0340 {QStringLiteral("snow and blizzard and blowing snow"), Snow}, 0341 {QStringLiteral("snow and blowing snow"), Snow}, 0342 {QStringLiteral("snow at times heavy"), Snow}, 0343 {QStringLiteral("snow at times heavy and blizzard"), Snow}, 0344 {QStringLiteral("snow at times heavy and blowing snow"), Snow}, 0345 {QStringLiteral("snow at times heavy mixed with freezing drizzle"), RainSnow}, 0346 {QStringLiteral("snow at times heavy mixed with freezing rain"), RainSnow}, 0347 {QStringLiteral("snow at times heavy mixed with ice pellets"), Snow}, 0348 {QStringLiteral("snow at times heavy mixed with rain"), RainSnow}, 0349 {QStringLiteral("snow at times heavy or freezing rain"), RainSnow}, 0350 {QStringLiteral("snow at times heavy or ice pellets"), Snow}, 0351 {QStringLiteral("snow at times heavy or rain"), RainSnow}, 0352 {QStringLiteral("snow mixed with freezing drizzle"), RainSnow}, 0353 {QStringLiteral("snow mixed with freezing rain"), RainSnow}, 0354 {QStringLiteral("snow mixed with ice pellets"), Snow}, 0355 {QStringLiteral("snow mixed with rain"), RainSnow}, 0356 {QStringLiteral("snow or freezing drizzle"), RainSnow}, 0357 {QStringLiteral("snow or freezing rain"), RainSnow}, 0358 {QStringLiteral("snow or ice pellets"), Snow}, 0359 {QStringLiteral("snow or rain"), RainSnow}, 0360 {QStringLiteral("snow squalls"), Snow}, 0361 {QStringLiteral("sunny"), ClearDay}, 0362 {QStringLiteral("sunny with cloudy periods"), PartlyCloudyDay}, 0363 {QStringLiteral("thunderstorms"), Thunderstorm}, 0364 {QStringLiteral("thunderstorms and possible hail"), Thunderstorm}, 0365 {QStringLiteral("wet flurries"), Flurries}, 0366 {QStringLiteral("wet flurries at times heavy"), Flurries}, 0367 {QStringLiteral("wet flurries at times heavy or rain snowers"), RainSnow}, 0368 {QStringLiteral("wet flurries or rain showers"), RainSnow}, 0369 {QStringLiteral("wet snow"), Snow}, 0370 {QStringLiteral("wet snow at times heavy"), Snow}, 0371 {QStringLiteral("wet snow at times heavy mixed with rain"), RainSnow}, 0372 {QStringLiteral("wet snow mixed with rain"), RainSnow}, 0373 {QStringLiteral("wet snow or rain"), RainSnow}, 0374 {QStringLiteral("windy"), NotAvailable}, 0375 0376 {QStringLiteral("chance of drizzle mixed with freezing drizzle"), LightRain}, 0377 {QStringLiteral("chance of flurries mixed with ice pellets"), Flurries}, 0378 {QStringLiteral("chance of flurries or ice pellets"), Flurries}, 0379 {QStringLiteral("chance of flurries or rain showers"), RainSnow}, 0380 {QStringLiteral("chance of flurries or thundershowers"), RainSnow}, 0381 {QStringLiteral("chance of freezing drizzle"), FreezingDrizzle}, 0382 {QStringLiteral("chance of freezing rain"), FreezingRain}, 0383 {QStringLiteral("chance of freezing rain mixed with snow"), RainSnow}, 0384 {QStringLiteral("chance of freezing rain or rain"), FreezingRain}, 0385 {QStringLiteral("chance of freezing rain or snow"), RainSnow}, 0386 {QStringLiteral("chance of light snow and blowing snow"), LightSnow}, 0387 {QStringLiteral("chance of light snow mixed with freezing drizzle"), LightSnow}, 0388 {QStringLiteral("chance of light snow mixed with ice pellets"), LightSnow}, 0389 {QStringLiteral("chance of light snow mixed with rain"), RainSnow}, 0390 {QStringLiteral("chance of light snow or freezing rain"), RainSnow}, 0391 {QStringLiteral("chance of light snow or ice pellets"), LightSnow}, 0392 {QStringLiteral("chance of light snow or rain"), RainSnow}, 0393 {QStringLiteral("chance of light wet snow"), Snow}, 0394 {QStringLiteral("chance of rain"), Rain}, 0395 {QStringLiteral("chance of rain at times heavy"), Rain}, 0396 {QStringLiteral("chance of rain mixed with snow"), RainSnow}, 0397 {QStringLiteral("chance of rain or drizzle"), Rain}, 0398 {QStringLiteral("chance of rain or freezing rain"), Rain}, 0399 {QStringLiteral("chance of rain or snow"), RainSnow}, 0400 {QStringLiteral("chance of rain showers or flurries"), RainSnow}, 0401 {QStringLiteral("chance of rain showers or wet flurries"), RainSnow}, 0402 {QStringLiteral("chance of severe thunderstorms"), Thunderstorm}, 0403 {QStringLiteral("chance of showers at times heavy"), Rain}, 0404 {QStringLiteral("chance of showers at times heavy or thundershowers"), Thunderstorm}, 0405 {QStringLiteral("chance of showers at times heavy or thunderstorms"), Thunderstorm}, 0406 {QStringLiteral("chance of showers or thundershowers"), Thunderstorm}, 0407 {QStringLiteral("chance of showers or thunderstorms"), Thunderstorm}, 0408 {QStringLiteral("chance of snow"), Snow}, 0409 {QStringLiteral("chance of snow and blizzard"), Snow}, 0410 {QStringLiteral("chance of snow mixed with freezing drizzle"), Snow}, 0411 {QStringLiteral("chance of snow mixed with freezing rain"), RainSnow}, 0412 {QStringLiteral("chance of snow mixed with rain"), RainSnow}, 0413 {QStringLiteral("chance of snow or rain"), RainSnow}, 0414 {QStringLiteral("chance of snow squalls"), Snow}, 0415 {QStringLiteral("chance of thundershowers"), Showers}, 0416 {QStringLiteral("chance of thunderstorms"), Thunderstorm}, 0417 {QStringLiteral("chance of thunderstorms and possible hail"), Thunderstorm}, 0418 {QStringLiteral("chance of wet flurries"), Flurries}, 0419 {QStringLiteral("chance of wet flurries at times heavy"), Flurries}, 0420 {QStringLiteral("chance of wet flurries or rain showers"), RainSnow}, 0421 {QStringLiteral("chance of wet snow"), Snow}, 0422 {QStringLiteral("chance of wet snow mixed with rain"), RainSnow}, 0423 {QStringLiteral("chance of wet snow or rain"), RainSnow}, 0424 }; 0425 } 0426 0427 QMap<QString, IonInterface::ConditionIcons> const &EnvCanadaIon::conditionIcons() const 0428 { 0429 static QMap<QString, ConditionIcons> const condval = setupConditionIconMappings(); 0430 return condval; 0431 } 0432 0433 QMap<QString, IonInterface::ConditionIcons> const &EnvCanadaIon::forecastIcons() const 0434 { 0435 static QMap<QString, ConditionIcons> const foreval = setupForecastIconMappings(); 0436 return foreval; 0437 } 0438 0439 QStringList EnvCanadaIon::validate(const QString &source) const 0440 { 0441 QStringList placeList; 0442 0443 QString sourceNormalized = source.toUpper(); 0444 QHash<QString, EnvCanadaIon::XMLMapInfo>::const_iterator it = m_places.constBegin(); 0445 while (it != m_places.constEnd()) { 0446 if (it.key().toUpper().contains(sourceNormalized)) { 0447 placeList.append(QStringLiteral("place|") + it.key()); 0448 } 0449 ++it; 0450 } 0451 0452 placeList.sort(); 0453 return placeList; 0454 } 0455 0456 // Get a specific Ion's data 0457 bool EnvCanadaIon::updateIonSource(const QString &source) 0458 { 0459 // qCDebug(IONENGINE_ENVCAN) << "updateIonSource()" << source; 0460 0461 // We expect the applet to send the source in the following tokenization: 0462 // ionname|validate|place_name - Triggers validation of place 0463 // ionname|weather|place_name - Triggers receiving weather of place 0464 0465 const QStringList sourceAction = source.split(QLatin1Char('|')); 0466 0467 // Guard: if the size of array is not 2 then we have bad data, return an error 0468 if (sourceAction.size() < 2) { 0469 setData(source, QStringLiteral("validate"), QStringLiteral("envcan|malformed")); 0470 return true; 0471 } 0472 0473 if (sourceAction[1] == QLatin1String("validate") && sourceAction.size() > 2) { 0474 const QStringList result = validate(sourceAction[2]); 0475 0476 const QString reply = (result.size() == 1 ? QString(QStringLiteral("envcan|valid|single|") + result[0]) 0477 : (result.size() > 1) ? QString(QStringLiteral("envcan|valid|multiple|") + result.join(QLatin1Char('|'))) 0478 : QString(QStringLiteral("envcan|invalid|single|") + sourceAction[2])); 0479 setData(source, QStringLiteral("validate"), reply); 0480 0481 return true; 0482 } 0483 if (sourceAction[1] == QLatin1String("weather") && sourceAction.size() > 2) { 0484 getXMLData(source); 0485 return true; 0486 } 0487 setData(source, QStringLiteral("validate"), QStringLiteral("envcan|malformed")); 0488 return true; 0489 } 0490 0491 // Parses city list and gets the correct city based on ID number 0492 void EnvCanadaIon::getXMLSetup() 0493 { 0494 // qCDebug(IONENGINE_ENVCAN) << "getXMLSetup()"; 0495 0496 // If network is down, we need to spin and wait 0497 0498 const QUrl url(QStringLiteral("http://dd.weather.gc.ca/citypage_weather/xml/siteList.xml")); 0499 0500 KIO::TransferJob *getJob = KIO::get(url, KIO::NoReload, KIO::HideProgressInfo); 0501 0502 m_xmlSetup.clear(); 0503 connect(getJob, &KIO::TransferJob::data, this, &EnvCanadaIon::setup_slotDataArrived); 0504 connect(getJob, &KJob::result, this, &EnvCanadaIon::setup_slotJobFinished); 0505 } 0506 0507 // Gets specific city XML data 0508 void EnvCanadaIon::getXMLData(const QString &source) 0509 { 0510 for (const QString &fetching : std::as_const(m_jobList)) { 0511 if (fetching == source) { 0512 // already getting this source and awaiting the data 0513 return; 0514 } 0515 } 0516 0517 // qCDebug(IONENGINE_ENVCAN) << source; 0518 0519 // Demunge source name for key only. 0520 QString dataKey = source; 0521 dataKey.remove(QStringLiteral("envcan|weather|")); 0522 const XMLMapInfo &place = m_places[dataKey]; 0523 0524 const QUrl url(QLatin1String("http://dd.weather.gc.ca/citypage_weather/xml/") + place.territoryName + QLatin1Char('/') + place.cityCode 0525 + QStringLiteral("_e.xml")); 0526 // url="file:///home/spstarr/Desktop/s0000649_e.xml"; 0527 // qCDebug(IONENGINE_ENVCAN) << "Will Try URL: " << url; 0528 0529 if (place.territoryName.isEmpty() && place.cityCode.isEmpty()) { 0530 setData(source, QStringLiteral("validate"), QStringLiteral("envcan|malformed")); 0531 return; 0532 } 0533 0534 KIO::TransferJob *getJob = KIO::get(url, KIO::Reload, KIO::HideProgressInfo); 0535 0536 m_jobXml.insert(getJob, new QXmlStreamReader); 0537 m_jobList.insert(getJob, source); 0538 0539 connect(getJob, &KIO::TransferJob::data, this, &EnvCanadaIon::slotDataArrived); 0540 connect(getJob, &KJob::result, this, &EnvCanadaIon::slotJobFinished); 0541 } 0542 0543 void EnvCanadaIon::setup_slotDataArrived(KIO::Job *job, const QByteArray &data) 0544 { 0545 Q_UNUSED(job) 0546 0547 if (data.isEmpty()) { 0548 // qCDebug(IONENGINE_ENVCAN) << "done!"; 0549 return; 0550 } 0551 0552 // Send to xml. 0553 // qCDebug(IONENGINE_ENVCAN) << data; 0554 m_xmlSetup.addData(data); 0555 } 0556 0557 void EnvCanadaIon::slotDataArrived(KIO::Job *job, const QByteArray &data) 0558 { 0559 if (data.isEmpty() || !m_jobXml.contains(job)) { 0560 return; 0561 } 0562 0563 // Send to xml. 0564 m_jobXml[job]->addData(data); 0565 } 0566 0567 void EnvCanadaIon::slotJobFinished(KJob *job) 0568 { 0569 // Dual use method, if we're fetching location data to parse we need to do this first 0570 const QString source = m_jobList.value(job); 0571 // qCDebug(IONENGINE_ENVCAN) << source << m_sourcesToReset.contains(source); 0572 setData(source, Data()); 0573 QXmlStreamReader *reader = m_jobXml.value(job); 0574 if (reader) { 0575 readXMLData(m_jobList[job], *reader); 0576 } 0577 0578 m_jobList.remove(job); 0579 delete m_jobXml[job]; 0580 m_jobXml.remove(job); 0581 0582 if (m_sourcesToReset.contains(source)) { 0583 m_sourcesToReset.removeAll(source); 0584 0585 // so the weather engine updates it's data 0586 forceImmediateUpdateOfAllVisualizations(); 0587 0588 // update the clients of our engine 0589 Q_EMIT forceUpdate(this, source); 0590 } 0591 } 0592 0593 void EnvCanadaIon::setup_slotJobFinished(KJob *job) 0594 { 0595 Q_UNUSED(job) 0596 const bool success = readXMLSetup(); 0597 m_xmlSetup.clear(); 0598 // qCDebug(IONENGINE_ENVCAN) << success << m_sourcesToReset; 0599 setInitialized(success); 0600 } 0601 0602 // Parse the city list and store into a QMap 0603 bool EnvCanadaIon::readXMLSetup() 0604 { 0605 bool success = false; 0606 QString territory; 0607 QString code; 0608 QString cityName; 0609 0610 // qCDebug(IONENGINE_ENVCAN) << "readXMLSetup()"; 0611 0612 while (!m_xmlSetup.atEnd()) { 0613 m_xmlSetup.readNext(); 0614 0615 const auto elementName = m_xmlSetup.name(); 0616 0617 if (m_xmlSetup.isStartElement()) { 0618 // XML ID code to match filename 0619 if (elementName == QLatin1String("site")) { 0620 code = m_xmlSetup.attributes().value(QStringLiteral("code")).toString(); 0621 } 0622 0623 if (elementName == QLatin1String("nameEn")) { 0624 cityName = m_xmlSetup.readElementText(); // Name of cities 0625 } 0626 0627 if (elementName == QLatin1String("provinceCode")) { 0628 territory = m_xmlSetup.readElementText(); // Provinces/Territory list 0629 } 0630 } 0631 0632 if (m_xmlSetup.isEndElement() && elementName == QLatin1String("site")) { 0633 EnvCanadaIon::XMLMapInfo info; 0634 QString tmp = cityName + QStringLiteral(", ") + territory; // Build the key name. 0635 0636 // Set the mappings 0637 info.cityCode = code; 0638 info.territoryName = territory; 0639 info.cityName = cityName; 0640 0641 // Set the string list, we will use for the applet to display the available cities. 0642 m_places[tmp] = info; 0643 success = true; 0644 } 0645 } 0646 0647 return (success && !m_xmlSetup.error()); 0648 } 0649 0650 void EnvCanadaIon::parseWeatherSite(WeatherData &data, QXmlStreamReader &xml) 0651 { 0652 while (!xml.atEnd()) { 0653 xml.readNext(); 0654 0655 const auto elementName = xml.name(); 0656 0657 if (xml.isStartElement()) { 0658 if (elementName == QLatin1String("license")) { 0659 data.creditUrl = xml.readElementText(); 0660 } else if (elementName == QLatin1String("location")) { 0661 parseLocations(data, xml); 0662 } else if (elementName == QLatin1String("warnings")) { 0663 // Cleanup warning list on update 0664 data.warnings.clear(); 0665 parseWarnings(data, xml); 0666 } else if (elementName == QLatin1String("currentConditions")) { 0667 parseConditions(data, xml); 0668 } else if (elementName == QLatin1String("forecastGroup")) { 0669 // Clean up forecast list on update 0670 data.forecasts.clear(); 0671 parseWeatherForecast(data, xml); 0672 } else if (elementName == QLatin1String("yesterdayConditions")) { 0673 parseYesterdayWeather(data, xml); 0674 } else if (elementName == QLatin1String("riseSet")) { 0675 parseAstronomicals(data, xml); 0676 } else if (elementName == QLatin1String("almanac")) { 0677 parseWeatherRecords(data, xml); 0678 } else { 0679 parseUnknownElement(xml); 0680 } 0681 } 0682 } 0683 } 0684 0685 // Parse Weather data main loop, from here we have to descend into each tag pair 0686 bool EnvCanadaIon::readXMLData(const QString &source, QXmlStreamReader &xml) 0687 { 0688 WeatherData data; 0689 0690 // qCDebug(IONENGINE_ENVCAN) << "readXMLData()"; 0691 0692 QString dataKey = source; 0693 dataKey.remove(QStringLiteral("envcan|weather|")); 0694 data.shortTerritoryName = m_places[dataKey].territoryName; 0695 while (!xml.atEnd()) { 0696 xml.readNext(); 0697 0698 if (xml.isEndElement()) { 0699 break; 0700 } 0701 0702 if (xml.isStartElement()) { 0703 if (xml.name() == QLatin1String("siteData")) { 0704 parseWeatherSite(data, xml); 0705 } else { 0706 parseUnknownElement(xml); 0707 } 0708 } 0709 } 0710 0711 bool solarDataSourceNeedsConnect = false; 0712 Plasma5Support::DataEngine *timeEngine = dataEngine(QStringLiteral("time")); 0713 if (timeEngine) { 0714 const bool canCalculateElevation = (data.observationDateTime.isValid() && (!qIsNaN(data.stationLatitude) && !qIsNaN(data.stationLongitude))); 0715 if (canCalculateElevation) { 0716 data.solarDataTimeEngineSourceName = QStringLiteral("%1|Solar|Latitude=%2|Longitude=%3|DateTime=%4") 0717 .arg(QString::fromUtf8(data.observationDateTime.timeZone().id())) 0718 .arg(data.stationLatitude) 0719 .arg(data.stationLongitude) 0720 .arg(data.observationDateTime.toString(Qt::ISODate)); 0721 solarDataSourceNeedsConnect = true; 0722 } 0723 0724 // check any previous data 0725 const auto it = m_weatherData.constFind(source); 0726 if (it != m_weatherData.constEnd()) { 0727 const QString &oldSolarDataTimeEngineSource = it.value().solarDataTimeEngineSourceName; 0728 0729 if (oldSolarDataTimeEngineSource == data.solarDataTimeEngineSourceName) { 0730 // can reuse elevation source (if any), copy over data 0731 data.isNight = it.value().isNight; 0732 solarDataSourceNeedsConnect = false; 0733 } else if (!oldSolarDataTimeEngineSource.isEmpty()) { 0734 // drop old elevation source 0735 timeEngine->disconnectSource(oldSolarDataTimeEngineSource, this); 0736 } 0737 } 0738 } 0739 0740 m_weatherData[source] = data; 0741 0742 // connect only after m_weatherData has the data, so the instant data push handling can see it 0743 if (solarDataSourceNeedsConnect) { 0744 timeEngine->connectSource(data.solarDataTimeEngineSourceName, this); 0745 } else { 0746 updateWeather(source); 0747 } 0748 0749 return !xml.error(); 0750 } 0751 0752 void EnvCanadaIon::parseFloat(float &value, QXmlStreamReader &xml) 0753 { 0754 bool ok = false; 0755 const float result = xml.readElementText().toFloat(&ok); 0756 if (ok) { 0757 value = result; 0758 } 0759 } 0760 0761 void EnvCanadaIon::parseDateTime(WeatherData &data, QXmlStreamReader &xml, WeatherData::WeatherEvent *event) 0762 { 0763 Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("dateTime")); 0764 0765 // What kind of date info is this? 0766 const QString dateType = xml.attributes().value(QStringLiteral("name")).toString(); 0767 const QString dateZone = xml.attributes().value(QStringLiteral("zone")).toString(); 0768 const QString dateUtcOffset = xml.attributes().value(QStringLiteral("UTCOffset")).toString(); 0769 0770 QString selectTimeStamp; 0771 0772 while (!xml.atEnd()) { 0773 xml.readNext(); 0774 0775 if (xml.isEndElement()) { 0776 break; 0777 } 0778 0779 const auto elementName = xml.name(); 0780 0781 if (xml.isStartElement()) { 0782 if (dateType == QLatin1String("xmlCreation")) { 0783 return; 0784 } 0785 if (dateZone == QLatin1String("UTC")) { 0786 return; 0787 } 0788 if (elementName == QLatin1String("year")) { 0789 xml.readElementText(); 0790 } else if (elementName == QLatin1String("month")) { 0791 xml.readElementText(); 0792 } else if (elementName == QLatin1String("day")) { 0793 xml.readElementText(); 0794 } else if (elementName == QLatin1String("hour")) 0795 xml.readElementText(); 0796 else if (elementName == QLatin1String("minute")) 0797 xml.readElementText(); 0798 else if (elementName == QLatin1String("timeStamp")) 0799 selectTimeStamp = xml.readElementText(); 0800 else if (elementName == QLatin1String("textSummary")) { 0801 if (dateType == QLatin1String("eventIssue")) { 0802 if (event) { 0803 event->timestamp = xml.readElementText(); 0804 } 0805 } else if (dateType == QLatin1String("observation")) { 0806 xml.readElementText(); 0807 QDateTime observationDateTime = QDateTime::fromString(selectTimeStamp, QStringLiteral("yyyyMMddHHmmss")); 0808 QTimeZone timeZone = QTimeZone(dateZone.toUtf8()); 0809 // if timezone id not recognized, fallback to utcoffset 0810 if (!timeZone.isValid()) { 0811 timeZone = QTimeZone(dateUtcOffset.toInt() * 3600); 0812 } 0813 if (observationDateTime.isValid() && timeZone.isValid()) { 0814 data.observationDateTime = observationDateTime; 0815 data.observationDateTime.setTimeZone(timeZone); 0816 } 0817 data.obsTimestamp = observationDateTime.toString(QStringLiteral("dd.MM.yyyy @ hh:mm")); 0818 } else if (dateType == QLatin1String("forecastIssue")) { 0819 data.forecastTimestamp = xml.readElementText(); 0820 } else if (dateType == QLatin1String("sunrise")) { 0821 data.sunriseTimestamp = xml.readElementText(); 0822 } else if (dateType == QLatin1String("sunset")) { 0823 data.sunsetTimestamp = xml.readElementText(); 0824 } else if (dateType == QLatin1String("moonrise")) { 0825 data.moonriseTimestamp = xml.readElementText(); 0826 } else if (dateType == QLatin1String("moonset")) { 0827 data.moonsetTimestamp = xml.readElementText(); 0828 } 0829 } 0830 } 0831 } 0832 } 0833 0834 void EnvCanadaIon::parseLocations(WeatherData &data, QXmlStreamReader &xml) 0835 { 0836 Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("location")); 0837 0838 while (!xml.atEnd()) { 0839 xml.readNext(); 0840 0841 if (xml.isEndElement()) { 0842 break; 0843 } 0844 0845 const auto elementName = xml.name(); 0846 0847 if (xml.isStartElement()) { 0848 if (elementName == QLatin1String("country")) { 0849 data.countryName = xml.readElementText(); 0850 } else if (elementName == QLatin1String("province") || elementName == QLatin1String("territory")) { 0851 data.longTerritoryName = xml.readElementText(); 0852 } else if (elementName == QLatin1String("name")) { 0853 data.cityName = xml.readElementText(); 0854 } else if (elementName == QLatin1String("region")) { 0855 data.regionName = xml.readElementText(); 0856 } else { 0857 parseUnknownElement(xml); 0858 } 0859 } 0860 } 0861 } 0862 0863 void EnvCanadaIon::parseWindInfo(WeatherData &data, QXmlStreamReader &xml) 0864 { 0865 Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("wind")); 0866 0867 while (!xml.atEnd()) { 0868 xml.readNext(); 0869 0870 if (xml.isEndElement()) { 0871 break; 0872 } 0873 0874 const auto elementName = xml.name(); 0875 0876 if (xml.isStartElement()) { 0877 if (elementName == QLatin1String("speed")) { 0878 parseFloat(data.windSpeed, xml); 0879 } else if (elementName == QLatin1String("gust")) { 0880 parseFloat(data.windGust, xml); 0881 } else if (elementName == QLatin1String("direction")) { 0882 data.windDirection = xml.readElementText(); 0883 } else if (elementName == QLatin1String("bearing")) { 0884 data.windDegrees = xml.attributes().value(QStringLiteral("degrees")).toString(); 0885 } else { 0886 parseUnknownElement(xml); 0887 } 0888 } 0889 } 0890 } 0891 0892 void EnvCanadaIon::parseConditions(WeatherData &data, QXmlStreamReader &xml) 0893 { 0894 Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("currentConditions")); 0895 data.temperature = qQNaN(); 0896 data.dewpoint = qQNaN(); 0897 data.condition = i18n("N/A"); 0898 data.humidex.clear(); 0899 data.stationID = i18n("N/A"); 0900 data.stationLatitude = qQNaN(); 0901 data.stationLongitude = qQNaN(); 0902 data.pressure = qQNaN(); 0903 data.visibility = qQNaN(); 0904 data.humidity = qQNaN(); 0905 0906 while (!xml.atEnd()) { 0907 xml.readNext(); 0908 0909 const auto elementName = xml.name(); 0910 0911 if (xml.isEndElement() && elementName == QLatin1String("currentConditions")) 0912 break; 0913 0914 if (xml.isStartElement()) { 0915 if (elementName == QLatin1String("station")) { 0916 data.stationID = xml.attributes().value(QStringLiteral("code")).toString(); 0917 QRegularExpression dumpDirection(QStringLiteral("[^0-9.]")); 0918 data.stationLatitude = xml.attributes().value(QStringLiteral("lat")).toString().remove(dumpDirection).toDouble(); 0919 data.stationLongitude = xml.attributes().value(QStringLiteral("lon")).toString().remove(dumpDirection).toDouble(); 0920 } else if (elementName == QLatin1String("dateTime")) { 0921 parseDateTime(data, xml); 0922 } else if (elementName == QLatin1String("condition")) { 0923 data.condition = xml.readElementText().trimmed(); 0924 } else if (elementName == QLatin1String("temperature")) { 0925 // prevent N/A text to result in 0.0 value 0926 parseFloat(data.temperature, xml); 0927 } else if (elementName == QLatin1String("dewpoint")) { 0928 // prevent N/A text to result in 0.0 value 0929 parseFloat(data.dewpoint, xml); 0930 } else if (elementName == QLatin1String("humidex")) { 0931 data.humidex = xml.readElementText(); 0932 } else if (elementName == QLatin1String("windChill")) { 0933 // prevent N/A text to result in 0.0 value 0934 parseFloat(data.windchill, xml); 0935 } else if (elementName == QLatin1String("pressure")) { 0936 data.pressureTendency = xml.attributes().value(QStringLiteral("tendency")).toString(); 0937 if (data.pressureTendency.isEmpty()) { 0938 data.pressureTendency = QStringLiteral("steady"); 0939 } 0940 parseFloat(data.pressure, xml); 0941 } else if (elementName == QLatin1String("visibility")) { 0942 parseFloat(data.visibility, xml); 0943 } else if (elementName == QLatin1String("relativeHumidity")) { 0944 parseFloat(data.humidity, xml); 0945 } else if (elementName == QLatin1String("wind")) { 0946 parseWindInfo(data, xml); 0947 } 0948 //} else { 0949 // parseUnknownElement(xml); 0950 //} 0951 } 0952 } 0953 } 0954 0955 void EnvCanadaIon::parseWarnings(WeatherData &data, QXmlStreamReader &xml) 0956 { 0957 WeatherData::WeatherEvent *warning = new WeatherData::WeatherEvent; 0958 0959 Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("warnings")); 0960 QString eventURL = xml.attributes().value(QStringLiteral("url")).toString(); 0961 0962 // envcan provides three type of events: 'warning', 'watch' and 'advisory' 0963 const auto mapToPriority = [](const QString &type) { 0964 if (type == QLatin1String("warning")) { 0965 return 3; 0966 } else if (type == QLatin1String("watch")) { 0967 return 2; 0968 } else { 0969 return 1; 0970 } 0971 }; 0972 0973 while (!xml.atEnd()) { 0974 xml.readNext(); 0975 0976 const auto elementName = xml.name(); 0977 0978 if (xml.isEndElement() && elementName == QLatin1String("warnings")) { 0979 break; 0980 } 0981 0982 if (xml.isStartElement()) { 0983 if (elementName == QLatin1String("dateTime")) { 0984 parseDateTime(data, xml, warning); 0985 if (!warning->timestamp.isEmpty() && !warning->url.isEmpty()) { 0986 data.warnings.append(warning); 0987 warning = new WeatherData::WeatherEvent; 0988 } 0989 } else if (elementName == QLatin1String("event")) { 0990 // Append new event to list. 0991 warning->url = eventURL; 0992 warning->description = xml.attributes().value(QStringLiteral("description")).toString(); 0993 warning->priority = mapToPriority(xml.attributes().value(QStringLiteral("type")).toString()); 0994 } else { 0995 if (xml.name() != QLatin1String("dateTime")) { 0996 parseUnknownElement(xml); 0997 } 0998 } 0999 } 1000 } 1001 delete warning; 1002 } 1003 1004 void EnvCanadaIon::parseWeatherForecast(WeatherData &data, QXmlStreamReader &xml) 1005 { 1006 WeatherData::ForecastInfo *forecast = new WeatherData::ForecastInfo; 1007 Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("forecastGroup")); 1008 1009 while (!xml.atEnd()) { 1010 xml.readNext(); 1011 1012 const auto elementName = xml.name(); 1013 1014 if (xml.isEndElement() && elementName == QLatin1String("forecastGroup")) { 1015 break; 1016 } 1017 1018 if (xml.isStartElement()) { 1019 if (elementName == QLatin1String("dateTime")) { 1020 parseDateTime(data, xml); 1021 } else if (elementName == QLatin1String("regionalNormals")) { 1022 parseRegionalNormals(data, xml); 1023 } else if (elementName == QLatin1String("forecast")) { 1024 parseForecast(data, xml, forecast); 1025 forecast = new WeatherData::ForecastInfo; 1026 } else { 1027 parseUnknownElement(xml); 1028 } 1029 } 1030 } 1031 delete forecast; 1032 } 1033 1034 void EnvCanadaIon::parseRegionalNormals(WeatherData &data, QXmlStreamReader &xml) 1035 { 1036 Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("regionalNormals")); 1037 1038 while (!xml.atEnd()) { 1039 xml.readNext(); 1040 1041 if (xml.isEndElement()) { 1042 break; 1043 } 1044 1045 const auto elementName = xml.name(); 1046 1047 if (xml.isStartElement()) { 1048 if (elementName == QLatin1String("textSummary")) { 1049 xml.readElementText(); 1050 } else if (elementName == QLatin1String("temperature") && xml.attributes().value(QStringLiteral("class")) == QLatin1String("high")) { 1051 // prevent N/A text to result in 0.0 value 1052 parseFloat(data.normalHigh, xml); 1053 } else if (elementName == QLatin1String("temperature") && xml.attributes().value(QStringLiteral("class")) == QLatin1String("low")) { 1054 // prevent N/A text to result in 0.0 value 1055 parseFloat(data.normalLow, xml); 1056 } 1057 } 1058 } 1059 } 1060 1061 void EnvCanadaIon::parseForecast(WeatherData &data, QXmlStreamReader &xml, WeatherData::ForecastInfo *forecast) 1062 { 1063 Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("forecast")); 1064 1065 while (!xml.atEnd()) { 1066 xml.readNext(); 1067 1068 const auto elementName = xml.name(); 1069 1070 if (xml.isEndElement() && elementName == QLatin1String("forecast")) { 1071 data.forecasts.append(forecast); 1072 break; 1073 } 1074 1075 if (xml.isStartElement()) { 1076 if (elementName == QLatin1String("period")) { 1077 forecast->forecastPeriod = xml.attributes().value(QStringLiteral("textForecastName")).toString(); 1078 } else if (elementName == QLatin1String("textSummary")) { 1079 forecast->forecastSummary = xml.readElementText(); 1080 } else if (elementName == QLatin1String("abbreviatedForecast")) { 1081 parseShortForecast(forecast, xml); 1082 } else if (elementName == QLatin1String("temperatures")) { 1083 parseForecastTemperatures(forecast, xml); 1084 } else if (elementName == QLatin1String("winds")) { 1085 parseWindForecast(forecast, xml); 1086 } else if (elementName == QLatin1String("precipitation")) { 1087 parsePrecipitationForecast(forecast, xml); 1088 } else if (elementName == QLatin1String("uv")) { 1089 data.UVRating = xml.attributes().value(QStringLiteral("category")).toString(); 1090 parseUVIndex(data, xml); 1091 // else if (elementName == QLatin1String("frost")) { FIXME: Wait until winter to see what this looks like. 1092 // parseFrost(xml, forecast); 1093 } else { 1094 if (elementName != QLatin1String("forecast")) { 1095 parseUnknownElement(xml); 1096 } 1097 } 1098 } 1099 } 1100 } 1101 1102 void EnvCanadaIon::parseShortForecast(WeatherData::ForecastInfo *forecast, QXmlStreamReader &xml) 1103 { 1104 Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("abbreviatedForecast")); 1105 1106 QString shortText; 1107 1108 while (!xml.atEnd()) { 1109 xml.readNext(); 1110 1111 const auto elementName = xml.name(); 1112 1113 if (xml.isEndElement() && elementName == QLatin1String("abbreviatedForecast")) { 1114 break; 1115 } 1116 1117 if (xml.isStartElement()) { 1118 if (elementName == QLatin1String("pop")) { 1119 parseFloat(forecast->popPrecent, xml); 1120 } 1121 if (elementName == QLatin1String("textSummary")) { 1122 shortText = xml.readElementText(); 1123 QMap<QString, ConditionIcons> forecastList = forecastIcons(); 1124 if ((forecast->forecastPeriod == QLatin1String("tonight")) || (forecast->forecastPeriod.contains(QLatin1String("night")))) { 1125 forecastList.insert(QStringLiteral("a few clouds"), FewCloudsNight); 1126 forecastList.insert(QStringLiteral("cloudy periods"), PartlyCloudyNight); 1127 forecastList.insert(QStringLiteral("chance of drizzle mixed with rain"), ChanceShowersNight); 1128 forecastList.insert(QStringLiteral("chance of drizzle"), ChanceShowersNight); 1129 forecastList.insert(QStringLiteral("chance of drizzle or rain"), ChanceShowersNight); 1130 forecastList.insert(QStringLiteral("chance of flurries"), ChanceSnowNight); 1131 forecastList.insert(QStringLiteral("chance of light snow"), ChanceSnowNight); 1132 forecastList.insert(QStringLiteral("chance of flurries at times heavy"), ChanceSnowNight); 1133 forecastList.insert(QStringLiteral("chance of showers or drizzle"), ChanceShowersNight); 1134 forecastList.insert(QStringLiteral("chance of showers"), ChanceShowersNight); 1135 forecastList.insert(QStringLiteral("clearing"), ClearNight); 1136 } else { 1137 forecastList.insert(QStringLiteral("a few clouds"), FewCloudsDay); 1138 forecastList.insert(QStringLiteral("cloudy periods"), PartlyCloudyDay); 1139 forecastList.insert(QStringLiteral("chance of drizzle mixed with rain"), ChanceShowersDay); 1140 forecastList.insert(QStringLiteral("chance of drizzle"), ChanceShowersDay); 1141 forecastList.insert(QStringLiteral("chance of drizzle or rain"), ChanceShowersDay); 1142 forecastList.insert(QStringLiteral("chance of flurries"), ChanceSnowDay); 1143 forecastList.insert(QStringLiteral("chance of light snow"), ChanceSnowDay); 1144 forecastList.insert(QStringLiteral("chance of flurries at times heavy"), ChanceSnowDay); 1145 forecastList.insert(QStringLiteral("chance of showers or drizzle"), ChanceShowersDay); 1146 forecastList.insert(QStringLiteral("chance of showers"), ChanceShowersDay); 1147 forecastList.insert(QStringLiteral("clearing"), ClearDay); 1148 } 1149 forecast->shortForecast = shortText; 1150 forecast->iconName = getWeatherIcon(forecastList, shortText.toLower()); 1151 } 1152 } 1153 } 1154 } 1155 1156 void EnvCanadaIon::parseUVIndex(WeatherData &data, QXmlStreamReader &xml) 1157 { 1158 Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("uv")); 1159 1160 while (!xml.atEnd()) { 1161 xml.readNext(); 1162 1163 const auto elementName = xml.name(); 1164 1165 if (xml.isEndElement() && elementName == QLatin1String("uv")) { 1166 break; 1167 } 1168 1169 if (xml.isStartElement()) { 1170 if (elementName == QLatin1String("index")) { 1171 data.UVIndex = xml.readElementText(); 1172 } 1173 if (elementName == QLatin1String("textSummary")) { 1174 xml.readElementText(); 1175 } 1176 } 1177 } 1178 } 1179 1180 void EnvCanadaIon::parseForecastTemperatures(WeatherData::ForecastInfo *forecast, QXmlStreamReader &xml) 1181 { 1182 Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("temperatures")); 1183 1184 while (!xml.atEnd()) { 1185 xml.readNext(); 1186 1187 const auto elementName = xml.name(); 1188 1189 if (xml.isEndElement() && elementName == QLatin1String("temperatures")) { 1190 break; 1191 } 1192 1193 if (xml.isStartElement()) { 1194 if (elementName == QLatin1String("temperature") && xml.attributes().value(QStringLiteral("class")) == QLatin1String("low")) { 1195 parseFloat(forecast->tempLow, xml); 1196 } else if (elementName == QLatin1String("temperature") && xml.attributes().value(QStringLiteral("class")) == QLatin1String("high")) { 1197 parseFloat(forecast->tempHigh, xml); 1198 } else if (elementName == QLatin1String("textSummary")) { 1199 xml.readElementText(); 1200 } 1201 } 1202 } 1203 } 1204 1205 void EnvCanadaIon::parsePrecipitationForecast(WeatherData::ForecastInfo *forecast, QXmlStreamReader &xml) 1206 { 1207 Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("precipitation")); 1208 1209 while (!xml.atEnd()) { 1210 xml.readNext(); 1211 1212 const auto elementName = xml.name(); 1213 1214 if (xml.isEndElement() && elementName == QLatin1String("precipitation")) { 1215 break; 1216 } 1217 1218 if (xml.isStartElement()) { 1219 if (elementName == QLatin1String("textSummary")) { 1220 forecast->precipForecast = xml.readElementText(); 1221 } else if (elementName == QLatin1String("precipType")) { 1222 forecast->precipType = xml.readElementText(); 1223 } else if (elementName == QLatin1String("accumulation")) { 1224 parsePrecipTotals(forecast, xml); 1225 } 1226 } 1227 } 1228 } 1229 1230 void EnvCanadaIon::parsePrecipTotals(WeatherData::ForecastInfo *forecast, QXmlStreamReader &xml) 1231 { 1232 Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("accumulation")); 1233 1234 while (!xml.atEnd()) { 1235 xml.readNext(); 1236 1237 const auto elementName = xml.name(); 1238 1239 if (xml.isEndElement() && elementName == QLatin1String("accumulation")) { 1240 break; 1241 } 1242 1243 if (elementName == QLatin1String("name")) { 1244 xml.readElementText(); 1245 } else if (elementName == QLatin1String("amount")) { 1246 forecast->precipTotalExpected = xml.readElementText(); 1247 } 1248 } 1249 } 1250 1251 void EnvCanadaIon::parseWindForecast(WeatherData::ForecastInfo *forecast, QXmlStreamReader &xml) 1252 { 1253 Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("winds")); 1254 1255 while (!xml.atEnd()) { 1256 xml.readNext(); 1257 1258 const auto elementName = xml.name(); 1259 1260 if (xml.isEndElement() && elementName == QLatin1String("winds")) { 1261 break; 1262 } 1263 1264 if (xml.isStartElement()) { 1265 if (elementName == QLatin1String("textSummary")) { 1266 forecast->windForecast = xml.readElementText(); 1267 } else { 1268 if (xml.name() != QLatin1String("winds")) { 1269 parseUnknownElement(xml); 1270 } 1271 } 1272 } 1273 } 1274 } 1275 1276 void EnvCanadaIon::parseYesterdayWeather(WeatherData &data, QXmlStreamReader &xml) 1277 { 1278 Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("yesterdayConditions")); 1279 1280 while (!xml.atEnd()) { 1281 xml.readNext(); 1282 1283 if (xml.isEndElement()) { 1284 break; 1285 } 1286 1287 const auto elementName = xml.name(); 1288 1289 if (xml.isStartElement()) { 1290 if (elementName == QLatin1String("temperature") && xml.attributes().value(QStringLiteral("class")) == QLatin1String("high")) { 1291 parseFloat(data.prevHigh, xml); 1292 } else if (elementName == QLatin1String("temperature") && xml.attributes().value(QStringLiteral("class")) == QLatin1String("low")) { 1293 parseFloat(data.prevLow, xml); 1294 } else if (elementName == QLatin1String("precip")) { 1295 data.prevPrecipType = xml.attributes().value(QStringLiteral("units")).toString(); 1296 if (data.prevPrecipType.isEmpty()) { 1297 data.prevPrecipType = QString::number(KUnitConversion::NoUnit); 1298 } 1299 data.prevPrecipTotal = xml.readElementText(); 1300 } 1301 } 1302 } 1303 } 1304 1305 void EnvCanadaIon::parseWeatherRecords(WeatherData &data, QXmlStreamReader &xml) 1306 { 1307 Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("almanac")); 1308 1309 while (!xml.atEnd()) { 1310 xml.readNext(); 1311 1312 const auto elementName = xml.name(); 1313 1314 if (xml.isEndElement() && elementName == QLatin1String("almanac")) { 1315 break; 1316 } 1317 1318 if (xml.isStartElement()) { 1319 if (elementName == QLatin1String("temperature") && xml.attributes().value(QStringLiteral("class")) == QLatin1String("extremeMax")) { 1320 parseFloat(data.recordHigh, xml); 1321 } else if (elementName == QLatin1String("temperature") && xml.attributes().value(QStringLiteral("class")) == QLatin1String("extremeMin")) { 1322 parseFloat(data.recordLow, xml); 1323 } else if (elementName == QLatin1String("precipitation") && xml.attributes().value(QStringLiteral("class")) == QLatin1String("extremeRainfall")) { 1324 parseFloat(data.recordRain, xml); 1325 } else if (elementName == QLatin1String("precipitation") && xml.attributes().value(QStringLiteral("class")) == QLatin1String("extremeSnowfall")) { 1326 parseFloat(data.recordSnow, xml); 1327 } 1328 } 1329 } 1330 } 1331 1332 void EnvCanadaIon::parseAstronomicals(WeatherData &data, QXmlStreamReader &xml) 1333 { 1334 Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("riseSet")); 1335 1336 while (!xml.atEnd()) { 1337 xml.readNext(); 1338 1339 const auto elementName = xml.name(); 1340 1341 if (xml.isEndElement() && elementName == QLatin1String("riseSet")) { 1342 break; 1343 } 1344 1345 if (xml.isStartElement()) { 1346 if (elementName == QLatin1String("disclaimer")) { 1347 xml.readElementText(); 1348 } else if (elementName == QLatin1String("dateTime")) { 1349 parseDateTime(data, xml); 1350 } 1351 } 1352 } 1353 } 1354 1355 // handle when no XML tag is found 1356 void EnvCanadaIon::parseUnknownElement(QXmlStreamReader &xml) const 1357 { 1358 while (!xml.atEnd()) { 1359 xml.readNext(); 1360 1361 if (xml.isEndElement()) { 1362 break; 1363 } 1364 1365 if (xml.isStartElement()) { 1366 parseUnknownElement(xml); 1367 } 1368 } 1369 } 1370 1371 void EnvCanadaIon::updateWeather(const QString &source) 1372 { 1373 // qCDebug(IONENGINE_ENVCAN) << "updateWeather()"; 1374 1375 const WeatherData &weatherData = m_weatherData[source]; 1376 1377 Plasma5Support::DataEngine::Data data; 1378 1379 data.insert(QStringLiteral("Country"), weatherData.countryName); 1380 data.insert(QStringLiteral("Place"), QVariant(weatherData.cityName + QStringLiteral(", ") + weatherData.shortTerritoryName)); 1381 data.insert(QStringLiteral("Region"), weatherData.regionName); 1382 1383 data.insert(QStringLiteral("Station"), weatherData.stationID.isEmpty() ? i18n("N/A") : weatherData.stationID.toUpper()); 1384 1385 const bool stationCoordValid = (!qIsNaN(weatherData.stationLatitude) && !qIsNaN(weatherData.stationLongitude)); 1386 1387 if (stationCoordValid) { 1388 data.insert(QStringLiteral("Latitude"), weatherData.stationLatitude); 1389 data.insert(QStringLiteral("Longitude"), weatherData.stationLongitude); 1390 } 1391 1392 // Real weather - Current conditions 1393 if (weatherData.observationDateTime.isValid()) { 1394 data.insert(QStringLiteral("Observation Timestamp"), weatherData.observationDateTime); 1395 } 1396 1397 data.insert(QStringLiteral("Observation Period"), weatherData.obsTimestamp); 1398 1399 if (!weatherData.condition.isEmpty()) { 1400 data.insert(QStringLiteral("Current Conditions"), i18nc("weather condition", weatherData.condition.toUtf8().data())); 1401 } 1402 // qCDebug(IONENGINE_ENVCAN) << "i18n condition string: " << qPrintable(condition(source)); 1403 1404 QMap<QString, ConditionIcons> conditionList = conditionIcons(); 1405 1406 if (weatherData.isNight) { 1407 conditionList.insert(QStringLiteral("decreasing cloud"), FewCloudsNight); 1408 conditionList.insert(QStringLiteral("mostly cloudy"), PartlyCloudyNight); 1409 conditionList.insert(QStringLiteral("partly cloudy"), PartlyCloudyNight); 1410 conditionList.insert(QStringLiteral("fair"), FewCloudsNight); 1411 } else { 1412 conditionList.insert(QStringLiteral("decreasing cloud"), FewCloudsDay); 1413 conditionList.insert(QStringLiteral("mostly cloudy"), PartlyCloudyDay); 1414 conditionList.insert(QStringLiteral("partly cloudy"), PartlyCloudyDay); 1415 conditionList.insert(QStringLiteral("fair"), FewCloudsDay); 1416 } 1417 1418 data.insert(QStringLiteral("Condition Icon"), getWeatherIcon(conditionList, weatherData.condition)); 1419 1420 if (!qIsNaN(weatherData.temperature)) { 1421 data.insert(QStringLiteral("Temperature"), weatherData.temperature); 1422 } 1423 if (!qIsNaN(weatherData.windchill)) { 1424 data.insert(QStringLiteral("Windchill"), weatherData.windchill); 1425 } 1426 if (!weatherData.humidex.isEmpty()) { 1427 data.insert(QStringLiteral("Humidex"), weatherData.humidex); 1428 } 1429 1430 // Used for all temperatures 1431 data.insert(QStringLiteral("Temperature Unit"), KUnitConversion::Celsius); 1432 1433 if (!qIsNaN(weatherData.dewpoint)) { 1434 data.insert(QStringLiteral("Dewpoint"), weatherData.dewpoint); 1435 } 1436 1437 if (!qIsNaN(weatherData.pressure)) { 1438 data.insert(QStringLiteral("Pressure"), weatherData.pressure); 1439 data.insert(QStringLiteral("Pressure Unit"), KUnitConversion::Kilopascal); 1440 data.insert(QStringLiteral("Pressure Tendency"), weatherData.pressureTendency); 1441 } 1442 1443 if (!qIsNaN(weatherData.visibility)) { 1444 data.insert(QStringLiteral("Visibility"), weatherData.visibility); 1445 data.insert(QStringLiteral("Visibility Unit"), KUnitConversion::Kilometer); 1446 } 1447 1448 if (!qIsNaN(weatherData.humidity)) { 1449 data.insert(QStringLiteral("Humidity"), weatherData.humidity); 1450 data.insert(QStringLiteral("Humidity Unit"), KUnitConversion::Percent); 1451 } 1452 1453 if (!qIsNaN(weatherData.windSpeed)) { 1454 data.insert(QStringLiteral("Wind Speed"), weatherData.windSpeed); 1455 } 1456 if (!qIsNaN(weatherData.windGust)) { 1457 data.insert(QStringLiteral("Wind Gust"), weatherData.windGust); 1458 } 1459 1460 if (!qIsNaN(weatherData.windSpeed) || !qIsNaN(weatherData.windGust)) { 1461 data.insert(QStringLiteral("Wind Speed Unit"), KUnitConversion::KilometerPerHour); 1462 } 1463 1464 if (!qIsNaN(weatherData.windSpeed) && static_cast<int>(weatherData.windSpeed) == 0) { 1465 data.insert(QStringLiteral("Wind Direction"), QStringLiteral("VR")); // Variable/calm 1466 } else if (!weatherData.windDirection.isEmpty()) { 1467 data.insert(QStringLiteral("Wind Direction"), weatherData.windDirection); 1468 } 1469 1470 if (!qIsNaN(weatherData.normalHigh)) { 1471 data.insert(QStringLiteral("Normal High"), weatherData.normalHigh); 1472 } 1473 if (!qIsNaN(weatherData.normalLow)) { 1474 data.insert(QStringLiteral("Normal Low"), weatherData.normalLow); 1475 } 1476 1477 // Check if UV index is available for the location 1478 if (!weatherData.UVIndex.isEmpty()) { 1479 data.insert(QStringLiteral("UV Index"), weatherData.UVIndex); 1480 } 1481 if (!weatherData.UVRating.isEmpty()) { 1482 data.insert(QStringLiteral("UV Rating"), weatherData.UVRating); 1483 } 1484 1485 const QList<WeatherData::WeatherEvent *> &warnings = weatherData.warnings; 1486 1487 data.insert(QStringLiteral("Total Warnings Issued"), warnings.size()); 1488 1489 for (int k = 0; k < warnings.size(); ++k) { 1490 const WeatherData::WeatherEvent *warning = warnings.at(k); 1491 const QString number = QString::number(k); 1492 1493 data.insert(QStringLiteral("Warning Priority ") + number, warning->priority); 1494 data.insert(QStringLiteral("Warning Description ") + number, warning->description); 1495 data.insert(QStringLiteral("Warning Info ") + number, warning->url); 1496 data.insert(QStringLiteral("Warning Timestamp ") + number, warning->timestamp); 1497 } 1498 1499 const QList<WeatherData::ForecastInfo *> &forecasts = weatherData.forecasts; 1500 1501 // Set number of forecasts per day/night supported 1502 data.insert(QStringLiteral("Total Weather Days"), forecasts.size()); 1503 1504 int i = 0; 1505 for (const WeatherData::ForecastInfo *forecastInfo : forecasts) { 1506 QString forecastPeriod = forecastInfo->forecastPeriod; 1507 if (forecastPeriod.isEmpty()) { 1508 forecastPeriod = i18n("N/A"); 1509 } else { 1510 // We need to shortform the day/night strings. 1511 1512 forecastPeriod.replace(QStringLiteral("Today"), i18n("day")); 1513 forecastPeriod.replace(QStringLiteral("Tonight"), i18nc("Short for tonight", "nite")); 1514 forecastPeriod.replace(QStringLiteral("night"), i18nc("Short for night, appended to the end of the weekday", "nt")); 1515 forecastPeriod.replace(QStringLiteral("Saturday"), i18nc("Short for Saturday", "Sat")); 1516 forecastPeriod.replace(QStringLiteral("Sunday"), i18nc("Short for Sunday", "Sun")); 1517 forecastPeriod.replace(QStringLiteral("Monday"), i18nc("Short for Monday", "Mon")); 1518 forecastPeriod.replace(QStringLiteral("Tuesday"), i18nc("Short for Tuesday", "Tue")); 1519 forecastPeriod.replace(QStringLiteral("Wednesday"), i18nc("Short for Wednesday", "Wed")); 1520 forecastPeriod.replace(QStringLiteral("Thursday"), i18nc("Short for Thursday", "Thu")); 1521 forecastPeriod.replace(QStringLiteral("Friday"), i18nc("Short for Friday", "Fri")); 1522 } 1523 const QString shortForecast = 1524 forecastInfo->shortForecast.isEmpty() ? i18n("N/A") : i18nc("weather forecast", forecastInfo->shortForecast.toUtf8().data()); 1525 1526 const QString tempHigh = qIsNaN(forecastInfo->tempHigh) ? QString() : QString::number(forecastInfo->tempHigh); 1527 const QString tempLow = qIsNaN(forecastInfo->tempLow) ? QString() : QString::number(forecastInfo->tempLow); 1528 const QString popPrecent = qIsNaN(forecastInfo->popPrecent) ? QString() : QString::number(forecastInfo->popPrecent); 1529 1530 data.insert(QStringLiteral("Short Forecast Day %1").arg(i), 1531 QStringLiteral("%1|%2|%3|%4|%5|%6").arg(forecastPeriod, forecastInfo->iconName, shortForecast, tempHigh, tempLow, popPrecent)); 1532 ++i; 1533 } 1534 1535 // yesterday 1536 if (!qIsNaN(weatherData.prevHigh)) { 1537 data.insert(QStringLiteral("Yesterday High"), weatherData.prevHigh); 1538 } 1539 if (!qIsNaN(weatherData.prevLow)) { 1540 data.insert(QStringLiteral("Yesterday Low"), weatherData.prevLow); 1541 } 1542 1543 const QString &prevPrecipTotal = weatherData.prevPrecipTotal; 1544 if (prevPrecipTotal == QLatin1String("Trace")) { 1545 data.insert(QStringLiteral("Yesterday Precip Total"), i18nc("precipitation total, very little", "Trace")); 1546 } else if (!prevPrecipTotal.isEmpty()) { 1547 data.insert(QStringLiteral("Yesterday Precip Total"), prevPrecipTotal); 1548 const QString &prevPrecipType = weatherData.prevPrecipType; 1549 const KUnitConversion::UnitId unit = (prevPrecipType == QLatin1String("mm") ? KUnitConversion::Millimeter 1550 : prevPrecipType == QLatin1String("cm") ? KUnitConversion::Centimeter 1551 : KUnitConversion::NoUnit); 1552 data.insert(QStringLiteral("Yesterday Precip Unit"), unit); 1553 } 1554 1555 // records 1556 if (!qIsNaN(weatherData.recordHigh)) { 1557 data.insert(QStringLiteral("Record High Temperature"), weatherData.recordHigh); 1558 } 1559 if (!qIsNaN(weatherData.recordLow)) { 1560 data.insert(QStringLiteral("Record Low Temperature"), weatherData.recordLow); 1561 } 1562 if (!qIsNaN(weatherData.recordRain)) { 1563 data.insert(QStringLiteral("Record Rainfall"), weatherData.recordRain); 1564 data.insert(QStringLiteral("Record Rainfall Unit"), KUnitConversion::Millimeter); 1565 } 1566 if (!qIsNaN(weatherData.recordSnow)) { 1567 data.insert(QStringLiteral("Record Snowfall"), weatherData.recordSnow); 1568 data.insert(QStringLiteral("Record Snowfall Unit"), KUnitConversion::Centimeter); 1569 } 1570 1571 data.insert(QStringLiteral("Credit"), i18nc("credit line, keep string short", "Data from Environment and Climate Change\302\240Canada")); 1572 data.insert(QStringLiteral("Credit Url"), weatherData.creditUrl); 1573 setData(source, data); 1574 } 1575 1576 void EnvCanadaIon::dataUpdated(const QString &sourceName, const Plasma5Support::DataEngine::Data &data) 1577 { 1578 const bool isNight = (data.value(QStringLiteral("Corrected Elevation")).toDouble() < 0.0); 1579 1580 for (auto end = m_weatherData.end(), it = m_weatherData.begin(); it != end; ++it) { 1581 auto &weatherData = it.value(); 1582 if (weatherData.solarDataTimeEngineSourceName == sourceName) { 1583 weatherData.isNight = isNight; 1584 updateWeather(it.key()); 1585 } 1586 } 1587 } 1588 1589 K_PLUGIN_CLASS_WITH_JSON(EnvCanadaIon, "ion-envcan.json") 1590 1591 #include "ion_envcan.moc"