mod indexes;
mod profile;
mod station_info;
use crate::error::*;
use chrono::NaiveDateTime;
use metfor::{
Celsius, CelsiusDiff, HectoPascal, JpKg, Kelvin, Knots, Meters, Mm, PaPS, WindSpdDir,
};
use optional::Optioned;
use std::error::Error;
#[derive(Debug)]
pub struct UpperAir {
pub num: i32, pub valid_time: NaiveDateTime, pub lead_time: i32, pub id: Option<String>, pub lat: Optioned<f64>, pub lon: Optioned<f64>, pub elevation: Optioned<Meters>,
pub show: Optioned<CelsiusDiff>, pub li: Optioned<CelsiusDiff>, pub swet: Optioned<f64>, pub kinx: Optioned<Celsius>, pub lclp: Optioned<HectoPascal>, pub pwat: Optioned<Mm>, pub totl: Optioned<f64>, pub cape: Optioned<JpKg>, pub lclt: Optioned<Kelvin>, pub cins: Optioned<JpKg>, pub eqlv: Optioned<HectoPascal>, pub lfc: Optioned<HectoPascal>, pub brch: Optioned<f64>,
pub pressure: Vec<Optioned<HectoPascal>>, pub temperature: Vec<Optioned<Celsius>>, pub wet_bulb: Vec<Optioned<Celsius>>, pub dew_point: Vec<Optioned<Celsius>>, pub theta_e: Vec<Optioned<Kelvin>>, pub wind: Vec<Optioned<WindSpdDir<Knots>>>, pub omega: Vec<Optioned<PaPS>>, pub height: Vec<Optioned<Meters>>, pub cloud_fraction: Vec<Optioned<f64>>, }
impl UpperAir {
pub fn parse(text: &str) -> Result<UpperAir, Box<dyn Error>> {
use self::indexes::Indexes;
use self::profile::Profile;
use self::station_info::StationInfo;
use crate::parse_util::find_blank_line;
let mut break_point = find_blank_line(text).ok_or_else(BufkitFileError::new)?;
let (station_info_section, the_rest) = text.split_at(break_point);
break_point = find_blank_line(the_rest).ok_or_else(BufkitFileError::new)?;
let (index_section, upper_air_section) = the_rest.split_at(break_point);
let station_info = StationInfo::parse(station_info_section)?;
let indexes = Indexes::parse(index_section)?;
let upper_air = Profile::parse(upper_air_section)?;
Ok(UpperAir {
num: station_info.num,
valid_time: station_info.valid_time,
lead_time: station_info.lead_time,
id: station_info.id,
lat: station_info.lat,
lon: station_info.lon,
elevation: station_info.elevation,
show: indexes.show,
li: indexes.li,
swet: indexes.swet,
kinx: indexes.kinx,
lclp: indexes.lclp,
pwat: indexes.pwat,
totl: indexes.totl,
cape: indexes.cape,
lclt: indexes.lclt,
cins: indexes.cins,
eqlv: indexes.eqlv,
lfc: indexes.lfc,
brch: indexes.brch,
pressure: upper_air.pressure,
temperature: upper_air.temperature,
wet_bulb: upper_air.wet_bulb,
dew_point: upper_air.dew_point,
theta_e: upper_air.theta_e,
wind: upper_air.wind,
omega: upper_air.omega,
height: upper_air.height,
cloud_fraction: upper_air.cloud_fraction,
})
}
pub fn validate(&self) -> Result<(), BufkitFileError> {
let len = self.pressure.len();
if len == 0 {
return Err(BufkitFileError::new());
}
let is_valid_length = |l| {
if l == 0 || l == len {
Ok(())
} else {
Err(BufkitFileError::new())
}
};
is_valid_length(self.temperature.len())?;
is_valid_length(self.wet_bulb.len())?;
is_valid_length(self.dew_point.len())?;
is_valid_length(self.theta_e.len())?;
is_valid_length(self.wind.len())?;
is_valid_length(self.omega.len())?;
is_valid_length(self.height.len())?;
is_valid_length(self.cloud_fraction.len())?;
Ok(())
}
}
#[cfg(test)]
mod test {
use super::*;
fn get_test_data() -> &'static str {
"STID = KMSO STNM = 727730 TIME = 170401/0100
SLAT = 46.87 SLON = -114.16 SELV = 1335.0
STIM = 1
SHOW = 8.12 LIFT = 8.00 SWET = 39.08 KINX = 14.88
LCLP = 780.77 PWAT = 9.28 TOTL = 39.55 CAPE = 0.00
LCLT = 272.88 CINS = 0.00 EQLV = -9999.00 LFCT = -9999.00
BRCH = 0.00
PRES TMPC TMWC DWPC THTE DRCT SKNT OMEG
CFRL HGHT
867.20 8.04 4.71 1.19 307.17 288.43 2.45 0.00
0.00 1353.07
863.50 7.64 4.42 0.99 306.96 293.63 3.40 0.00
0.00 1388.34
859.80 7.24 4.18 0.90 306.87 292.38 3.57 0.00
0.00 1423.71
856.10 6.94 3.99 0.81 306.89 292.38 3.57 0.00
0.00 1459.19
852.20 6.54 3.75 0.72 306.83 293.63 3.40 0.00
0.00 1496.70
848.30 6.14 3.52 0.65 306.78 293.63 3.40 0.00
0.00 1534.33
844.30 5.74 3.27 0.56 306.73 295.02 3.22 0.00
0.00 1573.06
840.30 5.44 3.09 0.49 306.81 295.02 3.22 0.00
0.00 1611.92
836.10 5.04 2.85 0.42 306.81 296.57 3.05 0.00
0.00 1652.86
831.70 4.54 2.55 0.32 306.69 298.30 2.87 0.00
0.00 1695.91
827.20 4.14 2.31 0.25 306.73 298.30 2.87 0.00
0.00 1740.11
822.60 3.74 2.17 0.40 307.00 304.99 2.37 0.00
0.00 1785.47
817.60 3.44 2.05 0.49 307.38 306.87 1.94 0.00
0.00 1835.00
812.50 3.04 1.84 0.49 307.57 309.81 1.52 0.00
0.00 1885.77
807.00 2.54 1.54 0.40 307.61 315.00 1.38 0.00
0.00 1940.80
801.10 2.04 1.04 -0.13 307.27 323.13 0.97 0.00
0.00 2000.13
794.60 1.44 0.66 -0.28 307.28 323.13 0.97 0.10
0.00 2065.88
787.00 0.64 0.15 -0.46 307.18 315.00 0.82 0.10
0.00 2143.23
778.20 -0.16 -0.39 -0.71 307.17 315.00 0.54 0.10
0.00 2233.47
768.10 -0.76 -1.33 -2.07 306.54 26.57 0.87 0.10
0.00 2338.03
756.50 -1.66 -2.29 -3.15 306.09 45.00 1.38 0.10
0.00 2459.47
743.50 -2.56 -3.39 -4.60 305.58 48.81 2.06 0.20
0.00 2597.31
728.70 -3.56 -4.82 -6.79 304.77 32.47 2.53 0.20
0.00 2756.60
712.10 -4.46 -6.45 -10.03 303.87 360.00 1.55 0.10
0.00 2938.45
693.70 -4.66 -7.82 -14.55 303.78 310.60 1.79 0.10
0.00 3144.54
673.70 -6.06 -9.48 -17.85 303.55 304.99 2.37 0.10
0.00 3374.08
652.00 -7.46 -11.05 -21.15 303.84 336.80 2.95 0.10
0.00 3629.57
629.00 -8.46 -12.58 -26.99 304.48 3.81 5.85 0.20
0.00 3908.49
605.10 -8.86 -13.70 -36.84 306.07 11.89 11.31 0.10
0.00 4208.48
580.60 -9.26 -14.46 -48.61 308.56 9.67 17.35 0.10
0.00 4528.02
555.50 -10.56 -15.48 -46.16 311.05 4.64 21.64 0.10
0.00 4868.57
530.00 -12.76 -16.86 -37.11 313.30 358.53 22.73 0.10
0.00 5228.30
504.20 -15.76 -18.96 -33.99 314.59 355.28 23.58 0.10
0.00 5606.56
478.10 -19.06 -21.32 -31.63 315.80 353.80 26.96 0.10
0.00 6004.58
451.90 -22.26 -23.73 -30.53 317.22 352.54 32.91 0.10
0.00 6421.27
425.50 -25.26 -26.27 -31.32 318.83 350.81 40.15 0.00
0.00 6860.87
399.00 -28.56 -29.45 -35.04 319.83 348.46 46.60 0.00
0.00 7324.52
372.10 -32.36 -33.18 -40.23 320.57 347.33 51.36 0.00
0.00 7820.49
344.70 -36.66 -37.33 -45.52 321.36 349.05 55.21 0.10
0.00 8354.86
316.80 -41.46 -41.91 -49.66 322.29 352.90 61.27 0.10
0.00 8933.26
289.20 -46.66 -46.90 -52.70 323.26 353.40 70.98 0.10
0.00 9544.55
263.10 -51.76 -51.92 -57.69 324.47 350.76 81.08 0.10
0.00 10164.59
239.40 -56.86 -56.94 -61.61 325.58 349.20 85.02 0.10
0.00 10769.30
218.00 -62.06 -62.12 -67.47 326.29 349.79 81.12 0.10
0.00 11355.03
198.70 -65.46 -65.51 -72.95 329.60 352.75 64.63 0.10
0.00 11923.20
181.30 -65.56 -65.62 -73.57 338.19 345.85 47.67 0.00
0.00 12480.21
165.60 -63.86 -63.95 -74.17 349.89 334.77 41.01 0.00
0.00 13032.85
151.10 -61.16 -9999.00 -9999.00 -9999.00 334.11 36.93 0.00
0.00 13597.84
137.30 -59.76 -9999.00 -9999.00 -9999.00 329.66 30.38 0.00
0.00 14194.10
124.00 -59.06 -9999.00 -9999.00 -9999.00 331.07 25.29 0.00
0.00 14831.55
111.30 -58.46 -9999.00 -9999.00 -9999.00 328.24 19.19 0.00
0.00 15509.64
98.90 -57.86 -9999.00 -9999.00 -9999.00 317.01 15.68 0.00
0.00 16252.97
86.90 -57.76 -9999.00 -9999.00 -9999.00 308.16 13.83 0.00
0.00 17068.31
75.10 -58.36 -9999.00 -9999.00 -9999.00 307.63 11.77 0.00
0.00 17987.13
63.60 -58.26 -58.80 -80.31 472.23 316.85 8.53 0.00
0.00 19032.36
52.30 -58.16 -9999.00 -9999.00 -9999.00 328.24 4.80 0.00
0.00 20263.10
41.00 -58.66 -59.44 -82.99 534.34 12.53 5.38 0.00
0.00 21793.21
29.80 -58.16 -59.27 -84.87 586.71 66.61 7.83 0.00
0.00 23798.77
18.70 -56.96 -58.85 -87.55 674.01 97.97 9.81 0.00
0.00 26739.45
7.60 -48.76 -55.67 -92.48 904.81 303.69 6.29 0.00
0.00 32545.28"
}
#[test]
fn test_parse() {
use chrono::NaiveDate;
use optional::some;
let test_data = get_test_data();
let snd = UpperAir::parse(test_data);
assert!(snd.is_ok());
let snd = snd.unwrap();
assert_eq!(snd.num, 727730);
assert_eq!(
snd.valid_time,
NaiveDate::from_ymd(2017, 4, 1).and_hms(1, 0, 0)
);
assert_eq!(snd.lead_time, 1);
assert_eq!(snd.lat, some(46.87));
assert_eq!(snd.lon, some(-114.16));
assert_eq!(snd.elevation, some(Meters(1335.0)));
assert_eq!(snd.show, some(CelsiusDiff(8.12)));
assert_eq!(snd.li, some(CelsiusDiff(8.0)));
assert_eq!(snd.swet, some(39.08));
assert_eq!(snd.kinx, some(Celsius(14.88)));
assert_eq!(snd.lclp, some(HectoPascal(780.77)));
assert_eq!(snd.pwat, some(Mm(9.28)));
assert_eq!(snd.totl, some(39.55));
assert_eq!(snd.cape, some(JpKg(0.0)));
assert_eq!(snd.lclt, some(Kelvin(272.88)));
assert_eq!(snd.cins, some(JpKg(0.0)));
assert!(snd.eqlv.is_none());
assert!(snd.lfc.is_none());
assert_eq!(snd.brch, some(0.0));
assert_eq!(snd.pressure[2], some(HectoPascal(859.8)));
assert_eq!(snd.temperature[2], some(Celsius(7.24)));
assert_eq!(snd.wet_bulb[2], some(Celsius(4.18)));
assert_eq!(snd.dew_point[2], some(Celsius(0.90)));
assert_eq!(snd.theta_e[2], some(Kelvin(306.87)));
assert_eq!(
snd.wind[2],
some(WindSpdDir {
direction: 292.38,
speed: Knots(3.57)
})
);
assert_eq!(snd.omega[2], some(PaPS(0.00)));
assert_eq!(snd.height[2], some(Meters(1423.71)));
assert_eq!(snd.cloud_fraction[2], some(0.0));
assert_eq!(snd.pressure.len(), 60);
assert_eq!(snd.temperature.len(), 60);
assert_eq!(snd.wet_bulb.len(), 60);
assert_eq!(snd.dew_point.len(), 60);
assert_eq!(snd.theta_e.len(), 60);
assert_eq!(snd.wind.len(), 60);
assert_eq!(snd.omega.len(), 60);
assert_eq!(snd.height.len(), 60);
assert_eq!(snd.cloud_fraction.len(), 60);
}
}