Skip to main content

ifc_georef/crs/
projected.rs

1//! Borrowed `IfcProjectedCRS` interpretation.
2
3use ifc_model::{EntityId, Model};
4
5use crate::crs::unit::{resolve_length_unit, LengthUnit};
6use crate::error::{GeorefError, GeorefResult};
7
8/// A projected coordinate reference system read from `IfcProjectedCRS`.
9#[derive(Debug, Clone, PartialEq)]
10pub struct ProjectedCrs {
11    /// The `IfcProjectedCRS` entity this was read from.
12    pub entity: EntityId,
13    /// Mandatory CRS identifier, conventionally an EPSG code such as
14    /// `EPSG:25832`.
15    pub name: String,
16    /// Optional human-readable description.
17    pub description: Option<String>,
18    /// Optional horizontal datum, such as `ETRS89`.
19    pub geodetic_datum: Option<String>,
20    /// Optional vertical datum, such as `DHHN2016`.
21    pub vertical_datum: Option<String>,
22    /// Optional projection name, such as `UTM`.
23    pub map_projection: Option<String>,
24    /// Optional projection zone, such as `32N`.
25    pub map_zone: Option<String>,
26    /// Explicit target unit. `None` means IFC inherits the project length unit.
27    pub map_unit: Option<LengthUnit>,
28}
29
30pub(crate) fn projected_crs(model: &Model, id: EntityId) -> GeorefResult<ProjectedCrs> {
31    let entity = model.get(id).ok_or(GeorefError::MissingEntity {
32        referrer: id,
33        missing: id,
34    })?;
35    if !entity.is_type("IFCPROJECTEDCRS") {
36        return Err(GeorefError::WrongType {
37            entity: id,
38            expected: "IFCPROJECTEDCRS",
39            actual: entity.type_name.to_string(),
40        });
41    }
42    // IFC4 ADD2 TC1 IfcCoordinateReferenceSystem contributes slots 0..3;
43    // IfcProjectedCRS adds MapProjection, MapZone, and MapUnit at 4..6.
44    let name = entity
45        .text(0)
46        .ok_or(GeorefError::MissingAttribute {
47            entity: id,
48            index: 0,
49            name: "Name",
50        })?
51        .to_owned();
52    let map_unit = match entity.attribute(6) {
53        None | Some(ifc_model::value::Value::Null) => None,
54        Some(value) => {
55            let unit = value.as_ref_id().ok_or(GeorefError::InvalidAttribute {
56                entity: id,
57                index: 6,
58                name: "MapUnit",
59            })?;
60            Some(resolve_length_unit(model, unit)?)
61        }
62    };
63    Ok(ProjectedCrs {
64        entity: id,
65        name,
66        description: entity.text(1).map(str::to_owned),
67        geodetic_datum: entity.text(2).map(str::to_owned),
68        vertical_datum: entity.text(3).map(str::to_owned),
69        map_projection: entity.text(4).map(str::to_owned),
70        map_zone: entity.text(5).map(str::to_owned),
71        map_unit,
72    })
73}