use las::{PointData, PointDataBuilder, Reader};
use std::{collections::BTreeMap, env, io::Write};
const CHUNK: u64 = 500_000;
#[derive(Debug)]
struct BBox {
min_x: f64,
max_x: f64,
min_y: f64,
max_y: f64,
min_z: f64,
max_z: f64,
}
impl BBox {
fn empty() -> Self {
Self {
min_x: f64::INFINITY,
max_x: f64::NEG_INFINITY,
min_y: f64::INFINITY,
max_y: f64::NEG_INFINITY,
min_z: f64::INFINITY,
max_z: f64::NEG_INFINITY,
}
}
fn update_from(&mut self, pd: &PointData) {
for v in pd.x() {
self.min_x = self.min_x.min(v);
self.max_x = self.max_x.max(v);
}
for v in pd.y() {
self.min_y = self.min_y.min(v);
self.max_y = self.max_y.max(v);
}
for v in pd.z() {
self.min_z = self.min_z.min(v);
self.max_z = self.max_z.max(v);
}
}
}
#[derive(Debug)]
struct IntensityStats {
min: u16,
max: u16,
sum: u64,
count: u64,
}
impl IntensityStats {
fn empty() -> Self {
Self {
min: u16::MAX,
max: u16::MIN,
sum: 0,
count: 0,
}
}
fn update_from(&mut self, pd: &PointData) {
for v in pd.intensity() {
self.min = self.min.min(v);
self.max = self.max.max(v);
self.sum += v as u64;
self.count += 1;
}
}
fn mean(&self) -> f64 {
if self.count > 0 {
self.sum as f64 / self.count as f64
} else {
0.0
}
}
}
fn print_progress(seen: u64, total: u64) {
let pct = if total > 0 {
(seen as f64 / total as f64) * 100.0
} else {
100.0
};
eprint!("\rprocessed {seen} / {total} points ({pct:.1}%)");
let _ = std::io::stderr().flush();
}
fn main() {
let input = env::args()
.nth(1)
.expect("usage: columns_streaming <input.las|laz>");
let mut reader = Reader::from_path(&input).expect("open input");
let total = reader.header().number_of_points();
let mut pd = PointDataBuilder::new().for_header(reader.header()).build();
let mut bbox = BBox::empty();
let mut intensity = IntensityStats::empty();
let mut hist: BTreeMap<u8, u64> = BTreeMap::new();
let mut seen = 0u64;
loop {
let n = reader.fill_points(CHUNK, &mut pd).expect("read chunk");
if n == 0 {
break;
}
seen += n;
bbox.update_from(&pd);
intensity.update_from(&pd);
for class in pd.classification() {
*hist.entry(class).or_insert(0) += 1;
}
print_progress(seen, total);
}
eprintln!();
assert_eq!(seen, total);
println!("points: {}", seen);
println!(
"bbox x: [{:.3}, {:.3}] ({:.3} wide)",
bbox.min_x,
bbox.max_x,
bbox.max_x - bbox.min_x
);
println!(
"bbox y: [{:.3}, {:.3}] ({:.3} wide)",
bbox.min_y,
bbox.max_y,
bbox.max_y - bbox.min_y
);
println!(
"bbox z: [{:.3}, {:.3}] ({:.3} wide)",
bbox.min_z,
bbox.max_z,
bbox.max_z - bbox.min_z
);
println!(
"intensity: min={} max={} mean={:.1}",
intensity.min,
intensity.max,
intensity.mean()
);
println!("classification histogram:");
for (class, count) in &hist {
println!(" {:3}: {}", class, count);
}
}