diff --git a/src/writer/vegalite/layer.rs b/src/writer/vegalite/layer.rs index 2ac08834..1331a0aa 100644 --- a/src/writer/vegalite/layer.rs +++ b/src/writer/vegalite/layer.rs @@ -2009,9 +2009,8 @@ impl BoxplotRenderer { // Build encoding templates for y and y2 fields let mut y_encoding = summary_prototype["encoding"][value_var1].clone(); y_encoding["field"] = json!(value_col); - let mut y2_encoding = summary_prototype["encoding"][value_var1].clone(); - y2_encoding["field"] = json!(value2_col); - y2_encoding["title"] = Value::Null; // Suppress y2 title to prevent "y, y2" axis label + // y2 is a secondary position channel — Vega-Lite only allows field/datum/value + let y2_encoding = json!({"field": value2_col}); // Lower whiskers (rule from y to y2, where y=q1 and y2=lower) let mut lower_whiskers = create_layer( diff --git a/src/writer/vegalite/mod.rs b/src/writer/vegalite/mod.rs index 72752676..fb1644fb 100644 --- a/src/writer/vegalite/mod.rs +++ b/src/writer/vegalite/mod.rs @@ -296,9 +296,9 @@ fn build_layer_encoding( continue; } - // Skip geometry aesthetic - it is structural (consumed by SpatialRenderer - // to build GeoJSON Features), not a visual encoding channel. - if aesthetic == "geometry" { + // Skip structural aesthetics that are consumed by renderers during data + // preparation but are not visual encoding channels in Vega-Lite. + if aesthetic == "geometry" || aesthetic == "type" { continue; } @@ -3105,4 +3105,28 @@ mod tests { ); } } + + #[test] + #[cfg(feature = "duckdb")] + fn test_boxplot_schema_validation() { + use crate::reader::{DuckDBReader, Reader}; + + let reader = DuckDBReader::from_connection_string("duckdb://memory").unwrap(); + reader + .execute_sql( + "CREATE TABLE box_data AS + SELECT 'A' AS grp, generate_series * 1.0 AS value FROM GENERATE_SERIES(1, 10) + UNION ALL + SELECT 'B' AS grp, generate_series * 1.0 + 4.0 AS value FROM GENERATE_SERIES(1, 10)", + ) + .unwrap(); + + let spec = reader + .execute("SELECT * FROM box_data VISUALISE grp AS x, value AS y DRAW boxplot") + .unwrap(); + + let writer = VegaLiteWriter::new(); + let json_str = writer.render(&spec).unwrap(); + assert_valid_vegalite(&json_str); + } }