### Octree usage example Source: https://docs.rs/oxigeo-copc/latest/oxigeo_copc/octree/struct.Octree.html?search=std%3A%3Avec Example showing how to create an Octree, insert a point, and check the length. ```rust use oxigeo_copc::point::{BoundingBox3D, Point3D}; use oxigeo_copc::octree::Octree; let bounds = BoundingBox3D::new(0.0, 0.0, 0.0, 100.0, 100.0, 100.0).unwrap(); let mut tree = Octree::new(bounds); tree.insert(Point3D::new(10.0, 20.0, 5.0)); assert_eq!(tree.len(), 1); ``` -------------------------------- ### Test format 3 GPS and RGB Source: https://docs.rs/oxigeo-copc/latest/src/oxigeo_copc/point_format.rs.html Starts testing format 3 GPS and RGB fields; the test body is incomplete in the source. ```rust #[test] fn test_format3_gps_and_rgb() { let rec = make_format3_record(1000, 2000, 3000, 99.9, 1000, 2000, 3000); let pt = deserialize_point(&rec, 3, [0.01, 0.01, 0.01], [0.0; 3]).expect("format 3"); ``` -------------------------------- ### test_chain_two_records (partial) Source: https://docs.rs/oxigeo-copc/latest/src/oxigeo_copc/extended_vlr.rs.html?search= Unit test that starts building a buffer with padding before the EVLR region. The test is incomplete in the source, but the visible portion sets up the buffer and start offset. ```rust #[test] fn test_chain_two_records() { let mut buf = vec![0u8; 8]; // some padding before the EVLR region let start = buf.len() as u64; ``` -------------------------------- ### Build format-10 point record (67 bytes = 30 base + 6 RGB + 2 NIR + 29 waveform) Source: https://docs.rs/oxigeo-copc/latest/src/oxigeo_copc/point_format.rs.html?search= Constructs a 67-byte format-10 record by extending a format-7 record with 2 bytes of NIR and 29 bytes of zeroed waveform data. The base record uses intensity 500 and GPS time. Useful for testing NIR parsing alongside a present but unchecked waveform packet. ```rust fn make_format10_record( raw_x: i32, raw_y: i32, raw_z: i32, gps_time: f64, r: u16, g: u16, b: u16, ) -> Vec { let mut rec = make_format7_record(raw_x, raw_y, raw_z, gps_time, r, g, b); rec.resize(67, 0); rec[36..38].copy_from_slice(&1000u16.to_le_bytes()); // NIR // Waveform bytes are left zeroed (bytes 38..67) rec } ``` -------------------------------- ### Test out-of-range start offset returns error Source: https://docs.rs/oxigeo-copc/latest/src/oxigeo_copc/extended_vlr.rs.html?search=std%3A%3Avec Uses a 10-byte buffer and a start offset of 1000, asserting that parse_evlr_chain returns an error. ```rust let buf = vec![0u8; 10]; assert!(parse_evlr_chain(&buf, 1000, 1).is_err()); ``` -------------------------------- ### LazChunkTable::chunk_offset Source: https://docs.rs/oxigeo-copc/latest/oxigeo_copc/laz/chunk_table/struct.LazChunkTable.html?search=Option%3CT%3E%2C+%28T+-%3E+U%29+-%3E+Option%3CU%3E Computes the byte offset (from file start) of chunk i. Chunk 0 starts at data_start; subsequent chunks accumulate byte_count from previous entries. ```APIDOC ## LazChunkTable::chunk_offset ### Description Computes the byte offset (from file start) of chunk `i`. Chunk 0 starts at `data_start`; subsequent chunks accumulate `byte_count` from previous entries. ### Method `pub fn chunk_offset(&self, data_start: u64, i: usize) -> u64` ### Parameters - `data_start` (u64) - Required - The byte offset from file start where chunk 0 begins. - `i` (usize) - Required - The index of the chunk whose offset is to be computed. ### Returns - (u64) - The byte offset from file start of chunk `i`. ``` -------------------------------- ### Build format-8 point record (38 bytes) with RGB and NIR Source: https://docs.rs/oxigeo-copc/latest/src/oxigeo_copc/point_format.rs.html?search=Option%3CT%3E%2C+%28T+-%3E+U%29+-%3E+Option%3CU%3E Extends the format 7 record to 38 bytes by appending 2 bytes of NIR data. The NIR channel is set to 1000. This format is used for point records that include near-infrared information. ```rust fn make_format8_record( raw_x: i32, raw_y: i32, raw_z: i32, gps_time: f64, r: u16, g: u16, b: u16, ) -> Vec { let mut rec = make_format7_record(raw_x, raw_y, raw_z, gps_time, r, g, b); rec.resize(38, 0); rec[36..38].copy_from_slice(&1000u16.to_le_bytes()); // NIR rec } ``` -------------------------------- ### Build format-3 point record (34 bytes) with GPS and RGB Source: https://docs.rs/oxigeo-copc/latest/src/oxigeo_copc/point_format.rs.html?search=Option%3CT%3E%2C+%28T+-%3E+U%29+-%3E+Option%3CU%3E Extends the format 0 record to 34 bytes by appending 8 bytes of GPS time and 6 bytes of RGB color data. The base record's intensity is set to 300. GPS time and each color channel use little-endian. ```rust fn make_format3_record( raw_x: i32, raw_y: i32, raw_z: i32, gps_time: f64, r: u16, g: u16, b: u16, ) -> Vec { let mut rec = make_format0_record(raw_x, raw_y, raw_z, 300); rec.resize(34, 0); rec[20..28].copy_from_slice(&gps_time.to_le_bytes()); rec[28..30].copy_from_slice(&r.to_le_bytes()); rec[30..32].copy_from_slice(&g.to_le_bytes()); rec[32..34].copy_from_slice(&b.to_le_bytes()); rec } ``` -------------------------------- ### Build format-2 point record (26 bytes) with RGB Source: https://docs.rs/oxigeo-copc/latest/src/oxigeo_copc/point_format.rs.html?search=Option%3CT%3E%2C+%28T+-%3E+U%29+-%3E+Option%3CU%3E Extends the format 0 record to 26 bytes by appending 6 bytes of RGB color data. The base record's intensity is set to 200. Each color channel (r, g, b) is written as u16 little-endian. ```rust fn make_format2_record(raw_x: i32, raw_y: i32, raw_z: i32, r: u16, g: u16, b: u16) -> Vec { let mut rec = make_format0_record(raw_x, raw_y, raw_z, 200); rec.resize(26, 0); rec[20..22].copy_from_slice(&r.to_le_bytes()); rec[22..24].copy_from_slice(&g.to_le_bytes()); rec[24..26].copy_from_slice(&b.to_le_bytes()); rec } ``` -------------------------------- ### Octree struct and usage example Source: https://docs.rs/oxigeo-copc/latest/oxigeo_copc/octree/struct.Octree.html?search= The Octree struct is an in-memory octree index over Point3D values. The example shows constructing a BoundingBox3D, creating an Octree, inserting a point, and asserting the tree length. ```rust pub struct Octree { /* private fields */ } ``` ```rust use oxigeo_copc::point::{BoundingBox3D, Point3D}; use oxigeo_copc::octree::Octree; let bounds = BoundingBox3D::new(0.0, 0.0, 0.0, 100.0, 100.0, 100.0).unwrap(); let mut tree = Octree::new(bounds); tree.insert(Point3D::new(10.0, 20.0, 5.0)); assert_eq!(tree.len(), 1); ``` -------------------------------- ### parse_evlr_chain function Source: https://docs.rs/oxigeo-copc/latest/src/oxigeo_copc/extended_vlr.rs.html?search= Walks the EVLR chain starting at start_of_first_evlr, parsing number_of_evlrs records sequentially. Returns an empty vector when the count is zero without touching bytes. Errors with CopcError::InvalidFormat on out-of-range start, truncation, or offset overflow. ```rust pub fn parse_evlr_chain( bytes: &[u8], start_of_first_evlr: u64, number_of_evlrs: u32, ) -> Result, CopcError> { if number_of_evlrs == 0 { return Ok(Vec::new()); } let mut offset = usize::try_from(start_of_first_evlr).map_err(|_| { CopcError::InvalidFormat(format!( "EVLR start offset {start_of_first_evlr} exceeds platform usize" )) })?; if offset > bytes.len() { return Err(CopcError::InvalidFormat(format!( "EVLR start offset {offset} exceeds file length {}", bytes.len() ))); } let mut evlrs = Vec::with_capacity(number_of_evlrs as usize); for i in 0..number_of_evlrs { let (evlr, next_offset) = parse_evlr(bytes, offset).map_err(|e| { CopcError::InvalidFormat(format!("Failed to parse EVLR #{i} at offset {offset}: {e}")) })?; evlrs.push(evlr); offset = next_offset; } Ok(evlrs) } ``` -------------------------------- ### Build format-7 point record (36 bytes) with RGB Source: https://docs.rs/oxigeo-copc/latest/src/oxigeo_copc/point_format.rs.html?search=Option%3CT%3E%2C+%28T+-%3E+U%29+-%3E+Option%3CU%3E Extends the format 6 record to 36 bytes by appending 6 bytes of RGB color data. The RGB values are written as u16 little-endian after the base 30-byte record. ```rust fn make_format7_record( raw_x: i32, raw_y: i32, raw_z: i32, gps_time: f64, r: u16, g: u16, b: u16, ) -> Vec { let mut rec = make_format6_record(raw_x, raw_y, raw_z, gps_time); rec.resize(36, 0); rec[30..32].copy_from_slice(&r.to_le_bytes()); rec[32..34].copy_from_slice(&g.to_le_bytes()); rec[34..36].copy_from_slice(&b.to_le_bytes()); rec } ``` -------------------------------- ### Build format-0 point record (Rust) Source: https://docs.rs/oxigeo-copc/latest/src/oxigeo_copc/copc_reader.rs.html Creates a 20-byte format-0 point record with given raw coordinates, intensity 50, return number 1, number of returns 1, and classification Ground (2). ```rust /// Build a format-0 point record (20 bytes). fn make_format0_point(raw_x: i32, raw_y: i32, raw_z: i32) -> Vec { let mut rec = vec![0u8; 20]; rec[0..4].copy_from_slice(&raw_x.to_le_bytes()); rec[4..8].copy_from_slice(&raw_y.to_le_bytes()); rec[8..12].copy_from_slice(&raw_z.to_le_bytes()); rec[12..14].copy_from_slice(&50u16.to_le_bytes()); // intensity rec[14] = 1 | (1 << 3); // return=1, num_returns=1 rec[15] = 2; // classification = Ground rec } ``` -------------------------------- ### Example: reading COPC file and querying points Source: https://docs.rs/oxigeo-copc/latest/oxigeo_copc/copc_reader/struct.CopcReader.html?search= Example usage of CopcReader. Reads a COPC file from disk, parses it, creates a bounding box, and queries points within that bounding box. Requires the file to exist and be a valid COPC file. ```rust use oxigeo_copc::copc_reader::CopcReader; use oxigeo_copc::point::BoundingBox3D; let data = std::fs::read("my_file.copc.laz").expect("read file"); let reader = CopcReader::from_bytes(&data).expect("parse COPC"); let bbox = BoundingBox3D::new(0.0, 0.0, 0.0, 100.0, 100.0, 50.0).expect("valid bbox"); let points = reader.query_points_in_bbox(&bbox).expect("query"); ``` -------------------------------- ### start_of_first_evlr Source: https://docs.rs/oxigeo-copc/latest/oxigeo_copc/las_header/struct.LasHeader.html?search=Option%3CT%3E%2C+%28T+-%3E+U%29+-%3E+Option%3CU%3E Returns the byte offset from the file start of the first Extended VLR. Returns 0 when the file has no EVLRs or is not LAS 1.4. ```APIDOC ## start_of_first_evlr ### Description Returns the byte offset from the file start of the first Extended VLR. Returns 0 when the file has no EVLRs or is not LAS 1.4. ### Method `pub fn start_of_first_evlr(&self) -> u64` ### Parameters - **self** (`&LasHeader`) - The header instance. ### Return Value - `u64` - The byte offset of the first Extended VLR, or 0 if none exist or not LAS 1.4. ``` -------------------------------- ### Build format-6 point record (Rust) Source: https://docs.rs/oxigeo-copc/latest/src/oxigeo_copc/copc_reader.rs.html Creates a 30-byte format-6 point record with given raw coordinates, intensity 100, return number 1, number of returns 1, classification Ground (2), and GPS time 1.0. ```rust /// Build a format-6 point record (30 bytes). fn make_format6_point(raw_x: i32, raw_y: i32, raw_z: i32) -> Vec { let mut rec = vec![0u8; 30]; rec[0..4].copy_from_slice(&raw_x.to_le_bytes()); rec[4..8].copy_from_slice(&raw_y.to_le_bytes()); rec[8..12].copy_from_slice(&raw_z.to_le_bytes()); rec[12..14].copy_from_slice(&100u16.to_le_bytes()); // intensity rec[14] = 1 | (1 << 4); // return_number=1, number_of_returns=1 rec[16] = 2; // classification = Ground rec[22..30].copy_from_slice(&1.0f64.to_le_bytes()); // GPS time rec } ``` -------------------------------- ### BitModel::new Source: https://docs.rs/oxigeo-copc/latest/oxigeo_copc/laz/arithmetic/struct.BitModel.html?search= Creates a fresh bit model with a 50/50 prior and the standard update cycle. ```APIDOC ## BitModel::new ### Description Creates a fresh bit model with a 50/50 prior and the standard update cycle. ### Method Constructor ### Signature ```rust pub fn new() -> Self ``` ### Parameters None ### Returns - **Self** - A new BitModel instance. ### Example ```rust let model = BitModel::new(); ``` ``` -------------------------------- ### Get number of EVLRs Source: https://docs.rs/oxigeo-copc/latest/src/oxigeo_copc/las_header.rs.html Returns the number of Extended VLRs declared in the header. Returns 0 when the file has no EVLRs or is not LAS 1.4. ```rust /// Number of Extended VLRs declared in the header. /// /// Returns 0 when the file has no EVLRs or is not LAS 1.4. pub fn number_of_evlrs(&self) -> u32 { self.number_of_evlrs } ``` -------------------------------- ### Point3D::new Source: https://docs.rs/oxigeo-copc/latest/oxigeo_copc/point/struct.Point3D.html?search=std%3A%3Avec Creates a new Point3D instance with specified coordinates and default values for other fields. ```APIDOC ## Point3D::new ### Description Creates a new point at `(x, y, z)` with all other fields zeroed / `None`. ### Method `pub fn new(x: f64, y: f64, z: f64) -> Self` ### Parameters - **x** (f64) - Required - X coordinate - **y** (f64) - Required - Y coordinate - **z** (f64) - Required - Z coordinate ### Returns - **Self** - A new `Point3D` instance ``` -------------------------------- ### Get bounding box as (min, max) Source: https://docs.rs/oxigeo-copc/latest/src/oxigeo_copc/las_header.rs.html Returns the bounding box as a tuple of two [f64; 3] arrays in (X, Y, Z) order. ```rust /// Return the bounding box as `(min, max)` in (X, Y, Z) order. pub fn bounds(&self) -> ([f64; 3], [f64; 3]) { ( [self.min_x, self.min_y, self.min_z], [self.max_x, self.max_y, self.max_z], ) } ``` -------------------------------- ### Build format-0 point record (20 bytes) Source: https://docs.rs/oxigeo-copc/latest/src/oxigeo_copc/point_format.rs.html?search=Option%3CT%3E%2C+%28T+-%3E+U%29+-%3E+Option%3CU%3E Constructs a 20-byte vector for a LAS format 0 point record with X, Y, Z, intensity, return number, number of returns, classification, scan angle rank, user data, and point source ID. Sets return_number=2, number_of_returns=3, classification=2 (Ground), scan_angle_rank=-5, user_data=42, point_source_id=7. ```rust fn make_format0_record(raw_x: i32, raw_y: i32, raw_z: i32, intensity: u16) -> Vec { let mut rec = vec![0u8; 20]; rec[0..4].copy_from_slice(&raw_x.to_le_bytes()); rec[4..8].copy_from_slice(&raw_y.to_le_bytes()); rec[8..12].copy_from_slice(&raw_z.to_le_bytes()); rec[12..14].copy_from_slice(&intensity.to_le_bytes()); // Byte 14: return_number=2 (bits 0-2), number_of_returns=3 (bits 3-5) rec[14] = 2 | (3 << 3); // Byte 15: classification = 2 (Ground) rec[15] = 2; // Byte 16: scan_angle_rank = -5 rec[16] = (-5i8) as u8; // Byte 17: user_data = 42 rec[17] = 42; // Bytes 18-19: point_source_id = 7 rec[18..20].copy_from_slice(&7u16.to_le_bytes()); rec } ``` -------------------------------- ### parse_evlr Source: https://docs.rs/oxigeo-copc/latest/oxigeo_copc/extended_vlr/fn.parse_evlr.html?search=Option%3CT%3E%2C+%28T+-%3E+U%29+-%3E+Option%3CU%3E Parses a single EVLR starting at the given offset within the data slice. Returns the parsed record and the byte offset immediately following its payload. ```APIDOC ## parse_evlr ### Description Parses a single EVLR starting at `offset` within `data`. Returns the parsed record and the byte offset immediately following its payload. ### Function Signature ```rust pub fn parse_evlr( data: &[u8], offset: usize, ) -> Result<(ExtendedVlr, usize), CopcError> ``` ### Parameters - **data** (`&[u8]`) - The byte slice containing the EVLR data. - **offset** (`usize`) - The byte offset within `data` where the EVLR starts. ### Returns - `Result<(ExtendedVlr, usize), CopcError>` - On success, returns a tuple containing the parsed `ExtendedVlr` and the byte offset immediately following its payload. On failure, returns a `CopcError`. ### Errors Returns `CopcError::InvalidFormat` when the header or the declared payload extends past the end of `data`, or when arithmetic would overflow. ``` -------------------------------- ### Point3D and BoundingBox3D Methods Source: https://docs.rs/oxigeo-copc/latest/src/oxigeo_copc/point.rs.html Documentation for the public methods of the Point3D and BoundingBox3D structs in the oxigeo_copc crate. ```APIDOC ## Point3D::with_gps_time ### Description Builder method that sets the GPS timestamp for a `Point3D` instance. ### Method `pub fn with_gps_time(mut self, t: f64) -> Self` ### Parameters - `t` (f64) - Required - GPS timestamp value to set. ### Returns `Self` - The updated `Point3D` instance with the GPS timestamp set. ``` ```APIDOC ## Point3D::with_nir ### Description Builder method that sets the near-infrared channel value for a `Point3D` instance. ### Method `pub fn with_nir(mut self, nir: u16) -> Self` ### Parameters - `nir` (u16) - Required - Near-infrared channel value to set. ### Returns `Self` - The updated `Point3D` instance with the near-infrared value set. ``` ```APIDOC ## Point3D::distance_to ### Description Computes the 3-D Euclidean distance from this point to another `Point3D`. ### Method `pub fn distance_to(&self, other: &Point3D) -> f64` ### Parameters - `other` (&Point3D) - Required - The other point to measure distance to. ### Returns `f64` - The Euclidean distance between the two points. ``` ```APIDOC ## Point3D::distance_2d ### Description Computes the 2-D horizontal distance (ignoring Z) from this point to another `Point3D`. ### Method `pub fn distance_2d(&self, other: &Point3D) -> f64` ### Parameters - `other` (&Point3D) - Required - The other point to measure distance to. ### Returns `f64` - The horizontal distance between the two points. ``` ```APIDOC ## Point3D::classification_name ### Description Returns the human-readable name for the ASPRS LAS 1.4 classification code of this point. ### Method `pub fn classification_name(&self) -> &'static str` ### Returns `&'static str` - The standard classification name for codes 0–18, or `"Reserved/Unknown"` for other codes. ``` ```APIDOC ## BoundingBox3D::new ### Description Constructs a new axis-aligned 3-D bounding box. Returns `None` if any `min > max` invariant is violated. ### Method `pub fn new(min_x: f64, min_y: f64, min_z: f64, max_x: f64, max_y: f64, max_z: f64) -> Option` ### Parameters - `min_x` (f64) - Required - Minimum X coordinate. - `min_y` (f64) - Required - Minimum Y coordinate. - `min_z` (f64) - Required - Minimum Z coordinate. - `max_x` (f64) - Required - Maximum X coordinate. - `max_y` (f64) - Required - Maximum Y coordinate. - `max_z` (f64) - Required - Maximum Z coordinate. ### Returns `Option` - The new bounding box, or `None` if any min coordinate exceeds the corresponding max coordinate. ``` ```APIDOC ## BoundingBox3D::from_points ### Description Builds the tightest bounding box that contains every point in the given slice. Returns `None` when `points` is empty. ### Method `pub fn from_points(points: &[Point3D]) -> Option` ### Parameters - `points` (&[Point3D]) - Required - Slice of points to enclose. ### Returns `Option` - The bounding box containing all points, or `None` if the slice is empty. ``` ```APIDOC ## BoundingBox3D::contains ### Description Returns `true` when the given point is strictly inside or on the boundary of the bounding box. ### Method `pub fn contains(&self, p: &Point3D) -> bool` ### Parameters - `p` (&Point3D) - Required - The point to test. ### Returns `bool` - `true` if the point is inside or on the boundary, otherwise `false`. ``` -------------------------------- ### Test chunk table byte offset cumulative Source: https://docs.rs/oxigeo-copc/latest/src/oxigeo_copc/laz/chunk_table.rs.html?search=Option%3CT%3E%2C+%28T+-%3E+U%29+-%3E+Option%3CU%3E Unit test verifying that chunk_offset correctly computes cumulative byte offsets for each chunk given a data start offset. ```rust #[test] fn test_chunk_table_byte_offset_cumulative() { let table = LazChunkTable { chunk_size: 50_000, point_count: 60, chunks: vec![ LazChunk { byte_count: 100, point_count: 20, }, LazChunk { byte_count: 250, point_count: 25, }, LazChunk { byte_count: 175, point_count: 15, }, ], }; let raw = serialize_chunk_table(&table); let parsed = parse_chunk_table(&raw, 0, raw.len(), 50_000).expect("parse"); let data_start: u64 = 1000; assert_eq!(parsed.chunk_offset(data_start, 0), 1000); assert_eq!(parsed.chunk_offset(data_start, 1), 1100); assert_eq!(parsed.chunk_offset(data_start, 2), 1350); assert_eq!(parsed.chunk_offset(data_start, 3), 1525); } ``` -------------------------------- ### Build format-1 point record (28 bytes) extending format 0 Source: https://docs.rs/oxigeo-copc/latest/src/oxigeo_copc/point_format.rs.html?search=Option%3CT%3E%2C+%28T+-%3E+U%29+-%3E+Option%3CU%3E Extends the format 0 record to 28 bytes by appending an 8-byte GPS time. The base record's intensity is set to 100. The GPS time is written as f64 little-endian. ```rust fn make_format1_record(raw_x: i32, raw_y: i32, raw_z: i32, gps_time: f64) -> Vec { let mut rec = make_format0_record(raw_x, raw_y, raw_z, 100); rec.resize(28, 0); rec[20..28].copy_from_slice(&gps_time.to_le_bytes()); rec } ``` -------------------------------- ### parse_evlr_chain Source: https://docs.rs/oxigeo-copc/latest/oxigeo_copc/extended_vlr/fn.parse_evlr_chain.html Parses the EVLR chain from a byte slice, returning all Extended VLRs. It starts at the given offset and parses the specified number of records, advancing past each header and payload. ```APIDOC ## parse_evlr_chain ### Description Walks the EVLR chain and returns every Extended VLR. Starting at `start_of_first_evlr`, sequentially parses `number_of_evlrs` records, advancing past each header and payload. A count of zero yields an empty vector without touching `bytes`. ### Function Signature ```rust pub fn parse_evlr_chain( bytes: &[u8], start_of_first_evlr: u64, number_of_evlrs: u32, ) -> Result, CopcError> ``` ### Parameters - `bytes` (&[u8]) - Required - The byte slice containing the EVLR chain. - `start_of_first_evlr` (u64) - Required - The offset in `bytes` where the first EVLR record begins. - `number_of_evlrs` (u32) - Required - The number of EVLR records to parse. ### Return Value - `Result, CopcError>` - On success, returns a vector of `ExtendedVlr` values. On failure, returns a `CopcError`. ### Errors Returns `CopcError::InvalidFormat` when `start_of_first_evlr` is out of range, when any record is truncated, or when offsets overflow. The function never panics on malformed input. ``` -------------------------------- ### build_geo_key_directory: Test helper to build GeoKeyDirectory bytes Source: https://docs.rs/oxigeo-copc/latest/src/oxigeo_copc/crs_vlr.rs.html Test helper that builds a GeoKeyDirectory byte buffer from a slice of `GeoKeyEntry` values, writing the 4-u16 header (version, revision, minor revision, key count) in little-endian order. ```rust fn build_geo_key_directory(keys: &[GeoKeyEntry]) -> Vec { let mut buf = Vec::new(); buf.extend_from_slice(&1u16.to_le_bytes()); // key_directory_version buf.extend_from_slice(&1u16.to_le_bytes()); // key_revision buf.extend_from_slice(&0u16.to_le_bytes()); // minor_revision buf.extend_from_slice(&(keys.len() as u16).to_le_bytes()); ``` -------------------------------- ### Build LAS point records for formats 0-3 Source: https://docs.rs/oxigeo-copc/latest/src/oxigeo_copc/point_format.rs.html These helper functions build LAS point records for formats 0 through 3. They are used to create test data for point record parsing. Each function writes the raw X, Y, Z coordinates and intensity, then fills the remaining fields with fixed values (e.g., return number 2, classification 2, scan angle -5, user data 42, point source ID 7). Format 1 adds GPS time, format 2 adds RGB, and format 3 adds both GPS time and RGB. ```rust fn make_format0_record(raw_x: i32, raw_y: i32, raw_z: i32, intensity: u16) -> Vec { let mut rec = vec![0u8; 20]; rec[0..4].copy_from_slice(&raw_x.to_le_bytes()); rec[4..8].copy_from_slice(&raw_y.to_le_bytes()); rec[8..12].copy_from_slice(&raw_z.to_le_bytes()); rec[12..14].copy_from_slice(&intensity.to_le_bytes()); // Byte 14: return_number=2 (bits 0-2), number_of_returns=3 (bits 3-5) rec[14] = 2 | (3 << 3); // Byte 15: classification = 2 (Ground) rec[15] = 2; // Byte 16: scan_angle_rank = -5 rec[16] = (-5i8) as u8; // Byte 17: user_data = 42 rec[17] = 42; // Bytes 18-19: point_source_id = 7 rec[18..20].copy_from_slice(&7u16.to_le_bytes()); rec } ``` ```rust fn make_format1_record(raw_x: i32, raw_y: i32, raw_z: i32, gps_time: f64) -> Vec { let mut rec = make_format0_record(raw_x, raw_y, raw_z, 100); rec.resize(28, 0); rec[20..28].copy_from_slice(&gps_time.to_le_bytes()); rec } ``` ```rust fn make_format2_record(raw_x: i32, raw_y: i32, raw_z: i32, r: u16, g: u16, b: u16) -> Vec { let mut rec = make_format0_record(raw_x, raw_y, raw_z, 200); rec.resize(26, 0); rec[20..22].copy_from_slice(&r.to_le_bytes()); rec[22..24].copy_from_slice(&g.to_le_bytes()); rec[24..26].copy_from_slice(&b.to_le_bytes()); rec } ``` ```rust fn make_format3_record( raw_x: i32, raw_y: i32, raw_z: i32, gps_time: f64, r: u16, g: u16, b: u16, ) -> Vec { let mut rec = make_format0_record(raw_x, raw_y, raw_z, 300); rec.resize(34, 0); rec[20..28].copy_from_slice(&gps_time.to_le_bytes()); rec[28..30].copy_from_slice(&r.to_le_bytes()); rec[30..32].copy_from_slice(&g.to_le_bytes()); rec[32..34].copy_from_slice(&b.to_le_bytes()); rec } ``` -------------------------------- ### parse_vlrs Source: https://docs.rs/oxigeo-copc/latest/oxigeo_copc/vlr_chain/fn.parse_vlrs.html Parses all VLRs from raw file bytes using the LAS header. Iterates header.number_of_vlrs times starting at header.header_size bytes from the beginning of data. Returns a vector of Vlr objects or a CopcError. ```APIDOC ## Function parse_vlrs ### Description Parses all VLRs from the raw file bytes using information in the LAS header. Iterates `header.number_of_vlrs` times starting at `header.header_size` bytes from the beginning of `data`. ### Signature ```rust pub fn parse_vlrs( data: &[u8], header: &LasHeader, ) -> Result, CopcError> ``` ### Parameters - **data** (`&[u8]`) - The raw file bytes. - **header** (`&LasHeader`) - The LAS header containing the number of VLRs and header size. ### Returns - `Result, CopcError>` - A vector of parsed VLRs on success, or a `CopcError` on failure. ### Errors - `CopcError::InvalidFormat` - Returned when the data is truncated or a VLR is malformed. ``` -------------------------------- ### test_parse_geo_key_directory_minimal: parse single key Source: https://docs.rs/oxigeo-copc/latest/src/oxigeo_copc/crs_vlr.rs.html?search=u32+-%3E+bool Builds a GeoKeyDirectory with one entry and verifies that parse_geo_key_directory returns the correct version and key. ```rust #[test] fn test_parse_geo_key_directory_minimal() { let entry = GeoKeyEntry { key_id: 1024, tiff_tag_location: 0, count: 1, value_offset: 1, }; let bytes = build_geo_key_directory(std::slice::from_ref(&entry)); let dir = parse_geo_key_directory(&bytes).expect("parse"); assert_eq!(dir.key_directory_version, 1); assert_eq!(dir.keys.len(), 1); assert_eq!(dir.keys[0], entry); } ``` -------------------------------- ### HeightProfile::along_line Source: https://docs.rs/oxigeo-copc/latest/oxigeo_copc/profile/struct.HeightProfile.html Extracts a height profile along a 2-D transect from a point cloud. This method takes an octree, start and end coordinates, corridor width, and bin count to generate the profile. ```APIDOC ## along_line ### Description Extract a height profile along a 2-D transect. ### Method pub fn along_line(octree: &Octree, x1: f64, y1: f64, x2: f64, y2: f64, corridor_width: f64, bin_count: u32) -> Self ### Parameters - **octree** (&Octree) - Required - Source point cloud. - **x1** (f64) - Required - Start x-coordinate of the transect. - **y1** (f64) - Required - Start y-coordinate of the transect. - **x2** (f64) - Required - End x-coordinate of the transect. - **y2** (f64) - Required - End y-coordinate of the transect. - **corridor_width** (f64) - Required - Half-width of the corridor on each side of the centre line (metres). - **bin_count** (u32) - Required - Number of bins to divide the transect into. ### Returns - **Self** - A new HeightProfile instance. ``` -------------------------------- ### Construct transect profile with octree query and binning Source: https://docs.rs/oxigeo-copc/latest/src/oxigeo_copc/profile.rs.html Builds a transect profile by querying an octree with a bounding box, projecting candidate points onto the transect axis, and binning them into segments. Uses root bounds for Z extents and filters by corridor width and length. ```rust let cmax_y = all_corners .iter() .map(|c| c.1) .fold(f64::NEG_INFINITY, f64::max); // Use the octree root bounds for Z extents. let root_min_z = octree .stats() .bounds .as_ref() .map(|b| b.min_z) .unwrap_or(f64::NEG_INFINITY); let root_max_z = octree .stats() .bounds .as_ref() .map(|b| b.max_z) .unwrap_or(f64::INFINITY); let candidates = if let Some(query_bbox) = BoundingBox3D::new(cmin_x, cmin_y, root_min_z, cmax_x, cmax_y, root_max_z) { octree.query_bbox(&query_bbox) } else { Vec::new() }; // Bucket each candidate into its bin. let mut bins: Vec> = (0..bin_count).map(|_| Vec::new()).collect(); for &pt in &candidates { // Project onto the transect axis. let rel_x = pt.x - x1; let rel_y = pt.y - y1; let along = rel_x * ux + rel_y * uy; let perp = rel_x * px + rel_y * py; // Check corridor width. if perp.abs() > cw { continue; } // Check within transect length. if total_length > 0.0 && (along < 0.0 || along > total_length) { continue; } let bin_idx = if total_length > 0.0 { let idx = (along / bin_width).floor() as usize; idx.min(bin_count - 1) } else { 0 }; bins[bin_idx].push(pt.clone()); } let segments: Vec = bins .into_iter() .enumerate() .map(|(i, pts)| { let dist = (i as f64 + 0.5) * bin_width; ProfileSegment::from_points(dist, pts) }) .collect(); let height_min = segments .iter() .filter(|s| s.point_count > 0) .map(|s| s.min_z) .fold(f64::INFINITY, f64::min); let height_max = segments .iter() .filter(|s| s.point_count > 0) .map(|s| s.max_z) .fold(f64::NEG_INFINITY, f64::max); Self { segments, bin_width, height_min, height_max, } ``` -------------------------------- ### LAS_MAGIC constant definition Source: https://docs.rs/oxigeo-copc/latest/oxigeo_copc/las_header/constant.LAS_MAGIC.html?search= Defines the four-byte magic number for LAS files as the byte string b"LASF". This constant must appear at the start of every LAS file. ```rust pub const LAS_MAGIC: &[u8] = b"LASF"; ``` -------------------------------- ### new(x: f64, y: f64, z: f64) -> Self Source: https://docs.rs/oxigeo-copc/latest/oxigeo_copc/point/struct.Point3D.html?search=Option%3CT%3E%2C+%28T+-%3E+U%29+-%3E+Option%3CU%3E Creates a new Point3D instance with the given coordinates and all other fields zeroed or None. ```APIDOC ## new(x: f64, y: f64, z: f64) -> Self ### Description Creates a new Point3D instance at the specified (x, y, z) coordinates with all other fields set to zero or None. ### Parameters - **x** (f64) - Required - The X coordinate. - **y** (f64) - Required - The Y coordinate. - **z** (f64) - Required - The Z coordinate. ### Returns - **Self** - A new Point3D instance. ``` -------------------------------- ### HeightProfile::along_line Source: https://docs.rs/oxigeo-copc/latest/oxigeo_copc/profile/struct.HeightProfile.html?search=Option%3CT%3E%2C+%28T+-%3E+U%29+-%3E+Option%3CU%3E Extracts a height profile along a 2-D transect from a point cloud. This method takes an octree reference, start and end coordinates, corridor width, and bin count to generate a HeightProfile. ```APIDOC ## Function: HeightProfile::along_line ### Description Extracts a height profile along a 2-D transect from a point cloud. ### Signature ```rust pub fn along_line( octree: &Octree, x1: f64, y1: f64, x2: f64, y2: f64, corridor_width: f64, bin_count: u32, ) -> Self ``` ### Parameters - **octree** (`&Octree`) - Required - Source point cloud. - **x1** (`f64`) - Required - X-coordinate of the start of the transect. - **y1** (`f64`) - Required - Y-coordinate of the start of the transect. - **x2** (`f64`) - Required - X-coordinate of the end of the transect. - **y2** (`f64`) - Required - Y-coordinate of the end of the transect. - **corridor_width** (`f64`) - Required - Half-width of the corridor on each side of the centre line (metres). - **bin_count** (`u32`) - Required - Number of bins to divide the transect into. ### Returns - **Self** - A new `HeightProfile` instance containing the extracted profile. ### Errors This function does not return a `Result` and does not explicitly handle errors. It is expected to succeed given valid inputs. ``` -------------------------------- ### decompress_format_0 Source: https://docs.rs/oxigeo-copc/latest/oxigeo_copc/laz/format_v1/fn.decompress_format_0.html?search= Decompress a LASzip v1 chunk encoded in point format 0. The compressed input must start with a 20-byte raw seed record, followed by an arithmetic-coded body for the remaining point_count - 1 records. ```APIDOC ## Function decompress_format_0 ### Description Decompress a LASzip v1 chunk encoded in point format 0. The compressed input must start with a 20-byte raw seed record, followed by an arithmetic-coded body for the remaining point_count - 1 records. ### Signature ```rust pub fn decompress_format_0( compressed: &[u8], point_count: usize, ) -> Result, CopcError> ``` ### Parameters - **compressed** (`&[u8]`) - Required - The compressed LASzip v1 chunk data. First 20 bytes are the raw seed record; remaining bytes are the arithmetic-coded body. - **point_count** (`usize`) - Required - The total number of points in the chunk. ### Returns - **Ok(Vec)** - The decompressed point data. - **Err(CopcError)** - Returns `CopcError::LazDecoderError` when the input is truncated, the seed record is too small, or the arithmetic decoder cannot initialize. ### Errors - `CopcError::LazDecoderError` - Returned when the input is truncated, the seed record is too small, or the arithmetic decoder cannot initialize. ``` -------------------------------- ### Point3D::new constructor Source: https://docs.rs/oxigeo-copc/latest/src/oxigeo_copc/point.rs.html?search= Creates a new Point3D at (x, y, z) with all other fields zeroed or None. Sets return_number and number_of_returns to 1. ```rust /// Create a new point at `(x, y, z)` with all other fields zeroed / `None`. #[inline] pub fn new(x: f64, y: f64, z: f64) -> Self { Self { x, y, z, intensity: 0, return_number: 1, number_of_returns: 1, classification: 0, scan_angle_rank: 0.0, user_data: 0, point_source_id: 0, gps_time: None, red: None, green: None, blue: None, nir: None, waveform: None, } } ``` -------------------------------- ### Parse waveform packet Source: https://docs.rs/oxigeo-copc/latest/src/oxigeo_copc/point_format.rs.html Parses a 29-byte waveform packet starting at a given offset within a record. The layout follows ASPRS LAS 1.4 R15 Tables 17-18. Returns CopcError::InvalidFormat if the record is too short. ```rust fn parse_waveform_packet(record: &[u8], offset: usize) -> Result { if record.len() < offset + 29 { return Err(CopcError::InvalidFormat(format!( "Record too short for waveform packet at offset {offset}: need {}, got {}", offset + 29, record.len() ))); } let w = &record[offset..offset + 29]; Ok(WaveformPacket { descriptor_index: w[0], byte_offset: u64::from_le_bytes([w[1], w[2], w[3], w[4], w[5], w[6], w[7], w[8]]), packet_size: u32::from_le_bytes([w[9], w[10], w[11], w[12]]), return_point_loc: f32::from_le_bytes([w[13], w[14], w[15], w[16]]), x_t: f32::from_le_bytes([w[17], w[18], w[19], w[20]]), y_t: f32::from_le_bytes([w[21], w[22], w[23], w[24]]), z_t: f32::from_le_bytes([w[25], w[26], w[27], w[28]]), }) } ``` -------------------------------- ### Point3D builder methods Source: https://docs.rs/oxigeo-copc/latest/src/oxigeo_copc/point.rs.html?search= Builder methods for setting intensity, classification, and color values on a Point3D. Each method consumes self and returns the modified point. ```rust /// Builder: set the intensity value. #[inline] pub fn with_intensity(mut self, intensity: u16) -> Self { self.intensity = intensity; self } /// Builder: set the ASPRS classification code. #[inline] pub fn with_classification(mut self, class: u8) -> Self { self.classification = class; self } /// Builder: set red / green / blue colour values. #[inline] pub fn with_color(mut self, r: u16, g: u16, b: u16) -> Self { self.red = Some(r); self.green = Some(g); self.blue = Some(b); self } ```