metar-taf-parser
Version:
Parse METAR and TAF reports
1,335 lines (1,295 loc) • 122 kB
JavaScript
import en from './locale/en.js';
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */
function __classPrivateFieldGet(receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
}
function __classPrivateFieldSet(receiver, state, value, kind, f) {
if (kind === "m") throw new TypeError("Private method is not writable");
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
}
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
var e = new Error(message);
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
};
class ParseError extends Error {
constructor(message) {
super(message);
this.name = "ParseError";
Object.setPrototypeOf(this, new.target.prototype);
}
}
class InvalidWeatherStatementError extends ParseError {
constructor(cause) {
super(typeof cause === "string"
? `Invalid weather string: ${cause}`
: "Invalid weather string");
this.name = "InvalidWeatherStatementError";
Object.setPrototypeOf(this, new.target.prototype);
if (typeof cause !== "string")
this.cause = cause;
}
}
/**
* Thrown when an input contains data elements that are recognized but
* intentionally not supported.
*/
class PartialWeatherStatementError extends InvalidWeatherStatementError {
constructor(partialMessage, part, total) {
super(`Input is partial TAF (${partialMessage}), see: https://github.com/aeharding/metar-taf-parser/issues/68`);
this.name = "PartialWeatherStatementError";
Object.setPrototypeOf(this, new.target.prototype);
this.part = part;
this.total = total;
}
}
/**
* Thrown when command marked as canParse, but couldn't parse when
* executing (for example, an invalid CloudQuantity)
*/
class CommandExecutionError extends ParseError {
constructor(message) {
super(message);
this.name = "CommandExecutionError";
Object.setPrototypeOf(this, new.target.prototype);
}
}
/**
* Should never occur
*/
class UnexpectedParseError extends ParseError {
constructor(message) {
super(message);
this.name = "UnexpectedParseError";
Object.setPrototypeOf(this, new.target.prototype);
}
}
/**
* Split behaving similar to Python's implementation
*/
function pySplit(string, separator, n) {
let split = string.split(separator);
// Note: Python implementation will automatically trim empty values if
// separator is undefined. Since this function is kinda meh, we'll just do it
// for any spaces (pretty close to their implementation, since a space is the
// default character to split on)
//
// https://docs.python.org/3/library/stdtypes.html?highlight=split#str.split
if (separator === " ")
split = split.filter((n) => n);
if (n == null || split.length <= n)
return split;
const out = split.slice(0, n);
out.push(split.slice(n).join(separator));
return out;
}
/**
* Access nested object properties by string path
*
* https://stackoverflow.com/a/22129960
*/
function resolve(obj, path, separator = ".") {
const properties = Array.isArray(path) ? path : path.split(separator);
return properties.reduce((prev, curr) => prev?.[curr], obj);
}
/**
* For safely casting input values
* @param input String that is expected to be in the snum
* @param enumExpected The enum to cast the input value to
* @throws RemarkExecutionError when input is not a key of enum
*/
function as(input, enumExpected) {
if (!Object.values(enumExpected).includes(input))
throw new CommandExecutionError(`${input} not found in ${Object.values(enumExpected)}`);
return input;
}
function _(path, lang) {
const translation = resolve(lang, path);
if (!translation || typeof translation !== "string")
return undefined;
return translation;
}
function format(message, ...args) {
if (!message)
return;
// All arguments must be defined, otherwise nothing is returned
for (const arg of args) {
if (arg === undefined)
return;
}
return message.replace(/{\d+}/g, (match) => {
const index = +match.slice(1, -1);
return `${args[index]}`;
});
}
class Command {
constructor(locale) {
this.locale = locale;
}
}
var _CeilingHeightCommand_regex;
class CeilingHeightCommand extends Command {
constructor() {
super(...arguments);
_CeilingHeightCommand_regex.set(this, /^CIG (\d{3})V(\d{3})\b/);
}
canParse(code) {
return __classPrivateFieldGet(this, _CeilingHeightCommand_regex, "f").test(code);
}
execute(code, remark) {
const matches = code.match(__classPrivateFieldGet(this, _CeilingHeightCommand_regex, "f"));
if (!matches)
throw new UnexpectedParseError("Match not found");
const min = +matches[1] * 100;
const max = +matches[2] * 100;
const description = format(_("Remark.Ceiling.Height", this.locale), min, max);
remark.push({
type: RemarkType.CeilingHeight,
description,
raw: matches[0],
min,
max,
});
return [code.replace(__classPrivateFieldGet(this, _CeilingHeightCommand_regex, "f"), "").trim(), remark];
}
}
_CeilingHeightCommand_regex = new WeakMap();
var _CeilingSecondLocationCommand_regex;
class CeilingSecondLocationCommand extends Command {
constructor() {
super(...arguments);
_CeilingSecondLocationCommand_regex.set(this, /^CIG (\d{3}) (\w+)\b/);
}
canParse(code) {
return __classPrivateFieldGet(this, _CeilingSecondLocationCommand_regex, "f").test(code);
}
execute(code, remark) {
const matches = code.match(__classPrivateFieldGet(this, _CeilingSecondLocationCommand_regex, "f"));
if (!matches)
throw new UnexpectedParseError("Match not found");
const height = +matches[1] * 100;
const location = matches[2];
const description = format(_("Remark.Ceiling.Second.Location", this.locale), height, location);
remark.push({
type: RemarkType.CeilingSecondLocation,
description,
raw: matches[0],
height,
location,
});
return [code.replace(__classPrivateFieldGet(this, _CeilingSecondLocationCommand_regex, "f"), "").trim(), remark];
}
}
_CeilingSecondLocationCommand_regex = new WeakMap();
var MetarType;
(function (MetarType) {
MetarType["METAR"] = "METAR";
MetarType["SPECI"] = "SPECI";
})(MetarType || (MetarType = {}));
var CloudQuantity;
(function (CloudQuantity) {
/**
* Sky clear
*/
CloudQuantity["SKC"] = "SKC";
/**
* Few
*/
CloudQuantity["FEW"] = "FEW";
/**
* Broken
*/
CloudQuantity["BKN"] = "BKN";
/**
* Scattered
*/
CloudQuantity["SCT"] = "SCT";
/**
* Overcast
*/
CloudQuantity["OVC"] = "OVC";
/**
* No significant cloud
*/
CloudQuantity["NSC"] = "NSC";
})(CloudQuantity || (CloudQuantity = {}));
var CloudType;
(function (CloudType) {
/**
* Cumulonimbus
*/
CloudType["CB"] = "CB";
/**
* Towering cumulus, cumulus congestus
*/
CloudType["TCU"] = "TCU";
/**
* Cirrus
*/
CloudType["CI"] = "CI";
/**
* Cirrocumulus
*/
CloudType["CC"] = "CC";
/**
* Cirrostratus
*/
CloudType["CS"] = "CS";
/**
* Altocumulus
*/
CloudType["AC"] = "AC";
/**
* Stratus
*/
CloudType["ST"] = "ST";
/**
* Cumulus
*/
CloudType["CU"] = "CU";
/**
* Astrostratus
*/
CloudType["AS"] = "AS";
/**
* Nimbostratus
*/
CloudType["NS"] = "NS";
/**
* Stratocumulus
*/
CloudType["SC"] = "SC";
})(CloudType || (CloudType = {}));
/**
* Moderate has no qualifier.
*/
var Intensity;
(function (Intensity) {
Intensity["LIGHT"] = "-";
/**
* Heavy or well-developed
*/
Intensity["HEAVY"] = "+";
Intensity["IN_VICINITY"] = "VC";
})(Intensity || (Intensity = {}));
var Descriptive;
(function (Descriptive) {
Descriptive["SHOWERS"] = "SH";
Descriptive["SHALLOW"] = "MI";
Descriptive["PATCHES"] = "BC";
Descriptive["PARTIAL"] = "PR";
Descriptive["DRIFTING"] = "DR";
Descriptive["THUNDERSTORM"] = "TS";
Descriptive["BLOWING"] = "BL";
Descriptive["FREEZING"] = "FZ";
})(Descriptive || (Descriptive = {}));
var Phenomenon;
(function (Phenomenon) {
Phenomenon["RAIN"] = "RA";
Phenomenon["DRIZZLE"] = "DZ";
Phenomenon["SNOW"] = "SN";
Phenomenon["SNOW_GRAINS"] = "SG";
Phenomenon["ICE_PELLETS"] = "PL";
Phenomenon["ICE_CRYSTALS"] = "IC";
Phenomenon["HAIL"] = "GR";
Phenomenon["SMALL_HAIL"] = "GS";
Phenomenon["UNKNOW_PRECIPITATION"] = "UP";
Phenomenon["FOG"] = "FG";
Phenomenon["VOLCANIC_ASH"] = "VA";
Phenomenon["MIST"] = "BR";
Phenomenon["HAZE"] = "HZ";
Phenomenon["WIDESPREAD_DUST"] = "DU";
Phenomenon["SMOKE"] = "FU";
Phenomenon["SAND"] = "SA";
Phenomenon["SPRAY"] = "PY";
Phenomenon["SQUALL"] = "SQ";
Phenomenon["SAND_WHIRLS"] = "PO";
Phenomenon["THUNDERSTORM"] = "TS";
Phenomenon["DUSTSTORM"] = "DS";
Phenomenon["SANDSTORM"] = "SS";
Phenomenon["FUNNEL_CLOUD"] = "FC";
Phenomenon["NO_SIGNIFICANT_WEATHER"] = "NSW";
})(Phenomenon || (Phenomenon = {}));
var TimeIndicator;
(function (TimeIndicator) {
TimeIndicator["AT"] = "AT";
TimeIndicator["FM"] = "FM";
TimeIndicator["TL"] = "TL";
})(TimeIndicator || (TimeIndicator = {}));
/**
* https://web.archive.org/web/20230318235549/https://aviationweather.gov/taf/decoder
*/
var WeatherChangeType;
(function (WeatherChangeType) {
/**
* FROM Group
*
* ie. `FM1600`
*
* The FM group is used when a rapid change, usually occuring in less than one
* hour, in prevailing conditions is expected. Typically, a rapid change of
* prevailing conditions to more or less a completely new set of prevailing
* conditions is associated with a synoptic feature passing through the
* terminal area (cold or warm frontal passage). Appended to the FM indicator
* is the four-digit hour and minute the change is expected to begin and
* continues until the next change group or until the end of the current
* forecast.
*
* A FM group will mark the beginning of a new line in a TAF report. Each FM
* group contains all the required elements -- wind, visibility, weather, and
* sky condition. Weather will be omitted in FM groups when it is not
* significant to aviation. FM groups will not include the contraction NSW.
*
* Examples:
*
* 1. `FM0100 SKC` - After 0100Z sky clear
* 2. `FM1430 OVC020` - After 1430Z ceiling two thousand overcast
*/
WeatherChangeType["FM"] = "FM";
/**
* BECOMING Group
*
* ie. `BECMG 2224`
*
* The BECMG group is used when a gradual change in conditions is expected
* over a longer time period, usually two hours. The time period when the
* change is expected is a four-digit group with the beginning hour and ending
* hour of the change period which follows the BECMG indicator. The gradual
* change will occur at an unspecified time within this time period. Only the
* conditions are carried over from the previous time group.
*
* Example:
*
* 1. `OVC012 BECMG 1416 BKN020` - Ceiling one thousand two hundred overcast.
* Then a gradual change to ceiling two thousand broken between 1400Z and
* 1600Z.
*/
WeatherChangeType["BECMG"] = "BECMG";
/**
* TEMPORARY Group
*
* ie. `TEMPO 1316`
*
* The TEMPO group is used for any conditions in wind, visibility, weather, or
* sky condition which are expected to last for generally less than an hour at
* a time (occasional), and are expected to occur during less than half the
* time period. The TEMPO indicator is followed by a four-digit group giving
* the beginning hour and ending hour of the time period during which the
* temporary conditions are expected. Only the changing forecast
* meteorological conditions are included in TEMPO groups. The omitted
* conditions are carried over from the previous time group.
*
* Examples:
*
* 1. `SCT030 TEMPO 1923 BKN030` - Three thousand scattered with occasional
* ceilings three thousand broken between 1900Z and 2300Z.
* 2. `4SM HZ TEMPO 0006 2SM BR HZ` - Visibility four in haze with occasional
* visibility two in mist and haze between 0000Z and 0600Z.
*/
WeatherChangeType["TEMPO"] = "TEMPO";
/**
* For periods up to 30 minutes (`INTER` or intermittent).
*
* Otherwise, similar to `TEMPO`
*/
WeatherChangeType["INTER"] = "INTER";
/**
* Probability Forecast
*
* ie. `PROB40 0006`
*
* The probability or chance of thunderstorms or other precipitation events
* occuring, along with associated weather conditions (wind, visibility, and
* sky conditions).
*
* The PROB40 group is used when the occurrence of thunderstorms or
* precipitation is in the 30% to less than 50% range, thus the probability
* value 40 is appended to the PROB contraction. This is followed by a
* four-digit group giving the beginning hour and ending hour of the time
* period during which the thunderstorms or precipitation is expected.
*
* Note: PROB40 will not be shown during the first six hours of a forecast.
*
* Examples:
*
* 1. `PROB40 2102 1/2SM +TSRA` - Chance between 2100Z and 0200Z of
* visibility one-half thunderstorm, heavy rain.
* 2. `PROB40 1014 1SM RASN` - Chance between 1000Z and 1400Z of visibility
* one rain and snow.
* 3. `PROB40 2024 2SM FZRA` - Chance between 2000Z and 0000Z of visibility
* two freezing rain.
*/
WeatherChangeType["PROB"] = "PROB";
})(WeatherChangeType || (WeatherChangeType = {}));
var Direction;
(function (Direction) {
Direction["E"] = "E";
Direction["ENE"] = "ENE";
Direction["ESE"] = "ESE";
Direction["N"] = "N";
Direction["NE"] = "NE";
Direction["NNE"] = "NNE";
Direction["NNW"] = "NNW";
Direction["NW"] = "NW";
Direction["S"] = "S";
Direction["SE"] = "SE";
Direction["SSE"] = "SSE";
Direction["SSW"] = "SSW";
Direction["SW"] = "SW";
Direction["W"] = "W";
Direction["WNW"] = "WNW";
Direction["WSW"] = "WSW";
})(Direction || (Direction = {}));
var DistanceUnit;
(function (DistanceUnit) {
DistanceUnit["Meters"] = "m";
DistanceUnit["StatuteMiles"] = "SM";
})(DistanceUnit || (DistanceUnit = {}));
var SpeedUnit;
(function (SpeedUnit) {
SpeedUnit["Knot"] = "KT";
SpeedUnit["MetersPerSecond"] = "MPS";
SpeedUnit["KilometersPerHour"] = "KM/H";
})(SpeedUnit || (SpeedUnit = {}));
/**
* Used to indicate the actual value is greater than or less than the value written
*
* For example,
*
* 1. `P6SM` = visibility greater than 6 statute miles
* 2. `M1/4SM` = visibility less than 1/4 statute mile
*/
var ValueIndicator;
(function (ValueIndicator) {
ValueIndicator["GreaterThan"] = "P";
ValueIndicator["LessThan"] = "M";
})(ValueIndicator || (ValueIndicator = {}));
var RunwayInfoTrend;
(function (RunwayInfoTrend) {
RunwayInfoTrend["Uprising"] = "U";
RunwayInfoTrend["Decreasing"] = "D";
RunwayInfoTrend["NoSignificantChange"] = "N";
})(RunwayInfoTrend || (RunwayInfoTrend = {}));
var RunwayInfoUnit;
(function (RunwayInfoUnit) {
RunwayInfoUnit["Feet"] = "FT";
RunwayInfoUnit["Meters"] = "m";
})(RunwayInfoUnit || (RunwayInfoUnit = {}));
var IcingIntensity;
(function (IcingIntensity) {
/**
* Trace Icing or None.
*
* Air Force code 0 means a trace of icing.
* World Meteorological Organization code 0 means no icing
*/
IcingIntensity["None"] = "0";
/** Light Mixed Icing. */
IcingIntensity["Light"] = "1";
/** Light Rime Icing In Cloud. */
IcingIntensity["LightRimeIcingCloud"] = "2";
/** Light Clear Icing In Precipitation. */
IcingIntensity["LightClearIcingPrecipitation"] = "3";
/** Moderate Mixed Icing. */
IcingIntensity["ModerateMixedIcing"] = "4";
/** Moderate Rime Icing In Cloud. */
IcingIntensity["ModerateRimeIcingCloud"] = "5";
/** Moderate Clear Icing In Precipitation. */
IcingIntensity["ModerateClearIcingPrecipitation"] = "6";
/** Severe Mixed Icing. */
IcingIntensity["SevereMixedIcing"] = "7";
/** Severe Rime Icing In Cloud. */
IcingIntensity["SevereRimeIcingCloud"] = "8";
/** Severe Clear Icing In Precipitation. */
IcingIntensity["SevereClearIcingPrecipitation"] = "9";
})(IcingIntensity || (IcingIntensity = {}));
var TurbulenceIntensity;
(function (TurbulenceIntensity) {
/** None. */
TurbulenceIntensity["None"] = "0";
/** Light turbulence. */
TurbulenceIntensity["Light"] = "1";
/** Moderate turbulence in clear air, occasional. */
TurbulenceIntensity["ModerateClearAirOccasional"] = "2";
/** Moderate turbulence in clear air, frequent. */
TurbulenceIntensity["ModerateClearAirFrequent"] = "3";
/** Moderate turbulence in cloud, occasional. */
TurbulenceIntensity["ModerateCloudOccasional"] = "4";
/** Moderate turbulence in cloud, frequent. */
TurbulenceIntensity["ModerateCloudFrequent"] = "5";
/** Severe turbulence in clear air, occasional. */
TurbulenceIntensity["SevereClearAirOccasional"] = "6";
/** Severe turbulence in clear air, frequent. */
TurbulenceIntensity["SevereClearAirFrequent"] = "7";
/** Severe turbulence in cloud, occasional. */
TurbulenceIntensity["SevereCloudOccasional"] = "8";
/** Severe turbulence in cloud, frequent. */
TurbulenceIntensity["SevereCloudFrequent"] = "9";
/** Extreme turbulence */
TurbulenceIntensity["Extreme"] = "X";
})(TurbulenceIntensity || (TurbulenceIntensity = {}));
var DepositType;
(function (DepositType) {
/** (runway clearance in progress) */
DepositType["NotReported"] = "/";
DepositType["ClearDry"] = "0";
DepositType["Damp"] = "1";
DepositType["WetWaterPatches"] = "2";
DepositType["RimeFrostCovered"] = "3";
DepositType["DrySnow"] = "4";
DepositType["WetSnow"] = "5";
DepositType["Slush"] = "6";
DepositType["Ice"] = "7";
DepositType["CompactedSnow"] = "8";
DepositType["FrozenRidges"] = "9";
})(DepositType || (DepositType = {}));
var DepositCoverage;
(function (DepositCoverage) {
/**
* Only reported by certain countries (e.g. Russia)
*/
DepositCoverage["None"] = "0";
/**
* Not reported (e.g. due to rwy clearance in progress)
*/
DepositCoverage["NotReported"] = "/";
DepositCoverage["Less10"] = "1";
DepositCoverage["From11To25"] = "2";
DepositCoverage["From26To50"] = "5";
DepositCoverage["From51To100"] = "9";
})(DepositCoverage || (DepositCoverage = {}));
var AltimeterUnit;
(function (AltimeterUnit) {
/**
* Inches of mercury (inHg)
*
* e.g. A2994 parses as 29.94 inHg
*/
AltimeterUnit["InHg"] = "inHg";
/**
* Hectopascals (hPa), also known as millibars
*
* e.g. Q1018 parses as 1018 millibars
*/
AltimeterUnit["HPa"] = "hPa";
})(AltimeterUnit || (AltimeterUnit = {}));
function degreesToCardinal(input) {
const degrees = +input;
if (isNaN(degrees))
return "VRB";
const dirs = [
"N",
"NNE",
"NE",
"ENE",
"E",
"ESE",
"SE",
"SSE",
"S",
"SSW",
"SW",
"WSW",
"W",
"WNW",
"NW",
"NNW",
];
const ix = Math.floor((degrees + 11.25) / 22.5);
return dirs[ix % 16];
}
function convertVisibility(input) {
if (input === "9999")
return {
indicator: ValueIndicator.GreaterThan,
value: +input,
unit: DistanceUnit.Meters,
};
return {
value: +input,
unit: DistanceUnit.Meters,
};
}
/**
* @param input May start with P or M, and must end with SM
* @returns Distance
*/
function convertNauticalMilesVisibility(input) {
let indicator;
let index = 0;
if (input.startsWith("P")) {
indicator = ValueIndicator.GreaterThan;
index = 1;
}
else if (input.startsWith("M")) {
indicator = ValueIndicator.LessThan;
index = 1;
}
return {
indicator,
value: convertFractionalAmount(input.slice(index, -2)),
unit: DistanceUnit.StatuteMiles,
};
}
/**
* Converts fractional and/or whole amounts
*
* Example "1/3", "1 1/3" and "1"
*/
function convertFractionalAmount(input) {
const [whole, fraction] = input.split(" ");
if (!fraction)
return parseFraction(whole);
return +whole + parseFraction(fraction);
}
function parseFraction(input) {
const [top, bottom] = input.split("/");
if (!bottom)
return +top;
return Math.round((+top / +bottom) * 100) / 100;
}
function convertTemperature(input) {
if (input.startsWith("M"))
return -pySplit(input, "M")[1];
return +input;
}
/**
* Converts number `.toFixed(1)` before outputting to match python implementation
*/
function convertTemperatureRemarks(sign, temperature) {
const temp = +temperature / 10;
if (sign === "0")
return temp;
return -temp;
}
function convertPrecipitationAmount(amount) {
return +amount / 100;
}
var _HailSizeCommand_regex;
class HailSizeCommand extends Command {
constructor() {
super(...arguments);
_HailSizeCommand_regex.set(this, /^GR ((\d\/\d)|((\d) ?(\d\/\d)?))/);
}
canParse(code) {
return __classPrivateFieldGet(this, _HailSizeCommand_regex, "f").test(code);
}
execute(code, remark) {
const matches = code.match(__classPrivateFieldGet(this, _HailSizeCommand_regex, "f"));
if (!matches)
throw new UnexpectedParseError("Match not found");
const description = format(_("Remark.Hail.0", this.locale), matches[1]);
remark.push({
type: RemarkType.HailSize,
description,
raw: matches[0],
size: convertFractionalAmount(matches[1]),
});
return [code.replace(__classPrivateFieldGet(this, _HailSizeCommand_regex, "f"), "").trim(), remark];
}
}
_HailSizeCommand_regex = new WeakMap();
var _HourlyMaximumMinimumTemperatureCommand_regex;
class HourlyMaximumMinimumTemperatureCommand extends Command {
constructor() {
super(...arguments);
_HourlyMaximumMinimumTemperatureCommand_regex.set(this, /^4([01])(\d{3})([01])(\d{3})\b/);
}
canParse(code) {
return __classPrivateFieldGet(this, _HourlyMaximumMinimumTemperatureCommand_regex, "f").test(code);
}
execute(code, remark) {
const matches = code.match(__classPrivateFieldGet(this, _HourlyMaximumMinimumTemperatureCommand_regex, "f"));
if (!matches)
throw new UnexpectedParseError("Match not found");
const description = format(_("Remark.Hourly.Maximum.Minimum.Temperature", this.locale), convertTemperatureRemarks(matches[1], matches[2]).toFixed(1), convertTemperatureRemarks(matches[3], matches[4]).toFixed(1));
remark.push({
type: RemarkType.HourlyMaximumMinimumTemperature,
description: description,
raw: matches[0],
max: convertTemperatureRemarks(matches[1], matches[2]),
min: convertTemperatureRemarks(matches[3], matches[4]),
});
return [code.replace(__classPrivateFieldGet(this, _HourlyMaximumMinimumTemperatureCommand_regex, "f"), "").trim(), remark];
}
}
_HourlyMaximumMinimumTemperatureCommand_regex = new WeakMap();
var _HourlyMaximumTemperatureCommand_regex;
class HourlyMaximumTemperatureCommand extends Command {
constructor() {
super(...arguments);
_HourlyMaximumTemperatureCommand_regex.set(this, /^1([01])(\d{3})\b/);
}
canParse(code) {
return __classPrivateFieldGet(this, _HourlyMaximumTemperatureCommand_regex, "f").test(code);
}
execute(code, remark) {
const matches = code.match(__classPrivateFieldGet(this, _HourlyMaximumTemperatureCommand_regex, "f"));
if (!matches)
throw new UnexpectedParseError("Match not found");
const description = format(_("Remark.Hourly.Maximum.Temperature", this.locale), convertTemperatureRemarks(matches[1], matches[2]).toFixed(1));
remark.push({
type: RemarkType.HourlyMaximumTemperature,
description: description,
raw: matches[0],
max: convertTemperatureRemarks(matches[1], matches[2]),
});
return [code.replace(__classPrivateFieldGet(this, _HourlyMaximumTemperatureCommand_regex, "f"), "").trim(), remark];
}
}
_HourlyMaximumTemperatureCommand_regex = new WeakMap();
var _HourlyMinimumTemperatureCommand_regex;
class HourlyMinimumTemperatureCommand extends Command {
constructor() {
super(...arguments);
_HourlyMinimumTemperatureCommand_regex.set(this, /^2([01])(\d{3})\b/);
}
canParse(code) {
return __classPrivateFieldGet(this, _HourlyMinimumTemperatureCommand_regex, "f").test(code);
}
execute(code, remark) {
const matches = code.match(__classPrivateFieldGet(this, _HourlyMinimumTemperatureCommand_regex, "f"));
if (!matches)
throw new UnexpectedParseError("Match not found");
const description = format(_("Remark.Hourly.Minimum.Temperature", this.locale), convertTemperatureRemarks(matches[1], matches[2]).toFixed(1));
remark.push({
type: RemarkType.HourlyMinimumTemperature,
description,
raw: matches[0],
min: convertTemperatureRemarks(matches[1], matches[2]),
});
return [code.replace(__classPrivateFieldGet(this, _HourlyMinimumTemperatureCommand_regex, "f"), "").trim(), remark];
}
}
_HourlyMinimumTemperatureCommand_regex = new WeakMap();
var _HourlyPrecipitationAmountCommand_regex;
class HourlyPrecipitationAmountCommand extends Command {
constructor() {
super(...arguments);
_HourlyPrecipitationAmountCommand_regex.set(this, /^P(\d{4})\b/);
}
canParse(code) {
return __classPrivateFieldGet(this, _HourlyPrecipitationAmountCommand_regex, "f").test(code);
}
execute(code, remark) {
const matches = code.match(__classPrivateFieldGet(this, _HourlyPrecipitationAmountCommand_regex, "f"));
if (!matches)
throw new UnexpectedParseError("Match not found");
const amount = +matches[1];
const description = format(_("Remark.Precipitation.Amount.Hourly", this.locale), amount);
remark.push({
type: RemarkType.HourlyPrecipitationAmount,
description,
raw: matches[0],
amount: amount / 100,
});
return [code.replace(__classPrivateFieldGet(this, _HourlyPrecipitationAmountCommand_regex, "f"), "").trim(), remark];
}
}
_HourlyPrecipitationAmountCommand_regex = new WeakMap();
var _HourlyPressureCommand_regex;
class HourlyPressureCommand extends Command {
constructor() {
super(...arguments);
_HourlyPressureCommand_regex.set(this, /^5(\d)(\d{3})\b/);
}
canParse(code) {
return __classPrivateFieldGet(this, _HourlyPressureCommand_regex, "f").test(code);
}
execute(code, remark) {
const matches = code.match(__classPrivateFieldGet(this, _HourlyPressureCommand_regex, "f"));
if (!matches)
throw new UnexpectedParseError("Match not found");
const part1 = _(`Remark.Barometer.${+matches[1]}`, this.locale);
const part2 = format(_("Remark.Pressure.Tendency", this.locale), +matches[2] / 10);
const description = part1 != null && part2 != null ? `${part1} ${part2}` : undefined;
remark.push({
type: RemarkType.HourlyPressure,
description,
raw: matches[0],
code: +matches[1],
pressureChange: +matches[2] / 10,
});
return [code.replace(__classPrivateFieldGet(this, _HourlyPressureCommand_regex, "f"), "").trim(), remark];
}
}
_HourlyPressureCommand_regex = new WeakMap();
var _HourlyTemperatureDewPointCommand_regex;
class HourlyTemperatureDewPointCommand extends Command {
constructor() {
super(...arguments);
_HourlyTemperatureDewPointCommand_regex.set(this, /^T([01])(\d{3})(([01])(\d{3}))?/);
}
canParse(code) {
return __classPrivateFieldGet(this, _HourlyTemperatureDewPointCommand_regex, "f").test(code);
}
execute(code, remark) {
const matches = code.match(__classPrivateFieldGet(this, _HourlyTemperatureDewPointCommand_regex, "f"));
if (!matches)
throw new UnexpectedParseError("Match not found");
const temperature = convertTemperatureRemarks(matches[1], matches[2]);
if (!matches[3]) {
const description = format(_("Remark.Hourly.Temperature.0", this.locale), temperature.toFixed(1));
remark.push({
type: RemarkType.HourlyTemperatureDewPoint,
description,
raw: matches[0],
temperature,
});
}
else {
const dewPoint = convertTemperatureRemarks(matches[4], matches[5]);
const description = format(_("Remark.Hourly.Temperature.Dew.Point", this.locale), temperature.toFixed(1), dewPoint.toFixed(1));
remark.push({
type: RemarkType.HourlyTemperatureDewPoint,
description,
raw: matches[0],
temperature,
dewPoint,
});
}
return [code.replace(__classPrivateFieldGet(this, _HourlyTemperatureDewPointCommand_regex, "f"), "").trim(), remark];
}
}
_HourlyTemperatureDewPointCommand_regex = new WeakMap();
var _IceAccretionCommand_regex;
class IceAccretionCommand extends Command {
constructor() {
super(...arguments);
_IceAccretionCommand_regex.set(this, /^l(\d)(\d{3})\b/);
}
canParse(code) {
return __classPrivateFieldGet(this, _IceAccretionCommand_regex, "f").test(code);
}
execute(code, remark) {
const matches = code.match(__classPrivateFieldGet(this, _IceAccretionCommand_regex, "f"));
if (!matches)
throw new UnexpectedParseError("Match not found");
const description = format(_("Remark.Ice.Accretion.Amount", this.locale), +matches[2], +matches[1]);
remark.push({
type: RemarkType.IceAccretion,
description,
raw: matches[0],
amount: +matches[2] / 100,
periodInHours: +matches[1],
});
return [code.replace(__classPrivateFieldGet(this, _IceAccretionCommand_regex, "f"), "").trim(), remark];
}
}
_IceAccretionCommand_regex = new WeakMap();
var _ObscurationCommand_regex;
class ObscurationCommand extends Command {
constructor() {
super(...arguments);
_ObscurationCommand_regex.set(this, /^([A-Z]{2}) ([A-Z]{3})(\d{3})/);
}
canParse(code) {
return __classPrivateFieldGet(this, _ObscurationCommand_regex, "f").test(code);
}
execute(code, remark) {
const matches = code.match(__classPrivateFieldGet(this, _ObscurationCommand_regex, "f"));
if (!matches)
throw new UnexpectedParseError("Match not found");
const quantity = as(matches[2], CloudQuantity);
const height = 100 * +matches[3];
const phenomenon = as(matches[1], Phenomenon);
const description = format(_("Remark.Obscuration", this.locale), _(`CloudQuantity.${quantity}`, this.locale), height, _(`Phenomenon.${phenomenon}`, this.locale));
remark.push({
type: RemarkType.Obscuration,
description,
raw: matches[0],
quantity,
height,
phenomenon,
});
return [code.replace(__classPrivateFieldGet(this, _ObscurationCommand_regex, "f"), "").trim(), remark];
}
}
_ObscurationCommand_regex = new WeakMap();
var _PrecipitationAmount24HourCommand_regex;
class PrecipitationAmount24HourCommand extends Command {
constructor() {
super(...arguments);
_PrecipitationAmount24HourCommand_regex.set(this, /^7(\d{4})\b/);
}
canParse(code) {
return __classPrivateFieldGet(this, _PrecipitationAmount24HourCommand_regex, "f").test(code);
}
execute(code, remark) {
const matches = code.match(__classPrivateFieldGet(this, _PrecipitationAmount24HourCommand_regex, "f"));
if (!matches)
throw new UnexpectedParseError("Match not found");
const amount = convertPrecipitationAmount(matches[1]);
const description = format(_("Remark.Precipitation.Amount.24", this.locale), amount);
remark.push({
type: RemarkType.PrecipitationAmount24Hour,
description,
raw: matches[0],
amount,
});
return [code.replace(__classPrivateFieldGet(this, _PrecipitationAmount24HourCommand_regex, "f"), "").trim(), remark];
}
}
_PrecipitationAmount24HourCommand_regex = new WeakMap();
var _PrecipitationAmount36HourCommand_regex;
class PrecipitationAmount36HourCommand extends Command {
constructor() {
super(...arguments);
_PrecipitationAmount36HourCommand_regex.set(this, /^([36])(\d{4})\b/);
}
canParse(code) {
return __classPrivateFieldGet(this, _PrecipitationAmount36HourCommand_regex, "f").test(code);
}
execute(code, remark) {
const matches = code.match(__classPrivateFieldGet(this, _PrecipitationAmount36HourCommand_regex, "f"));
if (!matches)
throw new UnexpectedParseError("Match not found");
const periodInHours = +matches[1];
const amount = convertPrecipitationAmount(matches[2]);
const description = format(_("Remark.Precipitation.Amount.3.6", this.locale), periodInHours, amount);
remark.push({
type: RemarkType.PrecipitationAmount36Hour,
description,
raw: matches[0],
periodInHours,
amount,
});
return [code.replace(__classPrivateFieldGet(this, _PrecipitationAmount36HourCommand_regex, "f"), "").trim(), remark];
}
}
_PrecipitationAmount36HourCommand_regex = new WeakMap();
var _PrecipitationBegEndCommand_regex;
class PrecipitationBegEndCommand extends Command {
constructor() {
super(...arguments);
_PrecipitationBegEndCommand_regex.set(this, /^(([A-Z]{2})?([A-Z]{2})B(\d{2})?(\d{2})E(\d{2})?(\d{2}))/);
}
canParse(code) {
return __classPrivateFieldGet(this, _PrecipitationBegEndCommand_regex, "f").test(code);
}
execute(code, remark) {
const matches = code.match(__classPrivateFieldGet(this, _PrecipitationBegEndCommand_regex, "f"));
if (!matches)
throw new UnexpectedParseError("Match not found");
const descriptive = matches[2] ? as(matches[2], Descriptive) : undefined;
const phenomenon = as(matches[3], Phenomenon);
const description = format(_("Remark.Precipitation.Beg.End", this.locale), descriptive ? _(`Descriptive.${descriptive}`, this.locale) : "", _(`Phenomenon.${phenomenon}`, this.locale), matches[4] || "", matches[5], matches[6] || "", matches[7]);
remark.push({
type: RemarkType.PrecipitationBegEnd,
description,
raw: matches[0],
descriptive,
phenomenon,
startHour: matches[4] ? +matches[4] : undefined,
startMin: +matches[5],
endHour: matches[6] ? +matches[6] : undefined,
endMin: +matches[7],
});
return [code.replace(__classPrivateFieldGet(this, _PrecipitationBegEndCommand_regex, "f"), "").trim(), remark];
}
}
_PrecipitationBegEndCommand_regex = new WeakMap();
var _PrevailingVisibilityCommand_regex;
class PrevailingVisibilityCommand extends Command {
constructor() {
super(...arguments);
_PrevailingVisibilityCommand_regex.set(this, /^VIS ((\d)*( )?(\d?\/?\d))V((\d)*( )?(\d?\/?\d))/);
}
canParse(code) {
return __classPrivateFieldGet(this, _PrevailingVisibilityCommand_regex, "f").test(code);
}
execute(code, remark) {
const matches = code.match(__classPrivateFieldGet(this, _PrevailingVisibilityCommand_regex, "f"));
if (!matches)
throw new UnexpectedParseError("Match not found");
const minVisibility = matches[1];
const maxVisibility = matches[5];
const description = format(_("Remark.Variable.Prevailing.Visibility", this.locale), minVisibility, maxVisibility);
remark.push({
type: RemarkType.PrevailingVisibility,
description,
raw: matches[0],
minVisibility: convertFractionalAmount(minVisibility),
maxVisibility: convertFractionalAmount(maxVisibility),
});
return [code.replace(__classPrivateFieldGet(this, _PrevailingVisibilityCommand_regex, "f"), "").trim(), remark];
}
}
_PrevailingVisibilityCommand_regex = new WeakMap();
var _SeaLevelPressureCommand_regex;
class SeaLevelPressureCommand extends Command {
constructor() {
super(...arguments);
_SeaLevelPressureCommand_regex.set(this, /^SLP(\d{2})(\d)/);
}
canParse(code) {
return __classPrivateFieldGet(this, _SeaLevelPressureCommand_regex, "f").test(code);
}
execute(code, remark) {
const matches = code.match(__classPrivateFieldGet(this, _SeaLevelPressureCommand_regex, "f"));
if (!matches)
throw new UnexpectedParseError("Match not found");
let pressure = matches[1].startsWith("9") ? "9" : "10";
pressure += matches[1] + "." + matches[2];
const description = format(_("Remark.Sea.Level.Pressure", this.locale), pressure);
remark.push({
type: RemarkType.SeaLevelPressure,
description,
raw: matches[0],
pressure: +pressure,
});
return [code.replace(__classPrivateFieldGet(this, _SeaLevelPressureCommand_regex, "f"), "").trim(), remark];
}
}
_SeaLevelPressureCommand_regex = new WeakMap();
var _SecondLocationVisibilityCommand_regex;
class SecondLocationVisibilityCommand extends Command {
constructor() {
super(...arguments);
_SecondLocationVisibilityCommand_regex.set(this, /^VIS ((\d)*( )?(\d?\/?\d)) (\w+)/);
}
canParse(code) {
return __classPrivateFieldGet(this, _SecondLocationVisibilityCommand_regex, "f").test(code);
}
execute(code, remark) {
const matches = code.match(__classPrivateFieldGet(this, _SecondLocationVisibilityCommand_regex, "f"));
if (!matches)
throw new UnexpectedParseError("Match not found");
const distance = matches[1];
const location = matches[5];
const description = format(_("Remark.Second.Location.Visibility", this.locale), distance, location);
remark.push({
type: RemarkType.SecondLocationVisibility,
description,
raw: matches[0],
distance: convertFractionalAmount(distance),
location,
});
return [code.replace(__classPrivateFieldGet(this, _SecondLocationVisibilityCommand_regex, "f"), "").trim(), remark];
}
}
_SecondLocationVisibilityCommand_regex = new WeakMap();
var _SectorVisibilityCommand_regex;
class SectorVisibilityCommand extends Command {
constructor() {
super(...arguments);
_SectorVisibilityCommand_regex.set(this, /^VIS ([A-Z]{1,2}) ((\d)*( )?(\d?\/?\d))/);
}
canParse(code) {
return __classPrivateFieldGet(this, _SectorVisibilityCommand_regex, "f").test(code);
}
execute(code, remark) {
const matches = code.match(__classPrivateFieldGet(this, _SectorVisibilityCommand_regex, "f"));
if (!matches)
throw new UnexpectedParseError("Match not found");
const direction = as(matches[1], Direction);
const description = format(_("Remark.Sector.Visibility", this.locale), _(`Converter.${direction}`, this.locale), matches[2]);
remark.push({
type: RemarkType.SectorVisibility,
description,
raw: matches[0],
direction,
distance: convertFractionalAmount(matches[2]),
});
return [code.replace(__classPrivateFieldGet(this, _SectorVisibilityCommand_regex, "f"), "").trim(), remark];
}
}
_SectorVisibilityCommand_regex = new WeakMap();
var _SmallHailSizeCommand_regex;
class SmallHailSizeCommand extends Command {
constructor() {
super(...arguments);
_SmallHailSizeCommand_regex.set(this, /^GR LESS THAN ((\d )?(\d\/\d)?)/);
}
canParse(code) {
return __classPrivateFieldGet(this, _SmallHailSizeCommand_regex, "f").test(code);
}
execute(code, remark) {
const matches = code.match(__classPrivateFieldGet(this, _SmallHailSizeCommand_regex, "f"));
if (!matches)
throw new UnexpectedParseError("Match not found");
const description = format(_("Remark.Hail.LesserThan", this.locale), matches[1]);
remark.push({
type: RemarkType.SmallHailSize,
description,
raw: matches[0],
size: convertFractionalAmount(matches[1]),
});
return [code.replace(__classPrivateFieldGet(this, _SmallHailSizeCommand_regex, "f"), "").trim(), remark];
}
}
_SmallHailSizeCommand_regex = new WeakMap();
var _SnowDepthCommand_regex;
class SnowDepthCommand extends Command {
constructor() {
super(...arguments);
_SnowDepthCommand_regex.set(this, /^4\/(\d{3})/);
}
canParse(code) {
return __classPrivateFieldGet(this, _SnowDepthCommand_regex, "f").test(code);
}
execute(code, remark) {
const matches = code.match(__classPrivateFieldGet(this, _SnowDepthCommand_regex, "f"));
if (!matches)
throw new UnexpectedParseError("Match not found");
const depth = +matches[1];
const description = format(_("Remark.Snow.Depth", this.locale), depth);
remark.push({
type: RemarkType.SnowDepth,
description,
raw: matches[0],
depth,
});
return [code.replace(__classPrivateFieldGet(this, _SnowDepthCommand_regex, "f"), "").trim(), remark];
}
}
_SnowDepthCommand_regex = new WeakMap();
var _SnowIncreaseCommand_regex;
class SnowIncreaseCommand extends Command {
constructor() {
super(...arguments);
_SnowIncreaseCommand_regex.set(this, /^SNINCR (\d+)\/(\d+)/);
}
canParse(code) {
return __classPrivateFieldGet(this, _SnowIncreaseCommand_regex, "f").test(code);
}
execute(code, remark) {
const matches = code.match(__classPrivateFieldGet(this, _SnowIncreaseCommand_regex, "f"));
if (!matches)
throw new UnexpectedParseError("Match not found");
const inchesLastHour = +matches[1];
const totalDepth = +matches[2];
const description = format(_("Remark.Snow.Increasing.Rapidly", this.locale), inchesLastHour, totalDepth);
remark.push({
type: RemarkType.SnowIncrease,
description,
raw: matches[0],
inchesLastHour,
totalDepth,
});
return [code.replace(__classPrivateFieldGet(this, _SnowIncreaseCommand_regex, "f"), "").trim(), remark];
}
}
_SnowIncreaseCommand_regex = new WeakMap();
var _SnowPelletsCommand_regex;
class SnowPelletsCommand extends Command {
constructor() {
super(...arguments);
_SnowPelletsCommand_regex.set(this, /^GS (LGT|MOD|HVY)/);
}
canParse(code) {
return __classPrivateFieldGet(this, _SnowPelletsCommand_regex, "f").test(code);
}
execute(code, remark) {
const matches = code.match(__classPrivateFieldGet(this, _SnowPelletsCommand_regex, "f"));
if (!matches)
throw new UnexpectedParseError("Match not found");
const description = format(_("Remark.Snow.Pellets", this.locale), _(`Remark.${matches[1]}`, this.locale));
remark.push({
type: RemarkType.SnowPellets,
description,
raw: matches[0],
amount: matches[1],
});
return [code.replace(__classPrivateFieldGet(this, _SnowPelletsCommand_regex, "f"), "").trim(), remark];
}
}
_SnowPelletsCommand_regex = new WeakMap();
var _SunshineDurationCommand_regex;
class SunshineDurationCommand extends Command {
constructor() {
super(...arguments);
_SunshineDurationCommand_regex.set(this, /^98(\d{3})/);
}
canParse(code) {
return __classPrivateFieldGet(this, _SunshineDurationCommand_regex, "f").test(code);
}
execute(code, remark) {
const matches = code.match(__classPrivateFieldGet(this, _SunshineDurationCommand_regex, "f"));
if (!matches)
throw new UnexpectedParseError("Match not found");
const duration = +matches[1];
const description = format(_("Remark.Sunshine.Duration", this.locale), duration);
remark.push({
type: RemarkType.SunshineDuration,
description,
raw: matches[0],
duration,
});
return [code.replace(__classPrivateFieldGet(this, _SunshineDurationCommand_regex, "f"), "").trim(), remark];
}
}
_SunshineDurationCommand_regex = new WeakMap();
var _SurfaceVisibilityCommand_regex;
class SurfaceVisibilityCommand extends Command {
constructor() {
super(...arguments);
_SurfaceVisibilityCommand_regex.set(this, /^SFC VIS ((\d)*( )?(\d?\/?\d))/);
}
canParse(code) {
return __classPrivateFieldGet(this, _SurfaceVisibilityCommand_regex, "f").test(code);
}
execute(code, remark) {
const matches = code.match(__classPrivateFieldGet(this, _SurfaceVisibilityCommand_regex, "f"));
if (!matches)
throw new UnexpectedParseError("Match not found");
const distance = matches[1];
const description = format(_("Remark.Surface.Visibility", this.locale), distance);
remark.push({
type: RemarkType.SurfaceVisibility,
description,
raw: matches[0],
distance: convertFractionalAmount(distance),
});
return [code.replace(__classPrivateFieldGet(this, _SurfaceVisibilityCommand_regex, "f"), "").trim(), remark];
}
}
_SurfaceVisibilityCommand_regex = new WeakMap();
var _ThunderStormLocationCommand_regex;
class ThunderStormLocationCommand extends Command {
constructor() {
super(...arguments);
_ThunderStormLocationCommand_regex.set(this, /^TS ([A-Z]{2})/);
}
canParse(code) {
return __classPrivateFieldGet(this, _ThunderStormLocationCommand_regex, "f").test(code);
}
execute(code, remark) {
const matches = code.match(__classPrivateFieldGet(this, _ThunderStormLocationCommand_regex, "f"));
if (!matches)
throw new UnexpectedParseError("Match not found");
const location = as(matches[1], Direction);
const description = format(_("Remark.Thunderstorm.Location.0", this.locale), _(`Converter.${location}`, this.locale));
remark.push({
type: RemarkType.ThunderStormLocation,
description,
raw: matches[0],
location,
});
return [code.replace(__classPrivateFieldGet(this, _ThunderStormLocationCommand_regex, "f"), "").trim(), remark];
}
}
_ThunderStormLocationCommand_regex = new WeakMap();
var _ThunderStormLocationMovingCommand_regex;
class ThunderStormLocationMovingCommand extends Command {
constructor() {
super(...arguments);
_ThunderStormLocationMovingCommand_regex.set(this, /^TS ([A-Z]{2}) MOV ([A-Z]{2})/);
}
canParse(code) {
return __classPrivateFie