1mod cancel;
2mod classification;
3mod error;
4mod pipeline;
5mod voxel_grid;
6
7pub use cancel::CancellationToken;
8pub use classification::ClassificationFilter;
9pub use error::{Result, VoxelizeError};
10pub use voxel_grid::{VoxelGrid, VoxelPoint, BOUNDED_QUEUE_CAPACITY, CHUNK_SIZE};
11
12use std::path::Path;
13
14use las::point::Format;
15use las::{Color, Point};
16
17use error::{join_consumer, join_producer};
18use pipeline::run_pipeline;
19
20pub struct VoxelizeConfig {
21 pub voxel_size: f64,
22 pub classification_filter: ClassificationFilter,
23 pub counts_as_intensity: bool,
24 pub cancel: Option<CancellationToken>,
25}
26
27impl VoxelizeConfig {
28 pub fn new(voxel_size: f64) -> Self {
29 Self {
30 voxel_size,
31 classification_filter: ClassificationFilter::all(),
32 counts_as_intensity: false,
33 cancel: None,
34 }
35 }
36}
37
38pub fn voxelize(input: &Path, output: &Path, config: &VoxelizeConfig) -> Result<()> {
39 if config.voxel_size <= 0.0 {
40 return Err(VoxelizeError::InvalidVoxelSize);
41 }
42
43 let header = {
44 let reader = las::Reader::from_path(input)?;
45 reader.header().clone()
46 };
47 let bounds = header.bounds();
48 let origin = [bounds.min.x, bounds.min.y, bounds.min.z];
49 let dimensions = [
50 bounds.max.x - bounds.min.x,
51 bounds.max.y - bounds.min.y,
52 bounds.max.z - bounds.min.z,
53 ];
54
55 let (producer, consumer) = run_pipeline(
56 input,
57 output.to_path_buf(),
58 header,
59 origin,
60 dimensions,
61 config.voxel_size,
62 config.classification_filter.clone(),
63 config.counts_as_intensity,
64 config.cancel.clone(),
65 );
66
67 let consumer_result = join_consumer(consumer);
68 let producer_result = join_producer(producer);
69
70 let cancelled = matches!(
71 (&consumer_result, &producer_result),
72 (Err(VoxelizeError::Cancelled), _) | (_, Err(VoxelizeError::Cancelled))
73 );
74
75 if cancelled {
76 let _ = std::fs::remove_file(output);
77 return Err(VoxelizeError::Cancelled);
78 }
79
80 consumer_result?;
81 producer_result?;
82
83 Ok(())
84}
85
86pub(crate) fn voxel_point_to_las_point(
87 voxel_point: &VoxelPoint,
88 format: &Format,
89 counts_as_intensity: bool,
90) -> Point {
91 let mut point = Point::default();
92 point.x = voxel_point.x;
93 point.y = voxel_point.y;
94 point.z = voxel_point.z;
95 if counts_as_intensity {
96 point.intensity = voxel_point.count;
97 }
98 if format.has_gps_time {
99 point.gps_time = Some(0.0);
100 }
101 if format.has_color {
102 point.color = Some(Color::default());
103 }
104 if format.has_waveform {
105 point.waveform = Some(Default::default());
106 }
107 if format.has_nir {
108 point.nir = Some(0);
109 }
110 if format.extra_bytes > 0 {
111 point.extra_bytes = vec![0; format.extra_bytes as usize];
112 }
113 point
114}