Skip to main content

ridge_core/
lib.rs

1//! ridge-core: the data pipeline behind ridgeline terrain art.
2//!
3//! Port of the Python `ridge_map` package (with the parts of `SRTM.py`,
4//! `numpy` and `scipy.ndimage` it relies on) to Rust, minus matplotlib:
5//! rendering is delegated to [`svg`] (server-side export) or to the web
6//! frontend, which consumes [`geometry::RidgeScene`] JSON.
7
8pub mod colormap;
9pub mod geometry;
10pub mod grid;
11pub mod preprocess;
12pub mod rotate;
13pub mod srtm;
14pub mod svg;
15
16pub use geometry::{RidgeRow, RidgeScene};
17pub use srtm::{Tile, TileSource};
18
19use serde::{Deserialize, Serialize};
20
21/// Default bounding box from upstream ridge_map: The White Mountains, NH.
22pub const DEFAULT_BBOX: Bbox = Bbox {
23    lon0: -71.928864,
24    lat0: 43.758201,
25    lon1: -70.957947,
26    lat1: 44.465151,
27};
28
29/// Geographic bounding box, `(long, lat, long, lat)` of the bottom-left and
30/// top-right corners, exactly like upstream `RidgeMap.__init__`.
31#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
32pub struct Bbox {
33    pub lon0: f64,
34    pub lat0: f64,
35    pub lon1: f64,
36    pub lat1: f64,
37}
38
39impl Bbox {
40    pub fn new(lon0: f64, lat0: f64, lon1: f64, lat1: f64) -> Self {
41        Self {
42            lon0,
43            lat0,
44            lon1,
45            lat1,
46        }
47    }
48
49    /// Bottom and top latitude (upstream `lats` property).
50    pub fn lats(&self) -> (f64, f64) {
51        (self.lat0, self.lat1)
52    }
53
54    /// Left and right longitude (upstream `longs` property).
55    pub fn longs(&self) -> (f64, f64) {
56        (self.lon0, self.lon1)
57    }
58
59    /// Figure aspect ratio (height / width) used by upstream `plot_map`.
60    pub fn ratio(&self) -> f64 {
61        (self.lat1 - self.lat0) / (self.lon1 - self.lon0)
62    }
63
64    pub fn is_valid(&self) -> bool {
65        self.lon1 > self.lon0
66            && self.lat1 > self.lat0
67            && self.lon0 >= -180.0
68            && self.lon1 <= 180.0
69            && self.lat0 >= -60.0
70            && self.lat1 <= 60.0
71    }
72}
73
74impl Default for Bbox {
75    fn default() -> Self {
76        DEFAULT_BBOX
77    }
78}
79
80#[derive(Debug, thiserror::Error)]
81pub enum Error {
82    #[error("invalid bounding box: {0:?}")]
83    InvalidBbox(Bbox),
84    #[error("elevation data contains no valid points for this bbox")]
85    EmptyData,
86    #[error("srtm tile error: {0}")]
87    Srtm(String),
88    #[error("io error: {0}")]
89    Io(#[from] std::io::Error),
90}
91
92pub type Result<T> = std::result::Result<T, Error>;