chartLine.mjs 5.41 KB
Newer Older
1
2
"use strict";

3
4
import {
  chartExportOptions,
5
  createFullTitleForLineOrColumnChart,
6
  createSubtitleForChart,
7
  createTooltipDateString,
8
} from "./chartHelpers.mjs";
9

10
/**
11
 * Format the response from SensorThings API to make it suitable for use in a line chart or column chart
12
13
14
 * @param {Array} obsArray Array of observations (timestamp + value) that is response from SensorThings API
 * @returns {Array} Array of formatted observations suitable for use in a line chart
 */
15
const formatSensorThingsApiResponseForLineOrColumnChart = function (obsArray) {
16
17
  if (!obsArray) return;

Pithon Kabiro's avatar
Pithon Kabiro committed
18
  return obsArray.map((result) => {
19
20
21
22
23
24
25
26
    const timestampObs = new Date(result[0].slice(0, -1)).getTime(); // slice() removes trailing "Z" character in timestamp
    const valueObs = result[1];
    return [timestampObs, valueObs];
  });
};

/**
 * Concatenates metadata properties to create a string for either the title or subtitle of a line chart
27
28
 * @param {Array} phenomenonNamesArr An array of phenomenon name strings
 * @returns {String} A string made up of combined phenomenon names
29
 */
30
31
const createCombinedTextForLineChartTitles = function (phenomenonNamesArr) {
  return phenomenonNamesArr.join(", ");
32
33
34
35
36
};

/**
 * Creates an options object for each series drawn in the line chart
 * @param {Array} formattedObsArraysForLineChart An array of formatted observation array(s) from one or more datastreams
37
 * @param {Array} buildingIdsPhenomenonNamesArr An array of string(s) made up of building ID(s) + phenomenon name(s)
38
39
40
41
 * @returns {Array} An array made up of series options object(s)
 */
const createSeriesOptionsForLineChart = function (
  formattedObsArraysForLineChart,
42
  buildingIdsPhenomenonNamesArr
43
) {
44
  // An array of colors, in hexadecimal format, provided by the global Highcharts object
45
46
  const seriesColors = Highcharts.getOptions().colors;

47
48
49
  // Create a copy of the colors array
  const seriesColorsArr = [...seriesColors];

50
  // Create an array of seriesOptions objects
51
  // Assumes that the observation array of arrays and building IDs + phenomenon names array are of equal length
52
  // Use one of the arrays for looping
53
54
55
  if (
    formattedObsArraysForLineChart.length !==
    buildingIdsPhenomenonNamesArr.length
56
  ) {
57
58
59
    throw new Error(
      "The observations array and phenomenon names array have different lengths"
    );
60
61
62
63
64
65
66
67
68
69
  } else {
    return formattedObsArraysForLineChart.map((formattedObsArray, i) => {
      return {
        name: `${buildingIdsPhenomenonNamesArr[i]}`,
        data: formattedObsArray,
        color: seriesColorsArr[i],
        turboThreshold: Number.MAX_VALUE, // #3404, remove after 4.0.5 release
      };
    });
  }
70
71
72
73
74
};

/**
 * Draw a line chart using Highcharts library
 * @param {Array} formattedObsArraysForLineChart An array made up of formatted observation array(s) suitable for use in a line chart
Pithon Kabiro's avatar
Pithon Kabiro committed
75
 * @param {Object} extractedFormattedDatastreamPropertiesArr An object that contains arrays of formatted Datastream properties
76
77
78
79
 * @returns {undefined} undefined
 */
const drawLineChartHighcharts = function (
  formattedObsArraysForLineChart,
Pithon Kabiro's avatar
Pithon Kabiro committed
80
  extractedFormattedDatastreamProperties
81
82
) {
  // Arrays of datastream properties
83
  let datastreamNamesArr,
84
    phenomenonNamesArr,
85
    buildingIdsPhenomenonNamesArr,
86
    unitOfMeasurementSymbolsArr,
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
    aggregationInterval,
    aggregationType;

  // Check whether the datastream properties are for aggregated observations
  if (extractedFormattedDatastreamProperties?.aggregationType === undefined) {
    // Case 1: No aggregation
    ({
      datastreamNamesArr,
      phenomenonNamesArr,
      buildingIdsPhenomenonNamesArr,
      unitOfMeasurementSymbolsArr,
    } = extractedFormattedDatastreamProperties);
  } else {
    // Case 2: Aggregation
    ({
      datastreamNamesArr,
      phenomenonNamesArr,
      buildingIdsPhenomenonNamesArr,
      unitOfMeasurementSymbolsArr,
      aggregationInterval,
      aggregationType,
    } = extractedFormattedDatastreamProperties);
  }
110

111
  // Chart title and subtitle text
112
113
114
115
116
  const textChartTitle = createFullTitleForLineOrColumnChart(
    phenomenonNamesArr,
    aggregationInterval,
    aggregationType
  );
117

118
  const textChartSubtitle = createSubtitleForChart(datastreamNamesArr);
119

120
121
122
  // Create the array of series options object(s)
  const seriesOptionsArr = createSeriesOptionsForLineChart(
    formattedObsArraysForLineChart,
123
    buildingIdsPhenomenonNamesArr,
124
125
126
127
128
129
130
131
132
133
134
135
136
    unitOfMeasurementSymbolsArr
  );

  Highcharts.stockChart("chart-line", {
    chart: {
      zoomType: "x",
    },

    rangeSelector: {
      selected: 5,
    },

    title: {
137
      text: textChartTitle,
138
      "align": "center",
139
140
141
    },

    subtitle: {
142
      text: textChartSubtitle,
143
      align: "center",
144
145
146
    },

    tooltip: {
147
148
149
150
      formatter() {
        // Our tooltip is split
        // this.x -- common for all points
        // this.points -- an array containing properties for each series
151
152
153
        return [
          `${createTooltipDateString(this.x, aggregationInterval)}`,
        ].concat(
154
155
156
157
158
159
160
161
162
163
164
165
          this.points
            ? this.points.map(
                (point, i) =>
                  `<span style="color:${point.color}">${
                    point.series.name
                  }</span>: <b>${point.y.toFixed(2)} ${
                    unitOfMeasurementSymbolsArr[i]
                  }</b>`
              )
            : []
        );
      },
166
167
    },

168
169
    exporting: chartExportOptions,

170
171
172
173
    series: seriesOptionsArr,
  });
};

174
175
176
177
export {
  formatSensorThingsApiResponseForLineOrColumnChart,
  drawLineChartHighcharts,
};