Files
gods-eye-view/server/providers/wind/decode.js

52 lines
1.7 KiB
JavaScript

/** Lazy shared ecCodes WASM instance, created only for an active wind request. */
let eccodesPromise = null;
function loadEccodes() {
eccodesPromise ??= import('@meri-imperiumi/eccodes-wasm')
.then((module) => module.createEccodes())
.catch((error) => {
eccodesPromise = null;
throw error;
});
return eccodesPromise;
}
/** Decode one bounded message in WASM memory without mounting the host filesystem. */
export async function decodeWindGribMessage(buffer, { eccodesModule } = {}) {
if (buffer.byteLength > 8 * 1024 * 1024)
throw new Error('Wind GRIB exceeds byte budget');
const eccodes = eccodesModule ?? (await loadEccodes());
// Everything after this await is synchronous. A shared virtual file is safe
// across U/V and model requests and its storage is released after each decode.
const file = '/gev-wind.grib2';
let handle = null;
try {
eccodes.writeFile(file, buffer);
handle = eccodes.openGrib(file);
const ni = handle.getLong('Ni');
const nj = handle.getLong('Nj');
if (
!Number.isInteger(ni) ||
!Number.isInteger(nj) ||
ni < 1 ||
nj < 1 ||
ni * nj > 2_000_000
)
throw new Error('Invalid wind GRIB geometry');
return {
ni,
nj,
lo1: handle.getDouble('longitudeOfFirstGridPointInDegrees'),
la1: handle.getDouble('latitudeOfFirstGridPointInDegrees'),
di: handle.getDouble('iDirectionIncrementInDegrees'),
dj: handle.getDouble('jDirectionIncrementInDegrees'),
values: handle.getDoubleArray('values'),
shortName: handle.getString('shortName'),
level: handle.getLong('level'),
units: handle.getString('units'),
};
} finally {
handle?.delete();
eccodes.writeFile(file, new Uint8Array(0));
}
}