use std::sync::Arc;
use ifc_lite_core::{EntityDecoder, EntityIndex, EntityScanner, GeoRefExtractor, IfcType};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
pub struct Georeferencing {
#[serde(skip_serializing_if = "Option::is_none")]
pub crs_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub geodetic_datum: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub vertical_datum: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub map_projection: Option<String>,
pub eastings: f64,
pub northings: f64,
pub orthogonal_height: f64,
pub x_axis_abscissa: f64,
pub x_axis_ordinate: f64,
pub scale: f64,
pub rotation_degrees: f64,
pub transform_matrix: [f64; 16],
#[serde(skip_serializing_if = "Option::is_none", default)]
pub crs_description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub map_zone: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub map_unit: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub map_unit_scale: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub source: Option<String>,
}
impl Georeferencing {
fn from_core(geo: &ifc_lite_core::GeoReference) -> Self {
Self {
crs_name: geo.crs_name.clone(),
geodetic_datum: geo.geodetic_datum.clone(),
vertical_datum: geo.vertical_datum.clone(),
map_projection: geo.map_projection.clone(),
eastings: geo.eastings,
northings: geo.northings,
orthogonal_height: geo.orthogonal_height,
x_axis_abscissa: geo.x_axis_abscissa,
x_axis_ordinate: geo.x_axis_ordinate,
scale: geo.scale,
rotation_degrees: geo.rotation().to_degrees(),
transform_matrix: geo.to_matrix(),
crs_description: geo.crs_description.clone(),
map_zone: geo.map_zone.clone(),
map_unit: geo.map_unit.clone(),
map_unit_scale: geo.map_unit_scale,
source: Some(geo.source.label().to_string()),
}
}
}
pub fn extract_georeferencing<T>(content: &T) -> Option<Georeferencing>
where
T: AsRef<[u8]> + ?Sized,
{
let content = content.as_ref();
let entity_index = Arc::new(crate::build_entity_index_parallel(content));
extract_georeferencing_with_index(content, &entity_index)
}
pub fn extract_georeferencing_with_index(
content: &[u8],
entity_index: &Arc<EntityIndex>,
) -> Option<Georeferencing> {
let mut decoder = EntityDecoder::with_arc_index(content, entity_index.clone());
let mut entity_types: Vec<(u32, IfcType)> = Vec::new();
let mut scanner = EntityScanner::new(content);
while let Some((id, type_name, _start, _end)) = scanner.next_entity() {
match type_name {
"IFCMAPCONVERSION" => entity_types.push((id, IfcType::IfcMapConversion)),
"IFCPROJECTEDCRS" => entity_types.push((id, IfcType::IfcProjectedCRS)),
"IFCPROPERTYSET" => entity_types.push((id, IfcType::IfcPropertySet)),
"IFCSITE" => entity_types.push((id, IfcType::IfcSite)),
_ => {}
}
}
if entity_types.is_empty() {
return None;
}
match GeoRefExtractor::extract(&mut decoder, &entity_types) {
Ok(Some(geo)) => Some(Georeferencing::from_core(&geo)),
Ok(None) => None,
Err(e) => {
tracing::debug!(error = %e, "Georeferencing extraction failed");
None
}
}
}
#[cfg(test)]
#[path = "georeferencing_tests.rs"]
mod tests;