survex_rs/
data.rs

1//! Data structures to represent processed Survex data
2
3use crate::station::{Point, Station};
4use petgraph::graph::{NodeIndex, UnGraph};
5use std::cell::RefCell;
6use std::rc::Rc;
7
8pub type Stations = Vec<RefStation>;
9pub type RefStation = Rc<RefCell<Station>>;
10pub type StationGraph = UnGraph<String, f64>;
11
12/// Handles the creation and management of stations, as well as holding the
13/// [`graph`][`petgraph::graph::Graph`] of stations.
14pub struct SurveyData {
15    pub stations: Stations,
16    pub graph: StationGraph,
17}
18
19impl Default for SurveyData {
20    /// Returns an empty [`SurveyData`] instance with no stations.
21    fn default() -> Self {
22        Self::new()
23    }
24}
25
26impl SurveyData {
27    /// Create an empty [`SurveyData`] instance with no stations or connections. This method should
28    /// not be used directly. Instead, create a [`SurveyData`] instance from a Survex file using the
29    /// [`load_from_path`][`crate::read::load_from_path`] helper function.
30    pub fn new() -> Self {
31        Self {
32            stations: Vec::new(),
33            graph: StationGraph::new_undirected(),
34        }
35    }
36
37    /// Retrieve a reference to a [`Station`] by its label. Only exact matches are returned. To
38    /// retrieve a station by partial label use
39    /// [`get_by_label_part`][`SurveyData::get_by_label_part`].
40    pub fn get_by_label(&self, label: &str) -> Option<RefStation> {
41        for station in &self.stations {
42            if station.borrow().label == label {
43                return Some(Rc::clone(station));
44            }
45        }
46        None
47    }
48
49    /// Retrieve a reference to a [`Station`] by its label, allowing for partial matches. If
50    /// multiple stations match the given label, [`None`] is returned, unless one of the matches is
51    /// an exact match, in which case that station is returned.
52    pub fn get_by_label_part(&self, label: &str) -> Option<RefStation> {
53        let matches = self
54            .stations
55            .iter()
56            .filter(|&node| node.borrow().label.contains(label))
57            .collect::<Vec<_>>();
58
59        if matches.len() == 1 {
60            return Some(Rc::clone(matches[0]));
61        } else {
62            for station in matches.iter() {
63                if station.borrow().label == label {
64                    return Some(Rc::clone(station));
65                }
66            }
67        }
68
69        // We have ruled out an exact match, so there is either no match or multiple matches, so
70        // just return None and hope the user can be more specific.
71        None
72    }
73
74    /// Retrieve a reference to a [`Station`] by its coordinates. If multiple stations exist at the
75    /// given coordinates, the first station found is returned.
76    pub fn get_by_coords(&self, coords: &Point) -> Option<RefStation> {
77        for station in &self.stations {
78            if station.borrow().coords == *coords {
79                return Some(Rc::clone(station));
80            }
81        }
82        None
83    }
84
85    /// Retrieve a reference to a [`Station`] by its index in the graph.
86    pub fn get_by_index(&self, index: NodeIndex) -> Option<RefStation> {
87        for station in &self.stations {
88            if station.borrow().index == index {
89                return Some(Rc::clone(station));
90            }
91        }
92        None
93    }
94
95    /// This helper method is used to add or update a [`Station`] to both the stations vector and
96    /// the graph.
97    ///
98    /// If a [`Station`] with the given label already exists, the existing station is updated with
99    /// the new coordinates. Otherwise, a new [`Station`] is created and added to the stations
100    /// vector and the graph. In either case, a reference to the station is returned in a tuple
101    /// along with the index of the station in the graph.
102    pub fn add_or_update(&mut self, coords: Point, label: &str) -> (RefStation, NodeIndex) {
103        if let Some(station) = self.get_by_label(label) {
104            let index = station.borrow().index;
105            let station_clone = Rc::clone(&station);
106            let mut station_mut = station.borrow_mut();
107            station_mut.coords = coords;
108            return (station_clone, index);
109        }
110
111        let index = self.graph.add_node(String::from(label));
112        let station = Station::new(String::from(label), coords, index);
113        let ref_station = Rc::new(RefCell::new(station));
114        let station_clone = Rc::clone(&ref_station);
115        self.stations.push(ref_station);
116        (station_clone, index)
117    }
118}