mod classification;
mod format;
mod scan_direction;
pub use self::{classification::Classification, format::Format, scan_direction::ScanDirection};
use crate::{raw, raw::point::Waveform, Color, Error, Result, Transform, Vector};
use thiserror::Error;
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Point {
pub x: f64,
pub y: f64,
pub z: f64,
pub intensity: u16,
pub return_number: u8,
pub number_of_returns: u8,
pub scan_direction: ScanDirection,
pub is_edge_of_flight_line: bool,
pub classification: Classification,
pub is_synthetic: bool,
pub is_key_point: bool,
pub is_withheld: bool,
pub is_overlap: bool,
pub scanner_channel: u8,
pub scan_angle: f32,
pub user_data: u8,
pub point_source_id: u16,
pub gps_time: Option<f64>,
pub color: Option<Color>,
pub waveform: Option<Waveform>,
pub nir: Option<u16>,
pub extra_bytes: Vec<u8>,
}
impl Point {
pub fn new(mut raw_point: raw::Point, transforms: &Vector<Transform>) -> Point {
let is_overlap = raw_point.flags.is_overlap();
raw_point.flags.clear_overlap_class();
Point {
x: transforms.x.direct(raw_point.x),
y: transforms.y.direct(raw_point.y),
z: transforms.z.direct(raw_point.z),
intensity: raw_point.intensity,
return_number: raw_point.flags.return_number(),
number_of_returns: raw_point.flags.number_of_returns(),
scan_direction: raw_point.flags.scan_direction(),
is_edge_of_flight_line: raw_point.flags.is_edge_of_flight_line(),
classification: raw_point
.flags
.to_classification()
.expect("Overlap classification should have been cleared"),
is_synthetic: raw_point.flags.is_synthetic(),
is_key_point: raw_point.flags.is_key_point(),
is_withheld: raw_point.flags.is_withheld(),
is_overlap,
scan_angle: raw_point.scan_angle.into(),
scanner_channel: raw_point.flags.scanner_channel(),
user_data: raw_point.user_data,
point_source_id: raw_point.point_source_id,
gps_time: raw_point.gps_time,
color: raw_point.color,
waveform: raw_point.waveform,
nir: raw_point.nir,
extra_bytes: raw_point.extra_bytes,
}
}
pub fn into_raw(self, transforms: &Vector<Transform>) -> Result<raw::Point> {
Ok(raw::Point {
x: transforms.x.inverse(self.x)?,
y: transforms.y.inverse(self.y)?,
z: transforms.z.inverse(self.z)?,
intensity: self.intensity,
flags: self.flags()?,
scan_angle: self.scan_angle.into(),
user_data: self.user_data,
point_source_id: self.point_source_id,
gps_time: self.gps_time,
color: self.color,
waveform: self.waveform,
nir: self.nir,
extra_bytes: self.extra_bytes,
})
}
pub fn flags(&self) -> Result<raw::point::Flags> {
if self.return_number > 15 {
Err(Error::ReturnNumber {
return_number: self.return_number,
version: None,
})
} else if self.number_of_returns > 15 {
Err(Error::ReturnNumber {
return_number: self.number_of_returns,
version: None,
})
} else if self.scanner_channel > 3 {
Err(Error::InvalidScannerChannel(self.scanner_channel))
} else {
let a = (self.number_of_returns << 4) + self.return_number;
let mut b = self.scanner_channel << 4;
if self.is_synthetic {
b += 1;
}
if self.is_key_point {
b += 2;
}
if self.is_withheld {
b += 4;
}
if self.is_overlap {
b += 8;
}
if self.scan_direction == ScanDirection::LeftToRight {
b += 64;
}
if self.is_edge_of_flight_line {
b += 128;
}
Ok(raw::point::Flags::ThreeByte(
a,
b,
self.classification.into(),
))
}
}
pub fn matches(&self, format: &Format) -> bool {
self.gps_time.is_some() == format.has_gps_time
&& self.color.is_some() == format.has_color
&& self.waveform.is_some() == format.has_waveform
&& self.nir.is_some() == format.has_nir
&& self.extra_bytes.len() == format.extra_bytes as usize
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn flags_invalid_return_number() {
assert!(Point {
return_number: 16,
..Default::default()
}
.flags()
.is_err());
}
#[test]
fn flags_invalid_number_of_returns() {
assert!(Point {
number_of_returns: 16,
..Default::default()
}
.flags()
.is_err());
}
#[test]
fn flags_invalid_scanner_channel() {
assert!(Point {
scanner_channel: 4,
..Default::default()
}
.flags()
.is_err());
}
#[test]
fn overlap() {
use crate::raw::point::Flags;
let raw_point = raw::Point {
flags: Flags::TwoByte(0, 12),
..Default::default()
};
let point = Point::new(raw_point, &Default::default());
assert_eq!(Classification::Unclassified, point.classification);
assert!(point.is_overlap);
}
}