Files
lucia-frontend/src/module/setChartData.js
Cindy Chang 4ba7961a5a sonar
2024-08-02 13:24:15 +08:00

247 lines
6.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import getMoment from 'moment';
/**
* 將後端的 chart data 加入最大、最小值,折線圖
* @param {array} baseData 後端給的 10 個時間點
* @param {number} xMax 2022-05-23T18:00:00
* @param {number} xMin 2022-05-23T08:00:00
* @param {boolean} isPercent 是否以百分比顯示
* @param {number} yMax case
* @param {number} yMin case
* @returns {array} 可直接套入 chart.js 的 data
*/
export function setLineChartData(baseData, xMax, xMin, isPercent, yMax, yMin) {
// 將 baseData 轉換為包含 x 和 y 屬性的物件陣列
let data = baseData.map(i => ({ x: i.x, y: i.y }));
// 計算 y 軸最小值
let b = calculateYMin(baseData, isPercent, yMin, yMax);
// 計算 y 軸最大值
let mf = calculateYMax(baseData, isPercent, yMin, yMax);
// 添加最小值
data.unshift({
x: xMin,
y: b,
});
// 添加最大值
data.push({
x: xMax,
y: mf,
});
return data;
};
/**
* 計算 y 軸最小值
*
* x 值為 0 ~ 11,
* 將三的座標(ax, ay), (bx, by), (cx, cy)命名為 (a, b), (c, d), (e, f)
* 最小值: (f - b)(c - a) = (e - a)(d - b),求 b = (ed - ad - fa - fc) / (e - c - a)
*/
function calculateYMin(baseData, isPercent, yMin, yMax) {
let a = 0;
let c = 1;
let d = baseData[0].y;
let e = 2;
let f = baseData[1].y;
let b = (e * d - a * d - f * a - f * c) / (e - c - a);
return clampValue(b, isPercent, yMin, yMax);
};
/**
* 計算 y 軸最大值
*
* x 值為 9 ~ 11,
* 將三的座標(ax, ay), (bx, by), (cx, cy)命名為 (a, b), (c, d), (e, f)
* 最大值: (f - b)(e - c) = (f - d)(e - a),求 f = (be - bc -de + da) / (a - c)
*/
function calculateYMax(baseData, isPercent, yMin, yMax) {
let ma = 9;
let mb = baseData[8].y;
let mc = 10;
let md = baseData[9].y;
let me = 11;
let mf = (mb * me - mb * mc - md * me + md * ma) / (ma - mc);
return clampValue(mf, isPercent, yMin, yMax);
};
/**
* 將值限制在指定範圍內
* 如果 isPercent 為 true則將值限制在 0 到 1 之間
* 否則,將值限制在 yMin 和 yMax 之間
*
* @param {number} value 需要被限制的值
* @param {boolean} isPercent 如果為 true表示值應該被限制在百分比範圍內0 到 1
* @param {number} min 非百分比情況下的最小允許值yMin
* @param {number} max 非百分比情況下的最大允許值yMax
* @returns {number} 被限制在指定範圍內的值
*/
function clampValue(value, isPercent, min, max) {
if (isPercent) {
if (value >= 1) {
return 1;
} else if (value <= 0) {
return 0;
}
} else {
if (value >= max) {
return max;
}
if (value <= min) {
return min;
}
}
return value;
};
/**
* 將後端的 chart data x 值轉換時間格式,長條圖
* @param {array} baseData 後端給的 10 個時間點
* @returns {array} 可直接套入 chart.js 的 data
*/
export function setBarChartData(baseData) {
let data = baseData.map(i =>{
return {
x: getMoment(i.x).format('YYYY/M/D hh:mm:ss'),
y: i.y
}
})
return data
};
/**
* 將一段時間均分成多段的時間點
* @param {string} startTime 開始時間(ex: 434) 總秒數
* @param {string} endTime 結束時間(ex: 259065) 總秒數
* @param {number} amount 切分成多少段
* @returns {array} 均分成多段的時間 array
*/
export function timeRange(minTime, maxTime, amount) {
// x 軸(時間軸)的範圍是最大-最小,從最小值按照 index 累加間距到最大值
const startTime = minTime;
const endTime = maxTime;
let timeRange = []; // return數據初始化
const timeGap = (endTime - startTime) / (amount - 1); // 切分成多少段
for (let i = 0; i < amount; i++) {
timeRange.push(startTime + timeGap * i);
}
timeRange = timeRange.map(value => Math.round(value));
return timeRange;
};
/**
* 將 y 軸的值分割成跟 x 時間軸一樣的等份,使用統計學 R 語言算出貝茲曲線攻勢
* @param {array} data 切分成多少段
* @param {number} yAmount 切分成多少段
* @param {number} yMax y 最大值
* @returns {array} 均分成多段的時間 array
*/
export function yTimeRange(data, yAmount, yMax) {
const yRange = [];
const yGap = (1/ (yAmount-1));
// 貝茲曲線公式
const threebsr = function (t, a1, a2, a3, a4) {
return (
(1 - t) * (1 - t) * (1 - t) * a1 +
3 * t * (1 - t)* (1 - t) * a2 +
3 * t * t * (1 - t) * a3 +
t * t * t * a4
)
};
for (let j = 0; j < data.length - 1; j++) {
for (let i = 0; i <= 1; i += yGap*11) {
yRange.push(threebsr(i, data[j].y, data[j].y, data[j + 1].y, data[j + 1].y));
}
}
if(yRange.length < yAmount) {
let len = yAmount - yRange.length;
for (let i = 0; i < len; i++) {
yRange.push(yRange[yRange.length - 1]);
}
}
else if(yRange.length > yAmount) {
let len = yRange.length - yAmount;
for(let i = 0; i < len; i++) {
yRange.splice(1, 1);
}
}
return yRange;
};
/**
* 找出選擇的時間點的 index
* @param {array} data xVal
* @param {number} xValue x 值
* @returns {numver} x index
*/
export function getXIndex(data, xValue) {
let closestIndex = xValue; // 假定第一个元素的索引是 0
let smallestDifference = Math.abs(xValue - data[0]); // 初始差值设为第一个元素与目标数的差值
for (let i = 0; i < data.length; i++) {
let difference = Math.abs(xValue - data[i]);
if (difference <= smallestDifference) {
closestIndex = i;
smallestDifference = difference;
}
}
return closestIndex;
};
/**
* 有 dhms 表達的時間單位
* @param {number} seconds 總秒數
* @returns {string}
*/
export function formatTime(seconds) {
if(!isNaN(seconds)) {
const remainingSeconds = seconds % 60;
const minutes = (Math.floor(seconds - remainingSeconds) / 60) % 60;
const hours = (Math.floor(seconds / 3600)) % 24;
const days = Math.floor(seconds / (3600 * 24));
let result = '';
if (days > 0) {
result += `${days}d`;
}
if (hours > 0) {
result += `${hours}h`;
}
if (minutes > 0) {
result += `${minutes}m`;
}
result += `${remainingSeconds}s`;
return result.trim(); // 去除最后一个空格
} else {
return null;
}
}
/**
* 只顯示最大的兩個單位
* @param {array} times 時間
* @returns {array}
*/
export function formatMaxTwo(times) {
const formattedTimes = [];
for (let time of times) {
let units = time.match(/\d+[dhms]/g); // 匹配數字和單位(天、小時、分鐘、秒)
let formattedTime = '';
let count = 0;
// 只保留最大的兩個單位
for (let unit of units) {
if (count >= 2) {
break;
}
formattedTime += unit + ' ';
count++;
}
formattedTimes.push(formattedTime.trim()); // 去除末尾的空格
}
return formattedTimes;
}