Projekt

Obecné

Profil

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