Version: [5435] 02viz - Geospatial Visualization Studio 0.9.0

## [0.9.0] - 2026-06-16

- Animated charts (play axis): bar-chart race, Gapminder bubbles and
trends over a time field in ECharts and Plotly
(bar/line/area/scatter/bubble/pie), with axes and colours stable
across frames and items still clickable to the map; pure-Python frame
builder, offline.

All notable changes to **02viz - Geospatial Visualization Studio** are
documented here.
Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) ·
versioning: [SemVer](https://semver.org/).

## [0.9.0] - 2026-06-16

- **Animated charts — a play axis.** Pick an *Animate by ▶* field
(typically a year or sequence) and the chart plays through that
field's values: a **bar-chart race**, **Gapminder-style bubbles**, or
composition/trends unfolding over time. Available for **bar, line,
area, scatter, bubble and pie** in the two interactive engines —
**ECharts** (a timeline with auto-play) and **Plotly** (a slider with
play / pause). A *Play speed* control sets the pace; the play axis
greys out for engines and chart types that cannot animate (Vega-Lite,
matplotlib, and types like box/heatmap/violin), consistent with the
existing engine-first gating.
- **Things animate in place, not jump.** Every frame shares one axis:
categories are merged into a stable, deterministically ordered union
(a district keeps its slot and colour), the value axis is fixed to the
global range across all frames, bubble sizes are scaled once globally
so a value maps to the same radius throughout, and grouped series keep
a consistent colour order. The current frame is shown as a subtitle
(ECharts) and on the slider (Plotly).
- **Still linked to the map.** Animated bars and points carry their
feature ids per frame, so clicking one during playback still selects
the matching features on the canvas (cross-filter dimming pauses while
animating, since each frame redraws the whole chart).
- **No new dependencies.** The frame builder is pure Python
(`transform.build_frames` / `frame_groups` / `union_categories` /
`align_values`); the vendored ECharts and Plotly engines do the rest
entirely offline, and an animated chart still exports as one
self-contained HTML file (with its play controls) like any other.
- Verified headless: real-QGIS checks for the frame builder
(numeric/lexicographic frame ordering, union axis, global bounds,
per-frame ids) and 12 animated engine×type pages, plus 12 animated
pages in headless Chrome — auto-play stepping, slider/play controls
present, zero JS exceptions; the bar-chart race, Gapminder bubbles,
animated pie and lines eyeballed. No regression in the 48 static
pages.

## [0.8.0] - 2026-06-13

- **One dock, three tabs** — the studio is now organised as **Charts ·
Map diagrams · Labels**, all sharing a single layer selector above the
tabs. Same purpose throughout — turn a layer's data into an elegant
visual — across three output surfaces.
- **Labels tab** — one click turns a field into well-placed,
publication-grade labels with a preset (clean subtle-halo / strong
halo / bold / plain), geometry-aware placement, built on native
`QgsPalLayerSettings` (`core/labels.py`). Prints and exports like any
QGIS labeling.
- **Embedded colour editor** — the palette is now an **inline swatch
strip** in the Charts tab: click a swatch to recolour it, `+`/`−` to
resize the palette, any edit switches to a custom palette — no modal
dialog. Added a **chart-title override** field. (The old pop-up
palette editor is gone.)
- **Optional advanced engine: matplotlib / seaborn** — a fourth engine
renders the spec to **publication-grade static figures** (eleven chart
types) and embeds them as a PNG in the same HTML shell, so the viewer
and one-file export work unchanged. It appears in the engine picker;
when its libraries are missing the dock offers a **one-click install**
into the QGIS Python (`python -m pip install --user`, explicit consent
only — `core/requirements.py`). The vendored JS engines stay
zero-dependency and fully offline; this is the opt-in
print/publication path (no chart→map interactivity on static images).
- **Zero2Visual** — the plugin's motto (“from zero to elegant visuals,
fast”) now reads in the dock header and About box; the Hub name and
"02viz" branding are unchanged.
- Verified headless: 86 checks on real QGIS Python (incl. the
matplotlib engine across 11 types, on-canvas labels and the dependency
detector) and 50 interactive chart pages in headless Chrome with zero
JS exceptions; the matplotlib figures eyeballed.

## [0.7.0] - 2026-06-13

- **Map diagrams (on-canvas charts)** — a new *Map diagrams…* dialog
draws native QGIS **pie / bar / stacked-bar / text** diagrams on every
feature, directly on the map canvas, sized in millimetres and coloured
with the studio palette. Built on `QgsDiagramRenderer`, so the
diagrams print, export to print layouts and follow the layer like any
other symbology. Numeric fields are pre-ticked (identifier columns
skipped); attribute-only tables are detected and declined.
- **Custom colour palettes** — the *Colors* selector gains a
**Custom…** entry that opens a swatch editor (add / remove / recolour
via the system colour picker, seeded from the active palette). The
result is written straight into the theme palette, so it recolours
ECharts, Plotly and Vega-Lite identically — single charts and Explore
dashboards alike.
- **Fixed: charts blank in the dock but fine on export** — the
embedded viewer gave the chart container no definite height until
after its first layout pass, so ECharts/Plotly/Vega measured a height
of 0 and painted nothing (while export-to-browser worked). The chart
box is now absolutely sized and re-fits once the view settles; the
dock also nudges a resize on load. Added an **↗ open-in-browser**
escape hatch and surfaced the active web backend (webengine / webkit)
in the status line.
- **Engine-first workflow** — the dock now asks for the **Engine
first**, then the chart **Type**, with unsupported types greyed out
for the chosen renderer from the start.
- **Explore ignores identifier columns** — `fid`, `id`, `gid`, `oid`,
`uuid`, `objectid`, `*_id`, and provider primary keys are no longer
profiled into meaningless histograms or count bars on the dashboard.
- **Publication-grade visual polish** — one shared type system across
all three engines, soft dashed horizontal gridlines (no vertical
chart-junk), clean card-style tooltips, rounded bar tops and minimal
value axes — output reads at Tableau quality.
- Verified headless: 73 checks on real QGIS Python (incl. the
on-canvas diagram renderer and the identifier-column filter) and 50
chart pages in headless Chrome with zero JS exceptions; the polished
output eyeballed across engines and themes.

## [0.6.0] - 2026-06-12

- **Six new advanced chart types** (17 total): **Mean ± σ band** (line
with a shaded ±1 standard-deviation envelope), **Mean ± σ bars** (bars
with std-dev whiskers), **Density (KDE)** (Gaussian kernel density
curves, optionally one per group), **Violin plot** (mirrored KDE
shapes with median dots), **Radar / spider** (multi-axis comparison
with per-axis scaling) and **Pareto (80/20)** (descending bars plus a
cumulative-share line on a second axis). All statistics — sample
standard deviation, Silverman-bandwidth KDE, violin polygons,
cumulative shares — are computed in pure Python and rendered by all
three engines from one spec; only radar is greyed out for Vega-Lite
(the grammar has no polar coordinates).
- **Color palettes** — a new *Colors* selector with 8 curated palettes
(Vivid, Colorblind safe, Viridis, Sunset, Ocean, Earth, Berry,
Grayscale print). The chosen palette overrides the theme palette
identically in ECharts, Plotly and Vega-Lite, for single charts and
Explore dashboards alike.
- Engine correctness fixes caught during visual verification: ECharts
custom-series whiskers and violins now set explicit axis extents
(custom series don't drive autoscaling, so +σ caps and violin tails
were clipped) and error whiskers use a contrasting colour; Vega-Lite
multi-series density sets `stack: null` (stacked-area zero-imputation
turned smooth KDE curves into a sawtooth) and the violin ranged area
dropped its `order` channel (it faceted every violin into degenerate
per-row paths).
- Verified headless: 71 checks on real QGIS Python and 50 chart pages
in headless Chrome with zero JS exceptions; every new engine×type
screenshot eyeballed.

## [0.5.0] - 2026-06-12

- **Third engine: Vega-Lite** — vendored `vega` + `vega-lite` (BSD-3,
compiled in the page, no vega-embed), fully offline. Renders 9 of the
11 chart types from the same spec contract (treemap/sunburst are not
part of the Vega-Lite grammar), including layered box plots from
precomputed quartiles and labelled heatmaps. Chart→map clicks and
map→chart cross-filter dimming work exactly as in the other engines.
- **Engine-aware chart types** — engines declare what they can draw
(`ChartEngine.supports`); the dock greys out unsupported types and
jumps to the nearest supported one.
- **Heatmap label contrast fixed in all engines** — on sequential
ramps, low-value cells are light, so white labels were invisible
there; white is now used only on dark cells (both extremes on
diverging ramps, upper end on sequential ones).

## [0.4.0] - 2026-06-12

- **Map → chart cross-filter**: selecting features on the canvas
instantly dims every non-selected bar, slice and point — in the single
chart and across all dashboard tiles — without a re-render (ECharts
per-item opacity, Plotly native `selectedpoints`). A freshly rendered
chart immediately reflects the current selection.
- **Crash-safe selection bridge**: chart clicks now reach QGIS through
the page title (`titleChanged`) instead of
`addToJavaScriptWindowObject`/`QWebChannel`, fixing an
access-violation crash on QGIS builds that ship the legacy QtWebKit
stack. One code path for WebKit and WebEngine; vendored
`qwebchannel.js` removed.
- The chart→map bridge logic is now covered by headless real-QGIS
tests (38 checks) and the cross-filter by the headless-Chrome harness
(24 pages).

yes

geo140195philo

2026-06-15T22:31:41.361526+00:00

3.28.0

4.99.0

None

no

Version management

Plugin details