use chrono::Utc;
use geo::LineString;
use tp_lib_core::models::{GnssPosition, NetRelation, Netelement};
use tp_lib_core::{calculate_train_path, PathConfig};
fn create_gnss(lat: f64, lon: f64, heading: Option<f64>) -> GnssPosition {
let mut gnss = GnssPosition::new(lat, lon, Utc::now().into(), "EPSG:4326".to_string()).unwrap();
gnss.heading = heading;
gnss
}
fn create_netelement(id: &str, coords: Vec<(f64, f64)>) -> Netelement {
Netelement::new(
id.to_string(),
LineString::from(coords),
"EPSG:4326".to_string(),
)
.unwrap()
}
#[test]
fn test_calculate_path_single_netelement_multiple_positions() {
use tp_lib_core::project_onto_path;
let netelements = vec![create_netelement(
"NE1",
vec![(4.350, 50.850), (4.360, 50.860)],
)];
let gnss = vec![
create_gnss(50.851, 4.351, None),
create_gnss(50.853, 4.353, None),
create_gnss(50.855, 4.355, None),
create_gnss(50.857, 4.357, None),
];
let netrelations = vec![];
let config = PathConfig::builder()
.cutoff_distance(500.0) .probability_threshold(0.0) .build()
.unwrap();
let result = calculate_train_path(&gnss, &netelements, &netrelations, &config);
assert!(result.is_ok());
let path_result = result.unwrap();
assert!(path_result.path.is_some(), "Expected path to be calculated");
let train_path = path_result.path.unwrap();
let projected = project_onto_path(&gnss, &train_path, &netelements, &config);
assert!(
projected.is_ok(),
"Projection failed: {:?}",
projected.err()
);
assert!(
!projected.unwrap().is_empty(),
"Expected projected positions"
);
}
#[test]
fn test_calculate_path_with_heading_constraints() {
let netelements = vec![
create_netelement("NE1", vec![(4.350, 50.850), (4.360, 50.850)]), create_netelement("NE2", vec![(4.350, 50.850), (4.350, 50.860)]), ];
let gnss = vec![
create_gnss(50.850, 4.351, Some(90.0)),
create_gnss(50.850, 4.353, Some(90.0)),
];
let netrelations = vec![];
let config = PathConfig::builder().heading_cutoff(45.0).build().unwrap();
let result = calculate_train_path(&gnss, &netelements, &netrelations, &config);
assert!(result.is_ok());
}
#[test]
fn test_calculate_path_branching_network() {
let netelements = vec![
create_netelement("NE1", vec![(4.350, 50.850), (4.355, 50.855)]),
create_netelement("NE2", vec![(4.355, 50.855), (4.360, 50.860)]),
create_netelement("NE3", vec![(4.355, 50.855), (4.360, 50.850)]),
];
let netrelations = vec![
NetRelation::new(
"NR1".to_string(),
"NE1".to_string(),
"NE2".to_string(),
1,
0,
true,
false,
)
.unwrap(),
NetRelation::new(
"NR2".to_string(),
"NE1".to_string(),
"NE3".to_string(),
1,
0,
true,
false,
)
.unwrap(),
];
let gnss = vec![
create_gnss(50.851, 4.351, None),
create_gnss(50.856, 4.356, None),
];
let config = PathConfig::default();
let result = calculate_train_path(&gnss, &netelements, &netrelations, &config);
assert!(result.is_ok());
}
#[test]
fn test_calculate_path_circular_network() {
let netelements = vec![
create_netelement("NE1", vec![(4.350, 50.850), (4.355, 50.850)]),
create_netelement("NE2", vec![(4.355, 50.850), (4.355, 50.855)]),
create_netelement("NE3", vec![(4.355, 50.855), (4.350, 50.855)]),
create_netelement("NE4", vec![(4.350, 50.855), (4.350, 50.850)]),
];
let netrelations = vec![
NetRelation::new(
"NR1".to_string(),
"NE1".to_string(),
"NE2".to_string(),
1,
0,
true,
false,
)
.unwrap(),
NetRelation::new(
"NR2".to_string(),
"NE2".to_string(),
"NE3".to_string(),
1,
0,
true,
false,
)
.unwrap(),
NetRelation::new(
"NR3".to_string(),
"NE3".to_string(),
"NE4".to_string(),
1,
0,
true,
false,
)
.unwrap(),
NetRelation::new(
"NR4".to_string(),
"NE4".to_string(),
"NE1".to_string(),
1,
0,
true,
false,
)
.unwrap(),
];
let gnss = vec![
create_gnss(50.850, 4.352, None),
create_gnss(50.852, 4.355, None),
create_gnss(50.855, 4.353, None),
];
let config = PathConfig::default();
let result = calculate_train_path(&gnss, &netelements, &netrelations, &config);
assert!(result.is_ok());
}
#[test]
fn test_calculate_path_max_candidates_limiting() {
let netelements = vec![
create_netelement("NE1", vec![(4.350, 50.850), (4.351, 50.851)]),
create_netelement("NE2", vec![(4.3501, 50.8501), (4.3511, 50.8511)]),
create_netelement("NE3", vec![(4.3502, 50.8502), (4.3512, 50.8512)]),
create_netelement("NE4", vec![(4.3503, 50.8503), (4.3513, 50.8513)]),
create_netelement("NE5", vec![(4.3504, 50.8504), (4.3514, 50.8514)]),
];
let gnss = vec![create_gnss(50.8502, 4.3502, None)];
let netrelations = vec![];
let config = PathConfig::builder()
.max_candidates(2)
.cutoff_distance(500.0)
.build()
.unwrap();
let result = calculate_train_path(&gnss, &netelements, &netrelations, &config);
assert!(result.is_ok());
}
#[test]
fn test_calculate_path_very_sparse_gnss() {
let netelements = vec![create_netelement(
"NE1",
vec![(4.350, 50.850), (4.365, 50.865)],
)];
let gnss = vec![
create_gnss(50.851, 4.351, None),
create_gnss(50.864, 4.364, None),
];
let netrelations = vec![];
let config = PathConfig::default();
let result = calculate_train_path(&gnss, &netelements, &netrelations, &config);
assert!(result.is_ok());
}
#[test]
fn test_calculate_path_dense_gnss_with_resampling() {
let netelements = vec![create_netelement(
"NE1",
vec![(4.350, 50.850), (4.360, 50.860)],
)];
let mut gnss = vec![];
for i in 0..20 {
let offset = i as f64 * 0.0005;
gnss.push(create_gnss(50.850 + offset, 4.350 + offset, None));
}
let netrelations = vec![];
let config = PathConfig::builder()
.resampling_distance(Some(50.0))
.build()
.unwrap();
let result = calculate_train_path(&gnss, &netelements, &netrelations, &config);
assert!(result.is_ok());
}
#[test]
fn test_calculate_path_unidirectional_netrelations() {
let netelements = vec![
create_netelement("NE1", vec![(4.350, 50.850), (4.355, 50.855)]),
create_netelement("NE2", vec![(4.355, 50.855), (4.360, 50.860)]),
];
let netrelations = vec![NetRelation::new(
"NR1".to_string(),
"NE1".to_string(),
"NE2".to_string(),
1,
0,
true,
false,
)
.unwrap()];
let gnss = vec![
create_gnss(50.851, 4.351, None),
create_gnss(50.856, 4.356, None),
];
let config = PathConfig::default();
let result = calculate_train_path(&gnss, &netelements, &netrelations, &config);
assert!(result.is_ok());
}
#[test]
fn test_calculate_path_all_modes_combined() {
let netelements = vec![
create_netelement("NE1", vec![(4.350, 50.850), (4.355, 50.855)]),
create_netelement("NE2", vec![(4.355, 50.855), (4.360, 50.860)]),
];
let netrelations = vec![NetRelation::new(
"NR1".to_string(),
"NE1".to_string(),
"NE2".to_string(),
1,
0,
true,
true,
)
.unwrap()];
let gnss = vec![
create_gnss(50.851, 4.351, Some(45.0)),
create_gnss(50.856, 4.356, Some(45.0)),
];
let config = PathConfig::builder()
.distance_scale(12.0)
.heading_scale(2.5)
.cutoff_distance(60.0)
.heading_cutoff(15.0)
.probability_threshold(0.2)
.max_candidates(4)
.resampling_distance(Some(25.0))
.path_only(false)
.debug_mode(true)
.build()
.unwrap();
let result = calculate_train_path(&gnss, &netelements, &netrelations, &config);
assert!(result.is_ok());
let path_result = result.unwrap();
assert!(path_result.debug_info.is_some());
}