Add color scales and detail panel entries for upper-air weather layers

This commit is contained in:
Graham McIntire 2026-04-03 16:54:13 -05:00
parent 08844d3515
commit bf8c6d4789
No known key found for this signature in database
GPG key ID: F4ABF488E6029E59

View file

@ -71,6 +71,114 @@ const COLOR_SCALES = {
format: v => v ? "Yes" : "No",
label: "Ducting Detected",
unit: ""
},
surface_rh: {
breakpoints: [
{ value: 0, r: 255, g: 144, b: 68 },
{ value: 30, r: 255, g: 229, b: 102 },
{ value: 60, r: 125, g: 255, b: 212 },
{ value: 80, r: 0, g: 255, b: 163 },
{ value: 100, r: 0, g: 200, b: 130 }
],
format: v => v != null ? `${v.toFixed(0)}%` : "N/A",
label: "Relative Humidity",
unit: "%"
},
surface_refractivity: {
breakpoints: [
{ value: 250, r: 255, g: 79, b: 79 },
{ value: 300, r: 255, g: 144, b: 68 },
{ value: 330, r: 255, g: 229, b: 102 },
{ value: 360, r: 125, g: 255, b: 212 },
{ value: 400, r: 0, g: 255, b: 163 }
],
format: v => v != null ? `${v.toFixed(1)} N` : "N/A",
label: "Surface Refractivity",
unit: "N"
},
temp_850mb: {
breakpoints: [
{ value: -20, r: 0, g: 0, b: 200 },
{ value: -5, r: 100, g: 149, b: 237 },
{ value: 5, r: 255, g: 255, b: 150 },
{ value: 15, r: 255, g: 140, b: 0 },
{ value: 30, r: 200, g: 0, b: 0 }
],
format: v => v != null ? `${v.toFixed(1)} °C` : "N/A",
label: "Temperature at 850mb",
unit: "°C"
},
dewpoint_850mb: {
breakpoints: [
{ value: -20, r: 139, g: 90, b: 43 },
{ value: -5, r: 194, g: 165, b: 116 },
{ value: 5, r: 173, g: 216, b: 230 },
{ value: 15, r: 65, g: 105, b: 225 },
{ value: 25, r: 0, g: 0, b: 180 }
],
format: v => v != null ? `${v.toFixed(1)} °C` : "N/A",
label: "Dewpoint at 850mb",
unit: "°C"
},
lapse_rate: {
breakpoints: [
{ value: 0, r: 0, g: 255, b: 163 },
{ value: 4, r: 125, g: 255, b: 212 },
{ value: 6.5, r: 255, g: 229, b: 102 },
{ value: 8, r: 255, g: 144, b: 68 },
{ value: 10, r: 255, g: 79, b: 79 }
],
format: v => v != null ? `${v.toFixed(1)} °C/km` : "N/A",
label: "Lapse Rate",
unit: "°C/km"
},
inversion_strength: {
breakpoints: [
{ value: 0, r: 80, g: 80, b: 80 },
{ value: 1, r: 255, g: 229, b: 102 },
{ value: 3, r: 125, g: 255, b: 212 },
{ value: 5, r: 0, g: 255, b: 163 },
{ value: 10, r: 0, g: 200, b: 130 }
],
format: v => v != null ? (v > 0 ? `+${v.toFixed(1)} °C` : "None") : "N/A",
label: "Inversion Strength",
unit: "°C"
},
inversion_base_m: {
breakpoints: [
{ value: 0, r: 0, g: 255, b: 163 },
{ value: 500, r: 125, g: 255, b: 212 },
{ value: 1000, r: 255, g: 229, b: 102 },
{ value: 2000, r: 255, g: 144, b: 68 },
{ value: 3000, r: 255, g: 79, b: 79 }
],
format: v => v != null ? `${v.toFixed(0)} m AGL` : "None",
label: "Inversion Base Height",
unit: "m"
},
duct_base_m: {
breakpoints: [
{ value: 0, r: 0, g: 255, b: 163 },
{ value: 200, r: 125, g: 255, b: 212 },
{ value: 500, r: 255, g: 229, b: 102 },
{ value: 1000, r: 255, g: 144, b: 68 },
{ value: 2000, r: 255, g: 79, b: 79 }
],
format: v => v != null ? `${v.toFixed(0)} m AGL` : "None",
label: "Duct Base Height",
unit: "m"
},
duct_strength: {
breakpoints: [
{ value: 0, r: 80, g: 80, b: 80 },
{ value: 5, r: 255, g: 229, b: 102 },
{ value: 10, r: 125, g: 255, b: 212 },
{ value: 20, r: 0, g: 255, b: 163 },
{ value: 40, r: 0, g: 200, b: 130 }
],
format: v => v != null ? (v > 0 ? `${v.toFixed(1)} M-units` : "None") : "N/A",
label: "Duct Strength",
unit: "M"
}
}
@ -99,12 +207,23 @@ function buildDetailHTML(point) {
const rows = [
{ label: "Temperature", value: COLOR_SCALES.temperature.format(point.temperature), color: "#ff9044" },
{ label: "Dewpoint Depression", value: COLOR_SCALES.dewpoint_depression.format(point.dewpoint_depression), color: "#7dffd4" },
{ label: "Boundary Layer", value: COLOR_SCALES.bl_height.format(point.bl_height), color: "#ffe566" },
{ label: "Precipitable Water", value: COLOR_SCALES.pwat.format(point.pwat), color: "#4169e1" },
{ label: "Refractivity Gradient", value: COLOR_SCALES.refractivity_gradient.format(point.refractivity_gradient), color: "#00ffa3" },
{ label: "Ducting Detected", value: COLOR_SCALES.ducting.format(point.ducting), color: point.ducting ? "#00ffa3" : "#ff4f4f" },
{ label: "Surface Pressure", value: point.surface_pressure_mb != null ? `${point.surface_pressure_mb.toFixed(1)} mb` : "N/A", color: "#aaa" }
{ label: "Td Depression", value: COLOR_SCALES.dewpoint_depression.format(point.dewpoint_depression), color: "#7dffd4" },
{ label: "Rel. Humidity", value: COLOR_SCALES.surface_rh.format(point.surface_rh), color: "#00ffa3" },
{ label: "Sfc Pressure", value: point.surface_pressure_mb != null ? `${point.surface_pressure_mb.toFixed(1)} mb` : "N/A", color: "#aaa" },
{ label: "Refractivity", value: COLOR_SCALES.surface_refractivity.format(point.surface_refractivity), color: "#00ffa3" },
{ label: "N-Gradient", value: COLOR_SCALES.refractivity_gradient.format(point.refractivity_gradient), color: "#00ffa3" },
{ label: "BL Height", value: COLOR_SCALES.bl_height.format(point.bl_height), color: "#ffe566" },
{ label: "PWAT", value: COLOR_SCALES.pwat.format(point.pwat), color: "#4169e1" },
{ separator: true },
{ label: "T @ 850mb", value: COLOR_SCALES.temp_850mb.format(point.temp_850mb), color: "#ff9044" },
{ label: "Td @ 850mb", value: COLOR_SCALES.dewpoint_850mb.format(point.dewpoint_850mb), color: "#4169e1" },
{ label: "Lapse Rate", value: COLOR_SCALES.lapse_rate.format(point.lapse_rate), color: "#ffe566" },
{ label: "Inversion", value: COLOR_SCALES.inversion_strength.format(point.inversion_strength), color: point.inversion_strength > 0 ? "#00ffa3" : "#666" },
{ label: "Inv. Base", value: COLOR_SCALES.inversion_base_m.format(point.inversion_base_m), color: point.inversion_base_m != null ? "#7dffd4" : "#666" },
{ separator: true },
{ label: "Ducting", value: COLOR_SCALES.ducting.format(point.ducting), color: point.ducting ? "#00ffa3" : "#ff4f4f" },
{ label: "Duct Base", value: COLOR_SCALES.duct_base_m.format(point.duct_base_m), color: point.duct_base_m != null ? "#00ffa3" : "#666" },
{ label: "Duct Strength", value: COLOR_SCALES.duct_strength.format(point.duct_strength), color: point.duct_strength != null ? "#00ffa3" : "#666" }
]
const mobile = isMobile()
@ -115,12 +234,15 @@ function buildDetailHTML(point) {
? `<div style="display:flex;justify-content:center;padding:6px 0 2px;"><div style="width:32px;height:4px;border-radius:2px;background:rgba(255,255,255,0.3);"></div></div>`
: ""
const tableRows = rows.map(r =>
`<tr>
const tableRows = rows.map(r => {
if (r.separator) {
return `<tr><td colspan="2" style="padding:2px 0;"><hr style="border:none;border-top:1px solid rgba(255,255,255,0.1);"></td></tr>`
}
return `<tr>
<td style="padding:3px 8px 3px 0;font-size:12px;white-space:nowrap;"><span style="color:${r.color};">\u25cf</span> ${r.label}</td>
<td style="padding:3px 0;font-size:12px;text-align:right;font-weight:600;">${r.value}</td>
</tr>`
).join("")
}).join("")
const time = point.valid_time
? new Date(point.valid_time).toISOString().replace("T", " ").slice(0, 16) + " UTC"