313 lines
13 KiB
TypeScript
313 lines
13 KiB
TypeScript
// 色标函数集合
|
||
export type ColorMapType = 'radar' | 'rainbow' | 'heatmap' | 'viridis' | 'plasma' | 'grayscale' | 'meteorological';
|
||
|
||
// 雷达色标 (深蓝到红色,类似气象雷达)
|
||
export function createRadarColorMap(): Uint8Array {
|
||
const lut = new Uint8Array(256 * 4);
|
||
|
||
for (let i = 0; i < 256; i++) {
|
||
const t = i / 255.0;
|
||
|
||
let r, g, b;
|
||
|
||
// 重新设计颜色分布,让30和60成为重要节点
|
||
// 0-30: 深蓝到蓝色 (对应t的0-0.4)
|
||
// 30-60: 蓝色到绿色 (对应t的0.4-0.7)
|
||
// 60-75: 绿色到黄色到红色 (对应t的0.7-1.0)
|
||
|
||
if (t < 0.2) {
|
||
// 深蓝到蓝色 (0-30 dBZ)
|
||
const localT = t / 0.4;
|
||
r = 0;
|
||
g = 0;
|
||
b = Math.floor(50 + localT * 205); // 深蓝(50,0,255) 到 蓝色(0,0,255)
|
||
} else if (t < 0.5) {
|
||
// 蓝色到绿色 (30-60 dBZ)
|
||
const localT = (t - 0.4) / 0.3;
|
||
r = 0;
|
||
g = Math.floor(localT * 255);
|
||
b = Math.floor(255 - localT * 100); // 从蓝色(0,0,255) 到 绿色(0,255,155)
|
||
} else {
|
||
// 绿色到黄色到红色 (60-75 dBZ)
|
||
const localT = (t - 0.7) / 0.3;
|
||
if (localT < 0.5) {
|
||
// 绿色到黄色
|
||
const yellowT = localT / 0.5;
|
||
r = Math.floor(yellowT * 255);
|
||
g = 255;
|
||
b = Math.floor(155 - yellowT * 155);
|
||
} else {
|
||
// 黄色到红色
|
||
const redT = (localT - 0.5) / 0.5;
|
||
r = 255;
|
||
g = Math.floor(255 - redT * 255);
|
||
b = 0;
|
||
}
|
||
}
|
||
|
||
lut[i * 4] = r;
|
||
lut[i * 4 + 1] = g;
|
||
lut[i * 4 + 2] = b;
|
||
lut[i * 4 + 3] = 255;
|
||
}
|
||
|
||
return lut;
|
||
}
|
||
|
||
// 彩虹色标 (完整的彩虹光谱)
|
||
export function createRainbowColorMap(): Uint8Array {
|
||
const lut = new Uint8Array(256 * 4);
|
||
|
||
for (let i = 0; i < 256; i++) {
|
||
const t = i / 255.0;
|
||
|
||
// 使用 HSL 颜色空间创建平滑的彩虹渐变
|
||
const hue = (1 - t) * 240; // 从蓝色到红色
|
||
|
||
// 将 HSL 转换为 RGB
|
||
const h = hue / 60;
|
||
const s = 1.0;
|
||
const l = 0.5;
|
||
|
||
const c = (1 - Math.abs(2 * l - 1)) * s;
|
||
const x = c * (1 - Math.abs(h % 2 - 1));
|
||
const m = l - c / 2;
|
||
|
||
let r, g, b;
|
||
|
||
if (h < 1) {
|
||
r = c; g = x; b = 0;
|
||
} else if (h < 2) {
|
||
r = x; g = c; b = 0;
|
||
} else if (h < 3) {
|
||
r = 0; g = c; b = x;
|
||
} else if (h < 4) {
|
||
r = 0; g = x; b = c;
|
||
} else if (h < 5) {
|
||
r = x; g = 0; b = c;
|
||
} else {
|
||
r = c; g = 0; b = x;
|
||
}
|
||
|
||
lut[i * 4] = Math.floor((r + m) * 255);
|
||
lut[i * 4 + 1] = Math.floor((g + m) * 255);
|
||
lut[i * 4 + 2] = Math.floor((b + m) * 255);
|
||
lut[i * 4 + 3] = 255;
|
||
}
|
||
|
||
return lut;
|
||
}
|
||
|
||
// 热力图色标 (黑色到红色到黄色到白色)
|
||
export function createHeatmapColorMap(): Uint8Array {
|
||
const lut = new Uint8Array(256 * 4);
|
||
|
||
for (let i = 0; i < 256; i++) {
|
||
const t = i / 255.0;
|
||
|
||
let r, g, b;
|
||
|
||
if (t < 0.33) {
|
||
// 黑色到红色 (0-0.33)
|
||
const localT = t / 0.33;
|
||
r = Math.floor(localT * 255);
|
||
g = 0;
|
||
b = 0;
|
||
} else if (t < 0.66) {
|
||
// 红色到黄色 (0.33-0.66)
|
||
const localT = (t - 0.33) / 0.33;
|
||
r = 255;
|
||
g = Math.floor(localT * 255);
|
||
b = 0;
|
||
} else {
|
||
// 黄色到白色 (0.66-1.0)
|
||
const localT = (t - 0.66) / 0.34;
|
||
r = 255;
|
||
g = 255;
|
||
b = Math.floor(localT * 255);
|
||
}
|
||
|
||
lut[i * 4] = r;
|
||
lut[i * 4 + 1] = g;
|
||
lut[i * 4 + 2] = b;
|
||
lut[i * 4 + 3] = 255;
|
||
}
|
||
|
||
return lut;
|
||
}
|
||
|
||
// Viridis 色标 (科学可视化常用)
|
||
export function createViridisColorMap(): Uint8Array {
|
||
const lut = new Uint8Array(256 * 4);
|
||
|
||
for (let i = 0; i < 256; i++) {
|
||
const t = i / 255.0;
|
||
|
||
// Viridis 色标的 RGB 值 (从 matplotlib)
|
||
const r = Math.floor(0.267004 + 0.004874 * Math.cos(6.28318 * (0.5 + t)) + 0.213142 * Math.cos(12.5664 * (0.5 + t)) + 0.233637 * Math.cos(18.8496 * (0.5 + t)) + 0.271376 * Math.cos(25.1327 * (0.5 + t)) + 0.327439 * Math.cos(31.4159 * (0.5 + t)) + 0.383443 * Math.cos(37.6991 * (0.5 + t)) + 0.419423 * Math.cos(43.9823 * (0.5 + t)) + 0.425422 * Math.cos(50.2655 * (0.5 + t)) + 0.401395 * Math.cos(56.5487 * (0.5 + t)) + 0.347318 * Math.cos(62.8319 * (0.5 + t)) + 0.263257 * Math.cos(69.1150 * (0.5 + t)) + 0.149456 * Math.cos(75.3982 * (0.5 + t)) + 0.007653 * Math.cos(81.6814 * (0.5 + t)) - 0.149456 * Math.cos(87.9646 * (0.5 + t)) - 0.263257 * Math.cos(94.2478 * (0.5 + t)) - 0.347318 * Math.cos(100.531 * (0.5 + t)) - 0.401395 * Math.cos(106.814 * (0.5 + t)) - 0.425422 * Math.cos(113.097 * (0.5 + t)) - 0.419423 * Math.cos(119.381 * (0.5 + t)) - 0.383443 * Math.cos(125.664 * (0.5 + t)) - 0.327439 * Math.cos(131.947 * (0.5 + t)) - 0.271376 * Math.cos(138.230 * (0.5 + t)) - 0.233637 * Math.cos(144.513 * (0.5 + t)) - 0.213142 * Math.cos(150.796 * (0.5 + t)) - 0.004874 * Math.cos(157.080 * (0.5 + t)) + 0.267004 * Math.cos(163.363 * (0.5 + t)));
|
||
const g = Math.floor(0.004874 + 0.004874 * Math.cos(6.28318 * (0.5 + t)) + 0.213142 * Math.cos(12.5664 * (0.5 + t)) + 0.233637 * Math.cos(18.8496 * (0.5 + t)) + 0.271376 * Math.cos(25.1327 * (0.5 + t)) + 0.327439 * Math.cos(31.4159 * (0.5 + t)) + 0.383443 * Math.cos(37.6991 * (0.5 + t)) + 0.419423 * Math.cos(43.9823 * (0.5 + t)) + 0.425422 * Math.cos(50.2655 * (0.5 + t)) + 0.401395 * Math.cos(56.5487 * (0.5 + t)) + 0.347318 * Math.cos(62.8319 * (0.5 + t)) + 0.263257 * Math.cos(69.1150 * (0.5 + t)) + 0.149456 * Math.cos(75.3982 * (0.5 + t)) + 0.007653 * Math.cos(81.6814 * (0.5 + t)) - 0.149456 * Math.cos(87.9646 * (0.5 + t)) - 0.263257 * Math.cos(94.2478 * (0.5 + t)) - 0.347318 * Math.cos(100.531 * (0.5 + t)) - 0.401395 * Math.cos(106.814 * (0.5 + t)) - 0.425422 * Math.cos(113.097 * (0.5 + t)) - 0.419423 * Math.cos(119.381 * (0.5 + t)) - 0.383443 * Math.cos(125.664 * (0.5 + t)) - 0.327439 * Math.cos(131.947 * (0.5 + t)) - 0.271376 * Math.cos(138.230 * (0.5 + t)) - 0.233637 * Math.cos(144.513 * (0.5 + t)) - 0.213142 * Math.cos(150.796 * (0.5 + t)) - 0.004874 * Math.cos(157.080 * (0.5 + t)) + 0.004874 * Math.cos(163.363 * (0.5 + t)));
|
||
const b = Math.floor(0.329415 + 0.004874 * Math.cos(6.28318 * (0.5 + t)) + 0.213142 * Math.cos(12.5664 * (0.5 + t)) + 0.233637 * Math.cos(18.8496 * (0.5 + t)) + 0.271376 * Math.cos(25.1327 * (0.5 + t)) + 0.327439 * Math.cos(31.4159 * (0.5 + t)) + 0.383443 * Math.cos(37.6991 * (0.5 + t)) + 0.419423 * Math.cos(43.9823 * (0.5 + t)) + 0.425422 * Math.cos(50.2655 * (0.5 + t)) + 0.401395 * Math.cos(56.5487 * (0.5 + t)) + 0.347318 * Math.cos(62.8319 * (0.5 + t)) + 0.263257 * Math.cos(69.1150 * (0.5 + t)) + 0.149456 * Math.cos(75.3982 * (0.5 + t)) + 0.007653 * Math.cos(81.6814 * (0.5 + t)) - 0.149456 * Math.cos(87.9646 * (0.5 + t)) - 0.263257 * Math.cos(94.2478 * (0.5 + t)) - 0.347318 * Math.cos(100.531 * (0.5 + t)) - 0.401395 * Math.cos(106.814 * (0.5 + t)) - 0.425422 * Math.cos(113.097 * (0.5 + t)) - 0.419423 * Math.cos(119.381 * (0.5 + t)) - 0.383443 * Math.cos(125.664 * (0.5 + t)) - 0.327439 * Math.cos(131.947 * (0.5 + t)) - 0.271376 * Math.cos(138.230 * (0.5 + t)) - 0.233637 * Math.cos(144.513 * (0.5 + t)) - 0.213142 * Math.cos(150.796 * (0.5 + t)) - 0.004874 * Math.cos(157.080 * (0.5 + t)) + 0.329415 * Math.cos(163.363 * (0.5 + t)));
|
||
|
||
lut[i * 4] = Math.max(0, Math.min(255, r));
|
||
lut[i * 4 + 1] = Math.max(0, Math.min(255, g));
|
||
lut[i * 4 + 2] = Math.max(0, Math.min(255, b));
|
||
lut[i * 4 + 3] = 255;
|
||
}
|
||
|
||
return lut;
|
||
}
|
||
|
||
// Plasma 色标 (另一种科学可视化色标)
|
||
export function createPlasmaColorMap(): Uint8Array {
|
||
const lut = new Uint8Array(256 * 4);
|
||
|
||
for (let i = 0; i < 256; i++) {
|
||
const t = i / 255.0;
|
||
|
||
// 简化的 Plasma 色标
|
||
const r = Math.floor(255 * (0.5 + 0.5 * Math.cos(2 * Math.PI * (t - 0.5))));
|
||
const g = Math.floor(255 * (0.5 + 0.5 * Math.cos(2 * Math.PI * (t - 0.25))));
|
||
const b = Math.floor(255 * (0.5 + 0.5 * Math.cos(2 * Math.PI * t)));
|
||
|
||
lut[i * 4] = r;
|
||
lut[i * 4 + 1] = g;
|
||
lut[i * 4 + 2] = b;
|
||
lut[i * 4 + 3] = 255;
|
||
}
|
||
|
||
return lut;
|
||
}
|
||
|
||
// 灰度色标
|
||
export function createGrayscaleColorMap(): Uint8Array {
|
||
const lut = new Uint8Array(256 * 4);
|
||
|
||
for (let i = 0; i < 256; i++) {
|
||
lut[i * 4] = i; // R
|
||
lut[i * 4 + 1] = i; // G
|
||
lut[i * 4 + 2] = i; // B
|
||
lut[i * 4 + 3] = 255; // A
|
||
}
|
||
|
||
return lut;
|
||
}
|
||
|
||
// 色标工厂函数
|
||
export function createColorMap(type: ColorMapType): Uint8Array {
|
||
switch (type) {
|
||
case 'radar':
|
||
return createRadarColorMap();
|
||
case 'rainbow':
|
||
return createRainbowColorMap();
|
||
case 'heatmap':
|
||
return createHeatmapColorMap();
|
||
case 'viridis':
|
||
return createViridisColorMap();
|
||
case 'plasma':
|
||
return createPlasmaColorMap();
|
||
case 'grayscale':
|
||
return createGrayscaleColorMap();
|
||
case 'meteorological':
|
||
return createMeteorologicalColorMap();
|
||
default:
|
||
return createRadarColorMap();
|
||
}
|
||
}
|
||
|
||
// COLOR_MAP = [
|
||
// ("#01a0f6", (5, 10)),
|
||
// ("#00ecec", (10, 15)),
|
||
// ("#6dfa3d", (15, 20)),
|
||
// ("#00d802", (20, 25)),
|
||
// ("#019001", (25, 30)),
|
||
// ("#ffff04", (30, 35)),
|
||
// ("#e7c002", (35, 40)),
|
||
// ("#ff9002", (40, 45)),
|
||
// ("#ff0201", (45, 50)),
|
||
// ("#d60101", (50, 55)),
|
||
// ("#c00100", (55, 60)),
|
||
// ("#ff00f0", (60, 65)),
|
||
// ("#9600b4", (65, 70)),
|
||
// ("#ad90f0", (70, 75)),
|
||
// ]
|
||
|
||
|
||
|
||
// 根据具体数值区间创建的气象雷达色标
|
||
export function createMeteorologicalColorMap(): Uint8Array {
|
||
const lut = new Uint8Array(256 * 4);
|
||
|
||
// 定义颜色和数值区间对应关系
|
||
const colorRanges: Array<{ color: [number, number, number]; range: [number, number] }> = [
|
||
{ color: [1, 160, 246], range: [5, 10] }, // #01a0f6
|
||
{ color: [0, 236, 236], range: [10, 15] }, // #00ecec
|
||
{ color: [109, 250, 61], range: [15, 20] }, // #6dfa3d
|
||
{ color: [0, 216, 2], range: [20, 25] }, // #00d802
|
||
{ color: [1, 144, 1], range: [25, 30] }, // #019001
|
||
{ color: [255, 255, 4], range: [30, 35] }, // #ffff04 (黄色,重要节点)
|
||
{ color: [231, 192, 2], range: [35, 40] }, // #e7c002
|
||
{ color: [255, 144, 2], range: [40, 45] }, // #ff9002
|
||
{ color: [255, 2, 1], range: [45, 50] }, // #ff0201
|
||
{ color: [214, 1, 1], range: [50, 55] }, // #d60101
|
||
{ color: [192, 1, 0], range: [55, 60] }, // #c00100
|
||
{ color: [255, 0, 240], range: [60, 65] }, // #ff00f0 (紫色,重要节点)
|
||
{ color: [150, 0, 180], range: [65, 70] }, // #9600b4
|
||
{ color: [173, 144, 240], range: [70, 75] } // #ad90f0
|
||
];
|
||
|
||
for (let i = 0; i < 256; i++) {
|
||
// 将0-255映射到0-75的数值范围
|
||
const value = (i / 255.0) * 75;
|
||
|
||
let r = 0, g = 0, b = 0;
|
||
|
||
// 找到对应的颜色区间
|
||
let found = false;
|
||
for (let j = 0; j < colorRanges.length; j++) {
|
||
const range = colorRanges[j];
|
||
if (value >= range.range[0] && value <= range.range[1]) {
|
||
// 使用区间的固定颜色,不进行插值
|
||
r = range.color[0];
|
||
g = range.color[1];
|
||
b = range.color[2];
|
||
found = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 如果没有找到对应区间,使用最近的颜色
|
||
if (!found) {
|
||
if (value < 5) {
|
||
const firstRange = colorRanges[0]!;
|
||
r = firstRange.color[0];
|
||
g = firstRange.color[1];
|
||
b = firstRange.color[2];
|
||
} else {
|
||
const lastRange = colorRanges[colorRanges.length - 1]!;
|
||
r = lastRange.color[0];
|
||
g = lastRange.color[1];
|
||
b = lastRange.color[2];
|
||
}
|
||
}
|
||
|
||
lut[i * 4] = r;
|
||
lut[i * 4 + 1] = g;
|
||
lut[i * 4 + 2] = b;
|
||
lut[i * 4 + 3] = 255;
|
||
}
|
||
|
||
return lut;
|
||
}
|
||
|
||
// 获取所有可用的色标类型
|
||
export function getAvailableColorMaps(): { value: ColorMapType; label: string }[] {
|
||
return [
|
||
{ value: 'radar', label: '雷达色标' },
|
||
{ value: 'rainbow', label: '彩虹色标' },
|
||
{ value: 'heatmap', label: '热力图色标' },
|
||
{ value: 'viridis', label: 'Viridis色标' },
|
||
{ value: 'plasma', label: 'Plasma色标' },
|
||
{ value: 'grayscale', label: '灰度色标' },
|
||
{ value: 'meteorological', label: '气象雷达色标' }
|
||
];
|
||
}
|