use crate::types::basic::{Double, Int, OSString};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[derive(Default)]
pub struct Orientation {
#[serde(rename = "@h", skip_serializing_if = "Option::is_none")]
pub h: Option<Double>,
#[serde(rename = "@p", skip_serializing_if = "Option::is_none")]
pub p: Option<Double>,
#[serde(rename = "@r", skip_serializing_if = "Option::is_none")]
pub r: Option<Double>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct RoadPosition {
#[serde(rename = "@roadId")]
pub road_id: OSString,
#[serde(rename = "@s")]
pub s: Double,
#[serde(rename = "@t")]
pub t: Double,
#[serde(rename = "Orientation", skip_serializing_if = "Option::is_none")]
pub orientation: Option<Orientation>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct LanePosition {
#[serde(rename = "@roadId")]
pub road_id: OSString,
#[serde(rename = "@laneId")]
pub lane_id: OSString,
#[serde(rename = "@s")]
pub s: Double,
#[serde(rename = "@offset")]
pub offset: Double,
#[serde(rename = "Orientation", skip_serializing_if = "Option::is_none")]
pub orientation: Option<Orientation>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct RelativeRoadPosition {
#[serde(rename = "@entityRef")]
pub entity_ref: OSString,
#[serde(rename = "@ds")]
pub ds: Double,
#[serde(rename = "@dt")]
pub dt: Double,
#[serde(rename = "Orientation", skip_serializing_if = "Option::is_none")]
pub orientation: Option<Orientation>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct RelativeLanePosition {
#[serde(rename = "@entityRef")]
pub entity_ref: OSString,
#[serde(rename = "@dLane")]
pub d_lane: Int,
#[serde(rename = "@ds")]
pub ds: Double,
#[serde(rename = "@offset")]
pub offset: Double,
#[serde(rename = "Orientation", skip_serializing_if = "Option::is_none")]
pub orientation: Option<Orientation>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct RoadCoordinate {
#[serde(rename = "@s")]
pub s: Double,
#[serde(rename = "@t")]
pub t: Double,
#[serde(rename = "@h", skip_serializing_if = "Option::is_none")]
pub h: Option<Double>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct LaneCoordinate {
#[serde(rename = "@s")]
pub s: Double,
#[serde(rename = "@offset")]
pub offset: Double,
#[serde(rename = "@h", skip_serializing_if = "Option::is_none")]
pub h: Option<Double>,
}
impl RoadPosition {
pub fn new(road_id: String, s: f64, t: f64) -> Self {
Self {
road_id: OSString::literal(road_id),
s: Double::literal(s),
t: Double::literal(t),
orientation: None,
}
}
pub fn with_orientation(road_id: String, s: f64, t: f64, orientation: Orientation) -> Self {
Self {
road_id: OSString::literal(road_id),
s: Double::literal(s),
t: Double::literal(t),
orientation: Some(orientation),
}
}
}
impl LanePosition {
pub fn new(road_id: String, lane_id: String, s: f64, offset: f64) -> Self {
Self {
road_id: OSString::literal(road_id),
lane_id: OSString::literal(lane_id),
s: Double::literal(s),
offset: Double::literal(offset),
orientation: None,
}
}
pub fn with_orientation(
road_id: String,
lane_id: String,
s: f64,
offset: f64,
orientation: Orientation,
) -> Self {
Self {
road_id: OSString::literal(road_id),
lane_id: OSString::literal(lane_id),
s: Double::literal(s),
offset: Double::literal(offset),
orientation: Some(orientation),
}
}
}
impl RelativeRoadPosition {
pub fn new(entity_ref: String, ds: f64, dt: f64) -> Self {
Self {
entity_ref: OSString::literal(entity_ref),
ds: Double::literal(ds),
dt: Double::literal(dt),
orientation: None,
}
}
pub fn with_orientation(
entity_ref: String,
ds: f64,
dt: f64,
orientation: Orientation,
) -> Self {
Self {
entity_ref: OSString::literal(entity_ref),
ds: Double::literal(ds),
dt: Double::literal(dt),
orientation: Some(orientation),
}
}
}
impl RelativeLanePosition {
pub fn new(entity_ref: String, d_lane: i32, ds: f64, offset: f64) -> Self {
Self {
entity_ref: OSString::literal(entity_ref),
d_lane: Int::literal(d_lane),
ds: Double::literal(ds),
offset: Double::literal(offset),
orientation: None,
}
}
pub fn with_orientation(
entity_ref: String,
d_lane: i32,
ds: f64,
offset: f64,
orientation: Orientation,
) -> Self {
Self {
entity_ref: OSString::literal(entity_ref),
d_lane: Int::literal(d_lane),
ds: Double::literal(ds),
offset: Double::literal(offset),
orientation: Some(orientation),
}
}
}
impl RoadCoordinate {
pub fn new(s: f64, t: f64) -> Self {
Self {
s: Double::literal(s),
t: Double::literal(t),
h: None,
}
}
pub fn with_height(s: f64, t: f64, h: f64) -> Self {
Self {
s: Double::literal(s),
t: Double::literal(t),
h: Some(Double::literal(h)),
}
}
pub fn center_line(s: f64) -> Self {
Self::new(s, 0.0)
}
pub fn with_offset(s: f64, t: f64) -> Self {
Self::new(s, t)
}
}
impl LaneCoordinate {
pub fn new(s: f64, offset: f64) -> Self {
Self {
s: Double::literal(s),
offset: Double::literal(offset),
h: None,
}
}
pub fn with_height(s: f64, offset: f64, h: f64) -> Self {
Self {
s: Double::literal(s),
offset: Double::literal(offset),
h: Some(Double::literal(h)),
}
}
pub fn center_line(s: f64) -> Self {
Self::new(s, 0.0)
}
pub fn with_offset(s: f64, offset: f64) -> Self {
Self::new(s, offset)
}
}
impl Orientation {
pub fn heading(h: f64) -> Self {
Self {
h: Some(Double::literal(h)),
p: None,
r: None,
}
}
pub fn new(h: f64, p: f64, r: f64) -> Self {
Self {
h: Some(Double::literal(h)),
p: Some(Double::literal(p)),
r: Some(Double::literal(r)),
}
}
}
impl Default for RoadCoordinate {
fn default() -> Self {
Self {
s: Double::literal(0.0),
t: Double::literal(0.0),
h: None,
}
}
}
impl Default for LaneCoordinate {
fn default() -> Self {
Self {
s: Double::literal(0.0),
offset: Double::literal(0.0),
h: None,
}
}
}
impl Default for RelativeRoadPosition {
fn default() -> Self {
Self {
entity_ref: OSString::literal("DefaultEntity".to_string()),
ds: Double::literal(0.0),
dt: Double::literal(0.0),
orientation: None,
}
}
}
impl Default for RelativeLanePosition {
fn default() -> Self {
Self {
entity_ref: OSString::literal("DefaultEntity".to_string()),
d_lane: Int::literal(0),
ds: Double::literal(0.0),
offset: Double::literal(0.0),
orientation: None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_road_position_creation() {
let pos = RoadPosition::new("0".to_string(), 100.0, 2.5);
assert_eq!(pos.road_id.as_literal().unwrap(), "0");
assert_eq!(pos.s.as_literal().unwrap(), &100.0);
assert_eq!(pos.t.as_literal().unwrap(), &2.5);
assert!(pos.orientation.is_none());
}
#[test]
fn test_lane_position_creation() {
let pos = LanePosition::new("0".to_string(), "-4".to_string(), 5.0, 0.0);
assert_eq!(pos.road_id.as_literal().unwrap(), "0");
assert_eq!(pos.lane_id.as_literal().unwrap(), "-4");
assert_eq!(pos.s.as_literal().unwrap(), &5.0);
assert_eq!(pos.offset.as_literal().unwrap(), &0.0);
assert!(pos.orientation.is_none());
}
#[test]
fn test_orientation_creation() {
let orientation = Orientation::heading(1.57);
assert_eq!(orientation.h.as_ref().unwrap().as_literal().unwrap(), &1.57);
assert!(orientation.p.is_none());
assert!(orientation.r.is_none());
}
#[test]
fn test_road_position_with_orientation() {
let orientation = Orientation::heading(0.5);
let pos = RoadPosition::with_orientation("1".to_string(), 50.0, -1.0, orientation);
assert_eq!(pos.road_id.as_literal().unwrap(), "1");
assert_eq!(pos.s.as_literal().unwrap(), &50.0);
assert_eq!(pos.t.as_literal().unwrap(), &-1.0);
assert!(pos.orientation.is_some());
assert_eq!(
pos.orientation.unwrap().h.unwrap().as_literal().unwrap(),
&0.5
);
}
#[test]
fn test_lane_position_serialization() {
let pos = LanePosition::new("0".to_string(), "-4".to_string(), 5.0, 0.0);
let xml = quick_xml::se::to_string(&pos).unwrap();
assert!(xml.contains("LanePosition"));
assert!(xml.contains("roadId=\"0\""));
assert!(xml.contains("laneId=\"-4\""));
assert!(xml.contains("s=\"5\""));
assert!(xml.contains("offset=\"0\""));
}
#[test]
fn test_relative_road_position_creation() {
let pos = RelativeRoadPosition::new("EgoVehicle".to_string(), 10.0, -2.0);
assert_eq!(pos.entity_ref.as_literal().unwrap(), "EgoVehicle");
assert_eq!(pos.ds.as_literal().unwrap(), &10.0);
assert_eq!(pos.dt.as_literal().unwrap(), &-2.0);
assert!(pos.orientation.is_none());
}
#[test]
fn test_relative_road_position_with_orientation() {
let orientation = Orientation::heading(0.5);
let pos =
RelativeRoadPosition::with_orientation("EgoVehicle".to_string(), 5.0, 1.5, orientation);
assert_eq!(pos.entity_ref.as_literal().unwrap(), "EgoVehicle");
assert_eq!(pos.ds.as_literal().unwrap(), &5.0);
assert_eq!(pos.dt.as_literal().unwrap(), &1.5);
assert!(pos.orientation.is_some());
assert_eq!(
pos.orientation.unwrap().h.unwrap().as_literal().unwrap(),
&0.5
);
}
#[test]
fn test_relative_lane_position_creation() {
let pos = RelativeLanePosition::new("EgoVehicle".to_string(), -1, 15.0, 0.5);
assert_eq!(pos.entity_ref.as_literal().unwrap(), "EgoVehicle");
assert_eq!(pos.d_lane, Int::literal(-1));
assert_eq!(pos.ds.as_literal().unwrap(), &15.0);
assert_eq!(pos.offset.as_literal().unwrap(), &0.5);
assert!(pos.orientation.is_none());
}
#[test]
fn test_relative_lane_position_with_orientation() {
let orientation = Orientation::new(1.57, 0.0, 0.0);
let pos = RelativeLanePosition::with_orientation(
"EgoVehicle".to_string(),
1,
20.0,
-1.0,
orientation,
);
assert_eq!(pos.entity_ref.as_literal().unwrap(), "EgoVehicle");
assert_eq!(pos.d_lane, Int::literal(1));
assert_eq!(pos.ds.as_literal().unwrap(), &20.0);
assert_eq!(pos.offset.as_literal().unwrap(), &-1.0);
assert!(pos.orientation.is_some());
let orient = pos.orientation.unwrap();
assert_eq!(orient.h.unwrap().as_literal().unwrap(), &1.57);
assert_eq!(orient.p.unwrap().as_literal().unwrap(), &0.0);
assert_eq!(orient.r.unwrap().as_literal().unwrap(), &0.0);
}
#[test]
fn test_relative_road_position_serialization() {
let pos = RelativeRoadPosition::new("EgoVehicle".to_string(), 10.0, -2.0);
let xml = quick_xml::se::to_string(&pos).unwrap();
assert!(xml.contains("RelativeRoadPosition"));
assert!(xml.contains("entityRef=\"EgoVehicle\""));
assert!(xml.contains("ds=\"10\""));
assert!(xml.contains("dt=\"-2\""));
}
#[test]
fn test_relative_lane_position_serialization() {
let pos = RelativeLanePosition::new("EgoVehicle".to_string(), -1, 15.0, 0.5);
let xml = quick_xml::se::to_string(&pos).unwrap();
assert!(xml.contains("RelativeLanePosition"));
assert!(xml.contains("entityRef=\"EgoVehicle\""));
assert!(xml.contains("dLane=\"-1\""));
assert!(xml.contains("ds=\"15\""));
assert!(xml.contains("offset=\"0.5\""));
}
#[test]
fn test_relative_position_defaults() {
let rel_road = RelativeRoadPosition::default();
assert_eq!(rel_road.entity_ref.as_literal().unwrap(), "DefaultEntity");
assert_eq!(rel_road.ds.as_literal().unwrap(), &0.0);
assert_eq!(rel_road.dt.as_literal().unwrap(), &0.0);
let rel_lane = RelativeLanePosition::default();
assert_eq!(rel_lane.entity_ref.as_literal().unwrap(), "DefaultEntity");
assert_eq!(rel_lane.d_lane, Int::literal(0));
assert_eq!(rel_lane.ds.as_literal().unwrap(), &0.0);
assert_eq!(rel_lane.offset.as_literal().unwrap(), &0.0);
}
}