use energyml_core::value::Reflect;
use energyml_domain::ArraySource as ValueSource;
use energyml_domain::reproject::{self, System as DomainSystem};
use crate::model::System;
use crate::read::Scene;
#[derive(Clone, Debug, Default, PartialEq)]
pub struct ObjectFrame {
pub system: Option<System>,
pub time: bool,
}
pub fn frame_of(source: &dyn ValueSource, representation: &dyn Reflect) -> Result<ObjectFrame, String> {
let Some(crs) = energyml_domain::crs::of(representation, source).map_err(|why| format!("{why}"))? else {
return Ok(ObjectFrame::default());
};
let system = DomainSystem::of(&crs).ok().map(|found| System {
epsg: found.epsg,
unit: found.unit,
});
Ok(ObjectFrame {
system,
time: crs.is_time(),
})
}
pub fn to_scene(points: &mut [[f64; 3]], frame: &ObjectFrame, scene: &Scene<'_>) -> Result<(), String> {
let factor = match (frame.time, scene.time_factor) {
(false, _) => None,
(true, Some(factor)) => Some(factor),
(true, None) => {
return Err(
"the object is in time and the scene has no time factor (the metres of depth a unit of \
time is worth), so it cannot be carried onto the depth axis"
.to_string(),
);
}
};
match (&frame.system, scene.system) {
(None, None) => {}
(Some(own), None) => {
return Err(format!(
"the object is in EPSG:{} ({}) and the scene names no system: it would be drawn against \
coordinates it does not share",
own.epsg, own.unit
));
}
(None, Some(target)) => {
return Err(format!(
"the object names no horizontal system (no CRS, or a CRS with no EPSG code) and the scene \
is in EPSG:{}: there is nothing to reproject it from",
target.epsg
));
}
(Some(own), Some(target)) => {
if own != target {
let from = DomainSystem {
epsg: own.epsg,
unit: own.unit.clone(),
};
let to = DomainSystem {
epsg: target.epsg,
unit: target.unit.clone(),
};
let moved =
reproject::reproject_placed(points, &from, &to, scene.reprojector).map_err(|why| {
format!(
"reprojecting from EPSG:{} to EPSG:{}: {why}",
own.epsg, target.epsg
)
})?;
points.copy_from_slice(&moved);
}
}
}
if let Some(factor) = factor {
for point in points.iter_mut() {
point[2] *= factor;
}
}
Ok(())
}
pub fn first_system(source: &dyn ValueSource, uris: &[String]) -> Option<System> {
uris.iter().find_map(|uri| {
let object = source.object_at(uri)?;
frame_of(source, object.as_reflect()).ok()?.system
})
}