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}