1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
//! Router module

#[macro_use]
pub mod macros;
pub mod flight_plan;
pub mod itinerary;
pub mod schedule;
pub mod vehicle;
pub mod vertiport;

use crate::grpc::client::GrpcClients;
use svc_gis_client_grpc::prelude::{gis::*, *};

pub enum BestPathError {
    ClientError,
    NoPathFound,
}

impl std::fmt::Display for BestPathError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
            BestPathError::ClientError => write!(f, "Client error"),
            BestPathError::NoPathFound => write!(f, "No path found"),
        }
    }
}

/// Get the best path between two vertiports or a between an aircraft and a vertiport
///  and the total length of the path in meters.
pub async fn best_path(
    request: &BestPathRequest,
    clients: &GrpcClients,
) -> Result<Vec<(Vec<PointZ>, f64)>, BestPathError> {
    let mut paths = match clients.gis.best_path(request.clone()).await {
        Ok(response) => response.into_inner().paths,
        Err(e) => {
            router_error!("(best_path) Failed to get best path: {e}");
            return Err(BestPathError::ClientError);
        }
    };

    if paths.is_empty() {
        router_error!("(best_path) No path found.");
        return Err(BestPathError::NoPathFound);
    }

    paths.sort_by(|a, b| {
        if a.distance_meters == b.distance_meters {
            std::cmp::Ordering::Equal
        } else if a.distance_meters < b.distance_meters {
            std::cmp::Ordering::Less
        } else {
            std::cmp::Ordering::Greater
        }
    });
    router_debug!("(best_path) svc-gis paths: {:?}", paths);

    // convert segments to GeoLineString
    let mut result: Vec<(Vec<PointZ>, f64)> = vec![];
    for path in paths {
        let Ok(points) = path
            .path
            .into_iter()
            .map(|node| match node.geom {
                Some(geom) => Ok(geom),
                None => {
                    router_error!("(best_path) No geometry found for node: {:#?}", node);
                    Err(BestPathError::NoPathFound)
                }
            })
            .collect::<Result<Vec<PointZ>, BestPathError>>()
        else {
            continue;
        };

        result.push((points, path.distance_meters.into()));
    }

    Ok(result)
}