export { LodashCustomizer, readUTCDate, setAxisLimits }
import { merge, isArray } from "lodash";
/**
* Implementation of lodash mergeWith customizer functions.
* Usage: https://lodash.com/docs/4.17.15#mergeWith.
*/
class LodashCustomizer {
/**
* Change the behavior of lodash array concatenation for deep merge.
* By default the lodash merge function follows the behavior of jQuery's deep extend.
* With this method, arrays are concatenated.
* @param {*} objValue - First argument of lodash mergeWith function.
* @param {*} srcValue - Second argument of lodash mergeWith function.
* @returns Concatenated Array.
*/
static concatArrays(objValue, srcValue) {
if (isArray(objValue)) {
return objValue.concat(srcValue);
}
}
/**
* Modifies lodash merge algorithm for values.
* If a regex expression matches for a specific key (defined in the patternDict) during the merge process, instead of replacing the srcValue by the objValue, the srcValue is kept.
* Consequently, for the predefined keys in patternDict, if objValue matches the pattern in patternDict, srcValue is returned. Else, objValue.
* @param {*} objValue - First argument of lodash mergeWith function.
* @param {*} srcValue - Second argument of lodash mergeWith function.
* @param {String} key - Third argument of lodash mergeWith function.
* @param {{}} [patternDict={}] - Contain the assignment of key names and regex patterns: { objValue1: pattern1, objValue2: pattern1, ...}, i.e., { mimeType: "^$", photoWidth: "^0$", ...}.
* @returns Derived value.
*/
static overwriteValues(objValue, srcValue, key, patternDict = {}) {
if (key != null && key in patternDict) {
if (new RegExp(patternDict[key]).test(objValue)) {
return srcValue
}
else return objValue
}
}
}
/**
* Convert a time string to a UTC date object.
* Observe: Does not change time zones.
* @param {String} date UTC Date in ISO time format.
* @returns {Date} Date time object.
*/
function readUTCDate(date) {
const zuluDate = date.toLowerCase().slice(-1) == 'z' ? date : date + 'Z';
return new Date(zuluDate)
}
/**
* For each plotly axis, set axis limits with respect to the data.
* @param {Array} data - Plotly data config.
* @param {Object} layout - Plotly layout config.
* @param {Number} [offsetFactor] - Space between axis and data point.
* @returns {Object} Updated layout config.
*/
function setAxisLimits(data, layout, offsetFactor = 0.02) {
const dataLimits = data.reduce((acc, curr, idx) => {
let axisCollect = acc;
const dataPoints = [curr.x, curr.y];
dataPoints.forEach((values, index) => {
if (axisCollect[index] == undefined)
axisCollect[index] = { 'minValue': [], 'maxValue': [] };
if (values) {
if (isFinite(Math.min(...values))) {
axisCollect[index]['minValue'].push(Math.min(...values));
axisCollect[index]['maxValue'].push(Math.max(...values));
}
else {
axisCollect[index]['minValue'].push(new Date(values.at(0)));
axisCollect[index]['maxValue'].push(new Date(values.at(-1)));
}
}
else {
axisCollect[index]['minValue'].push(undefined);
axisCollect[index]['maxValue'].push(undefined);
}
})
return axisCollect
}, [])
const layoutPlotly = dataLimits.reduce((acc, curr, idx) => {
let areDateObjectsOnAxis = curr['minValue'].some((item) => item instanceof Date)
const axisMinValue = Math.max(...curr['minValue'])
const axisMaxValue = Math.max(...curr['maxValue'])
const offsetValue = (axisMaxValue - axisMinValue) * offsetFactor;
let startValueWithOffset = axisMinValue - offsetValue;
let endValueWithOffset = axisMaxValue + offsetValue;
if (areDateObjectsOnAxis) {
startValueWithOffset = new Date(startValueWithOffset).toISOString();
endValueWithOffset = new Date(endValueWithOffset).toISOString();
}
const axisTypes = ['xaxis', 'yaxis', 'zaxis'];
const currAxisType = axisTypes[idx];
const layout = acc;
if (axisMinValue != axisMaxValue) {
layout[currAxisType] = {
"range": [
startValueWithOffset,
endValueWithOffset
],
minallowed: startValueWithOffset,
maxallowed: endValueWithOffset
};
}
return layout
}, {})
const layoutUpdated = merge(layoutPlotly, layout)
return layoutUpdated
}