ion_envcan.cpp 69.6 KB
Newer Older
1
2
3
4
5
/*
    SPDX-FileCopyrightText: 2007-2011, 2019 Shawn Starr <shawn.starr@rogers.com>

    SPDX-License-Identifier: GPL-2.0-or-later
*/
6
7
8
9
10

/* Ion for Environment Canada XML data */

#include "ion_envcan.h"

11
12
#include "ion_envcandebug.h"

13
#include <KIO/Job>
14
#include <KLocalizedString>
Alexander Lohnau's avatar
Alexander Lohnau committed
15
#include <KUnitConversion/Converter>
16

17
#include <QRegularExpression>
18
#include <QTimeZone>
19

20
WeatherData::WeatherData()
Alexander Lohnau's avatar
Alexander Lohnau committed
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
    : stationLatitude(qQNaN())
    , stationLongitude(qQNaN())
    , temperature(qQNaN())
    , dewpoint(qQNaN())
    , windchill(qQNaN())
    , pressure(qQNaN())
    , visibility(qQNaN())
    , humidity(qQNaN())
    , windSpeed(qQNaN())
    , windGust(qQNaN())
    , normalHigh(qQNaN())
    , normalLow(qQNaN())
    , prevHigh(qQNaN())
    , prevLow(qQNaN())
    , recordHigh(qQNaN())
    , recordLow(qQNaN())
    , recordRain(qQNaN())
    , recordSnow(qQNaN())
39
40
41
42
{
}

WeatherData::ForecastInfo::ForecastInfo()
Alexander Lohnau's avatar
Alexander Lohnau committed
43
44
45
    : tempHigh(qQNaN())
    , tempLow(qQNaN())
    , popPrecent(qQNaN())
46
47
48
{
}

49
50
// ctor, dtor
EnvCanadaIon::EnvCanadaIon(QObject *parent, const QVariantList &args)
Alexander Lohnau's avatar
Alexander Lohnau committed
51
    : IonInterface(parent, args)
52
{
53
54
    // Get the real city XML URL so we can parse this
    getXMLSetup();
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
}

void EnvCanadaIon::deleteForecasts()
{
    QMutableHashIterator<QString, WeatherData> it(m_weatherData);
    while (it.hasNext()) {
        it.next();
        WeatherData &item = it.value();
        qDeleteAll(item.warnings);
        item.warnings.clear();

        qDeleteAll(item.watches);
        item.watches.clear();

        qDeleteAll(item.forecasts);
        item.forecasts.clear();
    }
}

void EnvCanadaIon::reset()
{
    deleteForecasts();
    emitWhenSetup = true;
    m_sourcesToReset = sources();
    getXMLSetup();
}

EnvCanadaIon::~EnvCanadaIon()
{
    // Destroy each watch/warning stored in a QVector
    deleteForecasts();
}

88
QMap<QString, IonInterface::ConditionIcons> EnvCanadaIon::setupConditionIconMappings() const
89
{
Alexander Lohnau's avatar
Alexander Lohnau committed
90
    return QMap<QString, ConditionIcons>{
91

92
        // Explicit periods
Alexander Lohnau's avatar
Alexander Lohnau committed
93
94
95
96
        {QStringLiteral("mainly sunny"), FewCloudsDay},
        {QStringLiteral("mainly clear"), FewCloudsNight},
        {QStringLiteral("sunny"), ClearDay},
        {QStringLiteral("clear"), ClearNight},
97

98
        // Available conditions
Alexander Lohnau's avatar
Alexander Lohnau committed
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
        {QStringLiteral("blowing snow"), Snow},
        {QStringLiteral("cloudy"), Overcast},
        {QStringLiteral("distant precipitation"), LightRain},
        {QStringLiteral("drifting snow"), Flurries},
        {QStringLiteral("drizzle"), LightRain},
        {QStringLiteral("dust"), NotAvailable},
        {QStringLiteral("dust devils"), NotAvailable},
        {QStringLiteral("fog"), Mist},
        {QStringLiteral("fog bank near station"), Mist},
        {QStringLiteral("fog depositing ice"), Mist},
        {QStringLiteral("fog patches"), Mist},
        {QStringLiteral("freezing drizzle"), FreezingDrizzle},
        {QStringLiteral("freezing rain"), FreezingRain},
        {QStringLiteral("funnel cloud"), NotAvailable},
        {QStringLiteral("hail"), Hail},
        {QStringLiteral("haze"), Haze},
        {QStringLiteral("heavy blowing snow"), Snow},
        {QStringLiteral("heavy drifting snow"), Snow},
        {QStringLiteral("heavy drizzle"), LightRain},
        {QStringLiteral("heavy hail"), Hail},
        {QStringLiteral("heavy mixed rain and drizzle"), LightRain},
        {QStringLiteral("heavy mixed rain and snow shower"), RainSnow},
        {QStringLiteral("heavy rain"), Rain},
        {QStringLiteral("heavy rain and snow"), RainSnow},
        {QStringLiteral("heavy rainshower"), Rain},
        {QStringLiteral("heavy snow"), Snow},
        {QStringLiteral("heavy snow pellets"), Snow},
        {QStringLiteral("heavy snowshower"), Snow},
        {QStringLiteral("heavy thunderstorm with hail"), Thunderstorm},
        {QStringLiteral("heavy thunderstorm with rain"), Thunderstorm},
        {QStringLiteral("ice crystals"), Flurries},
        {QStringLiteral("ice pellets"), Hail},
        {QStringLiteral("increasing cloud"), Overcast},
        {QStringLiteral("light drizzle"), LightRain},
        {QStringLiteral("light freezing drizzle"), FreezingRain},
        {QStringLiteral("light freezing rain"), FreezingRain},
        {QStringLiteral("light rain"), LightRain},
        {QStringLiteral("light rainshower"), LightRain},
        {QStringLiteral("light snow"), LightSnow},
        {QStringLiteral("light snow pellets"), LightSnow},
        {QStringLiteral("light snowshower"), Flurries},
        {QStringLiteral("lightning visible"), Thunderstorm},
        {QStringLiteral("mist"), Mist},
        {QStringLiteral("mixed rain and drizzle"), LightRain},
        {QStringLiteral("mixed rain and snow shower"), RainSnow},
        {QStringLiteral("not reported"), NotAvailable},
        {QStringLiteral("rain"), Rain},
        {QStringLiteral("rain and snow"), RainSnow},
        {QStringLiteral("rainshower"), LightRain},
        {QStringLiteral("recent drizzle"), LightRain},
        {QStringLiteral("recent dust or sand storm"), NotAvailable},
        {QStringLiteral("recent fog"), Mist},
        {QStringLiteral("recent freezing precipitation"), FreezingDrizzle},
        {QStringLiteral("recent hail"), Hail},
        {QStringLiteral("recent rain"), Rain},
        {QStringLiteral("recent rain and snow"), RainSnow},
        {QStringLiteral("recent rainshower"), Rain},
        {QStringLiteral("recent snow"), Snow},
        {QStringLiteral("recent snowshower"), Flurries},
        {QStringLiteral("recent thunderstorm"), Thunderstorm},
        {QStringLiteral("recent thunderstorm with hail"), Thunderstorm},
        {QStringLiteral("recent thunderstorm with heavy hail"), Thunderstorm},
        {QStringLiteral("recent thunderstorm with heavy rain"), Thunderstorm},
        {QStringLiteral("recent thunderstorm with rain"), Thunderstorm},
        {QStringLiteral("sand or dust storm"), NotAvailable},
        {QStringLiteral("severe sand or dust storm"), NotAvailable},
        {QStringLiteral("shallow fog"), Mist},
        {QStringLiteral("smoke"), NotAvailable},
        {QStringLiteral("snow"), Snow},
        {QStringLiteral("snow crystals"), Flurries},
        {QStringLiteral("snow grains"), Flurries},
        {QStringLiteral("squalls"), Snow},
        {QStringLiteral("thunderstorm"), Thunderstorm},
        {QStringLiteral("thunderstorm with hail"), Thunderstorm},
        {QStringLiteral("thunderstorm with rain"), Thunderstorm},
        {QStringLiteral("thunderstorm with light rainshowers"), Thunderstorm},
        {QStringLiteral("thunderstorm with heavy rainshowers"), Thunderstorm},
        {QStringLiteral("thunderstorm with sand or dust storm"), Thunderstorm},
        {QStringLiteral("thunderstorm without precipitation"), Thunderstorm},
        {QStringLiteral("tornado"), NotAvailable},
179
    };
180
181
}

182
QMap<QString, IonInterface::ConditionIcons> EnvCanadaIon::setupForecastIconMappings() const
183
{
Alexander Lohnau's avatar
Alexander Lohnau committed
184
    return QMap<QString, ConditionIcons>{
185

186
        // Abbreviated forecast descriptions
Alexander Lohnau's avatar
Alexander Lohnau committed
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
        {QStringLiteral("a few flurries"), Flurries},
        {QStringLiteral("a few flurries mixed with ice pellets"), RainSnow},
        {QStringLiteral("a few flurries or rain showers"), RainSnow},
        {QStringLiteral("a few flurries or thundershowers"), RainSnow},
        {QStringLiteral("a few rain showers or flurries"), RainSnow},
        {QStringLiteral("a few rain showers or wet flurries"), RainSnow},
        {QStringLiteral("a few showers"), LightRain},
        {QStringLiteral("a few showers or drizzle"), LightRain},
        {QStringLiteral("a few showers or thundershowers"), Thunderstorm},
        {QStringLiteral("a few showers or thunderstorms"), Thunderstorm},
        {QStringLiteral("a few thundershowers"), Thunderstorm},
        {QStringLiteral("a few thunderstorms"), Thunderstorm},
        {QStringLiteral("a few wet flurries"), RainSnow},
        {QStringLiteral("a few wet flurries or rain showers"), RainSnow},
        {QStringLiteral("a mix of sun and cloud"), PartlyCloudyDay},
        {QStringLiteral("cloudy with sunny periods"), PartlyCloudyDay},
        {QStringLiteral("partly cloudy"), PartlyCloudyDay},
        {QStringLiteral("mainly cloudy"), PartlyCloudyDay},
        {QStringLiteral("mainly sunny"), FewCloudsDay},
        {QStringLiteral("sunny"), ClearDay},
        {QStringLiteral("blizzard"), Snow},
        {QStringLiteral("clear"), ClearNight},
        {QStringLiteral("cloudy"), Overcast},
        {QStringLiteral("drizzle"), LightRain},
        {QStringLiteral("drizzle mixed with freezing drizzle"), FreezingDrizzle},
        {QStringLiteral("drizzle mixed with rain"), LightRain},
        {QStringLiteral("drizzle or freezing drizzle"), LightRain},
        {QStringLiteral("drizzle or rain"), LightRain},
        {QStringLiteral("flurries"), Flurries},
        {QStringLiteral("flurries at times heavy"), Flurries},
        {QStringLiteral("flurries at times heavy or rain snowers"), RainSnow},
        {QStringLiteral("flurries mixed with ice pellets"), FreezingRain},
        {QStringLiteral("flurries or ice pellets"), FreezingRain},
        {QStringLiteral("flurries or rain showers"), RainSnow},
        {QStringLiteral("flurries or thundershowers"), Flurries},
        {QStringLiteral("fog"), Mist},
        {QStringLiteral("fog developing"), Mist},
        {QStringLiteral("fog dissipating"), Mist},
        {QStringLiteral("fog patches"), Mist},
        {QStringLiteral("freezing drizzle"), FreezingDrizzle},
        {QStringLiteral("freezing rain"), FreezingRain},
        {QStringLiteral("freezing rain mixed with rain"), FreezingRain},
        {QStringLiteral("freezing rain mixed with snow"), FreezingRain},
        {QStringLiteral("freezing rain or ice pellets"), FreezingRain},
        {QStringLiteral("freezing rain or rain"), FreezingRain},
        {QStringLiteral("freezing rain or snow"), FreezingRain},
        {QStringLiteral("ice fog"), Mist},
        {QStringLiteral("ice fog developing"), Mist},
        {QStringLiteral("ice fog dissipating"), Mist},
        {QStringLiteral("ice pellets"), Hail},
        {QStringLiteral("ice pellets mixed with freezing rain"), Hail},
        {QStringLiteral("ice pellets mixed with snow"), Hail},
        {QStringLiteral("ice pellets or snow"), RainSnow},
        {QStringLiteral("light snow"), LightSnow},
        {QStringLiteral("light snow and blizzard"), LightSnow},
        {QStringLiteral("light snow and blizzard and blowing snow"), Snow},
        {QStringLiteral("light snow and blowing snow"), LightSnow},
        {QStringLiteral("light snow mixed with freezing drizzle"), FreezingDrizzle},
        {QStringLiteral("light snow mixed with freezing rain"), FreezingRain},
        {QStringLiteral("light snow or ice pellets"), LightSnow},
        {QStringLiteral("light snow or rain"), RainSnow},
        {QStringLiteral("light wet snow"), RainSnow},
        {QStringLiteral("light wet snow or rain"), RainSnow},
        {QStringLiteral("local snow squalls"), Snow},
        {QStringLiteral("near blizzard"), Snow},
        {QStringLiteral("overcast"), Overcast},
        {QStringLiteral("increasing cloudiness"), Overcast},
        {QStringLiteral("increasing clouds"), Overcast},
        {QStringLiteral("periods of drizzle"), LightRain},
        {QStringLiteral("periods of drizzle mixed with freezing drizzle"), FreezingDrizzle},
        {QStringLiteral("periods of drizzle mixed with rain"), LightRain},
        {QStringLiteral("periods of drizzle or freezing drizzle"), FreezingDrizzle},
        {QStringLiteral("periods of drizzle or rain"), LightRain},
        {QStringLiteral("periods of freezing drizzle"), FreezingDrizzle},
        {QStringLiteral("periods of freezing drizzle or drizzle"), FreezingDrizzle},
        {QStringLiteral("periods of freezing drizzle or rain"), FreezingDrizzle},
        {QStringLiteral("periods of freezing rain"), FreezingRain},
        {QStringLiteral("periods of freezing rain mixed with ice pellets"), FreezingRain},
        {QStringLiteral("periods of freezing rain mixed with rain"), FreezingRain},
        {QStringLiteral("periods of freezing rain mixed with snow"), FreezingRain},
        {QStringLiteral("periods of freezing rain mixed with freezing drizzle"), FreezingRain},
        {QStringLiteral("periods of freezing rain or ice pellets"), FreezingRain},
        {QStringLiteral("periods of freezing rain or rain"), FreezingRain},
        {QStringLiteral("periods of freezing rain or snow"), FreezingRain},
        {QStringLiteral("periods of ice pellets"), Hail},
        {QStringLiteral("periods of ice pellets mixed with freezing rain"), Hail},
        {QStringLiteral("periods of ice pellets mixed with snow"), Hail},
        {QStringLiteral("periods of ice pellets or freezing rain"), Hail},
        {QStringLiteral("periods of ice pellets or snow"), Hail},
        {QStringLiteral("periods of light snow"), LightSnow},
        {QStringLiteral("periods of light snow and blizzard"), Snow},
        {QStringLiteral("periods of light snow and blizzard and blowing snow"), Snow},
        {QStringLiteral("periods of light snow and blowing snow"), LightSnow},
        {QStringLiteral("periods of light snow mixed with freezing drizzle"), RainSnow},
        {QStringLiteral("periods of light snow mixed with freezing rain"), RainSnow},
        {QStringLiteral("periods of light snow mixed with ice pellets"), LightSnow},
        {QStringLiteral("periods of light snow mixed with rain"), RainSnow},
        {QStringLiteral("periods of light snow or freezing drizzle"), RainSnow},
        {QStringLiteral("periods of light snow or freezing rain"), RainSnow},
        {QStringLiteral("periods of light snow or ice pellets"), LightSnow},
        {QStringLiteral("periods of light snow or rain"), RainSnow},
        {QStringLiteral("periods of light wet snow"), LightSnow},
        {QStringLiteral("periods of light wet snow mixed with rain"), RainSnow},
        {QStringLiteral("periods of light wet snow or rain"), RainSnow},
        {QStringLiteral("periods of rain"), Rain},
        {QStringLiteral("periods of rain mixed with freezing rain"), Rain},
        {QStringLiteral("periods of rain mixed with snow"), RainSnow},
        {QStringLiteral("periods of rain or drizzle"), Rain},
        {QStringLiteral("periods of rain or freezing rain"), Rain},
        {QStringLiteral("periods of rain or thundershowers"), Showers},
        {QStringLiteral("periods of rain or thunderstorms"), Thunderstorm},
        {QStringLiteral("periods of rain or snow"), RainSnow},
        {QStringLiteral("periods of snow"), Snow},
        {QStringLiteral("periods of snow and blizzard"), Snow},
        {QStringLiteral("periods of snow and blizzard and blowing snow"), Snow},
        {QStringLiteral("periods of snow and blowing snow"), Snow},
        {QStringLiteral("periods of snow mixed with freezing drizzle"), RainSnow},
        {QStringLiteral("periods of snow mixed with freezing rain"), RainSnow},
        {QStringLiteral("periods of snow mixed with ice pellets"), Snow},
        {QStringLiteral("periods of snow mixed with rain"), RainSnow},
        {QStringLiteral("periods of snow or freezing drizzle"), RainSnow},
        {QStringLiteral("periods of snow or freezing rain"), RainSnow},
        {QStringLiteral("periods of snow or ice pellets"), Snow},
        {QStringLiteral("periods of snow or rain"), RainSnow},
        {QStringLiteral("periods of rain or snow"), RainSnow},
        {QStringLiteral("periods of wet snow"), Snow},
        {QStringLiteral("periods of wet snow mixed with rain"), RainSnow},
        {QStringLiteral("periods of wet snow or rain"), RainSnow},
        {QStringLiteral("rain"), Rain},
        {QStringLiteral("rain at times heavy"), Rain},
        {QStringLiteral("rain at times heavy mixed with freezing rain"), FreezingRain},
        {QStringLiteral("rain at times heavy mixed with snow"), RainSnow},
        {QStringLiteral("rain at times heavy or drizzle"), Rain},
        {QStringLiteral("rain at times heavy or freezing rain"), Rain},
        {QStringLiteral("rain at times heavy or snow"), RainSnow},
        {QStringLiteral("rain at times heavy or thundershowers"), Showers},
        {QStringLiteral("rain at times heavy or thunderstorms"), Thunderstorm},
        {QStringLiteral("rain mixed with freezing rain"), FreezingRain},
        {QStringLiteral("rain mixed with snow"), RainSnow},
        {QStringLiteral("rain or drizzle"), Rain},
        {QStringLiteral("rain or freezing rain"), Rain},
        {QStringLiteral("rain or snow"), RainSnow},
        {QStringLiteral("rain or thundershowers"), Showers},
        {QStringLiteral("rain or thunderstorms"), Thunderstorm},
        {QStringLiteral("rain showers or flurries"), RainSnow},
        {QStringLiteral("rain showers or wet flurries"), RainSnow},
        {QStringLiteral("showers"), Showers},
        {QStringLiteral("showers at times heavy"), Showers},
        {QStringLiteral("showers at times heavy or thundershowers"), Showers},
        {QStringLiteral("showers at times heavy or thunderstorms"), Thunderstorm},
        {QStringLiteral("showers or drizzle"), Showers},
        {QStringLiteral("showers or thundershowers"), Thunderstorm},
        {QStringLiteral("showers or thunderstorms"), Thunderstorm},
        {QStringLiteral("smoke"), NotAvailable},
        {QStringLiteral("snow"), Snow},
        {QStringLiteral("snow and blizzard"), Snow},
        {QStringLiteral("snow and blizzard and blowing snow"), Snow},
        {QStringLiteral("snow and blowing snow"), Snow},
        {QStringLiteral("snow at times heavy"), Snow},
        {QStringLiteral("snow at times heavy and blizzard"), Snow},
        {QStringLiteral("snow at times heavy and blowing snow"), Snow},
        {QStringLiteral("snow at times heavy mixed with freezing drizzle"), RainSnow},
        {QStringLiteral("snow at times heavy mixed with freezing rain"), RainSnow},
        {QStringLiteral("snow at times heavy mixed with ice pellets"), Snow},
        {QStringLiteral("snow at times heavy mixed with rain"), RainSnow},
        {QStringLiteral("snow at times heavy or freezing rain"), RainSnow},
        {QStringLiteral("snow at times heavy or ice pellets"), Snow},
        {QStringLiteral("snow at times heavy or rain"), RainSnow},
        {QStringLiteral("snow mixed with freezing drizzle"), RainSnow},
        {QStringLiteral("snow mixed with freezing rain"), RainSnow},
        {QStringLiteral("snow mixed with ice pellets"), Snow},
        {QStringLiteral("snow mixed with rain"), RainSnow},
        {QStringLiteral("snow or freezing drizzle"), RainSnow},
        {QStringLiteral("snow or freezing rain"), RainSnow},
        {QStringLiteral("snow or ice pellets"), Snow},
        {QStringLiteral("snow or rain"), RainSnow},
        {QStringLiteral("snow squalls"), Snow},
        {QStringLiteral("sunny"), ClearDay},
        {QStringLiteral("sunny with cloudy periods"), PartlyCloudyDay},
        {QStringLiteral("thunderstorms"), Thunderstorm},
        {QStringLiteral("thunderstorms and possible hail"), Thunderstorm},
        {QStringLiteral("wet flurries"), Flurries},
        {QStringLiteral("wet flurries at times heavy"), Flurries},
        {QStringLiteral("wet flurries at times heavy or rain snowers"), RainSnow},
        {QStringLiteral("wet flurries or rain showers"), RainSnow},
        {QStringLiteral("wet snow"), Snow},
        {QStringLiteral("wet snow at times heavy"), Snow},
        {QStringLiteral("wet snow at times heavy mixed with rain"), RainSnow},
        {QStringLiteral("wet snow mixed with rain"), RainSnow},
        {QStringLiteral("wet snow or rain"), RainSnow},
        {QStringLiteral("windy"), NotAvailable},

        {QStringLiteral("chance of drizzle mixed with freezing drizzle"), LightRain},
        {QStringLiteral("chance of flurries mixed with ice pellets"), Flurries},
        {QStringLiteral("chance of flurries or ice pellets"), Flurries},
        {QStringLiteral("chance of flurries or rain showers"), RainSnow},
        {QStringLiteral("chance of flurries or thundershowers"), RainSnow},
        {QStringLiteral("chance of freezing drizzle"), FreezingDrizzle},
        {QStringLiteral("chance of freezing rain"), FreezingRain},
        {QStringLiteral("chance of freezing rain mixed with snow"), RainSnow},
        {QStringLiteral("chance of freezing rain or rain"), FreezingRain},
        {QStringLiteral("chance of freezing rain or snow"), RainSnow},
        {QStringLiteral("chance of light snow and blowing snow"), LightSnow},
        {QStringLiteral("chance of light snow mixed with freezing drizzle"), LightSnow},
        {QStringLiteral("chance of light snow mixed with ice pellets"), LightSnow},
        {QStringLiteral("chance of light snow mixed with rain"), RainSnow},
        {QStringLiteral("chance of light snow or freezing rain"), RainSnow},
        {QStringLiteral("chance of light snow or ice pellets"), LightSnow},
        {QStringLiteral("chance of light snow or rain"), RainSnow},
        {QStringLiteral("chance of light wet snow"), Snow},
        {QStringLiteral("chance of rain"), Rain},
        {QStringLiteral("chance of rain at times heavy"), Rain},
        {QStringLiteral("chance of rain mixed with snow"), RainSnow},
        {QStringLiteral("chance of rain or drizzle"), Rain},
        {QStringLiteral("chance of rain or freezing rain"), Rain},
        {QStringLiteral("chance of rain or snow"), RainSnow},
        {QStringLiteral("chance of rain showers or flurries"), RainSnow},
        {QStringLiteral("chance of rain showers or wet flurries"), RainSnow},
        {QStringLiteral("chance of severe thunderstorms"), Thunderstorm},
        {QStringLiteral("chance of showers at times heavy"), Rain},
        {QStringLiteral("chance of showers at times heavy or thundershowers"), Thunderstorm},
        {QStringLiteral("chance of showers at times heavy or thunderstorms"), Thunderstorm},
        {QStringLiteral("chance of showers or thundershowers"), Thunderstorm},
        {QStringLiteral("chance of showers or thunderstorms"), Thunderstorm},
        {QStringLiteral("chance of snow"), Snow},
        {QStringLiteral("chance of snow and blizzard"), Snow},
        {QStringLiteral("chance of snow mixed with freezing drizzle"), Snow},
        {QStringLiteral("chance of snow mixed with freezing rain"), RainSnow},
        {QStringLiteral("chance of snow mixed with rain"), RainSnow},
        {QStringLiteral("chance of snow or rain"), RainSnow},
        {QStringLiteral("chance of snow squalls"), Snow},
        {QStringLiteral("chance of thundershowers"), Showers},
        {QStringLiteral("chance of thunderstorms"), Thunderstorm},
        {QStringLiteral("chance of thunderstorms and possible hail"), Thunderstorm},
        {QStringLiteral("chance of wet flurries"), Flurries},
        {QStringLiteral("chance of wet flurries at times heavy"), Flurries},
        {QStringLiteral("chance of wet flurries or rain showers"), RainSnow},
        {QStringLiteral("chance of wet snow"), Snow},
        {QStringLiteral("chance of wet snow mixed with rain"), RainSnow},
        {QStringLiteral("chance of wet snow or rain"), RainSnow},
427
    };
428
429
}

Alexander Lohnau's avatar
Alexander Lohnau committed
430
QMap<QString, IonInterface::ConditionIcons> const &EnvCanadaIon::conditionIcons() const
431
432
433
434
435
{
    static QMap<QString, ConditionIcons> const condval = setupConditionIconMappings();
    return condval;
}

Alexander Lohnau's avatar
Alexander Lohnau committed
436
QMap<QString, IonInterface::ConditionIcons> const &EnvCanadaIon::forecastIcons() const
437
438
439
440
441
{
    static QMap<QString, ConditionIcons> const foreval = setupForecastIconMappings();
    return foreval;
}

Alexander Lohnau's avatar
Alexander Lohnau committed
442
QStringList EnvCanadaIon::validate(const QString &source) const
443
444
{
    QStringList placeList;
445

446
447
448
449
    QString sourceNormalized = source.toUpper();
    QHash<QString, EnvCanadaIon::XMLMapInfo>::const_iterator it = m_places.constBegin();
    while (it != m_places.constEnd()) {
        if (it.key().toUpper().contains(sourceNormalized)) {
450
            placeList.append(QStringLiteral("place|") + it.key());
451
452
453
454
455
456
457
458
459
        }
        ++it;
    }

    placeList.sort();
    return placeList;
}

// Get a specific Ion's data
Alexander Lohnau's avatar
Alexander Lohnau committed
460
bool EnvCanadaIon::updateIonSource(const QString &source)
461
{
Alexander Lohnau's avatar
Alexander Lohnau committed
462
    // qCDebug(IONENGINE_ENVCAN) << "updateIonSource()" << source;
463
464
465
466
467

    // We expect the applet to send the source in the following tokenization:
    // ionname|validate|place_name - Triggers validation of place
    // ionname|weather|place_name - Triggers receiving weather of place

468
    const QStringList sourceAction = source.split(QLatin1Char('|'));
469
470
471

    // Guard: if the size of array is not 2 then we have bad data, return an error
    if (sourceAction.size() < 2) {
472
        setData(source, QStringLiteral("validate"), QStringLiteral("envcan|malformed"));
473
474
475
        return true;
    }

476
477
    if (sourceAction[1] == QLatin1String("validate") && sourceAction.size() > 2) {
        const QStringList result = validate(sourceAction[2]);
478

479
480
481
        const QString reply = (result.size() == 1        ? QStringLiteral("envcan|valid|single|") + result[0]
                                   : (result.size() > 1) ? QStringLiteral("envcan|valid|multiple|") + result.join(QLatin1Char('|'))
                                                         : QStringLiteral("envcan|invalid|single|") + sourceAction[2]);
482
483
484
485
486
        setData(source, QStringLiteral("validate"), reply);

        return true;
    }
    if (sourceAction[1] == QLatin1String("weather") && sourceAction.size() > 2) {
487
488
489
        getXMLData(source);
        return true;
    }
490
    setData(source, QStringLiteral("validate"), QStringLiteral("envcan|malformed"));
491
    return true;
492
493
494
495
496
}

// Parses city list and gets the correct city based on ID number
void EnvCanadaIon::getXMLSetup()
{
Alexander Lohnau's avatar
Alexander Lohnau committed
497
    // qCDebug(IONENGINE_ENVCAN) << "getXMLSetup()";
498
499
500

    // If network is down, we need to spin and wait

501
    const QUrl url(QStringLiteral("http://dd.weather.gc.ca/citypage_weather/xml/siteList.xml"));
502

Alexander Lohnau's avatar
Alexander Lohnau committed
503
    KIO::TransferJob *getJob = KIO::get(url, KIO::NoReload, KIO::HideProgressInfo);
504
505

    m_xmlSetup.clear();
Alexander Lohnau's avatar
Alexander Lohnau committed
506
507
    connect(getJob, &KIO::TransferJob::data, this, &EnvCanadaIon::setup_slotDataArrived);
    connect(getJob, &KJob::result, this, &EnvCanadaIon::setup_slotJobFinished);
508
509
510
}

// Gets specific city XML data
Alexander Lohnau's avatar
Alexander Lohnau committed
511
void EnvCanadaIon::getXMLData(const QString &source)
512
{
Alexander Lohnau's avatar
Alexander Lohnau committed
513
    for (const QString &fetching : qAsConst(m_jobList)) {
514
515
516
517
518
519
        if (fetching == source) {
            // already getting this source and awaiting the data
            return;
        }
    }

Alexander Lohnau's avatar
Alexander Lohnau committed
520
    // qCDebug(IONENGINE_ENVCAN) << source;
521
522
523

    // Demunge source name for key only.
    QString dataKey = source;
524
    dataKey.remove(QStringLiteral("envcan|weather|"));
Alexander Lohnau's avatar
Alexander Lohnau committed
525
    const XMLMapInfo &place = m_places[dataKey];
526

Alexander Lohnau's avatar
Alexander Lohnau committed
527
528
529
530
    const QUrl url(QLatin1String("http://dd.weather.gc.ca/citypage_weather/xml/") + place.territoryName + QLatin1Char('/') + place.cityCode
                   + QStringLiteral("_e.xml"));
    // url="file:///home/spstarr/Desktop/s0000649_e.xml";
    // qCDebug(IONENGINE_ENVCAN) << "Will Try URL: " << url;
531

532
    if (place.territoryName.isEmpty() && place.cityCode.isEmpty()) {
533
        setData(source, QStringLiteral("validate"), QStringLiteral("envcan|malformed"));
534
535
536
        return;
    }

Alexander Lohnau's avatar
Alexander Lohnau committed
537
    KIO::TransferJob *getJob = KIO::get(url, KIO::Reload, KIO::HideProgressInfo);
538

539
540
    m_jobXml.insert(getJob, new QXmlStreamReader);
    m_jobList.insert(getJob, source);
541

Alexander Lohnau's avatar
Alexander Lohnau committed
542
543
    connect(getJob, &KIO::TransferJob::data, this, &EnvCanadaIon::slotDataArrived);
    connect(getJob, &KJob::result, this, &EnvCanadaIon::slotJobFinished);
544
545
546
547
548
549
550
}

void EnvCanadaIon::setup_slotDataArrived(KIO::Job *job, const QByteArray &data)
{
    Q_UNUSED(job)

    if (data.isEmpty()) {
Alexander Lohnau's avatar
Alexander Lohnau committed
551
        // qCDebug(IONENGINE_ENVCAN) << "done!";
552
553
554
555
        return;
    }

    // Send to xml.
Alexander Lohnau's avatar
Alexander Lohnau committed
556
    // qCDebug(IONENGINE_ENVCAN) << data;
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
    m_xmlSetup.addData(data);
}

void EnvCanadaIon::slotDataArrived(KIO::Job *job, const QByteArray &data)
{
    if (data.isEmpty() || !m_jobXml.contains(job)) {
        return;
    }

    // Send to xml.
    m_jobXml[job]->addData(data);
}

void EnvCanadaIon::slotJobFinished(KJob *job)
{
    // Dual use method, if we're fetching location data to parse we need to do this first
    const QString source = m_jobList.value(job);
Alexander Lohnau's avatar
Alexander Lohnau committed
574
    // qCDebug(IONENGINE_ENVCAN) << source << m_sourcesToReset.contains(source);
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
    setData(source, Data());
    QXmlStreamReader *reader = m_jobXml.value(job);
    if (reader) {
        readXMLData(m_jobList[job], *reader);
    }

    m_jobList.remove(job);
    delete m_jobXml[job];
    m_jobXml.remove(job);

    if (m_sourcesToReset.contains(source)) {
        m_sourcesToReset.removeAll(source);

        // so the weather engine updates it's data
        forceImmediateUpdateOfAllVisualizations();

        // update the clients of our engine
Laurent Montel's avatar
Laurent Montel committed
592
        Q_EMIT forceUpdate(this, source);
593
594
595
596
597
598
599
600
    }
}

void EnvCanadaIon::setup_slotJobFinished(KJob *job)
{
    Q_UNUSED(job)
    const bool success = readXMLSetup();
    m_xmlSetup.clear();
Alexander Lohnau's avatar
Alexander Lohnau committed
601
    // qCDebug(IONENGINE_ENVCAN) << success << m_sourcesToReset;
602
603
604
605
606
607
608
609
610
611
612
    setInitialized(success);
}

// Parse the city list and store into a QMap
bool EnvCanadaIon::readXMLSetup()
{
    bool success = false;
    QString territory;
    QString code;
    QString cityName;

Alexander Lohnau's avatar
Alexander Lohnau committed
613
    // qCDebug(IONENGINE_ENVCAN) << "readXMLSetup()";
614
615
616
617

    while (!m_xmlSetup.atEnd()) {
        m_xmlSetup.readNext();

618
        const auto elementName = m_xmlSetup.name();
619

620
621
        if (m_xmlSetup.isStartElement()) {
            // XML ID code to match filename
622
            if (elementName == QLatin1String("site")) {
623
                code = m_xmlSetup.attributes().value(QStringLiteral("code")).toString();
624
625
            }

626
            if (elementName == QLatin1String("nameEn")) {
627
628
629
                cityName = m_xmlSetup.readElementText(); // Name of cities
            }

630
            if (elementName == QLatin1String("provinceCode")) {
631
632
633
634
                territory = m_xmlSetup.readElementText(); // Provinces/Territory list
            }
        }

635
        if (m_xmlSetup.isEndElement() && elementName == QLatin1String("site")) {
636
            EnvCanadaIon::XMLMapInfo info;
637
            QString tmp = cityName + QStringLiteral(", ") + territory; // Build the key name.
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652

            // Set the mappings
            info.cityCode = code;
            info.territoryName = territory;
            info.cityName = cityName;

            // Set the string list, we will use for the applet to display the available cities.
            m_places[tmp] = info;
            success = true;
        }
    }

    return (success && !m_xmlSetup.error());
}

Alexander Lohnau's avatar
Alexander Lohnau committed
653
void EnvCanadaIon::parseWeatherSite(WeatherData &data, QXmlStreamReader &xml)
654
655
656
657
{
    while (!xml.atEnd()) {
        xml.readNext();

658
        const auto elementName = xml.name();
659

660
        if (xml.isStartElement()) {
661
            if (elementName == QLatin1String("license")) {
662
                data.creditUrl = xml.readElementText();
663
            } else if (elementName == QLatin1String("location")) {
664
                parseLocations(data, xml);
665
            } else if (elementName == QLatin1String("warnings")) {
666
667
668
669
                // Cleanup warning list on update
                data.warnings.clear();
                data.watches.clear();
                parseWarnings(data, xml);
670
            } else if (elementName == QLatin1String("currentConditions")) {
671
                parseConditions(data, xml);
672
            } else if (elementName == QLatin1String("forecastGroup")) {
673
674
675
                // Clean up forecast list on update
                data.forecasts.clear();
                parseWeatherForecast(data, xml);
676
            } else if (elementName == QLatin1String("yesterdayConditions")) {
677
                parseYesterdayWeather(data, xml);
678
            } else if (elementName == QLatin1String("riseSet")) {
679
                parseAstronomicals(data, xml);
680
            } else if (elementName == QLatin1String("almanac")) {
681
682
683
684
685
686
687
688
                parseWeatherRecords(data, xml);
            } else {
                parseUnknownElement(xml);
            }
        }
    }
}

689
// Parse Weather data main loop, from here we have to descend into each tag pair
Alexander Lohnau's avatar
Alexander Lohnau committed
690
bool EnvCanadaIon::readXMLData(const QString &source, QXmlStreamReader &xml)
691
692
693
{
    WeatherData data;

Alexander Lohnau's avatar
Alexander Lohnau committed
694
    // qCDebug(IONENGINE_ENVCAN) << "readXMLData()";
695
696

    QString dataKey = source;
697
    dataKey.remove(QStringLiteral("envcan|weather|"));
698
699
700
701
702
703
704
705
706
    data.shortTerritoryName = m_places[dataKey].territoryName;
    while (!xml.atEnd()) {
        xml.readNext();

        if (xml.isEndElement()) {
            break;
        }

        if (xml.isStartElement()) {
707
            if (xml.name() == QLatin1String("siteData")) {
708
709
710
711
712
713
714
                parseWeatherSite(data, xml);
            } else {
                parseUnknownElement(xml);
            }
        }
    }

715
    bool solarDataSourceNeedsConnect = false;
Alexander Lohnau's avatar
Alexander Lohnau committed
716
    Plasma::DataEngine *timeEngine = dataEngine(QStringLiteral("time"));
717
    if (timeEngine) {
Alexander Lohnau's avatar
Alexander Lohnau committed
718
        const bool canCalculateElevation = (data.observationDateTime.isValid() && (!qIsNaN(data.stationLatitude) && !qIsNaN(data.stationLongitude)));
719
        if (canCalculateElevation) {
720
            data.solarDataTimeEngineSourceName = QStringLiteral("%1|Solar|Latitude=%2|Longitude=%3|DateTime=%4")
Alexander Lohnau's avatar
Alexander Lohnau committed
721
722
723
724
                                                     .arg(QString::fromUtf8(data.observationDateTime.timeZone().id()))
                                                     .arg(data.stationLatitude)
                                                     .arg(data.stationLongitude)
                                                     .arg(data.observationDateTime.toString(Qt::ISODate));
725
726
727
728
729
730
            solarDataSourceNeedsConnect = true;
        }

        // check any previous data
        const auto it = m_weatherData.constFind(source);
        if (it != m_weatherData.constEnd()) {
Alexander Lohnau's avatar
Alexander Lohnau committed
731
            const QString &oldSolarDataTimeEngineSource = it.value().solarDataTimeEngineSourceName;
732
733
734
735
736
737
738
739
740
741
742
743

            if (oldSolarDataTimeEngineSource == data.solarDataTimeEngineSourceName) {
                // can reuse elevation source (if any), copy over data
                data.isNight = it.value().isNight;
                solarDataSourceNeedsConnect = false;
            } else if (!oldSolarDataTimeEngineSource.isEmpty()) {
                // drop old elevation source
                timeEngine->disconnectSource(oldSolarDataTimeEngineSource, this);
            }
        }
    }

744
    m_weatherData[source] = data;
745
746
747
748
749
750
751
752

    // connect only after m_weatherData has the data, so the instant data push handling can see it
    if (solarDataSourceNeedsConnect) {
        timeEngine->connectSource(data.solarDataTimeEngineSourceName, this);
    } else {
        updateWeather(source);
    }

753
754
755
    return !xml.error();
}

Alexander Lohnau's avatar
Alexander Lohnau committed
756
void EnvCanadaIon::parseFloat(float &value, QXmlStreamReader &xml)
757
758
759
760
761
762
763
764
{
    bool ok = false;
    const float result = xml.readElementText().toFloat(&ok);
    if (ok) {
        value = result;
    }
}

Alexander Lohnau's avatar
Alexander Lohnau committed
765
void EnvCanadaIon::parseDateTime(WeatherData &data, QXmlStreamReader &xml, WeatherData::WeatherEvent *event)
766
{
767
    Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("dateTime"));
768
769

    // What kind of date info is this?
770
771
    const QString dateType = xml.attributes().value(QStringLiteral("name")).toString();
    const QString dateZone = xml.attributes().value(QStringLiteral("zone")).toString();
772
    const QString dateUtcOffset = xml.attributes().value(QStringLiteral("UTCOffset")).toString();
773
774
775
776
777
778
779
780
781
782

    QString selectTimeStamp;

    while (!xml.atEnd()) {
        xml.readNext();

        if (xml.isEndElement()) {
            break;
        }

783
        const auto elementName = xml.name();
784

785
        if (xml.isStartElement()) {
786
            if (dateType == QLatin1String("xmlCreation")) {
787
788
                return;
            }
789
            if (dateZone == QLatin1String("UTC")) {
790
791
                return;
            }
792
            if (elementName == QLatin1String("year")) {
793
                xml.readElementText();
794
            } else if (elementName == QLatin1String("month")) {
795
                xml.readElementText();
796
            } else if (elementName == QLatin1String("day")) {
797
                xml.readElementText();
798
            } else if (elementName == QLatin1String("hour"))
799
                xml.readElementText();
800
            else if (elementName == QLatin1String("minute"))
801
                xml.readElementText();
802
            else if (elementName == QLatin1String("timeStamp"))
803
                selectTimeStamp = xml.readElementText();
804
            else if (elementName == QLatin1String("textSummary")) {
805
                if (dateType == QLatin1String("eventIssue")) {
806
807
808
                    if (event) {
                        event->timestamp = xml.readElementText();
                    }
809
                } else if (dateType == QLatin1String("observation")) {
810
                    xml.readElementText();
811
                    QDateTime observationDateTime = QDateTime::fromString(selectTimeStamp, QStringLiteral("yyyyMMddHHmmss"));
812
813
814
815
816
                    QTimeZone timeZone = QTimeZone(dateZone.toUtf8());
                    // if timezone id not recognized, fallback to utcoffset
                    if (!timeZone.isValid()) {
                        timeZone = QTimeZone(dateUtcOffset.toInt() * 3600);
                    }
817
818
819
                    if (observationDateTime.isValid() && timeZone.isValid()) {
                        data.observationDateTime = observationDateTime;
                        data.observationDateTime.setTimeZone(timeZone);
820
                    }
821
                    data.obsTimestamp = observationDateTime.toString(QStringLiteral("dd.MM.yyyy @ hh:mm"));
822
                } else if (dateType == QLatin1String("forecastIssue")) {
823
                    data.forecastTimestamp = xml.readElementText();
824
                } else if (dateType == QLatin1String("sunrise")) {
825
                    data.sunriseTimestamp = xml.readElementText();
826
                } else if (dateType == QLatin1String("sunset")) {
827
                    data.sunsetTimestamp = xml.readElementText();
828
                } else if (dateType == QLatin1String("moonrise")) {
829
                    data.moonriseTimestamp = xml.readElementText();
830
                } else if (dateType == QLatin1String("moonset")) {
831
832
833
834
835
836
837
                    data.moonsetTimestamp = xml.readElementText();
                }
            }
        }
    }
}

Alexander Lohnau's avatar
Alexander Lohnau committed
838
void EnvCanadaIon::parseLocations(WeatherData &data, QXmlStreamReader &xml)
839
{
840
    Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("location"));
841
842
843
844
845
846
847
848

    while (!xml.atEnd()) {
        xml.readNext();

        if (xml.isEndElement()) {
            break;
        }

849
        const auto elementName = xml.name();
850

851
        if (xml.isStartElement()) {
852
            if (elementName == QLatin1String("country")) {
853
                data.countryName = xml.readElementText();
854
            } else if (elementName == QLatin1String("province") || elementName == QLatin1String("territory")) {
855
                data.longTerritoryName = xml.readElementText();
856
            } else if (elementName == QLatin1String("name")) {
857
                data.cityName = xml.readElementText();
858
            } else if (elementName == QLatin1String("region")) {
859
860
861
862
863
864
865
866
                data.regionName = xml.readElementText();
            } else {
                parseUnknownElement(xml);
            }
        }
    }
}

Alexander Lohnau's avatar
Alexander Lohnau committed
867
void EnvCanadaIon::parseWindInfo(WeatherData &data, QXmlStreamReader &xml)
868
{
869
    Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("wind"));
870
871
872
873
874
875
876
877

    while (!xml.atEnd()) {
        xml.readNext();

        if (xml.isEndElement()) {
            break;
        }

878
        const auto elementName = xml.name();
879

880
        if (xml.isStartElement()) {
881
            if (elementName == QLatin1String("speed")) {
882
                parseFloat(data.windSpeed, xml);
883
            } else if (elementName == QLatin1String("gust")) {
884
                parseFloat(data.windGust, xml);
885
            } else if (elementName == QLatin1String("direction")) {
886
                data.windDirection = xml.readElementText();
887
            } else if (elementName == QLatin1String("bearing")) {
888
                data.windDegrees = xml.attributes().value(QStringLiteral("degrees")).toString();
889
890
891
892
893
894
895
            } else {
                parseUnknownElement(xml);
            }
        }
    }
}

Alexander Lohnau's avatar
Alexander Lohnau committed
896
void EnvCanadaIon::parseConditions(WeatherData &data, QXmlStreamReader &xml)
897
{
898
    Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("currentConditions"));
899
900
    data.temperature = qQNaN();
    data.dewpoint = qQNaN();
901
    data.condition = i18n("N/A");
902
    data.humidex.clear();
903
    data.stationID = i18n("N/A");
904
905
    data.stationLatitude = qQNaN();
    data.stationLongitude = qQNaN();
906
907
908
    data.pressure = qQNaN();
    data.visibility = qQNaN();
    data.humidity = qQNaN();
909
910
911
912

    while (!xml.atEnd()) {
        xml.readNext();

913
        const auto elementName = xml.name();
914
915

        if (xml.isEndElement() && elementName == QLatin1String("currentConditions"))
916
917
918
            break;

        if (xml.isStartElement()) {
919
            if (elementName == QLatin1String("station")) {
920
                data.stationID = xml.attributes().value(QStringLiteral("code")).toString();
921
                QRegularExpression dumpDirection(QStringLiteral("[^0-9.]"));
922
923
                data.stationLatitude = xml.attributes().value(QStringLiteral("lat")).toString().remove(dumpDirection).toDouble();
                data.stationLongitude = xml.attributes().value(QStringLiteral("lon")).toString().remove(dumpDirection).toDouble();
924
            } else if (elementName == QLatin1String("dateTime")) {
925
                parseDateTime(data, xml);
926
            } else if (elementName == QLatin1String("condition")) {
927
                data.condition = xml.readElementText().trimmed();
928
            } else if (elementName == QLatin1String("temperature")) {
929
930
                // prevent N/A text to result in 0.0 value
                parseFloat(data.temperature, xml);
931
            } else if (elementName == QLatin1String("dewpoint")) {
932
933
934
935
936
937
938
                // prevent N/A text to result in 0.0 value
                parseFloat(data.dewpoint, xml);
            } else if (elementName == QLatin1String("humidex")) {
                data.humidex = xml.readElementText();
            } else if (elementName == QLatin1String("windChill")) {
                // prevent N/A text to result in 0.0 value
                parseFloat(data.windchill, xml);
939
            } else if (elementName == QLatin1String("pressure")) {
940
                data.pressureTendency = xml.attributes().value(QStringLiteral("tendency")).toString();
941
                if (data.pressureTendency.isEmpty()) {
942
                    data.pressureTendency = QStringLiteral("steady");
943
                }
944
                parseFloat(data.pressure, xml);
945
            } else if (elementName == QLatin1String("visibility")) {
946
                parseFloat(data.visibility, xml);
947
            } else if (elementName == QLatin1String("relativeHumidity")) {
948
                parseFloat(data.humidity, xml);
949
            } else if (elementName == QLatin1String("wind")) {
950
951
952
953
954
955
956
957
958
                parseWindInfo(data, xml);
            }
            //} else {
            //    parseUnknownElement(xml);
            //}
        }
    }
}

Alexander Lohnau's avatar
Alexander Lohnau committed
959
void EnvCanadaIon::parseWarnings(WeatherData &data, QXmlStreamReader &xml)
960
961
962
963
{
    WeatherData::WeatherEvent *watch = new WeatherData::WeatherEvent;
    WeatherData::WeatherEvent *warning = new WeatherData::WeatherEvent;

964
    Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("warnings"));
965
    QString eventURL = xml.attributes().value(QStringLiteral("url")).toString();
966
967
968
969
970
    int flag = 0;

    while (!xml.atEnd()) {
        xml.readNext();

971
        const auto elementName = xml.name();
972
973

        if (xml.isEndElement() && elementName == QLatin1String("warnings")) {
974
975
976
977
            break;
        }

        if (xml.isStartElement()) {
978
            if (elementName == QLatin1String("dateTime")) {
979
980
981
982
983
984
985
                if (flag == 1) {
                    parseDateTime(data, xml, watch);
                }
                if (flag == 2) {
                    parseDateTime(data, xml, warning);
                }

Alexander Lohnau's avatar
Alexander Lohnau committed
986
                if (!warning->timestamp.isEmpty() && !warning->url.isEmpty()) {
987
988
989
990
991
992
993
994
                    data.warnings.append(warning);
                    warning = new WeatherData::WeatherEvent;
                }
                if (!watch->timestamp.isEmpty() && !watch->url.isEmpty()) {
                    data.watches.append(watch);
                    watch = new WeatherData::WeatherEvent;
                }

995
            } else if (elementName == QLatin1String("event")) {
996
                // Append new event to list.
997
998
                QString eventType = xml.attributes().value(QStringLiteral("type")).toString();
                if (eventType == QLatin1String("watch")) {
999
1000
                    watch->url = eventURL;
                    watch->type = eventType;
For faster browsing, not all history is shown. View entire blame