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
81
//! Data structures to represent processed Survex data

use crate::station::{Point, Station};
use petgraph::graph::{NodeIndex, UnGraph};
use std::cell::RefCell;
use std::rc::Rc;

type Stations = Vec<RefStation>;
type RefStation = Rc<RefCell<Station>>;
type StationGraph = UnGraph<String, f64>;

/// Handles the creation and management of stations, as well as holding the
/// [`graph`][`petgraph::graph::Graph`] of stations.
pub struct SurveyData {
    pub stations: Stations,
    pub graph: StationGraph,
}

impl Default for SurveyData {
    /// Returns an empty [`SurveyData`] instance with no stations.
    fn default() -> Self {
        Self::new()
    }
}

impl SurveyData {
    /// Create an empty [`SurveyData`] instance with no stations or connections. This method should
    /// not be used directly. Instead, create a [`SurveyData`] instance from a Survex file using the
    /// [`load_from_path`][`crate::read::load_from_path`] helper function.
    pub fn new() -> Self {
        Self {
            stations: Vec::new(),
            graph: StationGraph::new_undirected(),
        }
    }

    /// Retrieve a reference to a [`Station`] by its label.
    pub fn get_by_label(&self, label: &str) -> Option<RefStation> {
        for station in &self.stations {
            if station.borrow().label == label {
                return Some(Rc::clone(station));
            }
        }
        None
    }

    /// Retrieve a reference to a [`Station`] by its coordinates. If multiple stations exist at the
    /// given coordinates, the first station found is returned.
    pub fn get_by_coords(&self, coords: &Point) -> Option<RefStation> {
        for station in &self.stations {
            if station.borrow().coords == *coords {
                return Some(Rc::clone(station));
            }
        }
        None
    }

    /// This helper method is used to add or update a [`Station`] to both the stations vector and
    /// the graph.
    ///
    /// If a [`Station`] with the given label already exists, the existing station is updated with
    /// the new coordinates. Otherwise, a new [`Station`] is created and added to the stations
    /// vector and the graph. In either case, a reference to the station is returned in a tuple
    /// along with the index of the station in the graph.
    pub fn add_or_update(&mut self, coords: Point, label: &str) -> (RefStation, NodeIndex) {
        if let Some(station) = self.get_by_label(label) {
            let index = station.borrow().index;
            let station_clone = Rc::clone(&station);
            let mut station_mut = station.borrow_mut();
            station_mut.coords = coords;
            return (station_clone, index);
        }

        let index = self.graph.add_node(String::from(label));
        let station = Station::new(String::from(label), coords, index);
        let ref_station = Rc::new(RefCell::new(station));
        let station_clone = Rc::clone(&ref_station);
        self.stations.push(ref_station);
        (station_clone, index)
    }
}