appChart.js 13.8 KB
Newer Older
Pithon Kabiro's avatar
Pithon Kabiro committed
1
"use strict";
Pithon Kabiro's avatar
Pithon Kabiro committed
2

3
4
5
6
import {
  BASE_URL,
  QUERY_PARAMS_COMBINED,
} from "./src_modules/baseUrlPlusQueryParams.mjs";
7

8
import { calculateVorlaufMinusRuecklaufTemperature } from "./src_modules/calculateTemperatureDiff.mjs";
Pithon Kabiro's avatar
Pithon Kabiro committed
9

10
11
12
13
import {
  getMetadataPlusObservationsFromSingleOrMultipleDatastreams,
  isFetchingRawMetadataPlusObservationsSuccessful,
} from "./src_modules/fetchData.mjs";
Pithon Kabiro's avatar
Pithon Kabiro committed
14

15
import {
16
17
18
  formatDatastreamMetadataForChart,
  extractPropertiesFromFormattedDatastreamMetadata,
} from "./src_modules/fetchedDataProcessing.mjs";
Pithon Kabiro's avatar
Pithon Kabiro committed
19

Pithon Kabiro's avatar
Pithon Kabiro committed
20
import {
21
22
23
  formatSensorThingsApiResponseForLineOrColumnChart,
  drawLineChartHighcharts,
} from "./src_modules/chartLine.mjs";
Pithon Kabiro's avatar
Pithon Kabiro committed
24

25
import { drawColumnChartHighcharts } from "./src_modules/chartColumn.mjs";
Pithon Kabiro's avatar
Pithon Kabiro committed
26

27
import {
28
29
  showLoadingSpinner,
  hideLoadingSpinner,
30
31
  disableDrawChartButton,
  enableDrawChartButton,
32
} from "./src_modules/loadingIndicator.mjs";
33

34
import { vanillaSelectBox } from "./thirdparty/vanillaSelectBox.mjs";
Pithon Kabiro's avatar
Pithon Kabiro committed
35

36
import {
37
  extractObservationsWithinDatesInterval,
38
  extractUniqueCalendarDatesFromTimestamp,
39
40
41
} from "./src_modules/aggregateHelpers.mjs";

import {
42
43
44
45
46
47
  splitMultipleOptionsTextDelimitedBySlash,
  getSelectedOptionsFromAllDropDownLists,
  checkIfSelectedOptionsContainTemperatureDifference,
  deleteTemperatureDifferenceOptions,
  extractTemperatureDifferenceOptions,
  extractBuildingPlusSamplingRate,
48
49
  checkIfSelectedBuildingDataPointsOptionsAreValid,
  checkIfSelectedAggregationOptionsAreValid,
50
51
  getAbbreviationsForSelectedOptionsFromAllDropDownLists,
} from "./src_modules/dropDownListHelpers.mjs";
52

53
54
55
56
57
58
import {
  drawHeatmapBasedOnSelectedOptions,
  drawScatterPlotFromChartSelection,
  drawLineChartBasedOnSelectedAggregationOptions,
  drawColumnChartBasedOnSelectedAggregationOptions,
} from "./src_modules/dropDownListProcessing.mjs";
Pithon Kabiro's avatar
Pithon Kabiro committed
59

60
/**
61
62
63
 * Use the `vanillaDropDown` library to style the buildings & data points drop down list
 *
 * @returns {undefined}
64
 */
65
66
67
68
69
70
71
72
const styleBuildingsDataPointsDropDown = function () {
  // Create our dropdown list using `vanillaSelectBox`; supports the selection of multiple options
  new vanillaSelectBox("#drop-down--bldg-data-point", {
    "disableSelectAll": true,
    "maxSelect": 5,
    "placeHolder": "--Select--",
    "search": false,
  });
73
74
75
};

/**
76
77
78
 * Use the `vanillaDropDown` library to style the aggregation type drop down list
 *
 * @returns {undefined}
79
 */
80
81
82
83
84
85
86
87
const styleAggregationDropDown = function () {
  // Create our dropdown list using `vanillaSelectBox`
  new vanillaSelectBox("#drop-down--aggregation-type", {
    "disableSelectAll": true,
    "maxSelect": 1,
    "placeHolder": "--Select--",
    "search": false,
  });
88
89
};

90
/**
91
92
93
 * Use the `vanillaDropDown` library to style the third sampling rate down list
 *
 * @returns {undefined}
94
 */
95
96
97
98
99
100
101
102
const styleSamplingRateDropDown = function () {
  // Create our dropdown list using `vanillaSelectBox`
  new vanillaSelectBox("#drop-down--sampling-rate", {
    "disableSelectAll": true,
    "maxSelect": 1,
    "placeHolder": "--Select--",
    "search": false,
  });
103
104
105
};

/**
106
107
108
 * Use the `vanillaDropDown` library to style the chart type drop down list
 *
 * @returns {undefined}
109
 */
110
111
112
113
114
115
116
117
const styleChartTypeDropDown = function () {
  // Create our dropdown list using `vanillaSelectBox`
  new vanillaSelectBox("#drop-down--chart-type", {
    "disableSelectAll": true,
    "maxSelect": 1,
    "placeHolder": "--Select--",
    "search": false,
  });
118
119
};

120
/**
121
122
123
124
 * Callback function that wraps the logic of populating the linked drop down lists.
 * Will run on `DOMContentLoaded` event
 *
 * @returns {undefined}
125
 */
126
127
128
129
130
const afterDocumentLoads = function () {
  styleBuildingsDataPointsDropDown();
  styleAggregationDropDown();
  styleSamplingRateDropDown();
  styleChartTypeDropDown();
131
132
};

133
/**
134
135
136
137
138
139
 * Callback function that draws a chart using options from the drop-down list that
 * have been selected by a user.
 * Will be run when the user clicks a button
 *
 * @async
 * @returns {undefined} undefined
140
 */
141
const drawChartUsingSelectedOptions = async function () {
142
  try {
143
144
    const selectedOptionsAllDropDownLists =
      getSelectedOptionsFromAllDropDownLists();
145

146
    // Note: The chart type amd aggregation type + duration are the first and
147
148
    // third elements respectively, we have ignored the second and fourth elements
    const [selectedChartTypeArr, , selectedAggregationTypeDurationArr] =
149
      selectedOptionsAllDropDownLists;
150

151
152
153
154
    // Create an array of aggregation type and duration
    const selectedAggregationTypeDurationSplitNestedArr =
      splitMultipleOptionsTextDelimitedBySlash(
        selectedAggregationTypeDurationArr
155
156
      );

157
158
159
    // Separate the aggregation type and the aggregation duration strings
    const [selectedAggregationTypeDurationSplitArr] =
      selectedAggregationTypeDurationSplitNestedArr;
160

161
    const [selectedAggregationType, selectedAggregationDuration] =
162
      selectedAggregationTypeDurationSplitArr;
163

164
    // Array of building(s) + data point(s) + sampling rate
165
    const selectedBuildingsDataPointsSamplingRateAbbrevNestedArr =
166
167
      getAbbreviationsForSelectedOptionsFromAllDropDownLists(
        selectedOptionsAllDropDownLists
168
169
      );

170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
    // The values of these references is either equal to `true` or an error will be thrown
    // We would like the errors to be thrown at this point before we begin performing any asynchronous tasks
    const selectedAggregationOptionsAreValid =
      checkIfSelectedAggregationOptionsAreValid(
        selectedChartTypeArr,
        selectedAggregationType,
        selectedAggregationDuration
      );

    const selectedBuildingDataPointsOptionsAreValid =
      checkIfSelectedBuildingDataPointsOptionsAreValid(
        selectedChartTypeArr,
        selectedBuildingsDataPointsSamplingRateAbbrevNestedArr
      );

185
186
    // Check whether we have dT (temperature difference), if so, delete these options,
    // then compute abbreviations for raw observations
187
    const selectedBuildingsDataPointsSamplingRateAbbrevRawObsArr =
188
      checkIfSelectedOptionsContainTemperatureDifference(
189
        selectedBuildingsDataPointsSamplingRateAbbrevNestedArr
190
      )
191
        ? deleteTemperatureDifferenceOptions(
192
            selectedBuildingsDataPointsSamplingRateAbbrevNestedArr
193
          )
194
        : selectedBuildingsDataPointsSamplingRateAbbrevNestedArr;
195

196
197
    // Check if we have dT (temperature difference)
    const selectedBuildingsDataPointsSamplingRateAbbrevTempDiffArr =
198
      checkIfSelectedOptionsContainTemperatureDifference(
199
        selectedBuildingsDataPointsSamplingRateAbbrevNestedArr
200
201
      )
        ? extractTemperatureDifferenceOptions(
202
            selectedBuildingsDataPointsSamplingRateAbbrevNestedArr
203
204
205
206
207
208
          )
        : [];

    // Display the loading indicator
    showLoadingSpinner();

209
210
211
212
213
214
    // Disable the 'draw chart' button
    disableDrawChartButton();

    // Fetch the observations + metadata / raw observations
    const observationsRawPlusMetadataArr =
      selectedBuildingsDataPointsSamplingRateAbbrevRawObsArr.length === 0
215
216
217
218
        ? [[], []]
        : await getMetadataPlusObservationsFromSingleOrMultipleDatastreams(
            BASE_URL,
            QUERY_PARAMS_COMBINED,
219
            selectedBuildingsDataPointsSamplingRateAbbrevRawObsArr
220
221
          );

222
223
224
    // Fetch the observations + metadata / temperature difference (dT)
    const observationsTempDiffPlusMetadataArr =
      selectedBuildingsDataPointsSamplingRateAbbrevTempDiffArr.length === 0
225
226
227
228
229
        ? [[], []]
        : await calculateVorlaufMinusRuecklaufTemperature(
            BASE_URL,
            QUERY_PARAMS_COMBINED,
            extractBuildingPlusSamplingRate(
230
              selectedBuildingsDataPointsSamplingRateAbbrevTempDiffArr
231
232
233
            )
          );

234
    // Extract the combined arrays for observations and metadata / raw observations
235
    const [observationsRawNestedArr, metadataRawNestedArr] =
236
      observationsRawPlusMetadataArr;
237

238
    // Extract the combined arrays for observations and metadata / temperature difference (dT)
239
    const [observationsTempDiffNestedArr, metadataTempDiffNestedArr] =
240
      observationsTempDiffPlusMetadataArr;
241
242
243

    // Create a combined array of observations and metadata
    const observationsPlusMetadataCombined = [
244
245
      [...observationsRawNestedArr, ...observationsTempDiffNestedArr],
      [...metadataRawNestedArr, ...metadataTempDiffNestedArr],
246
    ];
247

248
249
    const [observationsComboNestedArr, metadataComboNestedArr] =
      observationsPlusMetadataCombined;
250

251
252
253
    // Create formatted array(s) for metadata - used by ALL chart types
    const formattedMetadataNestedArr = metadataComboNestedArr.map(
      (metadataObj) => formatDatastreamMetadataForChart(metadataObj)
254
255
    );

256
257
    // Extract the formatted metadata properties for the raw observations - used by ALL chart types
    const rawObservationsExtractedFormattedDatastreamProperties =
258
259
      extractPropertiesFromFormattedDatastreamMetadata(
        formattedMetadataNestedArr,
260
        false
261
262
      );

263
264
    // The formatted abbreviations array is nested
    const [selectedBuildingsDataPointsSamplingRateAbbrevArr] =
265
      selectedBuildingsDataPointsSamplingRateAbbrevNestedArr;
266

267
268
269
    // Extract the formatted sampling rate string - used by ALL chart types
    const [, , selectedSamplingRateAbbrev] =
      selectedBuildingsDataPointsSamplingRateAbbrevArr;
270

271
272
273
    // User-specified start date and end date for aggregation - used by MULTIPLE chart types
    const aggregationStartDate = "2020-01-01";
    const aggregationEndDate = "2020-12-31";
274

275
276
277
278
279
280
281
282
283
284
285
286
287
288
    // Create final array of observations- used by MULTIPLE chart types
    // If we are performing aggregation, it was envisioned that the user would
    // select observations that fall within a start and end date
    const observationsComboNestedFinalArr =
      selectedAggregationType === "None (raw data)"
        ? observationsComboNestedArr
        : observationsComboNestedArr.map((observationsArr) =>
            extractObservationsWithinDatesInterval(
              observationsArr,
              selectedSamplingRateAbbrev,
              aggregationStartDate,
              aggregationEndDate
            )
          );
289

290
    // Unique calendar dates - used by MULTIPLE chart types
291
    const uniqueCalendarDatesNestedArr = observationsComboNestedFinalArr.map(
292
293
294
295
      (observationsArr) =>
        extractUniqueCalendarDatesFromTimestamp(observationsArr)
    );

296
    for (const selectedChartType of selectedChartTypeArr) {
297
298
299
300
301
302
303
304
305
306
      switch (selectedChartType) {
        // We are interested in raw observations ONLY
        case "Heatmap":
          if (
            selectedAggregationOptionsAreValid &&
            selectedBuildingDataPointsOptionsAreValid
          ) {
            drawHeatmapBasedOnSelectedOptions(
              observationsComboNestedFinalArr,
              rawObservationsExtractedFormattedDatastreamProperties
307
            );
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
          }
          break;

        // We are interested in raw observations ONLY
        case "Scatter Plot":
          if (
            selectedAggregationOptionsAreValid &&
            selectedBuildingDataPointsOptionsAreValid
          ) {
            drawScatterPlotFromChartSelection(
              observationsComboNestedFinalArr,
              rawObservationsExtractedFormattedDatastreamProperties
            );
          }
          break;

        // We are interested in BOTH raw observations and aggregated observations
        case "Line":
          // Raw observations
          if (selectedAggregationType === "None (raw data)") {
            // Create formatted array(s) for observations
            const formattedRawObservationsLineChartNestedArr =
              observationsComboNestedFinalArr.map((observationsArr) =>
                formatSensorThingsApiResponseForLineOrColumnChart(
                  observationsArr
                )
              );

            drawLineChartHighcharts(
              formattedRawObservationsLineChartNestedArr,
              rawObservationsExtractedFormattedDatastreamProperties
339
            );
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
          }
          // Aggregated observations
          else {
            drawLineChartBasedOnSelectedAggregationOptions(
              selectedAggregationType,
              selectedAggregationDuration,
              observationsComboNestedFinalArr,
              selectedSamplingRateAbbrev,
              uniqueCalendarDatesNestedArr,
              formattedMetadataNestedArr
            );
          }
          break;

        // We are interested in BOTH raw observations and aggregated observations
        case "Column":
          // Raw observations
          if (selectedAggregationType === "None (raw data)") {
            // Create formatted array(s) for observations
            const formattedRawObservationsColumnChartNestedArr =
              observationsComboNestedFinalArr.map((observationsArr) =>
                formatSensorThingsApiResponseForLineOrColumnChart(
                  observationsArr
                )
              );

            drawColumnChartHighcharts(
              formattedRawObservationsColumnChartNestedArr,
              rawObservationsExtractedFormattedDatastreamProperties
            );
          }
          // Aggregated observations
          else {
            drawColumnChartBasedOnSelectedAggregationOptions(
              selectedAggregationType,
              selectedAggregationDuration,
              observationsComboNestedFinalArr,
              selectedSamplingRateAbbrev,
              uniqueCalendarDatesNestedArr,
              formattedMetadataNestedArr
            );
          }
          break;
383

384
385
        default:
          throw new Error("None of the chart type options were selected");
386
      }
387
    }
388
389
  } catch (err) {
    console.error(err);
390
391
392

    // Display a dialog window with the error message
    alert(err);
393
  } finally {
394
395
396
    // Enable the 'draw chart' button
    enableDrawChartButton();

397
398
    // Hide the loading indicator
    hideLoadingSpinner();
399
400
401
  }
};

402
403
404
405
406
document.addEventListener("DOMContentLoaded", afterDocumentLoads);

document
  .querySelector("#btn-draw-chart")
  .addEventListener("click", drawChartUsingSelectedOptions);