-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathlib.rs
More file actions
214 lines (191 loc) · 6.33 KB
/
lib.rs
File metadata and controls
214 lines (191 loc) · 6.33 KB
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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
//! High level extensions meant for an easy usage
//! Those functions are exposed in wasm-bindings
use liblrs::{lrs::LrmHandle, lrs_ext::*};
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
/// Struct exposed to js
pub struct Lrs {
lrs: ExtLrs,
}
#[derive(Clone, Copy)]
#[wasm_bindgen]
/// A geographical [`Point`], it can be either a projected or spherical coordinates.
pub struct Point {
/// Position on x-axis or `longitude`.
pub x: f64,
/// Position on y-axis or `latitude`.
pub y: f64,
}
#[wasm_bindgen]
impl Point {
/// Build a new geographical point.
///
/// When using spherical coordinates, longitude is x and latitude y
#[wasm_bindgen(constructor)]
pub fn new(x: f64, y: f64) -> Self {
Self { x, y }
}
}
impl From<geo_types::Point> for Point {
fn from(value: geo_types::Point) -> Self {
Self {
x: value.x(),
y: value.y(),
}
}
}
impl From<Point> for geo_types::Point<f64> {
fn from(value: Point) -> Self {
Self::new(value.x, value.y)
}
}
impl From<geo_types::Coord> for Point {
fn from(value: geo_types::Coord) -> Self {
Self {
x: value.x,
y: value.y,
}
}
}
#[wasm_bindgen(getter_with_clone)]
#[derive(Clone)]
/// Represent a position on an [`LrmScale`] relative as an `offset` to an [`Anchor`].
pub struct LrmScaleMeasure {
/// `name` of the reference [`Anchor`].
pub anchor_name: String,
/// `offset` to the reference [`Anchor`].
pub scale_offset: f64,
}
#[wasm_bindgen]
impl LrmScaleMeasure {
/// Build a new [`LrmMeasure`] from an [`Anchor`] `name` and the `offset` on the [`LrmScale`].
#[wasm_bindgen(constructor)]
pub fn new(anchor_name: &str, scale_offset: f64) -> Self {
Self {
anchor_name: anchor_name.to_owned(),
scale_offset,
}
}
}
impl From<&liblrs::lrm_scale::LrmScaleMeasure> for LrmScaleMeasure {
fn from(value: &liblrs::lrm_scale::LrmScaleMeasure) -> Self {
Self {
anchor_name: value.anchor_name.clone(),
scale_offset: value.scale_offset,
}
}
}
impl From<&LrmScaleMeasure> for liblrs::lrm_scale::LrmScaleMeasure {
fn from(val: &LrmScaleMeasure) -> Self {
Self {
anchor_name: val.anchor_name.clone(),
scale_offset: val.scale_offset,
}
}
}
#[wasm_bindgen]
/// An `Anchor` is a reference point for a given [`Curve`].
pub struct Anchor {
#[wasm_bindgen(getter_with_clone)]
/// `name` of the [`Anchor`].
pub name: String,
/// Projected position on the [`Curve`] (the reference point isn’t always on the curve).
pub position: Option<Point>,
/// Position on the [`Curve`].
pub curve_position: f64,
/// Position on the scale.
pub scale_position: f64,
}
impl From<&liblrs::lrm_scale::Anchor> for Anchor {
fn from(value: &liblrs::lrm_scale::Anchor) -> Self {
Self {
name: value.clone().id.unwrap_or_else(|| "-".to_owned()),
position: value.point.map(|p| p.into()),
curve_position: value.curve_position,
scale_position: value.scale_position,
}
}
}
#[wasm_bindgen(getter_with_clone)]
/// The result of a projection onto an [`LrmScale`].
pub struct LrmProjection {
/// Contains `measure` ([`LrmScaleMeasure`]) and `lrm` ([`LrmHandle`]).
pub measure: LrmScaleMeasure,
/// How far from the [`Lrm`] is the [`Point`] that has been projected.
pub orthogonal_offset: f64,
}
#[wasm_bindgen]
impl Lrs {
/// Load the data.
pub fn load(data: &[u8], planar: bool) -> Result<Lrs, String> {
ExtLrs::load(data, planar).map(|lrs| Self { lrs })
}
/// How many LRMs compose the LRS.
pub fn lrm_len(&self) -> usize {
self.lrs.lrm_len()
}
/// Return the geometry of the LRM.
pub fn get_lrm_geom(&self, index: usize) -> Result<Vec<Point>, String> {
self.lrs
.get_lrm_geom(index)
.map(|coords| coords.into_iter().map(|coord| coord.into()).collect())
.map_err(|e| e.to_string())
}
/// `id` of the [`LrmScale`].
pub fn get_lrm_scale_id(&self, index: usize) -> String {
self.lrs.get_lrm_scale_id(index)
}
/// All the [`Anchor`]s of a LRM.
pub fn get_anchors(&self, lrm_index: usize) -> Vec<Anchor> {
self.lrs
.get_anchors(lrm_index)
.iter()
.map(Anchor::from)
.collect()
}
/// Get the position given a [`LrmScaleMeasure`].
pub fn resolve(&self, lrm_index: usize, measure: &LrmScaleMeasure) -> Result<Point, String> {
self.lrs
.resolve(lrm_index, &measure.into())
.map(Point::from)
.map_err(|e| e.to_string())
}
/// Given two [`LrmScaleMeasure`]s, return a range of [`LineString`].
pub fn resolve_range(
&self,
lrm_index: usize,
from: &LrmScaleMeasure,
to: &LrmScaleMeasure,
) -> Result<Vec<Point>, String> {
self.lrs
.resolve_range(lrm_index, &from.into(), &to.into())
.map(|coords| coords.into_iter().map(|coord| coord.into()).collect())
}
/// Projects a [`Point`] on all applicable [`Traversal`]s to a given [`Lrm`].
/// The [`Point`] must be in the bounding box of the [`Curve`] of the [`Traversal`].
/// The result is sorted by `orthogonal_offset`: the nearest [`Lrm`] to the [`Point`] is the first item.
pub fn lookup(&self, point: Point, lrm_handle: usize) -> Vec<LrmProjection> {
self.lrs
.lookup(point.into(), LrmHandle(lrm_handle))
.iter()
.map(|p| LrmProjection {
measure: LrmScaleMeasure {
anchor_name: p.measure.measure.anchor_name.to_owned(),
scale_offset: p.measure.measure.scale_offset,
},
orthogonal_offset: p.orthogonal_offset,
})
.collect()
}
}
#[wasm_bindgen]
/// Display stacktrace in case of a panic.
pub fn set_panic_hook() {
// When the `console_error_panic_hook` feature is enabled, we can call the
// `set_panic_hook` function at least once during initialization, and then
// we will get better error messages if our code ever panics.
//
// For more details see
// https://github.com/rustwasm/console_error_panic_hook#readme
console_error_panic_hook::set_once();
}