{"spec_id":"pictogram-basic","library":"altair","language":"python","code":"\"\"\" anyplot.ai\npictogram-basic: Pictogram Chart (Isotype Visualization)\nLibrary: altair 6.1.0 | Python 3.13.13\nQuality: 88/100 | Updated: 2026-06-03\n\"\"\"\n\nimport importlib\nimport os\nimport sys\n\n\n# Drop script dir from sys.path so `altair` resolves to the package, not this file\nsys.path[:] = [p for p in sys.path if os.path.abspath(p or \".\") != os.path.dirname(os.path.abspath(__file__))]\nalt = importlib.import_module(\"altair\")\npd = importlib.import_module(\"pandas\")\nImage = importlib.import_module(\"PIL.Image\")\n\n# Theme tokens\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nELEVATED_BG = \"#FFFDF6\" if THEME == \"light\" else \"#242420\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\n\n# Imprint palette — positions 1–5 for 5 categories\nIMPRINT_PALETTE = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\"]\n\n# Data - Fruit production (thousands of tonnes)\ncategories = [\"Apples\", \"Oranges\", \"Bananas\", \"Grapes\", \"Mangoes\"]\nvalues = [35, 22, 18, 12, 8]\ncolors = IMPRINT_PALETTE\nunit_value = 5\nmax_icons = max(v // unit_value + (1 if v % unit_value else 0) for v in values)\ntop_value = max(values)\n\n# Build icon grid: one row per icon position per category\nrows = []\nfor cat, val, color in zip(categories, values, colors, strict=True):\n    full_icons = val // unit_value\n    remainder = (val % unit_value) / unit_value\n    for i in range(full_icons):\n        rows.append({\"category\": cat, \"col\": i, \"opacity\": 1.0, \"color\": color, \"value\": val})\n    if remainder > 0:\n        rows.append({\"category\": cat, \"col\": full_icons, \"opacity\": round(remainder, 2), \"color\": color, \"value\": val})\n\ndf = pd.DataFrame(rows)\n\n# Sort order: highest value first\nsort_order = [c for _, c in sorted(zip(values, categories, strict=True), reverse=True)]\n\ntitle_str = \"pictogram-basic · python · altair · anyplot.ai\"\n\n# Icons layer — circles in a grid\nicons = (\n    alt.Chart(df)\n    .mark_point(size=1200, filled=True, strokeWidth=0)\n    .encode(\n        x=alt.X(\n            \"col:Q\",\n            title=None,\n            scale=alt.Scale(domain=[-0.4, max_icons + 1.2]),\n            axis=alt.Axis(labels=False, ticks=False, domain=False, grid=False),\n        ),\n        y=alt.Y(\n            \"category:N\",\n            title=None,\n            sort=sort_order,\n            scale=alt.Scale(type=\"band\", paddingInner=0.4, paddingOuter=0.15),\n            axis=alt.Axis(\n                labelFontSize=14,\n                labelFontWeight=\"bold\",\n                labelColor=INK,\n                ticks=False,\n                domain=False,\n                grid=False,\n                labelPadding=15,\n            ),\n        ),\n        color=alt.Color(\"color:N\", scale=None),\n        opacity=alt.Opacity(\"opacity:Q\", scale=alt.Scale(domain=[0, 1]), legend=None),\n        tooltip=[alt.Tooltip(\"category:N\", title=\"Category\"), alt.Tooltip(\"value:Q\", title=\"Production (k tonnes)\")],\n    )\n)\n\n# Value label layer\nlabel_data = []\nfor cat, val in zip(categories, values, strict=True):\n    icon_count = val // unit_value + (1 if val % unit_value else 0)\n    label_data.append({\"category\": cat, \"col\": icon_count + 0.35, \"label\": f\"{val}k\", \"is_top\": val == top_value})\nlabel_df = pd.DataFrame(label_data)\n\ntop_labels = (\n    alt.Chart(label_df[label_df[\"is_top\"]])\n    .mark_text(align=\"left\", baseline=\"middle\", fontSize=17, fontWeight=\"bold\", color=\"#009E73\")\n    .encode(x=alt.X(\"col:Q\"), y=alt.Y(\"category:N\", sort=sort_order), text=alt.Text(\"label:N\"))\n)\n\nother_labels = (\n    alt.Chart(label_df[~label_df[\"is_top\"]])\n    .mark_text(align=\"left\", baseline=\"middle\", fontSize=12, color=INK_SOFT)\n    .encode(x=alt.X(\"col:Q\"), y=alt.Y(\"category:N\", sort=sort_order), text=alt.Text(\"label:N\"))\n)\n\n# Subtle highlight bar behind the top category for visual storytelling\nhighlight_df = pd.DataFrame([{\"category\": sort_order[0]}])\nhighlight = (\n    alt.Chart(highlight_df)\n    .mark_bar(color=\"#009E73\", opacity=0.11, cornerRadius=4)\n    .encode(y=alt.Y(\"category:N\", sort=sort_order), x=alt.value(0), x2=alt.value(620))\n)\n\n# Combine layers\ncombined = (\n    (highlight + icons + top_labels + other_labels)\n    .properties(\n        width=620,\n        height=320,\n        background=PAGE_BG,\n        title=alt.Title(\n            text=title_str,\n            subtitle=[\n                \"Global Fruit Production Comparison\",\n                f\"● = {unit_value}k tonnes  |  partial ● = fractional amount\",\n            ],\n            fontSize=16,\n            subtitleFontSize=11,\n            subtitleColor=INK_MUTED,\n            color=INK,\n            anchor=\"start\",\n            offset=15,\n        ),\n    )\n    .configure_view(fill=PAGE_BG, strokeWidth=0)\n    .configure_axis(\n        domainColor=INK_SOFT, tickColor=INK_SOFT, gridColor=INK, gridOpacity=0.12, labelColor=INK_SOFT, titleColor=INK\n    )\n    .configure_legend(fillColor=ELEVATED_BG, strokeColor=INK_SOFT, labelColor=INK_SOFT, titleColor=INK)\n)\n\n# Save PNG and HTML\ncombined.save(f\"plot-{THEME}.png\", scale_factor=4.0)\ncombined.save(f\"plot-{THEME}.html\")\n\n# Pad PNG to exact 3200×1800 target (landscape); do NOT crop\nTW, TH = 3200, 1800\n_img = Image.open(f\"plot-{THEME}.png\").convert(\"RGB\")\n_w, _h = _img.size\nif _w > TW or _h > TH:\n    raise SystemExit(\n        f\"altair vl-convert produced {_w}×{_h}, exceeds target {TW}×{TH}. \"\n        \"Shrink chart .properties(width=, height=) and re-render.\"\n    )\nif _w < TW or _h < TH:\n    _canvas = Image.new(\"RGB\", (TW, TH), PAGE_BG)\n    _canvas.paste(_img, ((TW - _w) // 2, (TH - _h) // 2))\n    _canvas.save(f\"plot-{THEME}.png\")\n"}