use std::sync::Arc;
use super::map_error::MapError;
use crate::{
model::network::{EdgeId, EdgeListId, Graph},
util::{fs::read_utils, geo::geo_io_utils},
};
use geo::LineString;
use kdam::{Bar, BarExt};
pub struct GeometryModel(Vec<LineString<f32>>);
impl GeometryModel {
pub fn new_from_vertices(
graph: Arc<Graph>,
edge_list_id: EdgeListId,
) -> Result<GeometryModel, MapError> {
let edges = create_linestrings_from_vertices(graph, edge_list_id)?;
Ok(GeometryModel(edges))
}
pub fn new_from_edges(
geometry_input_file: &String,
edge_list_id: EdgeListId,
graph: Arc<Graph>,
) -> Result<GeometryModel, MapError> {
let edge_list = graph.get_edge_list(&edge_list_id).map_err(|e| {
MapError::BuildError(format!(
"while creating GeometryModel for input file {geometry_input_file}, {e}"
))
})?;
let edge_list_len = edge_list.len();
let linestrings = read_linestrings(geometry_input_file, edge_list_len)?;
if linestrings.len() != edge_list_len {
Err(MapError::BuildError(format!("edge list {edge_list_id} geometry file {geometry_input_file} should have {edge_list_len} rows, found {}", linestrings.len())))
} else {
Ok(GeometryModel(linestrings))
}
}
pub fn geometries<'a>(&'a self) -> Box<dyn Iterator<Item = &'a LineString<f32>> + 'a> {
Box::new(self.0.iter())
}
pub fn get<'a>(&'a self, edge_id: &EdgeId) -> Option<&'a LineString<f32>> {
self.0.get(edge_id.0)
}
}
fn read_linestrings(
geometry_input_file: &String,
n_edges: usize,
) -> Result<Vec<geo::LineString<f32>>, MapError> {
let geoms = read_utils::read_raw_file(
geometry_input_file,
geo_io_utils::parse_wkt_linestring,
Some(Bar::builder().desc("link geometries").total(n_edges)),
None,
)
.map_err(|e: std::io::Error| {
MapError::BuildError(format!("error loading {geometry_input_file}: {e}"))
})?
.to_vec();
eprintln!();
Ok(geoms)
}
fn create_linestrings_from_vertices(
graph: Arc<Graph>,
edge_list_id: EdgeListId,
) -> Result<Vec<LineString<f32>>, MapError> {
let edge_list = graph.get_edge_list(&edge_list_id).map_err(|e| {
MapError::BuildError(format!("while creating GeometryModel from vertices, {e}"))
})?;
let n_edges = edge_list.len();
let mut pb = kdam::Bar::builder()
.total(n_edges)
.animation("fillup")
.desc("edge LineString geometry file")
.build()
.map_err(MapError::InternalError)?;
let edges = edge_list
.edges()
.map(|e| {
let src_v = graph.get_vertex(&e.src_vertex_id).map_err(|_| {
MapError::InternalError(format!(
"edge {} src vertex {} missing",
e.edge_id, e.src_vertex_id
))
})?;
let dst_v = graph.get_vertex(&e.dst_vertex_id).map_err(|_| {
MapError::InternalError(format!(
"edge {} dst vertex {} missing",
e.edge_id, e.dst_vertex_id
))
})?;
let linestring = geo::line_string![src_v.coordinate.0, dst_v.coordinate.0,];
let _ = pb.update(1);
Ok(linestring)
})
.collect::<Result<Vec<_>, MapError>>()?;
eprintln!();
Ok(edges)
}
#[cfg(test)]
mod tests {
use crate::util::{fs::read_utils::read_raw_file, geo::geo_io_utils::parse_wkt_linestring};
use std::path::PathBuf;
fn mock_geometry_file() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("src")
.join("model")
.join("map")
.join("test")
.join("geometries.txt")
}
#[test]
fn test_geometry_deserialization() {
let result = read_raw_file(mock_geometry_file(), parse_wkt_linestring, None, None).unwrap();
assert_eq!(result.len(), 3);
}
}