Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion rust/bambam-omf/src/collection/filter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ mod travel_mode_filter;
pub use bbox::Bbox;
pub use row_filter::RowFilter;
pub use row_filter_config::RowFilterConfig;
pub use travel_mode_filter::TravelModeFilter;
pub use travel_mode_filter::{MatchBehavior, TravelModeFilter};
9 changes: 5 additions & 4 deletions rust/bambam-omf/src/collection/record/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ pub use record_type::OvertureRecordType;
pub use transportation_collection::TransportationCollection;
pub use transportation_connector::TransportationConnectorRecord;
pub use transportation_segment::{
SegmentAccessRestriction, SegmentAccessRestrictionWhen, SegmentAccessType, SegmentClass,
SegmentDestination, SegmentFullType, SegmentHeading, SegmentMode, SegmentRecognized,
SegmentSpeedLimit, SegmentSpeedUnit, SegmentSubclass, SegmentSubtype, SegmentUsing,
TransportationSegmentRecord,
SegmentAccessRestriction, SegmentAccessRestrictionWhen, SegmentAccessRestrictionWhenVehicle,
SegmentAccessType, SegmentClass, SegmentDestination, SegmentFullType, SegmentHeading,
SegmentLengthUnit, SegmentMode, SegmentRecognized, SegmentSpeedLimit, SegmentSpeedUnit,
SegmentSubclass, SegmentSubtype, SegmentUnit, SegmentUsing, SegmentVehicleComparator,
SegmentVehicleDimension, TransportationSegmentRecord,
};

// Common structs and functions for many record types
Expand Down
98 changes: 94 additions & 4 deletions rust/bambam-omf/src/collection/record/transportation_segment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,18 @@ pub enum SegmentVehicleComparator {
LessThanEqual,
}

impl SegmentVehicleComparator {
pub fn apply(&self, value: f64, restriction: f64) -> bool {
match self {
SegmentVehicleComparator::GreaterThan => value > restriction,
SegmentVehicleComparator::GreaterThanEqual => value >= restriction,
SegmentVehicleComparator::Equal => value == restriction,
SegmentVehicleComparator::LessThan => value < restriction,
SegmentVehicleComparator::LessThanEqual => value <= restriction,
}
}
}

/// units in vehicle restrictions which may be length or weight units.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
#[serde(untagged)]
Expand All @@ -447,13 +459,52 @@ pub enum SegmentLengthUnit {
Kilometer,
}

impl SegmentLengthUnit {
pub fn to_uom(&self, value: f64) -> uom::si::f64::Length {
match self {
SegmentLengthUnit::Inches => uom::si::f64::Length::new::<uom::si::length::inch>(value),
SegmentLengthUnit::Feet => uom::si::f64::Length::new::<uom::si::length::foot>(value),
SegmentLengthUnit::Yard => uom::si::f64::Length::new::<uom::si::length::yard>(value),
SegmentLengthUnit::Mile => uom::si::f64::Length::new::<uom::si::length::mile>(value),
SegmentLengthUnit::Centimeter => {
uom::si::f64::Length::new::<uom::si::length::centimeter>(value)
}
SegmentLengthUnit::Meter => uom::si::f64::Length::new::<uom::si::length::meter>(value),
SegmentLengthUnit::Kilometer => {
uom::si::f64::Length::new::<uom::si::length::kilometer>(value)
}
}
}
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
#[serde(untagged)]
pub enum SegmentWeightUnit {
Imperial(SegmentImperialWeightUnit),
Metric(SegmentMetricWeightUnit),
}

impl SegmentWeightUnit {
pub fn to_uom(&self, value: f64) -> uom::si::f64::Mass {
use SegmentImperialWeightUnit as I;
use SegmentMetricWeightUnit as M;
use SegmentWeightUnit as SWU;

match self {
SWU::Imperial(I::Ounce) => uom::si::f64::Mass::new::<uom::si::mass::ounce>(value),
SWU::Imperial(I::Pound) => uom::si::f64::Mass::new::<uom::si::mass::pound>(value),
// Couldn't find "Stone" so we use the transformation to Kg
SWU::Imperial(I::Stone) => {
uom::si::f64::Mass::new::<uom::si::mass::kilogram>(value * 6.350288)
}
SWU::Imperial(I::LongTon) => uom::si::f64::Mass::new::<uom::si::mass::ton_long>(value),
SWU::Metric(M::Kilogram) => uom::si::f64::Mass::new::<uom::si::mass::kilogram>(value),
SWU::Metric(M::Gram) => uom::si::f64::Mass::new::<uom::si::mass::gram>(value),
SWU::Metric(M::MetricTon) => uom::si::f64::Mass::new::<uom::si::mass::ton>(value),
}
}
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
#[serde(rename_all = "snake_case")]
pub enum SegmentImperialWeightUnit {
Expand Down Expand Up @@ -625,11 +676,50 @@ impl SegmentAccessRestrictionWhen {

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct SegmentAccessRestrictionWhenVehicle {
dimension: SegmentVehicleDimension,
comparison: SegmentVehicleComparator,
value: f64,
pub dimension: SegmentVehicleDimension,
pub comparison: SegmentVehicleComparator,
pub value: f64,
#[serde(skip_serializing_if = "Option::is_none", default)]
unit: Option<SegmentUnit>,
pub unit: Option<SegmentUnit>,
}

impl SegmentAccessRestrictionWhenVehicle {
/// returns true if the when provided would pass the restriction
/// based on the comparison logic
pub fn is_valid(&self, when: &SegmentAccessRestrictionWhenVehicle) -> bool {
use SegmentUnit as SU;

if when.dimension != self.dimension {
return false;
}

match (&self.unit, &when.unit) {
(Some(SU::Length(this_unit)), Some(SU::Length(other_unit))) => {
let this_value_f64 = this_unit.to_uom(self.value).get::<uom::si::length::meter>();
let other_value_f64 = other_unit
.to_uom(when.value)
.get::<uom::si::length::meter>();
self.comparison.apply(other_value_f64, this_value_f64)
}
(Some(SU::Weight(this_unit)), Some(SU::Weight(other_unit))) => {
let this_value_f64 = this_unit
.to_uom(self.value)
.get::<uom::si::mass::kilogram>();
let other_value_f64 = other_unit
.to_uom(when.value)
.get::<uom::si::mass::kilogram>();
self.comparison.apply(other_value_f64, this_value_f64)
}

// Should be handled by the if statement checking the dimension but
// just to be sure
(Some(SU::Weight(_)), Some(SU::Length(_))) => false,
(Some(SU::Length(_)), Some(SU::Weight(_))) => false,

// If we miss any unit, check the raw values
_ => self.comparison.apply(when.value, self.value),
}
}
}

/// Describes objects that can be reached by following a transportation
Expand Down
138 changes: 136 additions & 2 deletions rust/bambam-omf/src/graph/segment_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,15 +219,38 @@ fn when_is_compatible(
}
}

// Check vehicle dimension filters
if let Some(restrictions_vehicle) = &restrictions.vehicle {
let all_restrictions_apply_to_all_vehicles = restrictions_vehicle.iter().all(|r_vehicle| {
if let Some(when_vehicles) = &when.vehicle {
when_vehicles
.iter()
.all(|w_vehicle| r_vehicle.is_valid(w_vehicle))
} else {
false
}
});

if !all_restrictions_apply_to_all_vehicles {
return false;
}
}

// If we got here, all specified fields in when are compatible
true
}

#[cfg(test)]
mod tests {
use super::*;
use crate::collection::record::{
OvertureMapsBbox, SegmentAccessType, SegmentMode, SegmentRecognized, SegmentUsing,
use crate::collection::{
filter::{MatchBehavior, TravelModeFilter},
record::{
OvertureMapsBbox, SegmentAccessRestrictionWhenVehicle, SegmentAccessType,
SegmentLengthUnit, SegmentMode, SegmentRecognized, SegmentUnit, SegmentUsing,
SegmentVehicleComparator, SegmentVehicleDimension,
},
SegmentClass,
};

#[test]
Expand Down Expand Up @@ -576,6 +599,98 @@ mod tests {
assert_eq!(result2.len(), 0);
}

#[test]
fn test_from_data_3() {
let access_restrictions = vec![
create_restriction_mode(SegmentAccessType::Designated, vec![SegmentMode::Hgv]),
create_restriction_when_vehicle(
SegmentAccessType::Denied,
SegmentAccessRestrictionWhenVehicle {
dimension: SegmentVehicleDimension::Height,
comparison: SegmentVehicleComparator::GreaterThan,
value: 13.0,
unit: Some(SegmentUnit::Length(SegmentLengthUnit::Feet)),
},
),
];

let segment = create_test_segment(Some(access_restrictions));

// Should pass simple drive filter
let filter1 = create_simple_drive_filter();
assert!(filter1.matches_filter(&segment));

// Should be denied when vehicle height is greater than 13 ft
let when1 = SegmentAccessRestrictionWhen {
during: None,
heading: Some(SegmentHeading::Forward),
using: None,
recognized: None,
mode: None,
vehicle: Some(vec![SegmentAccessRestrictionWhenVehicle {
dimension: SegmentVehicleDimension::Height,
comparison: SegmentVehicleComparator::GreaterThan,
value: 100.0,
unit: Some(SegmentUnit::Length(SegmentLengthUnit::Feet)),
}]),
};
let result1 = get_headings(&segment, Some(&when1)).unwrap();
assert_eq!(result1.len(), 0);

// But should be allowed in any other case
let when2 = SegmentAccessRestrictionWhen {
during: None,
heading: Some(SegmentHeading::Forward),
using: None,
recognized: None,
mode: None,
vehicle: Some(vec![SegmentAccessRestrictionWhenVehicle {
dimension: SegmentVehicleDimension::Height,
comparison: SegmentVehicleComparator::GreaterThan,
value: 10.,
unit: Some(SegmentUnit::Length(SegmentLengthUnit::Feet)),
}]),
};
let result2 = get_headings(&segment, Some(&when2)).unwrap();
assert_eq!(result2, vec![SegmentHeading::Forward]);

// also when no vehicle is specified
let when3 = SegmentAccessRestrictionWhen {
during: None,
heading: Some(SegmentHeading::Forward),
using: None,
recognized: None,
mode: None,
vehicle: None,
};
let result3 = get_headings(&segment, Some(&when3)).unwrap();
assert_eq!(result3, vec![SegmentHeading::Forward]);
}

// Helper to represent a typical drive mode filter
fn create_simple_drive_filter() -> TravelModeFilter {
let classes = vec![
SegmentClass::Motorway,
SegmentClass::Trunk,
SegmentClass::Primary,
SegmentClass::Secondary,
SegmentClass::Tertiary,
SegmentClass::Residential,
SegmentClass::LivingStreet,
SegmentClass::Unclassified,
SegmentClass::Service,
SegmentClass::Unknown,
]
.into_iter()
.collect();

TravelModeFilter::MatchesClasses {
classes,
behavior: MatchBehavior::Include,
allow_unset: true,
}
}

/// Helper to create a minimal segment for testing
fn create_test_segment(
access_restrictions: Option<Vec<SegmentAccessRestriction>>,
Expand Down Expand Up @@ -648,6 +763,25 @@ mod tests {
}
}

/// Helper to create a simple access restriction with vehicle detail
fn create_restriction_when_vehicle(
access_type: SegmentAccessType,
vehicle: SegmentAccessRestrictionWhenVehicle,
) -> SegmentAccessRestriction {
SegmentAccessRestriction {
access_type,
when: Some(SegmentAccessRestrictionWhen {
during: None,
heading: None,
using: None,
recognized: None,
mode: None,
vehicle: Some(vec![vehicle]),
}),
vehicle: None,
}
}

/// Helper to create "Denied all + Allowed specific" pattern for a heading
fn create_denied_all_allowed_specific(
heading: SegmentHeading,
Expand Down
2 changes: 1 addition & 1 deletion rust/bambam-omf/src/graph/segment_split.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ impl SegmentSplit {
Some(h) if h == heading => true,
_ => false,
},
None => false,
None => true,
})
.collect_vec();

Expand Down
Loading