use ifc_lite_core::GeoReference;
use ifc_lite_processing::extract_georeferencing;
const FIXTURE: &str = "../../tests/models/ifc5/Georeferencing_georeferenced-bridge-deck.ifc";
const EASTINGS: f64 = 545991.679663973;
const NORTHINGS: f64 = 4184941.96970872;
const ORTHO_HEIGHT: f64 = 0.0;
const X_AXIS_ABSCISSA: f64 = -0.0977396728779572; const X_AXIS_ORDINATE: f64 = 0.995212015776392; const SCALE: f64 = 0.9996;
fn read_fixture() -> Option<String> {
match std::fs::read_to_string(FIXTURE) {
Ok(s) => Some(s),
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
eprintln!(
"skipping georef map-conversion math test: fixture missing at {FIXTURE} — \
run `pnpm fixtures` to fetch it"
);
None
}
Err(e) => panic!("failed to read fixture {FIXTURE}: {e}"),
}
}
#[test]
fn map_conversion_transforms_local_point_to_expected_world_coordinate() {
let Some(content) = read_fixture() else {
return;
};
let geo = extract_georeferencing(content.as_bytes())
.expect("bridge-deck fixture must yield georeferencing (IfcMapConversion #38)");
assert!((geo.eastings - EASTINGS).abs() < 1e-6);
assert!((geo.northings - NORTHINGS).abs() < 1e-6);
assert!((geo.orthogonal_height - ORTHO_HEIGHT).abs() < 1e-9);
assert!((geo.x_axis_abscissa - X_AXIS_ABSCISSA).abs() < 1e-12);
assert!((geo.x_axis_ordinate - X_AXIS_ORDINATE).abs() < 1e-12);
assert!((geo.scale - SCALE).abs() < 1e-12);
let norm =
geo.x_axis_abscissa * geo.x_axis_abscissa + geo.x_axis_ordinate * geo.x_axis_ordinate;
assert!(
(norm - 1.0).abs() < 1e-9,
"X-axis direction not unit length: {norm}"
);
const LOCAL: (f64, f64, f64) = (100.0, 50.0, 5.0);
const EXPECTED_E: f64 = 545932.168909724;
const EXPECTED_N: f64 = 4185036.566072967;
const EXPECTED_H: f64 = 4.998;
let core_geo = GeoReference {
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,
..GeoReference::new()
};
let (e, n, h) = core_geo.local_to_map(LOCAL.0, LOCAL.1, LOCAL.2);
assert!(
(e - EXPECTED_E).abs() < 1e-6,
"eastings: got {e}, expected {EXPECTED_E}"
);
assert!(
(n - EXPECTED_N).abs() < 1e-6,
"northings: got {n}, expected {EXPECTED_N}"
);
assert!(
(h - EXPECTED_H).abs() < 1e-9,
"height: got {h}, expected {EXPECTED_H}"
);
let m = &geo.transform_matrix;
let me = m[0] * LOCAL.0 + m[4] * LOCAL.1 + m[8] * LOCAL.2 + m[12];
let mn = m[1] * LOCAL.0 + m[5] * LOCAL.1 + m[9] * LOCAL.2 + m[13];
let mh = m[2] * LOCAL.0 + m[6] * LOCAL.1 + m[10] * LOCAL.2 + m[14];
assert!((me - EXPECTED_E).abs() < 1e-6, "matrix eastings: got {me}");
assert!((mn - EXPECTED_N).abs() < 1e-6, "matrix northings: got {mn}");
assert!((mh - EXPECTED_H).abs() < 1e-9, "matrix height: got {mh}");
let (rx, ry, rz) = core_geo.map_to_local(e, n, h);
assert!((rx - LOCAL.0).abs() < 1e-6, "round-trip x: got {rx}");
assert!((ry - LOCAL.1).abs() < 1e-6, "round-trip y: got {ry}");
assert!((rz - LOCAL.2).abs() < 1e-9, "round-trip z: got {rz}");
assert!(
(geo.rotation_degrees - 95.6090255829533).abs() < 1e-9,
"rotation_degrees: got {}",
geo.rotation_degrees
);
}