mod las;
#[cfg(feature = "laz")]
mod laz;
use crate::{Error, Header, PointData, PointDataBuilder, Result};
use std::{
fs::File,
io::{BufReader, Seek},
path::Path,
};
trait ReadPoints {
fn fill_into_bytes(&mut self, n: u64, out: &mut Vec<u8>, record_len: usize) -> Result<u64>;
fn seek(&mut self, index: u64) -> Result<()>;
fn header(&self) -> &Header;
}
#[cfg(feature = "laz")]
#[derive(Debug, Clone, Copy)]
pub enum LazParallelism {
#[cfg(feature = "laz-parallel")]
Yes,
No,
}
#[cfg_attr(
feature = "laz",
doc = "Currently, the only option is the selection of LAZ parallelism via [`LazParallelism`]."
)]
#[cfg_attr(
not(feature = "laz"),
doc = "Currently, the only option is the selection of LAZ parallelism via `LazParallelism`."
)]
#[derive(Debug, Clone, Copy)]
pub struct ReaderOptions {
#[cfg(feature = "laz")]
laz_parallelism: LazParallelism,
}
impl ReaderOptions {
#[cfg(feature = "laz")]
pub fn with_laz_parallelism(mut self, laz_parallelism: LazParallelism) -> Self {
self.laz_parallelism = laz_parallelism;
self
}
}
impl Default for ReaderOptions {
fn default() -> Self {
#[cfg(feature = "laz-parallel")]
{
Self {
laz_parallelism: LazParallelism::Yes,
}
}
#[cfg(all(feature = "laz", not(feature = "laz-parallel")))]
{
Self {
laz_parallelism: LazParallelism::No,
}
}
#[cfg(not(feature = "laz"))]
{
Self {}
}
}
}
#[allow(missing_debug_implementations)]
pub struct Reader {
point_reader: Box<dyn ReadPoints>,
}
impl Reader {
pub fn new<R: std::io::Read + Seek + Send + Sync + 'static>(read: R) -> Result<Reader> {
Self::with_options(read, ReaderOptions::default())
}
pub fn with_options<R: std::io::Read + Seek + Send + Sync + 'static>(
mut read: R,
options: ReaderOptions,
) -> Result<Reader> {
let header = Header::new(&mut read)?;
if header.point_format().is_compressed {
#[cfg(feature = "laz")]
{
let point_reader: Box<dyn ReadPoints> = match options.laz_parallelism {
#[cfg(feature = "laz-parallel")]
LazParallelism::Yes => {
laz::PointReader::new_parallel(read, header).map(Box::new)?
}
LazParallelism::No => laz::PointReader::new(read, header).map(Box::new)?,
};
Ok(Reader { point_reader })
}
#[cfg(not(feature = "laz"))]
{
Err(Error::LaszipNotEnabled)
}
} else {
let _ = options;
Ok(Reader {
point_reader: Box::new(las::PointReader::new(read, header)?),
})
}
}
pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Reader> {
File::open(path)
.map_err(Error::from)
.and_then(|file| Reader::new(BufReader::new(file)))
}
pub fn header(&self) -> &Header {
self.point_reader.header()
}
pub fn read_points(&mut self, n: u64) -> Result<PointData> {
let mut pd = PointDataBuilder::new()
.for_header(self.point_reader.header())
.build();
let _ = self.fill_points(n, &mut pd)?;
Ok(pd)
}
pub fn read_all(&mut self) -> Result<PointData> {
let remaining = self.point_reader.header().number_of_points();
self.read_points(remaining)
}
pub fn fill_points(&mut self, n: u64, target: &mut PointData) -> Result<u64> {
let header = self.point_reader.header();
if target.format() != header.point_format() {
*target = PointDataBuilder::new().for_header(header).build();
}
let record_len = target.record_len();
let bytes = target.take_bytes_mut();
self.point_reader.fill_into_bytes(n, bytes, record_len)
}
pub fn seek(&mut self, position: u64) -> Result<()> {
self.point_reader.seek(position)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{Point, Writer};
#[test]
fn seek() {
let mut writer = Writer::default();
writer.write_point(Default::default()).unwrap();
let point = Point {
x: 1.,
y: 2.,
z: 3.,
..Default::default()
};
writer.write_point(point.clone()).unwrap();
let mut reader = Reader::new(writer.into_inner().unwrap()).unwrap();
reader.seek(1).unwrap();
let pd = reader.read_points(1).unwrap();
assert_eq!(pd.len(), 1);
let got = pd.points().next().unwrap().unwrap();
assert_eq!(point, got);
let rest = reader.read_points(1).unwrap();
assert!(rest.is_empty());
}
}