use crate::{model::road_network::vertex_id::VertexId, util::geo::coord::InternalCoord};
use allocative::Allocative;
use geo::coord;
use serde::de;
#[derive(Copy, Clone, Default, Debug, Allocative)]
pub struct Vertex {
pub vertex_id: VertexId,
pub coordinate: InternalCoord<f32>,
}
impl Vertex {
pub fn new(vertex_id: usize, x: f32, y: f32) -> Self {
Self {
vertex_id: VertexId(vertex_id),
coordinate: InternalCoord(coord! {x: x, y: y}),
}
}
pub fn to_tuple_underlying(&self) -> (f32, f32) {
(self.coordinate.x, self.coordinate.y)
}
pub fn x(&self) -> f32 {
self.coordinate.x
}
pub fn y(&self) -> f32 {
self.coordinate.y
}
}
const VERTEX_ID: &str = "vertex_id";
const X_COORDINATE: &str = "x";
const Y_COORDINATE: &str = "y";
impl<'de> de::Deserialize<'de> for Vertex {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
struct VertexVisitor;
impl<'de> de::Visitor<'de> for VertexVisitor {
type Value = Vertex;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a vertex_id, x, and y field")
}
fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
where
V: de::MapAccess<'de>,
{
let mut vertex_id_result: Option<usize> = None;
let mut x_result: Option<f32> = None;
let mut y_result: Option<f32> = None;
let mut vertex_result: Option<Vertex> = None;
let mut next: Option<(&str, &str)> = map.next_entry()?;
while next.is_some() {
if let Some((key, value)) = next {
match key {
VERTEX_ID => {
let id: usize = value.parse().map_err(|e| {
de::Error::custom(format!(
"unable to parse vertex_id '{}': {}",
&value, e
))
})?;
vertex_id_result = Some(id);
}
X_COORDINATE => {
let x_coord: f32 = value.parse().map_err(|e| {
de::Error::custom(format!(
"unable to parse x '{}': {}",
&value, e
))
})?;
x_result = Some(x_coord);
}
Y_COORDINATE => {
let y_coord: f32 = value.parse().map_err(|e| {
de::Error::custom(format!(
"unable to parse y '{}': {}",
&value, e
))
})?;
y_result = Some(y_coord);
}
&_ => {} }
} else {
return Err(de::Error::custom("internal error"));
}
match vertex_id_result.zip(x_result).zip(y_result) {
Some(((vertex_id, x), y)) => {
vertex_result = Some(Vertex::new(vertex_id, x, y));
next = None;
}
None => {
next = map.next_entry()?;
}
}
}
match vertex_result {
None => Err(de::Error::custom("failed to deserialize Vertex")),
Some(vertex) => Ok(vertex),
}
}
}
deserializer.deserialize_map(VertexVisitor {})
}
}
#[cfg(test)]
mod tests {
use std::path::PathBuf;
use csv;
use geo::Coord;
use crate::{
model::{property::vertex::Vertex, road_network::vertex_id::VertexId},
util::geo::coord::InternalCoord,
};
#[test]
fn test_deserialize_csv() {
let filepath = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("src")
.join("model")
.join("property")
.join("test")
.join("vertices.csv");
let expected: Vec<Vertex> = vec![
Vertex {
vertex_id: VertexId(5),
coordinate: InternalCoord(Coord {
x: -105.20423,
y: 39.71221,
}),
},
Vertex {
vertex_id: VertexId(10),
coordinate: InternalCoord(Coord {
x: -105.22927,
y: 39.75842,
}),
},
Vertex {
vertex_id: VertexId(15),
coordinate: InternalCoord(Coord {
x: -105.19725,
y: 39.76073,
}),
},
];
let mut reader = csv::Reader::from_path(filepath).unwrap();
let mut result: Vec<Vertex> = vec![];
for row in reader.deserialize() {
let vertex: Vertex = row.unwrap();
result.push(vertex);
}
for idx in [0, 1, 2] {
assert_eq!(
result[idx].vertex_id, expected[idx].vertex_id,
"vertex ids didn't match expected"
);
assert_eq!(
result[idx].coordinate, expected[idx].coordinate,
"coordinate didn't match expected"
);
}
}
}