diff --git a/python/src/lib.rs b/python/src/lib.rs index e8dcad1..ec07e46 100644 --- a/python/src/lib.rs +++ b/python/src/lib.rs @@ -3,9 +3,9 @@ use std::path::PathBuf; -use liblrs::lrs::LrmHandle; +use liblrs::builder::Properties; +use liblrs::lrs::{self, LrmHandle}; use liblrs::lrs_ext::*; -use liblrs::{builder::Properties, lrs::LrsBase}; use pyo3::{exceptions::PyTypeError, prelude::*}; /// Holds the whole Linear Referencing System. @@ -230,6 +230,17 @@ impl Anchor { } } +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, + } + } +} + #[pyclass] /// The result of a projection onto an [`LrmScale`]. pub struct LrmProjection { @@ -241,13 +252,11 @@ pub struct LrmProjection { pub orthogonal_offset: f64, } -impl From<&liblrs::lrm_scale::Anchor> for Anchor { - fn from(value: &liblrs::lrm_scale::Anchor) -> Self { +impl From for LrmProjection { + fn from(value: lrs::LrmProjection) -> 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, + measure: (&value.measure.measure).into(), + orthogonal_offset: value.orthogonal_offset, } } } @@ -256,8 +265,8 @@ impl From<&liblrs::lrm_scale::Anchor> for Anchor { impl Lrs { /// Load the data. #[new] - pub fn load(data: &[u8]) -> PyResult { - ExtLrs::load(data) + pub fn load(data: &[u8], planar: bool) -> PyResult { + ExtLrs::load(data, planar) .map(|lrs| Self { lrs }) .map_err(|e| PyTypeError::new_err(e.to_string())) } @@ -300,9 +309,8 @@ impl Lrs { /// Get the positon along the curve given a [`LrmScaleMeasure`] /// The value will be between 0.0 and 1.0, both included pub fn locate_point(&self, lrm_index: usize, measure: &LrmScaleMeasure) -> PyResult { - self.lrs.lrs.lrms[lrm_index] - .scale - .locate_point(&measure.into()) + self.lrs + .locate_point(lrm_index, &(measure.into())) .map_err(|e| PyTypeError::new_err(e.to_string())) } @@ -321,7 +329,7 @@ impl Lrs { /// Given a ID returns the corresponding lrs index (or None if not found) pub fn find_lrm(&self, lrm_id: &str) -> Option { - self.lrs.lrs.get_lrm(lrm_id).map(|handle| handle.0) + self.lrs.find_lrm(lrm_id) } /// Projects a [`Point`] on all applicable [`Traversal`]s to a given [`Lrm`]. @@ -329,16 +337,9 @@ impl Lrs { /// The result is sorted by `orthogonal_offset`: the nearest [`Lrm`] to the [`Point`] is the first item. fn lookup(&self, point: Point, lrm_handle: usize) -> Vec { self.lrs - .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, - }) + .into_iter() + .map(|p| p.into()) .collect() } } @@ -457,10 +458,10 @@ impl Builder { } /// Builds the lrs to be used directly - pub fn build_lrs(&mut self, properties: Properties) -> PyResult { + pub fn build_lrs(&mut self, properties: Properties, planar: bool) -> PyResult { let lrs = self .inner - .build_lrs(properties) + .build_lrs(properties, planar) .map_err(|e| PyTypeError::new_err(e.to_string()))?; Ok(Lrs { lrs }) } diff --git a/src/builder.rs b/src/builder.rs index e27e667..8e983cc 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -407,8 +407,8 @@ impl<'fbb> Builder<'fbb> { } /// Builds the LRS from the data. - pub fn build_lrs(&mut self, properties: Properties) -> Result { - ExtLrs::load(self.build_data(properties)) + pub fn build_lrs(&mut self, properties: Properties, planar: bool) -> Result { + ExtLrs::load(self.build_data(properties), planar) } /// Return the mapping between a traversal id and its index in the builder. @@ -643,7 +643,7 @@ mod tests { }; b.add_lrm("lrm", traversal, &[aol, aol2], properties!()); - let lrs = b.build_lrs(properties!()).unwrap(); + let lrs = b.build_lrs(properties!(), true).unwrap(); let lrm = lrs .resolve( 0, diff --git a/src/lrs_ext.rs b/src/lrs_ext.rs index ee3d049..f8a919f 100644 --- a/src/lrs_ext.rs +++ b/src/lrs_ext.rs @@ -3,62 +3,88 @@ use geo::{Coord, Point}; -use crate::curves::{Curve, SphericalLineStringCurve}; -use crate::lrm_scale::Anchor; +use crate::curves::{Curve, PlanarLineStringCurve, SphericalLineStringCurve}; use crate::lrm_scale::LrmScaleMeasure; -use crate::lrs::{self, TraversalPosition}; -use crate::lrs::{LrsBase, LrsError}; - -type Lrs = lrs::Lrs; +use crate::lrm_scale::{Anchor, LrmScaleError}; +use crate::lrs::{Lrm, LrmHandle, LrmProjection, Lrs, LrsError}; +use crate::lrs::{LrsBase, TraversalPosition}; /// Struct exposed to js. -pub struct ExtLrs { - /// The linear referencing system - pub lrs: Lrs, +pub enum ExtLrs { + /// LRS with spherical coordinates. + Spherical(Lrs), + /// LRS with planar coordinates. + Planar(Lrs), } impl ExtLrs { /// Load the data. - pub fn load(data: &[u8]) -> Result { - Lrs::from_bytes(data) - .map(|lrs| Self { lrs }) - .map_err(|err| err.to_string()) + pub fn load(data: &[u8], planar: bool) -> Result { + if planar { + Lrs::::from_bytes(data).map(ExtLrs::Planar) + } else { + Lrs::::from_bytes(data).map(ExtLrs::Spherical) + } + .map_err(|err| err.to_string()) } /// How many LRMs compose the LRS. pub fn lrm_len(&self) -> usize { - self.lrs.lrm_len() + match self { + ExtLrs::Spherical(lrs) => lrs.lrm_len(), + ExtLrs::Planar(lrs) => lrs.lrm_len(), + } + } + + /// Given a ID returns the corresponding lrs index (or None if not found) + pub fn find_lrm(&self, lrm_id: &str) -> Option { + match self { + ExtLrs::Spherical(lrs) => lrs.get_lrm(lrm_id).map(|handle| handle.0), + ExtLrs::Planar(lrs) => lrs.get_lrm(lrm_id).map(|handle| handle.0), + } + } + + fn get_lrm(&self, index: usize) -> &Lrm { + match self { + ExtLrs::Spherical(lrs) => &lrs.lrms[index], + ExtLrs::Planar(lrs) => &lrs.lrms[index], + } } /// Return the geometry of the LRM. pub fn get_lrm_geom(&self, index: usize) -> Result, String> { - let lrm = self.lrs.lrms.get(index).ok_or("Invalid index")?; - self.lrs - .get_linestring(lrm.reference_traversal) - .map_err(|err| err.to_string()) - .map(|linestring| linestring.0) + let lrm = self.get_lrm(index); + match self { + ExtLrs::Spherical(lrs) => lrs.get_linestring(lrm.reference_traversal), + ExtLrs::Planar(lrs) => lrs.get_linestring(lrm.reference_traversal), + } + .map_err(|err| err.to_string()) + .map(|linestring| linestring.0) } /// `id` of the [`LrmScale`]. pub fn get_lrm_scale_id(&self, index: usize) -> String { - self.lrs.lrms[index].scale.id.clone() + self.get_lrm(index).scale.id.clone() } /// All the [`Anchor`]s of a LRM. pub fn get_anchors(&self, lrm_index: usize) -> Vec { - self.lrs.lrms[lrm_index].scale.anchors.to_vec() + self.get_lrm(lrm_index).scale.anchors.to_vec() } /// Get the position given a [`LrmScaleMeasure`]. pub fn resolve(&self, lrm_index: usize, measure: &LrmScaleMeasure) -> Result { - let lrm = &self.lrs.lrms[lrm_index]; + let lrm = self.get_lrm(lrm_index); let curve_position = lrm.scale.locate_point(measure)?.clamp(0., 1.0); let traversal_position = TraversalPosition { curve_position, traversal: lrm.reference_traversal, }; - self.lrs.locate_traversal(traversal_position) + match self { + ExtLrs::Spherical(lrs) => lrs.locate_traversal(traversal_position), + ExtLrs::Planar(lrs) => lrs.locate_traversal(traversal_position), + } } /// Given two [`LrmScaleMeasure`]s, return a range of [`LineString`]. @@ -68,9 +94,8 @@ impl ExtLrs { from: &LrmScaleMeasure, to: &LrmScaleMeasure, ) -> Result, String> { - let lrm = &self.lrs.lrms[lrm_index]; + let lrm = self.get_lrm(lrm_index); let scale = &lrm.scale; - let curve = &self.lrs.traversals[lrm.reference_traversal.0].curve; let from = scale .locate_point(from) .map_err(|e| e.to_string())? @@ -80,9 +105,37 @@ impl ExtLrs { .map_err(|e| e.to_string())? .clamp(0., 1.); - match curve.sublinestring(from, to) { + let sublinestring = match self { + ExtLrs::Spherical(lrs) => lrs.traversals[lrm.reference_traversal.0] + .curve + .sublinestring(from, to), + ExtLrs::Planar(lrs) => lrs.traversals[lrm.reference_traversal.0] + .curve + .sublinestring(from, to), + }; + + match sublinestring { Some(linestring) => Ok(linestring.0), None => Err("Could not find sublinestring".to_string()), } } + + /// Given a point, return the [`LrmProjection`]s. + pub fn lookup(&self, point: Point, lrm_handle: LrmHandle) -> Vec { + match self { + ExtLrs::Spherical(lrs) => lrs.lookup(point, lrm_handle), + ExtLrs::Planar(lrs) => lrs.lookup(point, lrm_handle), + } + } + + /// Get the positon along the curve given a [`LrmScaleMeasure`] + /// The value will be between 0.0 and 1.0, both included + pub fn locate_point( + &self, + lrm_index: usize, + measure: &LrmScaleMeasure, + ) -> Result { + let lrm = self.get_lrm(lrm_index); + lrm.scale.locate_point(measure) + } } diff --git a/wasm/html_demo/index.js b/wasm/html_demo/index.js index cd3a7db..d577fd0 100644 --- a/wasm/html_demo/index.js +++ b/wasm/html_demo/index.js @@ -13,7 +13,7 @@ set_panic_hook() async function file_selected(el) { const [file] = el.target.files; const data = await file.arrayBuffer() - const lrs = await Lrs.load(new Uint8Array(data)); + const lrs = await Lrs.load(new Uint8Array(data), planar=false); const curves_features = [] const anchors_features = [] diff --git a/wasm/src/lib.rs b/wasm/src/lib.rs index 1bb0e20..2b07b13 100644 --- a/wasm/src/lib.rs +++ b/wasm/src/lib.rs @@ -1,10 +1,7 @@ //! High level extensions meant for an easy usage //! Those functions are exposed in wasm-bindings -use liblrs::{ - lrs::{LrmHandle, LrsBase}, - lrs_ext::*, -}; +use liblrs::{lrs::LrmHandle, lrs_ext::*}; use wasm_bindgen::prelude::*; #[wasm_bindgen] @@ -135,8 +132,8 @@ pub struct LrmProjection { #[wasm_bindgen] impl Lrs { /// Load the data. - pub fn load(data: &[u8]) -> Result { - ExtLrs::load(data).map(|lrs| Self { lrs }) + pub fn load(data: &[u8], planar: bool) -> Result { + ExtLrs::load(data, planar).map(|lrs| Self { lrs }) } /// How many LRMs compose the LRS. @@ -191,7 +188,6 @@ impl Lrs { /// 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 { self.lrs - .lrs .lookup(point.into(), LrmHandle(lrm_handle)) .iter() .map(|p| LrmProjection {