Skip to content

Commit e164f46

Browse files
authored
Merge pull request #2290 from ORNL-AMO/issue-1724
Issue 1724 - allow user to select multiple predictors in weather data
2 parents 752265a + 48b109a commit e164f46

File tree

4 files changed

+197
-58
lines changed

4 files changed

+197
-58
lines changed

src/app/weather-data/weather-data.component.css

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

55
.wrapper.main-content {
66
height: auto;
7+
}
8+
9+
.predictor-row {
10+
min-height: 35px;
711
}
Lines changed: 98 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
@if (inDashboard) {
2-
<div class="upload-banner banner d-flex justify-content-between w-100">
3-
<div class="banner-title">
4-
<h3><span class="fa fa-temperature-half"></span> Weather Data</h3>
5-
</div>
2+
<div class="upload-banner banner d-flex justify-content-between w-100">
3+
<div class="banner-title">
4+
<h3><span class="fa fa-temperature-half"></span> Weather Data</h3>
65
</div>
6+
</div>
77
}
88

99
<div class="wrapper main-content">
@@ -13,20 +13,20 @@ <h4>
1313
<p>
1414
This page provides a detailed look of the weather data available for use by VERIFI. The weather data is pulled
1515
from <a href="https://www.ncei.noaa.gov/" target="_blank">The National Centers for Environmental
16-
Information</a>. If quality
17-
data is found, VERIFI can automatically calculate heating and cooling degree days, average relative humidity and
18-
average dry bulb temperature. The calculated weather data can
19-
be used as predictor data for analysis.
20-
</p>
21-
<p>
22-
For much more information on all things degree days, visit <a href="https://www.degreedays.net/"
23-
target="_blank">https://www.degreedays.net/</a>
24-
</p>
25-
<p>
26-
Average relative humidty and dry bulb temperature are calculated using a weighted average where the weights are
27-
the number of minutes that a reading covers.
28-
</p>
29-
<hr>
16+
Information</a>. If quality
17+
data is found, VERIFI can automatically calculate heating and cooling degree days, average relative humidity and
18+
average dry bulb temperature. The calculated weather data can
19+
be used as predictor data for analysis.
20+
</p>
21+
<p>
22+
For much more information on all things degree days, visit <a href="https://www.degreedays.net/"
23+
target="_blank">https://www.degreedays.net/</a>
24+
</p>
25+
<p>
26+
Average relative humidity and dry bulb temperature are calculated using a weighted average where the weights are
27+
the number of minutes that a reading covers.
28+
</p>
29+
<hr>
3030
<router-outlet></router-outlet>
3131
</div>
3232

@@ -36,49 +36,101 @@ <h4>
3636
<button class="item-right" (click)="cancelApplyToFacility()">x</button>
3737
</div>
3838
<div class="popup-body">
39-
@if (weatherDataSelection == 'HDD') {
40-
<p>Select the facility to create new heating degree day predictors using
41-
the selected weather station and temperature threshold.</p>
42-
}
43-
@if (weatherDataSelection == 'CDD') {
44-
<p>Select the facility to create new cooling degree day predictors using
45-
the selected weather station and temperature threshold.</p>
46-
}
4739
@if (weatherDataSelection == 'degreeDays') {
48-
<p>Select the facility to create new heating and cooling degree day
40+
<p>Select the facility to create new heating and cooling degree day
4941
predictors using the selected weather station and temperature thresholds.</p>
5042
}
51-
@if (weatherDataSelection == 'relativeHumidity') {
52-
<p>Select the facility to create new relative humidity
53-
predictors using the weighted average relative humidity of the selected station.</p>
54-
}
55-
@if (weatherDataSelection == 'dryBulbTemp') {
56-
<p>Select the facility to create new dry bulb temperature
57-
predictors using the weighted average dry bulb temp of the selected station.</p>
43+
@else {
44+
<p>Select the facility and one or more predictor types below to create predictors using the selected weather station
45+
and temperature thresholds.</p>
5846
}
47+
5948
<form>
60-
<select class="form-select" name="selectedFacility" [(ngModel)]="selectedFacility"
61-
(change)="setFacilityData()">
49+
<select class="form-select" name="selectedFacility" [(ngModel)]="selectedFacility" (change)="setFacilityData()">
6250
@for (facility of facilities; track facility) {
63-
<option [ngValue]="facility">{{facility.name}}</option>
51+
<option [ngValue]="facility">{{facility.name}}</option>
6452
}
6553
</select>
54+
55+
@if(selectedFacility) {
56+
<div class="d-flex flex-column mb-3 mt-3">
57+
@if(cddSelected || hddSelected) {
58+
<div class="row justify-content-end">
59+
<div class="col-6">
60+
<span class="text-muted fst-italic">Base Temperature</span>
61+
</div>
62+
</div>
63+
}
64+
<div class="row g-2 align-items-center mb-2 flex-nowrap predictor-row">
65+
<div class="col-6">
66+
<input type="checkbox" id="cddSelected" name="cddSelected" class="form-check-input"
67+
[(ngModel)]="cddSelected">
68+
<label for="cddSelected" class="fw-bold ps-2">Cooling Degree Days</label>
69+
</div>
70+
@if(cddSelected) {
71+
<div class="col-6">
72+
<div class="input-group">
73+
<input type="number" class="form-control border-primary bg-light" name="cddBaseTemp"
74+
[(ngModel)]="cddBaseTemp">
75+
<span class="input-group-text">&#8457;</span>
76+
</div>
77+
</div>
78+
}
79+
</div>
80+
81+
<div class="row g-2 align-items-center mb-2 flex-nowrap predictor-row">
82+
<div class="col-6">
83+
<input type="checkbox" id="hddSelected" name="hddSelected" class="form-check-input"
84+
[(ngModel)]="hddSelected">
85+
<label for="hddSelected" class="fw-bold ps-2">Heating Degree Days</label>
86+
</div>
87+
@if(hddSelected) {
88+
<div class="col-6">
89+
<div class="input-group">
90+
<input type="number" class="form-control border-primary bg-light" name="hddBaseTemp"
91+
[(ngModel)]="hddBaseTemp">
92+
<span class="input-group-text">&#8457;</span>
93+
</div>
94+
</div>
95+
}
96+
</div>
97+
98+
<div class="row g-2 align-items-center mb-2 flex-nowrap predictor-row">
99+
<div class="col-12">
100+
<input type="checkbox" id="relativeHumiditySelected" name="relativeHumiditySelected"
101+
class="form-check-input" [(ngModel)]="relativeHumiditySelected">
102+
<label for="relativeHumiditySelected" class="fw-bold ps-2">
103+
Relative Humidity
104+
</label>
105+
</div>
106+
</div>
107+
108+
<div class="row g-2 align-items-center mb-2 flex-nowrap predictor-row">
109+
<div class="col-12">
110+
<input type="checkbox" id="dryBulbTempSelected" name="dryBulbTempSelected" class="form-check-input"
111+
[(ngModel)]="dryBulbTempSelected">
112+
<label for="dryBulbTempSelected" class="fw-bold ps-2">
113+
Dry Bulb Temperature
114+
</label>
115+
</div>
116+
</div>
117+
</div>
118+
}
66119
</form>
67120
@if (facilityPredictorData?.length == 0 && facilityMeterData?.length != 0) {
68-
<div class="alert alert-warning">
69-
No facility predictor entries found. Entries will be created to match facility meter dates.
70-
</div>
121+
<div class="alert alert-warning">
122+
No facility predictor entries found. Entries will be created to match facility meter dates.
123+
</div>
71124
}
72125
@if (facilityMeterData?.length == 0) {
73-
<div class="alert alert-danger">
74-
No meter data found for this facility, predictors cannot be created.
75-
</div>
126+
<div class="alert alert-danger">
127+
No meter data found for this facility, predictors cannot be created.
128+
</div>
76129
}
77130
</div>
78131
<div class="saveCancel popup-footer text-end">
79132
<button class="btn btn-secondary" (click)="cancelApplyToFacility()">Cancel</button>
80-
<button class="btn action-btn" (click)="confirmCreate()"
81-
[disabled]="!selectedFacility || facilityMeterData?.length == 0">Create
82-
Predictors</button>
133+
<button class="btn action-btn" (click)="confirmCreate()" [disabled]="isButtonDisabled()">Create
134+
Predictors</button>
83135
</div>
84136
</div>

src/app/weather-data/weather-data.component.ts

Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,14 @@ export class WeatherDataComponent {
3232
facilityPredictorData: Array<IdbPredictorData>;
3333
facilityMeterData: Array<IdbUtilityMeterData>;
3434
inDashboard: boolean = false;
35+
cddSelected: boolean = false;
36+
hddSelected: boolean = false;
37+
relativeHumiditySelected: boolean = false;
38+
dryBulbTempSelected: boolean = false;
39+
cddBaseTemp: number;
40+
hddBaseTemp: number;
41+
selectedValues: Array<{ name: WeatherDataSelection, value?: number }> = [];
42+
3543
constructor(
3644
private weatherDataService: WeatherDataService,
3745
private facilityDbService: FacilitydbService,
@@ -53,6 +61,7 @@ export class WeatherDataComponent {
5361
if (this.applyToFacility) {
5462
this.weatherDataSelection = this.weatherDataService.weatherDataSelection;
5563
this.facilities = this.facilityDbService.accountFacilities.getValue();
64+
this.setWeatherDataSelection();
5665
if (this.weatherDataService.selectedFacility) {
5766
let facilityExists: IdbFacility = this.facilities.find(facility => { return facility.guid == this.weatherDataService.selectedFacility.guid });
5867
if (facilityExists) {
@@ -74,18 +83,84 @@ export class WeatherDataComponent {
7483
ngOnDestroy() {
7584
this.applyToFacilitySub.unsubscribe();
7685
}
77-
86+
7887
cancelApplyToFacility() {
7988
this.weatherDataService.applyToFacility.next(false);
8089
}
8190

91+
setWeatherDataSelection() {
92+
this.cddSelected = false;
93+
this.hddSelected = false;
94+
this.relativeHumiditySelected = false;
95+
this.dryBulbTempSelected = false;
96+
this.cddBaseTemp = undefined;
97+
this.hddBaseTemp = undefined;
98+
99+
switch (this.weatherDataSelection) {
100+
case 'degreeDays':
101+
this.cddSelected = true;
102+
this.hddSelected = true;
103+
this.cddBaseTemp = this.weatherDataService.coolingTemp;
104+
this.hddBaseTemp = this.weatherDataService.heatingTemp;
105+
break;
106+
case 'CDD':
107+
this.cddSelected = true;
108+
this.cddBaseTemp = this.weatherDataService.coolingTemp;
109+
break;
110+
case 'HDD':
111+
this.hddSelected = true;
112+
this.hddBaseTemp = this.weatherDataService.heatingTemp;
113+
break;
114+
case 'relativeHumidity':
115+
this.relativeHumiditySelected = true;
116+
break;
117+
case 'dryBulbTemp':
118+
this.dryBulbTempSelected = true;
119+
break;
120+
}
121+
}
122+
123+
isButtonDisabled(): boolean {
124+
if (!this.selectedFacility || this.facilityMeterData?.length == 0) {
125+
return true;
126+
}
127+
if (!this.cddSelected && !this.hddSelected && !this.relativeHumiditySelected && !this.dryBulbTempSelected) {
128+
return true;
129+
}
130+
if (this.cddSelected && (this.cddBaseTemp == undefined || this.cddBaseTemp == null)) {
131+
return true;
132+
}
133+
if (this.hddSelected && (this.hddBaseTemp == undefined || this.hddBaseTemp == null)) {
134+
return true;
135+
}
136+
137+
return false;
138+
}
139+
140+
setSelectedValues() {
141+
this.selectedValues = [];
142+
if (this.cddSelected) {
143+
this.selectedValues.push({ name: 'CDD', value: this.cddBaseTemp });
144+
}
145+
if (this.hddSelected) {
146+
this.selectedValues.push({ name: 'HDD', value: this.hddBaseTemp });
147+
}
148+
if (this.relativeHumiditySelected) {
149+
this.selectedValues.push({ name: 'relativeHumidity' });
150+
}
151+
if (this.dryBulbTempSelected) {
152+
this.selectedValues.push({ name: 'dryBulbTemp' });
153+
}
154+
}
155+
82156
async confirmCreate() {
157+
this.setSelectedValues();
83158
//Create weather data predictors and data for selected facility.
84159
this.analyticsService.sendEvent('weather_data_predictors');
85160
this.weatherDataService.applyToFacility.next(false);
86161
this.loadingService.setLoadingMessage('Adding Predictors...');
87162
this.loadingService.setLoadingStatus(true);
88-
let results: "success" | "error" = await this.weatherPredictorManagementService.createPredictorsFromWeatherDataPage(this.selectedFacility);
163+
let results: "success" | "error" = await this.weatherPredictorManagementService.createPredictorsFromWeatherDataPage(this.selectedFacility, this.selectedValues);
89164

90165
// let selectedAccount: IdbAccount = this.accountDbService.selectedAccount.getValue();
91166
// let hddPredictor: IdbPredictor;

src/app/weather-data/weather-predictor-management.service.ts

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ import { Month, Months } from '../shared/form-data/months';
3232
export class WeatherPredictorManagementService {
3333

3434
hasWarning: boolean = false;
35+
heatingTemp: number;
36+
coolingTemp: number;
3537

3638
constructor(private accountDbService: AccountdbService,
3739
private weatherDataService: WeatherDataService,
@@ -46,42 +48,48 @@ export class WeatherPredictorManagementService {
4648
) {
4749
}
4850

49-
async createPredictorsFromWeatherDataPage(selectedFacility: IdbFacility): Promise<"success" | "error"> {
51+
async createPredictorsFromWeatherDataPage(selectedFacility: IdbFacility, selectedValues: Array<{ name: WeatherDataSelection, value?: number }>): Promise<"success" | "error"> {
5052
let selectedAccount: IdbAccount = this.accountDbService.selectedAccount.getValue();
5153
let hddPredictor: IdbPredictor;
5254
let cddPredictor: IdbPredictor;
5355
let relativeHumidityPredictor: IdbPredictor;
5456
let dryBulbTempPredictor: IdbPredictor;
55-
if (this.weatherDataService.weatherDataSelection == 'HDD' || this.weatherDataService.weatherDataSelection == 'degreeDays') {
57+
if (selectedValues.find(val => val.name == 'HDD')) {
5658
//create HDD predictor
5759
hddPredictor = getNewIdbPredictor(selectedFacility.accountId, selectedFacility.guid);
58-
hddPredictor.name = 'HDD Generated ' + '(' + this.weatherDataService.heatingTemp + "F)";
60+
let hddValue: number = selectedValues.find(val => val.name == 'HDD').value;
61+
this.heatingTemp = hddValue;
62+
63+
hddPredictor.name = 'HDD Generated ' + '(' + this.heatingTemp + "F)";
64+
hddPredictor.heatingBaseTemperature = this.heatingTemp;
5965
hddPredictor.predictorType = 'Weather';
6066
hddPredictor.weatherDataType = 'HDD';
6167
hddPredictor.weatherStationName = this.weatherDataService.selectedStation.name;
62-
hddPredictor.heatingBaseTemperature = this.weatherDataService.heatingTemp;
6368
hddPredictor.weatherStationId = this.weatherDataService.selectedStation.ID;
6469
await firstValueFrom(this.predictorDbService.addWithObservable(hddPredictor));
6570
//add predictor to analysis
6671
await this.analysisDbService.addAnalysisPredictor(hddPredictor);
6772

6873
}
6974

70-
if (this.weatherDataService.weatherDataSelection == 'CDD' || this.weatherDataService.weatherDataSelection == 'degreeDays') {
75+
if (selectedValues.find(val => val.name == 'CDD')) {
7176
//create CDD predictor
7277
cddPredictor = getNewIdbPredictor(selectedFacility.accountId, selectedFacility.guid);
73-
cddPredictor.name = 'CDD Generated ' + '(' + this.weatherDataService.coolingTemp + "F)";
78+
let cddValue: number = selectedValues.find(val => val.name == 'CDD').value;
79+
this.coolingTemp = cddValue;
80+
81+
cddPredictor.name = 'CDD Generated ' + '(' + this.coolingTemp + "F)";
82+
cddPredictor.coolingBaseTemperature = this.coolingTemp;
7483
cddPredictor.predictorType = 'Weather';
7584
cddPredictor.weatherDataType = 'CDD';
7685
cddPredictor.weatherStationName = this.weatherDataService.selectedStation.name;
77-
cddPredictor.coolingBaseTemperature = this.weatherDataService.coolingTemp;
7886
cddPredictor.weatherStationId = this.weatherDataService.selectedStation.ID;
7987
await firstValueFrom(this.predictorDbService.addWithObservable(cddPredictor));
8088
//add predictor to analysis
8189
await this.analysisDbService.addAnalysisPredictor(cddPredictor);
8290
}
8391

84-
if (this.weatherDataService.weatherDataSelection == 'relativeHumidity') {
92+
if (selectedValues.find(val => val.name == 'relativeHumidity')) {
8593
//create relative humidity predictor
8694
relativeHumidityPredictor = getNewIdbPredictor(selectedFacility.accountId, selectedFacility.guid);
8795
relativeHumidityPredictor.name = "Relative Humidity";
@@ -94,7 +102,7 @@ export class WeatherPredictorManagementService {
94102
await this.analysisDbService.addAnalysisPredictor(relativeHumidityPredictor);
95103
}
96104

97-
if (this.weatherDataService.weatherDataSelection == 'dryBulbTemp') {
105+
if (selectedValues.find(val => val.name == 'dryBulbTemp')) {
98106
//create dry bulb temp predictor
99107
dryBulbTempPredictor = getNewIdbPredictor(selectedFacility.accountId, selectedFacility.guid);
100108
dryBulbTempPredictor.name = "Dry Bulb Temp";
@@ -131,7 +139,7 @@ export class WeatherPredictorManagementService {
131139
this.loadingService.setLoadingMessage('Calculating Predictors: ' + dateStr + ' ...');
132140

133141
//ISSUE: 1822
134-
let degreeDays: Array<DetailDegreeDay> = await getDetailedDataForMonth(weatherData, entryDate.getMonth(), entryDate.getFullYear(), this.weatherDataService.heatingTemp, this.weatherDataService.coolingTemp, this.weatherDataService.selectedStation.ID, this.weatherDataService.selectedStation.name)
142+
let degreeDays: Array<DetailDegreeDay> = await getDetailedDataForMonth(weatherData, entryDate.getMonth(), entryDate.getFullYear(), this.heatingTemp, this.coolingTemp, this.weatherDataService.selectedStation.ID, this.weatherDataService.selectedStation.name)
135143
// let degreeDays: Array<DetailDegreeDay> = await this.degreeDaysService.getDetailedDataForMonth(entryDate.getMonth(), this.weatherDataService.heatingTemp, this.weatherDataService.coolingTemp)
136144
let hasErrors: DetailDegreeDay = degreeDays.find(degreeDay => {
137145
return degreeDay.gapInData == true

0 commit comments

Comments
 (0)