use ifc_model::EntityId;
use ifc_schema::SchemaVersion;
use super::reference::SiteReference;
use crate::conversion::ProjectToMap;
use crate::error::{GeorefError, GeorefResult};
pub const SITE_ELEVATION_TOLERANCE_M: f64 = 0.01;
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq)]
pub struct SiteElevationComparison {
pub site: EntityId,
pub ref_elevation: f64,
pub orthogonal_height: f64,
pub site_origin_map_height: f64,
pub difference: f64,
pub tolerance: f64,
pub target_crs: EntityId,
pub vertical_datum: Option<String>,
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq)]
pub enum SiteElevationCheck {
RefElevationAbsent,
NoMapConversion,
Consistent(SiteElevationComparison),
Disagreement(SiteElevationComparison),
}
pub fn relate_site_elevation(
site: &SiteReference,
map: Option<&ProjectToMap>,
site_origin_z: f64,
tolerance: f64,
) -> GeorefResult<SiteElevationCheck> {
if !tolerance.is_finite() || tolerance < 0.0 {
return Err(GeorefError::InvalidParameter {
name: "tolerance",
value: tolerance,
});
}
if !site_origin_z.is_finite() {
return Err(GeorefError::InvalidParameter {
name: "site_origin_z",
value: site_origin_z,
});
}
if site.version == SchemaVersion::Ifc2x3 && map.is_some() {
return Err(GeorefError::UnsupportedSchema {
token: "IFC2X3".to_owned(),
});
}
let Some(ref_elevation) = site.ref_elevation else {
return Ok(SiteElevationCheck::RefElevationAbsent);
};
let Some(map) = map else {
return Ok(SiteElevationCheck::NoMapConversion);
};
let orthogonal_height = map.map_point([0.0, 0.0, 0.0])[2];
let site_origin_map_height = map.map_point([0.0, 0.0, site_origin_z])[2];
let difference = ref_elevation - site_origin_map_height;
let comparison = SiteElevationComparison {
site: site.entity,
ref_elevation,
orthogonal_height,
site_origin_map_height,
difference,
tolerance,
target_crs: map.target_crs.entity,
vertical_datum: map.target_crs.vertical_datum.clone(),
};
Ok(if difference.abs() <= tolerance {
SiteElevationCheck::Consistent(comparison)
} else {
SiteElevationCheck::Disagreement(comparison)
})
}