Skip to main content

spatialrust_io/e57/
schema.rs

1use e57::PointCloud as E57PointCloud;
2use spatialrust_core::{DType, FieldSemantic, PointField, PointSchema, StandardSchemas};
3
4use crate::error::{e57_format, IoError};
5
6/// Builds a SpatialRust schema for points read from an E57 scan descriptor.
7#[must_use]
8pub fn schema_for_e57_pointcloud(pc: &E57PointCloud) -> PointSchema {
9    let mut schema = StandardSchemas::point_xyz();
10    if pc.has_intensity() {
11        schema = schema.with_field(PointField::scalar(
12            "intensity",
13            FieldSemantic::Intensity,
14            DType::F32,
15        ));
16    }
17    if pc.has_color() {
18        schema = schema
19            .with_field(PointField::scalar("r", FieldSemantic::ColorR, DType::U8))
20            .with_field(PointField::scalar("g", FieldSemantic::ColorG, DType::U8))
21            .with_field(PointField::scalar("b", FieldSemantic::ColorB, DType::U8));
22    }
23    schema
24}
25
26/// Selects an E57 prototype and export schema for writing a SpatialRust cloud.
27pub fn schema_from_point_cloud(schema: &PointSchema) -> (PointSchema, Vec<e57::Record>) {
28    let export_schema = export_schema_for_cloud(schema);
29    let prototype = e57_prototype_from_schema(&export_schema);
30    (export_schema, prototype)
31}
32
33fn export_schema_for_cloud(source: &PointSchema) -> PointSchema {
34    let mut schema = StandardSchemas::point_xyz();
35    if source.find_semantic(FieldSemantic::Intensity).is_some() {
36        schema = schema.with_field(PointField::scalar(
37            "intensity",
38            FieldSemantic::Intensity,
39            DType::F32,
40        ));
41    }
42    if source.find_semantic(FieldSemantic::ColorR).is_some() {
43        schema = schema
44            .with_field(PointField::scalar("r", FieldSemantic::ColorR, DType::U8))
45            .with_field(PointField::scalar("g", FieldSemantic::ColorG, DType::U8))
46            .with_field(PointField::scalar("b", FieldSemantic::ColorB, DType::U8));
47    }
48    schema
49}
50
51/// Builds an E57 point prototype from a SpatialRust schema.
52#[must_use]
53pub fn e57_prototype_from_schema(schema: &PointSchema) -> Vec<e57::Record> {
54    let mut prototype = vec![
55        e57::Record::CARTESIAN_X_F32,
56        e57::Record::CARTESIAN_Y_F32,
57        e57::Record::CARTESIAN_Z_F32,
58    ];
59    if schema.find_semantic(FieldSemantic::Intensity).is_some() {
60        prototype.push(e57::Record::INTENSITY_UNIT_F32);
61    }
62    if schema.find_semantic(FieldSemantic::ColorR).is_some() {
63        prototype.push(e57::Record::COLOR_RED_U8);
64        prototype.push(e57::Record::COLOR_GREEN_U8);
65        prototype.push(e57::Record::COLOR_BLUE_U8);
66    }
67    prototype
68}
69
70/// Validates that a SpatialRust cloud can be exported to E57.
71pub fn validate_export_schema(schema: &PointSchema) -> Result<(), IoError> {
72    schema.validate_positions().map_err(IoError::from)?;
73    if schema.find_semantic(FieldSemantic::Intensity).is_none()
74        && schema.find_semantic(FieldSemantic::ColorR).is_none()
75        && schema.len() > 3
76    {
77        return Err(e57_format(
78            "E57 export supports xyz with optional intensity and rgb only".to_owned(),
79        ));
80    }
81    Ok(())
82}
83
84#[cfg(test)]
85mod tests {
86    use super::{e57_prototype_from_schema, schema_for_e57_pointcloud};
87    use e57::PointCloud as E57PointCloud;
88    use spatialrust_core::{FieldSemantic, StandardSchemas};
89
90    #[test]
91    fn builds_xyz_prototype() {
92        let prototype = e57_prototype_from_schema(&StandardSchemas::point_xyz());
93        assert_eq!(prototype.len(), 3);
94    }
95
96    #[test]
97    fn builds_xyzi_prototype() {
98        let prototype = e57_prototype_from_schema(&StandardSchemas::point_xyzi());
99        assert_eq!(prototype.len(), 4);
100    }
101
102    #[test]
103    fn schema_from_empty_e57_scan() {
104        let pc = E57PointCloud::default();
105        let schema = schema_for_e57_pointcloud(&pc);
106        assert!(schema.find_semantic(FieldSemantic::PositionX).is_some());
107    }
108}