From 428d9fcfaf80b47b24de8fb71c06f14a337beece Mon Sep 17 00:00:00 2001 From: jorgecardleitao <149073281+jorgecardleitao@users.noreply.github.com> Date: Tue, 23 Jan 2024 18:44:07 +0100 Subject: [PATCH] Made legs contain positions (#17) This allows deriving leg properties directly, as well as identifying if a leg passed through a location (e.g. to track airports). --- examples/dk_jets.rs | 2 +- examples/period.rs | 20 ++++++------- examples/single_day.rs | 6 ++-- src/legs.rs | 68 ++++++++++++++++++++---------------------- 4 files changed, 47 insertions(+), 49 deletions(-) diff --git a/examples/dk_jets.rs b/examples/dk_jets.rs index e19644f..905c674 100644 --- a/examples/dk_jets.rs +++ b/examples/dk_jets.rs @@ -159,7 +159,7 @@ async fn main() -> Result<(), Box> { .iter() .map(|(_, legs)| { legs.iter() - .map(|leg| emissions(leg.from.pos(), leg.to.pos(), Class::First) / 1000.0) + .map(|leg| emissions(leg.from().pos(), leg.to().pos(), Class::First) / 1000.0) .sum::() }) .sum::(); diff --git a/examples/period.rs b/examples/period.rs index 03a8d00..75b985c 100644 --- a/examples/period.rs +++ b/examples/period.rs @@ -136,22 +136,22 @@ async fn main() -> Result<(), Box> { for leg in &legs { log::info!( "{},{},{},{},{},{},{},{},{}", - leg.from.datetime(), - leg.from.latitude(), - leg.from.longitude(), - leg.from.altitude(), - leg.to.datetime(), - leg.to.latitude(), - leg.to.longitude(), - leg.to.altitude(), - leg.maximum_altitude + leg.from().datetime(), + leg.from().latitude(), + leg.from().longitude(), + leg.from().altitude(), + leg.to().datetime(), + leg.to().latitude(), + leg.to().longitude(), + leg.to().altitude(), + leg.maximum_altitude(), ); } let commercial_to_private_ratio = 10.0; let commercial_emissions_tons = legs .iter() - .map(|leg| emissions(leg.from.pos(), leg.to.pos(), Class::First) / 1000.0) + .map(|leg| emissions(leg.from().pos(), leg.to().pos(), Class::First) / 1000.0) .sum::(); let emissions_tons = Fact { claim: (commercial_emissions_tons * commercial_to_private_ratio) as usize, diff --git a/examples/single_day.rs b/examples/single_day.rs index 240b1f9..9a7455d 100644 --- a/examples/single_day.rs +++ b/examples/single_day.rs @@ -104,11 +104,11 @@ async fn flight_date( log::info!("Number of legs: {}", legs.len()); Ok(legs.into_iter().filter_map(|leg| { - let is_leg = matches!(leg.from, Position::Grounded{..}) & matches!(leg.to, Position::Grounded{..}); + let is_leg = matches!(leg.from(), Position::Grounded{..}) & matches!(leg.to(), Position::Grounded{..}); if !is_leg { - log::info!("{:?} -> {:?} skipped", leg.from, leg.to); + log::info!("{:?} -> {:?} skipped", leg.from(), leg.to()); } - is_leg.then_some((leg.from, leg.to)) + is_leg.then_some((leg.from().clone(), leg.to().clone())) }).map(|(from, to)| { let emissions = emissions(from.pos(), to.pos(), Class::First); diff --git a/src/legs.rs b/src/legs.rs index b76d1ca..9b76ca6 100644 --- a/src/legs.rs +++ b/src/legs.rs @@ -4,60 +4,58 @@ use crate::Position; /// between two positions. #[derive(Debug, Clone)] pub struct Leg { - pub from: Position, - pub to: Position, - /// in feet - pub maximum_altitude: f64, + /// Sequence of positions defining the leg. Ends may start Flying, when the first/last observed + /// position was flying. Otherwise, first and last are Grounded. + positions: Vec, } impl Leg { + /// Positions of the leg + pub fn positions(&self) -> &[Position] { + &self.positions + } + /// Leg geo distance in km pub fn distance(&self) -> f64 { - self.from.distace(&self.to) + self.from().distace(&self.to()) } /// Leg duration pub fn duration(&self) -> time::Duration { - self.to.datetime() - self.from.datetime() + self.to().datetime() - self.from().datetime() + } + + pub fn maximum_altitude(&self) -> f64 { + self.positions + .iter() + .map(|p| p.altitude() as u32) + .max() + .unwrap() as f64 + } + + pub fn from(&self) -> &Position { + self.positions.first().unwrap() + } + + pub fn to(&self) -> &Position { + self.positions.last().unwrap() } } /// Returns a set of [`Leg`]s from a sequence of [`Position`]s. -fn all_legs(mut positions: impl Iterator) -> Vec { +pub fn all_legs(mut positions: impl Iterator) -> Vec { let Some(mut prev_position) = positions.next() else { return vec![]; }; - let first = prev_position.clone(); + let mut sequence: Vec = vec![]; let mut legs: Vec = vec![]; - let mut maximum_altitude = first.altitude(); positions.for_each(|position| { - maximum_altitude = position.altitude().max(maximum_altitude); - match (&prev_position, &position) { - (Position::Grounded { .. }, Position::Flying { .. }) => { - // departed, still do not know to where - legs.push(Leg { - from: prev_position.clone(), - to: prev_position.clone(), - maximum_altitude, - }); - } - (Position::Flying { .. }, Position::Grounded { .. }) => { - // arrived - if let Some(leg) = legs.last_mut() { - // there is a leg - set its arrival position - leg.to = position.clone(); - } else { - // if it was initially flying, need to push to the leg - legs.push(Leg { - from: first.clone(), - to: position.clone(), - maximum_altitude, - }); - maximum_altitude = 0.0 - } - } - _ => {} + sequence.push(position.clone()); + if let (Position::Flying { .. }, Position::Grounded { .. }) = (&prev_position, &position) { + legs.push(Leg { + positions: std::mem::take(&mut sequence), + }); }; prev_position = position; });