1#![allow(clippy::upper_case_acronyms)]
2use std::{fs::File, io::BufWriter, io::Seek, io::Write, path::Path};
3
4use anyhow::{Context, Result};
5use las_rs::Builder;
6use pasture_core::{containers::BorrowedBuffer, layout::PointLayout};
7
8use crate::{base::PointWriter, las::las_point_format_from_point_layout};
9
10use super::{path_is_compressed_las_file, RawLASWriter, RawLAZWriter};
11
12enum WriterVariant<T: Write + Seek + Send + 'static> {
13 LAS(RawLASWriter<T>),
14 LAZ(RawLAZWriter<T>),
15}
16
17pub struct LASWriter<T: Write + Seek + Send + 'static> {
23 writer: WriterVariant<T>,
24}
25
26impl<T: Write + Seek + Send + 'static> LASWriter<T> {
27 pub fn from_writer_and_point_layout(
32 writer: T,
33 point_layout: &PointLayout,
34 is_compressed: bool,
35 ) -> Result<Self> {
36 let point_format = las_point_format_from_point_layout(point_layout);
38 let mut header_builder = Builder::from((1, 4));
39 header_builder.point_format = point_format;
40 header_builder.transforms = las_rs::Vector {
41 x: las_rs::Transform {
42 offset: 0.0,
43 scale: 0.001,
44 },
45 y: las_rs::Transform {
46 offset: 0.0,
47 scale: 0.001,
48 },
49 z: las_rs::Transform {
50 offset: 0.0,
51 scale: 0.001,
52 },
53 };
54 let las_header = header_builder
55 .into_header()
56 .context("Could not default-create LAS header")?;
57 Self::from_writer_and_header(writer, las_header, is_compressed)
58 }
59
60 pub fn from_writer_and_header(
63 writer: T,
64 header: las::Header,
65 is_compressed: bool,
66 ) -> Result<Self> {
67 let raw_writer: WriterVariant<T> = if is_compressed {
68 WriterVariant::LAZ(RawLAZWriter::from_write_and_header(writer, header)?)
69 } else {
70 WriterVariant::LAS(RawLASWriter::from_write_and_header(writer, header)?)
71 };
72 Ok(Self { writer: raw_writer })
73 }
74
75 pub fn into_inner(self) -> Result<T> {
78 match self.writer {
79 WriterVariant::LAS(writer) => writer.into_inner(),
80 WriterVariant::LAZ(writer) => writer.into_inner(),
81 }
82 }
83}
84
85impl LASWriter<BufWriter<File>> {
86 pub fn from_path_and_header<P: AsRef<Path>>(path: P, header: las::Header) -> Result<Self> {
88 let is_compressed = path_is_compressed_las_file(path.as_ref())?;
89 let writer = BufWriter::new(File::create(path)?);
90 Self::from_writer_and_header(writer, header, is_compressed)
91 }
92
93 pub fn from_path_and_point_layout<P: AsRef<Path>>(
95 path: P,
96 point_layout: &PointLayout,
97 ) -> Result<Self> {
98 let is_compressed = path_is_compressed_las_file(path.as_ref())?;
99 let writer = BufWriter::new(File::create(path)?);
100 Self::from_writer_and_point_layout(writer, point_layout, is_compressed)
101 }
102}
103
104impl<T: Write + Seek + Send + 'static> PointWriter for LASWriter<T> {
105 fn write<'a, B: BorrowedBuffer<'a>>(&mut self, points: &'a B) -> Result<()> {
106 match &mut self.writer {
107 WriterVariant::LAS(writer) => writer.write(points),
108 WriterVariant::LAZ(writer) => writer.write(points),
109 }
110 }
111
112 fn flush(&mut self) -> Result<()> {
113 match &mut self.writer {
114 WriterVariant::LAS(writer) => writer.flush(),
115 WriterVariant::LAZ(writer) => writer.flush(),
116 }
117 }
118
119 fn get_default_point_layout(&self) -> &PointLayout {
120 match &self.writer {
121 WriterVariant::LAS(writer) => writer.get_default_point_layout(),
122 WriterVariant::LAZ(writer) => writer.get_default_point_layout(),
123 }
124 }
125}
126
127#[cfg(test)]
128mod tests {
129 use std::{io::Cursor, path::PathBuf};
130
131 use las::{point::Format, Builder};
132 use pasture_core::{
133 containers::{BorrowedBufferExt, MakeBufferFromLayout, OwningBuffer, VectorBuffer},
134 layout::PointType,
135 nalgebra::Vector3,
136 };
137 use scopeguard::defer;
138
139 use crate::{
140 base::PointReader,
141 las::{
142 LASReader, LasPointFormat0, LasPointFormat1, LasPointFormat2, LasPointFormat3,
143 LasPointFormat4, LasPointFormat5,
144 },
145 };
146 use pasture_derive::PointType;
147
148 use super::*;
149
150 #[repr(C, packed)]
151 #[derive(Debug, Clone, Copy, PointType, bytemuck::AnyBitPattern, bytemuck::NoUninit)]
152 struct TestPoint {
153 #[pasture(BUILTIN_POSITION_3D)]
154 pub position: Vector3<f64>,
155 #[pasture(BUILTIN_COLOR_RGB)]
156 pub color: Vector3<u16>,
157 }
158
159 fn get_test_points_custom_format() -> Vec<TestPoint> {
160 vec![
161 TestPoint {
162 position: Vector3::new(1.0, 2.0, 3.0),
163 color: Vector3::new(128, 129, 130),
164 },
165 TestPoint {
166 position: Vector3::new(4.0, 5.0, 6.0),
167 color: Vector3::new(1024, 1025, 1026),
168 },
169 ]
170 }
171
172 fn get_test_points_las_format_0() -> Vec<LasPointFormat0> {
173 vec![
174 LasPointFormat0 {
175 classification: 1,
176 edge_of_flight_line: 0,
177 intensity: 1,
178 number_of_returns: 1,
179 point_source_id: 1,
180 position: Vector3::new(1.0, 1.0, 1.0),
181 return_number: 1,
182 scan_angle_rank: 1,
183 scan_direction_flag: 0,
184 user_data: 1,
185 },
186 LasPointFormat0 {
187 classification: 2,
188 edge_of_flight_line: 1,
189 intensity: 2,
190 number_of_returns: 2,
191 point_source_id: 2,
192 position: Vector3::new(2.0, 2.0, 2.0),
193 return_number: 2,
194 scan_angle_rank: 2,
195 scan_direction_flag: 1,
196 user_data: 2,
197 },
198 ]
199 }
200
201 fn get_test_points_las_format_1() -> Vec<LasPointFormat1> {
202 vec![
203 LasPointFormat1 {
204 classification: 1,
205 edge_of_flight_line: 0,
206 intensity: 1,
207 number_of_returns: 1,
208 point_source_id: 1,
209 position: Vector3::new(1.0, 1.0, 1.0),
210 return_number: 1,
211 scan_angle_rank: 1,
212 scan_direction_flag: 0,
213 user_data: 1,
214 gps_time: 1234.0,
215 },
216 LasPointFormat1 {
217 classification: 2,
218 edge_of_flight_line: 1,
219 intensity: 2,
220 number_of_returns: 2,
221 point_source_id: 2,
222 position: Vector3::new(2.0, 2.0, 2.0),
223 return_number: 2,
224 scan_angle_rank: 2,
225 scan_direction_flag: 1,
226 user_data: 2,
227 gps_time: 5678.0,
228 },
229 ]
230 }
231
232 fn get_test_points_las_format_2() -> Vec<LasPointFormat2> {
233 vec![
234 LasPointFormat2 {
235 classification: 1,
236 edge_of_flight_line: 0,
237 intensity: 1,
238 number_of_returns: 1,
239 point_source_id: 1,
240 position: Vector3::new(1.0, 1.0, 1.0),
241 return_number: 1,
242 scan_angle_rank: 1,
243 scan_direction_flag: 0,
244 user_data: 1,
245 color_rgb: Vector3::new(128, 129, 130),
246 },
247 LasPointFormat2 {
248 classification: 2,
249 edge_of_flight_line: 1,
250 intensity: 2,
251 number_of_returns: 2,
252 point_source_id: 2,
253 position: Vector3::new(2.0, 2.0, 2.0),
254 return_number: 2,
255 scan_angle_rank: 2,
256 scan_direction_flag: 1,
257 user_data: 2,
258 color_rgb: Vector3::new(1024, 1025, 1026),
259 },
260 ]
261 }
262
263 fn get_test_points_las_format_3() -> Vec<LasPointFormat3> {
264 vec![
265 LasPointFormat3 {
266 classification: 1,
267 edge_of_flight_line: 0,
268 intensity: 1,
269 number_of_returns: 1,
270 point_source_id: 1,
271 position: Vector3::new(1.0, 1.0, 1.0),
272 return_number: 1,
273 scan_angle_rank: 1,
274 scan_direction_flag: 0,
275 user_data: 1,
276 color_rgb: Vector3::new(128, 129, 130),
277 gps_time: 1234.0,
278 },
279 LasPointFormat3 {
280 classification: 2,
281 edge_of_flight_line: 1,
282 intensity: 2,
283 number_of_returns: 2,
284 point_source_id: 2,
285 position: Vector3::new(2.0, 2.0, 2.0),
286 return_number: 2,
287 scan_angle_rank: 2,
288 scan_direction_flag: 1,
289 user_data: 2,
290 color_rgb: Vector3::new(1024, 1025, 1026),
291 gps_time: 5678.0,
292 },
293 ]
294 }
295
296 fn get_test_points_las_format_4() -> Vec<LasPointFormat4> {
297 vec![
298 LasPointFormat4 {
299 classification: 1,
300 edge_of_flight_line: 0,
301 intensity: 1,
302 number_of_returns: 1,
303 point_source_id: 1,
304 position: Vector3::new(1.0, 1.0, 1.0),
305 return_number: 1,
306 scan_angle_rank: 1,
307 scan_direction_flag: 0,
308 user_data: 1,
309 gps_time: 1234.0,
310 byte_offset_to_waveform_data: 10,
311 return_point_waveform_location: 20.0,
312 wave_packet_descriptor_index: 30,
313 waveform_packet_size: 40,
314 waveform_parameters: Vector3::new(1.0, 2.0, 3.0),
315 },
316 LasPointFormat4 {
317 classification: 2,
318 edge_of_flight_line: 1,
319 intensity: 2,
320 number_of_returns: 2,
321 point_source_id: 2,
322 position: Vector3::new(2.0, 2.0, 2.0),
323 return_number: 2,
324 scan_angle_rank: 2,
325 scan_direction_flag: 1,
326 user_data: 2,
327 gps_time: 5678.0,
328 byte_offset_to_waveform_data: 11,
329 return_point_waveform_location: 22.0,
330 wave_packet_descriptor_index: 33,
331 waveform_packet_size: 44,
332 waveform_parameters: Vector3::new(4.0, 5.0, 6.0),
333 },
334 ]
335 }
336
337 fn get_test_points_las_format_5() -> Vec<LasPointFormat5> {
338 vec![
339 LasPointFormat5 {
340 classification: 1,
341 edge_of_flight_line: 0,
342 intensity: 1,
343 number_of_returns: 1,
344 point_source_id: 1,
345 position: Vector3::new(1.0, 1.0, 1.0),
346 return_number: 1,
347 scan_angle_rank: 1,
348 scan_direction_flag: 0,
349 user_data: 1,
350 gps_time: 1234.0,
351 color_rgb: Vector3::new(128, 129, 130),
352 byte_offset_to_waveform_data: 10,
353 return_point_waveform_location: 20.0,
354 wave_packet_descriptor_index: 30,
355 waveform_packet_size: 40,
356 waveform_parameters: Vector3::new(1.0, 2.0, 3.0),
357 },
358 LasPointFormat5 {
359 classification: 2,
360 edge_of_flight_line: 1,
361 intensity: 2,
362 number_of_returns: 2,
363 point_source_id: 2,
364 position: Vector3::new(2.0, 2.0, 2.0),
365 return_number: 2,
366 scan_angle_rank: 2,
367 scan_direction_flag: 1,
368 user_data: 2,
369 gps_time: 5678.0,
370 color_rgb: Vector3::new(1024, 1025, 1026),
371 byte_offset_to_waveform_data: 11,
372 return_point_waveform_location: 22.0,
373 wave_packet_descriptor_index: 33,
374 waveform_packet_size: 44,
375 waveform_parameters: Vector3::new(4.0, 5.0, 6.0),
376 },
377 ]
378 }
379
380 fn prepare_point_buffer<T: PointType + Clone + Copy>(test_points: &[T]) -> VectorBuffer {
381 test_points.iter().copied().collect()
382 }
383
384 #[test]
385 fn test_write_las_format_0() -> Result<()> {
386 let source_points = get_test_points_las_format_0();
387 let source_point_buffer = prepare_point_buffer(&source_points);
388
389 let mut test_file_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
392 test_file_path.push("test_write_las_format_0.las");
393
394 defer! {
395 std::fs::remove_file(&test_file_path).expect("Removing test file failed!");
396 }
397
398 let mut las_header_builder = Builder::from((1, 4));
399 las_header_builder.point_format = Format::new(0)?;
400
401 {
402 let mut writer = LASWriter::from_path_and_header(
403 &test_file_path,
404 las_header_builder.into_header().unwrap(),
405 )?;
406 writer.write(&source_point_buffer)?;
407 writer.flush()?;
408 }
409
410 {
411 let mut reader = LASReader::from_path(&test_file_path, false)?;
412 let mut read_points_buffer =
413 VectorBuffer::new_from_layout(reader.get_default_point_layout().clone());
414 read_points_buffer.resize(source_points.len());
415 reader.read_into(&mut read_points_buffer, source_points.len())?;
416 let read_points: Vec<LasPointFormat0> = read_points_buffer.view().into_iter().collect();
417
418 assert_eq!(read_points, source_points);
419 }
420
421 Ok(())
422 }
423
424 #[test]
425 fn test_write_las_format_0_different_layout() -> Result<()> {
426 let source_points = get_test_points_custom_format();
427 let source_point_buffer = prepare_point_buffer(&source_points);
428
429 let mut test_file_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
432 test_file_path.push("test_write_las_format_0_different_layout.las");
433
434 defer! {
435 std::fs::remove_file(&test_file_path).expect("Removing test file failed!");
436 }
437
438 let mut las_header_builder = Builder::from((1, 4));
439 las_header_builder.point_format = Format::new(0)?;
440
441 {
442 let mut writer = LASWriter::from_path_and_header(
443 &test_file_path,
444 las_header_builder.into_header().unwrap(),
445 )?;
446 writer.write(&source_point_buffer)?;
447 writer.flush()?;
448 }
449
450 {
451 let mut reader = LASReader::from_path(&test_file_path, false)?;
452 let mut read_points_buffer =
453 VectorBuffer::new_from_layout(reader.get_default_point_layout().clone());
454 read_points_buffer.resize(source_points.len());
455 reader.read_into(&mut read_points_buffer, source_points.len())?;
456 let read_points: Vec<LasPointFormat0> = read_points_buffer.view().into_iter().collect();
457
458 for (source, read) in source_points.iter().zip(read_points.iter()) {
459 assert_eq!(
460 { source.position },
461 { read.position },
462 "Position of read point is different than of source point!"
463 );
464 assert_eq!(
465 0, read.classification,
466 "Classification of read point was not zero!"
467 );
468 assert_eq!(
469 0, read.edge_of_flight_line,
470 "Edge of flight line of read point was not false!"
471 );
472 assert_eq!(
473 0,
474 { read.intensity },
475 "Intensity of read point was not zero!"
476 );
477 assert_eq!(
478 0, read.number_of_returns,
479 "Number of returns of read point was not zero!"
480 );
481 assert_eq!(
482 0,
483 { read.point_source_id },
484 "Point source ID of read point was not zero!"
485 );
486 assert_eq!(
487 0, read.return_number,
488 "Return number of read point was not zero!"
489 );
490 assert_eq!(
491 0, read.scan_angle_rank,
492 "Scan angle rank of read point was not zero!"
493 );
494 assert_eq!(
495 0, read.scan_direction_flag,
496 "Scan direction flag of read point was not false!"
497 );
498 assert_eq!(0, read.user_data, "User data of read point was not zero!");
499 }
500 }
501
502 Ok(())
503 }
504
505 #[test]
506 fn test_write_las_format_1() -> Result<()> {
507 let source_points = get_test_points_las_format_1();
508 let source_point_buffer = prepare_point_buffer(&source_points);
509
510 let mut test_file_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
513 test_file_path.push("test_write_las_format_1.las");
514
515 defer! {
516 std::fs::remove_file(&test_file_path).expect("Removing test file failed!");
517 }
518
519 let mut las_header_builder = Builder::from((1, 4));
520 las_header_builder.point_format = Format::new(1)?;
521
522 {
523 let mut writer = LASWriter::from_path_and_header(
524 &test_file_path,
525 las_header_builder.into_header().unwrap(),
526 )?;
527 writer.write(&source_point_buffer)?;
528 writer.flush()?;
529 }
530
531 {
532 let mut reader = LASReader::from_path(&test_file_path, false)?;
533 let mut read_points_buffer =
534 VectorBuffer::new_from_layout(reader.get_default_point_layout().clone());
535 read_points_buffer.resize(source_points.len());
536 reader.read_into(&mut read_points_buffer, source_points.len())?;
537 let read_points: Vec<LasPointFormat1> = read_points_buffer.view().into_iter().collect();
538
539 assert_eq!(read_points, source_points);
540 }
541
542 Ok(())
543 }
544
545 #[test]
546 fn test_write_las_format_1_different_layout() -> Result<()> {
547 let source_points = get_test_points_custom_format();
548 let source_point_buffer = prepare_point_buffer(&source_points);
549
550 let mut test_file_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
553 test_file_path.push("test_write_las_format_1_different_layout.las");
554
555 defer! {
556 std::fs::remove_file(&test_file_path).expect("Removing test file failed!");
557 }
558
559 let mut las_header_builder = Builder::from((1, 4));
560 las_header_builder.point_format = Format::new(1)?;
561
562 {
563 let mut writer = LASWriter::from_path_and_header(
564 &test_file_path,
565 las_header_builder.into_header().unwrap(),
566 )?;
567 writer.write(&source_point_buffer)?;
568 writer.flush()?;
569 }
570
571 {
572 let mut reader = LASReader::from_path(&test_file_path, false)?;
573 let mut read_points_buffer =
574 VectorBuffer::new_from_layout(reader.get_default_point_layout().clone());
575 read_points_buffer.resize(source_points.len());
576 reader.read_into(&mut read_points_buffer, source_points.len())?;
577 let read_points: Vec<LasPointFormat1> = read_points_buffer.view().into_iter().collect();
578
579 for (source, read) in source_points.iter().zip(read_points.iter()) {
580 assert_eq!(
581 { source.position },
582 { read.position },
583 "Position of read point is different than of source point!"
584 );
585 assert_eq!(
586 0, read.classification,
587 "Classification of read point was not zero!"
588 );
589 assert_eq!(
590 0, read.edge_of_flight_line,
591 "Edge of flight line of read point was not false!"
592 );
593 assert_eq!(
594 0,
595 { read.intensity },
596 "Intensity of read point was not zero!"
597 );
598 assert_eq!(
599 0, read.number_of_returns,
600 "Number of returns of read point was not zero!"
601 );
602 assert_eq!(
603 0,
604 { read.point_source_id },
605 "Point source ID of read point was not zero!"
606 );
607 assert_eq!(
608 0, read.return_number,
609 "Return number of read point was not zero!"
610 );
611 assert_eq!(
612 0, read.scan_angle_rank,
613 "Scan angle rank of read point was not zero!"
614 );
615 assert_eq!(
616 0, read.scan_direction_flag,
617 "Scan direction flag of read point was not false!"
618 );
619 assert_eq!(0, read.user_data, "User data of read point was not zero!");
620 assert_eq!(
621 0.0,
622 { read.gps_time },
623 "GPS time of read point was not zero!"
624 );
625 }
626 }
627
628 Ok(())
629 }
630
631 #[test]
632 fn test_write_las_format_2() -> Result<()> {
633 let source_points = get_test_points_las_format_2();
634 let source_point_buffer = prepare_point_buffer(&source_points);
635
636 let mut test_file_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
639 test_file_path.push("test_write_las_format_2.las");
640
641 defer! {
642 std::fs::remove_file(&test_file_path).expect("Removing test file failed!");
643 }
644
645 let mut las_header_builder = Builder::from((1, 4));
646 las_header_builder.point_format = Format::new(2)?;
647
648 {
649 let mut writer = LASWriter::from_path_and_header(
650 &test_file_path,
651 las_header_builder.into_header().unwrap(),
652 )?;
653 writer.write(&source_point_buffer)?;
654 writer.flush()?;
655 }
656
657 {
658 let mut reader = LASReader::from_path(&test_file_path, false)?;
659 let mut read_points_buffer =
660 VectorBuffer::new_from_layout(reader.get_default_point_layout().clone());
661 read_points_buffer.resize(source_points.len());
662 reader.read_into(&mut read_points_buffer, source_points.len())?;
663 let read_points: Vec<LasPointFormat2> = read_points_buffer.view().into_iter().collect();
664
665 assert_eq!(read_points, source_points);
666 }
667
668 Ok(())
669 }
670
671 #[test]
672 fn test_write_las_format_2_different_layout() -> Result<()> {
673 let source_points = get_test_points_custom_format();
674 let source_point_buffer = prepare_point_buffer(&source_points);
675
676 let mut test_file_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
679 test_file_path.push("test_write_las_format_2_different_layout.las");
680
681 defer! {
682 std::fs::remove_file(&test_file_path).expect("Removing test file failed!");
683 }
684
685 let mut las_header_builder = Builder::from((1, 4));
686 las_header_builder.point_format = Format::new(2)?;
687
688 {
689 let mut writer = LASWriter::from_path_and_header(
690 &test_file_path,
691 las_header_builder.into_header().unwrap(),
692 )?;
693 writer.write(&source_point_buffer)?;
694 writer.flush()?;
695 }
696
697 {
698 let mut reader = LASReader::from_path(&test_file_path, false)?;
699 let mut read_points_buffer =
700 VectorBuffer::new_from_layout(reader.get_default_point_layout().clone());
701 read_points_buffer.resize(source_points.len());
702 reader.read_into(&mut read_points_buffer, source_points.len())?;
703 let read_points: Vec<LasPointFormat2> = read_points_buffer.view().into_iter().collect();
704
705 for (source, read) in source_points.iter().zip(read_points.iter()) {
706 assert_eq!(
707 { source.position },
708 { read.position },
709 "Position of read point is different than of source point!"
710 );
711 assert_eq!(
712 { source.color },
713 { read.color_rgb },
714 "Color of read point is different than of source point!"
715 );
716 assert_eq!(
717 0, read.classification,
718 "Classification of read point was not zero!"
719 );
720 assert_eq!(
721 0, read.edge_of_flight_line,
722 "Edge of flight line of read point was not false!"
723 );
724 assert_eq!(
725 0,
726 { read.intensity },
727 "Intensity of read point was not zero!"
728 );
729 assert_eq!(
730 0, read.number_of_returns,
731 "Number of returns of read point was not zero!"
732 );
733 assert_eq!(
734 0,
735 { read.point_source_id },
736 "Point source ID of read point was not zero!"
737 );
738 assert_eq!(
739 0, read.return_number,
740 "Return number of read point was not zero!"
741 );
742 assert_eq!(
743 0, read.scan_angle_rank,
744 "Scan angle rank of read point was not zero!"
745 );
746 assert_eq!(
747 0, read.scan_direction_flag,
748 "Scan direction flag of read point was not false!"
749 );
750 assert_eq!(0, read.user_data, "User data of read point was not zero!");
751 }
752 }
753
754 Ok(())
755 }
756
757 #[test]
758 fn test_write_las_format_3() -> Result<()> {
759 let source_points = get_test_points_las_format_3();
760 let source_point_buffer = prepare_point_buffer(&source_points);
761
762 let mut test_file_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
765 test_file_path.push("test_write_las_format_3.las");
766
767 defer! {
768 std::fs::remove_file(&test_file_path).expect("Removing test file failed!");
769 }
770
771 let mut las_header_builder = Builder::from((1, 4));
772 las_header_builder.point_format = Format::new(3)?;
773
774 {
775 let mut writer = LASWriter::from_path_and_header(
776 &test_file_path,
777 las_header_builder.into_header().unwrap(),
778 )?;
779 writer.write(&source_point_buffer)?;
780 writer.flush()?;
781 }
782
783 {
784 let mut reader = LASReader::from_path(&test_file_path, false)?;
785 let mut read_points_buffer =
786 VectorBuffer::new_from_layout(reader.get_default_point_layout().clone());
787 read_points_buffer.resize(source_points.len());
788 reader.read_into(&mut read_points_buffer, source_points.len())?;
789 let read_points: Vec<LasPointFormat3> = read_points_buffer.view().into_iter().collect();
790
791 assert_eq!(read_points, source_points);
792 }
793
794 Ok(())
795 }
796
797 #[test]
798 fn test_write_las_format_3_different_layout() -> Result<()> {
799 let source_points = get_test_points_custom_format();
800 let source_point_buffer = prepare_point_buffer(&source_points);
801
802 let mut test_file_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
805 test_file_path.push("test_write_las_format_3_different_layout.las");
806
807 defer! {
808 std::fs::remove_file(&test_file_path).expect("Removing test file failed!");
809 }
810
811 let mut las_header_builder = Builder::from((1, 4));
812 las_header_builder.point_format = Format::new(3)?;
813
814 {
815 let mut writer = LASWriter::from_path_and_header(
816 &test_file_path,
817 las_header_builder.into_header().unwrap(),
818 )?;
819 writer.write(&source_point_buffer)?;
820 writer.flush()?;
821 }
822
823 {
824 let mut reader = LASReader::from_path(&test_file_path, false)?;
825 let mut read_points_buffer =
826 VectorBuffer::new_from_layout(reader.get_default_point_layout().clone());
827 read_points_buffer.resize(source_points.len());
828 reader.read_into(&mut read_points_buffer, source_points.len())?;
829 let read_points: Vec<LasPointFormat3> = read_points_buffer.view().into_iter().collect();
830
831 for (source, read) in source_points.iter().zip(read_points.iter()) {
832 assert_eq!(
833 { source.position },
834 { read.position },
835 "Position of read point is not equal to position of source point!"
836 );
837 assert_eq!(
838 { source.color },
839 { read.color_rgb },
840 "Color of read point is not equal to color of source point!"
841 );
842 assert_eq!(
843 0, read.classification,
844 "Classification of read point was not zero!"
845 );
846 assert_eq!(
847 0, read.edge_of_flight_line,
848 "Edge of flight line of read point was not false!"
849 );
850 assert_eq!(
851 0,
852 { read.intensity },
853 "Intensity of read point was not zero!"
854 );
855 assert_eq!(
856 0, read.number_of_returns,
857 "Number of returns of read point was not zero!"
858 );
859 assert_eq!(
860 0,
861 { read.point_source_id },
862 "Point source ID of read point was not zero!"
863 );
864 assert_eq!(
865 0, read.return_number,
866 "Return number of read point was not zero!"
867 );
868 assert_eq!(
869 0, read.scan_angle_rank,
870 "Scan angle rank of read point was not zero!"
871 );
872 assert_eq!(
873 0, read.scan_direction_flag,
874 "Scan direction flag of read point was not false!"
875 );
876 assert_eq!(0, read.user_data, "User data of read point was not zero!");
877 assert_eq!(
878 0.0,
879 { read.gps_time },
880 "GPS time of read point was not zero!"
881 );
882 }
883 }
884
885 Ok(())
886 }
887
888 #[test]
889 fn test_write_las_format_4() -> Result<()> {
890 let source_points = get_test_points_las_format_4();
891 let source_point_buffer = prepare_point_buffer(&source_points);
892
893 let mut test_file_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
896 test_file_path.push("test_write_las_format_4.las");
897
898 defer! {
899 std::fs::remove_file(&test_file_path).expect("Removing test file failed!");
900 }
901
902 let mut las_header_builder = Builder::from((1, 4));
903 las_header_builder.point_format = Format::new(4)?;
904
905 {
906 let mut writer = LASWriter::from_path_and_header(
907 &test_file_path,
908 las_header_builder.into_header().unwrap(),
909 )?;
910 writer.write(&source_point_buffer)?;
911 writer.flush()?;
912 }
913
914 {
915 let mut reader = LASReader::from_path(&test_file_path, false)?;
916 let mut read_points_buffer =
917 VectorBuffer::new_from_layout(reader.get_default_point_layout().clone());
918 read_points_buffer.resize(source_points.len());
919 reader.read_into(&mut read_points_buffer, source_points.len())?;
920 let read_points: Vec<LasPointFormat4> = read_points_buffer.view().into_iter().collect();
921
922 assert_eq!(read_points, source_points);
923 }
924
925 Ok(())
926 }
927
928 #[test]
929 fn test_write_las_format_4_different_layout() -> Result<()> {
930 let source_points = get_test_points_custom_format();
931 let source_point_buffer = prepare_point_buffer(&source_points);
932
933 let mut test_file_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
936 test_file_path.push("test_write_las_format_4_different_layout.las");
937
938 defer! {
939 std::fs::remove_file(&test_file_path).expect("Removing test file failed!");
940 }
941
942 let mut las_header_builder = Builder::from((1, 4));
943 las_header_builder.point_format = Format::new(4)?;
944
945 {
946 let mut writer = LASWriter::from_path_and_header(
947 &test_file_path,
948 las_header_builder.into_header().unwrap(),
949 )?;
950 writer.write(&source_point_buffer)?;
951 writer.flush()?;
952 }
953
954 {
955 let mut reader = LASReader::from_path(&test_file_path, false)?;
956 let mut read_points_buffer =
957 VectorBuffer::new_from_layout(reader.get_default_point_layout().clone());
958 read_points_buffer.resize(source_points.len());
959 reader.read_into(&mut read_points_buffer, source_points.len())?;
960 let read_points: Vec<LasPointFormat4> = read_points_buffer.view().into_iter().collect();
961
962 for (source, read) in source_points.iter().zip(read_points.iter()) {
963 assert_eq!(
964 { source.position },
965 { read.position },
966 "Position of read point is different than of source point!"
967 );
968 assert_eq!(
969 0, read.classification,
970 "Classification of read point was not zero!"
971 );
972 assert_eq!(
973 0, read.edge_of_flight_line,
974 "Edge of flight line of read point was not false!"
975 );
976 assert_eq!(
977 0,
978 { read.intensity },
979 "Intensity of read point was not zero!"
980 );
981 assert_eq!(
982 0, read.number_of_returns,
983 "Number of returns of read point was not zero!"
984 );
985 assert_eq!(
986 0,
987 { read.point_source_id },
988 "Point source ID of read point was not zero!"
989 );
990 assert_eq!(
991 0, read.return_number,
992 "Return number of read point was not zero!"
993 );
994 assert_eq!(
995 0, read.scan_angle_rank,
996 "Scan angle rank of read point was not zero!"
997 );
998 assert_eq!(
999 0, read.scan_direction_flag,
1000 "Scan direction flag of read point was not false!"
1001 );
1002 assert_eq!(0, read.user_data, "User data of read point was not zero!");
1003 assert_eq!(
1004 0,
1005 { read.byte_offset_to_waveform_data },
1006 "Byte offset to waveform data of read point was not zero!"
1007 );
1008 assert_eq!(
1009 0.0,
1010 { read.return_point_waveform_location },
1011 "Return point waveform location of read point was not zero!"
1012 );
1013 assert_eq!(
1014 0, read.wave_packet_descriptor_index,
1015 "Wave packet descriptor index of read point was not zero!"
1016 );
1017 assert_eq!(
1018 0,
1019 { read.waveform_packet_size },
1020 "Waveform packet size of read point was not zero!"
1021 );
1022 assert_eq!(
1023 { Vector3::<f32>::new(0.0, 0.0, 0.0) },
1024 { read.waveform_parameters },
1025 "Waveform parameters of read point were not zero!"
1026 );
1027 }
1028 }
1029
1030 Ok(())
1031 }
1032
1033 #[test]
1034 fn test_write_las_format_5() -> Result<()> {
1035 let source_points = get_test_points_las_format_5();
1036 let source_point_buffer = prepare_point_buffer(&source_points);
1037
1038 let mut test_file_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
1041 test_file_path.push("test_write_las_format_5.las");
1042
1043 defer! {
1044 std::fs::remove_file(&test_file_path).expect("Removing test file failed!");
1045 }
1046
1047 let mut las_header_builder = Builder::from((1, 4));
1048 las_header_builder.point_format = Format::new(5)?;
1049
1050 {
1051 let mut writer = LASWriter::from_path_and_header(
1052 &test_file_path,
1053 las_header_builder.into_header().unwrap(),
1054 )?;
1055 writer.write(&source_point_buffer)?;
1056 writer.flush()?;
1057 }
1058
1059 {
1060 let mut reader = LASReader::from_path(&test_file_path, false)?;
1061 let mut read_points_buffer =
1062 VectorBuffer::new_from_layout(reader.get_default_point_layout().clone());
1063 read_points_buffer.resize(source_points.len());
1064 reader.read_into(&mut read_points_buffer, source_points.len())?;
1065 let read_points: Vec<LasPointFormat5> = read_points_buffer.view().into_iter().collect();
1066
1067 assert_eq!(read_points, source_points);
1068 }
1069
1070 Ok(())
1071 }
1072
1073 #[test]
1074 fn test_write_las_format_5_different_layout() -> Result<()> {
1075 let source_points = get_test_points_custom_format();
1076 let source_point_buffer = prepare_point_buffer(&source_points);
1077
1078 let mut test_file_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
1081 test_file_path.push("test_write_las_format_5_different_layout.las");
1082
1083 defer! {
1084 std::fs::remove_file(&test_file_path).expect("Removing test file failed!");
1085 }
1086
1087 let mut las_header_builder = Builder::from((1, 4));
1088 las_header_builder.point_format = Format::new(5)?;
1089
1090 {
1091 let mut writer = LASWriter::from_path_and_header(
1092 &test_file_path,
1093 las_header_builder.into_header().unwrap(),
1094 )?;
1095 writer.write(&source_point_buffer)?;
1096 writer.flush()?;
1097 }
1098
1099 {
1100 let mut reader = LASReader::from_path(&test_file_path, false)?;
1101 let mut read_points_buffer =
1102 VectorBuffer::new_from_layout(reader.get_default_point_layout().clone());
1103 read_points_buffer.resize(source_points.len());
1104 reader.read_into(&mut read_points_buffer, source_points.len())?;
1105 let read_points: Vec<LasPointFormat5> = read_points_buffer.view().into_iter().collect();
1106
1107 for (source, read) in source_points.iter().zip(read_points.iter()) {
1108 assert_eq!(
1109 { source.position },
1110 { read.position },
1111 "Position of read point is different than of source point!"
1112 );
1113 assert_eq!(
1114 { source.color },
1115 { read.color_rgb },
1116 "Colors of read point are different from colors in source point!"
1117 );
1118 assert_eq!(
1119 0, read.classification,
1120 "Classification of read point was not zero!"
1121 );
1122 assert_eq!(
1123 0, read.edge_of_flight_line,
1124 "Edge of flight line of read point was not false!"
1125 );
1126 assert_eq!(
1127 0,
1128 { read.intensity },
1129 "Intensity of read point was not zero!"
1130 );
1131 assert_eq!(
1132 0, read.number_of_returns,
1133 "Number of returns of read point was not zero!"
1134 );
1135 assert_eq!(
1136 0,
1137 { read.point_source_id },
1138 "Point source ID of read point was not zero!"
1139 );
1140 assert_eq!(
1141 0, read.return_number,
1142 "Return number of read point was not zero!"
1143 );
1144 assert_eq!(
1145 0, read.scan_angle_rank,
1146 "Scan angle rank of read point was not zero!"
1147 );
1148 assert_eq!(
1149 0, read.scan_direction_flag,
1150 "Scan direction flag of read point was not false!"
1151 );
1152 assert_eq!(0, read.user_data, "User data of read point was not zero!");
1153 assert_eq!(
1154 0.0,
1155 { read.gps_time },
1156 "GPS time of read point was not zero!"
1157 );
1158 assert_eq!(
1159 0,
1160 { read.byte_offset_to_waveform_data },
1161 "Byte offset to waveform data of read point was not zero!"
1162 );
1163 assert_eq!(
1164 0.0,
1165 { read.return_point_waveform_location },
1166 "Return point waveform location of read point was not zero!"
1167 );
1168 assert_eq!(
1169 0, read.wave_packet_descriptor_index,
1170 "Wave packet descriptor index of read point was not zero!"
1171 );
1172 assert_eq!(
1173 0,
1174 { read.waveform_packet_size },
1175 "Waveform packet size of read point was not zero!"
1176 );
1177 assert_eq!(
1178 { Vector3::<f32>::new(0.0, 0.0, 0.0) },
1179 { read.waveform_parameters },
1180 "Waveform parameters of read point were not zero!"
1181 );
1182 }
1183 }
1184
1185 Ok(())
1186 }
1187
1188 #[test]
1189 fn test_las_writer_into_inner() -> Result<()> {
1190 let source_points = get_test_points_las_format_0();
1191 let source_point_buffer = prepare_point_buffer(&source_points);
1192
1193 let mut cursor = Cursor::new(Vec::<u8>::new());
1194
1195 let mut las_header_builder = Builder::from((1, 4));
1196 las_header_builder.point_format = Format::new(0)?;
1197
1198 {
1199 let mut writer = LASWriter::from_writer_and_header(
1200 cursor,
1201 las_header_builder.into_header().unwrap(),
1202 false,
1203 )?;
1204 writer.write(&source_point_buffer)?;
1205
1206 cursor = writer.into_inner()?;
1207 }
1208
1209 let vec = cursor.into_inner();
1212 assert!(!vec.is_empty());
1213
1214 Ok(())
1215 }
1216
1217 #[test]
1218 fn test_laz_writer_into_inner() -> Result<()> {
1219 let source_points = get_test_points_las_format_0();
1220 let source_point_buffer = prepare_point_buffer(&source_points);
1221
1222 let mut cursor = Cursor::new(Vec::<u8>::new());
1223
1224 let mut las_header_builder = Builder::from((1, 4));
1225 las_header_builder.point_format = Format::new(0)?;
1226
1227 {
1228 let mut writer = LASWriter::from_writer_and_header(
1229 cursor,
1230 las_header_builder.into_header().unwrap(),
1231 true,
1232 )?;
1233 writer.write(&source_point_buffer)?;
1234
1235 cursor = writer.into_inner()?;
1236 }
1237
1238 let vec = cursor.into_inner();
1241 assert!(!vec.is_empty());
1242
1243 Ok(())
1244 }
1245}