{"spec_id":"scatter-pitch-events","library":"matplotlib","language":"python","code":"\"\"\" anyplot.ai\nscatter-pitch-events: Soccer Pitch Event Map\nLibrary: matplotlib 3.11.0 | Python 3.13.14\nQuality: 89/100 | Updated: 2026-06-21\n\"\"\"\n\nimport os\nimport sys\n\n\n# Prevent this script's filename from shadowing the installed matplotlib package\nsys.path = [p for p in sys.path if os.path.abspath(p) != os.path.dirname(os.path.abspath(__file__))]\n\nimport matplotlib.patches as patches\nimport matplotlib.patheffects as pe\nimport matplotlib.pyplot as plt\nimport numpy as np\nfrom matplotlib.colors import to_rgba\n\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\"\n\n# Imprint palette — positions 1→4 for the 4 event types\nIMPRINT_PALETTE = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\", \"#99B314\"]\nANYPLOT_AMBER = \"#DDCC77\"  # warning / caution semantic anchor\n\nc_pass = IMPRINT_PALETTE[0]  # brand green — passes\nc_shot = IMPRINT_PALETTE[1]  # lavender — shots\nc_tackle = IMPRINT_PALETTE[2]  # blue — tackles\nc_intercept = IMPRINT_PALETTE[3]  # ochre — interceptions\n\n# Pitch-specific surface colors (not chrome — the pitch is the data canvas)\nPITCH_BG = \"#3a7d44\" if THEME == \"light\" else \"#1e4d2b\"\nPITCH_LINE = \"#FAF8F1\" if THEME == \"light\" else \"#e0e0e0\"\n\n# Data\nnp.random.seed(42)\n\nn_passes = 70\nn_shots = 25\nn_tackles = 40\nn_interceptions = 35\n\npass_x = np.random.uniform(10, 95, n_passes)\npass_y = np.random.uniform(5, 63, n_passes)\npass_dx = np.random.uniform(-15, 25, n_passes)\npass_dy = np.random.uniform(-15, 15, n_passes)\npass_end_x = np.clip(pass_x + pass_dx, 0, 105)\npass_end_y = np.clip(pass_y + pass_dy, 0, 68)\npass_success = np.random.choice([True, False], n_passes, p=[0.78, 0.22])\n\nshot_x = np.random.uniform(70, 104, n_shots)\nshot_y = np.random.uniform(15, 53, n_shots)\nshot_dx = np.clip(105 - shot_x, 1, 35) * np.random.uniform(0.5, 1.0, n_shots)\nshot_dy = (34 - shot_y) * np.random.uniform(-0.3, 0.3, n_shots)\nshot_success = np.random.choice([True, False], n_shots, p=[0.35, 0.65])\n\ntackle_x = np.random.uniform(5, 80, n_tackles)\ntackle_y = np.random.uniform(5, 63, n_tackles)\ntackle_success = np.random.choice([True, False], n_tackles, p=[0.65, 0.35])\n\nintercept_x = np.random.uniform(15, 85, n_interceptions)\nintercept_y = np.random.uniform(5, 63, n_interceptions)\nintercept_success = np.random.choice([True, False], n_interceptions, p=[0.80, 0.20])\n\n# Plot — figsize=(8, 4.5) dpi=400 → exactly 3200 × 1800 px\nfig, ax = plt.subplots(figsize=(8, 4.5), dpi=400, facecolor=PAGE_BG)\nax.set_facecolor(PITCH_BG)\n\n# Pitch outline and markings (FIFA standard: 105m × 68m)\nlw = 1.5\n\nax.add_patch(patches.Rectangle((0, 0), 105, 68, linewidth=lw, edgecolor=PITCH_LINE, facecolor=\"none\"))\nax.plot([52.5, 52.5], [0, 68], color=PITCH_LINE, linewidth=lw)\nax.add_patch(patches.Circle((52.5, 34), 9.15, linewidth=lw, edgecolor=PITCH_LINE, facecolor=\"none\"))\nax.plot(52.5, 34, \"o\", color=PITCH_LINE, markersize=3)\n\n# Left penalty area\nax.add_patch(patches.Rectangle((0, 13.84), 16.5, 40.32, linewidth=lw, edgecolor=PITCH_LINE, facecolor=\"none\"))\nax.add_patch(patches.Rectangle((0, 24.84), 5.5, 18.32, linewidth=lw, edgecolor=PITCH_LINE, facecolor=\"none\"))\nax.plot(11, 34, \"o\", color=PITCH_LINE, markersize=3)\nax.add_patch(patches.Arc((11, 34), 18.3, 18.3, angle=0, theta1=-53, theta2=53, color=PITCH_LINE, linewidth=lw))\n\n# Right penalty area\nax.add_patch(patches.Rectangle((88.5, 13.84), 16.5, 40.32, linewidth=lw, edgecolor=PITCH_LINE, facecolor=\"none\"))\nax.add_patch(patches.Rectangle((99.5, 24.84), 5.5, 18.32, linewidth=lw, edgecolor=PITCH_LINE, facecolor=\"none\"))\nax.plot(94, 34, \"o\", color=PITCH_LINE, markersize=3)\nax.add_patch(patches.Arc((94, 34), 18.3, 18.3, angle=0, theta1=127, theta2=233, color=PITCH_LINE, linewidth=lw))\n\n# Corner arcs\nfor cx, cy in [(0, 0), (0, 68), (105, 0), (105, 68)]:\n    t1 = 0 if cx == 0 and cy == 0 else (270 if cx == 105 and cy == 0 else (90 if cx == 0 and cy == 68 else 180))\n    ax.add_patch(patches.Arc((cx, cy), 2, 2, angle=0, theta1=t1, theta2=t1 + 90, color=PITCH_LINE, linewidth=lw))\n\n# Goals\nax.plot([0, 0], [30.34, 37.66], color=PITCH_LINE, linewidth=3, solid_capstyle=\"round\")\nax.plot([105, 105], [30.34, 37.66], color=PITCH_LINE, linewidth=3, solid_capstyle=\"round\")\n\n# Attacking zone highlight (right third) — tactical storytelling focal point\nzone_highlight = patches.FancyBboxPatch(\n    (70, 5), 33, 58, boxstyle=\"round,pad=2\", facecolor=ANYPLOT_AMBER, alpha=0.07, edgecolor=\"none\", zorder=1\n)\nax.add_patch(zone_highlight)\nax.text(\n    86.5,\n    66,\n    \"Attacking Third\",\n    fontsize=7,\n    color=ANYPLOT_AMBER,\n    alpha=0.9,\n    ha=\"center\",\n    va=\"top\",\n    fontweight=\"bold\",\n    path_effects=[pe.withStroke(linewidth=1.5, foreground=PITCH_BG)],\n)\n\n# Events - passes (arrows with origin markers)\nfor i in range(n_passes):\n    alpha = 0.75 if pass_success[i] else 0.35\n    ax.annotate(\n        \"\",\n        xy=(pass_end_x[i], pass_end_y[i]),\n        xytext=(pass_x[i], pass_y[i]),\n        arrowprops={\"arrowstyle\": \"->\", \"color\": c_pass, \"lw\": 0.8, \"alpha\": alpha},\n    )\n    ax.plot(\n        pass_x[i], pass_y[i], \"o\", color=c_pass, markersize=4, alpha=alpha, markeredgecolor=\"white\", markeredgewidth=0.3\n    )\n\n# Events - shots (bold arrows with star markers)\nfor i in range(n_shots):\n    alpha = 0.9 if shot_success[i] else 0.3\n    ax.annotate(\n        \"\",\n        xy=(shot_x[i] + shot_dx[i], shot_y[i] + shot_dy[i]),\n        xytext=(shot_x[i], shot_y[i]),\n        arrowprops={\"arrowstyle\": \"-|>\", \"color\": c_shot, \"lw\": 1.0, \"alpha\": alpha, \"mutation_scale\": 12},\n    )\n    ax.plot(\n        shot_x[i],\n        shot_y[i],\n        \"*\",\n        color=c_shot,\n        markersize=12,\n        alpha=alpha,\n        markeredgecolor=\"white\",\n        markeredgewidth=0.4,\n        path_effects=[pe.withStroke(linewidth=0.8, foreground=PITCH_BG)],\n    )\n\n# Events - tackles (triangles) — vectorized with per-point alpha\ntackle_rgba = np.array([to_rgba(c_tackle, a) for a in np.where(tackle_success, 0.8, 0.25)])\nax.scatter(tackle_x, tackle_y, marker=\"^\", s=80, c=tackle_rgba, edgecolors=\"white\", linewidth=0.4, zorder=5)\n\n# Events - interceptions (diamonds) — vectorized with per-point alpha\nintercept_rgba = np.array([to_rgba(c_intercept, a) for a in np.where(intercept_success, 0.85, 0.35)])\nax.scatter(intercept_x, intercept_y, marker=\"D\", s=60, c=intercept_rgba, edgecolors=\"white\", linewidth=0.4, zorder=5)\n\n# Axes\nax.set_xlim(-3, 108)\nax.set_ylim(-5, 73)\nax.set_aspect(\"equal\")\nax.axis(\"off\")\n\n# Title — 55 chars, within 67-char baseline so fontsize stays at 12pt\ntitle = \"scatter-pitch-events · python · matplotlib · anyplot.ai\"\ntitle_fontsize = max(8, round(12 * (67 / len(title))))\nax.set_title(\n    title,\n    fontsize=title_fontsize,\n    fontweight=\"medium\",\n    color=INK,\n    pad=12,\n    path_effects=[pe.withStroke(linewidth=2, foreground=PAGE_BG)],\n)\n\n# Legend\nlegend_elements = [\n    plt.Line2D([0], [0], marker=\"o\", color=\"w\", markerfacecolor=c_pass, markersize=7, label=\"Pass\", linestyle=\"None\"),\n    plt.Line2D([0], [0], marker=\"*\", color=\"w\", markerfacecolor=c_shot, markersize=10, label=\"Shot\", linestyle=\"None\"),\n    plt.Line2D(\n        [0], [0], marker=\"^\", color=\"w\", markerfacecolor=c_tackle, markersize=7, label=\"Tackle\", linestyle=\"None\"\n    ),\n    plt.Line2D(\n        [0],\n        [0],\n        marker=\"D\",\n        color=\"w\",\n        markerfacecolor=c_intercept,\n        markersize=7,\n        label=\"Interception\",\n        linestyle=\"None\",\n    ),\n    plt.Line2D(\n        [0],\n        [0],\n        marker=\"s\",\n        color=\"w\",\n        markerfacecolor=\"#aaaaaa\",\n        markersize=7,\n        label=\"Successful (bright)\",\n        linestyle=\"None\",\n    ),\n    plt.Line2D(\n        [0],\n        [0],\n        marker=\"s\",\n        color=\"w\",\n        markerfacecolor=\"#555555\",\n        markersize=7,\n        label=\"Unsuccessful (faded)\",\n        linestyle=\"None\",\n    ),\n]\nleg = ax.legend(\n    handles=legend_elements,\n    loc=\"lower center\",\n    ncol=6,\n    fontsize=8,\n    framealpha=0.85,\n    facecolor=ELEVATED_BG,\n    edgecolor=INK_SOFT,\n    labelcolor=INK_SOFT,\n    bbox_to_anchor=(0.5, -0.06),\n)\n\n# Save — no bbox_inches to preserve exact 3200×1800 canvas\nfig.subplots_adjust(top=0.92, bottom=0.10, left=0.02, right=0.98)\nplt.savefig(f\"plot-{THEME}.png\", dpi=400, facecolor=PAGE_BG)\nplt.close()\n"}