gistools/readers/las/
types.rs

1use crate::parsers::{Buffer, RGBA, Reader};
2use alloc::{string::String, vec, vec::Vec};
3use s2json::{BBox3D, MValue, ValuePrimitive, VectorPoint};
4use serde::{Deserialize, Serialize};
5
6/// Extended VARIABLE LENGTH RECORDS:
7/// The Extended Variable Length Records are used to add custom data to the LAZ Header Block.
8/// This record type allows data to be much larger in size.
9#[derive(Debug, Default, Clone, PartialEq, Eq)]
10pub struct LASExtendedVariableLengthRecord {
11    /// Reserved `unsigned short 2 bytes`
12    pub reserved: u16,
13    /// User ID `char[16] 16 bytes`
14    pub user_id: String,
15    /// Record ID `unsigned short 2 bytes`
16    pub record_id: u16,
17    /// Record Length After Header `unsigned short 2 bytes` (8 bytes for EVLR)
18    pub record_length: u64,
19    /// Description `char[32] 32 bytes`
20    pub description: String,
21    /// The data of the record
22    pub data: Option<Vec<u8>>,
23}
24impl LASExtendedVariableLengthRecord {
25    /// Create an LASVariableLengthRecord from reader
26    pub fn from_reader<T: Reader>(reader: &T, offset: u64) -> Self {
27        let record_length = reader.uint16_le(Some(offset + 20)) as u64;
28        LASExtendedVariableLengthRecord {
29            reserved: reader.uint16_le(Some(offset)),
30            user_id: reader.parse_string(Some(offset + 2), Some(16)),
31            record_id: reader.uint16_le(Some(offset + 18)),
32            record_length,
33            description: reader.parse_string(Some(offset + 22), Some(32)),
34            data: if record_length > 0 {
35                Some(reader.slice(Some(offset + 54), Some(offset + 54 + record_length)))
36            } else {
37                None
38            },
39        }
40    }
41    /// Create an LASExtendedVariableLengthRecord from reader
42    pub fn from_reader_extended<T: Reader>(reader: &T, offset: u64) -> Self {
43        let record_length = reader.uint64_le(Some(offset + 20));
44        LASExtendedVariableLengthRecord {
45            reserved: reader.uint16_le(Some(offset)),
46            user_id: reader.parse_string(Some(offset + 2), Some(16)),
47            record_id: reader.uint16_le(Some(offset + 18)),
48            record_length,
49            description: reader.parse_string(Some(offset + 28), Some(32)),
50            data: if record_length > 0 {
51                Some(reader.slice(Some(offset + 60), Some(offset + 60 + record_length)))
52            } else {
53                None
54            },
55        }
56    }
57}
58
59/// LAS Header Block
60/// Any field in the Public Header Block that is not required and is not used must be zero filled.
61#[derive(Debug, Default, Clone, PartialEq)]
62pub struct LASHeader {
63    /// File Signature ("LASF") `char[4] 4 bytes`
64    /// The file signature must contain the four characters "LASF", and it is required by the LAS
65    /// specification. These four characters can be checked by user software as a quick look initial
66    /// determination of file type.
67    pub signature: String,
68    /// File Source ID `unsigned short 2 bytes`
69    ///
70    /// File Source ID (Flight Line Number if this file was derived from an original flight line):
71    /// This field should be set to a value between 1 and 65,535, inclusive. A value of zero (0)
72    /// is interpreted to mean that an ID has not been assigned. In this case, processing software is
73    /// free to assign any LAS 1.2 3 valid number. Note that this scheme allows a LIDAR project to
74    /// contain up to 65,535 unique sources. A source can
75    pub source_id: u16,
76    /// Global Encoding `unsigned short 2 bytes`.
77    ///
78    /// The meaning of GPS Time in the Point Records
79    /// - 0 (not set) -> GPS time in the point record fields is GPS Week Time (the same as previous
80    ///   versions of LAS).
81    /// - 1 (set) -> GPS Time is standard GPS Time (satellite GPS Time) minus 1 x 109. The offset
82    ///   moves the time back to near zero to improve floating point resolution.
83    pub encoding: u16,
84    /// Project ID - GUID data 1 `unsigned long 4 bytes`. 0 means no project ID
85    pub project_id1: u32,
86    /// Project ID - GUID data 2 unsigned short 2 byte. 0 means no project ID
87    pub project_id2: u16,
88    /// Project ID - GUID data 3 `unsigned short 2 byte`. 0 means no project ID
89    pub project_id3: u16,
90    /// Project ID - GUID data 4 `unsigned char[8] 8 bytes`. 0 means no project ID
91    pub project_id4: String,
92    /// Version Major `unsigned char 1 byte`
93    pub major_version: u8,
94    /// Version Minor `unsigned char 1 byte`
95    pub minor_version: u8,
96    /// System Identifier `char[32] 32 bytes`
97    pub system_identifier: String,
98    /// Generating Software `char[32] 32 bytes`
99    pub generating_software: String,
100    /// File Creation Day Year `unsigned short 2 bytes`. 0 means no creation date
101    ///
102    /// Day, expressed as an unsigned short, on which this file was created. Day is computed as the
103    /// Greenwich Mean Time (GMT) day. January 1 is considered day 1.
104    pub file_creation_day: u16,
105    /// File Creation Day Year `unsigned short 2 bytes`. 0 means no creation date
106    ///
107    /// The year, expressed as a four digit number, in which the file was created.
108    pub file_creation_year: u16,
109    /// Header Size `unsigned short 2 bytes`
110    ///
111    /// The size, in bytes, of the Public Header Block itself. In the event that the header is extended
112    /// by a software application through the addition of data at the end of the header, the Header
113    /// Size field must be updated with the new header size. Extension of the Public Header Block is
114    /// discouraged; the Variable Length Records should be used whenever possible to add custom header
115    /// data. In the event a generating software package adds data to the Public Header Block, this
116    /// data must be placed at the end of the structure and the Header Size must be updated to reflect
117    /// the new size.
118    pub header_size: u16,
119    /// Offset to Point Data `unsigned int 4 bytes`
120    ///
121    /// The actual number of bytes from the beginning of the file to the first field of the first point
122    /// record data field. This data offset must be updated if any software adds data from the Public
123    /// Header Block or adds/removes data to/from the Variable Length Records.
124    pub offset_to_points: u32,
125    /// Number of Variable Length Records `unsigned int 4 bytes`
126    /// This field contains the current number of Variable Length Records. This number must be updated
127    /// if the number of Variable Length Records changes at any time.
128    pub num_variable_length_records: u32,
129    /// Point Data Format ID `unsigned short 1 byte`
130    ///
131    /// The point data format ID corresponds to the point data record format type.
132    /// LAS 1.4 defines types 0-10.
133    pub point_data_format_id: u8,
134    /// Point Data Record Length `unsigned short 2 bytes`
135    pub point_data_record_length: u16,
136    /// Number of point records `unsigned long 4 bytes`
137    pub num_points: u32,
138    /// Number of points by return `unsigned long[5] 20 bytes`
139    pub num_points_by_return: [u32; 5],
140    /// X scale factor `double 8 bytes`
141    pub x_scale_factor: f64,
142    /// Y scale factor `double 8 bytes`
143    pub y_scale_factor: f64,
144    /// Z scale factor `double 8 bytes`
145    pub z_scale_factor: f64,
146    /// X offset `double 8 bytes`
147    pub x_offset: f64,
148    /// Y offset `double 8 bytes`
149    pub y_offset: f64,
150    /// Z offset `double 8 bytes`
151    pub z_offset: f64,
152    /// Max X `double 8 bytes`
153    pub max_x: f64,
154    /// Min X `double 8 bytes`
155    pub min_x: f64,
156    /// Max Y `double 8 bytes`
157    pub max_y: f64,
158    /// Min Y `double 8 bytes`
159    pub min_y: f64,
160    /// Max Z `double 8 bytes`
161    pub max_z: f64,
162    /// Min Z `double 8 bytes`
163    pub min_z: f64,
164    /// Start of Waveform Data Packet Record - `Unsigned long long 8 bytes`
165    pub waveform_data_packet_offset: u64,
166    /// Start of first Extended Variable Length Record - `unsigned long long 8 bytes`
167    pub extended_variable_length_record_offset: u64,
168    /// Number of Extended Variable Length Records - `unsigned long 4 bytes`
169    pub extended_variable_length_size: u32,
170    /// Number of points by return `unsigned long long [15] 120 bytes *`
171    pub num_points_by_return_ll: [u64; 15],
172}
173impl LASHeader {
174    /// Get the bounding box
175    pub fn bbox(&self) -> BBox3D {
176        BBox3D::new(self.min_x, self.min_y, self.min_z, self.max_x, self.max_y, self.max_z)
177    }
178    /// Create from a reader
179    pub fn from_reader<T: Reader>(reader: &T) -> Self {
180        let mut header = LASHeader {
181            // Main components
182            signature: reader.parse_string(Some(0), Some(4)),
183            source_id: reader.uint16_le(Some(4)),
184            encoding: reader.uint16_le(Some(6)),
185            project_id1: reader.uint32_le(Some(8)),
186            project_id2: reader.uint16_le(Some(12)),
187            project_id3: reader.uint16_le(Some(14)),
188            project_id4: reader.parse_string(Some(16), Some(8)),
189            major_version: reader.uint8(Some(24)),
190            minor_version: reader.uint8(Some(25)),
191            system_identifier: reader.parse_string(Some(26), Some(32)),
192            generating_software: reader.parse_string(Some(58), Some(32)),
193            file_creation_day: reader.uint16_le(Some(90)),
194            file_creation_year: reader.uint16_le(Some(92)),
195            header_size: reader.uint16_le(Some(94)),
196            offset_to_points: reader.uint32_le(Some(96)),
197            num_variable_length_records: reader.uint32_le(Some(100)),
198            point_data_format_id: reader.uint8(Some(104)),
199            point_data_record_length: reader.uint16_le(Some(105)),
200            num_points: reader.uint32_le(Some(107)),
201            num_points_by_return: [
202                reader.uint32_le(Some(111)),
203                reader.uint32_le(Some(115)),
204                reader.uint32_le(Some(119)),
205                reader.uint32_le(Some(123)),
206                reader.uint32_le(Some(127)),
207            ],
208            x_scale_factor: reader.f64_le(Some(131)),
209            y_scale_factor: reader.f64_le(Some(139)),
210            z_scale_factor: reader.f64_le(Some(147)),
211            x_offset: reader.f64_le(Some(155)),
212            y_offset: reader.f64_le(Some(163)),
213            z_offset: reader.f64_le(Some(171)),
214            max_x: reader.f64_le(Some(179)),
215            min_x: reader.f64_le(Some(187)),
216            max_y: reader.f64_le(Some(195)),
217            min_y: reader.f64_le(Some(203)),
218            max_z: reader.f64_le(Some(211)),
219            min_z: reader.f64_le(Some(219)),
220            ..Default::default()
221        };
222        // 1.4 header components
223        if header.header_size > 227 {
224            header.waveform_data_packet_offset = reader.uint64_le(Some(227));
225        }
226        if header.header_size > 235 {
227            header.extended_variable_length_record_offset = reader.uint64_le(Some(235));
228        }
229        if header.header_size > 243 {
230            header.extended_variable_length_size = reader.uint32_le(Some(243));
231        }
232        // re-adjust numPoints and numPointsByReturn if header includes modern numPoints variable
233        if header.header_size > 247 {
234            header.num_points = reader.uint32_le(Some(247));
235        }
236        // set new numPointsByReturn if header includes
237        if header.header_size > 251 {
238            let mut cur_offset = 251;
239            for i in 0..15 {
240                header.num_points_by_return_ll[i] = reader.uint64_le(Some(cur_offset));
241                cur_offset += 8;
242            }
243        }
244
245        header
246    }
247}
248
249/// Enum representing the LAZ header item type.
250/// NOTE: The number in the name, for example in “Point10”, refers to the LAS and LAZ
251/// version where that type got added.
252#[repr(u8)]
253#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
254pub enum LAZHeaderItemType {
255    /// `BYTE` (extra bytes that are appended to a LAS Point Data Record Format 0 to 5)
256    #[default]
257    Byte = 0,
258    /// `SHORT` (reserved, unsupported)
259    Short = 1,
260    /// `INT` (reserved, unsupported)
261    Int = 2,
262    /// `LONG` (reserved, unsupported)
263    Long = 3,
264    /// `FLOAT` (reserved, unsupported)
265    Float = 4,
266    /// `DOUBLE` (reserved, unsupported)
267    Double = 5,
268    /// `POINT10` (LAS Point Data Record Format 0, containing the core fields that are shared
269    /// between LAS Point Data Record Formats 0 to 5)
270    Point10 = 6,
271    /// `GPSTIME11` (the GPS Time field that is added for LAS Point Data Record Formats 1, 3,
272    /// 4 and 5)
273    GpsTime11 = 7,
274    /// `RGB12` (the R, G and B fields that are added for LAS Point Data Record Formats 2, 3 and 5)
275    Rgb12 = 8,
276    /// `WAVEPACKET13` (the 7 fields for the Waveform packet that are added for LAS Point Data
277    /// Record Formats 4 and 5)
278    WavePacket13 = 9,
279    /// `POINT14` (LAS Point Data Record Format 6, containing the core fields that are shared
280    /// between LAS Point Data Record Formats 6 to 10)
281    Point14 = 10,
282    /// `RGB14` (the R, G and B fields that are added for LAS Point Data Record Format 7)
283    Rgb14 = 11,
284    /// `RGBNIR14` (the R, G, B and NIR (near infrared) fields that are added for LAS Point
285    /// Data Record Formats 8 and 10)
286    RgbNir14 = 12,
287    /// `WAVEPACKET14` (the 7 fields for the Waveform packet that are added for LAS Point Data
288    /// Record Formast 9 and 10)
289    WavePacket14 = 13,
290    /// `BYTE14` (extra bytes that are appended to a LAS Point Data Record Format 6 to 10)
291    Byte14 = 14,
292}
293impl From<u16> for LAZHeaderItemType {
294    fn from(value: u16) -> Self {
295        match value {
296            1 => Self::Short,
297            2 => Self::Int,
298            3 => Self::Long,
299            4 => Self::Float,
300            5 => Self::Double,
301            6 => Self::Point10,
302            7 => Self::GpsTime11,
303            8 => Self::Rgb12,
304            9 => Self::WavePacket13,
305            10 => Self::Point14,
306            11 => Self::Rgb14,
307            12 => Self::RgbNir14,
308            13 => Self::WavePacket14,
309            14 => Self::Byte14,
310            _ => Self::Byte,
311        }
312    }
313}
314
315/// Enum representing the LAZ Item type
316#[repr(u8)]
317#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
318pub enum LAZCompressor {
319    /// No Compression (Uncompressed Standard LAS file)
320    #[default]
321    None = 0,
322    /// Pointwise compression (only for point types 0 to 5)
323    Pointwise = 1,
324    /// Pointwise and chunked compression (only for point types 0 to 5)
325    PointwiseAndChunked = 2,
326    /// Layered and chunked compression (only for point types 6 to 10)
327    LayeredAndChunked = 3,
328}
329impl From<u16> for LAZCompressor {
330    fn from(value: u16) -> Self {
331        match value {
332            1 => Self::Pointwise,
333            2 => Self::PointwiseAndChunked,
334            3 => Self::LayeredAndChunked,
335            _ => Self::None,
336        }
337    }
338}
339
340/// A LAZ Header Item
341#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
342pub struct LAZHeaderItem {
343    /// U16 type: 2 bytes * num_items
344    pub r#type: LAZHeaderItemType,
345    /// U16 size: 2 bytes * num_items
346    pub size: u16,
347    /// U16 version: 2 bytes * num_items
348    pub version: u16,
349}
350
351/// A LAZ Header
352#[derive(Debug, Default, Clone, PartialEq, Eq)]
353pub struct LAZHeader {
354    /// Compressor unsigned short 2 bytes *
355    pub compressor: LAZCompressor,
356    /// Coder unsigned short 2 bytes *
357    pub coder: u16,
358    /// Version Major unsigned char 1 byte *
359    pub version_major: u8,
360    /// Version Minor unsigned char 1 byte *
361    pub version_minor: u8,
362    /// Version Revision unsigned short 2 bytes *
363    pub version_revision: u16,
364    /// Options unsigned long 4 bytes *
365    pub options: u32,
366    /// Chunk Size unsigned long 4 bytes *
367    pub chunk_size: u32,
368    /// Number of special EVLRs signed long long 8 bytes *
369    pub num_special_evlrs: i64,
370    /// Offset of special EVLRs signed long long 8 bytes *
371    pub offset_special_evlrs: i64,
372    /// Number of Items unsigned short 2 bytes *
373    pub num_items: u16,
374    /// Item records Array of “Item record” 6 bytes * Number of Items *
375    pub items: Vec<LAZHeaderItem>,
376}
377impl LAZHeader {
378    /// Build LAZ Header
379    pub fn from_bytes(data: Vec<u8>) -> Self {
380        let mut raw_header = Buffer::from(data);
381        let mut header = LAZHeader {
382            compressor: LAZCompressor::from(raw_header.get_u16_at(0)),
383            coder: raw_header.get_u16_at(2),
384            version_major: raw_header.get_u8_at(4),
385            version_minor: raw_header.get_u8_at(5),
386            version_revision: raw_header.get_u16_at(6),
387            options: raw_header.get_u32_at(8),
388            chunk_size: raw_header.get_u32_at(12),
389            num_special_evlrs: raw_header.get_i64_at(16),
390            offset_special_evlrs: raw_header.get_i64_at(24),
391            num_items: raw_header.get_u16_at(32),
392            items: vec![],
393        };
394        // Parse items
395        for i in 0..header.num_items as usize {
396            header.items.push(LAZHeaderItem {
397                r#type: (raw_header.get_u16_at(34 + i * 6)).into(),
398                size: raw_header.get_u16_at(36 + i * 6),
399                version: raw_header.get_u16_at(38 + i * 6),
400            });
401        }
402
403        header
404    }
405}
406
407/// A Waveform Packet of type 13 or 14
408#[derive(Debug, Default, Clone, PartialEq, MValue, ValuePrimitive, Serialize, Deserialize)]
409pub struct WavePacket {
410    /// Wave Packet Descriptor Index
411    pub descriptor_index: u8,
412    /// Byte offset to Waveform Packet Data
413    pub offset: u64,
414    /// Waveform packet size in bytes
415    pub length: u32,
416    /// Return Point location
417    pub return_point: f32,
418    /// X
419    pub x_t: f32,
420    /// Y
421    pub y_t: f32,
422    /// Z
423    pub z_t: f32,
424}
425impl WavePacket {
426    /// Build LAS Point from Format4 or Format5
427    pub fn from_reader<T: Reader>(reader: &T, offset: u64) -> Self {
428        WavePacket {
429            descriptor_index: reader.uint8(Some(offset)),
430            offset: reader.uint64_le(Some(offset + 1)),
431            length: reader.uint32_le(Some(offset + 9)),
432            return_point: reader.f32_le(Some(offset + 13)),
433            x_t: reader.f32_le(Some(offset + 17)),
434            y_t: reader.f32_le(Some(offset + 21)),
435            z_t: reader.f32_le(Some(offset + 25)),
436        }
437    }
438
439    /// Convert to bytes
440    pub fn to_bytes(&self) -> Vec<u8> {
441        let mut buf = Buffer::default();
442        buf.set_u8_at(0, self.descriptor_index);
443        buf.set_u64_at(1, self.offset);
444        buf.set_u32_at(9, self.length);
445        buf.set_f32_at(13, self.return_point);
446        buf.set_f32_at(17, self.x_t);
447        buf.set_f32_at(21, self.y_t);
448        buf.set_f32_at(25, self.z_t);
449        buf.take()
450    }
451}
452
453/// A Classification Flag as an enum
454#[derive(Debug, Default, Clone, PartialEq)]
455pub enum ClassFlag {
456    /// Synthetic
457    Synthetic,
458    /// Key-point
459    KeyPoint,
460    /// Withheld
461    Withheld,
462    /// Overlap
463    Overlap,
464    /// Unknown
465    #[default]
466    Unknown,
467}
468impl From<u8> for ClassFlag {
469    fn from(class: u8) -> Self {
470        match class {
471            0 => ClassFlag::Synthetic,
472            1 => ClassFlag::KeyPoint,
473            2 => ClassFlag::Withheld,
474            3 => ClassFlag::Overlap,
475            _ => ClassFlag::Unknown,
476        }
477    }
478}
479
480/// A LAS Point Data Record. Compatible with Point Data Record Format 0 to 10
481#[derive(Debug, Default, Clone, PartialEq, MValue, Serialize, Deserialize)]
482pub struct LASPoint {
483    // POINT10 components inherited
484    /// X coordinate
485    pub x: i32,
486    /// Y coordinate
487    pub y: i32,
488    /// Z coordinate
489    pub z: i32,
490    /// Intensity
491    pub intensity: u16,
492    /// flags
493    pub flags: u8,
494    //? flags start
495    /// Return Number
496    pub return_number: u8,
497    /// Number of Returns
498    pub number_of_returns: u8,
499    /// Scan Direction Flag
500    pub scan_direction_flag: bool,
501    /// Edge of Flight Line
502    pub edge_of_flight_line: bool,
503    //? flags end
504    /// Classification
505    pub classification: u8,
506    //? flags2 start
507    /// True if it's synthetic
508    pub is_synthetic: bool,
509    /// True if it's key point
510    pub is_key_point: bool,
511    /// True if it's withheld
512    pub is_withheld: bool,
513    //? flags2 end
514    /// Overlap
515    pub scan_angle_rank: i8,
516    /// User Data
517    pub user_data: u8,
518    /// Point Source ID
519    pub point_source_id: u16,
520
521    // POINT14 EXTENSION
522    /// Legacy Point Type
523    pub legacy_point_type: u8,
524    /// Legacy Classification
525    pub legacy_classification: u8,
526    /// Legacy Return Number
527    pub legacy_return_number: u8,
528    /// Legacy Number of Returns
529    pub legacy_number_of_returns: u8,
530    /// Legacy Scan Direction Flag
531    pub legacy_scan_angle_rank: i8,
532    /// Scanner Channel is used to indicate the channel (scanner head) of a multichannel system
533    pub scanner_channel: u8,
534    /// Classification flags are used to indicate special characteristics associated with the point.
535    pub class_flag: u8,
536    /// The Scan Angle is a signed short that represents the rotational position of the
537    /// emitted laser pulse with respect to the vertical of the coordinate system of the data. Down in the
538    /// data coordinate system is the 0.0 position. Each increment represents 0.006 degrees.
539    pub scan_angle: i16,
540
541    // GPSTIME11
542    /// GPS Time Change
543    pub gps_time_change: Option<u8>,
544    /// GPS Time
545    pub gps_time: Option<f64>,
546
547    // RGB12 & RGB14
548    /// RGB Color
549    pub rgba: Option<RGBA>,
550
551    // WAVEPACKET13 & WAVEPACKET14
552    /// Wave Packet Data
553    pub wave_packet: Option<WavePacket>,
554
555    // NIR
556    /// NIR: The NIR (near infrared) channel value associated with this point.
557    pub nir: Option<u16>,
558}
559impl LASPoint {
560    /// Build LAS Point from Format0
561    pub fn format0<T: Reader>(reader: &T, offset: u64) -> Self {
562        let mut res = LASPoint::default();
563        res.inject_point10(reader, offset);
564        res
565    }
566    /// Build LAS Point from Format1
567    pub fn format1<T: Reader>(reader: &T, offset: u64) -> Self {
568        let mut res = LASPoint::default();
569        res.inject_point10(reader, offset);
570        res.inject_gps_time(reader, offset + 20);
571        res
572    }
573    /// Build LAS Point from Format2
574    pub fn format2<T: Reader>(reader: &T, offset: u64) -> Self {
575        let mut res = LASPoint::default();
576        res.inject_point10(reader, offset);
577        res.inject_rgb(reader, offset + 20);
578        res
579    }
580    /// Build LAS Point from Format3
581    pub fn format3<T: Reader>(reader: &T, offset: u64) -> Self {
582        let mut res = LASPoint::default();
583        res.inject_point10(reader, offset);
584        res.inject_gps_time(reader, offset + 20);
585        res.inject_rgb(reader, offset + 28);
586        res
587    }
588    /// Build LAS Point from Format4
589    pub fn format4<T: Reader>(reader: &T, offset: u64) -> Self {
590        let mut res = LASPoint::default();
591        res.inject_point10(reader, offset);
592        res.inject_gps_time(reader, offset + 20);
593        res.inject_wave_packet(reader, offset + 28);
594        res
595    }
596    /// Build LAS Point from Format5
597    pub fn format5<T: Reader>(reader: &T, offset: u64) -> Self {
598        let mut res = LASPoint::default();
599        res.inject_point10(reader, offset);
600        res.inject_gps_time(reader, offset + 20);
601        res.inject_rgb(reader, offset + 28);
602        res.inject_wave_packet(reader, offset + 34);
603        res
604    }
605    /// Build LAS Point from Format6
606    pub fn format6<T: Reader>(reader: &T, offset: u64) -> Self {
607        let mut res = LASPoint::default();
608        res.inject_point14(reader, offset, false);
609        res
610    }
611    /// Build LAS Point from Format7
612    pub fn format7<T: Reader>(reader: &T, offset: u64) -> Self {
613        let mut res = LASPoint::default();
614        res.inject_point14(reader, offset, false);
615        res.inject_rgb(reader, offset + 30);
616        res
617    }
618    /// Build LAS Point from Format8
619    pub fn format8<T: Reader>(reader: &T, offset: u64) -> Self {
620        let mut res = LASPoint::default();
621        res.inject_point14(reader, offset, false);
622        res.inject_rgb_nir(reader, offset + 30);
623        res
624    }
625    /// Build LAS Point from Format9
626    pub fn format9<T: Reader>(reader: &T, offset: u64) -> Self {
627        let mut res = LASPoint::default();
628        res.inject_point14(reader, offset, false);
629        res.inject_wave_packet(reader, offset + 30);
630        res
631    }
632    /// Build LAS Point from Format10
633    pub fn format10<T: Reader>(reader: &T, offset: u64) -> Self {
634        let mut res = LASPoint::default();
635        res.inject_point14(reader, offset, false);
636        res.inject_rgb_nir(reader, offset + 30);
637        res.inject_wave_packet(reader, offset + 38);
638        res
639    }
640    /// Inject POINT10
641    pub fn inject_point10<T: Reader>(&mut self, reader: &T, offset: u64) {
642        let flags1 = reader.uint8(Some(offset + 14));
643        let flags2 = reader.uint8(Some(offset + 15));
644        self.x = reader.int32_le(Some(offset));
645        self.y = reader.int32_le(Some(offset + 4));
646        self.z = reader.int32_le(Some(offset + 8));
647        self.intensity = reader.uint16_le(Some(offset + 12));
648        self.set_flags(flags1, false);
649        self.set_flags2(flags2);
650        self.scan_angle_rank = reader.int8(Some(offset + 16));
651        self.user_data = reader.uint8(Some(offset + 17));
652        self.point_source_id = reader.uint16_le(Some(offset + 18));
653    }
654    /// Inject temporary POINT14
655    pub fn inject_point14_temp<T: Reader>(&mut self, reader: &T, offset: u64) {
656        let flags1 = reader.uint8(Some(offset + 14));
657        let flags2 = reader.uint8(Some(offset + 15));
658        let flags3 = reader.uint8(Some(offset + 16));
659        self.x = reader.int32_le(Some(offset));
660        self.y = reader.int32_le(Some(offset + 4));
661        self.z = reader.int32_le(Some(offset + 8));
662        self.intensity = reader.uint16_le(Some(offset + 12));
663        self.flags = flags1;
664        self.return_number = flags1 & 0b0000_1111;
665        self.number_of_returns = (flags1 & 0b1111_0000) >> 4;
666        self.class_flag = flags2 & 0b0000_1111;
667        self.scanner_channel = (flags2 & 0b0011_0000) >> 4;
668        self.scan_direction_flag = (flags2 & 0b0100_0000) != 0;
669        self.edge_of_flight_line = (flags2 & 0b1000_0000) != 0;
670        self.classification = flags3;
671        self.user_data = reader.uint8(Some(offset + 17));
672        self.scan_angle = reader.int16_le(Some(offset + 18));
673        self.point_source_id = reader.uint16_le(Some(offset + 20));
674    }
675    /// Inject POINT14
676    pub fn inject_point14<T: Reader>(&mut self, reader: &T, offset: u64, compressed: bool) {
677        let flags1 = reader.uint8(Some(offset + 14));
678        let flags2 = reader.uint8(Some(offset + 15));
679        let flags3 = reader.uint8(Some(offset + 22));
680        self.x = reader.int32_le(Some(offset));
681        self.y = reader.int32_le(Some(offset + 4));
682        self.z = reader.int32_le(Some(offset + 8));
683        self.intensity = reader.uint16_le(Some(offset + 12));
684        self.set_flags(flags1, true);
685        self.set_classification(flags2);
686        self.set_flags3(flags3);
687        self.legacy_scan_angle_rank = reader.int8(Some(offset + 16));
688        self.user_data = reader.uint8(Some(offset + 17));
689        self.scan_angle = reader.int16_le(Some(offset + 18));
690        self.point_source_id = reader.uint16_le(Some(offset + 20));
691        // Compressed LASzip 1.4 points only
692        if compressed {
693            self.classification = reader.uint8(Some(offset + 23));
694            let flags4 = reader.uint8(Some(offset + 24));
695            self.return_number = flags4 & 0b0000_1111;
696            self.number_of_returns = (flags4 & 0b1111_0000) >> 4;
697            self.gps_time_change = Some(reader.uint8(Some(offset + 28)));
698            self.gps_time = Some(reader.f64_le(Some(offset + 29)));
699            self.inject_rgb(reader, offset + 37);
700        }
701    }
702    /// Set Flags
703    pub fn set_flags(&mut self, flags: u8, point14: bool) {
704        self.flags = flags;
705        if point14 {
706            self.legacy_return_number = flags & 0b0000_0111; // 3 bits (bits 0 – 2)
707            self.legacy_number_of_returns = (flags & 0b0011_1000) >> 2; // 3 bits (bits 3 – 5)
708        } else {
709            self.return_number = flags & 0b0000_0111; // 4 bits (bits 0 – 3)
710            self.number_of_returns = (flags & 0b0011_1000) >> 3; // 4 bits (bits 4 – 7)
711        }
712        self.scan_direction_flag = (flags & 0b0100_0000) != 0; // 1 bit (bit 6)
713        self.edge_of_flight_line = (flags & 0b1000_0000) != 0; // 1 bit (bit 7)
714    }
715    /// Set Flags 2
716    pub fn set_flags2(&mut self, class: u8) {
717        self.classification = class;
718        self.class_flag = class & 0b1111; // 4 bis (bit 0 - 3)
719        self.scanner_channel = (class & 0b0011_0000) >> 4; // 2 bits (bit 4 - 5)
720        self.is_synthetic = (class & 0b0010_0000) != 0;
721        self.is_key_point = (class & 0b0100_0000) != 0;
722        self.is_withheld = (class & 0b1000_0000) != 0;
723    }
724    /// Set Flags 2 14
725    pub fn set_classification(&mut self, class: u8) {
726        self.legacy_classification = class & 0b1_1111;
727        self.is_synthetic = (class & 0b0010_0000) != 0;
728        self.is_key_point = (class & 0b0100_0000) != 0;
729        self.is_withheld = (class & 0b1000_0000) != 0;
730    }
731    /// get the class flag as an enum
732    pub fn class_flag(&self) -> ClassFlag {
733        self.class_flag.into()
734    }
735    /// class type
736    pub fn class_type(&self, point14: bool) -> LASClassification {
737        if point14 { self.legacy_classification.into() } else { self.classification.into() }
738    }
739    /// class type 14
740    pub fn class_type14(&self) -> LASClassification14 {
741        self.classification.into()
742    }
743    /// Set Classification14
744    pub fn set_flags3(&mut self, class: u8) {
745        self.legacy_point_type = class & 0b11;
746        self.scanner_channel = (class & 0b1100) >> 2;
747        self.class_flag = (class & 0b1111_0000) >> 4;
748    }
749    /// Inject GPSTIME11
750    pub fn inject_gps_time<T: Reader>(&mut self, reader: &T, offset: u64) {
751        self.gps_time = Some(reader.f64_le(Some(offset)));
752    }
753    /// Inject RGB12 & RGB14
754    pub fn inject_rgb<T: Reader>(&mut self, reader: &T, offset: u64) {
755        self.rgba = Some(RGBA::from_reader(reader, Some(offset)));
756    }
757    /// Inject NIR
758    pub fn inject_nir<T: Reader>(&mut self, reader: &T, offset: u64) {
759        self.nir = Some(reader.uint16_le(Some(offset)));
760    }
761    /// Inject 8 bytes (2 bytes each for R, G, B, and NIR)
762    pub fn inject_rgb_nir<T: Reader>(&mut self, reader: &T, offset: u64) {
763        self.inject_rgb(reader, offset);
764        self.inject_nir(reader, offset + 6);
765    }
766    /// Inject WAVEPACKET13 & WAVEPACKET14
767    pub fn inject_wave_packet<T: Reader>(&mut self, reader: &T, offset: u64) {
768        self.wave_packet = Some(WavePacket::from_reader(reader, offset));
769    }
770    /// To Vector Point
771    pub fn to_vector_point(&self, header: &LASHeader) -> VectorPoint<LASPoint> {
772        let LASHeader {
773            x_offset,
774            y_offset,
775            z_offset,
776            x_scale_factor,
777            y_scale_factor,
778            z_scale_factor,
779            ..
780        } = header;
781        VectorPoint::new_xyz(
782            self.x as f64 * x_scale_factor + x_offset,
783            self.y as f64 * y_scale_factor + y_offset,
784            self.z as f64 * z_scale_factor + z_offset,
785            Some(self.clone()),
786        )
787    }
788    /// To Buffer
789    pub fn to_buffer_14(&self, compressed: bool) -> Vec<u8> {
790        let mut buf = Buffer::new(vec![0u8; 48]);
791        buf.set_i32_at(0, self.x);
792        buf.set_i32_at(4, self.y);
793        buf.set_i32_at(8, self.z);
794        buf.set_u16_at(12, self.intensity);
795        buf.set_u8_at(
796            14,
797            (self.return_number & 0b0000_0111)
798                | ((self.number_of_returns & 0b0000_0111) << 3)
799                | ((self.scan_direction_flag as u8) << 6)
800                | ((self.edge_of_flight_line as u8) << 7),
801        );
802        buf.set_u8_at(
803            15,
804            self.legacy_classification
805                | (self.is_synthetic as u8) << 5
806                | (self.is_key_point as u8) << 6
807                | (self.is_withheld as u8) << 7,
808        );
809        buf.set_i8_at(16, self.legacy_scan_angle_rank);
810        buf.set_u8_at(17, self.user_data);
811        // FIX FROM HERE
812        buf.set_i16_at(18, self.scan_angle);
813        buf.set_u16_at(20, self.point_source_id);
814        if compressed {
815            buf.set_u8_at(
816                22,
817                self.legacy_point_type | (self.scanner_channel << 2) | (self.class_flag << 4),
818            );
819            buf.set_u8_at(23, self.classification);
820            buf.set_u8_at(24, self.return_number + (self.number_of_returns << 4));
821            buf.set_u8_at(28, self.gps_time_change.unwrap_or(0));
822            buf.set_f64_at(29, self.gps_time.unwrap_or(0.));
823            let (r, g, b, _) = self.rgba.unwrap_or_default().to_u16s();
824            buf.set_u16_at(37, r);
825            buf.set_u16_at(39, g);
826            buf.set_u16_at(41, b);
827        }
828        buf.take()
829    }
830}
831
832/// A Classification Type Flag as an enum
833#[derive(Debug, Default, Clone, PartialEq)]
834pub enum LASClassification {
835    /// Created, Never Classified
836    CreatedNeverClassified,
837    /// Unclassified
838    #[default]
839    Unclassified,
840    /// Ground
841    Ground,
842    /// Low Vegetation
843    LowVegetation,
844    /// Medium Vegetation
845    MediumVegetation,
846    /// High Vegetation
847    HighVegetation,
848    /// Building
849    Building,
850    /// Low Point Noise
851    LowPointNoise,
852    /// Model Key-point (mass point)
853    ModelKeyPointMassPoint,
854    /// Water
855    Water,
856    /// Overlap Points
857    OverlapPoints,
858    /// Reserved
859    Reserved,
860}
861impl From<u8> for LASClassification {
862    fn from(class: u8) -> Self {
863        match class {
864            0 => Self::CreatedNeverClassified,
865            1 => Self::Unclassified,
866            2 => Self::Ground,
867            3 => Self::LowVegetation,
868            4 => Self::MediumVegetation,
869            5 => Self::HighVegetation,
870            6 => Self::Building,
871            7 => Self::LowPointNoise,
872            8 => Self::ModelKeyPointMassPoint,
873            9 => Self::Water,
874            12 => Self::OverlapPoints,
875            _ => Self::Reserved,
876        }
877    }
878}
879
880/// A Classification Type Flag as an enum
881#[derive(Debug, Default, Clone, PartialEq)]
882pub enum LASClassification14 {
883    /// Created, Never Classified
884    CreatedNeverClassified,
885    /// Unclassified
886    #[default]
887    Unclassified,
888    /// Ground
889    Ground,
890    /// Low Vegetation
891    LowVegetation,
892    /// Medium Vegetation
893    MediumVegetation,
894    /// High Vegetation
895    HighVegetation,
896    /// Building
897    Building,
898    /// Low Point Noise
899    LowPointNoise,
900    /// Model Key-point (mass point)
901    ModelKeyPointMassPoint,
902    /// Water
903    Water,
904    /// Rail
905    Rail,
906    /// Road Surface
907    RoadSurface,
908    /// Wire – Guard (Shield)
909    WireGuardShield,
910    /// Wire – Conductor (Phase)
911    WireConductorPhase,
912    /// Transmission Tower
913    TransmissionTower,
914    /// Wire-structure Connector (e.g. Insulator)
915    WireStructureConnector,
916    /// Bridge Deck
917    BridgeDeck,
918    /// High Noise
919    HighNoise,
920    /// Overhead Structure (e.g., conveyors, mining equipment, traffic lights)
921    OverheadSructure,
922    /// Ignored Ground (e.g., breakline proximity)
923    IgnoredGround,
924    /// Snow
925    Snow,
926    /// Temporal Exclusion (Features excluded due to changes over time between
927    /// data sources, e.g., water levels, landslides, permafrost)
928    TemporalExclusion,
929    /// Reserved
930    Reserved,
931    /// User Definable
932    UserDefinable,
933}
934impl From<u8> for LASClassification14 {
935    fn from(class: u8) -> Self {
936        match class {
937            0 => Self::CreatedNeverClassified,
938            1 => Self::Unclassified,
939            2 => Self::Ground,
940            3 => Self::LowVegetation,
941            4 => Self::MediumVegetation,
942            5 => Self::HighVegetation,
943            6 => Self::Building,
944            7 => Self::LowPointNoise,
945            8 => Self::ModelKeyPointMassPoint,
946            9 => Self::Water,
947            10 => Self::Rail,
948            11 => Self::RoadSurface,
949            13 => Self::WireGuardShield,
950            14 => Self::WireConductorPhase,
951            15 => Self::TransmissionTower,
952            16 => Self::WireStructureConnector,
953            17 => Self::BridgeDeck,
954            18 => Self::HighNoise,
955            19 => Self::OverheadSructure,
956            20 => Self::IgnoredGround,
957            21 => Self::Snow,
958            22 => Self::TemporalExclusion,
959            // 23..=63 => Self::Reserved,
960            64..=255 => Self::UserDefinable,
961            _ => Self::Reserved,
962        }
963    }
964}