metar-taf-parser 7.0.0 → 7.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -175,6 +175,10 @@ function findSeaLevelPressure(remarks: Remark[]): number | undefined {
175
175
  }
176
176
  ```
177
177
 
178
+ ## Determining flight category, ceiling, etc
179
+
180
+ Because certain abstractions such as flight category and flight ceiling can vary by country, this logic is left up to you to implement. However, if you're looking for somewhere to start, check out the example site (based on United States flight rules) in [example/src/helpers/metarTaf.ts](https://github.com/aeharding/metar-taf-parser/blob/main/example/src/helpers/metarTaf.ts). Feel free to copy - it's MIT licensed.
181
+
178
182
  ## Development
179
183
 
180
184
  ### Example site
@@ -275,6 +275,57 @@ declare enum RunwayInfoUnit {
275
275
  Feet = "FT",
276
276
  Meters = "m"
277
277
  }
278
+ declare enum IcingIntensity {
279
+ /**
280
+ * Trace Icing or None.
281
+ *
282
+ * Air Force code 0 means a trace of icing.
283
+ * World Meteorological Organization code 0 means no icing
284
+ */
285
+ None = "0",
286
+ /** Light Mixed Icing. */
287
+ Light = "1",
288
+ /** Light Rime Icing In Cloud. */
289
+ LightRimeIcingCloud = "2",
290
+ /** Light Clear Icing In Precipitation. */
291
+ LightClearIcingPrecipitation = "3",
292
+ /** Moderate Mixed Icing. */
293
+ ModerateMixedIcing = "4",
294
+ /** Moderate Rime Icing In Cloud. */
295
+ ModerateRimeIcingCloud = "5",
296
+ /** Moderate Clear Icing In Precipitation. */
297
+ ModerateClearIcingPrecipitation = "6",
298
+ /** Severe Mixed Icing. */
299
+ SevereMixedIcing = "7",
300
+ /** Severe Rime Icing In Cloud. */
301
+ SevereRimeIcingCloud = "8",
302
+ /** Severe Clear Icing In Precipitation. */
303
+ SevereClearIcingPrecipitation = "9"
304
+ }
305
+ declare enum TurbulenceIntensity {
306
+ /** None. */
307
+ None = "0",
308
+ /** Light turbulence. */
309
+ Light = "1",
310
+ /** Moderate turbulence in clear air, occasional. */
311
+ ModerateClearAirOccasional = "2",
312
+ /** Moderate turbulence in clear air, frequent. */
313
+ ModerateClearAirFrequent = "3",
314
+ /** Moderate turbulence in cloud, occasional. */
315
+ ModerateCloudOccasional = "4",
316
+ /** Moderate turbulence in cloud, frequent. */
317
+ ModerateCloudFrequent = "5",
318
+ /** Severe turbulence in clear air, occasional. */
319
+ SevereClearAirOccasional = "6",
320
+ /** Severe turbulence in clear air, frequent. */
321
+ SevereClearAirFrequent = "7",
322
+ /** Severe turbulence in cloud, occasional. */
323
+ SevereCloudOccasional = "8",
324
+ /** Severe turbulence in cloud, frequent. */
325
+ SevereCloudFrequent = "9",
326
+ /** Extreme turbulence */
327
+ Extreme = "X"
328
+ }
278
329
 
279
330
  declare const _default: {
280
331
  CloudQuantity: {
@@ -1042,22 +1093,6 @@ declare enum RemarkType {
1042
1093
  }
1043
1094
  declare type Remark = IUnknownRemark | IDefaultCommandRemark | ICeilingHeightRemark | ICeilingSecondLocationRemark | IHailSizeRemark | IHourlyMaximumMinimumTemperatureRemark | IHourlyMaximumTemperatureRemark | IHourlyMinimumTemperatureRemark | IHourlyPrecipitationAmountRemark | IHourlyPressureRemark | IHourlyTemperatureDewPointRemark | IIceAccretionRemark | IObscurationRemark | IPrecipitationAmount24HourRemark | IPrecipitationAmount36HourRemark | IPrecipitationAmount36HourRemark | IPrecipitationBegRemark | IPrecipitationBegEndRemark | IPrecipitationEndRemark | IPrevailingVisibilityRemark | ISeaLevelPressureRemark | ISecondLocationVisibilityRemark | ISectorVisibilityRemark | ISmallHailSizeRemark | ISnowDepthRemark | ISnowIncreaseRemark | ISnowPelletsRemark | ISunshineDurationRemark | ISurfaceVisibilityRemark | IThunderStormLocationRemark | IThunderStormLocationMovingRemark | ITornadicActivityBegRemark | ITornadicActivityBegEndRemark | ITornadicActivityEndRemark | ITowerVisibilityRemark | IVariableSkyRemark | IVariableSkyHeightRemark | IVirgaDirectionRemark | IWaterEquivalentSnowRemark | IWindPeakCommandRemark | IWindShiftRemark | IWindShiftFropaRemark;
1044
1095
 
1045
- interface ICountry {
1046
- name: string;
1047
- }
1048
- interface IAirport {
1049
- name: string;
1050
- city: string;
1051
- country: string;
1052
- iata: string;
1053
- icao: string;
1054
- latitude: string;
1055
- longitude: string;
1056
- altitude: string;
1057
- timezone: string;
1058
- dst: boolean;
1059
- tzDatabase: unknown;
1060
- }
1061
1096
  interface IWind {
1062
1097
  speed: number;
1063
1098
  direction: string;
@@ -1122,6 +1157,12 @@ interface ICloud {
1122
1157
  height?: number;
1123
1158
  quantity: CloudQuantity;
1124
1159
  type?: CloudType;
1160
+ /**
1161
+ * Very uncommon. For example "FEW025TCU/CB" seen at airport VOTR.
1162
+ *
1163
+ * This property can be ignored in almost all cases.
1164
+ */
1165
+ secondaryType?: CloudType;
1125
1166
  }
1126
1167
  interface IFlags {
1127
1168
  /**
@@ -1169,11 +1210,14 @@ interface ITime {
1169
1210
  }
1170
1211
  interface IAbstractWeatherCode extends IAbstractWeatherContainer, ITime {
1171
1212
  day?: number;
1172
- airport?: IAirport;
1173
1213
  message: string;
1174
1214
  station: string;
1175
1215
  trends: IAbstractTrend[];
1176
1216
  }
1217
+ interface ITafGroups {
1218
+ turbulence?: ITurbulence[];
1219
+ icing?: IIcing[];
1220
+ }
1177
1221
  interface IAbstractWeatherCodeDated extends IAbstractWeatherCode {
1178
1222
  issued: Date;
1179
1223
  }
@@ -1188,7 +1232,7 @@ interface IMetar extends IAbstractWeatherCode {
1188
1232
  */
1189
1233
  trends: IMetarTrend[];
1190
1234
  }
1191
- interface ITAF extends IAbstractWeatherCode {
1235
+ interface ITAF extends IAbstractWeatherCode, ITafGroups {
1192
1236
  validity: IValidity;
1193
1237
  maxTemperature?: ITemperature;
1194
1238
  minTemperature?: ITemperature;
@@ -1208,7 +1252,7 @@ interface IMetarTrendTime extends ITime {
1208
1252
  interface IMetarTrend extends IAbstractTrend {
1209
1253
  times: IMetarTrendTime[];
1210
1254
  }
1211
- interface IBaseTAFTrend extends IAbstractTrend {
1255
+ interface IBaseTAFTrend extends IAbstractTrend, ITafGroups {
1212
1256
  /**
1213
1257
  * Will not be found on FM trends. May exist on others.
1214
1258
  *
@@ -1260,6 +1304,36 @@ interface IValidityDated extends IAbstractValidity, IEndValidity {
1260
1304
  interface IFMValidity extends IAbstractValidity {
1261
1305
  startMinutes: number;
1262
1306
  }
1307
+ /**
1308
+ * Represents icing in a TAF.
1309
+ *
1310
+ * http://prnfc.org/wp-content/uploads/2016/12/AF-METAR-TAF-Codes.pdf#page=28
1311
+ *
1312
+ * Top of icing = `baseHeight` + `depth`
1313
+ */
1314
+ interface IIcing {
1315
+ /** The intensity of the icing. */
1316
+ intensity: IcingIntensity;
1317
+ /** The base of the icing layer in feet. */
1318
+ baseHeight: number;
1319
+ /** The icing layer depth in feet. */
1320
+ depth: number;
1321
+ }
1322
+ /**
1323
+ * Represents turbulence in a TAF.
1324
+ *
1325
+ * http://prnfc.org/wp-content/uploads/2016/12/AF-METAR-TAF-Codes.pdf#page=29
1326
+ *
1327
+ * Top of icing = `baseHeight` + `depth`
1328
+ */
1329
+ interface ITurbulence {
1330
+ /** The intensity of the turbulence. */
1331
+ intensity: TurbulenceIntensity;
1332
+ /** The base limit of the turbulence layer in feet. */
1333
+ baseHeight: number;
1334
+ /** The turbulence layer depth in feet. */
1335
+ depth: number;
1336
+ }
1263
1337
 
1264
1338
  interface IMetarDated extends IMetar {
1265
1339
  issued: Date;
@@ -1388,4 +1462,4 @@ declare function parseTAF(rawTAF: string, options?: IMetarTAFParserOptions): ITA
1388
1462
  declare function parseTAF(rawTAF: string, options?: IMetarTAFParserOptionsDated): ITAFDated;
1389
1463
  declare function parseTAFAsForecast(rawTAF: string, options: IMetarTAFParserOptionsDated): IForecastContainer;
1390
1464
 
1391
- export { CloudQuantity, CloudType, CommandExecutionError, Descriptive, Direction, Distance, DistanceUnit, Forecast, IAbstractTrend, IAbstractValidity, IAbstractWeatherCode, IAbstractWeatherCodeDated, IAbstractWeatherContainer, IAirport, IBaseRemark, IBaseTAFTrend, ICeilingHeightRemark, ICeilingSecondLocationRemark, ICloud, ICompositeForecast, ICountry, IEndValidity, IFMValidity, IFlags, IForecastContainer, IHourlyMaximumMinimumTemperatureRemark, IHourlyMaximumTemperatureRemark, IHourlyMinimumTemperatureRemark, IHourlyPrecipitationAmountRemark, IHourlyPressureRemark, IHourlyTemperatureDewPointRemark, IIceAccretionRemark, IMetar, IMetarTAFParserOptions, IMetarTAFParserOptionsDated, IMetarTrend, IMetarTrendTime, IObscurationRemark, IPrecipitationAmount24HourRemark, IPrecipitationAmount36HourRemark, IPrecipitationBegEndRemark, IPrevailingVisibilityRemark, IRunwayInfo, ISeaLevelPressureRemark, ISecondLocationVisibilityRemark, ISectorVisibilityRemark, ISmallHailSizeRemark, ISnowIncreaseRemark, ISnowPelletsRemark, ISunshineDurationRemark, ISurfaceVisibilityRemark, ITAF, ITAFDated, ITemperature, ITemperatureDated, IThunderStormLocationMovingRemark, IThunderStormLocationRemark, ITime, ITornadicActivityBegEndRemark, ITornadicActivityBegRemark, ITornadicActivityEndRemark, ITowerVisibilityRemark, IUnknownRemark, IValidity, IValidityDated, IVariableSkyHeightRemark, IVariableSkyRemark, IVirgaDirectionRemark, IWaterEquivalentSnowRemark, IWeatherCondition, IWind, IWindPeakCommandRemark, IWindShear, IWindShiftFropaRemark, Intensity, InvalidWeatherStatementError, Locale, ParseError, Phenomenon, Remark, RemarkType, RunwayInfoTrend, RunwayInfoUnit, SpeedUnit, TAFTrend, TAFTrendDated, TimeIndicator, TimestampOutOfBoundsError, UnexpectedParseError, ValueIndicator, Visibility, WeatherChangeType, getCompositeForecastForDate, isWeatherConditionValid, parseMetar, parseTAF, parseTAFAsForecast };
1465
+ export { CloudQuantity, CloudType, CommandExecutionError, Descriptive, Direction, Distance, DistanceUnit, Forecast, IAbstractTrend, IAbstractValidity, IAbstractWeatherCode, IAbstractWeatherCodeDated, IAbstractWeatherContainer, IBaseRemark, IBaseTAFTrend, ICeilingHeightRemark, ICeilingSecondLocationRemark, ICloud, ICompositeForecast, IEndValidity, IFMValidity, IFlags, IForecastContainer, IHourlyMaximumMinimumTemperatureRemark, IHourlyMaximumTemperatureRemark, IHourlyMinimumTemperatureRemark, IHourlyPrecipitationAmountRemark, IHourlyPressureRemark, IHourlyTemperatureDewPointRemark, IIceAccretionRemark, IIcing, IMetar, IMetarTAFParserOptions, IMetarTAFParserOptionsDated, IMetarTrend, IMetarTrendTime, IObscurationRemark, IPrecipitationAmount24HourRemark, IPrecipitationAmount36HourRemark, IPrecipitationBegEndRemark, IPrevailingVisibilityRemark, IRunwayInfo, ISeaLevelPressureRemark, ISecondLocationVisibilityRemark, ISectorVisibilityRemark, ISmallHailSizeRemark, ISnowIncreaseRemark, ISnowPelletsRemark, ISunshineDurationRemark, ISurfaceVisibilityRemark, ITAF, ITAFDated, ITafGroups, ITemperature, ITemperatureDated, IThunderStormLocationMovingRemark, IThunderStormLocationRemark, ITime, ITornadicActivityBegEndRemark, ITornadicActivityBegRemark, ITornadicActivityEndRemark, ITowerVisibilityRemark, ITurbulence, IUnknownRemark, IValidity, IValidityDated, IVariableSkyHeightRemark, IVariableSkyRemark, IVirgaDirectionRemark, IWaterEquivalentSnowRemark, IWeatherCondition, IWind, IWindPeakCommandRemark, IWindShear, IWindShiftFropaRemark, IcingIntensity, Intensity, InvalidWeatherStatementError, Locale, ParseError, Phenomenon, Remark, RemarkType, RunwayInfoTrend, RunwayInfoUnit, SpeedUnit, TAFTrend, TAFTrendDated, TimeIndicator, TimestampOutOfBoundsError, TurbulenceIntensity, UnexpectedParseError, ValueIndicator, Visibility, WeatherChangeType, getCompositeForecastForDate, isWeatherConditionValid, parseMetar, parseTAF, parseTAFAsForecast };
@@ -473,6 +473,59 @@ var RunwayInfoUnit;
473
473
  RunwayInfoUnit["Feet"] = "FT";
474
474
  RunwayInfoUnit["Meters"] = "m";
475
475
  })(RunwayInfoUnit || (RunwayInfoUnit = {}));
476
+ var IcingIntensity;
477
+ (function (IcingIntensity) {
478
+ /**
479
+ * Trace Icing or None.
480
+ *
481
+ * Air Force code 0 means a trace of icing.
482
+ * World Meteorological Organization code 0 means no icing
483
+ */
484
+ IcingIntensity["None"] = "0";
485
+ /** Light Mixed Icing. */
486
+ IcingIntensity["Light"] = "1";
487
+ /** Light Rime Icing In Cloud. */
488
+ IcingIntensity["LightRimeIcingCloud"] = "2";
489
+ /** Light Clear Icing In Precipitation. */
490
+ IcingIntensity["LightClearIcingPrecipitation"] = "3";
491
+ /** Moderate Mixed Icing. */
492
+ IcingIntensity["ModerateMixedIcing"] = "4";
493
+ /** Moderate Rime Icing In Cloud. */
494
+ IcingIntensity["ModerateRimeIcingCloud"] = "5";
495
+ /** Moderate Clear Icing In Precipitation. */
496
+ IcingIntensity["ModerateClearIcingPrecipitation"] = "6";
497
+ /** Severe Mixed Icing. */
498
+ IcingIntensity["SevereMixedIcing"] = "7";
499
+ /** Severe Rime Icing In Cloud. */
500
+ IcingIntensity["SevereRimeIcingCloud"] = "8";
501
+ /** Severe Clear Icing In Precipitation. */
502
+ IcingIntensity["SevereClearIcingPrecipitation"] = "9";
503
+ })(IcingIntensity || (IcingIntensity = {}));
504
+ var TurbulenceIntensity;
505
+ (function (TurbulenceIntensity) {
506
+ /** None. */
507
+ TurbulenceIntensity["None"] = "0";
508
+ /** Light turbulence. */
509
+ TurbulenceIntensity["Light"] = "1";
510
+ /** Moderate turbulence in clear air, occasional. */
511
+ TurbulenceIntensity["ModerateClearAirOccasional"] = "2";
512
+ /** Moderate turbulence in clear air, frequent. */
513
+ TurbulenceIntensity["ModerateClearAirFrequent"] = "3";
514
+ /** Moderate turbulence in cloud, occasional. */
515
+ TurbulenceIntensity["ModerateCloudOccasional"] = "4";
516
+ /** Moderate turbulence in cloud, frequent. */
517
+ TurbulenceIntensity["ModerateCloudFrequent"] = "5";
518
+ /** Severe turbulence in clear air, occasional. */
519
+ TurbulenceIntensity["SevereClearAirOccasional"] = "6";
520
+ /** Severe turbulence in clear air, frequent. */
521
+ TurbulenceIntensity["SevereClearAirFrequent"] = "7";
522
+ /** Severe turbulence in cloud, occasional. */
523
+ TurbulenceIntensity["SevereCloudOccasional"] = "8";
524
+ /** Severe turbulence in cloud, frequent. */
525
+ TurbulenceIntensity["SevereCloudFrequent"] = "9";
526
+ /** Extreme turbulence */
527
+ TurbulenceIntensity["Extreme"] = "X";
528
+ })(TurbulenceIntensity || (TurbulenceIntensity = {}));
476
529
 
477
530
  function degreesToCardinal(input) {
478
531
  const degrees = +input;
@@ -1741,7 +1794,7 @@ function isWeatherConditionValid(weather) {
1741
1794
  weather.descriptive == Descriptive.SHOWERS));
1742
1795
  }
1743
1796
 
1744
- var _CloudCommand_cloudRegex, _MainVisibilityCommand_regex, _WindCommand_regex, _WindVariationCommand_regex, _WindShearCommand_regex, _VerticalVisibilityCommand_regex, _MinimalVisibilityCommand_regex, _MainVisibilityNauticalMilesCommand_regex, _CommandSupplier_commands$1;
1797
+ var _CloudCommand_cloudRegex, _MainVisibilityCommand_regex, _WindCommand_regex, _WindVariationCommand_regex, _WindShearCommand_regex, _VerticalVisibilityCommand_regex, _MinimalVisibilityCommand_regex, _MainVisibilityNauticalMilesCommand_regex, _CommandSupplier_commands$2;
1745
1798
  /**
1746
1799
  * This function creates a wind element.
1747
1800
  * @param wind The wind object
@@ -1761,18 +1814,17 @@ function makeWind(direction, speed, gust, unit) {
1761
1814
  }
1762
1815
  class CloudCommand {
1763
1816
  constructor() {
1764
- _CloudCommand_cloudRegex.set(this, /^([A-Z]{3})(\d{3})?([A-Z]{2,3})?$/);
1817
+ _CloudCommand_cloudRegex.set(this, /^([A-Z]{3})(?:\/{3}|(\d{3}))?(?:\/{3}|(?:([A-Z]{2,3})(?:\/([A-Z]{2,3}))?))?$/);
1765
1818
  }
1766
1819
  parse(cloudString) {
1767
1820
  const m = cloudString.match(__classPrivateFieldGet(this, _CloudCommand_cloudRegex, "f"));
1768
1821
  if (!m)
1769
1822
  return;
1770
- const quantity = CloudQuantity[m[1]];
1823
+ const quantity = as(m[1], CloudQuantity);
1771
1824
  const height = 100 * +m[2] || undefined;
1772
- const type = CloudType[m[3]];
1773
- if (!quantity)
1774
- return;
1775
- return { quantity, height, type };
1825
+ const type = m[3] ? as(m[3], CloudType) : undefined;
1826
+ const secondaryType = m[4] ? as(m[4], CloudType) : undefined;
1827
+ return { quantity, height, type, secondaryType };
1776
1828
  }
1777
1829
  execute(container, cloudString) {
1778
1830
  const cloud = this.parse(cloudString);
@@ -1811,7 +1863,7 @@ class MainVisibilityCommand {
1811
1863
  _MainVisibilityCommand_regex = new WeakMap();
1812
1864
  class WindCommand {
1813
1865
  constructor() {
1814
- _WindCommand_regex.set(this, /^(VRB|\d{3})(\d{2})G?(\d{2})?(KT|MPS|KM\/H)?/);
1866
+ _WindCommand_regex.set(this, /^(VRB|[0-3]\d{2})(\d{2})G?(\d{2})?(KT|MPS|KM\/H)?/);
1815
1867
  }
1816
1868
  canParse(windString) {
1817
1869
  return __classPrivateFieldGet(this, _WindCommand_regex, "f").test(windString);
@@ -1924,9 +1976,9 @@ class MainVisibilityNauticalMilesCommand {
1924
1976
  }
1925
1977
  }
1926
1978
  _MainVisibilityNauticalMilesCommand_regex = new WeakMap();
1927
- class CommandSupplier$1 {
1979
+ class CommandSupplier$2 {
1928
1980
  constructor() {
1929
- _CommandSupplier_commands$1.set(this, [
1981
+ _CommandSupplier_commands$2.set(this, [
1930
1982
  new WindShearCommand(),
1931
1983
  new WindCommand(),
1932
1984
  new WindVariationCommand(),
@@ -1938,13 +1990,13 @@ class CommandSupplier$1 {
1938
1990
  ]);
1939
1991
  }
1940
1992
  get(input) {
1941
- for (const command of __classPrivateFieldGet(this, _CommandSupplier_commands$1, "f")) {
1993
+ for (const command of __classPrivateFieldGet(this, _CommandSupplier_commands$2, "f")) {
1942
1994
  if (command.canParse(input))
1943
1995
  return command;
1944
1996
  }
1945
1997
  }
1946
1998
  }
1947
- _CommandSupplier_commands$1 = new WeakMap();
1999
+ _CommandSupplier_commands$2 = new WeakMap();
1948
2000
 
1949
2001
  var _AltimeterCommand_regex;
1950
2002
  class AltimeterCommand {
@@ -2048,16 +2100,76 @@ class TemperatureCommand {
2048
2100
  }
2049
2101
  _TemperatureCommand_regex = new WeakMap();
2050
2102
 
2051
- var _CommandSupplier_commands;
2052
- class CommandSupplier {
2103
+ var _CommandSupplier_commands$1;
2104
+ class CommandSupplier$1 {
2053
2105
  constructor() {
2054
- _CommandSupplier_commands.set(this, [
2106
+ _CommandSupplier_commands$1.set(this, [
2055
2107
  new RunwayCommand(),
2056
2108
  new TemperatureCommand(),
2057
2109
  new AltimeterCommand(),
2058
2110
  new AltimeterMercuryCommand(),
2059
2111
  ]);
2060
2112
  }
2113
+ get(input) {
2114
+ for (const command of __classPrivateFieldGet(this, _CommandSupplier_commands$1, "f")) {
2115
+ if (command.canParse(input))
2116
+ return command;
2117
+ }
2118
+ }
2119
+ }
2120
+ _CommandSupplier_commands$1 = new WeakMap();
2121
+
2122
+ var _IcingCommand_regex;
2123
+ class IcingCommand {
2124
+ constructor() {
2125
+ _IcingCommand_regex.set(this, /^6(\d)(\d{3})(\d)$/);
2126
+ }
2127
+ canParse(input) {
2128
+ return __classPrivateFieldGet(this, _IcingCommand_regex, "f").test(input);
2129
+ }
2130
+ execute(container, input) {
2131
+ const matches = input.match(__classPrivateFieldGet(this, _IcingCommand_regex, "f"));
2132
+ if (!matches)
2133
+ throw new UnexpectedParseError("Match not found");
2134
+ if (!container.icing)
2135
+ container.icing = [];
2136
+ container.icing.push({
2137
+ intensity: as(matches[1], IcingIntensity),
2138
+ baseHeight: +matches[2] * 100,
2139
+ depth: +matches[3] * 1000,
2140
+ });
2141
+ }
2142
+ }
2143
+ _IcingCommand_regex = new WeakMap();
2144
+
2145
+ var _TurbulenceCommand_regex;
2146
+ class TurbulenceCommand {
2147
+ constructor() {
2148
+ _TurbulenceCommand_regex.set(this, /^5(\d|X)(\d{3})(\d)$/);
2149
+ }
2150
+ canParse(input) {
2151
+ return __classPrivateFieldGet(this, _TurbulenceCommand_regex, "f").test(input);
2152
+ }
2153
+ execute(container, input) {
2154
+ const matches = input.match(__classPrivateFieldGet(this, _TurbulenceCommand_regex, "f"));
2155
+ if (!matches)
2156
+ throw new UnexpectedParseError("Match not found");
2157
+ if (!container.turbulence)
2158
+ container.turbulence = [];
2159
+ container.turbulence.push({
2160
+ intensity: as(matches[1], TurbulenceIntensity),
2161
+ baseHeight: +matches[2] * 100,
2162
+ depth: +matches[3] * 1000,
2163
+ });
2164
+ }
2165
+ }
2166
+ _TurbulenceCommand_regex = new WeakMap();
2167
+
2168
+ var _CommandSupplier_commands;
2169
+ class CommandSupplier {
2170
+ constructor() {
2171
+ _CommandSupplier_commands.set(this, [new TurbulenceCommand(), new IcingCommand()]);
2172
+ }
2061
2173
  get(input) {
2062
2174
  for (const command of __classPrivateFieldGet(this, _CommandSupplier_commands, "f")) {
2063
2175
  if (command.canParse(input))
@@ -2067,7 +2179,7 @@ class CommandSupplier {
2067
2179
  }
2068
2180
  _CommandSupplier_commands = new WeakMap();
2069
2181
 
2070
- var _AbstractParser_INTENSITY_REGEX, _AbstractParser_CAVOK, _AbstractParser_commonSupplier, _MetarParser_commandSupplier, _TAFParser_validityPattern, _RemarkParser_supplier;
2182
+ var _AbstractParser_INTENSITY_REGEX, _AbstractParser_CAVOK, _AbstractParser_commonSupplier, _MetarParser_commandSupplier, _TAFParser_commandSupplier, _TAFParser_validityPattern, _RemarkParser_supplier;
2071
2183
  /**
2072
2184
  * Parses the delivery time of a METAR/TAF
2073
2185
  * @param abstractWeatherCode The TAF or METAR object
@@ -2171,7 +2283,7 @@ class AbstractParser {
2171
2283
  // #TOKENIZE_REGEX = /\s((?=\d\/\dSM)(?<!\s\d\s)|(?!\d\/\dSM))|=/;
2172
2284
  _AbstractParser_INTENSITY_REGEX.set(this, /^(-|\+|VC)/);
2173
2285
  _AbstractParser_CAVOK.set(this, "CAVOK");
2174
- _AbstractParser_commonSupplier.set(this, new CommandSupplier$1());
2286
+ _AbstractParser_commonSupplier.set(this, new CommandSupplier$2());
2175
2287
  }
2176
2288
  parseWeatherCondition(input) {
2177
2289
  let intensity;
@@ -2240,7 +2352,14 @@ class AbstractParser {
2240
2352
  }
2241
2353
  const command = __classPrivateFieldGet(this, _AbstractParser_commonSupplier, "f").get(input);
2242
2354
  if (command) {
2243
- return command.execute(abstractWeatherContainer, input);
2355
+ try {
2356
+ return command.execute(abstractWeatherContainer, input);
2357
+ }
2358
+ catch (error) {
2359
+ if (error instanceof CommandExecutionError)
2360
+ return false;
2361
+ throw error;
2362
+ }
2244
2363
  }
2245
2364
  const weatherCondition = this.parseWeatherCondition(input);
2246
2365
  if (isWeatherConditionValid(weatherCondition)) {
@@ -2256,7 +2375,7 @@ class MetarParser extends AbstractParser {
2256
2375
  super(...arguments);
2257
2376
  this.AT = "AT";
2258
2377
  this.TL = "TL";
2259
- _MetarParser_commandSupplier.set(this, new CommandSupplier());
2378
+ _MetarParser_commandSupplier.set(this, new CommandSupplier$1());
2260
2379
  }
2261
2380
  /**
2262
2381
  * Parses a trend of a metar
@@ -2354,6 +2473,7 @@ class TAFParser extends AbstractParser {
2354
2473
  this.PROB = "PROB";
2355
2474
  this.TX = "TX";
2356
2475
  this.TN = "TN";
2476
+ _TAFParser_commandSupplier.set(this, new CommandSupplier());
2357
2477
  _TAFParser_validityPattern.set(this, /^\d{4}\/\d{4}$/);
2358
2478
  }
2359
2479
  /**
@@ -2406,13 +2526,17 @@ class TAFParser extends AbstractParser {
2406
2526
  };
2407
2527
  for (let i = index + 1; i < lines[0].length; i++) {
2408
2528
  const token = lines[0][i];
2529
+ const tafCommand = __classPrivateFieldGet(this, _TAFParser_commandSupplier, "f").get(token);
2409
2530
  if (token == this.RMK) {
2410
2531
  parseRemark(taf, lines[0], i, this.locale);
2411
2532
  break;
2412
2533
  }
2534
+ else if (tafCommand) {
2535
+ tafCommand.execute(taf, token);
2536
+ }
2413
2537
  else {
2414
- parseFlags(taf, token);
2415
2538
  this.generalParse(taf, token);
2539
+ parseFlags(taf, token);
2416
2540
  }
2417
2541
  }
2418
2542
  const minMaxTemperatureLines = [
@@ -2537,6 +2661,7 @@ class TAFParser extends AbstractParser {
2537
2661
  */
2538
2662
  parseTrend(index, line, trend) {
2539
2663
  for (let i = index; i < line.length; i++) {
2664
+ const tafCommand = __classPrivateFieldGet(this, _TAFParser_commandSupplier, "f").get(line[i]);
2540
2665
  if (line[i] === this.RMK) {
2541
2666
  parseRemark(trend, line, i, this.locale);
2542
2667
  break;
@@ -2544,6 +2669,9 @@ class TAFParser extends AbstractParser {
2544
2669
  // already parsed
2545
2670
  else if (__classPrivateFieldGet(this, _TAFParser_validityPattern, "f").test(line[i]))
2546
2671
  continue;
2672
+ else if (tafCommand) {
2673
+ tafCommand.execute(trend, line[i]);
2674
+ }
2547
2675
  else
2548
2676
  super.generalParse(trend, line[i]);
2549
2677
  }
@@ -2556,7 +2684,7 @@ class TAFParser extends AbstractParser {
2556
2684
  };
2557
2685
  }
2558
2686
  }
2559
- _TAFParser_validityPattern = new WeakMap();
2687
+ _TAFParser_commandSupplier = new WeakMap(), _TAFParser_validityPattern = new WeakMap();
2560
2688
  class RemarkParser {
2561
2689
  constructor(locale) {
2562
2690
  this.locale = locale;
@@ -2592,7 +2720,7 @@ _RemarkParser_supplier = new WeakMap();
2592
2720
  * @param minute Minute (from the report)
2593
2721
  * @returns
2594
2722
  */
2595
- function determineReportIssuedDate(date, day, hour, minute) {
2723
+ function determineReportDate(date, day, hour, minute = 0) {
2596
2724
  // Some TAF reports do not include a delivery time
2597
2725
  if (day == null || hour == null)
2598
2726
  return date;
@@ -2608,17 +2736,6 @@ function determineReportIssuedDate(date, day, hour, minute) {
2608
2736
  }))
2609
2737
  .sort((a, b) => a.difference - b.difference)[0].date;
2610
2738
  }
2611
- function getReportDate(issued, day, hour, minute = 0) {
2612
- let date = new Date(issued);
2613
- if (day < date.getUTCDate()) {
2614
- date = addMonthsUTC(date, 1);
2615
- }
2616
- date.setUTCDate(day);
2617
- date.setUTCHours(hour);
2618
- if (minute != null)
2619
- date.setUTCMinutes(minute);
2620
- return date;
2621
- }
2622
2739
  function setDateComponents(date, day, hour, minute) {
2623
2740
  date.setUTCDate(day);
2624
2741
  date.setUTCHours(hour);
@@ -2641,30 +2758,30 @@ function addMonthsUTC(date, count) {
2641
2758
  function metarDatesHydrator(report, date) {
2642
2759
  return {
2643
2760
  ...report,
2644
- issued: determineReportIssuedDate(date, report.day, report.hour, report.minute),
2761
+ issued: determineReportDate(date, report.day, report.hour, report.minute),
2645
2762
  };
2646
2763
  }
2647
2764
 
2648
2765
  function tafDatesHydrator(report, date) {
2649
- const issued = determineReportIssuedDate(date, report.day, report.hour, report.minute);
2766
+ const issued = determineReportDate(date, report.day, report.hour, report.minute);
2650
2767
  return {
2651
2768
  ...report,
2652
2769
  issued,
2653
2770
  validity: {
2654
2771
  ...report.validity,
2655
- start: getReportDate(issued, report.validity.startDay, report.validity.startHour),
2656
- end: getReportDate(issued, report.validity.endDay, report.validity.endHour),
2772
+ start: determineReportDate(issued, report.validity.startDay, report.validity.startHour),
2773
+ end: determineReportDate(issued, report.validity.endDay, report.validity.endHour),
2657
2774
  },
2658
2775
  minTemperature: report.minTemperature
2659
2776
  ? {
2660
2777
  ...report.minTemperature,
2661
- date: getReportDate(issued, report.minTemperature.day, report.minTemperature.hour),
2778
+ date: determineReportDate(issued, report.minTemperature.day, report.minTemperature.hour),
2662
2779
  }
2663
2780
  : undefined,
2664
2781
  maxTemperature: report.maxTemperature
2665
2782
  ? {
2666
2783
  ...report.maxTemperature,
2667
- date: getReportDate(issued, report.maxTemperature.day, report.maxTemperature.hour),
2784
+ date: determineReportDate(issued, report.maxTemperature.day, report.maxTemperature.hour),
2668
2785
  }
2669
2786
  : undefined,
2670
2787
  trends: report.trends.map((trend) => ({
@@ -2674,13 +2791,13 @@ function tafDatesHydrator(report, date) {
2674
2791
  case WeatherChangeType.FM:
2675
2792
  return {
2676
2793
  ...trend.validity,
2677
- start: getReportDate(issued, trend.validity.startDay, trend.validity.startHour, trend.validity.startMinutes),
2794
+ start: determineReportDate(issued, trend.validity.startDay, trend.validity.startHour, trend.validity.startMinutes),
2678
2795
  };
2679
2796
  default:
2680
2797
  return {
2681
2798
  ...trend.validity,
2682
- start: getReportDate(issued, trend.validity.startDay, trend.validity.startHour),
2683
- end: getReportDate(issued, trend.validity.endDay, trend.validity.endHour),
2799
+ start: determineReportDate(issued, trend.validity.startDay, trend.validity.startHour),
2800
+ end: determineReportDate(issued, trend.validity.endDay, trend.validity.endHour),
2684
2801
  };
2685
2802
  }
2686
2803
  })(),
@@ -2692,8 +2809,8 @@ function getForecastFromTAF(taf) {
2692
2809
  const { trends, wind, visibility, verticalVisibility, windShear, cavok, remark, remarks, clouds, weatherConditions, initialRaw, validity, ...tafWithoutBaseProperties } = taf;
2693
2810
  return {
2694
2811
  ...tafWithoutBaseProperties,
2695
- start: getReportDate(taf.issued, taf.validity.startDay, taf.validity.startHour),
2696
- end: getReportDate(taf.issued, taf.validity.endDay, taf.validity.endHour),
2812
+ start: determineReportDate(taf.issued, taf.validity.startDay, taf.validity.startHour),
2813
+ end: determineReportDate(taf.issued, taf.validity.endDay, taf.validity.endHour),
2697
2814
  forecast: hydrateEndDates([makeInitialForecast(taf), ...taf.trends], taf.validity),
2698
2815
  };
2699
2816
  }
@@ -2712,6 +2829,8 @@ function makeInitialForecast(taf) {
2712
2829
  clouds: taf.clouds,
2713
2830
  weatherConditions: taf.weatherConditions,
2714
2831
  raw: taf.initialRaw,
2832
+ turbulence: taf.turbulence,
2833
+ icing: taf.icing,
2715
2834
  validity: {
2716
2835
  // End day/hour are for end of the entire TAF
2717
2836
  startDay: taf.validity.startDay,
@@ -2781,6 +2900,8 @@ function hydrateEndDates(trends, reportValidity) {
2781
2900
  * it needs to be populated
2782
2901
  */
2783
2902
  function hydrateWithPreviousContextIfNeeded(forecast, context) {
2903
+ // BECMG is the only forecast type that inherits old conditions
2904
+ // Anything else starts anew
2784
2905
  if (forecast.type !== WeatherChangeType.BECMG || !context)
2785
2906
  return forecast;
2786
2907
  // Remarks should not be carried over
@@ -2790,6 +2911,12 @@ function hydrateWithPreviousContextIfNeeded(forecast, context) {
2790
2911
  // vertical visibility should not be carried over, if clouds exist
2791
2912
  if (forecast.clouds.length)
2792
2913
  delete context.verticalVisibility;
2914
+ // CAVOK should not propagate if anything other than wind changes
2915
+ if (forecast.clouds.length ||
2916
+ forecast.verticalVisibility ||
2917
+ forecast.weatherConditions.length ||
2918
+ forecast.visibility)
2919
+ delete context.cavok;
2793
2920
  forecast = {
2794
2921
  ...context,
2795
2922
  ...forecast,
@@ -2865,4 +2992,4 @@ function parse(rawReport, options, parser, datesHydrator) {
2865
2992
  }
2866
2993
  }
2867
2994
 
2868
- export { CloudQuantity, CloudType, CommandExecutionError, Descriptive, Direction, DistanceUnit, Intensity, InvalidWeatherStatementError, ParseError, Phenomenon, RemarkType, RunwayInfoTrend, RunwayInfoUnit, SpeedUnit, TimeIndicator, TimestampOutOfBoundsError, UnexpectedParseError, ValueIndicator, WeatherChangeType, getCompositeForecastForDate, isWeatherConditionValid, parseMetar, parseTAF, parseTAFAsForecast };
2995
+ export { CloudQuantity, CloudType, CommandExecutionError, Descriptive, Direction, DistanceUnit, IcingIntensity, Intensity, InvalidWeatherStatementError, ParseError, Phenomenon, RemarkType, RunwayInfoTrend, RunwayInfoUnit, SpeedUnit, TimeIndicator, TimestampOutOfBoundsError, TurbulenceIntensity, UnexpectedParseError, ValueIndicator, WeatherChangeType, getCompositeForecastForDate, isWeatherConditionValid, parseMetar, parseTAF, parseTAFAsForecast };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metar-taf-parser",
3
- "version": "7.0.0",
3
+ "version": "7.1.0",
4
4
  "description": "Parse METAR and TAF reports",
5
5
  "homepage": "https://aeharding.github.io/metar-taf-parser",
6
6
  "keywords": [