{"spec_id":"polar-line","library":"altair","language":"python","code":"\"\"\" anyplot.ai\npolar-line: Polar Line Plot\nLibrary: altair 6.1.0 | Python 3.13.13\nQuality: 92/100 | Updated: 2026-05-12\n\"\"\"\n\nimport os\nimport sys\n\n\n# Remove current directory from path to avoid importing this file as altair\ncwd = os.getcwd()\nif cwd in sys.path:\n    sys.path.remove(cwd)\nscript_dir = os.path.dirname(os.path.abspath(__file__))\nif script_dir in sys.path:\n    sys.path.remove(script_dir)\n\nimport altair as alt\nimport numpy as np\nimport pandas as pd\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# Okabe-Ito palette (positions 1-2 for two series)\nIMPRINT = [\"#009E73\", \"#C475FD\"]\n\n# Data - Seasonal temperature pattern (cyclical)\nnp.random.seed(42)\nmonths = np.arange(0, 360, 30)  # 12 months as degrees\nmonth_names = [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"]\n\n# Two cities with different seasonal patterns\ncity_a_temp = np.array([5, 7, 12, 18, 24, 28, 30, 29, 24, 17, 10, 6])\ncity_b_temp = np.array([28, 27, 24, 18, 12, 8, 7, 9, 14, 20, 24, 27])\n\n# Normalize temperatures to 0-1 range for polar visualization\ncity_a_norm = (city_a_temp - city_a_temp.min()) / (city_a_temp.max() - city_a_temp.min()) * 0.4 + 0.3\ncity_b_norm = (city_b_temp - city_b_temp.min()) / (city_b_temp.max() - city_b_temp.min()) * 0.4 + 0.3\n\n# Convert polar to cartesian for Altair\ntheta_rad_months = np.radians(months)\ntheta_rad_closed = np.append(theta_rad_months, theta_rad_months[0])\n\ncity_a_closed = np.append(city_a_norm, city_a_norm[0])\ncity_b_closed = np.append(city_b_norm, city_b_norm[0])\n\nx_a = city_a_closed * np.cos(theta_rad_closed)\ny_a = city_a_closed * np.sin(theta_rad_closed)\nx_b = city_b_closed * np.cos(theta_rad_closed)\ny_b = city_b_closed * np.sin(theta_rad_closed)\n\ndf = pd.DataFrame(\n    {\n        \"x\": np.concatenate([x_a, x_b]),\n        \"y\": np.concatenate([y_a, y_b]),\n        \"order\": list(range(len(x_a))) + list(range(len(x_b))),\n        \"city\": [\"Northern City\"] * len(x_a) + [\"Southern City\"] * len(x_b),\n    }\n)\n\n# Create polar grid - concentric circles\ngrid_circles = []\nfor r in [0.2, 0.4, 0.6, 0.8]:\n    theta_circle = np.linspace(0, 2 * np.pi, 100)\n    x_c = r * np.cos(theta_circle)\n    y_c = r * np.sin(theta_circle)\n    for i in range(len(x_c)):\n        grid_circles.append({\"x\": x_c[i], \"y\": y_c[i], \"r\": str(r), \"order\": i})\ngrid_df = pd.DataFrame(grid_circles)\n\n# Create radial lines\nradials = []\nfor angle in months:\n    angle_rad = np.radians(angle)\n    radials.append({\"x\": 0, \"y\": 0, \"x2\": 0.85 * np.cos(angle_rad), \"y2\": 0.85 * np.sin(angle_rad)})\nradials_df = pd.DataFrame(radials)\n\n# Month labels\nlabels = []\nfor angle, name in zip(months, month_names, strict=True):\n    angle_rad = np.radians(angle)\n    labels.append({\"x\": 0.95 * np.cos(angle_rad), \"y\": 0.95 * np.sin(angle_rad), \"label\": name})\nlabels_df = pd.DataFrame(labels)\n\n# Concentric grid circles\ncircles_chart = (\n    alt.Chart(grid_df)\n    .mark_line(strokeWidth=1.5, opacity=0.5)\n    .encode(\n        x=alt.X(\"x:Q\", axis=None, scale=alt.Scale(domain=[-1.1, 1.1])),\n        y=alt.Y(\"y:Q\", axis=None, scale=alt.Scale(domain=[-1.1, 1.1])),\n        detail=\"r:N\",\n        order=\"order:O\",\n        color=alt.value(INK_SOFT),\n    )\n)\n\n# Radial grid lines\nradials_chart = (\n    alt.Chart(radials_df)\n    .mark_rule(strokeWidth=1.5, opacity=0.5)\n    .encode(x=\"x:Q\", y=\"y:Q\", x2=\"x2:Q\", y2=\"y2:Q\", color=alt.value(INK_SOFT))\n)\n\n# Month labels\nlabels_chart = (\n    alt.Chart(labels_df)\n    .mark_text(fontSize=22, fontWeight=\"bold\")\n    .encode(x=\"x:Q\", y=\"y:Q\", text=\"label:N\", color=alt.value(INK))\n)\n\n# Data lines\nlines_chart = (\n    alt.Chart(df)\n    .mark_line(strokeWidth=4, opacity=0.9)\n    .encode(\n        x=\"x:Q\",\n        y=\"y:Q\",\n        color=alt.Color(\n            \"city:N\",\n            scale=alt.Scale(domain=[\"Northern City\", \"Southern City\"], range=IMPRINT),\n            legend=alt.Legend(\n                title=\"City\",\n                titleFontSize=20,\n                labelFontSize=18,\n                orient=\"bottom\",\n                direction=\"horizontal\",\n                fillColor=ELEVATED_BG,\n                strokeColor=INK_SOFT,\n                titleColor=INK,\n                labelColor=INK_SOFT,\n            ),\n        ),\n        order=\"order:O\",\n    )\n)\n\n# Data points\npoints_chart = (\n    alt.Chart(df)\n    .mark_point(size=250, filled=True, opacity=0.9)\n    .encode(\n        x=\"x:Q\",\n        y=\"y:Q\",\n        color=alt.Color(\n            \"city:N\", scale=alt.Scale(domain=[\"Northern City\", \"Southern City\"], range=IMPRINT), legend=None\n        ),\n    )\n)\n\n# Combine all layers\nchart = (\n    alt.layer(circles_chart, radials_chart, labels_chart, lines_chart, points_chart)\n    .properties(\n        width=1200,\n        height=1200,\n        title=alt.Title(\n            text=\"polar-line · altair · anyplot.ai\",\n            subtitle=\"Monthly Temperature Patterns\",\n            fontSize=28,\n            subtitleFontSize=22,\n            anchor=\"middle\",\n            color=INK,\n            subtitleColor=INK_SOFT,\n        ),\n        background=PAGE_BG,\n    )\n    .configure_view(strokeWidth=0, fill=PAGE_BG)\n)\n\n# Save (1200 * 3 = 3600 for square format)\nchart.save(f\"plot-{THEME}.png\", scale_factor=3.0)\nchart.save(f\"plot-{THEME}.html\")\n"}