mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 10:24:02 +00:00
Number(null) is 0, so the cockpit cloud profile treated a missing weather code as clear but available. The briefing's three checks now share one helper, and the current-state note is its own bullet instead of folding into the route one.
94 lines
3.0 KiB
JavaScript
94 lines
3.0 KiB
JavaScript
function clamp01(value) {
|
|
return Math.min(1, Math.max(0, Number(value) || 0));
|
|
}
|
|
|
|
function range01(value, min, max) {
|
|
if (!Number.isFinite(Number(value)) || max <= min) return 0;
|
|
return clamp01((Number(value) - min) / (max - min));
|
|
}
|
|
|
|
/**
|
|
* Convert source weather observations into bounded visual strengths. The WMO
|
|
* code selects the effect family; numeric observations only control strength.
|
|
* Missing weather fails clear instead of inventing conditions.
|
|
*/
|
|
export function deriveWeatherEffectProfile(weather) {
|
|
const reported = weather?.weatherCode;
|
|
// Number(null) and Number('') are 0 (clear), so a missing code is checked first.
|
|
if (
|
|
reported == null ||
|
|
reported === '' ||
|
|
!Number.isFinite(Number(reported))
|
|
) {
|
|
return {
|
|
available: false,
|
|
cloud: 0,
|
|
rain: 0,
|
|
snow: 0,
|
|
fog: 0,
|
|
haze: 0,
|
|
droplets: 0,
|
|
storm: 0,
|
|
wind: 0,
|
|
windDirectionDeg: 0,
|
|
};
|
|
}
|
|
|
|
const code = Number(weather.weatherCode);
|
|
const cover = range01(weather.cloudCoverPct, 8, 100);
|
|
const precipitation = Math.max(0, Number(weather.precipitationMm) || 0);
|
|
const wind = range01(weather.windKph, 4, 90);
|
|
const visibilityM = Number(weather.visibilityM);
|
|
const visibilityFog = Number.isFinite(visibilityM)
|
|
? range01(12000 - visibilityM, 0, 11500)
|
|
: 0;
|
|
|
|
const drizzle = code >= 51 && code <= 57;
|
|
const rainCode =
|
|
(code >= 61 && code <= 67) || (code >= 80 && code <= 82) || code >= 95;
|
|
const snowCode = (code >= 71 && code <= 77) || (code >= 85 && code <= 86);
|
|
const fogCode = code === 45 || code === 48;
|
|
const stormCode = code >= 95;
|
|
|
|
const observedPrecip = range01(precipitation, 0.05, 8);
|
|
const rainFloor = drizzle ? 0.12 : rainCode ? (stormCode ? 0.68 : 0.28) : 0;
|
|
const snowFloor = snowCode ? 0.3 : 0;
|
|
const rain = drizzle || rainCode ? Math.max(rainFloor, observedPrecip) : 0;
|
|
const snow = snowCode ? Math.max(snowFloor, observedPrecip) : 0;
|
|
const fog = Math.max(fogCode ? 0.78 : 0, visibilityFog);
|
|
const storm = stormCode ? Math.max(0.55, observedPrecip) : 0;
|
|
const forcedCloud = stormCode
|
|
? 0.92
|
|
: rain || snow
|
|
? 0.7
|
|
: fogCode
|
|
? 0.55
|
|
: 0;
|
|
const cloud = Math.max(cover, forcedCloud);
|
|
|
|
return {
|
|
available: true,
|
|
cloud: clamp01(cloud),
|
|
rain: clamp01(rain),
|
|
snow: clamp01(snow),
|
|
fog: clamp01(fog),
|
|
haze: clamp01(Math.max(fog * 0.78, cloud * 0.14 + (rain + snow) * 0.2)),
|
|
droplets: clamp01(rain * (0.58 + storm * 0.42)),
|
|
storm: clamp01(storm),
|
|
wind,
|
|
windDirectionDeg: Number.isFinite(Number(weather.windDirectionDeg))
|
|
? ((Number(weather.windDirectionDeg) % 360) + 360) % 360
|
|
: 0,
|
|
};
|
|
}
|
|
|
|
/** Fade ground-weather overlays as the camera climbs above their plausible layer. */
|
|
export function weatherAltitudeFactors(altitudeM) {
|
|
const altitude = Math.max(0, Number(altitudeM) || 0);
|
|
return {
|
|
precipitation: 1 - range01(altitude, 6000, 14000),
|
|
cloud: 1 - range01(altitude, 14000, 24000),
|
|
haze: 1 - range01(altitude, 9000, 20000),
|
|
};
|
|
}
|