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
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
59
import { drawColumnChartBasedOnSelectedAggregationOptions } from "./src_modules/dropDownListChartColumn.mjs";

import { drawHeatmapBasedOnSelectedOptions } from "./src_modules/dropDownListChartHeatmap.mjs";

import { drawLineChartBasedOnSelectedAggregationOptions } from "./src_modules/dropDownListChartLine.mjs";

import { drawScatterPlotFromChartSelection } from "./src_modules/dropDownListChartScatterPlot.mjs";
Pithon Kabiro's avatar
Pithon Kabiro committed
60

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

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

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

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

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

134
/**
135
136
137
138
139
140
 * 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
141
 */
142
const drawChartUsingSelectedOptions = async function () {
143
  try {
144
145
    const selectedOptionsAllDropDownLists =
      getSelectedOptionsFromAllDropDownLists();
146

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

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

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

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

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

171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
    // 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
      );

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

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

    // Display the loading indicator
    showLoadingSpinner();

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

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

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

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

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

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

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

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

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

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

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

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

276
277
278
279
280
281
282
283
284
285
286
287
288
289
    // 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
            )
          );
290

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

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

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

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

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

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

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