use js_sys::Array;
use leaflet_velocity_sys::{
VelocityDataHeader as VelocityDataHeaderSys, VelocityLayer as VelocityLayerSys,
VelocityLayerData as VelocityLayerDataSys, VelocityLayerOption as VelocityLayerOptionSys,
velocity_layer,
};
use leptos::prelude::*;
use leptos_leaflet::prelude::LeafletMapContext;
use wasm_bindgen::JsValue;
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
pub struct VelocityDataHeader {
pub parameter_number: usize,
pub parameter_category: usize,
pub dx: f64,
pub dy: f64,
pub nx: usize,
pub ny: usize,
pub la1: f64,
pub la2: f64,
pub lo1: f64,
pub lo2: f64,
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
pub struct VelocityData {
pub header: VelocityDataHeader,
pub data: Vec<f64>,
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
pub struct VelocityLayerOptions {
pub display_values: bool,
pub max_velocity: f64,
pub frame_rate: f64,
pub color_scale: Vec<String>,
pub line_width: f64,
pub particle_multiplier: f64,
pub particle_age: f64,
pub velocity_scale: f64,
pub keyboard: bool,
pub data: [VelocityData; 2],
}
impl VelocityData {
fn to_js(&self) -> VelocityLayerDataSys {
let header = VelocityDataHeaderSys::new();
header.set_parameter_number(self.header.parameter_number);
header.set_parameter_category(self.header.parameter_category);
header.set_dx(self.header.dx);
header.set_dy(self.header.dy);
header.set_nx(self.header.nx);
header.set_ny(self.header.ny);
header.set_la1(self.header.la1);
header.set_la2(self.header.la2);
header.set_lo1(self.header.lo1);
header.set_lo2(self.header.lo2);
let layer_data = VelocityLayerDataSys::new();
layer_data.set_header(header);
layer_data.set_data(Array::from_iter(
self.data
.iter()
.map(|v| if v.is_nan() { 0. } else { *v })
.map(JsValue::from_f64),
));
layer_data
}
}
#[component(transparent)]
pub fn VelocityLayer(#[prop(into)] data: Signal<Option<VelocityLayerOptions>>) -> impl IntoView {
let map_context = use_context::<LeafletMapContext>().expect("Map context not found");
let (options, set_options) = signal_local(VelocityLayerOptionSys::new());
let (overlay, set_overlay) = signal_local(None::<VelocityLayerSys>);
Effect::new(move |_| {
if let Some(map) = map_context.map() {
set_overlay.set(
velocity_layer(options.read_untracked().as_ref())
.add_to(&map)
.into(),
);
}
});
Effect::new(move |_| {
if let Some(data) = data.read().as_ref() {
let options = options.get_untracked();
options.set_color_scale(Array::from_iter(
data.color_scale.iter().map(|s| JsValue::from_str(s)),
));
options.set_display_values(data.display_values);
options.set_frame_rate(data.frame_rate);
options.set_line_width(data.line_width);
options.set_particle_multiplier(data.particle_multiplier);
options.set_particle_age(data.particle_age);
options.set_velocity_scale(data.velocity_scale);
options.set_max_velocity(data.max_velocity);
options.set_keyboard(data.keyboard);
options.set_data(Array::from_iter([
data.data[0].to_js(),
data.data[1].to_js(),
]));
set_options.set(options);
}
});
Effect::new(move |_| {
let options = options.read();
if let Some(overlay) = overlay.get_untracked() {
overlay.set_options(options.as_ref());
}
});
on_cleanup(move || {
if let Some(prev) = overlay.get_untracked() {
prev._clear_wind();
prev.remove();
}
set_overlay.set(None);
});
}