appChart.js 13.1 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
27
  showLoadingSpinner,
  hideLoadingSpinner,
} from "./src_modules/loadingIndicator.mjs";
28

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

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

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

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

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

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

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

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

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

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

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

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

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

156
    const [selectedAggregationType, selectedAggregationDuration] =
157
      selectedAggregationTypeDurationSplitArr;
158

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

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

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

185
186
    const selectedBuildingsDataPointsSamplingRateAbbrevComputedCopyArr = [
      ...selectedBuildingsDataPointsSamplingRateAbbrevNestedArr,
187
188
    ];

189
    // Check if we have non-computed
190
    const selectedBuildingsDataPointsSamplingRateAbbrevNonComputedArr =
191
      checkIfSelectedOptionsContainTemperatureDifference(
192
        selectedBuildingsDataPointsSamplingRateAbbrevNonComputedCopyArr
193
      )
194
        ? deleteTemperatureDifferenceOptions(
195
            selectedBuildingsDataPointsSamplingRateAbbrevNonComputedCopyArr
196
          )
197
        : selectedBuildingsDataPointsSamplingRateAbbrevNonComputedCopyArr;
198
199

    // Check if we have computed / dT
200
    const selectedBuildingsDataPointsSamplingRateAbbrevComputedArr =
201
      checkIfSelectedOptionsContainTemperatureDifference(
202
        selectedBuildingsDataPointsSamplingRateAbbrevComputedCopyArr
203
204
      )
        ? extractTemperatureDifferenceOptions(
205
            selectedBuildingsDataPointsSamplingRateAbbrevComputedCopyArr
206
207
208
209
210
211
212
          )
        : [];

    // Display the loading indicator
    showLoadingSpinner();

    // Fetch the observations + metadata / non-computed
213
214
    const observationsPlusMetadataNonComputedArr =
      selectedBuildingsDataPointsSamplingRateAbbrevNonComputedArr.length === 0
215
216
217
218
        ? [[], []]
        : await getMetadataPlusObservationsFromSingleOrMultipleDatastreams(
            BASE_URL,
            QUERY_PARAMS_COMBINED,
219
            selectedBuildingsDataPointsSamplingRateAbbrevNonComputedArr
220
221
222
          );

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

    // Extract the combined arrays for observations and metadata / non-computed
    const [observationsNestedNonComputedArr, metadataNestedNonComputedArr] =
236
      observationsPlusMetadataNonComputedArr;
237
238
239

    // Extract the combined arrays for observations and metadata / computed (dT)
    const [observationsNestedComputedArr, metadataNestedComputedArr] =
240
      observationsPlusMetadataComputedArr;
241
242
243
244
245
246

    // Create a combined array of observations and metadata
    const observationsPlusMetadataCombined = [
      [...observationsNestedNonComputedArr, ...observationsNestedComputedArr],
      [...metadataNestedNonComputedArr, ...metadataNestedComputedArr],
    ];
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
    // Extract the formatted metadata properties - used by ALL chart types
257
258
259
    const extractedFormattedDatastreamProperties =
      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
    // Extract observations within the user-specified start and end date - used by MULTIPLE chart types
    const observationsAggregationNestedArr = observationsComboNestedArr.map(
      (obsArr) =>
        extractObservationsWithinDatesInterval(
          obsArr,
          "60min",
          aggregationStartDate,
          aggregationEndDate
        )
284
285
    );

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

292
    for (const selectedChartType of selectedChartTypeArr) {
293
294
295
      if (selectedChartType === "Heatmap") {
        // We are interested in raw observations
        if (
296
297
          selectedAggregationOptionsAreValid &&
          selectedBuildingDataPointsOptionsAreValid
298
299
300
301
302
303
304
        ) {
          drawHeatmapBasedOnSelectedOptions(
            observationsComboNestedArr,
            extractedFormattedDatastreamProperties
          );
        }
      }
305

306
307
308
      if (selectedChartType === "Scatter Plot") {
        // We are interested in raw observations
        if (
309
310
          selectedAggregationOptionsAreValid &&
          selectedBuildingDataPointsOptionsAreValid
311
312
313
314
315
316
317
        ) {
          drawScatterPlotFromChartSelection(
            observationsComboNestedArr,
            extractedFormattedDatastreamProperties
          );
        }
      }
318

319
320
321
322
      if (selectedChartType === "Line") {
        // We are interested in raw observations or aggregated observations

        // Raw observations
323
        if (selectedAggregationType === "None (raw data)") {
324
325
326
327
328
329
330
331
332
333
334
335
336
337
          // Create formatted array(s) for observations
          const formattedRawObservationsLineChartNestedArr =
            observationsComboNestedArr.map((observationsArr) =>
              formatSensorThingsApiResponseForLineOrColumnChart(observationsArr)
            );

          drawLineChartHighcharts(
            formattedRawObservationsLineChartNestedArr,
            extractedFormattedDatastreamProperties
          );
        }
        // Aggregated observations
        else {
          drawLineChartBasedOnSelectedAggregationOptions(
338
            selectedAggregationType,
339
340
341
342
343
344
345
346
            selectedAggregationDuration,
            observationsAggregationNestedArr,
            selectedSamplingRateAbbrev,
            uniqueCalendarDatesNestedArr,
            formattedMetadataNestedArr
          );
        }
      }
347

348
349
350
351
      if (selectedChartType === "Column") {
        // We are interested in raw observations or aggregated observations

        // Raw observations
352
        if (selectedAggregationType === "None (raw data)") {
353
354
355
356
357
358
359
360
361
362
363
364
365
366
          // Create formatted array(s) for observations
          const formattedRawObservationsColumnChartNestedArr =
            observationsComboNestedArr.map((observationsArr) =>
              formatSensorThingsApiResponseForLineOrColumnChart(observationsArr)
            );

          drawColumnChartHighcharts(
            formattedRawObservationsColumnChartNestedArr,
            extractedFormattedDatastreamProperties
          );
        }
        // Aggregated observations
        else {
          drawColumnChartBasedOnSelectedAggregationOptions(
367
            selectedAggregationType,
368
369
370
371
372
373
374
375
            selectedAggregationDuration,
            observationsAggregationNestedArr,
            selectedSamplingRateAbbrev,
            uniqueCalendarDatesNestedArr,
            formattedMetadataNestedArr
          );
        }
      }
376
    }
377
378
  } catch (err) {
    console.error(err);
379
380
381
  } finally {
    // Hide the loading indicator
    hideLoadingSpinner();
382
383
384
  }
};

385
386
387
388
389
document.addEventListener("DOMContentLoaded", afterDocumentLoads);

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