use axiolid_core::Transform3;
use crate::conversion::ProjectToMap;
use crate::error::{GeorefError, GeorefResult};
pub fn compose_project_frame(
operation: &ProjectToMap,
project_frame: Transform3,
) -> GeorefResult<Transform3> {
let determinant = project_frame.matrix3.determinant();
if !determinant.is_finite() || determinant.abs() <= f64::EPSILON {
return Err(GeorefError::DegenerateProjectFrame);
}
Ok(operation.transform * project_frame)
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use axiolid_core::{Mat3, Point3, Vec3};
use ifc_model::value::Value;
use ifc_model::{Entity, EntityId, Model};
use super::*;
use crate::conversion::resolve_project_to_map;
fn id(value: u64) -> EntityId {
EntityId(value)
}
fn r(value: u64) -> Value {
Value::Ref(id(value))
}
fn real(value: f64) -> Value {
Value::Real(value)
}
fn model_with_map_conversion() -> Model {
let mut model = Model::new();
model.insert(
id(1),
Entity::new("IFCGEOMETRICREPRESENTATIONCONTEXT", vec![]),
);
model.insert(
id(2),
Entity::new(
"IFCPROJECTEDCRS",
vec![Value::Text(Arc::from("EPSG:25832"))],
),
);
model.insert(
id(4),
Entity::new(
"IFCMAPCONVERSION",
vec![
r(1),
r(2),
real(1000.0),
real(2000.0),
real(50.0),
Value::Null,
Value::Null,
real(1.0),
],
),
);
model
}
#[test]
fn composes_a_translated_project_frame_into_map_coordinates() {
let model = model_with_map_conversion();
let operation = resolve_project_to_map(&model, id(4), 1.0).expect("resolves");
let project_frame =
Transform3::from_mat3_translation(Mat3::IDENTITY, Vec3::new(10.0, 20.0, 0.0));
let map_frame = compose_project_frame(&operation, project_frame).expect("composes");
let mapped = map_frame.transform_point3(Point3::ZERO);
assert_eq!(mapped, Vec3::new(1010.0, 2020.0, 50.0));
}
#[test]
fn refuses_a_singular_project_frame() {
let model = model_with_map_conversion();
let operation = resolve_project_to_map(&model, id(4), 1.0).expect("resolves");
let singular = Transform3::from_mat3_translation(
Mat3::from_cols(
Vec3::new(1.0, 0.0, 0.0),
Vec3::ZERO,
Vec3::new(0.0, 0.0, 1.0),
),
Vec3::ZERO,
);
assert!(matches!(
compose_project_frame(&operation, singular),
Err(GeorefError::DegenerateProjectFrame)
));
}
#[test]
fn composition_order_applies_the_project_frame_before_the_map_operation() {
let model = model_with_map_conversion();
let operation = resolve_project_to_map(&model, id(4), 1.0).expect("resolves");
let rotated = Transform3::from_mat3_translation(
Mat3::from_cols(Vec3::new(0.0, 1.0, 0.0), Vec3::new(-1.0, 0.0, 0.0), Vec3::Z),
Vec3::new(5.0, 0.0, 0.0),
);
let map_frame = compose_project_frame(&operation, rotated).expect("composes");
let mapped = map_frame.transform_point3(Point3::new(1.0, 0.0, 0.0));
assert_eq!(mapped, Vec3::new(1005.0, 2001.0, 50.0));
}
}