Skip to content

Commit b76bac7

Browse files
authored
Merge pull request #552 from ledpup/issues/climate-records
Issues/climate records
2 parents 8e3ff5c + 8d4e7ff commit b76bac7

27 files changed

Lines changed: 878 additions & 498 deletions

ClimateExplorer.Core/DataPreparation/DataSetBuilder.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public async Task<BuildDataSetResult> BuildDataSet(PostDataSetsRequestBody reque
4242
new BuildDataSetResult
4343
{
4444
DataPoints = dataPoints,
45-
RawDataRecords = request.IncludeRawDataRecords ? series.DataRecords : null,
45+
RawDataRecords = request.IncludeRawDataRecords == true ? series.DataRecords : null,
4646
UnitOfMeasure = series.UnitOfMeasure,
4747
};
4848
}
@@ -106,7 +106,7 @@ public ChartableDataPoint[] BuildDataSetFromDataRecords(DataRecord[] dataRecords
106106
var aggregatedBins = BinAggregator.AggregateBins(filteredRawBins, request.BinAggregationFunction, request.BucketAggregationFunction, request.CupAggregationFunction, request.SeriesTransformation);
107107

108108
// Calculate final value based on bin aggregates
109-
var finalBins = FinalBinValueCalculator.CalculateFinalBinValues(aggregatedBins, request.Anomaly);
109+
var finalBins = FinalBinValueCalculator.CalculateFinalBinValues(aggregatedBins, request.Anomaly == true);
110110

111111
Console.WriteLine("AggregateBins completed in " + sw.Elapsed);
112112
sw.Restart();

ClimateExplorer.Core/DataPreparation/Model/PostDataSetsRequestBody.cs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,30 +4,30 @@
44

55
public class PostDataSetsRequestBody
66
{
7-
public SeriesDerivationTypes SeriesDerivationType { get; set; }
7+
public SeriesDerivationTypes SeriesDerivationType { get; set; } = SeriesDerivationTypes.ReturnSingleSeries;
88
public SeriesSpecification[]? SeriesSpecifications { get; set; }
9-
public SeriesTransformations SeriesTransformation { get; set; }
10-
9+
public SeriesTransformations SeriesTransformation { get; set; } = SeriesTransformations.Identity;
1110
public string? CustomTransformation { get; set; }
1211

12+
public required BinGranularities BinningRule { get; set; }
13+
public required ContainerAggregationFunctions BinAggregationFunction { get; set; }
14+
public ContainerAggregationFunctions BucketAggregationFunction { get; set; }
15+
public ContainerAggregationFunctions CupAggregationFunction { get; set; }
16+
17+
public required int CupSize { get; set; }
18+
public required float RequiredCupDataProportion { get; set; }
19+
public required float RequiredBucketDataProportion { get; set; }
20+
public required float RequiredBinDataProportion { get; set; }
21+
1322
public SouthernHemisphereTemperateSeasons? FilterToSouthernHemisphereTemperateSeason { get; set; }
1423
public SouthernHemisphereTropicalSeasons? FilterToTropicalSeason { get; set; }
1524
public int? FilterToYearsAfterAndIncluding { get; set; }
1625
public int? FilterToYearsBefore { get; set; }
1726
public short? FilterToYear { get; set; }
1827

19-
public int CupSize { get; set; }
20-
public float RequiredCupDataProportion { get; set; }
21-
public float RequiredBucketDataProportion { get; set; }
22-
public float RequiredBinDataProportion { get; set; }
23-
24-
public BinGranularities BinningRule { get; set; }
25-
public ContainerAggregationFunctions BinAggregationFunction { get; set; }
26-
public ContainerAggregationFunctions BucketAggregationFunction { get; set; }
27-
public ContainerAggregationFunctions CupAggregationFunction { get; set; }
28-
public bool Anomaly { get; set; }
28+
public bool? Anomaly { get; set; }
2929

30-
public bool IncludeRawDataRecords { get; set; }
30+
public bool? IncludeRawDataRecords { get; set; }
3131

3232
public DataResolution? MinimumDataResolution { get; set; }
3333
}

ClimateExplorer.Core/Model/ClimateRecord.cs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,14 @@
22

33
using static ClimateExplorer.Core.Enums;
44

5-
public enum RecordType
6-
{
7-
High,
8-
Low,
9-
}
10-
115
public class ClimateRecord
126
{
137
public DataType DataType { get; set; }
14-
public RecordType RecordType { get; set; }
158
public DataAdjustment? DataAdjustment { get; set; }
169
public DataResolution DataResolution { get; set; }
1710
public UnitOfMeasure UnitOfMeasure { get; set; }
1811
public int Year { get; set; }
1912
public int Month { get; set; }
2013
public int? Day { get; set; }
2114
public double Value { get; set; }
22-
public int NumberOfTimes { get; set; }
2315
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace ClimateExplorer.Core.Model;
2+
3+
public class ClimateRecordsResponse
4+
{
5+
public List<ClimateRecord> Records { get; set; } = [];
6+
public int? StartYear { get; set; }
7+
public int? EndYear { get; set; }
8+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
namespace ClimateExplorer.Web.Client.Services;
2+
3+
using ClimateExplorer.Core.Calculators;
4+
using ClimateExplorer.Core.DataPreparation;
5+
using ClimateExplorer.Core.Model;
6+
using ClimateExplorer.Core.ViewModel;
7+
using ClimateExplorer.Web.Client.UiModel;
8+
using ClimateExplorer.Web.UiModel;
9+
using ClimateExplorer.WebApiClient.Services;
10+
using static ClimateExplorer.Core.Enums;
11+
12+
public static class ClimateDataHelper
13+
{
14+
public static async Task<LocationAnomalySummary?> CalculateAnomaly(
15+
IDataService dataService,
16+
IEnumerable<DataSetDefinitionViewModel> dataSetDefinitions,
17+
Location location,
18+
List<DataSubstitute> dataSubstitutes,
19+
ContainerAggregationFunctions function,
20+
BinGranularities binGranularity = BinGranularities.ByYear)
21+
{
22+
var series = await GetData(dataService, dataSetDefinitions, location, dataSubstitutes, function, binGranularity);
23+
24+
if (series == null || series.DataRecords.Count == 0)
25+
{
26+
return null;
27+
}
28+
29+
var average = series.DataRecords.Average(x => x.Value)!.Value;
30+
31+
var anomalyRecords =
32+
series.DataRecords
33+
.Where(x => x.Value != null)
34+
.Select(x => new YearlyValues(
35+
((YearBinIdentifier)BinIdentifier.Parse(x.BinId!)).Year,
36+
x.Value!.Value - average,
37+
x.Value!.Value,
38+
x.Value!.Value / average * 100D))
39+
.ToList();
40+
41+
var anomaly = AnomalyCalculator.CalculateAnomaly(series.DataRecords);
42+
43+
return new LocationAnomalySummary { CalculatedAnomaly = anomaly, DataSet = series, AnomalyRecords = anomalyRecords };
44+
}
45+
46+
public static async Task<DataSet?> GetData(
47+
IDataService dataService,
48+
IEnumerable<DataSetDefinitionViewModel> dataSetDefinitions,
49+
Location location,
50+
List<DataSubstitute> dataSubstitutes,
51+
ContainerAggregationFunctions function,
52+
BinGranularities binGranularity = BinGranularities.ByYear)
53+
{
54+
var measurementForLocation =
55+
DataSetDefinitionViewModel.GetDataSetDefinitionAndMeasurement(
56+
dataSetDefinitions,
57+
location.Id,
58+
dataSubstitutes,
59+
throwIfNoMatch: false)!;
60+
61+
if (measurementForLocation == null)
62+
{
63+
return null;
64+
}
65+
66+
var spec = new SeriesSpecification
67+
{
68+
DataAdjustment = measurementForLocation.MeasurementDefinition!.DataAdjustment,
69+
DataSetDefinitionId = measurementForLocation.DataSetDefinition!.Id,
70+
DataType = measurementForLocation.MeasurementDefinition.DataType,
71+
LocationId = location.Id,
72+
};
73+
74+
var series =
75+
await dataService.PostDataSet(
76+
binGranularity,
77+
function,
78+
function,
79+
function,
80+
SeriesValueOptions.Value,
81+
[spec],
82+
SeriesDerivationTypes.ReturnSingleSeries,
83+
1.0f,
84+
1.0f,
85+
0.7f,
86+
14,
87+
SeriesTransformations.Identity);
88+
89+
return series!;
90+
}
91+
}

ClimateExplorer.Web.Client/Shared/ChartSeriesView.razor

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,7 @@
162162
<Select TValue="Colours"
163163
Size="Size.Small"
164164
Value="@ChartSeries.RequestedColour"
165-
ValueChanged="@OnColourChanged"
166-
Disabled="@(ChartSeries.DisplayStyle == SeriesDisplayStyle.Bar)">
165+
ValueChanged="@OnColourChanged">
167166
@foreach (var colour in (Colours[])Enum.GetValues(typeof(Colours)))
168167
{
169168
<SelectItem Value="@colour">@GetColourName(colour)</SelectItem>

ClimateExplorer.Web.Client/Shared/ChartView.razor.cs

Lines changed: 17 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -747,6 +747,16 @@ private static bool IsCompatibleUnitOfMeasure(UnitOfMeasure seriesUnitOfMeasure,
747747
return seriesUnitOfMeasure == filterUnitOfMeasure;
748748
}
749749

750+
private static string GetChartLabel(SeriesTransformations seriesTransformation, string? customTransformation, string defaultLabel, SeriesAggregationOptions seriesAggregationOptions)
751+
{
752+
return seriesTransformation switch
753+
{
754+
SeriesTransformations.DayOfYearIfFrost => seriesAggregationOptions == SeriesAggregationOptions.Maximum ? "Last day of frost" : "First day of frost",
755+
SeriesTransformations.Custom => ChartSeriesDefinition.GetFriendlyCustomTransformationLabel(customTransformation ?? "Custom transformation"),
756+
_ => defaultLabel,
757+
};
758+
}
759+
750760
private object CreateChartOptions(string title, string subtitle, dynamic scales)
751761
{
752762
return new
@@ -1109,44 +1119,6 @@ await ChartLogic.AddDataSetToChart(
11091119
return trendlines;
11101120
}
11111121

1112-
private void RebuildChartSeriesListToReflectSelectedYears()
1113-
{
1114-
var years = SelectedYears!.Any() ? SelectedYears!.Select(x => (short?)x).ToList() : new List<short?>() { null };
1115-
1116-
List<ChartSeriesDefinition> newCsds = new List<ChartSeriesDefinition>();
1117-
1118-
var uniqueChartSeriesList = ChartSeriesList!.Distinct(new ChartSeriesDefinition.ChartSeriesDefinitionComparerWhichIgnoresYearAndIsLocked()).ToArray();
1119-
1120-
foreach (var csd in uniqueChartSeriesList)
1121-
{
1122-
foreach (var year in years)
1123-
{
1124-
newCsds.Add(
1125-
new ChartSeriesDefinition()
1126-
{
1127-
SeriesDerivationType = SeriesDerivationTypes.ReturnSingleSeries,
1128-
SourceSeriesSpecifications = csd.SourceSeriesSpecifications,
1129-
Aggregation = csd.Aggregation,
1130-
BinGranularity = year == null ? BinGranularities.ByYear : BinGranularities.ByYearAndMonth,
1131-
DisplayStyle = csd.DisplayStyle,
1132-
IsLocked = csd.IsLocked,
1133-
ShowTrendline = csd.ShowTrendline,
1134-
SecondaryCalculation = csd.SecondaryCalculation,
1135-
Smoothing = csd.Smoothing,
1136-
SmoothingWindow = csd.SmoothingWindow,
1137-
Value = csd.Value,
1138-
Year = year,
1139-
SeriesTransformation = csd.SeriesTransformation,
1140-
GroupingThreshold = csd.GroupingThreshold,
1141-
MinimumDataResolution = csd.MinimumDataResolution,
1142-
});
1143-
}
1144-
}
1145-
1146-
Logger!.LogInformation("RebuildChartSeriesListToReflectSelectedYears() setting ChartSeriesList");
1147-
ChartSeriesList = newCsds;
1148-
}
1149-
11501122
private void BuildProcessedDataSets(List<SeriesWithData> chartSeriesWithData, bool chartAllData)
11511123
{
11521124
var l = new LogAugmenter(Logger!, "BuildProcessedDataSets");
@@ -1318,16 +1290,6 @@ [.. chartBins
13181290
l.LogInformation("leaving");
13191291
}
13201292

1321-
private string GetChartLabel(SeriesTransformations seriesTransformation, string? customTransformation, string defaultLabel, SeriesAggregationOptions seriesAggregationOptions)
1322-
{
1323-
return seriesTransformation switch
1324-
{
1325-
SeriesTransformations.DayOfYearIfFrost => seriesAggregationOptions == SeriesAggregationOptions.Maximum ? "Last day of frost" : "First day of frost",
1326-
SeriesTransformations.Custom => ChartSeriesDefinition.GetFriendlyCustomTransformationLabel(customTransformation ?? "Custom transformation"),
1327-
_ => defaultLabel,
1328-
};
1329-
}
1330-
13311293
private dynamic BuildChartScales()
13321294
{
13331295
dynamic scales = new ExpandoObject();
@@ -1351,12 +1313,18 @@ private dynamic BuildChartScales()
13511313
// Build a global min/max per axis from the full source datasets, before any display range filtering.
13521314
// This ensures the y-axis range reflects the complete dataset even when ChartAllData is false.
13531315
var axisMinMax = new Dictionary<string, (double Min, double Max)>();
1316+
var axisHasBarSeries = new HashSet<string>();
13541317
foreach (var swd in ChartSeriesWithData!)
13551318
{
13561319
var cs = swd.ChartSeries!;
13571320
var uom = cs.SourceSeriesSpecifications!.First().MeasurementDefinition!.UnitOfMeasure;
13581321
var axisId = ChartLogic.GetYAxisId(cs.SeriesTransformation, cs.CustomTransformation, uom, cs.Aggregation);
13591322

1323+
if (cs.DisplayStyle == SeriesDisplayStyle.Bar)
1324+
{
1325+
axisHasBarSeries.Add(axisId);
1326+
}
1327+
13601328
var values = swd.PreProcessedDataSet!.DataRecords!
13611329
.Select(x => x.Value)
13621330
.Where(v => v.HasValue)
@@ -1390,7 +1358,7 @@ private dynamic BuildChartScales()
13901358
{
13911359
axisMinMax.TryGetValue(axisId, out var globalMinMax);
13921360
var axisRange = globalMinMax.Max - globalMinMax.Min;
1393-
var axisPadding = axisRange * 0.02;
1361+
var axisPadding = !axisHasBarSeries.Contains(axisId) ? axisRange * 0.02 : 0.0;
13941362
((IDictionary<string, object>)scales).Add(
13951363
axisId,
13961364
new

ClimateExplorer.Web.Client/Shared/LocationComponents/ChangeLocation.razor

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@
1212
<CloseButton />
1313
</ModalHeader>
1414
<ModalBody>
15-
<Div Class="mt-1"><Button Clicked="@NearMeClicked" Color="Color.Primary">Near me</Button></Div>
15+
<Div Class="mt-1 d-flex gap-4">
16+
<Button Clicked="@NearMeClicked" Color="Color.Primary">Near me</Button>
17+
<Button Clicked="@RandomLocationClicked" Color="Color.Secondary">Random location</Button>
18+
</Div>
1619

1720
@if (BrowserLocationErrorMessage != null)
1821
{
@@ -21,8 +24,6 @@
2124
</Div>
2225
}
2326

24-
<Div Class="mt-1"><Button Clicked="@RandomLocationClicked" Color="Color.Secondary">Random location</Button></Div>
25-
2627
@if (LocationDictionary is not null)
2728
{
2829
<Field Class="mt-3">

ClimateExplorer.Web.Client/Shared/LocationComponents/DriestYears.razor

Lines changed: 0 additions & 45 deletions
This file was deleted.

ClimateExplorer.Web.Client/Shared/LocationComponents/DriestYears.razor.cs

Lines changed: 0 additions & 21 deletions
This file was deleted.

0 commit comments

Comments
 (0)