diff --git a/Cargo.toml b/Cargo.toml index 2eb0d24..31a68e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,10 @@ path = "tools/dla/main.rs" name = "point-cloud" path = "tools/point-cloud/main.rs" +[[bin]] +name = "transform" +path = "tools/transform/main.rs" + [dependencies] clap = {version="4.0", features=["derive"]} hex = "0.4" diff --git a/tools/transform/cmdline.rs b/tools/transform/cmdline.rs new file mode 100644 index 0000000..9093ac9 --- /dev/null +++ b/tools/transform/cmdline.rs @@ -0,0 +1,61 @@ +use clap::{Parser, ValueEnum}; +use std::path::PathBuf; + +use generative::wkio::GeometryFormat; + +#[derive(Debug, Clone, ValueEnum)] +pub enum Projection { + DropX, + DropY, + DropZ, + Pca, + Svd, + // Isometric, +} + +/// Perform transformations on geometries +#[derive(Debug, Parser)] +#[clap(name = "transform")] +pub struct CmdlineOptions { + /// Increase logging verbosity. Defaults to ERROR level. + #[clap(short, long, action = clap::ArgAction::Count)] + pub verbosity: u8, + + /// Output file to write result to. Defaults to stdout. + #[clap(short, long)] + pub output: Option, + + /// Output geometry format. + #[clap(short = 'O', long, default_value = "wkt")] + pub output_format: GeometryFormat, + + /// Input file to read input from. Defaults to stdin. + #[clap(short, long)] + pub input: Option, + + /// Input geometry format. + #[clap(short = 'I', long, default_value = "wkt")] + pub input_format: GeometryFormat, + + /// CCW rotation, in degrees. + #[clap(short, long, default_value = "0.0")] + pub rotation: f64, + + /// Additive offset in the x direction + #[clap(long, default_value = "0.0")] + pub x_offset: f64, + + /// Additive offset in the y direction + #[clap(long, default_value = "0.0")] + pub y_offset: f64, + + /// Multiplicative scale in the x direction + #[clap(short, long, default_value = "1.0")] + pub x_scale: f64, + + /// Multiplicative scale in the y direction + #[clap(short, long, default_value = "1.0")] + pub y_scale: f64, + // TODO: Add projections + // pub projection: Projection, +} diff --git a/tools/transform/main.rs b/tools/transform/main.rs new file mode 100644 index 0000000..c526fab --- /dev/null +++ b/tools/transform/main.rs @@ -0,0 +1,36 @@ +mod cmdline; + +use clap::Parser; +use generative::stdio::{get_input_reader, get_output_writer}; +use generative::wkio::{read_geometries, write_geometries}; +use stderrlog::ColorChoice; + +fn main() { + let args = cmdline::CmdlineOptions::parse(); + + stderrlog::new() + .verbosity(args.verbosity as usize + 1) // Default to WARN level. + .color(ColorChoice::Auto) + .init() + .expect("Failed to initialize stderrlog"); + + let writer = get_output_writer(args.output).unwrap(); + let reader = get_input_reader(args.input).unwrap(); + + let geometries = read_geometries(reader, &args.input_format); + write_geometries(writer, geometries, &args.output_format); + + // TODO: I don't think there needs to be any flattening + // * linear transforms can be done with the MapCoordsInplace trait on-the-fly + // * projections can be done by collecting CoordsIter into an array, performing the projection, + // and running back over the geometries with MapCoordsInplace to replace the coordinates. + // In either case, I don't think there needs to be a recursive flattener, or a flattened and + // tagged point array. + + // TODO: Describe the offset, scale, and rotate as a linear transformation. + // TODO: Determe if it's more efficient (with my problem size) to do it on-the-fly or all at once. + // TODO: Perhaps allow switching between buffered and dynamic approaches? Or perhaps chunk the + // transformations? + // TODO: Projections + // TODO: WKB I/O +}