| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123 |
- /**
- * 功能诊断热力图按指标含义配色,不用同一条色带。
- * 异常率、次数、角度:数值越大越需要关注。
- * 功率曲线符合度:数值越高越好,颜色按「偏离满分」加深。
- */
- const SCALES = {
- anomaly: { cap: 100 },
- conformity: { cap: 100, invert: true },
- degree: { cap: 15 },
- count: { cap: 40 },
- };
- /** 报告和浅色页面用浅底色带;科技蓝页面用深底色带,贴近大屏底色 */
- const PALETTES = {
- light: {
- anomaly: { low: "#dbeafe", high: "#1d4ed8" },
- conformity: { low: "#dcfce7", high: "#15803d" },
- degree: { low: "#ffedd5", high: "#c2410c" },
- count: { low: "#ede9fe", high: "#6d28d9" },
- },
- dark: {
- anomaly: { low: "#16324a", high: "#22d3ee" },
- conformity: { low: "#14382c", high: "#34d399" },
- degree: { low: "#3a2a16", high: "#f59e0b" },
- count: { low: "#2a2244", high: "#c4b5fd" },
- },
- };
- function familyOf(metric, tone = "light") {
- const scale = SCALES[metric] || SCALES.anomaly;
- const palette = PALETTES[tone] || PALETTES.light;
- const colors = palette[metric] || palette.anomaly;
- return { ...scale, ...colors };
- }
- function parseHex(hex) {
- const text = String(hex || "").replace("#", "");
- return [0, 2, 4].map((index) => parseInt(text.slice(index, index + 2), 16));
- }
- function mixHex(from, to, amount) {
- const start = parseHex(from);
- const end = parseHex(to);
- const t = Math.max(0, Math.min(1, amount));
- const mixed = start.map((channel, index) =>
- Math.round(channel + (end[index] - channel) * t),
- );
- return `#${mixed.map((channel) => channel.toString(16).padStart(2, "0")).join("")}`;
- }
- function relativeLuminance(hex) {
- const [r, g, b] = parseHex(hex).map((channel) => {
- const value = channel / 255;
- return value <= 0.03928 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4;
- });
- return 0.2126 * r + 0.7152 * g + 0.0722 * b;
- }
- /** 行标签或字段类型 → 配色家族 */
- export function resolveHeatmapMetric(meta = {}) {
- const label = String(meta.label || "");
- const mapKey = String(meta.mapKey || "");
- const kind = String(meta.valueKind || "");
- if (
- kind === "degree" ||
- /静态偏航误差|最小桨距角/.test(label) ||
- /yawerror|minpitch/i.test(mapKey)
- ) {
- return "degree";
- }
- if (
- kind === "count" ||
- /偏航次数/.test(label) ||
- /yawcount/i.test(mapKey)
- ) {
- return "count";
- }
- if (
- /风功率曲线情况|功率曲线符合|符合度/.test(label) ||
- /powercurve/i.test(mapKey)
- ) {
- return "conformity";
- }
- return "anomaly";
- }
- export function heatmapFamilyColors(metric, tone = "light") {
- const family = familyOf(metric, tone);
- return [family.low, family.high];
- }
- /** 映射到 0~1。符合度按偏离满分计算,其余按绝对值相对上限计算 */
- export function heatmapSeverity(value, metric) {
- const family = familyOf(metric);
- const num = Number(value);
- const magnitude = Number.isFinite(num) ? Math.abs(num) : 0;
- const capped = Math.min(magnitude, family.cap) / family.cap;
- return family.invert ? 1 - capped : capped;
- }
- export function heatmapLabelColor(value, metric, tone = "light") {
- const [low, high] = heatmapFamilyColors(metric, tone);
- const color = mixHex(low, high, heatmapSeverity(value, metric));
- const lightText = tone === "dark" ? "#d8efff" : "#ffffff";
- const darkText = tone === "dark" ? "#071018" : "#0f172a";
- return relativeLuminance(color) < (tone === "dark" ? 0.42 : 0.45)
- ? lightText
- : darkText;
- }
- /** 每个指标单独一条 visualMap,热力图才能按不同色带上色 */
- export function heatmapMetricVisualMap(metric, seriesIndex, tone = "light") {
- return {
- type: "continuous",
- show: false,
- min: 0,
- max: 1,
- dimension: 2,
- seriesIndex,
- inRange: { color: heatmapFamilyColors(metric, tone) },
- };
- }
|