{"spec_id":"genome-track-multi","library":"matplotlib","language":"python","code":"\"\"\" anyplot.ai\ngenome-track-multi: Genome Track Viewer\nLibrary: matplotlib 3.10.9 | Python 3.13.13\nQuality: 90/100 | Updated: 2026-06-02\n\"\"\"\n\nimport os\n\nimport matplotlib.patches as mpatches\nimport matplotlib.patheffects as pe\nimport matplotlib.pyplot as plt\nimport numpy as np\nfrom matplotlib.collections import LineCollection\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\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\n\n# Imprint categorical palette — positions 1→7 assigned across all tracks\nGENE_COLOR = \"#009E73\"  # position 1 — gene exons (★ first categorical series)\nSNP_COLOR = \"#C475FD\"  # position 2 — SNP variants\nCOVERAGE_COLOR = \"#4467A3\"  # position 3 — read depth (blue: semantic fit for sequencing flow)\nINDEL_COLOR = \"#BD8233\"  # position 4 — indel variants\nPROMOTER_COLOR = \"#AE3030\"  # position 5 — promoter elements (active regulation)\nENHANCER_COLOR = \"#2ABCCD\"  # position 6 — enhancer elements\nINSULATOR_COLOR = \"#954477\"  # position 7 — insulator elements\n\n# Data: chr7 HOXA gene cluster region\nnp.random.seed(42)\nchrom = \"chr7\"\nregion_start = 27_200_000\nregion_end = 27_280_000\n\ngenes = [\n    {\n        \"name\": \"HOXA1\",\n        \"start\": 27_204_000,\n        \"end\": 27_214_000,\n        \"strand\": \"+\",\n        \"exons\": [(27_204_000, 27_205_500), (27_207_000, 27_208_200), (27_211_000, 27_214_000)],\n    },\n    {\n        \"name\": \"HOXA2\",\n        \"start\": 27_220_000,\n        \"end\": 27_232_000,\n        \"strand\": \"+\",\n        \"exons\": [(27_220_000, 27_221_800), (27_225_000, 27_226_500), (27_229_500, 27_232_000)],\n    },\n    {\n        \"name\": \"HOXA3\",\n        \"start\": 27_240_000,\n        \"end\": 27_258_000,\n        \"strand\": \"-\",\n        \"exons\": [(27_240_000, 27_242_500), (27_247_000, 27_249_000), (27_254_000, 27_258_000)],\n    },\n    {\n        \"name\": \"HOXA4\",\n        \"start\": 27_262_000,\n        \"end\": 27_275_000,\n        \"strand\": \"+\",\n        \"exons\": [(27_262_000, 27_264_000), (27_268_000, 27_270_500), (27_273_000, 27_275_000)],\n    },\n]\n\n# Coverage: simulated RNA-seq read depth\ncoverage_positions = np.linspace(region_start, region_end, 800)\ncoverage_base = np.random.exponential(5, 800)\nfor gene in genes:\n    for exon_start, exon_end in gene[\"exons\"]:\n        mask = (coverage_positions >= exon_start) & (coverage_positions <= exon_end)\n        coverage_base[mask] += np.random.exponential(40, mask.sum())\ncoverage_depth = np.convolve(coverage_base, np.ones(5) / 5, mode=\"same\")\n\n# Variants: SNPs and indels with quality scores\nvariant_positions = np.array(\n    [\n        27_205_200,\n        27_208_100,\n        27_215_000,\n        27_221_500,\n        27_226_200,\n        27_233_000,\n        27_241_800,\n        27_248_500,\n        27_255_500,\n        27_263_500,\n        27_269_000,\n        27_274_200,\n        27_237_000,\n        27_260_000,\n        27_270_800,\n    ]\n)\nvariant_types = [\"SNP\"] * 10 + [\"indel\"] * 5\nvariant_quality = np.random.uniform(20, 100, len(variant_positions))\n\n# Regulatory elements\nregulatory_elements = [\n    {\"type\": \"Promoter\", \"start\": 27_202_000, \"end\": 27_204_000},\n    {\"type\": \"Enhancer\", \"start\": 27_216_000, \"end\": 27_219_000},\n    {\"type\": \"Promoter\", \"start\": 27_218_500, \"end\": 27_220_000},\n    {\"type\": \"Enhancer\", \"start\": 27_234_000, \"end\": 27_238_000},\n    {\"type\": \"Promoter\", \"start\": 27_238_500, \"end\": 27_240_000},\n    {\"type\": \"Insulator\", \"start\": 27_258_500, \"end\": 27_261_000},\n    {\"type\": \"Promoter\", \"start\": 27_260_500, \"end\": 27_262_000},\n    {\"type\": \"Enhancer\", \"start\": 27_276_000, \"end\": 27_279_000},\n]\nreg_colors = {\"Promoter\": PROMOTER_COLOR, \"Enhancer\": ENHANCER_COLOR, \"Insulator\": INSULATOR_COLOR}\n\n# Exon positions collected for cross-track guide bands\nall_exons = [exon for gene in genes for exon in gene[\"exons\"]]\n\n# Title (53 chars — within 67-char baseline, no scaling needed)\ntitle = \"genome-track-multi · python · matplotlib · anyplot.ai\"\n\n# Figure — 3200×1800 landscape canvas\nfig, axes = plt.subplots(\n    4, 1, figsize=(8, 4.5), dpi=400, sharex=True, facecolor=PAGE_BG, gridspec_kw={\"height_ratios\": [2, 2.5, 1.5, 1.5]}\n)\nfig.subplots_adjust(left=0.115, right=0.97, top=0.90, bottom=0.14, hspace=0.08)\n\n# Subtle exon guide bands across all four tracks — connects peaks/variants/elements to gene exons\nfor ax in axes:\n    ax.set_facecolor(PAGE_BG)\n    for exon_s, exon_e in all_exons:\n        ax.axvspan(exon_s, exon_e, alpha=0.05, color=GENE_COLOR, zorder=0)\n\n# Track 1: Gene annotations\nax_genes = axes[0]\nfor gene in genes:\n    yc = 0.5\n    ax_genes.plot(\n        [gene[\"start\"], gene[\"end\"]], [yc, yc], color=GENE_COLOR, linewidth=1.5, solid_capstyle=\"butt\", alpha=0.45\n    )\n    n_chevrons = max(2, int((gene[\"end\"] - gene[\"start\"]) / 3000))\n    for cx in np.linspace(gene[\"start\"] + 1500, gene[\"end\"] - 1500, n_chevrons):\n        dx = 600 if gene[\"strand\"] == \"+\" else -600\n        ax_genes.plot([cx - dx, cx, cx - dx], [yc - 0.12, yc, yc + 0.12], color=GENE_COLOR, linewidth=1.0, alpha=0.55)\n    for exon_start, exon_end in gene[\"exons\"]:\n        rect = mpatches.FancyBboxPatch(\n            (exon_start, yc - 0.28),\n            exon_end - exon_start,\n            0.56,\n            boxstyle=\"round,pad=0,rounding_size=200\",\n            facecolor=GENE_COLOR,\n            edgecolor=PAGE_BG,\n            linewidth=0.8,\n        )\n        ax_genes.add_patch(rect)\n    arrow_char = \"▶\" if gene[\"strand\"] == \"+\" else \"◀\"\n    txt = ax_genes.text(\n        (gene[\"start\"] + gene[\"end\"]) / 2,\n        yc + 0.48,\n        f\"{gene['name']} {arrow_char}\",\n        fontsize=8,\n        va=\"bottom\",\n        ha=\"center\",\n        color=INK,\n        fontweight=\"semibold\",\n    )\n    txt.set_path_effects([pe.withStroke(linewidth=2, foreground=PAGE_BG)])\n\nax_genes.set_ylim(-0.15, 1.35)\nax_genes.set_ylabel(\"Genes\", fontsize=10, fontweight=\"medium\", labelpad=6, color=INK)\nax_genes.set_yticks([])\nfor spine in ax_genes.spines.values():\n    spine.set_visible(False)\nax_genes.tick_params(axis=\"x\", which=\"both\", bottom=False)\n\n# Track 2: Coverage (RNA-seq depth)\nax_cov = axes[1]\nax_cov.set_axisbelow(True)\nax_cov.yaxis.grid(True, alpha=0.12, linewidth=0.6, color=INK)\nax_cov.fill_between(coverage_positions, coverage_depth, alpha=0.28, color=COVERAGE_COLOR, linewidth=0)\nax_cov.plot(coverage_positions, coverage_depth, color=COVERAGE_COLOR, linewidth=1.2, alpha=0.85)\nax_cov.set_ylabel(\"Coverage\\n(depth)\", fontsize=10, fontweight=\"medium\", labelpad=6, color=INK)\nax_cov.set_ylim(0, coverage_depth.max() * 1.15)\nax_cov.tick_params(axis=\"y\", labelsize=8, colors=INK_SOFT, labelcolor=INK_SOFT)\nax_cov.tick_params(axis=\"x\", which=\"both\", bottom=False)\nax_cov.spines[\"top\"].set_visible(False)\nax_cov.spines[\"right\"].set_visible(False)\nax_cov.spines[\"bottom\"].set_visible(False)\nax_cov.spines[\"left\"].set_color(INK_SOFT)\n\n# Track 3: Variants (lollipop chart)\nax_var = axes[2]\nax_var.set_axisbelow(True)\nax_var.yaxis.grid(True, alpha=0.12, linewidth=0.6, color=INK)\nstem_lines, stem_colors_list = [], []\nsnp_x, snp_y, indel_x, indel_y = [], [], [], []\nfor pos, vtype, qual in zip(variant_positions, variant_types, variant_quality, strict=False):\n    h = qual / 100\n    stem_lines.append([(pos, 0), (pos, h)])\n    stem_colors_list.append(SNP_COLOR if vtype == \"SNP\" else INDEL_COLOR)\n    if vtype == \"SNP\":\n        snp_x.append(pos)\n        snp_y.append(h)\n    else:\n        indel_x.append(pos)\n        indel_y.append(h)\n\nax_var.add_collection(LineCollection(stem_lines, colors=stem_colors_list, linewidths=1.5, alpha=0.55))\nax_var.scatter(\n    snp_x, snp_y, color=SNP_COLOR, marker=\"o\", s=55, edgecolors=PAGE_BG, linewidth=0.6, zorder=3, label=\"SNP\"\n)\nax_var.scatter(\n    indel_x, indel_y, color=INDEL_COLOR, marker=\"D\", s=45, edgecolors=PAGE_BG, linewidth=0.6, zorder=3, label=\"Indel\"\n)\nleg_var = ax_var.legend(\n    fontsize=8, loc=\"upper right\", framealpha=0.9, edgecolor=INK_SOFT, handletextpad=0.4, borderpad=0.4\n)\nif leg_var:\n    leg_var.get_frame().set_facecolor(ELEVATED_BG)\n    plt.setp(leg_var.get_texts(), color=INK_SOFT)\nax_var.set_ylabel(\"Variants\\n(quality)\", fontsize=10, fontweight=\"medium\", labelpad=6, color=INK)\nax_var.set_ylim(0, 1.3)\nax_var.set_yticks([0, 0.5, 1.0])\nax_var.set_yticklabels([\"0\", \"50\", \"100\"])\nax_var.tick_params(axis=\"y\", labelsize=8, colors=INK_SOFT, labelcolor=INK_SOFT)\nax_var.tick_params(axis=\"x\", which=\"both\", bottom=False)\nax_var.spines[\"top\"].set_visible(False)\nax_var.spines[\"right\"].set_visible(False)\nax_var.spines[\"bottom\"].set_visible(False)\nax_var.spines[\"left\"].set_color(INK_SOFT)\n\n# Track 4: Regulatory elements\nax_reg = axes[3]\nfor elem in regulatory_elements:\n    c = reg_colors[elem[\"type\"]]\n    rect = mpatches.FancyBboxPatch(\n        (elem[\"start\"], 0.15),\n        elem[\"end\"] - elem[\"start\"],\n        0.7,\n        boxstyle=\"round,pad=0,rounding_size=300\",\n        facecolor=c,\n        edgecolor=PAGE_BG,\n        linewidth=0.8,\n        alpha=0.88,\n    )\n    ax_reg.add_patch(rect)\n    if elem[\"end\"] - elem[\"start\"] > 2000:\n        txt = ax_reg.text(\n            (elem[\"start\"] + elem[\"end\"]) / 2,\n            0.5,\n            elem[\"type\"][0],\n            fontsize=8,\n            ha=\"center\",\n            va=\"center\",\n            color=\"white\",\n            fontweight=\"bold\",\n        )\n        txt.set_path_effects([pe.withStroke(linewidth=2, foreground=c)])\n\nreg_patches = [mpatches.Patch(color=c, label=t) for t, c in reg_colors.items()]\nleg_reg = ax_reg.legend(handles=reg_patches, fontsize=8, loc=\"upper right\", framealpha=0.9, edgecolor=INK_SOFT, ncol=3)\nif leg_reg:\n    leg_reg.get_frame().set_facecolor(ELEVATED_BG)\n    plt.setp(leg_reg.get_texts(), color=INK_SOFT)\nax_reg.set_ylim(0, 1.2)\nax_reg.set_ylabel(\"Regulatory\", fontsize=10, fontweight=\"medium\", labelpad=6, color=INK)\nax_reg.set_yticks([])\nax_reg.spines[\"top\"].set_visible(False)\nax_reg.spines[\"right\"].set_visible(False)\nax_reg.spines[\"left\"].set_visible(False)\nax_reg.spines[\"bottom\"].set_color(INK_SOFT)\n\n# Shared x-axis (displayed on bottom track only)\nax_reg.set_xlim(region_start, region_end)\nax_reg.set_xlabel(f\"Genomic Position — {chrom} (Mb)\", fontsize=10, color=INK)\nax_reg.tick_params(axis=\"x\", labelsize=8, colors=INK_SOFT, labelcolor=INK_SOFT)\nax_reg.xaxis.set_major_formatter(plt.FuncFormatter(lambda x, _: f\"{x / 1e6:.2f}\"))\n\n# Title\nfig.suptitle(title, fontsize=12, fontweight=\"medium\", color=INK, y=0.97)\n\nplt.savefig(f\"plot-{THEME}.png\", dpi=400, facecolor=PAGE_BG)\n"}