use std::path::Path;
use crate::core::errors::LasError;
use crate::core::las_file::LASFile;
use crate::reader::data::NullPolicy;
#[cfg(not(feature = "python"))]
use std::collections::HashMap;
#[cfg(not(feature = "python"))]
use crate::core::types::{CurveItem, ItemWrapper};
#[derive(Debug, Clone)]
pub struct ReadOptions {
pub ignore_header_errors: bool,
pub mnemonic_case: Option<String>,
pub ignore_data: bool,
pub null_policy: Option<NullPolicy>,
pub read_policy: Option<String>,
pub ignore_comments: Vec<String>,
}
impl Default for ReadOptions {
fn default() -> Self {
ReadOptions {
ignore_header_errors: false,
mnemonic_case: None,
ignore_data: false,
null_policy: None,
read_policy: Some("comma-decimal-mark".to_string()),
ignore_comments: vec!["#".to_string()],
}
}
}
pub fn parse(content: &str) -> Result<LASFile, LasError> {
parse_with(content, &ReadOptions::default())
}
pub fn parse_with(content: &str, opts: &ReadOptions) -> Result<LASFile, LasError> {
crate::reader::read_las(
content,
opts.ignore_header_errors,
opts.mnemonic_case.as_deref(),
opts.ignore_data,
opts.null_policy.clone(),
opts.read_policy.clone(),
&opts.ignore_comments,
)
}
pub fn read_file<P: AsRef<Path>>(path: P) -> Result<LASFile, LasError> {
read_file_with(path, &ReadOptions::default())
}
pub fn read_file_with<P: AsRef<Path>>(path: P, opts: &ReadOptions) -> Result<LASFile, LasError> {
let path = path.as_ref();
let bytes = std::fs::read(path)
.map_err(|e| LasError::Io(format!("Cannot read file {}: {}", path.display(), e)))?;
if bytes.len() >= 4 && &bytes[0..4] == b"LASF" {
return Err(LasError::Data(format!(
"{} is a binary LiDAR LAS file, not a Log ASCII Standard file",
path.display()
)));
}
let content = decode_bytes(&bytes);
parse_with(&content, opts)
}
fn decode_bytes(bytes: &[u8]) -> String {
if let Some(rest) = bytes.strip_prefix(b"\xEF\xBB\xBF") {
return String::from_utf8_lossy(rest).into_owned();
}
if bytes.starts_with(b"\xFF\xFE") {
let (cow, _, _) = encoding_rs::UTF_16LE.decode(bytes);
return cow.into_owned();
}
if bytes.starts_with(b"\xFE\xFF") {
let (cow, _, _) = encoding_rs::UTF_16BE.decode(bytes);
return cow.into_owned();
}
if let Ok(s) = std::str::from_utf8(bytes) {
return s.to_owned();
}
let (cow, _, _) = encoding_rs::WINDOWS_1252.decode(bytes);
cow.into_owned()
}
#[cfg(not(feature = "python"))]
impl LASFile {
pub fn curve_mnemonics(&self) -> Vec<&str> {
self.curves_section
.items
.iter()
.map(|item| item.session_mnemonic())
.collect()
}
pub fn get_curve(&self, mnemonic: &str) -> Option<&CurveItem> {
let idx = self.curves_section.find_index_by_mnemonic(mnemonic)?;
match &self.curves_section.items[idx] {
ItemWrapper::Curve(c) => Some(c),
_ => None,
}
}
pub fn curve_data(&self, mnemonic: &str) -> Option<&[f64]> {
self.get_curve(mnemonic).map(|c| c.curve_data.as_slice())
}
pub fn index(&self) -> Option<&[f64]> {
let pos = self.curves_section.index_curve_position()?;
match &self.curves_section.items[pos] {
ItemWrapper::Curve(c) => Some(c.curve_data.as_slice()),
_ => None,
}
}
pub fn well_value(&self, mnemonic: &str) -> Option<String> {
let idx = self.well_section.find_index_by_mnemonic(mnemonic)?;
Some(self.well_section.items[idx].value().display_str())
}
pub fn to_las_string(&self) -> Result<String, LasError> {
let empty: HashMap<usize, String> = HashMap::new();
crate::writer::format_las(
self, None, None, false, None, None, " ", " ", None, &empty, None, None, None,
)
}
pub fn write_file<P: AsRef<Path>>(&self, path: P) -> Result<(), LasError> {
let path = path.as_ref();
let text = self.to_las_string()?;
std::fs::write(path, text)
.map_err(|e| LasError::Io(format!("Cannot write file {}: {}", path.display(), e)))
}
}