chartColumn.mjs 6.59 KB
Newer Older
1
2
"use strict";

Pithon Kabiro's avatar
Pithon Kabiro committed
3
4
import {
  chartExportOptions,
5
  createFullTitleForLineOrColumnChart,
6
  createCombinedTextDelimitedByComma,
7
  createSubtitleForChart,
8
  abbreviateTemperaturePhenomenonNames,
9
  createTooltipDateString,
Pithon Kabiro's avatar
Pithon Kabiro committed
10
11
} from "./chartHelpers.mjs";

Pithon Kabiro's avatar
Pithon Kabiro committed
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/**
 * Format a computed aggregation result to make it suitable for a column chart
 * @param {Array} calendarDatesMonthsStrArr An array of unique calendar dates strings (in "YYYY-MM-DD" fromat) or unique calendar months strings (in "YYYY-MM" format)
 * @param {Array} aggregatedValuesArr An array of aggregated values
 * @returns {Array} An array of formatted aggregation values suitable for use in a column chart
 */
const formatAggregationResultForColumnChart = function (
  calendarDatesMonthsStrArr,
  aggregatedValuesArr
) {
  if (!calendarDatesMonthsStrArr || !aggregatedValuesArr) return;

  // Create an array of Unix timestamp strings
  const timestampsArr = calendarDatesMonthsStrArr.map((calendarStr) =>
    new Date(calendarStr).getTime()
  );

  // Combine timestamp and value pairs
  // The timestamps array and values array have same lengths, use one for looping
  return timestampsArr.map((timestamp, i) => [
    timestamp,
    aggregatedValuesArr[i],
  ]);
};

/**
 * Creates an options object for each series drawn in a column chart
 * @param {Array} formattedAggregatedResultForColumnChart An array of formatted aggregated result array(s) from one or more datastreams
Pithon Kabiro's avatar
Pithon Kabiro committed
40
 * @param {Array} buildingIdsPhenomenonNamesArr An array of string(s) made up of building ID(s) + phenomenon name(s)
Pithon Kabiro's avatar
Pithon Kabiro committed
41
42
43
44
 * @returns {Array} An array made up of series options object(s)
 */
const createSeriesOptionsForColumnChart = function (
  formattedAggregatedResultForColumnChart,
Pithon Kabiro's avatar
Pithon Kabiro committed
45
  buildingIdsPhenomenonNamesArr
Pithon Kabiro's avatar
Pithon Kabiro committed
46
47
48
49
) {
  // Create an array of seriesOptions objects
  // Assumes that the observation array of arrays, phenomenon names array and phenomenon symbols array are of equal length
  // Use one of the arrays for looping
50
  if (
Pithon Kabiro's avatar
Pithon Kabiro committed
51
52
    formattedAggregatedResultForColumnChart.length !==
    buildingIdsPhenomenonNamesArr.length
53
  ) {
54
55
56
    throw new Error(
      "The observations array and phenomenon names array have different lengths"
    );
57
58
59
60
61
62
63
64
65
66
67
  } else {
    return formattedAggregatedResultForColumnChart.map(
      (formattedAggResArray, i) => {
        return {
          name: `${buildingIdsPhenomenonNamesArr[i]}`,
          data: formattedAggResArray,
          turboThreshold: Number.MAX_VALUE, // #3404, remove after 4.0.5 release
        };
      }
    );
  }
Pithon Kabiro's avatar
Pithon Kabiro committed
68
69
};

70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
/**
 * Create string for the y-axis title of a column chart
 *
 * @param {Array} phenomenonNamesArr Array of phenomenon name strings
 * @param {Array} unitOfMeasurementSymbolsArr Array of unit of measurement symbol strings
 * @returns {String} Y-axis title string for column chart
 */
const createYAxisTitleTextColumnChart = function (
  phenomenonNamesArr,
  unitOfMeasurementSymbolsArr
) {
  // The phenomenon names and unit of measurement arrays should have equal lengths
  // Use one of the arrays for looping
  if (phenomenonNamesArr.length !== unitOfMeasurementSymbolsArr.length) {
    throw new Error(
      "The phenomenon names array and unit of measurement symbols array have different lengths"
    );
  } else {
    const combinedNameSymbolArr = phenomenonNamesArr.map(
      (phenomenonName, i) =>
        `${phenomenonName} [${unitOfMeasurementSymbolsArr[i]}]`
    );

    return createCombinedTextDelimitedByComma(combinedNameSymbolArr);
  }
};

Pithon Kabiro's avatar
Pithon Kabiro committed
97
98
99
100
/**
 * Draw a column chart using Highcharts library
 * @param {Array} formattedAggResultArraysForColumnChart An array made up of formatted aggregated result array(s) suitable for use in a column chart
 * @param {Object} extractedFormattedDatastreamProperties An object that contains arrays of formatted Datastream properties
101
 * @returns {undefined} undefined
Pithon Kabiro's avatar
Pithon Kabiro committed
102
103
104
105
106
 */
const drawColumnChartHighcharts = function (
  formattedAggResultArraysForColumnChart,
  extractedFormattedDatastreamProperties
) {
107
108
109
  // Formatted datastream properties
  let datastreamNamesArr,
    phenomenonNamesArr,
Pithon Kabiro's avatar
Pithon Kabiro committed
110
    buildingIdsPhenomenonNamesArr,
Pithon Kabiro's avatar
Pithon Kabiro committed
111
    unitOfMeasurementSymbolsArr,
Pithon Kabiro's avatar
Pithon Kabiro committed
112
    aggregationInterval,
113
114
115
    aggregationType;

  // Check whether the datastream properties are for aggregated observations
116
  // Case 1: No aggregation
117
118
119
120
121
122
123
  if (extractedFormattedDatastreamProperties?.aggregationType === undefined) {
    ({
      datastreamNamesArr,
      phenomenonNamesArr,
      buildingIdsPhenomenonNamesArr,
      unitOfMeasurementSymbolsArr,
    } = extractedFormattedDatastreamProperties);
124
125
126
  }
  // Case 2: Aggregation
  else {
127
128
129
130
131
132
133
134
135
    ({
      datastreamNamesArr,
      phenomenonNamesArr,
      buildingIdsPhenomenonNamesArr,
      unitOfMeasurementSymbolsArr,
      aggregationInterval,
      aggregationType,
    } = extractedFormattedDatastreamProperties);
  }
Pithon Kabiro's avatar
Pithon Kabiro committed
136
137

  // Create the array of series options object(s)
Pithon Kabiro's avatar
Pithon Kabiro committed
138
139
  const seriesOptionsArr = createSeriesOptionsForColumnChart(
    formattedAggResultArraysForColumnChart,
Pithon Kabiro's avatar
Pithon Kabiro committed
140
141
142
    buildingIdsPhenomenonNamesArr
  );

143
  const textChartTitle = createFullTitleForLineOrColumnChart(
Pithon Kabiro's avatar
Pithon Kabiro committed
144
    phenomenonNamesArr,
Pithon Kabiro's avatar
Pithon Kabiro committed
145
146
    aggregationInterval,
    aggregationType
Pithon Kabiro's avatar
Pithon Kabiro committed
147
148
  );

149
  const textChartSubtitle = createSubtitleForChart(datastreamNamesArr);
Pithon Kabiro's avatar
Pithon Kabiro committed
150

151
  const textYAxisTitle = createYAxisTitleTextColumnChart(
152
    abbreviateTemperaturePhenomenonNames(phenomenonNamesArr),
153
154
155
    unitOfMeasurementSymbolsArr
  );

Pithon Kabiro's avatar
Pithon Kabiro committed
156
157
158
159
160
161
162
  Highcharts.chart("chart-column", {
    chart: {
      type: "column",
      zoomType: "x",
    },

    title: {
Pithon Kabiro's avatar
Pithon Kabiro committed
163
      text: textChartTitle,
164
      "align": "center",
Pithon Kabiro's avatar
Pithon Kabiro committed
165
166
167
    },

    subtitle: {
Pithon Kabiro's avatar
Pithon Kabiro committed
168
      text: textChartSubtitle,
169
      "align": "center",
Pithon Kabiro's avatar
Pithon Kabiro committed
170
171
172
173
174
175
176
177
178
    },

    xAxis: {
      type: "datetime",
      crosshair: true,
    },

    yAxis: {
      title: {
179
        text: textYAxisTitle,
Pithon Kabiro's avatar
Pithon Kabiro committed
180
181
182
183
      },
    },

    tooltip: {
Pithon Kabiro's avatar
Pithon Kabiro committed
184
185
186
187
188
      formatter() {
        // Our tooltip is shared
        // this.x -- common for all points
        // this.points -- an array containing properties for each series
        // Note that our `reduce` method is in this format:
189
        // ((previousValue, currentValue, currentIndex) => {...}, initialValue)
Pithon Kabiro's avatar
Pithon Kabiro committed
190
        return this.points.reduce(
191
192
          (previousValue, point, i) =>
            `${previousValue} <br/> <span style="color:${point.color}">${
Pithon Kabiro's avatar
Pithon Kabiro committed
193
194
195
196
              point.series.name
            }</span>: <b>${point.y.toFixed(2)} ${
              unitOfMeasurementSymbolsArr[i]
            }</b>`,
197
          `${createTooltipDateString(this.x, aggregationInterval)}`
Pithon Kabiro's avatar
Pithon Kabiro committed
198
199
        );
      },
Pithon Kabiro's avatar
Pithon Kabiro committed
200
201
202
203
204
205
206
207
208
209
      shared: true,
    },

    plotOptions: {
      column: {
        pointPadding: 0.2,
        borderWidth: 0,
      },
    },

210
211
    exporting: chartExportOptions,

Pithon Kabiro's avatar
Pithon Kabiro committed
212
213
214
215
216
    series: seriesOptionsArr,
  });
};

export { formatAggregationResultForColumnChart, drawColumnChartHighcharts };