Skip to main content

ridge_core/
lib.rs

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