use metfor::{Celsius, HectoPascal, Knots, Meters, WindSpdDir}; use optional::Optioned; use sounding_analysis::{Sounding, StationInfo}; use std::{fs::File, io::Read, path::PathBuf, str::FromStr}; pub fn load_all_test_files() -> [Sounding; 4] { let snd1 = load_test_file("standard.csv"); let snd2 = load_test_file("complex_dendritic.csv"); let snd3 = load_test_file("multiple_warm_layers_aloft.csv"); let snd4 = load_test_file("multiple_inversions_aloft.csv"); [snd1, snd2, snd3, snd4] } fn load_test_file(fname: &str) -> Sounding { let mut test_path = PathBuf::new(); test_path.push("test_data"); test_path.push(fname); load_test_csv_sounding(&test_path) } fn load_test_csv_sounding(location: &PathBuf) -> Sounding { let mut f = File::open(location).expect(&format!("Error opening file: {:#?}", location)); let mut contents = String::new(); f.read_to_string(&mut contents) .expect(&format!("Error reading file: {:#?}", location)); let lines: Vec<&str> = contents.split('\n').collect(); let mut line_iter = lines.iter(); // // Parse profile data // let mut height: Vec> = Vec::with_capacity(lines.len()); let mut temp: Vec> = Vec::with_capacity(lines.len()); let mut wb: Vec> = Vec::with_capacity(lines.len()); let mut dp: Vec> = Vec::with_capacity(lines.len()); let mut press: Vec> = Vec::with_capacity(lines.len()); let mut wind: Vec>> = Vec::with_capacity(lines.len()); for line in line_iter.by_ref() { if line.starts_with("### Surface Data ###") || line.starts_with("### Analysis Int Section ###") || line.starts_with("### Analysis Float Section ###") { break; } let tokens: Vec<&str> = line.split(',').collect(); if tokens.len() < 6 { continue; } let t_c: Option = f64::from_str(tokens[1]).ok().map(Celsius); let dp_c: Option = f64::from_str(tokens[2]).ok().map(Celsius); let press_hpa: Option = f64::from_str(tokens[3]).ok().map(HectoPascal).into(); let wb_c: Option = t_c .and_then(|t| dp_c.and_then(|dp| press_hpa.and_then(|p| metfor::wet_bulb(t, dp, p)))); let wspd = f64::from_str(tokens[4]).ok(); let wdir = f64::from_str(tokens[5]).ok(); let wind_val: Option> = wspd.and_then(|wspd| { wdir.map(|wdir| WindSpdDir { speed: Knots(wspd), direction: wdir, }) }); height.push(f64::from_str(tokens[0]).ok().map(Meters).into()); temp.push(t_c.into()); wb.push(wb_c.into()); dp.push(dp_c.into()); press.push(press_hpa.into()); wind.push(wind_val.into()); } let mut snd = Sounding::new() .with_height_profile(height) .with_temperature_profile(temp) .with_wet_bulb_profile(wb) .with_dew_point_profile(dp) .with_pressure_profile(press) .with_wind_profile(wind); // // Surface data // for line in line_iter.by_ref() { if line.starts_with("### Analysis Int Section ###") || line.starts_with("### Analysis Float Section ###") { break; } let tokens: Vec<&str> = line.split(',').collect(); if tokens.len() < 6 { continue; } let height = f64::from_str(tokens[0]).ok().map(Meters); let t_c = f64::from_str(tokens[1]).ok().map(Celsius); let dp_c = f64::from_str(tokens[2]).ok().map(Celsius); let press_hpa = f64::from_str(tokens[3]).ok().map(HectoPascal); let wspd = f64::from_str(tokens[4]).ok(); let wdir = f64::from_str(tokens[5]).ok(); let wind = wspd.and_then(|wspd| { wdir.map(|wdir| WindSpdDir { speed: Knots(wspd), direction: wdir, }) }); snd = snd .with_sfc_temperature(t_c) .with_station_info(StationInfo::new().with_elevation(height)) .with_sfc_dew_point(dp_c) .with_station_pressure(press_hpa) .with_sfc_wind(wind) } snd }