Skip to content

Commit 5b29f9a

Browse files
committed
- Added File metadata parsing feature for displaying data source, time range, and location
- Added Progress indicator during navigation transitions
1 parent c86b6ed commit 5b29f9a

12 files changed

+748
-278
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.ComponentModel;
4+
using System.Linq;
5+
using System.Text;
6+
using System.Threading.Tasks;
7+
8+
namespace eBirdDataVisualizer.Core.Models;
9+
10+
public class DataSourceMetadata
11+
{
12+
public string Name
13+
{
14+
get; set;
15+
}
16+
17+
public string Location
18+
{
19+
get; set;
20+
}
21+
22+
public int YearStart
23+
{
24+
get; set;
25+
}
26+
27+
public int YearEnd
28+
{
29+
get; set;
30+
}
31+
32+
public Month MonthStart
33+
{
34+
get; set;
35+
}
36+
37+
public Month MonthEnd
38+
{
39+
get; set;
40+
}
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace eBirdDataVisualizer.Core.Models;
8+
9+
public enum Month
10+
{
11+
January = 0,
12+
February = 1,
13+
March = 2,
14+
April = 3,
15+
May = 4,
16+
June = 5,
17+
July = 6,
18+
August = 7,
19+
September = 8,
20+
October = 9,
21+
November = 10,
22+
December = 11
23+
}
24+
25+
public class MonthData : IComparable
26+
{
27+
public Month Month
28+
{
29+
get; set;
30+
}
31+
32+
public ICollection<double> SampleSizes
33+
{
34+
get; set;
35+
}
36+
37+
public string Q1String => $"{Month} Q1: {SampleSizes.ElementAt(0)}";
38+
public string Q2String => $"{Month} Q2: {SampleSizes.ElementAt(1)}";
39+
public string Q3String => $"{Month} Q3: {SampleSizes.ElementAt(2)}";
40+
public string Q4String => $"{Month} Q4: {SampleSizes.ElementAt(3)}";
41+
42+
public string Q1Tag => $"{Month}Q1";
43+
public string Q2Tag => $"{Month}Q2";
44+
public string Q3Tag => $"{Month}Q3";
45+
public string Q4Tag => $"{Month}Q4";
46+
47+
public int CompareTo(object obj)
48+
{
49+
return Month.CompareTo((obj as MonthData).Month);
50+
}
51+
}

eBirdDataVisualizer.Core/Services/BirdDataService.cs

+135-21
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Data;
44
using System.Diagnostics;
55
using System.Linq;
6+
using System.Security.Cryptography.X509Certificates;
67
using System.Text;
78
using System.Threading.Tasks;
89
using eBirdDataVisualizer.Core.Models;
@@ -12,9 +13,12 @@ namespace eBirdDataVisualizer.Core.Services;
1213

1314
public interface IBirdDataService
1415
{
15-
public Task<IEnumerable<Bird>> GetGridDataAsync();
16+
public Task<IEnumerable<Bird>> GetBirdDataAsync();
17+
public Task<IEnumerable<MonthData>> GetMonthDataAsync();
18+
public Task<DataSourceMetadata> GetMetadataAsync();
1619
public void ClearData();
1720
public Task<bool> ParseData(string data);
21+
public Task ParseMetadata(string name);
1822
}
1923

2024
public class BirdDataService : IBirdDataService
@@ -30,7 +34,20 @@ public class BirdDataService : IBirdDataService
3034
static DataColumn JanuaryQ3 = new DataColumn(nameof(JanuaryQ3), typeof(double));
3135
static DataColumn JanuaryQ4 = new DataColumn(nameof(JanuaryQ4), typeof(double));
3236

37+
static DataTable Months = new DataTable(nameof(Months));
38+
static DataColumn Month = new DataColumn(nameof(Month), typeof(Month));
39+
static DataColumn SampleSizes = new DataColumn(nameof(SampleSizes), typeof(List<double>));
40+
41+
static DataTable Metadata = new DataTable(nameof(Metadata));
42+
static DataColumn Name = new DataColumn(nameof(Name), typeof(string));
43+
static DataColumn YearStart = new DataColumn(nameof(YearStart), typeof(int));
44+
static DataColumn YearEnd = new DataColumn(nameof(YearEnd), typeof(int));
45+
static DataColumn MonthStart = new DataColumn(nameof(MonthStart), typeof(Month));
46+
static DataColumn MonthEnd = new DataColumn(nameof(MonthEnd), typeof(Month));
47+
static DataColumn Location = new DataColumn(nameof(Location), typeof(string));
48+
3349
private static List<Bird> allBirds = new List<Bird>();
50+
private static List<MonthData> allMonths = new List<MonthData>();
3451

3552
static BirdDataService()
3653
{
@@ -43,31 +60,54 @@ static BirdDataService()
4360
Birds.Columns.Add(JanuaryQ3);
4461
Birds.Columns.Add(JanuaryQ4);
4562

46-
Birds.Rows.Add(0, "Black-bellied Whistling-Duck", "Dendrocygna autumnalis", new List<double>() { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.655E-4, 2.082E-4, 4.693E-4, 4.624E-4, 8.357E-4, 9.092E-4, 8.3E-5, 0.0012603, 0.0012944, 4.97E-5, 0.0015, 0.0010597, 4.18E-4, 7.671E-4, 1.218E-4, 6.09E-5, 0.0, 0.0, 0.0019542, 0.0, 4.4E-5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 });
47-
48-
Birds.Rows.Add(3, "West Indian Whistling-Duck", "Dendrocygna arborea", new List<double>() { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0015, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 });
49-
50-
Birds.Rows.Add(4, "Fulvous Whistling-Duck", "Dendrocygna bicolor", new List<double>() { 0.0015, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0015, 0.0, 0.0015, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0015, 3.95E-5, 0.0015, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0015, 0.0015, 0.0015, 0.0015, 0.0, 0.0, 0.0, 0.0, 0.0015, 0.0015, 0.0, 0.0015, 0.0, 0.0015, 0.0, 0.0, 0.0015 });
51-
52-
Birds.Rows.Add(1, "Emu", "Dromaius novaehollandiae", new List<double>() { 0.0, 0.0, 0.0, 0.0, 4.44E-5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 });
53-
54-
Birds.Rows.Add(2, "Snow Goose", "Anser caerulescens", new List<double>() { 0.0164127, 0.0130752, 0.0147944, 0.0115589, 0.0134523, 0.0070272, 0.0060272, 0.0100091, 0.0097213, 0.0075751, 0.0053493, 0.0031462, 0.0010052, 6.83E-4, 6.52E-4, 4.76E-4, 4.164E-4, 7.41E-5, 3.391E-4, 2.279E-4, 1.186E-4, 8.3E-5, 1.8E-4, 0.0, 1.491E-4, 5.39E-5, 5.89E-5, 4.18E-5, 1.18E-4, 1.828E-4, 1.218E-4, 7.95E-5, 2.555E-4, 6.514E-4, 9.051E-4, 0.0011868, 0.0010745, 0.0026718, 0.0040783, 0.0059102, 0.0064931, 0.0110383, 0.0122762, 0.0232245, 0.0200967, 0.0151272, 0.0184791, 0.030209 });
63+
Months.Columns.Add(Month);
64+
Months.Columns.Add(SampleSizes);
5565

56-
Birds.Rows.Add(5, "Barnacle Goose", "Branta leucopsis", new List<double>() { 3.17E-5, 4.22E-5, 4.12E-5, 0.0015, 3.108E-4, 5.69E-5, 3.57E-5, 0.0015, 0.0015, 1.209E-4, 1.207E-4, 0.0015, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0015, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 3.205E-4, 5.19E-5, 0.0 });
57-
58-
Birds.Rows.Add(5, "Egyptian Goose", "Alopochen aegyptiaca", new List<double>() { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0015, 0.0, 0.0, 0.0, 0.0, 0.0, 4.152E-4, 1.35E-4, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 3.97E-5, 5.11E-5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.48E-5, 0.0, 0.0, 0.0, 0.0 });
66+
Metadata.Columns.Add(Name);
67+
Metadata.Columns.Add(Location);
68+
Metadata.Columns.Add(YearStart);
69+
Metadata.Columns.Add(YearEnd);
70+
Metadata.Columns.Add(MonthStart);
71+
Metadata.Columns.Add(MonthEnd);
5972

6073
DataSet.Tables.Add(Birds);
74+
DataSet.Tables.Add(Months);
75+
DataSet.Tables.Add(Metadata);
6176
}
6277

63-
public BirdDataService()
78+
public void ClearData()
6479
{
80+
for (int i = 0; i < DataSet.Tables.Count; ++i)
81+
DataSet.Tables[i].Rows.Clear();
82+
}
6583

84+
private static DataSourceMetadata AllMetadata()
85+
{
86+
DataSourceMetadata metadata = new DataSourceMetadata();
87+
foreach (DataRow row in DataSet.Tables[DataSet.Tables.IndexOf(Metadata)].Rows)
88+
{
89+
metadata.Name = (string)row[Name];
90+
metadata.YearStart = (int)row[YearStart];
91+
metadata.YearEnd = (int)row[YearEnd];
92+
metadata.MonthStart = (Month)row[MonthStart];
93+
metadata.MonthEnd = (Month)row[MonthEnd];
94+
metadata.Location = (string)row[Location];
95+
}
96+
return metadata;
6697
}
6798

68-
public void ClearData()
99+
private static IEnumerable<MonthData> AllMonths()
69100
{
70-
DataSet.Clear();
101+
List<MonthData> months = new List<MonthData>();
102+
foreach (DataRow row in DataSet.Tables[DataSet.Tables.IndexOf(Months)].Rows)
103+
{
104+
months.Add(new MonthData()
105+
{
106+
Month = (Month)row[Month],
107+
SampleSizes = (List<double>)row[SampleSizes]
108+
});
109+
}
110+
return months;
71111
}
72112

73113
private static IEnumerable<Bird> AllBirds()
@@ -81,7 +121,6 @@ private static IEnumerable<Bird> AllBirds()
81121
BirdId = (int)row[BirdId],
82122
CommonName = (string)row[CommonName],
83123
ScientificName = (string)row[ScientificName],
84-
Frequency = frequency,
85124
JanuaryQ1 = (double)frequency[0],
86125
JanuaryQ2 = (double)frequency[1],
87126
JanuaryQ3 = (double)frequency[2],
@@ -135,16 +174,39 @@ private static IEnumerable<Bird> AllBirds()
135174
return birds;
136175
}
137176

138-
public async Task<IEnumerable<Bird>> GetGridDataAsync()
177+
public async Task<IEnumerable<Bird>> GetBirdDataAsync()
139178
{
140179
allBirds = new List<Bird>(AllBirds());
141180

142181
await Task.CompletedTask;
143182
return allBirds;
144183
}
145184

185+
public async Task<IEnumerable<MonthData>> GetMonthDataAsync()
186+
{
187+
allMonths = new List<MonthData>(AllMonths());
188+
189+
await Task.CompletedTask;
190+
return allMonths;
191+
}
192+
193+
public async Task<DataSourceMetadata> GetMetadataAsync()
194+
{
195+
var data = AllMetadata();
196+
await Task.CompletedTask;
197+
return data;
198+
}
199+
200+
/// <summary>
201+
/// Parse data loaded from an eBird barchart .txt file.
202+
/// </summary>
203+
/// <param name="data"></param>
204+
/// <returns></returns>
146205
public async Task<bool> ParseData(string data)
147206
{
207+
//Months.Rows.Clear();
208+
//Birds.Rows.Clear();
209+
148210
const string frequencyKey = "Frequency of observations in the selected location(s)";
149211
const string numberTaxaKey = "Number of taxa";
150212
const string sampleSizeKey = "Sample Size";
@@ -153,11 +215,37 @@ public async Task<bool> ParseData(string data)
153215

154216
try
155217
{
156-
// Parse the histogram data text:
218+
// Parse the monthly sample-size data:
219+
var sampleSizes = data.Trim().Split('\n') // split text into lines
220+
.Where(l => l != String.Empty) // skip empty lines
221+
.SkipWhile(l => !l.Contains(sampleSizeKey)).First() // skip forward to the line containing sample size data
222+
.Split('\t') // separate the line by the value delimiter
223+
.Where(v => v != String.Empty && Double.TryParse(v, out _)) // exclude empty items and non-numeric values
224+
.Select(v => System.Convert.ToDouble(v)).ToList(); // cast the remaining items to doubles and return as list
225+
226+
// Parse the histogram data for bird entries:
157227
var lines = data.Trim().Split('\n') // split text into lines
158-
.Where(l => l != "") // skip empty lines
228+
.Where(l => l != String.Empty) // skip empty lines
159229
.SkipWhile(l => !l.Contains(sampleSizeKey)) // skip forward to the line containing sample size data
160230
.Skip(1).ToList(); // skip the sample size data line; subsequent lines should be bird entries
231+
232+
Months.Rows.Clear();
233+
Birds.Rows.Clear();
234+
235+
int monthCounter = 0;
236+
List<double> samples = new List<double>();
237+
for (var i = 0; i < sampleSizes.Count() + 1; ++i)
238+
{
239+
if (i > 0 && i % 4 == 0)
240+
{
241+
Months.Rows.Add((Month)Enum.GetValues(typeof(Month)).GetValue(monthCounter), samples);
242+
monthCounter++;
243+
samples = new List<double>();
244+
}
245+
if (i < sampleSizes.Count())
246+
samples.Add(sampleSizes[i]);
247+
}
248+
161249
for (var i = 0; i < lines.Count(); ++i)
162250
{
163251
// the common name appears first in the data entry:
@@ -166,7 +254,7 @@ public async Task<bool> ParseData(string data)
166254
var scientificNameSplit = commonNameSplit.Last().Split("</em>)");
167255
var commonName = commonNameSplit.First();
168256
var scientificName = scientificNameSplit.First();
169-
var frequencyData = scientificNameSplit.Last().Trim().Split('\t').Where(x => x != "").Select(x => System.Convert.ToDouble(x)).ToList();
257+
var frequencyData = scientificNameSplit.Last().Trim().Split('\t').Where(x => x != String.Empty).Select(x => System.Convert.ToDouble(x)).ToList();
170258

171259
Birds.Rows.Add(i, commonName, scientificName, frequencyData);
172260
}
@@ -181,4 +269,30 @@ public async Task<bool> ParseData(string data)
181269
await Task.CompletedTask;
182270
return importResult;
183271
}
272+
273+
public async Task ParseMetadata(string name)
274+
{
275+
var yearStart = 0;
276+
var yearEnd = 0;
277+
var monthStart = Models.Month.January;
278+
var monthEnd = Models.Month.January;
279+
var location = name.Split("__").First().Split('_').Last();
280+
var timeInfo = name.Split("__").Last().Split("barchart.txt").First().Split('_').Where(x => x != String.Empty);
281+
if (timeInfo.Count() == 4)
282+
{
283+
try
284+
{
285+
yearStart = int.Parse(timeInfo.ElementAt(0));
286+
yearEnd = int.Parse(timeInfo.ElementAt(1));
287+
monthStart = (Month)(int.Parse(timeInfo.ElementAt(2)) - 1);
288+
monthEnd = (Month)(int.Parse(timeInfo.ElementAt(3)) - 1);
289+
}
290+
catch (Exception) { }
291+
}
292+
293+
Metadata.Rows.Clear();
294+
Metadata.Rows.Add(name, location, yearStart, yearEnd, monthStart, monthEnd);
295+
await Task.CompletedTask;
296+
return;
297+
}
184298
}

0 commit comments

Comments
 (0)