import { map, max, uniq, sortBy, flatten, find } from "lodash"; import { createNumberFormatter } from "@/lib/value-format"; const defaultColorScheme = [ [0, "#356aff"], [0.14, "#4a7aff"], [0.28, "#5d87ff"], [0.42, "#7398ff"], [0.56, "#fb8c8c"], [0.71, "#ec6463"], [0.86, "#ec4949"], [1, "#e92827"], ]; function prepareSeries(series, options, additionalOptions) { const { colorScheme, formatNumber } = additionalOptions; const plotlySeries = { x: [], y: [], z: [], type: "heatmap", name: "", colorscale: colorScheme, }; plotlySeries.x = uniq(map(series.data, v => v.x)); plotlySeries.y = uniq(map(series.data, v => v.y)); if (options.sortX) { plotlySeries.x = sortBy(plotlySeries.x); } if (options.sortY) { plotlySeries.y = sortBy(plotlySeries.y); } if (options.reverseX) { plotlySeries.x.reverse(); } if (options.reverseY) { plotlySeries.y.reverse(); } const zMax = max(map(series.data, d => d.zVal)); // Use text trace instead of default annotation for better performance const dataLabels = { x: [], y: [], mode: "text", hoverinfo: "skip", showlegend: false, text: [], textfont: { color: [], }, }; for (let i = 0; i < plotlySeries.y.length; i += 1) { const item = []; for (let j = 0; j < plotlySeries.x.length; j += 1) { const datum = find(series.data, { x: plotlySeries.x[j], y: plotlySeries.y[i] }); const zValue = (datum && datum.zVal) || 0; item.push(zValue); if (isFinite(zMax) && options.showDataLabels) { dataLabels.x.push(plotlySeries.x[j]); dataLabels.y.push(plotlySeries.y[i]); dataLabels.text.push(formatNumber(zValue)); if (options.colorScheme && options.colorScheme === "Custom...") { dataLabels.textfont.color.push("white"); } else { dataLabels.textfont.color.push(zValue / zMax < 0.25 ? "white" : "black"); } } } plotlySeries.z.push(item); } if (isFinite(zMax) && options.showDataLabels) { return [plotlySeries, dataLabels]; } return [plotlySeries]; } export default function prepareHeatmapData(seriesList, options) { let colorScheme = []; if (!options.colorScheme) { colorScheme = defaultColorScheme; } else if (options.colorScheme === "Custom...") { colorScheme = [ [0, options.heatMinColor], [1, options.heatMaxColor], ]; } else { colorScheme = options.colorScheme; } const additionalOptions = { colorScheme, formatNumber: createNumberFormatter(options.numberFormat), }; return flatten(map(seriesList, series => prepareSeries(series, options, additionalOptions))); }