mod las;
#[cfg(feature = "laz")]
mod laz;
use crate::{raw, Builder, Error, Header, Point, Result, Vlr};
use std::{
cmp::Ordering,
fs::File,
io::{BufReader, Seek, SeekFrom},
path::Path,
};
trait ReadPoints {
fn read_point(&mut self) -> Result<Option<Point>>;
fn read_points(&mut self, n: u64, points: &mut Vec<Point>) -> Result<u64>;
fn seek(&mut self, index: u64) -> Result<()>;
fn header(&self) -> &Header;
}
#[allow(missing_debug_implementations)]
pub struct PointIterator<'a> {
point_reader: &'a mut dyn ReadPoints,
}
impl Iterator for PointIterator<'_> {
type Item = Result<Point>;
fn next(&mut self) -> Option<Self::Item> {
self.point_reader.read_point().transpose()
}
}
#[deprecated(
since = "0.9.0",
note = "This interface has been refactored so that importing `Read` is no longer required"
)]
pub trait Read {
fn header(&self) -> &Header;
fn read(&mut self) -> Option<Result<Point>>;
fn read_n(&mut self, n: u64) -> Result<Vec<Point>>;
fn read_n_into(&mut self, n: u64, points: &mut Vec<Point>) -> Result<u64>;
fn read_all_points(&mut self, points: &mut Vec<Point>) -> Result<u64>;
fn seek(&mut self, position: u64) -> Result<()>;
fn points(&mut self) -> PointIterator<'_>;
}
#[allow(missing_debug_implementations)]
pub struct Reader {
point_reader: Box<dyn ReadPoints>,
}
impl Reader {
pub fn new<R: std::io::Read + Seek + Send + 'static>(mut read: R) -> Result<Reader> {
use std::io::Read;
let raw_header = raw::Header::read_from(&mut read)?;
let mut position = u64::from(raw_header.header_size);
let number_of_variable_length_records = raw_header.number_of_variable_length_records;
let offset_to_point_data = u64::from(raw_header.offset_to_point_data);
let offset_to_end_of_points = raw_header.offset_to_end_of_points();
let evlr = raw_header.evlr;
let mut builder = Builder::new(raw_header)?;
for _ in 0..number_of_variable_length_records {
let vlr = raw::Vlr::read_from(&mut read, false).map(Vlr::new)?;
position += vlr.len(false) as u64;
builder.vlrs.push(vlr);
}
match position.cmp(&offset_to_point_data) {
Ordering::Less => {
let _ = read
.by_ref()
.take(offset_to_point_data - position)
.read_to_end(&mut builder.vlr_padding)?;
}
Ordering::Equal => {} Ordering::Greater => {
return Err(Error::OffsetToPointDataTooSmall(
offset_to_point_data as u32,
))
}
}
let _ = read.seek(SeekFrom::Start(offset_to_end_of_points))?;
if let Some(evlr) = evlr {
if !builder.point_format.is_compressed {
match evlr.start_of_first_evlr.cmp(&offset_to_end_of_points) {
Ordering::Less => {
return Err(Error::OffsetToEvlrsTooSmall(evlr.start_of_first_evlr));
}
Ordering::Equal => {} Ordering::Greater => {
let n = evlr.start_of_first_evlr - offset_to_end_of_points;
let _ = read
.by_ref()
.take(n)
.read_to_end(&mut builder.point_padding)?;
}
}
}
let _ = read.seek(SeekFrom::Start(evlr.start_of_first_evlr))?;
builder
.evlrs
.push(raw::Vlr::read_from(&mut read, true).map(Vlr::new)?);
}
let _ = read.seek(SeekFrom::Start(offset_to_point_data))?;
if let Some(version) = builder.minimum_supported_version() {
if version > builder.version {
log::warn!(
"upgrading las version to {} (from {})",
version,
builder.version
);
builder.version = version;
}
}
let header = builder.into_header()?;
if header.point_format().is_compressed {
#[cfg(feature = "laz")]
{
Ok(Reader {
point_reader: Box::new(laz::PointReader::new(read, header)?),
})
}
#[cfg(not(feature = "laz"))]
{
Err(Error::LaszipNotEnabled)
}
} else {
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_point(&mut self) -> Result<Option<Point>> {
self.point_reader.read_point()
}
pub fn read_points(&mut self, n: u64) -> Result<Vec<Point>> {
let mut points = Vec::with_capacity(n.try_into()?);
let _ = self.point_reader.read_points(n, &mut points)?;
Ok(points)
}
pub fn read_points_into(&mut self, n: u64, points: &mut Vec<Point>) -> Result<u64> {
self.point_reader.read_points(n, points)
}
#[deprecated(
since = "0.9.0",
note = "Use read_point() instead, which returns a Result<Option<Point>>"
)]
pub fn read(&mut self) -> Option<Result<Point>> {
self.point_reader.read_point().transpose()
}
#[deprecated(since = "0.9.0", note = "Use read_points() instead")]
pub fn read_n(&mut self, n: u64) -> Result<Vec<Point>> {
self.read_points(n)
}
#[deprecated(since = "0.9.0", note = "Use read_points_into() instead")]
pub fn read_n_into(&mut self, n: u64, points: &mut Vec<Point>) -> Result<u64> {
self.read_points_into(n, points)
}
pub fn read_all_points_into(&mut self, points: &mut Vec<Point>) -> Result<u64> {
let point_count = self.point_reader.header().number_of_points();
self.point_reader.read_points(point_count, points)
}
#[deprecated(since = "0.9.0", note = "Use read_all_points_into() instead")]
pub fn read_all_points(&mut self, points: &mut Vec<Point>) -> Result<u64> {
self.read_all_points_into(points)
}
pub fn seek(&mut self, position: u64) -> Result<()> {
self.point_reader.seek(position)
}
pub fn points(&mut self) -> PointIterator<'_> {
PointIterator {
point_reader: &mut *self.point_reader,
}
}
}
#[allow(deprecated)]
impl Read for Reader {
fn header(&self) -> &Header {
self.header()
}
fn read(&mut self) -> Option<Result<Point>> {
self.read()
}
fn read_n(&mut self, n: u64) -> Result<Vec<Point>> {
self.read_n(n)
}
fn read_n_into(&mut self, n: u64, points: &mut Vec<Point>) -> Result<u64> {
self.read_n_into(n, points)
}
fn read_all_points(&mut self, points: &mut Vec<Point>) -> Result<u64> {
self.read_all_points(points)
}
fn seek(&mut self, position: u64) -> Result<()> {
self.seek(position)
}
fn points(&mut self) -> PointIterator<'_> {
self.points()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::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();
assert_eq!(point, reader.read_point().unwrap().unwrap());
assert!(reader.read_point().unwrap().is_none());
}
}