42 lines
2.2 KiB
JavaScript
42 lines
2.2 KiB
JavaScript
(function (root) {
|
|
'use strict';
|
|
|
|
const CHANNELS = [
|
|
{ channel: 'red', name: 'ts', label: 'Surface temperature', unit: 'K', factor: 1 },
|
|
{ channel: 'green', name: 'pr', label: 'Precipitation', unit: 'mm/day', factor: 86400000 },
|
|
{ channel: 'blue', name: 'sg', label: 'Elevation', unit: 'm', factor: null }
|
|
];
|
|
|
|
function create(index, gravity) {
|
|
if (!index || !Array.isArray(index.fields) || !Number.isFinite(gravity) || gravity <= 0) return null;
|
|
const fields = CHANNELS.map(channel => {
|
|
const field = index.fields.find(item => item.name === channel.name);
|
|
if (!field || !Array.isArray(field.shape) || field.shape.length !== 4 || field.shape[1] !== 1 || !field.shape[0] || field.shape[2] < 1 || field.shape[3] < 1 || !Number.isFinite(field.min) || !Number.isFinite(field.max)) return null;
|
|
const expectedUnit = channel.name === 'ts' ? 'K' : channel.name === 'pr' ? 'm s-1' : 'm2 s-2';
|
|
if (field.unit !== expectedUnit) return null;
|
|
return Object.assign({}, channel, {
|
|
factor: channel.factor === null ? 1 / gravity : channel.factor,
|
|
min: field.min * (channel.factor === null ? 1 / gravity : channel.factor),
|
|
max: field.max * (channel.factor === null ? 1 / gravity : channel.factor),
|
|
shape: field.shape.slice()
|
|
});
|
|
});
|
|
if (fields.some(field => !field || !Number.isFinite(field.min) || !Number.isFinite(field.max) || field.max < field.min)) return null;
|
|
const [reference] = fields;
|
|
if (fields.some(field => field.shape[2] !== reference.shape[2] || field.shape[3] !== reference.shape[3])) return null;
|
|
const timeCount = Math.max(...fields.map(field => field.shape[0]));
|
|
if (fields.some(field => field.shape[0] !== 1 && field.shape[0] !== timeCount)) return null;
|
|
return { channels: fields, timeCount };
|
|
}
|
|
|
|
function normalise(value, channel) {
|
|
if (!channel || !Number.isFinite(value)) return null;
|
|
const converted = value * channel.factor;
|
|
if (!Number.isFinite(converted)) return null;
|
|
if (channel.max === channel.min) return converted === 0 ? 0 : 0.5;
|
|
return Math.max(0, Math.min(1, (converted - channel.min) / (channel.max - channel.min)));
|
|
}
|
|
|
|
root.ExoComposite = { create, normalise };
|
|
})(window);
|