Skip to content

feat(matplotlib): implement map-tile-background#7753

Merged
MarkusNeusinger merged 6 commits into
mainfrom
implementation/map-tile-background/matplotlib
May 27, 2026
Merged

feat(matplotlib): implement map-tile-background#7753
MarkusNeusinger merged 6 commits into
mainfrom
implementation/map-tile-background/matplotlib

Conversation

@github-actions
Copy link
Copy Markdown
Contributor

Implementation: map-tile-background - python/matplotlib

Implements the python/matplotlib version of map-tile-background.

File: plots/map-tile-background/implementations/python/matplotlib.py

Parent Issue: #3756


🤖 impl-generate workflow

github-actions Bot added 2 commits May 27, 2026 15:18
Regen from quality 91. Addressed:
- CQ-01: removed helper functions (lat_lon_to_tile, tile_to_lat_lon, fetch_tile,
  get_map_tiles) — all tile math inlined as flat script
- Canvas: corrected to figsize=(8, 4.5) dpi=400 → 3200×1800 px (was 16×9 dpi=300)
- ANYPLOT_THEME: full light/dark handling with theme-adaptive tile provider
  (OSM standard for light, CartoDB dark_all for dark)
- Colormap: replaced forbidden YlOrRd with imprint_seq (#009E73 → #4467A3)
- Title: added python · language token; updated anyplot.ai suffix
- Save: plot-{THEME}.png pattern (was bare plot.png)
- VQ-07: added attraction name labels with ELEVATED_BG background boxes
- Chrome: all text/spines/colorbar/attribution use INK/INK_SOFT/INK_MUTED tokens
@claude
Copy link
Copy Markdown
Contributor

claude Bot commented May 27, 2026

AI Review - Attempt 1/3

Image Description

Light render (plot-light.png): The plot renders on a warm off-white (#FAF8F1) figure background with an OpenStreetMap tile layer covering the map area showing central Rome. Twelve scatter markers are overlaid, colored and sized by daily visitor count using the imprint_seq colormap (brand green #009E73 at low counts transitioning to blue #4467A3 at high counts). A colorbar on the right is labeled "Daily Visitors" with a range of ~2,000–7,200. Each attraction has an annotation label in a rounded, semi-transparent elevated-background box. The title "Rome Tourist Attractions · map-tile-background · python · matplotlib · anyplot.ai" is clearly readable in dark ink. Axis tick labels use degree notation (12.430°E / 41.880°N). The "© OpenStreetMap contributors" attribution box is in the bottom-right corner. Top and right spines are removed. All text is readable against the light background. Legibility: PASS.

Dark render (plot-dark.png): The same layout renders on a warm near-black (#1A1A17) figure background, with CARTO dark all-tiles providing an appropriate dark map surface. Data marker colors are identical to the light render (same imprint_seq progression from green to blue). Title, axis labels, tick labels, colorbar labels, and attribution text all use light INK tokens and are clearly readable against the dark background. The annotation label boxes use ELEVATED_BG (#242420) with matching alpha, providing contrast from both the dark map tiles and the dark figure background. No dark-on-dark failures observed. Legibility: PASS.

Score: 87/100

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

Visual Quality (27/30)

  • VQ-01: Text Legibility (7/8) — All font sizes explicitly set; title, axis labels, and ticks are well-proportioned. Attraction name labels at fontsize=5 are functional but borderline small when the image is scaled to mobile (~400 px) widths.
  • VQ-02: No Overlap (4/6) — Label crowding in the dense western cluster (Vatican Museums, St. Peter's Basilica, Castel Sant'Angelo) causes labels to sit uncomfortably close; background boxes prevent full overlap but the separation is marginal.
  • VQ-03: Element Visibility (6/6) — Marker sizes (60–350 pts²) and alpha=0.9 are well-suited to 12 sparse points; edgecolors=PAGE_BG provides clean definition.
  • VQ-04: Color Accessibility (2/2) — imprint_seq is perceptually uniform and CVD-safe; size redundancy further aids discrimination.
  • VQ-05: Layout & Canvas (4/4) — Map fills the canvas well; colorbar placement is appropriate; no content cut off; canvas is 3200×1800 px as expected.
  • VQ-06: Axis Labels & Title (2/2) — "Longitude" / "Latitude" with formatted degree notation; descriptive title prefix.
  • VQ-07: Palette Compliance (2/2) — imprint_seq correctly applied for continuous visitor data; figure backgrounds are #FAF8F1 (light) / #1A1A17 (dark); all chrome tokens are theme-correct in both renders.

Design Excellence (14/20)

  • DE-01: Aesthetic Sophistication (6/8) — The theme-adaptive tile provider choice (OSM for light, CARTO dark for dark) demonstrates genuine design sophistication. Dual encoding of visitor count via size and color creates layered depth. Well above typical defaults.
  • DE-02: Visual Refinement (4/6) — Grid correctly omitted for a map context; L-shaped spine frame; styled colorbar and attribution box with theme-matching colors. Good refinement, though the colorbar outline and label positioning could be more polished.
  • DE-03: Data Storytelling (4/6) — Dual encoding creates clear visual hierarchy: the Colosseum and Vatican dominate visually, immediately communicating they are the top attractions. Geographic map context tells a natural spatial story.

Spec Compliance (15/15)

  • SC-01: Plot Type (5/5) — Correct tile-background map with data points overlaid.
  • SC-02: Required Features (4/4) — Multiple tile providers (OSM + CARTO), auto-fit bounding box zoom, markers with contrast edge, proper provider attribution all present.
  • SC-03: Data Mapping (3/3) — Longitude on x-axis, latitude on y-axis; full geographic extent shown with formatted tick labels.
  • SC-04: Title & Legend (3/3) — Title follows {Descriptive Title} · {spec-id} · python · matplotlib · anyplot.ai format exactly. Colorbar labeled "Daily Visitors."

Data Quality (14/15)

  • DQ-01: Feature Coverage (6/6) — All spec features demonstrated: tile background, point overlay, color + size encoding, labels, attribution.
  • DQ-02: Realistic Context (5/5) — Rome tourist attractions with visitor counts is a neutral, comprehensible, real-world scenario.
  • DQ-03: Appropriate Scale (3/4) — Coordinates are correct for Rome. Visitor count values are in the right relative ordering but are approximately 3× lower than real daily figures (Colosseum ~7,200/day vs. ~20,000+ actual). Proportions plausible but not factually precise.

Code Quality (10/10)

  • CQ-01: KISS Structure (3/3) — Linear structure: imports → tokens → data → tile fetch → plot → save. No functions or classes.
  • CQ-02: Reproducibility (2/2) — No random data; all values are hardcoded constants — fully deterministic.
  • CQ-03: Clean Imports (2/2) — All imports (io, math, os, urllib.request, matplotlib, numpy, PIL) are used.
  • CQ-04: Code Elegance (2/2) — Clean, Pythonic code; tile stitching loop is necessary complexity; no over-engineering.
  • CQ-05: Output & API (1/1) — Saves as plot-{THEME}.png with no bbox_inches='tight'; current matplotlib API.

Library Mastery (7/10)

  • LM-01: Idiomatic Usage (4/5) — Uses ax methods throughout; ax.imshow with geographic extent for raster data; FuncFormatter for axis tick labels; plt.colorbar — all idiomatic patterns.
  • LM-02: Distinctive Features (3/5) — ax.imshow with lat/lon extent to display stitched map tiles is a meaningful matplotlib-specific technique. FuncFormatter for degree notation adds flavor. However, the heavy lifting (tile fetching, PIL stitching) is done outside matplotlib rather than leveraging matplotlib-native map infrastructure.

Score Caps Applied

  • None

Strengths

  • Theme-adaptive tile provider selection (OSM light / CARTO dark) is a sophisticated design decision that goes beyond the spec requirement
  • Dual encoding (size + color) for visitor count creates strong visual hierarchy with the Colosseum and Vatican immediately prominent
  • Full theme-adaptive chrome with INK token cascade correctly applied to colorbar, attribution box, annotation labels, spines, and tick params in both renders
  • No bbox_inches='tight' — canvas is exactly 3200×1800 px as required
  • imprint_seq colormap correctly chosen for single-polarity continuous visitor data

Weaknesses

  • Attraction name labels at fontsize=5 are borderline small at mobile widths (~400 px); increase to fontsize=6 and adjust xytext offsets to maintain map readability
  • Label crowding in the dense Vatican/Castel Sant'Angelo/St. Peter's cluster — several labels sit too close; increase xytext offsets or use directional offsets per attraction to spread labels and reduce near-overlap
  • Daily visitor counts are ~3× lower than actual figures (Colosseum is ~20,000+/day, not 7,200); update values to better reflect real-world scale while preserving correct relative ordering
  • DE-02 headroom: colorbar styling and annotation label placement could be more intentional to push visual refinement further

Issues Found

  1. VQ-02 LABEL CROWDING: Vatican Museums, St. Peter's Basilica, and Castel Sant'Angelo labels are too close in the dense western cluster, with borderline near-overlap.
    • Fix: Use per-attraction directional xytext offsets (e.g., Vatican Museums → offset left, St. Peter's → offset down-left) to spread labels and eliminate near-overlap.
  2. VQ-01 SMALL LABELS: fontsize=5 for attraction labels is borderline at mobile scale.
    • Fix: Increase annotation fontsize to 6 and adjust xytext offsets accordingly.
  3. DQ-03 VISITOR COUNTS: Values underestimate real daily visitors by ~3×.
    • Fix: Scale up visitor counts to be closer to real-world figures (e.g., Colosseum 20,000+, Vatican 18,000+, Trevi 15,000+).

AI Feedback for Next Attempt

Three targeted improvements: (1) Increase attraction label fontsize from 5 to 6 and use per-attraction directional xytext offsets to eliminate label crowding in the western Vatican/Castel cluster. (2) Scale visitor count values upward to better reflect actual Rome tourist figures (~3× current values). (3) Minor DE-02 polish: refine annotation label placement so they don't crowd the marker symbols, and improve colorbar styling.

Verdict: REJECTED

@github-actions github-actions Bot added quality:87 Quality score 87/100 ai-rejected Quality not OK, triggers update labels May 27, 2026
@github-actions github-actions Bot added ai-attempt-1 First repair attempt and removed ai-rejected Quality not OK, triggers update labels May 27, 2026
@github-actions
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
Copy link
Copy Markdown
Contributor

claude Bot commented May 27, 2026

AI Review - Attempt 2/3

Image Description

Light render (plot-light.png): The plot displays a warm off-white (#FAF8F1) background with a detailed OpenStreetMap tile layer covering central Rome. Twelve scatter markers are overlaid at correct geographic coordinates, sized and colored by daily visitor count using the imprint_seq colormap (brand green #009E73 → blue #4467A3). The Colosseum and Vatican Museums markers are largest (highest visitor counts), creating a clear visual hierarchy. Each landmark has a semi-transparent annotation label box (fontsize 6pt) with per-attraction directional offsets. A vertical colorbar on the right is labeled "Daily Visitors" with tick labels in INK_SOFT. Axis tick labels use degree notation (e.g., 12.430°E, 41.880°N). A tile attribution box sits in the bottom-right corner. All text (title, axis labels, ticks, colorbar label, annotation labels) is dark-colored and clearly readable against the light background. Title "Rome Tourist Attractions · map-tile-background · python · matplotlib · anyplot.ai" is legible at computed fontsize. Legibility verdict: PASS.

Dark render (plot-dark.png): Same layout on a warm near-black (#1A1A17) background with a CARTO dark tile replacing the OSM light tile — an intentional theme-adaptive provider swap that ensures the tile palette matches the application surface. All chrome elements (title, axis labels, tick labels, colorbar label, annotation box text) switch to light colors (INK/INK_SOFT tokens), with no dark-on-dark failures observed. Data colors are identical to the light render — imprint_seq green-to-blue markers at the same sizes and positions. The annotation label boxes use ELEVATED_BG (#242420) fill, making them readable on the dark tile background. Attribution text correctly displays the CARTO copyright. Legibility verdict: PASS. Data colors consistent with light render — chrome only flips as required.

Score: 86/100

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

Visual Quality (27/30)

  • VQ-01: Text Legibility (7/8) — All sizes explicitly set; title computed via formula; axis labels 10pt; ticks 8pt; annotation labels 6pt (small but appropriate for a dense map). Minor: 6pt annotation text is at the readable floor at full resolution.
  • VQ-02: No Overlap (4/6) — Label crowding in the dense central cluster (Vatican Museums, Castel Sant'Angelo, Piazza Navona, St. Peter's Basilica, Campo de' Fiori). Per-attraction directional offsets help but don't fully resolve box overlap for 12 landmarks packed into a small geographic area.
  • VQ-03: Element Visibility (6/6) — 12 sparse points with markers sized 60–350 sq pts; dual encoding (size + color) ensures all landmarks are clearly visible and distinguishable against both tile backgrounds.
  • VQ-04: Color Accessibility (2/2) — imprint_seq is CVD-safe; PAGE_BG edge color on markers provides good separation; no red-green sole-signal issues.
  • VQ-05: Layout & Canvas (4/4) — Canvas gate passed; 16:9 landscape appropriate for a map; colorbar fits comfortably; proportions well-balanced.
  • VQ-06: Axis Labels & Title (2/2) — "Longitude" / "Latitude" labels; FuncFormatter adds degree notation (°E / °N); title in correct format with descriptive prefix.
  • VQ-07: Palette Compliance (2/2) — imprint_seq used correctly for continuous visitor data; #FAF8F1 (light) / #1A1A17 (dark) backgrounds; both renders are theme-correct.

Design Excellence (12/20)

  • DE-01: Aesthetic Sophistication (5/8) — Theme-adaptive tile provider (OSM for light, CARTO dark for dark) is a thoughtful design decision that goes clearly above defaults. Dual encoding adds visual interest. Semi-transparent annotation boxes add polish. Not quite publication-ready — overall composition is functional rather than curated.
  • DE-02: Visual Refinement (4/6) — Top/right spines removed; no grid (correct decision for a tile-backed map); colorbar styled with INK_SOFT tokens; annotation boxes use rounded corners with themed fill and alpha. Clear improvement over library defaults.
  • DE-03: Data Storytelling (3/6) — Geographic context plus dual encoding (size × color) creates a visual hierarchy where high-traffic landmarks (Colosseum, Vatican) are immediately prominent. However, there is no narrative emphasis — e.g., no highlight annotation on the top landmark, no grouping by district. The data is well-displayed but not actively interpreted for the viewer.

Spec Compliance (15/15)

  • SC-01: Plot Type (5/5) — Correct: scatter data overlay on a tile-based background, exactly as specified.
  • SC-02: Required Features (4/4) — Multiple tile providers (OSM + CARTO dark); auto-fit bounding box with padding; markers with contrast edge (PAGE_BG linewidth=1.5); tile attribution included as required.
  • SC-03: Data Mapping (3/3) — Longitude on X-axis, latitude on Y-axis; axis limits auto-fitted to data bounds; all 12 points visible.
  • SC-04: Title & Legend (3/3) — Title is "Rome Tourist Attractions · map-tile-background · python · matplotlib · anyplot.ai" (correct format with descriptive prefix); colorbar labeled "Daily Visitors".

Data Quality (15/15)

  • DQ-01: Feature Coverage (6/6) — Tile background, overlay scatter, color encoding, size encoding, label annotations, colorbar, and attribution all demonstrated.
  • DQ-02: Realistic Context (5/5) — Rome tourist attractions with plausible daily visitor counts (Colosseum ~21K, Vatican ~20.5K, Pantheon ~15K) — neutral, real-world scenario. Well-known landmarks, no controversial content.
  • DQ-03: Appropriate Scale (4/4) — Visitor counts align with publicly reported figures; geographic extent covers central Rome at an appropriate zoom level.

Code Quality (10/10)

  • CQ-01: KISS Structure (3/3) — Flat: tokens → data → tile fetch → stitch → plot → save. No functions or classes.
  • CQ-02: Reproducibility (2/2) — Hardcoded deterministic data; no randomness.
  • CQ-03: Clean Imports (2/2) — All 8 imports are used: io, math, os, urllib.request, matplotlib.pyplot, numpy, LinearSegmentedColormap, PIL.Image.
  • CQ-04: Code Elegance (2/2) — Clean, idiomatic; tile-fetching loop is concise; no fake UI elements.
  • CQ-05: Output & API (1/1) — Saves as plot-{THEME}.png; no bbox_inches='tight'; no deprecated APIs.

Library Mastery (7/10)

  • LM-01: Idiomatic Usage (4/5) — ax.imshow for tile layer, ax.scatter with c/cmap/s, ax.annotate with bbox, FuncFormatter for degree ticks, plt.colorbar — all correct matplotlib idioms. Minor: plt.savefig / plt.colorbar rather than fig.savefig / fig.colorbar (minor pyplot-level usage vs. OO API).
  • LM-02: Distinctive Features (3/5) — Uses PIL for tile image stitching, custom Mercator math for tile coordinate conversion, ax.imshow as a geographic background canvas — these are distinctive matplotlib capabilities that leverage its image-handling strengths. Not fully library-unique (the tile math is pure Python), but meaningfully above generic usage.

Score Caps Applied

  • None — no cap conditions triggered (VQ-02=4, VQ-03=6, SC-01=5, DQ-02=5, DE-01=5, DE-02=4, CQ-04=2).

Strengths

  • Theme-adaptive tile provider (OSM light / CARTO dark) is an excellent design decision that makes both renders visually coherent on their respective surfaces
  • Perfect spec compliance: tile attribution, auto-fit bounds, contrast marker edges, multiple providers — all requirements met
  • Dual encoding (size × color via imprint_seq) effectively communicates visitor-count hierarchy at a glance
  • Flawless code quality: KISS structure, clean imports, deterministic data, no bbox_inches='tight'
  • imprint_seq correctly used for continuous data; theme tokens applied consistently throughout colorbar, spines, annotation boxes, and attribution

Weaknesses

  • Label crowding in the dense central Rome cluster — Vatican Museums, Castel Sant'Angelo, Piazza Navona, St. Peter's Basilica, and Campo de' Fiori annotation boxes overlap in both renders; consider reducing to the top 8 by visitor count and/or increasing annotation font to 7pt with tighter bbox padding so fewer boxes need to coexist
  • DE-03 limited: the dual encoding creates hierarchy but no focal emphasis — adding a single highlight (e.g., text annotation marking the top visitor site, or a thin connecting arc/arrow for the Add workflow diagram for new prototype discovery #1 landmark) would lift storytelling
  • Annotation labels at 6pt are at the readable floor; 7pt with tighter padding would improve legibility without increasing crowding

Issues Found

  1. VQ-02 PARTIAL: Label annotation boxes overlap in the central cluster (Vatican / Castel Sant'Angelo / Piazza Navona / St. Peter's area)
    • Fix: Reduce displayed labels to top 8 landmarks by visitor count, or increase per-attraction offsets for the clustered central landmarks to better separate boxes
  2. DE-03 LOW: No explicit focal emphasis or narrative

AI Feedback for Next Attempt

Good foundation — tile-adaptive theming and dual encoding are strong. Two targeted improvements: (1) reduce label crowding in the central cluster by showing only top-8 landmarks or increasing their offset magnitudes; (2) add minimal focal emphasis (a single annotation on the top landmark or bold outline on the top-3 markers) to lift DE-03 from display to storytelling. Keep all other design decisions as-is.

Verdict: APPROVED

@github-actions github-actions Bot added quality:86 Quality score 86/100 ai-approved Quality OK, ready for merge and removed quality:87 Quality score 87/100 labels May 27, 2026
@MarkusNeusinger MarkusNeusinger merged commit 7c6355e into main May 27, 2026
3 checks passed
@MarkusNeusinger MarkusNeusinger deleted the implementation/map-tile-background/matplotlib branch May 27, 2026 15:36
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:86 Quality score 86/100

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant