mosaicmap/lib/color-maps.ts
2025-08-13 21:54:20 +08:00

313 lines
13 KiB
TypeScript
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.

// 色标函数集合
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: '气象雷达色标' }
];
}