use serde_json::Value as J;
use wasm_bindgen::prelude::*;
use crate::data::Value;
use crate::geom::sf::GeomSf;
use crate::prelude::*;
use crate::spatial::SfProjection;
#[wasm_bindgen]
pub fn render_geo(spec_json: &str) -> Result<String, JsValue> {
render_geo_impl(spec_json).map_err(|e| JsValue::from_str(&e))
}
fn render_geo_impl(spec_json: &str) -> Result<String, String> {
let v: J = serde_json::from_str(spec_json).map_err(|e| format!("bad spec JSON: {e}"))?;
let geom = v["geometry"]
.as_array()
.ok_or("spec.geometry must be an array of WKT strings")?;
let geometry: Vec<Value> = geom
.iter()
.map(|g| Value::Str(g.as_str().unwrap_or_default().to_string()))
.collect();
let mut cols: Vec<(String, Vec<Value>)> = vec![("geometry".to_string(), geometry)];
let mut aes = Aes::new();
let has_fill = v.get("fill").and_then(|f| f.as_array()).is_some();
if let Some(fill) = v.get("fill").and_then(|f| f.as_array()) {
let col = fill
.iter()
.map(|x| x.as_f64().map(Value::Float).unwrap_or(Value::Na))
.collect();
cols.push(("__fill".to_string(), col));
aes = aes.fill("__fill");
}
if let Some(label) = v.get("label").and_then(|l| l.as_array()) {
let col = label
.iter()
.map(|x| Value::Str(x.as_str().unwrap_or_default().to_string()))
.collect();
cols.push(("__label".to_string(), col));
aes = aes.label("__label");
}
let num = |k: &str, d: u32| v.get(k).and_then(|x| x.as_u64()).unwrap_or(d as u64) as u32;
let (width, height) = (num("width", 800), num("height", 600));
let projection = match v.get("projection").and_then(|x| x.as_str()) {
Some("mercator") => SfProjection::Mercator,
_ => SfProjection::PlateCarree,
};
let mut plot = GGPlot::new(cols)
.aes(aes)
.geom_sf_with(GeomSf::default().project(projection))
.coord_sf()
.theme(theme_minimal());
if has_fill {
plot = plot.scale_fill_viridis_c();
}
if let Some(t) = v.get("title").and_then(|x| x.as_str()) {
plot = plot.title(t);
}
plot.render_svg_native_with_size(width, height)
.map_err(|e| format!("render failed: {e:?}"))
}
#[cfg(feature = "canvas")]
#[wasm_bindgen]
pub fn render_scatter_rgba(spec_json: &str) -> Result<Vec<u8>, JsValue> {
render_scatter_impl(spec_json).map_err(|e| JsValue::from_str(&e))
}
#[cfg(feature = "canvas")]
fn render_scatter_impl(spec_json: &str) -> Result<Vec<u8>, String> {
let v: J = serde_json::from_str(spec_json).map_err(|e| format!("bad spec JSON: {e}"))?;
let nums = |key: &str| -> Result<Vec<Value>, String> {
Ok(v[key]
.as_array()
.ok_or_else(|| format!("spec.{key} must be an array of numbers"))?
.iter()
.map(|x| x.as_f64().map(Value::Float).unwrap_or(Value::Na))
.collect())
};
let mut cols: Vec<(String, Vec<Value>)> =
vec![("x".to_string(), nums("x")?), ("y".to_string(), nums("y")?)];
let mut aes = Aes::new().x("x").y("y");
let has_color = v.get("color").and_then(|c| c.as_array()).is_some();
if let Some(c) = v.get("color").and_then(|c| c.as_array()) {
let col = c
.iter()
.map(|s| Value::Str(s.as_str().unwrap_or_default().to_string()))
.collect();
cols.push(("color".to_string(), col));
aes = aes.color("color");
}
let num = |k: &str, d: u32| v.get(k).and_then(|x| x.as_u64()).unwrap_or(d as u64) as u32;
let (width, height) = (num("width", 800), num("height", 600));
let mut plot = GGPlot::new(cols)
.aes(aes)
.geom_point()
.theme(theme_minimal());
if has_color {
plot = plot.scale_color_brewer(crate::scale::palettes::PaletteName::Set1);
}
if let Some(t) = v.get("title").and_then(|x| x.as_str()) {
plot = plot.title(t);
}
let (_, _, rgba) = plot
.render_rgba_with_size(width, height)
.map_err(|e| format!("render failed: {e:?}"))?;
Ok(rgba)
}