{"spec_id":"heatmap-annotated","library":"makie","language":"julia","code":"# anyplot.ai\n# heatmap-annotated: Annotated Heatmap\n# Library: makie 0.21.9 | Julia 1.11.9\n# Quality: 83/100 | Created: 2026-08-05\n\nusing CairoMakie\nusing Colors\nusing Random\nusing Statistics\nusing Printf\n\nRandom.seed!(42)\n\n# --- Theme tokens -----------------------------------------------------------\nconst THEME    = get(ENV, \"ANYPLOT_THEME\", \"light\")\nconst PAGE_BG  = THEME == \"light\" ? colorant\"#FAF8F1\" : colorant\"#1A1A17\"\nconst INK      = THEME == \"light\" ? colorant\"#1A1A17\" : colorant\"#F0EFE8\"\nconst INK_SOFT = THEME == \"light\" ? colorant\"#4A4A44\" : colorant\"#B8B7B0\"\nconst MIDPOINT = THEME == \"light\" ? colorant\"#FAF8F1\" : colorant\"#1A1A17\"\n\n# Imprint diverging colormap — correlations have a meaningful zero midpoint\nconst ANYPLOT_DIV = cgrad([colorant\"#AE3030\", MIDPOINT, colorant\"#4467A3\"])\n\n# Cell-annotation contrast colors — chosen per cell from the fill's own\n# luminance, so text stays legible whether the cell lands on the saturated\n# red/blue ends or the near-background midpoint of the diverging scale.\nconst DARK_TEXT  = colorant\"#1A1A17\"\nconst LIGHT_TEXT = colorant\"#F0EFE8\"\n\n# --- Data ---------------------------------------------------------------\nvariables = [\n    \"Temperature\", \"Humidity\", \"Wind Speed\", \"Pressure\",\n    \"Rainfall\", \"Solar Radiation\", \"Cloud Cover\", \"Visibility\",\n]\nn_vars = length(variables)\nn_samples = 200\n\ncloud_cover = randn(n_samples) .* 15 .+ 50\ntemperature = randn(n_samples) .* 5 .+ 20\nhumidity = -0.6 .* temperature .+ randn(n_samples) .* 8 .+ 70\nwind_speed = randn(n_samples) .* 3 .+ 15\npressure = -0.4 .* temperature .+ randn(n_samples) .* 5 .+ 1013\nrainfall = 0.5 .* humidity .+ randn(n_samples) .* 10\nsolar_radiation = 0.7 .* temperature .- 0.4 .* cloud_cover .+ randn(n_samples) .* 10 .+ 200\nvisibility = -0.5 .* humidity .- 0.3 .* cloud_cover .+ randn(n_samples) .* 5 .+ 20\n\nmeasurements = hcat(\n    temperature, humidity, wind_speed, pressure,\n    rainfall, solar_radiation, cloud_cover, visibility,\n)\ncorr_matrix = cor(measurements)\n\n# --- Plot -----------------------------------------------------------------\nfig = Figure(\n    resolution = (1200, 1200),\n    fontsize = 14,\n    backgroundcolor = PAGE_BG,\n)\n\nax = Axis(\n    fig[1, 1];\n    title = \"heatmap-annotated · julia · makie · anyplot.ai\",\n    titlesize = 20,\n    titlecolor = INK,\n    xticks = (1:n_vars, variables),\n    yticks = (1:n_vars, variables),\n    xticklabelrotation = pi / 4,\n    xticklabelcolor = INK_SOFT,\n    yticklabelcolor = INK_SOFT,\n    xticklabelsize = 13,\n    yticklabelsize = 13,\n    backgroundcolor = PAGE_BG,\n    topspinevisible = false,\n    rightspinevisible = false,\n    leftspinevisible = false,\n    bottomspinevisible = false,\n    xgridvisible = false,\n    ygridvisible = false,\n    aspect = DataAspect(),\n    yreversed = true,\n)\n\n# Diagonal self-correlations (always 1.00) are redundant next to the\n# off-diagonal relationships the matrix exists to reveal, so the fill mask\n# swaps them for NaN and renders them in a muted neutral tone instead of\n# solid colormap blue -- the value still prints, just visually quieted.\nconst NEUTRAL_FILL = RGBA(convert(RGB, INK_SOFT), 0.12)\ndisplay_matrix = copy(corr_matrix)\nfor i in 1:n_vars\n    display_matrix[i, i] = NaN\nend\n\nhm = heatmap!(\n    ax, 1:n_vars, 1:n_vars, display_matrix;\n    colormap = ANYPLOT_DIV, colorrange = (-1, 1), nan_color = NEUTRAL_FILL,\n)\n\n# Subtle grid at cell boundaries keeps near-zero-correlation cells (whose\n# fill sits close to the theme background by design) visually separated\n# from the canvas without touching the data colors themselves.\nfor k in 0.5:(n_vars + 0.5)\n    hlines!(ax, k; xmin = 0, xmax = 1, color = (INK_SOFT, 0.15), linewidth = 1)\n    vlines!(ax, k; ymin = 0, ymax = 1, color = (INK_SOFT, 0.15), linewidth = 1)\nend\n\nconst STRONG_THRESHOLD = 0.5\nfor i in 1:n_vars, j in 1:n_vars\n    value = corr_matrix[i, j]\n    is_diagonal = i == j\n    is_strong = !is_diagonal && abs(value) >= STRONG_THRESHOLD\n\n    if is_diagonal\n        text_color = INK_SOFT\n        label_fontsize = 13\n    else\n        fill_color = ANYPLOT_DIV[(value + 1) / 2]\n        luminance = 0.299 * red(fill_color) + 0.587 * green(fill_color) + 0.114 * blue(fill_color)\n        text_color = luminance > 0.5 ? DARK_TEXT : LIGHT_TEXT\n        label_fontsize = 16\n    end\n\n    # A crisp outline calls out the strongest relationships (|r| >= 0.5) so\n    # they read as the focal point instead of competing equally with weak\n    # correlations for attention.\n    if is_strong\n        poly!(\n            ax, Rect2f(i - 0.5, j - 0.5, 1, 1);\n            color = :transparent, strokecolor = INK, strokewidth = 2.5,\n        )\n    end\n\n    text!(\n        ax, i, j;\n        text = @sprintf(\"%.2f\", value),\n        align = (:center, :center),\n        fontsize = label_fontsize,\n        font = is_strong ? :bold : :regular,\n        color = text_color,\n    )\nend\n\nColorbar(\n    fig[1, 2], hm;\n    label = \"Correlation\",\n    labelsize = 14,\n    labelcolor = INK,\n    ticklabelsize = 12,\n    ticklabelcolor = INK_SOFT,\n    tickcolor = INK_SOFT,\n)\n\n# --- Save -------------------------------------------------------------------\nsave(\"plot-$(THEME).png\", fig; px_per_unit = 2)\n"}