Skip to content

feat(chartjs): implement choropleth-basic - #11190

Merged
MarkusNeusinger merged 6 commits into
mainfrom
implementation/choropleth-basic/chartjs
Sep 2, 2026
Merged

feat(chartjs): implement choropleth-basic#11190
MarkusNeusinger merged 6 commits into
mainfrom
implementation/choropleth-basic/chartjs

Conversation

@github-actions

@github-actions github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Implementation: choropleth-basic - javascript/chartjs

Implements the javascript/chartjs version of choropleth-basic.

File: plots/choropleth-basic/implementations/javascript/chartjs.js

Parent Issue: #3069


🤖 impl-generate workflow

@claude

claude Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

AI Review - Attempt 1/3

Image Description

Light render (plot-light.png): Warm off-white background matching #FAF8F1. Title "Renewable Electricity Share · choropleth-basic · javascript · chartjs · anyplot.ai" in dark ink, centered top, clearly legible. Bottom legend has six horizontal swatches ("< 10% renewable" ... "No data") with dark-ink labels. Faint Cartesian gridlines cross the canvas (a chart-style grid, not a map graticule). 32 country tiles (square point markers) are positioned by longitude/latitude, filled with the Imprint sequential scale (green → blue) binned into 5 buckets, plus 2 muted-gray "No data" tiles (NG, ID). Each tile carries a 2-letter country code in a contrast-appropriate text color. No landmass outlines, ocean/land distinction, or region boundaries are present — the layout reads as a scatter/tile map rather than a shaded-polygon choropleth. All text is readable against the light background.

Dark render (plot-dark.png): Warm near-black background matching #1A1A17. Title and legend text render in light ink, clearly visible. Tile fill colors are pixel-identical to the light render — confirmed, only chrome flipped. In-tile country-code text colors remain legible per-tile thanks to the luminance-based text-color logic; no dark-on-dark failures observed on any tile, including the muted-gray "No data" tiles. All text readable against the dark background.

Both renders pass the theme-readability check; no light-on-light or dark-on-dark failures found.

Score: 79/100

Category Score Max
Visual Quality 26 30
Design Excellence 12 20
Spec Compliance 10 15
Data Quality 15 15
Code Quality 9 10
Library Mastery 7 10
Total 79 100

Visual Quality (26/30)

  • VQ-01: Text Legibility (6/8) — title scales with length and is clearly legible; legend at 16px; in-tile 12px country codes are small relative to the 3200×1800 canvas but remain legible in both themes
  • VQ-02: No Overlap (6/6) — no overlapping tiles, labels, or legend entries
  • VQ-03: Element Visibility (5/6) — uniform 20px-radius tiles are clearly visible for 32 points; size doesn't vary with value but density is moderate
  • VQ-04: Color Accessibility (2/2) — luminance-based per-tile text color plus redundant country-code labels avoid hue-only signaling
  • VQ-05: Layout & Canvas (3/4) — tiles/legend well distributed but axis bounds (-170..179 / -58..82) are wider than the actual data extent, leaving uneven empty margins
  • VQ-06: Axis Labels & Title (2/2) — Cartesian axes intentionally unlabeled (appropriate for a map-style plot); legend carries descriptive, unit-bearing labels
  • VQ-07: Palette Compliance (2/2) — correctly built from imprint_seq; identical data colors across themes; both backgrounds theme-correct

Design Excellence (12/20)

  • DE-01: Aesthetic Sophistication (4/8) - Well-configured but reads as a plain scatter/tile grid rather than a polished map; no basemap or land/ocean distinction
  • DE-02: Visual Refinement (4/6) - Subtle grid and edge-highlighted tiles, but the grid still reads as chart gridlines rather than a map graticule
  • DE-03: Data Storytelling (4/6) - Real geographic clustering gives some hierarchy, but the green-for-low/blue-for-high mapping works against the intended story

Spec Compliance (10/15)

  • SC-01: Plot Type (2/5) — spec requires a choropleth (shaded regions); implementation is a coordinate-positioned tile/scatter map with no region polygons
  • SC-02: Required Features (2/4) — legend and missing-data handling present; no map projection, no region boundaries
  • SC-03: Data Mapping (3/3) — X=longitude, Y=latitude, color=value; all 32 points correctly visible
  • SC-04: Title & Legend (3/3) — title format correct; legend labels correctly describe bins + no-data

Data Quality (15/15)

  • DQ-01: Feature Coverage (6/6) — full 1%–98% range, all continents represented, includes missing data
  • DQ-02: Realistic Context (5/5) — renewable electricity share is a real, neutral, comprehensible scenario
  • DQ-03: Appropriate Scale (4/4) — values closely track real-world figures (Norway 98%, Brazil 84%, Saudi Arabia 1%, South Korea 9%)

Code Quality (9/10)

  • CQ-01: KISS Structure (2/3) — mostly linear flow, but several small helper functions are needed for the binned continuous scale
  • CQ-02: Reproducibility (2/2) — fully deterministic hardcoded data
  • CQ-03: Clean Imports (2/2) — no unused imports
  • CQ-04: Code Elegance (2/2) — appropriately complex, no fake functionality
  • CQ-05: Output & API (1/1) — follows the mount-node contract correctly

Library Mastery (7/10)

  • LM-01: Idiomatic Usage (4/5) - per-bin datasets for automatic legend, tooltip callbacks, documented plugin lifecycle hook
  • LM-02: Distinctive Features (3/5) - custom plugin using getDatasetMeta/afterDatasetsDraw is Chart.js-specific, though the tile-map approach itself isn't unique to this library

Score Caps Applied

  • None (SC-01=2, not 0; DE-01=4 and DE-02=4, not both ≤2; no other cap conditions met)

Strengths

  • Excellent theme-adaptive rendering: data tile colors are pixel-identical between light and dark renders, while chrome correctly flips with no dark-on-dark or light-on-light failures
  • Highly realistic, factually accurate renewable-electricity-share data for 32 countries spanning every continent (Norway 98%, Brazil 84%, Saudi Arabia 1%, South Korea 9%)
  • Honest, well-documented handling of Chart.js's lack of native geo/polygon support — no chartjs-chart-geo plugin, no fake interactivity, comments transparently explain the tile-map substitution
  • Missing-data handling implemented exactly per spec: two countries render as muted gray "No data" tiles with a dedicated legend entry
  • Per-tile text color chosen via luminance check so every country code stays legible regardless of the underlying fill color
  • Clean, deterministic, near-KISS code; correct mandated title format; legend labels precisely match the bins

Weaknesses

  • SC-01/SC-02: This is not a true choropleth — no shaded region polygons/boundaries, no map projection or basemap. Chart.js core genuinely cannot fill geographic polygons without the disallowed chartjs-chart-geo plugin, so a literal choropleth may be infeasible — but get closer: draw a simple continent-outline basemap as additional line datasets (an array of [lon, lat] points per landmass stroked with t.grid) so the tiles read as sitting on Earth instead of floating in blank space.
  • Sequential color direction is semantically backward: the worst bin ("< 10% renewable") is brand green #009E73 while the best bin ("≥ 65% renewable") is blue — most readers associate green with high renewable share. Reverse the imprint_seq interpolation (or the bin-to-color mapping) so high-renewable countries read as more green.
  • DE-01/DE-02: currently looks like a plain scatter grid rather than a polished map. Add the basemap outline suggested above and tone down or remove the Cartesian x/y grid (it reads as chart gridlines, not a map graticule).
  • VQ-05: axis bounds (-170..179 / -58..82) are wider than the actual data's bounding box, leaving uneven empty margins in the north and far southeast — tighten to the real data extent.

Issues Found

  1. SC-01 MAJOR: No region boundaries/polygons or map projection — a tile/scatter map substitutes for a choropleth
    • Fix: Add a simple continent-outline basemap (line datasets from [lon, lat] arrays) for geographic context; this is the single highest-value repair
  2. DE-03/VQ-07 interaction LOW: Green mapped to the worst renewable-share bin undercuts the data story
    • Fix: Reverse the imprint_seq bin-to-color direction so high-renewable countries render greener
  3. VQ-05 LOW: Axis bounds wider than actual data extent, wasting canvas space
    • Fix: Tighten x.min/x.max and y.min/y.max to the real longitude/latitude bounding box of the 32 countries

AI Feedback for Next Attempt

The core data, code cleanliness, and theme handling are strong — the main gap is that this doesn't yet read as a map. Add a lightweight continent-outline basemap (a few line datasets stroked with the grid color) behind the tiles so the geographic context is visible even without true polygon fills, tighten the axis bounds to the data's real extent to reduce wasted canvas space, and reverse the sequential color direction so high-renewable countries render in brand green rather than the lowest bin. These changes address the SC-01/SC-02 gap directly without reaching for any disallowed plugin.

Verdict: REJECTED

@github-actions github-actions Bot added quality:79 Quality score 79/100 ai-rejected Quality not OK, triggers update labels Sep 2, 2026
@github-actions github-actions Bot added ai-attempt-1 First repair attempt and removed ai-rejected Quality not OK, triggers update labels Sep 2, 2026
Attempt 1/4 - fixes based on AI review
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

🔧 Repair Attempt 1/4

Applied fixes based on AI review feedback.

Status: Repair completed, re-triggering review...


🤖 impl-repair

@claude

claude Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

AI Review - Attempt 2/3

Image Description

Light render (plot-light.png): Warm off-white background matching #FAF8F1. Title "Renewable Electricity Share · choropleth-basic · javascript · chartjs · anyplot.ai" in dark ink, centered top, clearly legible. A lightweight continent-outline graticule (thin gray strokes, no fill) now sits behind the tiles, giving real geographic context — North/South America, Africa/Europe, Asia, and Australia are all recognizable silhouettes. 32 country tiles (square markers) are positioned by longitude/latitude, filled with the Imprint sequential scale binned into 5 buckets; the direction is now correct — high-renewable countries (Norway, Brazil, Colombia, Kenya, New Zealand) render in brand green #009E73, low-renewable countries (Saudi Arabia, South Africa, South Korea) render in blue — plus 2 muted-gray "No data" tiles (NG, ID). Each tile carries a legible 2-letter country code in a contrast-appropriate text color. The bottom legend has six horizontal swatches with dark-ink labels, all readable. Axis bounds are visibly tighter than attempt 1 — less wasted margin around the data extent. All text is readable against the light background.

Dark render (plot-dark.png): Warm near-black background matching #1A1A17. Title and legend text render in light ink, clearly visible. Continent outlines remain visible (stroked in the theme's grid color) against the dark background. Tile fill colors are pixel-identical to the light render — confirmed, only chrome flipped. In-tile country-code text colors remain legible per-tile via the luminance-based logic; no dark-on-dark failures observed on any tile, including the muted-gray "No data" tiles. All text readable against the dark background.

Both renders pass the theme-readability check; no light-on-light or dark-on-dark failures found.

Score: 85/100

Category Score Max
Visual Quality 27 30
Design Excellence 15 20
Spec Compliance 12 15
Data Quality 15 15
Code Quality 9 10
Library Mastery 7 10
Total 85 100

Visual Quality (27/30)

  • VQ-01: Text Legibility (6/8) — title scales with length and is clearly legible; legend at 16px; in-tile 12px country codes are small relative to the 3200×1800 canvas but remain legible in both themes
  • VQ-02: No Overlap (6/6) — no overlapping tiles, labels, legend entries, or basemap strokes
  • VQ-03: Element Visibility (5/6) — uniform 20px-radius tiles are clearly visible for 32 points; size doesn't vary with value but density is moderate
  • VQ-04: Color Accessibility (2/2) — luminance-based per-tile text color plus redundant country-code labels avoid hue-only signaling
  • VQ-05: Layout & Canvas (4/4) — axis bounds (x: -118..182, y: -48..68) are now tightened to the real data extent, removing the excess empty margin flagged in attempt 1
  • VQ-06: Axis Labels & Title (2/2) — Cartesian axes intentionally unlabeled (appropriate for a map-style plot); legend carries descriptive, unit-bearing labels
  • VQ-07: Palette Compliance (2/2) — correctly built from imprint_seq, direction now semantically correct (green = high); identical data colors across themes; both backgrounds theme-correct

Design Excellence (15/20)

  • DE-01: Aesthetic Sophistication (5/8) - The continent-outline basemap meaningfully improves polish; still reads as a positioned-tile map rather than a filled-region choropleth
  • DE-02: Visual Refinement (5/6) - Basemap strokes now read as a map graticule rather than chart gridlines; tiles remain crisply edge-highlighted
  • DE-03: Data Storytelling (5/6) - Green-for-high/blue-for-low now matches reader intuition ("more green = more renewable"), and the geographic layout plus continent context creates a clear visual narrative

Spec Compliance (12/15)

  • SC-01: Plot Type (3/5) — still not a true choropleth (no filled region polygons/projection), but the added continent-outline basemap materially closes the gap given Chart.js core's lack of native geo geometry
  • SC-02: Required Features (3/4) — legend, missing-data handling, and now geographic basemap context are present; still lacks a real map projection or region boundaries
  • SC-03: Data Mapping (3/3) — X=longitude, Y=latitude, color=value; all 32 points correctly visible
  • SC-04: Title & Legend (3/3) — title format correct; legend labels correctly describe bins + no-data

Data Quality (15/15)

  • DQ-01: Feature Coverage (6/6) — full 1%–98% range, all continents represented, includes missing data
  • DQ-02: Realistic Context (5/5) — renewable electricity share is a real, neutral, comprehensible scenario
  • DQ-03: Appropriate Scale (4/4) — values closely track real-world figures (Norway 98%, Brazil 84%, Saudi Arabia 1%, South Korea 9%)

Code Quality (9/10)

  • CQ-01: KISS Structure (2/3) — mostly linear flow, but several small helper functions are needed for the binned continuous scale and basemap construction
  • CQ-02: Reproducibility (2/2) — fully deterministic hardcoded data
  • CQ-03: Clean Imports (2/2) — no unused imports
  • CQ-04: Code Elegance (2/2) — appropriately complex, no fake functionality
  • CQ-05: Output & API (1/1) — follows the mount-node contract correctly

Library Mastery (7/10)

  • LM-01: Idiomatic Usage (4/5) - per-bin datasets for automatic legend, tooltip callbacks, documented plugin lifecycle hook
  • LM-02: Distinctive Features (3/5) - custom plugin using getDatasetMeta/afterDatasetsDraw for label drawing plus a line-dataset basemap is a Chart.js-specific composition, though the tile-map substitution itself isn't unique to this library

Score Caps Applied

  • None (SC-01=3, not 0; DE-01=5 and DE-02=5, not both ≤2; no other cap conditions met)

Strengths

  • All three attempt-1 weaknesses were addressed directly: continent-outline basemap adds geographic context, sequential color direction now runs green-for-high (matching reader intuition), and axis bounds are tightened to the real data extent
  • Excellent theme-adaptive rendering: data tile colors are pixel-identical between light and dark renders, while chrome correctly flips with no dark-on-dark or light-on-light failures
  • Highly realistic, factually accurate renewable-electricity-share data for 32 countries spanning every continent
  • Honest, well-documented handling of Chart.js's lack of native geo/polygon support — no chartjs-chart-geo plugin, no fake interactivity
  • Missing-data handling implemented exactly per spec: two countries render as muted gray "No data" tiles with a dedicated legend entry
  • Per-tile text color chosen via luminance check so every country code stays legible regardless of the underlying fill color

Weaknesses

  • SC-01/SC-02: Still not a literal choropleth — no filled region polygons or map projection. This is a genuine Chart.js core limitation (no chartjs-chart-geo), and the basemap is the right compensating move; further improvement would require accepting the current ceiling on SC-01 or returning NOT_FEASIBLE, which is not clearly warranted since the tile-map + basemap communicates the same regional-value story reasonably well.
  • CQ-01: The half-dozen small helper functions (hexToRgb, lerp, seqRgb, toCss, luminance, binIndex) push slightly past pure KISS, though each is justified by the binned-continuous-scale requirement.
  • Continent outlines are stylized/approximate rather than accurate coastlines — acceptable given the constraints, but worth noting as a simplification.

Issues Found

  1. CQ-01 LOW: Several small helper functions for color math and binning
    • Fix: Not required — the helpers are each single-purpose and justified by the continuous-scale requirement; no action needed unless further consolidation is trivial
  2. SC-01 note (no fix expected): Genuine library ceiling — Chart.js core cannot fill geographic polygons without a disallowed plugin
    • Fix: None available within the "No Workarounds" constraint; current tile+basemap approach is the appropriate compensating design

AI Feedback for Next Attempt

This attempt resolves all three concrete weaknesses flagged previously (basemap, color direction, axis bounds) and the score reflects that. The remaining gap versus a full 100 is structural — a literal filled-polygon choropleth isn't achievable in Chart.js core without the disallowed chartjs-chart-geo plugin — so further gains would come from small polish (e.g., slightly larger in-tile labels, or trimming a helper function or two for KISS) rather than a large restructuring.

Verdict: APPROVED

@github-actions github-actions Bot added quality:85 Quality score 85/100 ai-approved Quality OK, ready for merge and removed quality:79 Quality score 79/100 labels Sep 2, 2026
@MarkusNeusinger
MarkusNeusinger merged commit bc9d54d into main Sep 2, 2026
3 checks passed
@MarkusNeusinger
MarkusNeusinger deleted the implementation/choropleth-basic/chartjs branch September 2, 2026 16:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-approved Quality OK, ready for merge ai-attempt-1 First repair attempt quality:85 Quality score 85/100

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant