use std::{
error::Error,
fs,
io::{Read, stdin},
sync::mpsc,
thread::{self, JoinHandle},
};
use clap::{Parser, Subcommand};
use dipr::{PrecipRate, parse_dipr};
use geojson::{FeatureCollection, GeoJson};
use shapefile::{
Error as ShapefileError, Point, Writer,
dbase::{FieldValue, Record, TableWriterBuilder},
record::polygon::GenericPolygon,
};
fn read_and_convert(input: &str) -> Result<PrecipRate, Box<dyn Error>> {
if input == "-" {
let mut input_buf = vec![];
stdin().read_to_end(&mut input_buf)?;
Ok(parse_dipr(&input_buf)?)
} else {
let input_file = fs::read(input)?;
Ok(parse_dipr(&input_file)?)
}
}
fn convert_to_shapefile(
dipr: PrecipRate,
skip_zeros: bool,
output: &str,
) -> Result<(), Box<dyn Error>> {
let (tx, rx) = mpsc::channel::<(GenericPolygon<Point>, FieldValue)>();
const PRECIP_RATE_FIELD_NAME: &str = "Precip Rate";
let table_builder =
TableWriterBuilder::new().add_float_field(PRECIP_RATE_FIELD_NAME.try_into().unwrap(), 5, 3);
let mut writer = Writer::from_path(output, table_builder)?;
let mut record = Record::default();
let writer_thread: JoinHandle<Result<(), ShapefileError>> = thread::spawn(move || {
for (polygon, precip_rate) in rx {
record.insert(PRECIP_RATE_FIELD_NAME.to_string(), precip_rate);
writer.write_shape_and_record(&polygon, &record)?;
}
Ok(())
});
for bin in dipr.into_shapefile_iter(skip_zeros) {
tx.send(bin)?;
}
drop(tx);
let _ = writer_thread.join();
Ok(())
}
fn convert_to_geojson(dipr: PrecipRate, skip_zeros: bool) -> Result<(), Box<dyn Error>> {
println!(
"{}",
GeoJson::FeatureCollection(FeatureCollection {
features: dipr.into_geojson_iter(skip_zeros).collect(),
..Default::default()
})
);
Ok(())
}
#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
struct DiprCli {
#[command(subcommand)]
action: Action,
}
#[derive(Debug, Subcommand)]
enum Action {
Info {
input: String,
},
ToGeojson {
input: String,
#[arg(long)]
skip_zeros: bool,
},
ToShapefile {
input: String,
#[arg(long)]
skip_zeros: bool,
output: String,
},
}
fn main() -> Result<(), Box<dyn Error>> {
let args = DiprCli::parse();
match args.action {
Action::Info { input } => {
let dipr = read_and_convert(&input)?;
println!("{}", dipr);
}
Action::ToGeojson { input, skip_zeros } => {
let dipr = read_and_convert(&input)?;
convert_to_geojson(dipr, skip_zeros)?;
}
Action::ToShapefile {
input,
skip_zeros,
output,
} => {
let dipr = read_and_convert(&input)?;
convert_to_shapefile(dipr, skip_zeros, &output)?
}
};
Ok(())
}