From 96ac82f995e196d399c50bee9739583541f032b7 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Sun, 10 May 2026 11:38:34 -0700 Subject: [PATCH] work on faceting --- rust/doc/src/spv/graphs.md | 19 ++ rust/doc/src/spv/legacy-detail-xml.md | 2 +- rust/pspp/src/file.rs | 2 +- rust/pspp/src/spv/read/graph.rs | 55 +++- rust/pspp/src/spv/read/legacy_bin.rs | 2 +- .../pspp/src/spv/testdata/graph5.expected.svg | 200 ++++++++++++++ rust/pspp/src/spv/testdata/graph5.xml | 246 ++++++++++++++++++ 7 files changed, 509 insertions(+), 17 deletions(-) create mode 100644 rust/pspp/src/spv/testdata/graph5.expected.svg create mode 100644 rust/pspp/src/spv/testdata/graph5.xml diff --git a/rust/doc/src/spv/graphs.md b/rust/doc/src/spv/graphs.md index 6544d9b99b..909db531d0 100644 --- a/rust/doc/src/spv/graphs.md +++ b/rust/doc/src/spv/graphs.md @@ -913,3 +913,22 @@ used. Whether a given variable has a specified format is not considered. The details are muddled; for example, it appears that if one of the variables lacks a specified format entirely, then none of their formats is used. + + +Nice column and row facets found by searching for 2-facetLevel xmls: +/home/blp/pspp/spv/spv/04488e072882e8f8b0c3c687e272d4af/00000000293_-4000816061397075370_chart.xml + +Different contents for different cells: +/home/blp/pspp/spv/spv/271f44f7ba0037acfe63ef8e3e7c8918/0000000003312_-6910783111487815678_chart.xml + +Two dotted (not crossed) facet levels (this is the only example): +/home/blp/pspp/spv/spv/cfe40eafe2c4122adbb5412f3d463bcd/00000000302_7579179872145113090_chart.xml + +Rotated matrix chart with multiple facetLevels: +/home/blp/pspp/spv/spv/301cd2376ccc9f5db2fe0df3aa91eac0/00000000013__chart.xml + +Rotated weird text chart with multiple facetLevels: +/home/blp/pspp/spv/spv/66c4eb85507e7b950ff06a0aad36a466/00000002282__chart.xml + +Normal-ish barchart with multiple facetLevels: +/home/blp/pspp/spv/spv/9538e0d28ff95b21336ea19a551affca/00000000072_2564001948504162306_chart.xml diff --git a/rust/doc/src/spv/legacy-detail-xml.md b/rust/doc/src/spv/legacy-detail-xml.md index e3945ceb88..2d62594490 100644 --- a/rust/doc/src/spv/legacy-detail-xml.md +++ b/rust/doc/src/spv/legacy-detail-xml.md @@ -682,7 +682,7 @@ course, but it is usefully indicative. Each `facetLevel` contains an `axis`, which in turn may contain a [`label`](#the-label-element) for the `facetLevel` and does contain a -`majorTicks` element. +`majorTicks` element with the following attributes: * `labelAngle` Normally 0. The value -90 causes inner column or outer row labels diff --git a/rust/pspp/src/file.rs b/rust/pspp/src/file.rs index adcfe2146c..2955b455a7 100644 --- a/rust/pspp/src/file.rs +++ b/rust/pspp/src/file.rs @@ -131,7 +131,7 @@ impl FileType { where R: Read + Seek, { - let mut buf = vec![0; 512]; + let mut buf = vec![0; 1024]; let mut n = 0; while n < buf.capacity() { let count = reader.read(&mut buf[n..])?; diff --git a/rust/pspp/src/spv/read/graph.rs b/rust/pspp/src/spv/read/graph.rs index 6d220de556..c5fbba5410 100644 --- a/rust/pspp/src/spv/read/graph.rs +++ b/rust/pspp/src/spv/read/graph.rs @@ -507,7 +507,7 @@ impl Visualization { axis_domains[axis] = AxisDomain { domain, formatter: formatter.unwrap_or_else(|| Formatter::default_for_locale(locale)), - label: axis2.and_then(|axis| axis.label.as_ref()), + label: dbg!(axis2.and_then(|axis| axis.label.as_ref())), major_ticks: axis2.and_then(|axis| axis.major_ticks.as_ref()), }; } @@ -758,7 +758,7 @@ impl Visualization { } } } - for (y_index, _y_facet) in y_facets.clone().enumerate() { + for (y_index, y_facet) in y_facets.clone().enumerate() { let rect = rect.subrect(0, nx_facets, y_index, ny_facets); // Y axis. let x = rect.0[Axis2::X].0; @@ -791,19 +791,41 @@ impl Visualization { .set("text-anchor", "end"), ); } - if let Some(label) = &axis_domains[Axis3::Y].label { - let x = x - 10.0 - y_axis_text_width - font_height; - let y = rect.0[Axis2::Y].midpoint(); - graph_svg.append( - svg::node::element::Text::new(label.text()) - .set("x", x) - .set("y", y) - .set("dominant-baseline", "hanging") - .set("text-anchor", "middle") - .set("transform", format!("rotate(-90, {x}, {y})")), - ); - } + + let label = zip(&y_facet, &faceting[Axis2::Y]) + .map(|(value, series)| { + if let Some(label) = &value.label { + label.clone() + } else { + series.formatter.display(&value.value).to_string() + } + }) + .join(", "); + let x = x - 10.0 - y_axis_text_width - font_height; + let y = rect.0[Axis2::Y].midpoint(); + graph_svg.append( + svg::node::element::Text::new(label) + .set("x", x) + .set("y", y) + .set("dominant-baseline", "hanging") + .set("text-anchor", "middle") + .set("transform", format!("rotate(-90, {x}, {y})")), + ); + } + + if let Some(label) = &axis_domains[Axis3::Y].label { + let x = rect.0[Axis2::X].0 - 10.0 - y_axis_text_width - font_height; + let y = rect.0[Axis2::Y].midpoint(); + graph_svg.append( + svg::node::element::Text::new(dbg!(label.text())) + .set("x", x) + .set("y", y) + .set("dominant-baseline", "hanging") + .set("text-anchor", "middle") + .set("transform", format!("rotate(-90, {x}, {y})")), + ); } + for (x_index, x_facet) in x_facets.enumerate() { for (y_index, y_facet) in y_facets.clone().enumerate() { let rect = rect.subrect(x_index, nx_facets, y_index, ny_facets); @@ -5181,4 +5203,9 @@ mod tests { fn graph4() { test_graph_file("graph4"); } + + #[test] + fn graph5() { + test_graph_file("graph5"); + } } diff --git a/rust/pspp/src/spv/read/legacy_bin.rs b/rust/pspp/src/spv/read/legacy_bin.rs index a7d36d2a3a..3631c66131 100644 --- a/rust/pspp/src/spv/read/legacy_bin.rs +++ b/rust/pspp/src/spv/read/legacy_bin.rs @@ -137,8 +137,8 @@ impl DataSources { } } } - assert_eq!(n_maps, count); } + assert_eq!(n_maps, count); (labels.len() as u32).write_le(&mut bin).unwrap(); for (label, frequency) in &labels { diff --git a/rust/pspp/src/spv/testdata/graph5.expected.svg b/rust/pspp/src/spv/testdata/graph5.expected.svg new file mode 100644 index 0000000000..56073e1ebb --- /dev/null +++ b/rust/pspp/src/spv/testdata/graph5.expected.svg @@ -0,0 +1,200 @@ + + + + + + + + + + + + +Value of Variable 4 + + + + + + + + + + + +Value of Variable 4 + + + + + + +.00 + + + +1.00 + + + +2.00 + + + +3.00 + + + +4.00 + + + +5.00 + + + +6.00 + + +4 + + + + + + +.00 + + + +1.00 + + + +2.00 + + + +3.00 + + + +4.00 + + + +5.00 + + + +6.00 + + +5 + + + + + + +.00 + + + +1.00 + + + +2.00 + + + +3.00 + + + +4.00 + + + +5.00 + + + +6.00 + + +7 + + + + + + +.00 + + + +1.00 + + + +2.00 + + + +3.00 + + + +4.00 + + + +5.00 + + + +6.00 + + +6 + + +Frequency + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/rust/pspp/src/spv/testdata/graph5.xml b/rust/pspp/src/spv/testdata/graph5.xml new file mode 100644 index 0000000000..b3123bf6e9 --- /dev/null +++ b/rust/pspp/src/spv/testdata/graph5.xml @@ -0,0 +1,246 @@ + + + + + + + + + + + $WEIGHT;V11;V12;V4 + double;double;double;double + 5;101;4;101 + 4;101;5;101 + 2;101;7;101 + 1;102;4;101 + 7;102;5;101 + 4;102;6;101 + 5;101;4;102 + 1;101;6;102 + 10;102;7;102 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +