use wasm_bindgen::prelude::*;
use crate::{build_trace as core_build_trace, Location};
#[wasm_bindgen(js_name = "buildTrace")]
pub fn build_trace(flat: &[f64]) -> Option<WasmTrace> {
let locations: Vec<Location> = flat
.chunks_exact(3)
.map(|c| Location {
longitude: c[0],
latitude: c[1],
altitude: c[2],
})
.collect();
core_build_trace(&locations)
.ok()
.map(|inner| WasmTrace { inner })
}
#[wasm_bindgen]
pub struct WasmTrace {
inner: crate::trace::Trace,
}
#[wasm_bindgen]
impl WasmTrace {
#[wasm_bindgen(getter)]
pub fn total_distance(&self) -> f64 {
self.inner.total_distance
}
#[wasm_bindgen(getter)]
pub fn total_elevation_gain(&self) -> f64 {
self.inner.total_elevation_gain
}
#[wasm_bindgen(getter)]
pub fn total_elevation_loss(&self) -> f64 {
self.inner.total_elevation_loss
}
#[wasm_bindgen(getter)]
pub fn location_count(&self) -> u32 {
self.inner.locations.len() as u32
}
#[wasm_bindgen(getter)]
pub fn locations_flat(&self) -> Vec<f64> {
self.inner
.locations
.iter()
.flat_map(|l| [l.longitude, l.latitude, l.altitude])
.collect()
}
#[wasm_bindgen(getter)]
pub fn cumulative_distances(&self) -> Vec<f64> {
self.inner.cumulative_distances.clone()
}
#[wasm_bindgen(getter)]
pub fn cumulative_elevation_gains(&self) -> Vec<f64> {
self.inner.cumulative_elevation_gains.clone()
}
#[wasm_bindgen(getter)]
pub fn cumulative_elevation_losses(&self) -> Vec<f64> {
self.inner.cumulative_elevation_losses.clone()
}
#[wasm_bindgen(getter)]
pub fn slopes(&self) -> Vec<f64> {
self.inner.slopes.clone()
}
#[wasm_bindgen(getter)]
pub fn peaks(&self) -> Vec<u32> {
self.inner.peaks.iter().map(|&i| i as u32).collect()
}
#[wasm_bindgen(getter)]
pub fn valleys(&self) -> Vec<u32> {
self.inner.valleys.iter().map(|&i| i as u32).collect()
}
pub fn index_at_distance(&self, dist_km: f64) -> u32 {
self.inner.index_at_distance(dist_km) as u32
}
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())
}
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))
}
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),
)
}
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)
}
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| locs_to_flat(&locs))
.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)
}
}
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,
}