appChart.js 13.6 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
183
    // Check whether we have dT (temperature difference), if so, delete these options,
    // then compute abbreviations for raw observations
184
    const selectedBuildingsDataPointsSamplingRateAbbrevRawObsArr =
185
      checkIfSelectedOptionsContainTemperatureDifference(
186
        selectedBuildingsDataPointsSamplingRateAbbrevNestedArr
187
      )
188
        ? deleteTemperatureDifferenceOptions(
189
            selectedBuildingsDataPointsSamplingRateAbbrevNestedArr
190
          )
191
        : selectedBuildingsDataPointsSamplingRateAbbrevNestedArr;
192

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

    // Display the loading indicator
    showLoadingSpinner();

206
207
208
209
210
211
    // Disable the 'draw chart' button
    disableDrawChartButton();

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

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

231
232
233
    // Extract the combined arrays for observations and metadata / raw observations
    const [observationsRawNestedArr, metadataTempDiffNestedArr] =
      observationsRawPlusMetadataArr;
234

235
    // Extract the combined arrays for observations and metadata / temperature difference (dT)
236
    const [observationsNestedComputedArr, metadataNestedComputedArr] =
237
      observationsTempDiffPlusMetadataArr;
238
239
240

    // Create a combined array of observations and metadata
    const observationsPlusMetadataCombined = [
241
242
      [...observationsRawNestedArr, ...observationsNestedComputedArr],
      [...metadataTempDiffNestedArr, ...metadataNestedComputedArr],
243
    ];
244

245
246
    const [observationsComboNestedArr, metadataComboNestedArr] =
      observationsPlusMetadataCombined;
247

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

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

260
261
    // The formatted abbreviations array is nested
    const [selectedBuildingsDataPointsSamplingRateAbbrevArr] =
262
      selectedBuildingsDataPointsSamplingRateAbbrevNestedArr;
263

264
265
266
    // Extract the formatted sampling rate string - used by ALL chart types
    const [, , selectedSamplingRateAbbrev] =
      selectedBuildingsDataPointsSamplingRateAbbrevArr;
267

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

272
273
274
275
276
277
278
279
280
281
282
283
284
285
    // 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
            )
          );
286

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

293
    for (const selectedChartType of selectedChartTypeArr) {
294
295
296
297
298
299
300
301
302
303
      switch (selectedChartType) {
        // We are interested in raw observations ONLY
        case "Heatmap":
          if (
            selectedAggregationOptionsAreValid &&
            selectedBuildingDataPointsOptionsAreValid
          ) {
            drawHeatmapBasedOnSelectedOptions(
              observationsComboNestedFinalArr,
              rawObservationsExtractedFormattedDatastreamProperties
304
            );
305
306
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
          }
          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
336
            );
337
338
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
          }
          // 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;
380

381
382
        default:
          throw new Error("None of the chart type options were selected");
383
      }
384
    }
385
386
  } catch (err) {
    console.error(err);
387
  } finally {
388
389
390
    // Enable the 'draw chart' button
    enableDrawChartButton();

391
392
    // Hide the loading indicator
    hideLoadingSpinner();
393
394
395
  }
};

396
397
398
399
400
document.addEventListener("DOMContentLoaded", afterDocumentLoads);

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