Add Treemap and Sunburst trace types; upgrade plotly.js from 3.0.1 to 3.6.0#406
Conversation
Add hierarchical Treemap and Sunburst traces, mirroring the existing Pie trace pattern: - Treemap<V>: labels/parents/values hierarchy with BranchValues, plus Tiling (Packing) and PathBar (Side) helper structs - Sunburst<V>: same hierarchy plus Leaf, rotation and inside_text_orientation - New Treemap/Sunburst PlotType variants and top-level re-exports - treemapcolorway/extendtreemapcolors Layout options - Unit tests, doctests, basic_charts examples and book pages Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the shared common::Marker on Treemap with a treemap-specific
Marker exposing the treemap-only attributes: pad (Pad{t,l,r,b}),
corner_radius, and depth_fade (true/false/"reversed"). The shared
color/colorscale/colorbar/line/pattern machinery is retained, and the
scatter-only fields (size, symbol, ...) that don't apply to treemaps are
dropped. Showcased in the styled_treemap example.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…lone subcommands) (#4080) * feat(viz): treemap & sunburst hierarchy panels + standalone subcommands Add categorical part-to-whole hierarchy charts to `viz`, backed by the Treemap/Sunburst trace types from the plotly.rs fork (PR plotly/plotly.rs#406). `viz smart` now adds a hierarchy panel when the dataset has 2+ genuine (String) low-cardinality dimensions, auto-selecting the chart by depth per visualization best practice: a treemap for a shallow (2-level) hierarchy (area encodes size for accurate comparison) and a sunburst for a deeper (3-level) one (rings emphasize parent-child structure). Override with `--hierarchy-style auto|treemap|sunburst`. The chosen dimensions keep their own frequency bars; the panel is domain-based, so (like map/geo/3D) it renders via the inline path. Restricting dims to String type with cardinality >= 3 keeps numeric codes/booleans out and avoids forcing small dashboards inline. Also add standalone `viz treemap` / `viz sunburst` subcommands (--cols for the hierarchy levels, optional additive --value/--agg). Treemap tiles use the fork's treemap-specific Marker (rounded corners, inner padding, white outline). - one-pass leaf accumulation + pure flat-array builder (top-N per level with an "Other (k)" bucket, path-joined ids, rolled-up branchvalues=total) - 5 unit + 6 integration tests - regenerated docs/help/viz.md, the qsv-viz MCP skill, and the examples gallery (incl. two `viz smart --dictionary infer` dashboards showcasing both charts) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(viz): validate --value measure for treemap/sunburst (roborev #3190) In value mode, `accumulate_hierarchy_counts` silently coerced parse failures to 0.0 and accepted negative / non-finite values, so a typo'd or non-numeric --value column produced a blank or misleading area chart while the command succeeded. Now non-empty value cells must parse to a finite, non-negative number; empty cells stay a benign missing measure (skipped). Unusable cells are skipped and tallied with a warning, and an all-unusable measure column returns a clear error instead of charting zeros. Adds value-sum and all-invalid-value tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(viz): error on any invalid --value cell for treemap/sunburst (roborev #3191) Follow-up to #3190: invalid non-empty --value cells only warned (when at least one valid value existed), so a partially-malformed measure column still produced a "successful" treemap/sunburst with rows silently dropped — quietly misstating every part-to-whole proportion. Make any unusable measure cell (non-numeric, negative, or non-finite) a hard error reporting the bad-cell count; empty cells remain a benign missing measure (skipped). Adds a mixed valid/invalid test alongside the all-invalid case. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(viz): add standalone treemap & sunburst figures to the examples gallery The gallery showcased the treemap/sunburst hierarchy only via the `viz smart` dashboards; add the two standalone chart types as individual figures too: - treemap: customer_spend plan -> region, sized by summed monthly_spend (exercises the validated --value path + the treemap-specific marker) - sunburst: sales_sample region -> product_category -> payment_method Regenerated gallery.html (29 figures). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replaces the three vendored plotly.min.js copies (plotly, plotly_static, docs/book) with v3.6.0 and bumps the pinned CDN version strings in plot.rs, the jupyter notebook template, plotly_static template, and the book header. Closes plotly#407 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Surfaces the user-facing attributes added across plotly.js 3.1.0 through 3.6.0 (now bundled), following the existing Option<T> + FieldSetter pattern: - Layout: hoversort (HoverSort), hoveranywhere, clickanywhere - Axis: zerolinelayer (ZeroLineLayer), minorloglabels, modebardisable (ModeBarDisable), ticklabelposition (TickLabelPosition), unifiedhovertitle (UnifiedHoverTitle), and ExponentFormat::SIExtended - Legend: maxheight - Configuration: displayNotifier - common::Label (hover labels): showarrow - common::Pattern: path (arbitrary SVG path fill) - Candlestick/Ohlc: hovertemplate - hovertemplatefallback / texttemplatefallback across applicable traces These are additive (new fields/enums/variants). Because FieldSetter and layout_structs generate per-field Restyle/Relayout enum variants, adding any field is a semver-breaking change; this targets the next breaking release (0.15.0), consistent with the Treemap/Sunburst additions already on this branch. Refs plotly#407 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Plotly.js pattern schema marks `path` as arrayOk (pathsrc present), like shape/bgcolor/fgcolor/size/solidity. Modeling it as Option<String> prevented per-point custom SVG path fills. Switch to Option<Dim<String>> so FieldSetter generates both scalar and array setters, and add a serialize_pattern_path test covering both forms. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Pushed two more commits:
Updated the PR description with the Plotly.js 3.0.1→3.6.0 upgrade (#407) and the full list of newly exposed attributes. All builds/clippy/nightly-fmt/tests pass locally (the only failing tests are the pre-existing browser-export ones that need a chromedriver binary). |
Plotly's `insidetextorientation` attribute expects the full words `horizontal`/`radial`/`tangential`/`auto`, but the field reused the general `Orientation` enum which serializes to single-letter codes (`h`/`v`/`r`/`t`). `Orientation::Radial` emitted `"r"`, which plotly.js silently coerces to the default `"auto"` — so setting a radial sunburst orientation was a no-op. Add a sunburst-specific `InsideTextOrientation` enum (`#[serde(rename_all = "lowercase")]` -> full words) and switch the field to it. The shared `Orientation` enum is left untouched so bars, boxes, legends and sankey keep their correct `h`/`v` codes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Sunburst inside_text_orientation setter takes the Sunburst-specific InsideTextOrientation enum, not common::Orientation. Update the example import and call so the basic-charts example crate compiles. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…l sunburst text, unified hover) (#4085) * feat(viz): adopt plotly.js 3.6.0 attributes (richer OHLC hover, radial sunburst text, unified hover) Upgrades the dathere plotly fork pin to its plotly.js 3.0.1 -> 3.6.0 branch (feat/treemap-sunburst-traces @ aeb1e59, upstream PR plotly/plotly.rs#406) and adopts three new additive, non-breaking attributes in `viz`: - Candlestick/OHLC `hover_template`: clean Open/High/Low/Close readout with `<extra></extra>` to drop the trace-name box, plus a defensive `hover_template_fallback` (financial traces have known hover-variable gaps). - Sunburst `inside_text_orientation(Radial)`: label+value+percent runs along each ring's spoke so deep-path sectors stay legible (standalone + smart panel). - `x unified` hover (Layout `hover_mode`) scoped to ordered-x chart kinds (line, candlestick, ohlc) — one tooltip per x across series. Excludes scatter/bar/box; the smart dashboard builds its own layout and is untouched. Regenerates the example gallery (now CDN plotly 3.6.0) so the figures reflect the new attributes. Adds/extends tests for all three (viz_candlestick, viz_ohlc, viz_sunburst_standalone, new viz_line_unified_hover). Verified: 126 viz tests pass; clippy clean; candlestick hover confirmed to resolve %{open}/%{high}/%{low}/%{close} at runtime in a browser. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(viz): emit valid "radial" sunburst inside-text orientation (#3211) `Sunburst::inside_text_orientation(Orientation::Radial)` serialized to `"insidetextorientation":"r"` — the shared `Orientation` enum's single-letter code (correct for bars/boxes/legends/sankey, wrong here). plotly.js 3.6.0 only accepts the full words `horizontal`/`radial`/`tangential`/`auto` and silently coerces `"r"` to the default `"auto"`, so the radial sunburst text was a no-op and the test locked in the invalid value. Fix in the dathere plotly fork by adding a sunburst-specific `InsideTextOrientation` enum that serializes to full words, leaving the general `Orientation` enum untouched. Re-pin `Cargo.lock` at the fork branch tip (3c185f8), switch the call site, drop the now-unused `Orientation` import, update the test to assert `"radial"`, and regenerate the gallery. Browser-verified against plotly.js 3.6.0: `_fullData` resolves the orientation to `"radial"` (not coerced to `"auto"`) and labels render radially along each ring's spoke. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(viz): regenerate --dictionary infer gallery figures (radial sunburst) Refresh the two LLM-dependent smart-dashboard gallery artifacts (smart_dict_treemap.html, smart_dict_sunburst.html) from the current qsv binary via describegpt `--dictionary infer` (local LM Studio, google/gemma-3-27b), and rebuild gallery.html to reference them. The sunburst dict figure now carries the valid `"insidetextorientation":"radial"` attribute (was the no-op `"r"`); both pages embed plotly.js 3.6.0 from the CDN. Browser-verified: 14 and 5 panels respectively, 0 render errors, sunburst resolves to radial. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Thanks for the PR. I will review it soon |
|
Great! FYI, once this lands, I have dathere#2 that's based on this. BTW, if you want to check out how qsv uses plotly.rs, check https://github.com/dathere/qsv/wiki/Visualization |
|
@jqnatividad Thank you very much for the contribution. |
|
Thanks for merging this PR @andrei-ng . Per your guidance, I submitted #410 to add choropleth map support. FYI, I plan on submitting Violin plot support and experimenting on making it easy to create something like the Gapminder animation (https://plotly.com/javascript/gapminder-example/). |
Adds two hierarchical trace types —
TreemapandSunburst— to theplotlycrate, upgrades the bundled Plotly.js, and exposes the new attributes that came with it. The trace types follow the existingPiepattern and share Plotly'slabels/parents/ids/valuesdata model.What's added
Trace types
Treemap<V>: hierarchy fields (branch_values,count,level,max_depth),domain, full text/hover set,Tiling(Packing) andPathBar(Side) helper structs, and a dedicatedtreemap::Markerexposing the treemap-onlypad,corner_radius, anddepth_fadeattributes (on top of the shared color/colorscale/colorbar/line/pattern machinery).Sunburst<V>: same hierarchy/text/hover set plusLeaf,rotation, andinside_text_orientation.PlotType::Treemap/PlotType::Sunburstvariants and top-level re-exports; newtreemapcolorway/extendtreemapcolorslayout options.Bundled Plotly.js upgrade (#407)
plotly.min.jscopies (plotly/resource,plotly_static/resource,docs/book) and the pinned CDN version strings inplot.rs, the Jupyter template,plotly_static/template.rs, and the book header.New 3.1–3.6 attributes exposed
Additive bindings for the user-facing attributes added across Plotly.js 3.1.0 → 3.6.0, following the existing
Option<T>+FieldSetterpattern:Layout:hoversort(HoverSort),hoveranywhere,clickanywhereAxis:zerolinelayer(ZeroLineLayer),minorloglabels,modebardisable(ModeBarDisable),ticklabelposition(TickLabelPosition),unifiedhovertitle(UnifiedHoverTitle), andExponentFormat::SIExtendedLegend:maxheightConfiguration:displayNotifiercommon::Label(hover labels):showarrowcommon::Pattern:path(arbitrary SVG path fill,arrayOk)Candlestick/Ohlc:hovertemplatehovertemplatefallback/texttemplatefallbackacross applicable tracesTests & docs
basic_chartsexamples, and book pages.Compatibility note
Because
FieldSetter/layout_structsgenerate a per-field variant in theRestyle*/RelayoutLayoutenums, adding any field is a semver-breaking change (cargo-semver-checksflagsenum_variant_added). Combined with the newPlotTypeandExponentFormatvariants, this targets the next breaking release (0.15.0).I used Claude Code Opus 4.8 and reviewed the code and tested it with my project - qsv.
See https://github.com/dathere/qsv/wiki/Visualization
Closes #405
Closes #407