use wasm_bindgen::prelude::*;
use crate::Location;
use super::options::{WasmAnalyzeOptions, WasmRecalibrateOptions};
#[wasm_bindgen]
pub struct Trace {
inner: crate::trace::Trace,
waypoints: Vec<crate::waypoint::Waypoint>,
metadata: crate::gpx::GpxMetadata,
}
impl Trace {
pub(crate) fn new(
inner: crate::trace::Trace,
waypoints: Vec<crate::waypoint::Waypoint>,
metadata: crate::gpx::GpxMetadata,
) -> Self {
Self {
inner,
waypoints,
metadata,
}
}
pub(crate) fn inner(&self) -> &crate::trace::Trace {
&self.inner
}
pub(crate) fn waypoints(&self) -> &[crate::waypoint::Waypoint] {
&self.waypoints
}
pub(crate) fn metadata(&self) -> &crate::gpx::GpxMetadata {
&self.metadata
}
}
#[wasm_bindgen]
impl Trace {
#[wasm_bindgen(getter, js_name = "totalDistance")]
pub fn total_distance(&self) -> f64 {
self.inner.total_distance
}
#[wasm_bindgen(getter, js_name = "totalElevationGain")]
pub fn total_elevation_gain(&self) -> f64 {
self.inner.total_elevation_gain
}
#[wasm_bindgen(getter, js_name = "totalElevationLoss")]
pub fn total_elevation_loss(&self) -> f64 {
self.inner.total_elevation_loss
}
#[wasm_bindgen(getter, js_name = "locationCount")]
pub fn location_count(&self) -> u32 {
self.inner.locations.len() as u32
}
#[wasm_bindgen(js_name = "getLocationsFlat")]
pub fn locations_flat(&self) -> Vec<f64> {
self.inner
.locations
.iter()
.flat_map(|l| [l.longitude, l.latitude, l.altitude])
.collect()
}
#[wasm_bindgen(js_name = "getCumulativeDistances")]
pub fn cumulative_distances(&self) -> Vec<f64> {
self.inner.cumulative_distances.clone()
}
#[wasm_bindgen(js_name = "getCumulativeElevationGains")]
pub fn cumulative_elevation_gains(&self) -> Vec<f64> {
self.inner.cumulative_elevation_gains.clone()
}
#[wasm_bindgen(js_name = "getCumulativeElevationLosses")]
pub fn cumulative_elevation_losses(&self) -> Vec<f64> {
self.inner.cumulative_elevation_losses.clone()
}
#[wasm_bindgen(js_name = "getSlopes")]
pub fn slopes(&self) -> Vec<f64> {
self.inner.slopes.clone()
}
#[wasm_bindgen(js_name = "getPeaks")]
pub fn peaks(&self) -> Vec<u32> {
self.inner.peaks.iter().map(|&i| i as u32).collect()
}
#[wasm_bindgen(js_name = "getValleys")]
pub fn valleys(&self) -> Vec<u32> {
self.inner.valleys.iter().map(|&i| i as u32).collect()
}
#[wasm_bindgen(js_name = "indexAtDistance")]
pub fn index_at_distance(&self, dist_km: f64) -> u32 {
self.inner.index_at_distance(dist_km) as u32
}
#[wasm_bindgen(js_name = "pointAtDistance")]
pub fn point_at_distance(&self, dist_km: f64) -> Option<JsValue> {
self.inner
.point_at_distance(dist_km)
.and_then(|loc| serde_wasm_bindgen::to_value(loc).ok())
}
#[wasm_bindgen(js_name = "findClosestPoint")]
pub fn find_closest_point(
&self,
longitude: f64,
latitude: f64,
altitude: f64,
) -> Option<JsValue> {
let target = Location {
longitude,
latitude,
altitude,
};
closest_result(self.inner.find_closest_point(&target))
}
#[wasm_bindgen(js_name = "findClosestPointFrom")]
pub fn find_closest_point_from(
&self,
longitude: f64,
latitude: f64,
altitude: f64,
start_from: u32,
) -> Option<JsValue> {
let target = Location {
longitude,
latitude,
altitude,
};
closest_result(
self.inner
.find_closest_point_from(&target, start_from as usize),
)
}
#[wasm_bindgen(js_name = "sliceBetweenDistances")]
pub fn slice_between_distances(&self, start_km: f64, end_km: f64) -> Option<Vec<f64>> {
self.inner
.slice_between_distances(start_km, end_km)
.map(locs_to_flat)
}
#[wasm_bindgen(js_name = "getSection")]
pub fn get_section(&self, start_index: u32, end_index: u32) -> Result<Vec<f64>, JsError> {
self.inner
.get_section(start_index as usize, end_index as usize)
.map(locs_to_flat)
.map_err(|e| JsError::new(&e.to_string()))
}
pub fn area(&self) -> JsValue {
serde_wasm_bindgen::to_value(self.inner.area()).unwrap_or(JsValue::NULL)
}
pub fn elevation(&self) -> JsValue {
serde_wasm_bindgen::to_value(self.inner.elevation()).unwrap_or(JsValue::NULL)
}
pub fn climbs(&self) -> JsValue {
serde_wasm_bindgen::to_value(self.inner.climbs()).unwrap_or(JsValue::UNDEFINED)
}
pub fn analyze(&self, options: JsValue) -> Option<JsValue> {
let options: WasmAnalyzeOptions = serde_wasm_bindgen::from_value(options)
.map_err(|e| super::warn(&format!("navigo: analyze() options error: {e}")))
.ok()?;
let analysis = super::compute_route_analysis(self, &options);
serde_wasm_bindgen::to_value(&analysis)
.map_err(|e| super::warn(&format!("navigo: analyze() serialization error: {e}")))
.ok()
}
pub fn recalibrate(&self, options: JsValue) -> Option<JsValue> {
let options: WasmRecalibrateOptions = serde_wasm_bindgen::from_value(options)
.map_err(|e| super::warn(&format!("navigo: recalibrate() options error: {e}")))
.ok()?;
let recalibration = super::compute_recalibration(self, &options);
serde_wasm_bindgen::to_value(&recalibration)
.map_err(|e| super::warn(&format!("navigo: recalibrate() serialization error: {e}")))
.ok()
}
}
fn closest_result(result: Option<(&Location, usize, f64)>) -> Option<JsValue> {
let (loc, idx, dist) = result?;
serde_wasm_bindgen::to_value(&ClosestPointResult {
location: loc,
index: idx as u32,
distance: dist,
})
.ok()
}
fn locs_to_flat(locs: &[Location]) -> Vec<f64> {
locs.iter()
.flat_map(|l| [l.longitude, l.latitude, l.altitude])
.collect()
}
#[derive(serde::Serialize)]
struct ClosestPointResult<'a> {
location: &'a Location,
index: u32,
distance: f64,
}