metar-decoder
Version:
Convert METAR weather reports to JavaScript objects
146 lines (145 loc) • 5.58 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.decode = void 0;
const axios_1 = require("axios");
const constants_1 = require("./constants");
const utils_1 = require("./utils");
function decode(metar) {
const data = {};
const sections = metar.split(' ');
if (sections.length === 0)
return null;
data.rvr = [];
data.clouds = [];
data.weather = [];
data.auto = sections[1] === 'AUTO';
data.cavok = metar.includes('CAVOK');
if (data.cavok) {
data.visibility = 9999;
sections.splice(sections.indexOf('CAVOK'), 1);
}
// WEATHER
for (let i = sections.length - 1; i >= 0; i--) {
if (constants_1.weather[sections[i]]) {
const uncodedWeather = sections[i];
data.weather.push({
abbreviation: uncodedWeather,
meaning: constants_1.weather[uncodedWeather],
});
sections.splice(i, 1);
}
}
// AIRPORT
data.airport = sections.shift();
// TIME
const timeSection = sections.shift();
const now = new Date();
data.recorded_at = new Date(Date.UTC(now.getFullYear(), now.getMonth(), Number(timeSection.slice(0, 2)), Number(timeSection.slice(2, 4)), Number(timeSection.slice(4, 6))));
// WIND
const windSection = sections.shift();
data.wind = {};
data.wind.direction = windSection.slice(0, 3);
data.wind.speed = Number(windSection.slice(3, 5));
if (windSection[5] === 'G') {
data.wind.guest = Number(windSection.slice(5, 7));
data.wind.unit = windSection.slice(7, 9);
}
else {
data.wind.guest = null;
data.wind.unit = windSection.slice(5, 7);
}
// VARIATION
if (/(\d){3}V(\d){3}/g.test(sections[0])) {
const [min, max] = sections.shift().split('V').map(Number);
data.wind.variation = {
min,
max,
};
}
// VISIBILITY
if (!data.cavok) {
data.visibility = Number(sections.shift());
// MIN VISIBILITY
if ((0, utils_1.matchExact)(/(\d){4}(W|E|N|S|(NE)|(NW)|(SE)|(SW))?/, sections[0])) {
const min_visibility = sections.shift();
data.min_visibility = {
visibility: Number(min_visibility.slice(0, 4)),
direction: min_visibility.slice(4, min_visibility.length),
};
}
}
// RVR
while (sections[0].startsWith('R')) {
const [runway, visibility] = sections.shift().substring(1).split('/');
const isVariable = visibility.includes('V');
data.rvr.push({
runway,
visibility: !isVariable
? visibility.slice(0, visibility.length - 1)
: {
min: visibility.slice(0, 4),
max: visibility.slice(5, 9),
},
trend: constants_1.rvrTrend[visibility.slice(-1)],
});
}
// CLOUDS
if (/((SCT)|(BKN)|(FEW)|(OVC))(\d{3})((CB)|(TCB))?/.test(sections[0])) {
while (constants_1.clouds.map((c) => c.code).includes(sections[0].slice(0, 3))) {
const uncodedCloud = sections.shift();
const cloud = constants_1.clouds.find((c) => c.code === uncodedCloud.slice(0, 3));
if (!cloud)
continue;
data.clouds.push(Object.assign(Object.assign({}, cloud), { cumulonimbus: uncodedCloud.includes('CB'), altitude: Number(uncodedCloud.slice(3, 6)) * 100 }));
}
}
if (sections[0].startsWith('VV')) {
const vv = sections.shift();
data.vertical_visibility = vv.substring(2);
}
// TEMP
if (/(\d){2}\/(\d){2}/.test(sections[0])) {
const [temperature, dewpoint] = sections.shift().split('/').map(Number);
data.dewpoint = dewpoint,
data.temperature = temperature;
}
// BARO
if ((0, utils_1.matchExact)(/(Q|A)(\d){4}/, sections[0])) {
const baro = sections.shift();
data.qnh = baro.startsWith('A')
? (0, utils_1.inHgtoHpa)(Number(`${baro.slice(1, 3)}.${baro.slice(2, 5)}`))
: Number(baro.slice(1, 5));
}
// TREND
if (constants_1.trends[sections[0]]) {
data.trend = {
type: constants_1.trends[sections[0]],
full: sections.join(' '),
};
}
data.fetchAirport = () => __awaiter(this, void 0, void 0, function* () {
if (typeof data.airport === 'object')
return data.airport;
try {
const { data: json } = yield axios_1.default.get(`https://airports.larrrssss.workers.dev/${data.airport}`);
if (json.status && json.status === 404)
return null;
data.airport = json;
return data.airport;
}
catch (e) {
return null;
}
});
return data;
}
exports.decode = decode;
;