diff --git a/plots/hexbin-map-geographic/implementations/python/letsplot.py b/plots/hexbin-map-geographic/implementations/python/letsplot.py index ed097c41d6..c39690db78 100644 --- a/plots/hexbin-map-geographic/implementations/python/letsplot.py +++ b/plots/hexbin-map-geographic/implementations/python/letsplot.py @@ -1,9 +1,11 @@ -""" pyplots.ai +"""anyplot.ai hexbin-map-geographic: Hexagonal Binning Map -Library: letsplot 4.8.2 | Python 3.13.11 -Quality: 91/100 | Created: 2026-01-20 +Library: letsplot | Python 3.13 +Quality: pending | Updated: 2026-05-27 """ +import os + import numpy as np import pandas as pd from lets_plot import ( @@ -20,7 +22,7 @@ ggsize, labs, layer_tooltips, - scale_fill_viridis, + scale_fill_gradient, theme, theme_minimal, ) @@ -29,35 +31,38 @@ LetsPlot.setup_html() +# Theme tokens +THEME = os.getenv("ANYPLOT_THEME", "light") +PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17" +ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420" +INK = "#1A1A17" if THEME == "light" else "#F0EFE8" +INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" +INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F" +BASEMAP_FILL = "#E4E4DC" if THEME == "light" else "#2E2E2A" +BASEMAP_COLOR = "#AAAAAA" if THEME == "light" else "#5A5A54" + # Data: Simulated taxi pickup locations in New York City area -# Large dataset to demonstrate hexagonal binning aggregation np.random.seed(42) n_points = 15000 -# Manhattan cluster (heavy taxi activity) manhattan_lat = np.random.normal(40.758, 0.025, n_points // 2) manhattan_lon = np.random.normal(-73.985, 0.015, n_points // 2) -# Midtown East cluster midtown_lat = np.random.normal(40.752, 0.018, n_points // 4) midtown_lon = np.random.normal(-73.972, 0.012, n_points // 4) -# JFK Airport area cluster jfk_lat = np.random.normal(40.641, 0.012, n_points // 6) jfk_lon = np.random.normal(-73.778, 0.015, n_points // 6) -# LaGuardia Airport area cluster lga_lat = np.random.normal(40.773, 0.008, n_points // 12) lga_lon = np.random.normal(-73.872, 0.010, n_points // 12) -# Combine all locations latitude = np.concatenate([manhattan_lat, midtown_lat, jfk_lat, lga_lat]) longitude = np.concatenate([manhattan_lon, midtown_lon, jfk_lon, lga_lon]) df = pd.DataFrame({"lat": latitude, "lon": longitude}) # Simplified basemap: NYC borough outlines for geographic context -# Manhattan outline (simplified polygon) manhattan_outline = pd.DataFrame( { "lon": [-74.02, -73.97, -73.93, -73.91, -73.93, -73.97, -74.01, -74.02], @@ -67,7 +72,6 @@ } ) -# Brooklyn outline (simplified) brooklyn_outline = pd.DataFrame( { "lon": [-74.04, -73.95, -73.85, -73.83, -73.86, -73.95, -74.03, -74.04], @@ -77,7 +81,6 @@ } ) -# Queens outline (simplified) queens_outline = pd.DataFrame( { "lon": [-73.96, -73.82, -73.70, -73.72, -73.76, -73.85, -73.93, -73.96], @@ -87,41 +90,46 @@ } ) -# Combine borough outlines df_boroughs = pd.concat([manhattan_outline, brooklyn_outline, queens_outline], ignore_index=True) -# Create hexbin map with geographic context +# Title — scale fontsize for length +title = "NYC Taxi Pickups · hexbin-map-geographic · python · letsplot · anyplot.ai" +title_size = max(11, round(16 * 67 / len(title))) + +anyplot_theme = theme( + plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG), + panel_background=element_rect(fill=PAGE_BG), + panel_grid_major=element_line(color=INK_MUTED, size=0.2), + panel_grid_minor=element_blank(), + axis_title=element_text(color=INK, size=12), + axis_text=element_text(color=INK_SOFT, size=10), + plot_title=element_text(color=INK, size=title_size), + legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT), + legend_text=element_text(color=INK_SOFT, size=10), + legend_title=element_text(color=INK, size=11), + legend_position="right", +) + plot = ( ggplot() - # Basemap: Borough outlines for geographic context + geom_polygon( - aes(x="lon", y="lat", group="borough"), data=df_boroughs, fill="#E8E8E8", color="#999999", size=0.6, alpha=0.7 + aes(x="lon", y="lat", group="borough"), + data=df_boroughs, + fill=BASEMAP_FILL, + color=BASEMAP_COLOR, + size=0.6, + alpha=0.7, ) - # Hexagonal binning layer - the main visualization + geom_hex( aes(x="lon", y="lat"), data=df, bins=[40, 40], alpha=0.85, tooltips=layer_tooltips().line("Pickups|@..count..") ) - # Sequential colormap for count aggregation - + scale_fill_viridis(name="Pickup\nCount", option="plasma") - + labs(x="Longitude", y="Latitude", title="NYC Taxi Pickups · hexbin-map-geographic · letsplot · pyplots.ai") + + scale_fill_gradient(low="#009E73", high="#4467A3", name="Pickup\nCount") + + labs(x="Longitude (°)", y="Latitude (°)", title=title) + coord_fixed(ratio=1.0, xlim=[-74.05, -73.68], ylim=[40.55, 40.90]) - + ggsize(1600, 900) + + ggsize(800, 450) + theme_minimal() - + theme( - plot_title=element_text(size=24, face="bold"), - axis_title=element_text(size=20), - axis_text=element_text(size=16), - legend_title=element_text(size=16), - legend_text=element_text(size=14), - legend_position="right", - panel_grid_major=element_line(color="#D0D0D0", size=0.3), - panel_grid_minor=element_blank(), - panel_background=element_rect(fill="#F5F5F5"), - ) + + anyplot_theme ) -# Save PNG (scale 3x for 4800 x 2700 px) -ggsave(plot, "plot.png", path=".", scale=3) - -# Save HTML for interactive version with tooltips -ggsave(plot, "plot.html", path=".") +ggsave(plot, f"plot-{THEME}.png", path=".", scale=4) +ggsave(plot, f"plot-{THEME}.html", path=".") diff --git a/plots/hexbin-map-geographic/metadata/python/letsplot.yaml b/plots/hexbin-map-geographic/metadata/python/letsplot.yaml index 7637344611..530a4e35a6 100644 --- a/plots/hexbin-map-geographic/metadata/python/letsplot.yaml +++ b/plots/hexbin-map-geographic/metadata/python/letsplot.yaml @@ -1,210 +1,21 @@ +# Per-library metadata for letsplot implementation of hexbin-map-geographic +# Auto-generated by impl-generate.yml + library: letsplot +language: python specification_id: hexbin-map-geographic created: '2026-01-20T21:14:00Z' -updated: '2026-01-20T21:16:48Z' -generated_by: claude-opus-4-5-20251101 -workflow_run: 21187394996 +updated: '2026-05-27T13:31:30Z' +generated_by: claude-sonnet +workflow_run: 26513971833 issue: 3767 -python_version: 3.13.11 -library_version: 4.8.2 -preview_url: https://storage.googleapis.com/anyplot-images/plots/hexbin-map-geographic/letsplot/plot.png -preview_html: https://storage.googleapis.com/anyplot-images/plots/hexbin-map-geographic/letsplot/plot.html -quality_score: 91 +language_version: 3.13.13 +library_version: 4.10.1 +preview_url_light: https://storage.googleapis.com/anyplot-images/plots/hexbin-map-geographic/python/letsplot/plot-light.png +preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/hexbin-map-geographic/python/letsplot/plot-dark.png +preview_html_light: https://storage.googleapis.com/anyplot-images/plots/hexbin-map-geographic/python/letsplot/plot-light.html +preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/hexbin-map-geographic/python/letsplot/plot-dark.html +quality_score: null review: - strengths: - - Excellent real-world NYC taxi data scenario demonstrating practical hexbin map - use case - - Proper use of lets-plot grammar of graphics with layered approach (basemap polygon - + hexbin) - - Interactive tooltips enabled via layer_tooltips showing pickup count on hover - - Plasma colormap provides excellent visual density gradient with colorblind accessibility - - Geographic context through simplified borough outlines enhances interpretability - weaknesses: - - Axis labels missing degree symbols (e.g., Longitude (°) instead of just Longitude) - image_description: 'The plot displays a hexagonal binning map of NYC taxi pickup - locations. Three distinct clusters are visible: a large central cluster in the - Manhattan area (around -74.0 longitude, 40.75 latitude) with a bright yellow core - indicating the highest density (300+ pickups), a medium cluster around LaGuardia - Airport area (around -73.87, 40.77), and another cluster in the JFK Airport area - (around -73.78, 40.65). The hexagons use the plasma colormap, transitioning from - dark purple (low counts) through pink to bright yellow (high counts). Simplified - borough outlines (Manhattan, Brooklyn, Queens) are shown as light gray polygons - providing geographic context. The title includes spec-id, library name, and pyplots.ai - branding. Axes show Longitude and Latitude labels. A color legend labeled "Pickup - Count" appears on the right with scale from ~0 to 300+.' - criteria_checklist: - visual_quality: - score: 36 - max: 40 - items: - - id: VQ-01 - name: Text Legibility - score: 10 - max: 10 - passed: true - comment: Title at 24pt, axis labels at 20pt, tick labels at 16pt - all perfectly - readable - - id: VQ-02 - name: No Overlap - score: 8 - max: 8 - passed: true - comment: No overlapping text elements, hexagons are distinct - - id: VQ-03 - name: Element Visibility - score: 7 - max: 8 - passed: true - comment: Hexagons well-sized for density with good alpha, though some edge - hexagons appear sparse - - id: VQ-04 - name: Color Accessibility - score: 5 - max: 5 - passed: true - comment: Plasma colormap is colorblind-safe with excellent contrast - - id: VQ-05 - name: Layout Balance - score: 4 - max: 5 - passed: true - comment: Good use of canvas with plot filling ~60%, minor empty space in lower - left corner - - id: VQ-06 - name: Axis Labels - score: 1 - max: 2 - passed: true - comment: Labels say Longitude and Latitude but lack units (°) - - id: VQ-07 - name: Grid & Legend - score: 1 - max: 2 - passed: true - comment: Grid is subtle, legend well-placed; grid could be slightly more subdued - spec_compliance: - score: 24 - max: 25 - items: - - id: SC-01 - name: Plot Type - score: 8 - max: 8 - passed: true - comment: Correct hexagonal binning map for geographic data - - id: SC-02 - name: Data Mapping - score: 5 - max: 5 - passed: true - comment: Longitude on X, Latitude on Y, count aggregation correct - - id: SC-03 - name: Required Features - score: 4 - max: 5 - passed: true - comment: Has hexbin, basemap overlay, colormap, transparency, tooltips; hexagon - size not explicitly configurable in demo - - id: SC-04 - name: Data Range - score: 3 - max: 3 - passed: true - comment: Axes show full NYC area with all data points visible - - id: SC-05 - name: Legend Accuracy - score: 2 - max: 2 - passed: true - comment: Legend correctly labeled Pickup Count - - id: SC-06 - name: Title Format - score: 2 - max: 2 - passed: true - comment: Title follows {description} · {spec-id} · {library} · pyplots.ai - format - data_quality: - score: 18 - max: 20 - items: - - id: DQ-01 - name: Feature Coverage - score: 7 - max: 8 - passed: true - comment: Shows multiple density clusters demonstrating aggregation, but missing - demonstration of other aggregation methods (sum, mean) - - id: DQ-02 - name: Realistic Context - score: 7 - max: 7 - passed: true - comment: NYC taxi pickup data is a perfect, neutral, real-world scenario - - id: DQ-03 - name: Appropriate Scale - score: 4 - max: 5 - passed: true - comment: 15,000 points appropriate for hexbin; coordinates realistic for NYC - code_quality: - score: 9 - max: 10 - items: - - id: CQ-01 - name: KISS Structure - score: 3 - max: 3 - passed: true - comment: 'Linear flow: imports → data → plot → save' - - id: CQ-02 - name: Reproducibility - score: 3 - max: 3 - passed: true - comment: np.random.seed(42) ensures reproducibility - - id: CQ-03 - name: Clean Imports - score: 2 - max: 2 - passed: true - comment: All imports are used - - id: CQ-04 - name: No Deprecated API - score: 1 - max: 1 - passed: true - comment: Uses current lets-plot API - - id: CQ-05 - name: Output Correct - score: 0 - max: 1 - passed: false - comment: Saves to plot.png but uses path=. parameter unnecessarily - library_features: - score: 4 - max: 5 - items: - - id: LF-01 - name: Distinctive Features - score: 4 - max: 5 - passed: true - comment: Uses geom_hex, layer_tooltips for hover interactivity, scale_fill_viridis, - coord_fixed, geom_polygon for basemap overlay - good use of lets-plot grammar - of graphics approach - verdict: APPROVED -impl_tags: - dependencies: [] - techniques: - - layer-composition - - hover-tooltips - - html-export - patterns: - - data-generation - dataprep: - - binning - styling: - - custom-colormap - - alpha-blending - - grid-styling + strengths: [] + weaknesses: []