appChart.js 13.9 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
import { getMetadataPlusObservationsFromSingleOrMultipleDatastreams } from "./src_modules/fetchData.mjs";
Pithon Kabiro's avatar
Pithon Kabiro committed
11

12
import {
13
14
15
  formatDatastreamMetadataForChart,
  extractPropertiesFromFormattedDatastreamMetadata,
} from "./src_modules/fetchedDataProcessing.mjs";
Pithon Kabiro's avatar
Pithon Kabiro committed
16

Pithon Kabiro's avatar
Pithon Kabiro committed
17
import {
18
19
20
  formatSensorThingsApiResponseForLineOrColumnChart,
  drawLineChartHighcharts,
} from "./src_modules/chartLine.mjs";
Pithon Kabiro's avatar
Pithon Kabiro committed
21

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

24
import {
25
26
  showLoadingSpinner,
  hideLoadingSpinner,
27
28
  disableDrawChartButton,
  enableDrawChartButton,
29
} from "./src_modules/loadingIndicator.mjs";
30

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

33
import {
34
  extractObservationsWithinDatesInterval,
35
  extractUniqueCalendarDatesFromTimestamp,
36
37
38
} from "./src_modules/aggregateHelpers.mjs";

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

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

57
/**
58
59
60
 * Use the `vanillaDropDown` library to style the buildings & data points drop down list
 *
 * @returns {undefined}
61
 */
62
63
64
65
66
67
68
69
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,
  });
70
71
72
};

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

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

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

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

130
/**
131
132
133
134
135
136
 * 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
137
 */
138
const drawChartUsingSelectedOptions = async function () {
139
  try {
140
141
    const selectedOptionsAllDropDownLists =
      getSelectedOptionsFromAllDropDownLists();
142

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

148
149
150
151
    // Create an array of aggregation type and duration
    const selectedAggregationTypeDurationSplitNestedArr =
      splitMultipleOptionsTextDelimitedBySlash(
        selectedAggregationTypeDurationArr
152
153
      );

154
155
156
    // Separate the aggregation type and the aggregation duration strings
    const [selectedAggregationTypeDurationSplitArr] =
      selectedAggregationTypeDurationSplitNestedArr;
157

158
    const [selectedAggregationType, selectedAggregationDuration] =
159
      selectedAggregationTypeDurationSplitArr;
160

161
    // Array of building(s) + data point(s) + sampling rate
162
    const selectedBuildingsDataPointsSamplingRateAbbrevNestedArr =
163
164
      getAbbreviationsForSelectedOptionsFromAllDropDownLists(
        selectedOptionsAllDropDownLists
165
166
      );

167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
    // 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
      );

182
    // Create copies of the arrays of building(s) + data point(s) + sampling rate
183
    const selectedBuildingsDataPointsSamplingRateAbbrevRawObsCopyArr = [
184
      ...selectedBuildingsDataPointsSamplingRateAbbrevNestedArr,
185
186
    ];

187
    const selectedBuildingsDataPointsSamplingRateAbbrevTempDiffCopyArr = [
188
      ...selectedBuildingsDataPointsSamplingRateAbbrevNestedArr,
189
190
    ];

191
192
    // Check if we have raw observations
    const selectedBuildingsDataPointsSamplingRateAbbrevRawObsArr =
193
      checkIfSelectedOptionsContainTemperatureDifference(
194
        selectedBuildingsDataPointsSamplingRateAbbrevRawObsCopyArr
195
      )
196
        ? deleteTemperatureDifferenceOptions(
197
            selectedBuildingsDataPointsSamplingRateAbbrevRawObsCopyArr
198
          )
199
        : selectedBuildingsDataPointsSamplingRateAbbrevRawObsCopyArr;
200

201
202
    // Check if we have dT (temperature difference)
    const selectedBuildingsDataPointsSamplingRateAbbrevTempDiffArr =
203
      checkIfSelectedOptionsContainTemperatureDifference(
204
        selectedBuildingsDataPointsSamplingRateAbbrevTempDiffCopyArr
205
206
      )
        ? extractTemperatureDifferenceOptions(
207
            selectedBuildingsDataPointsSamplingRateAbbrevTempDiffCopyArr
208
209
210
211
212
213
          )
        : [];

    // Display the loading indicator
    showLoadingSpinner();

214
215
216
217
218
219
    // Disable the 'draw chart' button
    disableDrawChartButton();

    // Fetch the observations + metadata / raw observations
    const observationsRawPlusMetadataArr =
      selectedBuildingsDataPointsSamplingRateAbbrevRawObsArr.length === 0
220
221
222
223
        ? [[], []]
        : await getMetadataPlusObservationsFromSingleOrMultipleDatastreams(
            BASE_URL,
            QUERY_PARAMS_COMBINED,
224
            selectedBuildingsDataPointsSamplingRateAbbrevRawObsArr
225
226
          );

227
228
229
    // Fetch the observations + metadata / temperature difference (dT)
    const observationsTempDiffPlusMetadataArr =
      selectedBuildingsDataPointsSamplingRateAbbrevTempDiffArr.length === 0
230
231
232
233
234
        ? [[], []]
        : await calculateVorlaufMinusRuecklaufTemperature(
            BASE_URL,
            QUERY_PARAMS_COMBINED,
            extractBuildingPlusSamplingRate(
235
              selectedBuildingsDataPointsSamplingRateAbbrevTempDiffArr
236
237
238
            )
          );

239
240
241
    // Extract the combined arrays for observations and metadata / raw observations
    const [observationsRawNestedArr, metadataTempDiffNestedArr] =
      observationsRawPlusMetadataArr;
242

243
    // Extract the combined arrays for observations and metadata / temperature difference (dT)
244
    const [observationsNestedComputedArr, metadataNestedComputedArr] =
245
      observationsTempDiffPlusMetadataArr;
246
247
248

    // Create a combined array of observations and metadata
    const observationsPlusMetadataCombined = [
249
250
      [...observationsRawNestedArr, ...observationsNestedComputedArr],
      [...metadataTempDiffNestedArr, ...metadataNestedComputedArr],
251
    ];
252

253
254
    const [observationsComboNestedArr, metadataComboNestedArr] =
      observationsPlusMetadataCombined;
255

256
257
258
    // Create formatted array(s) for metadata - used by ALL chart types
    const formattedMetadataNestedArr = metadataComboNestedArr.map(
      (metadataObj) => formatDatastreamMetadataForChart(metadataObj)
259
260
    );

261
262
    // Extract the formatted metadata properties for the raw observations - used by ALL chart types
    const rawObservationsExtractedFormattedDatastreamProperties =
263
264
      extractPropertiesFromFormattedDatastreamMetadata(
        formattedMetadataNestedArr,
265
        false
266
267
      );

268
269
    // The formatted abbreviations array is nested
    const [selectedBuildingsDataPointsSamplingRateAbbrevArr] =
270
      selectedBuildingsDataPointsSamplingRateAbbrevNestedArr;
271

272
273
274
    // Extract the formatted sampling rate string - used by ALL chart types
    const [, , selectedSamplingRateAbbrev] =
      selectedBuildingsDataPointsSamplingRateAbbrevArr;
275

276
277
278
    // User-specified start date and end date for aggregation - used by MULTIPLE chart types
    const aggregationStartDate = "2020-01-01";
    const aggregationEndDate = "2020-12-31";
279

280
281
282
283
284
285
286
287
288
289
290
291
292
293
    // 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
            )
          );
294

295
    // Unique calendar dates - used by MULTIPLE chart types
296
    const uniqueCalendarDatesNestedArr = observationsComboNestedFinalArr.map(
297
298
299
300
      (observationsArr) =>
        extractUniqueCalendarDatesFromTimestamp(observationsArr)
    );

301
    for (const selectedChartType of selectedChartTypeArr) {
302
303
304
305
306
307
308
309
310
311
      switch (selectedChartType) {
        // We are interested in raw observations ONLY
        case "Heatmap":
          if (
            selectedAggregationOptionsAreValid &&
            selectedBuildingDataPointsOptionsAreValid
          ) {
            drawHeatmapBasedOnSelectedOptions(
              observationsComboNestedFinalArr,
              rawObservationsExtractedFormattedDatastreamProperties
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
339
340
341
342
343
          }
          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
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
383
384
385
386
387
          }
          // 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;
388

389
390
        default:
          throw new Error("None of the chart type options were selected");
391
      }
392
    }
393
394
  } catch (err) {
    console.error(err);
395
  } finally {
396
397
398
    // Enable the 'draw chart' button
    enableDrawChartButton();

399
400
    // Hide the loading indicator
    hideLoadingSpinner();
401
402
403
  }
};

404
405
406
407
408
document.addEventListener("DOMContentLoaded", afterDocumentLoads);

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