Projekt

Obecné

Profil

Stáhnout (17.1 KB) Statistiky
| Větev: | Tag: | Revize:
1 cdf3c217 A-Konig
//
2
// Author: A. Konig
3
//
4
5
using ServerApp.DataDownload;
6 1407c8ba A-Konig
using ServerApp.Parser.OutputInfo;
7 c9eed50c A-Konig
using System;
8
using System.Collections.Generic;
9 1407c8ba A-Konig
using System.Globalization;
10 c9eed50c A-Konig
using System.IO;
11
using System.Net;
12
using System.Text.Json;
13 1407c8ba A-Konig
using static System.Text.Json.JsonElement;
14 c9eed50c A-Konig
15 cdf3c217 A-Konig
namespace ServerApp.WeatherPredictionParser
16 c9eed50c A-Konig
{
17 1407c8ba A-Konig
    /// <summary>
18
    /// Class representing a parser for json prediction data
19
    /// </summary>
20
    /// <author>A. Konig</author>
21 4b847de5 A-Konig
    public class JsonParser : IJsonParser
22 c9eed50c A-Konig
    {
23 1407c8ba A-Konig
        /// <summary> Data loader </summary>
24 c9eed50c A-Konig
        DataDownloader loader;
25 1407c8ba A-Konig
        /// <summary> Currently parsed day </summary>
26
        DateTime currParsedDay;
27
        /// <summary> Sunrise time of currently parsed day </summary>
28
        DateTime sunriseTime;
29
        /// <summary> Sunset time of currently parsed day </summary>
30
        DateTime sunsetTime;
31 c9eed50c A-Konig
32 1407c8ba A-Konig
        /// <summary>
33
        /// Constructor
34
        /// </summary>
35
        /// <param name="loader"></param>
36 c9eed50c A-Konig
        public JsonParser(DataDownloader loader)
37
        {
38
            this.loader = loader;
39
        }
40
41 346be8db A-Konig
        /// <summary>
42
        /// Get predictions from Predictions that are within specified time span
43
        /// From-to including
44
        /// If from == DateTime.Min then all until to
45
        /// If to  == DateTime.Max then all starting from from
46
        /// </summary>
47
        /// <param name="from">DateTime from</param>
48
        /// <param name="to">DateTime to</param>
49
        /// <returns>List of predictions that fit specified criteria or null if incorrect input</returns>
50
        public override List<WeatherInfo> GetPredictionForTime(DateTime from, DateTime to)
51
        {
52
            if (Predictions == null)
53
                return null;
54
55
            List<WeatherInfo> res = new List<WeatherInfo>();
56
57
            if (from == DateTime.MinValue)
58
                from = Predictions[0].startTime;
59
60
            if (to == DateTime.MaxValue) {
61
                DateTime dt = Predictions[Predictions.Count - 1].startTime;
62
                int hour = dt.Hour + Predictions[Predictions.Count - 1].intervalLength;
63
                bool addDay = false;
64
                if (hour >= 24)
65
                {
66
                    hour -= 24;
67
                    addDay = true;
68
                }
69
                to = new DateTime(dt.Year, dt.Month, dt.Day, hour, dt.Minute, dt.Second);
70
                if (addDay)
71
                    to = to.AddDays(1);
72
            }
73
74
            if (from > to)
75
                return null;
76
77
            // for all parsed weather info
78
            foreach (WeatherInfo pred in Predictions)
79
            {
80
                int hour = pred.startTime.Hour + pred.intervalLength;
81
                bool addDay = false;
82
                if (hour >= 24)
83
                {
84
                    hour -= 24;
85
                    addDay = true;
86
                }
87
                DateTime endTime = new DateTime(pred.startTime.Year, pred.startTime.Month, pred.startTime.Day, hour, pred.startTime.Minute, pred.startTime.Second);
88
                if (addDay)
89
                    endTime = endTime.AddDays(1);
90
91
                // if both end and start not outside of interval
92
                if (!((pred.startTime < from && endTime <= from) || (pred.startTime > to && endTime > to)))
93
                    res.Add(pred);
94
            }
95
96
            return res;
97
        }
98
99
100 1407c8ba A-Konig
        /// <summary>
101
        /// Parse weather prediction
102
        /// Results is in attributes current for current weather and pred for weather prediction for today, tommorrow and day after tommorrow
103
        /// </summary>
104 cdf3c217 A-Konig
        override public void ParsePrediction()
105 c9eed50c A-Konig
        {
106
            // TODO ask DataDownloader for download said file and return path to it
107 1407c8ba A-Konig
            
108 c9eed50c A-Konig
            // get file
109 4b847de5 A-Konig
            string file = loader.DownloadWeatherPrediction();
110 c9eed50c A-Konig
            DateTime now = DateTime.Now;
111
            Console.WriteLine(File.Exists(file));
112
113 4b847de5 A-Konig
            Current = new WeatherInfo();
114
            Predictions = new List<WeatherInfo>();
115 cdf3c217 A-Konig
116
            if (!File.Exists(file))
117
                return;
118
119
            // read file
120 1407c8ba A-Konig
            string data = File.ReadAllText(file);
121 c9eed50c A-Konig
122 cdf3c217 A-Konig
            // parse
123 c9eed50c A-Konig
            JsonDocument doc = JsonDocument.Parse(data);
124
            JsonElement root = doc.RootElement;
125 1407c8ba A-Konig
            var weatherP = root.EnumerateObject();
126 c9eed50c A-Konig
127 1407c8ba A-Konig
            while (weatherP.MoveNext())
128 c9eed50c A-Konig
            {
129 1407c8ba A-Konig
                string name = weatherP.Current.Name;
130
                Console.WriteLine(name);
131 c9eed50c A-Konig
132 1407c8ba A-Konig
                switch (name)
133 c9eed50c A-Konig
                {
134 1407c8ba A-Konig
                    // current weather
135
                    case "current_condition":
136
                        {
137
                            ArrayEnumerator currentWeather = weatherP.Current.Value.EnumerateArray();
138 4b847de5 A-Konig
                            Current = ParseCurrentWeather(currentWeather);
139 1407c8ba A-Konig
140
                            break;
141
                        }
142
                    // weather prediction
143
                    case "weather":
144
                        {
145
                            ArrayEnumerator weather = weatherP.Current.Value.EnumerateArray();
146
                            ParseWeatherPredict(weather);
147
148
                            break;
149
                        }
150 c9eed50c A-Konig
                }
151
            }
152
153 1407c8ba A-Konig
            // sunrise + sunset into data
154
            EncompassSunRiseSetTimes();
155
156 9fb55c71 A-Konig
            //TestConsoleOutput();
157 1407c8ba A-Konig
        }
158
159 cdf3c217 A-Konig
        /// <summary>
160
        /// Test console output
161
        /// </summary>
162 1407c8ba A-Konig
        private void TestConsoleOutput()
163
        {
164 4b847de5 A-Konig
            Console.WriteLine(Current);
165
            foreach (WeatherInfo w in Predictions)
166 1407c8ba A-Konig
                Console.WriteLine(w);
167 c9eed50c A-Konig
        }
168
169 1407c8ba A-Konig
        /// <summary>
170
        /// Change data in a way that they now reflect sunrise and sunset times
171
        /// If current time under sunrise or over sunset -> WeatherConditions is Dark
172
        /// If prediction time is under sunrise or over sunset and more than half of the interval is under/over said time -> WeatherCondition is Dark
173
        /// </summary>
174
        private void EncompassSunRiseSetTimes()
175
        {
176
            // change current weather
177 4b847de5 A-Konig
            if ((Current.startTime.TimeOfDay > sunsetTime.TimeOfDay) || (Current.startTime.TimeOfDay < sunriseTime.TimeOfDay))
178
                Current.condition = WeatherConditions.Dark;
179 1407c8ba A-Konig
180
            // change prediction
181 4b847de5 A-Konig
            for (int i = 0; i < Predictions.Count - 1; i++)
182 1407c8ba A-Konig
            {
183 4b847de5 A-Konig
                WeatherInfo w = Predictions[i];
184
                WeatherInfo wNext = Predictions[i + 1];
185 1407c8ba A-Konig
186 c0dffb79 A-Konig
                // if wNext time < than w time then it is prediction from the next day -> add 24 to correctly calculate timespan
187 1407c8ba A-Konig
                int timespan = wNext.startTime.Hour - w.startTime.Hour;
188 c0dffb79 A-Konig
                if (wNext.startTime.Hour < w.startTime.Hour)
189
                    timespan = (wNext.startTime.Hour+24) - w.startTime.Hour;
190
                
191 1407c8ba A-Konig
                w.intervalLength = timespan;
192
193
                // if start under sunset
194
                if (w.startTime.TimeOfDay > sunsetTime.TimeOfDay)
195
                    w.condition = WeatherConditions.Dark;
196
197
                // if start under sunrise
198
                if (w.startTime.TimeOfDay < sunriseTime.TimeOfDay)
199
                {
200
                    double howMuch = ((sunriseTime.Hour * 60 + sunriseTime.Minute) - (w.startTime.Hour * 60 + w.startTime.Minute)) / 60.0;
201
202
                    if (howMuch >= timespan / 2.0)
203
                        w.condition = WeatherConditions.Dark;
204
                }
205
206
                // if start under sunrise
207
                TimeSpan endTime = new TimeSpan(w.startTime.TimeOfDay.Hours + timespan, w.startTime.TimeOfDay.Minutes, 0);
208
                if (endTime > sunsetTime.TimeOfDay)
209
                {
210
                    double howMuch = ((endTime.Hours * 60 + endTime.Minutes) - (sunsetTime.Hour * 60 + sunsetTime.Minute)) / 60.0;
211
212
                    if (howMuch >= timespan / 2.0)
213
                        w.condition = WeatherConditions.Dark;
214
                }
215
            }
216
217
            // last prediction
218 4b847de5 A-Konig
            WeatherInfo wLast = Predictions[Predictions.Count - 1];
219 1407c8ba A-Konig
            TimeSpan endTimeW = new TimeSpan(24, 0, 0);
220
            int timespanLast = endTimeW.Hours - wLast.startTime.Hour;
221
            wLast.intervalLength = timespanLast;
222
223
            // if start under sunset
224
            if (wLast.startTime.TimeOfDay > sunsetTime.TimeOfDay)
225
                wLast.condition = WeatherConditions.Dark;
226
227
            // if start under sunrise
228
            if (wLast.startTime.TimeOfDay < sunriseTime.TimeOfDay)
229
            {
230
                double howMuch = ((sunriseTime.Hour * 60 + sunriseTime.Minute) - (wLast.startTime.Hour * 60 + wLast.startTime.Minute)) / 60.0;
231
232
                if (howMuch >= timespanLast / 2.0)
233
                    wLast.condition = WeatherConditions.Dark;
234
            }
235
236
            // if start under sunrise
237
            if (endTimeW > sunsetTime.TimeOfDay)
238
            {
239
                double howMuch = ((endTimeW.Hours * 60 + endTimeW.Minutes) - (sunsetTime.Hour * 60 + sunsetTime.Minute)) / 60.0;
240
241
                if (howMuch >= timespanLast / 2.0)
242
                    wLast.condition = WeatherConditions.Dark;
243
            }
244
        }
245
246
        /// <summary>
247
        /// Parse weather prediction
248
        /// </summary>
249
        /// <param name="weather"> ArrayEnumerator of weather predictions </param>
250
        private void ParseWeatherPredict(ArrayEnumerator weather)
251
        {
252
            while (weather.MoveNext())
253
            {
254
                // prediction for one day
255
                var obj = weather.Current.EnumerateObject();
256
257
                while (obj.MoveNext())
258
                {
259
                    switch (obj.Current.Name)
260
                    {
261
                        case "date":
262
                            {
263
                                DateTime.TryParseExact(obj.Current.Value.GetString(), "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out currParsedDay);
264
                                break;
265
                            }
266
                        // hourly predictions
267
                        case "hourly":
268
                            {
269
                                ParseHourly(obj.Current.Value.EnumerateArray());
270
                                break;
271
                            }
272
                        // sunset / sunrise hours
273
                        case "astronomy":
274
                            {
275
                                ParseAstronomy(obj.Current.Value.EnumerateArray());
276
                                break;
277
                            }
278
                    }
279
                }
280
            }
281
        }
282
283
        /// <summary>
284
        /// Parse sunrise and sunset times
285
        /// </summary>
286
        /// <param name="astronomy"> Astronomy array enumerator </param>
287
        private void ParseAstronomy(ArrayEnumerator astronomy)
288
        {
289
            while (astronomy.MoveNext())
290
            {
291
                var astrInfo = astronomy.Current.EnumerateObject();
292
                
293
                while (astrInfo.MoveNext())
294
                {
295
                    switch (astrInfo.Current.Name)
296
                    {
297
                        case "sunrise":
298
                            {
299
                                DateTime.TryParseExact(astrInfo.Current.Value.GetString(), "hh:mm tt", CultureInfo.InvariantCulture, DateTimeStyles.None, out sunriseTime);
300
                                Console.WriteLine("\t sunrise time : " + sunriseTime + " " + astrInfo.Current.Value.GetString());
301
                                break;
302
                            }
303
                        case "sunset":
304
                            {
305
                                DateTime.TryParseExact(astrInfo.Current.Value.GetString(), "hh:mm tt", CultureInfo.InvariantCulture, DateTimeStyles.None, out sunsetTime);
306
                                Console.WriteLine("\t sunset time : " + sunsetTime + " " + astrInfo.Current.Value.GetString());
307
                                break;
308
                            }
309
                    }
310
                }
311
            }
312
        }
313
314
        /// <summary>
315
        /// Parse hourly predictions
316
        /// </summary>
317
        /// <param name="hourly">Enumerated array of hourly predictions</param>
318
        private void ParseHourly(ArrayEnumerator hourly)
319
        {
320
            while (hourly.MoveNext())
321
            {
322
                // one hourly prediction
323
                var oneH = hourly.Current.EnumerateObject();
324
                WeatherInfo weather = new WeatherInfo();
325
326
                while (oneH.MoveNext())
327
                {
328
                    switch (oneH.Current.Name)
329
                    {
330
                        case "FeelsLikeC":
331
                            {
332
                                Double.TryParse(oneH.Current.Value.GetString(), out weather.temp);
333
                                break;
334
                            }
335
                        case "cloudcover":
336
                            {
337
                                int cloudCover;
338
                                Int32.TryParse(oneH.Current.Value.GetString(), out cloudCover);
339
                                weather.condition = ValueToConditions.CloudCoverToConditions(cloudCover);
340
                                break;
341
                            }
342
                        // take into account highest value from "chanceofrain" and "chaceofsnow"
343
                        case "chanceofrain":
344
                            {
345
                                int rain;
346
                                Int32.TryParse(oneH.Current.Value.GetString(), out rain);
347
                                weather.rain = rain > weather.rain ? rain : weather.rain;
348
                                break;
349
                            }
350
                        case "chanceofsnow":
351
                            {
352
                                int snow;
353
                                Int32.TryParse(oneH.Current.Value.GetString(), out snow);
354
                                weather.rain = snow > weather.rain ? snow : weather.rain;
355
                                break;
356
                            }
357
                        // wind kmph has to be translated to mps
358
                        case "WindGustKmph":
359
                            {
360
                                double windkmh;
361
                                Double.TryParse(oneH.Current.Value.GetString(), out windkmh);
362
                                weather.wind= windkmh * 1000 / (60.0*60.0);
363
                                break;
364
                            }
365
                        case "time":
366
                            {
367
                                int h;
368
                                Int32.TryParse(oneH.Current.Value.GetString(), out h);
369
                                h /= 100;
370
                                DateTime time = new DateTime(currParsedDay.Year, currParsedDay.Month, currParsedDay.Day, h, 0, 0);
371
                                weather.startTime = time;
372
                                break;
373
                            }
374
                    }
375
                }
376
377
                // Console.WriteLine(weather.ToString());
378 4b847de5 A-Konig
                Predictions.Add(weather);
379 1407c8ba A-Konig
            }
380 c9eed50c A-Konig
381
        }
382 1407c8ba A-Konig
383
        /// <summary>
384
        /// Parse current weather
385
        /// </summary>
386
        /// <param name="currentWeather">Enumerated hour of weather data</param>
387
        /// <returns>WeatherInfo with current weather</returns>
388
        private WeatherInfo ParseCurrentWeather(ArrayEnumerator currentWeather)
389
        {
390
            WeatherInfo res = new WeatherInfo();
391
            //res.current = true;
392
393
            while (currentWeather.MoveNext())
394
            {
395
                var obj = currentWeather.Current.EnumerateObject();
396
397
                while (obj.MoveNext())
398
                {
399
                    switch (obj.Current.Name)
400
                    {
401
                        case "localObsDateTime":
402
                            {
403
                                DateTime.TryParse(obj.Current.Value.GetString(), out res.startTime);
404
                                break;
405
                            }
406
                        case "FeelsLikeC":
407
                            {
408
                                Double.TryParse(obj.Current.Value.GetString(), out res.temp);
409
                                break;
410
                            }
411
                        case "cloudcover":
412
                            {
413
                                int cloudCover;
414
                                Int32.TryParse(obj.Current.Value.GetString(), out cloudCover);
415
                                res.condition = ValueToConditions.CloudCoverToConditions(cloudCover);
416
                                break;
417
                            }
418
                        case "precipMM":
419
                            {
420
                                double rainMM;
421
                                Double.TryParse(obj.Current.Value.GetString(), out rainMM);
422
                                res.rain = rainMM > 0 ? 100 : 0;
423
                                break;
424
                            }
425
                        case "windspeedKmph":
426
                            {
427
                                double wind;
428
                                Double.TryParse(obj.Current.Value.GetString(), out wind);
429
                                res.wind = wind * 1000 / (60.0 * 60.0);
430
                                break;
431
                            }
432
                    }
433
                }
434
435
            }
436
437
            return res;
438
        }
439
440 c9eed50c A-Konig
    }
441
}