use std::fs::File;
use std::io::Cursor;
use std::io::{Read, Seek, SeekFrom};
use crate::las::gps::{v2::GpsTimeCompressor, v2::GpsTimeDecompressor, GpsTime};
use crate::las::point0::{v2::LasPoint0Compressor, v2::LasPoint0Decompressor, Point0};
use crate::las::rgb::{v2::LasRGBCompressor, v2::LasRGBDecompressor, RGB};
use crate::las::v1;
use crate::packers::Packable;
use crate::record::{FieldCompressor, FieldDecompressor};
use crate::record::{
RecordCompressor, RecordDecompressor, SequentialPointRecordCompressor,
SequentialPointRecordDecompressor,
};
#[test]
fn test_compression_decompression_of_point_10() {
let mut compressor =
SequentialPointRecordCompressor::new(std::io::Cursor::new(Vec::<u8>::new()));
compressor.add_field_compressor(LasPoint0Compressor::default());
let n: i32 = 10000;
let mut buf = [0u8; 20];
for i in 0..n {
let point = Point0 {
x: i,
y: i + 1000,
z: i + 10000,
intensity: (i + (1 << 15)) as u16,
return_number: ((i >> 3) & 0x7) as u8,
number_of_returns_of_given_pulse: (i & 0x7) as u8,
scan_direction_flag: (i & 1) != 0,
edge_of_flight_line: ((i + 1) & 1) != 0,
classification: (i % 256) as u8,
scan_angle_rank: (i % 128) as i8,
user_data: ((i >> 4) % 256) as u8,
point_source_id: (i * 30 % (1 << 16)) as u16,
};
point.pack_into(&mut buf);
compressor.compress_next(&buf).unwrap();
}
compressor.done().unwrap();
let compressed_data = compressor.into_inner().into_inner();
let mut decompressor =
SequentialPointRecordDecompressor::new(std::io::Cursor::new(compressed_data));
decompressor.add_field_decompressor(LasPoint0Decompressor::default());
for i in 0..n {
decompressor.decompress_next(&mut buf).unwrap();
let point = Point0::unpack_from(&buf);
let expected_point = Point0 {
x: i,
y: i + 1000,
z: i + 10000,
intensity: (i + (1 << 15)) as u16,
return_number: ((i >> 3) & 0x7) as u8,
number_of_returns_of_given_pulse: (i & 0x7) as u8,
scan_direction_flag: (i & 1) != 0,
edge_of_flight_line: ((i + 1) & 1) != 0,
classification: (i % 256) as u8,
scan_angle_rank: (i % 128) as i8,
user_data: ((i >> 4) % 256) as u8,
point_source_id: (i * 30 % (1 << 16)) as u16,
};
assert_eq!(point, expected_point);
}
}
#[test]
fn test_rgb() {
let mut compressor = SequentialPointRecordCompressor::new(Cursor::new(Vec::<u8>::new()));
compressor.add_field_compressor(LasRGBCompressor::default());
let n = 10000;
let mut buf = [0u8; 6];
for i in 0..n {
let rgb = RGB {
red: (i + 1000) % 65535,
green: (i + 5000) % 65535,
blue: (i + 10000) % 65535,
};
rgb.pack_into(&mut buf);
compressor.compress_next(&buf).unwrap();
}
compressor.done().unwrap();
let compressed_data = compressor.into_inner().into_inner();
let mut decompressor =
SequentialPointRecordDecompressor::new(std::io::Cursor::new(compressed_data));
decompressor.add_field_decompressor(LasRGBDecompressor::default());
for i in 0..n {
let expected_rgb = RGB {
red: (i + 1000) % 65535,
green: (i + 5000) % 65535,
blue: (i + 10000) % 65535,
};
decompressor.decompress_next(&mut buf).unwrap();
let rgb = RGB::unpack_from(&buf);
assert_eq!(rgb, expected_rgb);
}
}
#[test]
fn test_gps_time() {
let mut compressor = SequentialPointRecordCompressor::new(Cursor::new(Vec::<u8>::new()));
compressor.add_field_compressor(GpsTimeCompressor::default());
let n = 10000;
let mut buf = [0u8; std::mem::size_of::<i64>()];
for i in 0..n {
let gps_time = GpsTime {
value: (i + 48741) % std::i64::MAX,
};
gps_time.pack_into(&mut buf);
compressor.compress_next(&buf).unwrap();
}
compressor.done().unwrap();
let compressed_data = compressor.into_inner().into_inner();
let mut decompressor = SequentialPointRecordDecompressor::new(Cursor::new(compressed_data));
decompressor.add_field_decompressor(GpsTimeDecompressor::default());
for i in 0..n {
let expected_gps_time = GpsTime {
value: (i + 48741) % std::i64::MAX,
};
decompressor.decompress_next(&mut buf).unwrap();
let gps_time = GpsTime::unpack_from(&buf);
assert_eq!(expected_gps_time, gps_time);
}
}
const LAS_HEADER_SIZE: u64 = 227;
const NUM_POINTS: usize = 1065;
const VLR_HEADER_SIZE: u64 = 54;
macro_rules! loop_test_on_buffer {
($test_name:ident, $source_las:expr, $point_size:expr, $point_start:expr, $field_compressors:expr, $field_decompressors:expr) => {
#[test]
fn $test_name() {
use crate::record::RecordCompressor;
use crate::record::RecordDecompressor;
let mut las_file = File::open($source_las).unwrap();
las_file.seek(SeekFrom::Start($point_start)).unwrap();
let mut expected_buf = [0u8; $point_size];
let mut compressor =
SequentialPointRecordCompressor::new(Cursor::new(Vec::<u8>::new()));
for c in $field_compressors {
compressor.add_boxed_compressor(c);
}
for _ in 0..NUM_POINTS {
las_file.read_exact(&mut expected_buf).unwrap();
compressor.compress_next(&expected_buf).unwrap();
}
compressor.done().unwrap();
let mut compression_output = compressor.into_inner();
compression_output.set_position(0);
let mut decompressor = SequentialPointRecordDecompressor::new(compression_output);
for d in $field_decompressors {
decompressor.add_boxed_decompressor(d);
}
let mut buf = [0u8; $point_size];
las_file.seek(SeekFrom::Start($point_start)).unwrap();
for i in 0..NUM_POINTS {
las_file.read_exact(&mut expected_buf).unwrap();
decompressor
.decompress_next(&mut buf)
.expect(&format!("Failed to decompress point {}", i));
let mut s;
let mut e = 0;
for j in 0..$point_size / 32 {
s = j * 32;
e = (j + 1) * 32;
assert_eq!(
buf[s..e],
expected_buf[s..e],
"Buffers[{}..{}] for point {} are not eq!",
s,
e,
i
);
}
assert_eq!(buf[e..], expected_buf[e..]);
}
}
};
}
macro_rules! vec_of_boxed_buffer_compressors {
($($x: expr),*) => {{
let mut vector = Vec::new();
$(vector.push(Box::new($x) as Box<dyn FieldCompressor<_> + Send + Sync>);)*
vector
}}
}
macro_rules! vec_of_boxed_buffer_decompressors {
($($x: expr),*) => {{
let mut vector = Vec::new();
$(vector.push(Box::new($x) as Box<dyn FieldDecompressor<_> + Send + Sync>);)*
vector
}}
}
loop_test_on_buffer!(
test_point_0_buffer,
"tests/data/point10.las",
20,
LAS_HEADER_SIZE,
vec_of_boxed_buffer_compressors![v1::LasPoint0Compressor::default()],
vec_of_boxed_buffer_decompressors![v1::LasPoint0Decompressor::default()]
);
loop_test_on_buffer!(
test_point_1_buffer,
"tests/data/point-time.las",
28,
LAS_HEADER_SIZE,
vec_of_boxed_buffer_compressors![
v1::LasPoint0Compressor::default(),
v1::LasGpsTimeCompressor::default()
],
vec_of_boxed_buffer_decompressors![
v1::LasPoint0Decompressor::default(),
v1::LasGpsTimeDecompressor::default()
]
);
loop_test_on_buffer!(
test_point_2_buffer,
"tests/data/point-color.las",
26,
LAS_HEADER_SIZE,
vec_of_boxed_buffer_compressors![
v1::LasPoint0Compressor::default(),
v1::LasRGBCompressor::default()
],
vec_of_boxed_buffer_decompressors![
v1::LasPoint0Decompressor::default(),
v1::LasRGBDecompressor::default()
]
);
loop_test_on_buffer!(
test_point_3_buffer,
"tests/data/point-time-color.las",
34,
LAS_HEADER_SIZE,
vec_of_boxed_buffer_compressors![
v1::LasPoint0Compressor::default(),
v1::LasGpsTimeCompressor::default(),
v1::LasRGBCompressor::default()
],
vec_of_boxed_buffer_decompressors![
v1::LasPoint0Decompressor::default(),
v1::LasGpsTimeDecompressor::default(),
v1::LasRGBDecompressor::default()
]
);
loop_test_on_buffer!(
test_point_3_extra_bytes_buffer,
"tests/data/extra-bytes.las",
61,
LAS_HEADER_SIZE + VLR_HEADER_SIZE + (5 * 192),
vec_of_boxed_buffer_compressors![
v1::LasPoint0Compressor::default(),
v1::LasGpsTimeCompressor::default(),
v1::LasRGBCompressor::default(),
v1::LasExtraByteCompressor::new(27)
],
vec_of_boxed_buffer_decompressors![
v1::LasPoint0Decompressor::default(),
v1::LasGpsTimeDecompressor::default(),
v1::LasRGBDecompressor::default(),
v1::LasExtraByteDecompressor::new(27)
]
);