Skip to content

Commit e541bde

Browse files
committed
lrs: expose planar coordinate system
Signed-off-by: Baptiste Prevot <pro.baptiste.prevot@gmail.com>
1 parent a83b3d9 commit e541bde

5 files changed

Lines changed: 112 additions & 62 deletions

File tree

python/src/lib.rs

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
44
use std::path::PathBuf;
55

6-
use liblrs::lrs::LrmHandle;
6+
use liblrs::builder::Properties;
7+
use liblrs::lrs::{self, LrmHandle};
78
use liblrs::lrs_ext::*;
8-
use liblrs::{builder::Properties, lrs::LrsBase};
99
use pyo3::{exceptions::PyTypeError, prelude::*};
1010

1111
/// Holds the whole Linear Referencing System.
@@ -230,6 +230,17 @@ impl Anchor {
230230
}
231231
}
232232

233+
impl From<&liblrs::lrm_scale::Anchor> for Anchor {
234+
fn from(value: &liblrs::lrm_scale::Anchor) -> Self {
235+
Self {
236+
name: value.clone().id.unwrap_or_else(|| "-".to_owned()),
237+
position: value.point.map(|p| p.into()),
238+
curve_position: value.curve_position,
239+
scale_position: value.scale_position,
240+
}
241+
}
242+
}
243+
233244
#[pyclass]
234245
/// The result of a projection onto an [`LrmScale`].
235246
pub struct LrmProjection {
@@ -241,13 +252,11 @@ pub struct LrmProjection {
241252
pub orthogonal_offset: f64,
242253
}
243254

244-
impl From<&liblrs::lrm_scale::Anchor> for Anchor {
245-
fn from(value: &liblrs::lrm_scale::Anchor) -> Self {
255+
impl From<lrs::LrmProjection> for LrmProjection {
256+
fn from(value: lrs::LrmProjection) -> Self {
246257
Self {
247-
name: value.clone().id.unwrap_or_else(|| "-".to_owned()),
248-
position: value.point.map(|p| p.into()),
249-
curve_position: value.curve_position,
250-
scale_position: value.scale_position,
258+
measure: (&value.measure.measure).into(),
259+
orthogonal_offset: value.orthogonal_offset,
251260
}
252261
}
253262
}
@@ -256,8 +265,8 @@ impl From<&liblrs::lrm_scale::Anchor> for Anchor {
256265
impl Lrs {
257266
/// Load the data.
258267
#[new]
259-
pub fn load(data: &[u8]) -> PyResult<Lrs> {
260-
ExtLrs::load(data)
268+
pub fn load(data: &[u8], planar: bool) -> PyResult<Lrs> {
269+
ExtLrs::load(data, planar)
261270
.map(|lrs| Self { lrs })
262271
.map_err(|e| PyTypeError::new_err(e.to_string()))
263272
}
@@ -300,9 +309,8 @@ impl Lrs {
300309
/// Get the positon along the curve given a [`LrmScaleMeasure`]
301310
/// The value will be between 0.0 and 1.0, both included
302311
pub fn locate_point(&self, lrm_index: usize, measure: &LrmScaleMeasure) -> PyResult<f64> {
303-
self.lrs.lrs.lrms[lrm_index]
304-
.scale
305-
.locate_point(&measure.into())
312+
self.lrs
313+
.locate_point(lrm_index, &(measure.into()))
306314
.map_err(|e| PyTypeError::new_err(e.to_string()))
307315
}
308316

@@ -321,24 +329,17 @@ impl Lrs {
321329

322330
/// Given a ID returns the corresponding lrs index (or None if not found)
323331
pub fn find_lrm(&self, lrm_id: &str) -> Option<usize> {
324-
self.lrs.lrs.get_lrm(lrm_id).map(|handle| handle.0)
332+
self.lrs.find_lrm(lrm_id)
325333
}
326334

327335
/// Projects a [`Point`] on all applicable [`Traversal`]s to a given [`Lrm`].
328336
/// The [`Point`] must be in the bounding box of the [`Curve`] of the [`Traversal`].
329337
/// The result is sorted by `orthogonal_offset`: the nearest [`Lrm`] to the [`Point`] is the first item.
330338
fn lookup(&self, point: Point, lrm_handle: usize) -> Vec<LrmProjection> {
331339
self.lrs
332-
.lrs
333340
.lookup(point.into(), LrmHandle(lrm_handle))
334-
.iter()
335-
.map(|p| LrmProjection {
336-
measure: LrmScaleMeasure {
337-
anchor_name: p.measure.measure.anchor_name.to_owned(),
338-
scale_offset: p.measure.measure.scale_offset,
339-
},
340-
orthogonal_offset: p.orthogonal_offset,
341-
})
341+
.into_iter()
342+
.map(|p| p.into())
342343
.collect()
343344
}
344345
}
@@ -457,10 +458,10 @@ impl Builder {
457458
}
458459

459460
/// Builds the lrs to be used directly
460-
pub fn build_lrs(&mut self, properties: Properties) -> PyResult<Lrs> {
461+
pub fn build_lrs(&mut self, properties: Properties, planar: bool) -> PyResult<Lrs> {
461462
let lrs = self
462463
.inner
463-
.build_lrs(properties)
464+
.build_lrs(properties, planar)
464465
.map_err(|e| PyTypeError::new_err(e.to_string()))?;
465466
Ok(Lrs { lrs })
466467
}

src/builder.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -407,8 +407,8 @@ impl<'fbb> Builder<'fbb> {
407407
}
408408

409409
/// Builds the LRS from the data.
410-
pub fn build_lrs(&mut self, properties: Properties) -> Result<ExtLrs, String> {
411-
ExtLrs::load(self.build_data(properties))
410+
pub fn build_lrs(&mut self, properties: Properties, planar: bool) -> Result<ExtLrs, String> {
411+
ExtLrs::load(self.build_data(properties), planar)
412412
}
413413

414414
/// Return the mapping between a traversal id and its index in the builder.
@@ -643,7 +643,7 @@ mod tests {
643643
};
644644
b.add_lrm("lrm", traversal, &[aol, aol2], properties!());
645645

646-
let lrs = b.build_lrs(properties!()).unwrap();
646+
let lrs = b.build_lrs(properties!(), true).unwrap();
647647
let lrm = lrs
648648
.resolve(
649649
0,

src/lrs_ext.rs

Lines changed: 79 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,62 +3,88 @@
33
44
use geo::{Coord, Point};
55

6-
use crate::curves::{Curve, SphericalLineStringCurve};
7-
use crate::lrm_scale::Anchor;
6+
use crate::curves::{Curve, PlanarLineStringCurve, SphericalLineStringCurve};
87
use crate::lrm_scale::LrmScaleMeasure;
9-
use crate::lrs::{self, TraversalPosition};
10-
use crate::lrs::{LrsBase, LrsError};
11-
12-
type Lrs = lrs::Lrs<SphericalLineStringCurve>;
8+
use crate::lrm_scale::{Anchor, LrmScaleError};
9+
use crate::lrs::{Lrm, LrmHandle, LrmProjection, Lrs, LrsError};
10+
use crate::lrs::{LrsBase, TraversalPosition};
1311

1412
/// Struct exposed to js.
15-
pub struct ExtLrs {
16-
/// The linear referencing system
17-
pub lrs: Lrs,
13+
pub enum ExtLrs {
14+
/// LRS with spherical coordinates.
15+
Spherical(Lrs<SphericalLineStringCurve>),
16+
/// LRS with planar coordinates.
17+
Planar(Lrs<PlanarLineStringCurve>),
1818
}
1919

2020
impl ExtLrs {
2121
/// Load the data.
22-
pub fn load(data: &[u8]) -> Result<ExtLrs, String> {
23-
Lrs::from_bytes(data)
24-
.map(|lrs| Self { lrs })
25-
.map_err(|err| err.to_string())
22+
pub fn load(data: &[u8], planar: bool) -> Result<ExtLrs, String> {
23+
if planar {
24+
Lrs::<PlanarLineStringCurve>::from_bytes(data).map(|lrs| ExtLrs::Planar(lrs))
25+
} else {
26+
Lrs::<SphericalLineStringCurve>::from_bytes(data).map(|lrs| ExtLrs::Spherical(lrs))
27+
}
28+
.map_err(|err| err.to_string())
2629
}
2730

2831
/// How many LRMs compose the LRS.
2932
pub fn lrm_len(&self) -> usize {
30-
self.lrs.lrm_len()
33+
match self {
34+
ExtLrs::Spherical(lrs) => lrs.lrm_len(),
35+
ExtLrs::Planar(lrs) => lrs.lrm_len(),
36+
}
37+
}
38+
39+
/// Given a ID returns the corresponding lrs index (or None if not found)
40+
pub fn find_lrm(&self, lrm_id: &str) -> Option<usize> {
41+
match self {
42+
ExtLrs::Spherical(lrs) => lrs.get_lrm(lrm_id).map(|handle| handle.0),
43+
ExtLrs::Planar(lrs) => lrs.get_lrm(lrm_id).map(|handle| handle.0),
44+
}
45+
}
46+
47+
fn get_lrm(&self, index: usize) -> &Lrm {
48+
match self {
49+
ExtLrs::Spherical(lrs) => &lrs.lrms[index],
50+
ExtLrs::Planar(lrs) => &lrs.lrms[index],
51+
}
3152
}
3253

3354
/// Return the geometry of the LRM.
3455
pub fn get_lrm_geom(&self, index: usize) -> Result<Vec<geo::Coord>, String> {
35-
let lrm = self.lrs.lrms.get(index).ok_or("Invalid index")?;
36-
self.lrs
37-
.get_linestring(lrm.reference_traversal)
38-
.map_err(|err| err.to_string())
39-
.map(|linestring| linestring.0)
56+
let lrm = self.get_lrm(index);
57+
match self {
58+
ExtLrs::Spherical(lrs) => lrs.get_linestring(lrm.reference_traversal),
59+
ExtLrs::Planar(lrs) => lrs.get_linestring(lrm.reference_traversal),
60+
}
61+
.map_err(|err| err.to_string())
62+
.map(|linestring| linestring.0)
4063
}
4164

4265
/// `id` of the [`LrmScale`].
4366
pub fn get_lrm_scale_id(&self, index: usize) -> String {
44-
self.lrs.lrms[index].scale.id.clone()
67+
self.get_lrm(index).scale.id.clone()
4568
}
4669

4770
/// All the [`Anchor`]s of a LRM.
4871
pub fn get_anchors(&self, lrm_index: usize) -> Vec<Anchor> {
49-
self.lrs.lrms[lrm_index].scale.anchors.to_vec()
72+
self.get_lrm(lrm_index).scale.anchors.to_vec()
5073
}
5174

5275
/// Get the position given a [`LrmScaleMeasure`].
5376
pub fn resolve(&self, lrm_index: usize, measure: &LrmScaleMeasure) -> Result<Point, LrsError> {
54-
let lrm = &self.lrs.lrms[lrm_index];
77+
let lrm = self.get_lrm(lrm_index);
5578
let curve_position = lrm.scale.locate_point(measure)?.clamp(0., 1.0);
5679

5780
let traversal_position = TraversalPosition {
5881
curve_position,
5982
traversal: lrm.reference_traversal,
6083
};
61-
self.lrs.locate_traversal(traversal_position)
84+
match self {
85+
ExtLrs::Spherical(lrs) => lrs.locate_traversal(traversal_position),
86+
ExtLrs::Planar(lrs) => lrs.locate_traversal(traversal_position),
87+
}
6288
}
6389

6490
/// Given two [`LrmScaleMeasure`]s, return a range of [`LineString`].
@@ -68,9 +94,8 @@ impl ExtLrs {
6894
from: &LrmScaleMeasure,
6995
to: &LrmScaleMeasure,
7096
) -> Result<Vec<Coord>, String> {
71-
let lrm = &self.lrs.lrms[lrm_index];
97+
let lrm = self.get_lrm(lrm_index);
7298
let scale = &lrm.scale;
73-
let curve = &self.lrs.traversals[lrm.reference_traversal.0].curve;
7499
let from = scale
75100
.locate_point(from)
76101
.map_err(|e| e.to_string())?
@@ -80,9 +105,37 @@ impl ExtLrs {
80105
.map_err(|e| e.to_string())?
81106
.clamp(0., 1.);
82107

83-
match curve.sublinestring(from, to) {
108+
let sublinestring = match self {
109+
ExtLrs::Spherical(lrs) => lrs.traversals[lrm.reference_traversal.0]
110+
.curve
111+
.sublinestring(from, to),
112+
ExtLrs::Planar(lrs) => lrs.traversals[lrm.reference_traversal.0]
113+
.curve
114+
.sublinestring(from, to),
115+
};
116+
117+
match sublinestring {
84118
Some(linestring) => Ok(linestring.0),
85119
None => Err("Could not find sublinestring".to_string()),
86120
}
87121
}
122+
123+
/// Given a point, return the [`LrmProjection`]s.
124+
pub fn lookup(&self, point: Point, lrm_handle: LrmHandle) -> Vec<LrmProjection> {
125+
match self {
126+
ExtLrs::Spherical(lrs) => lrs.lookup(point.into(), lrm_handle),
127+
ExtLrs::Planar(lrs) => lrs.lookup(point.into(), lrm_handle),
128+
}
129+
}
130+
131+
/// Get the positon along the curve given a [`LrmScaleMeasure`]
132+
/// The value will be between 0.0 and 1.0, both included
133+
pub fn locate_point(
134+
&self,
135+
lrm_index: usize,
136+
measure: &LrmScaleMeasure,
137+
) -> Result<f64, LrmScaleError> {
138+
let lrm = self.get_lrm(lrm_index);
139+
lrm.scale.locate_point(measure)
140+
}
88141
}

wasm/html_demo/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ set_panic_hook()
1313
async function file_selected(el) {
1414
const [file] = el.target.files;
1515
const data = await file.arrayBuffer()
16-
const lrs = await Lrs.load(new Uint8Array(data));
16+
const lrs = await Lrs.load(new Uint8Array(data), planar=false);
1717

1818
const curves_features = []
1919
const anchors_features = []

wasm/src/lib.rs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
//! High level extensions meant for an easy usage
22
//! Those functions are exposed in wasm-bindings
33
4-
use liblrs::{
5-
lrs::{LrmHandle, LrsBase},
6-
lrs_ext::*,
7-
};
4+
use liblrs::{lrs::LrmHandle, lrs_ext::*};
85
use wasm_bindgen::prelude::*;
96

107
#[wasm_bindgen]
@@ -135,8 +132,8 @@ pub struct LrmProjection {
135132
#[wasm_bindgen]
136133
impl Lrs {
137134
/// Load the data.
138-
pub fn load(data: &[u8]) -> Result<Lrs, String> {
139-
ExtLrs::load(data).map(|lrs| Self { lrs })
135+
pub fn load(data: &[u8], planar: bool) -> Result<Lrs, String> {
136+
ExtLrs::load(data, planar).map(|lrs| Self { lrs })
140137
}
141138

142139
/// How many LRMs compose the LRS.
@@ -191,7 +188,6 @@ impl Lrs {
191188
/// The result is sorted by `orthogonal_offset`: the nearest [`Lrm`] to the [`Point`] is the first item.
192189
pub fn lookup(&self, point: Point, lrm_handle: usize) -> Vec<LrmProjection> {
193190
self.lrs
194-
.lrs
195191
.lookup(point.into(), LrmHandle(lrm_handle))
196192
.iter()
197193
.map(|p| LrmProjection {

0 commit comments

Comments
 (0)