{"spec_id":"tree-phylogenetic","library":"pygal","language":"python","code":"\"\"\" anyplot.ai\ntree-phylogenetic: Phylogenetic Tree Diagram\nLibrary: pygal 3.1.0 | Python 3.13.13\nQuality: 68/100 | Updated: 2026-05-15\n\"\"\"\n\nimport cairosvg\nimport pygal\nfrom pygal.style import Style\n\n\n# Primate phylogenetic tree based on mitochondrial DNA divergence (simplified)\n# Tree structure: ((((Human, Chimpanzee), Gorilla), Orangutan), (Gibbon, Macaque))\n# Scaled to use more canvas width - multiply x positions to spread tree wider\nscale_factor = 1.35\nspecies_data = [\n    (\"Human\", 0.95 * scale_factor, 0),\n    (\"Chimpanzee\", 0.95 * scale_factor, 1.5),\n    (\"Gorilla\", 0.82 * scale_factor, 3),\n    (\"Orangutan\", 0.62 * scale_factor, 5),\n    (\"Gibbon\", 0.95 * scale_factor, 7),\n    (\"Macaque\", 0.95 * scale_factor, 8.5),\n]\n\n# Y positions from species data\nspecies_y = {name: y for name, _, y in species_data}\nspecies_x = {name: x for name, x, _ in species_data}\n\n# Define tree connections\ntree_segments = []\n\n# Human-Chimpanzee clade (most recent common ancestor)\nhc_ancestor_x = 0.82 * scale_factor\nhc_ancestor_y = (species_y[\"Human\"] + species_y[\"Chimpanzee\"]) / 2\ntree_segments.append([(hc_ancestor_x, species_y[\"Human\"]), (species_x[\"Human\"], species_y[\"Human\"])])\ntree_segments.append([(hc_ancestor_x, species_y[\"Chimpanzee\"]), (species_x[\"Chimpanzee\"], species_y[\"Chimpanzee\"])])\ntree_segments.append([(hc_ancestor_x, species_y[\"Human\"]), (hc_ancestor_x, species_y[\"Chimpanzee\"])])\n\n# Human-Chimp-Gorilla clade\nhcg_ancestor_x = 0.62 * scale_factor\nhcg_ancestor_y = (hc_ancestor_y + species_y[\"Gorilla\"]) / 2\ntree_segments.append([(hcg_ancestor_x, hc_ancestor_y), (hc_ancestor_x, hc_ancestor_y)])\ntree_segments.append([(hcg_ancestor_x, species_y[\"Gorilla\"]), (species_x[\"Gorilla\"], species_y[\"Gorilla\"])])\ntree_segments.append([(hcg_ancestor_x, hc_ancestor_y), (hcg_ancestor_x, species_y[\"Gorilla\"])])\n\n# Great apes clade including Orangutan\ngreat_apes_x = 0.41 * scale_factor\ngreat_apes_y = (hcg_ancestor_y + species_y[\"Orangutan\"]) / 2\ntree_segments.append([(great_apes_x, hcg_ancestor_y), (hcg_ancestor_x, hcg_ancestor_y)])\ntree_segments.append([(great_apes_x, species_y[\"Orangutan\"]), (species_x[\"Orangutan\"], species_y[\"Orangutan\"])])\ntree_segments.append([(great_apes_x, hcg_ancestor_y), (great_apes_x, species_y[\"Orangutan\"])])\n\n# Gibbon-Macaque clade\ngm_ancestor_x = 0.68 * scale_factor\ngm_ancestor_y = (species_y[\"Gibbon\"] + species_y[\"Macaque\"]) / 2\ntree_segments.append([(gm_ancestor_x, species_y[\"Gibbon\"]), (species_x[\"Gibbon\"], species_y[\"Gibbon\"])])\ntree_segments.append([(gm_ancestor_x, species_y[\"Macaque\"]), (species_x[\"Macaque\"], species_y[\"Macaque\"])])\ntree_segments.append([(gm_ancestor_x, species_y[\"Gibbon\"]), (gm_ancestor_x, species_y[\"Macaque\"])])\n\n# Root: connects great apes and gibbon-macaque clades (x=0)\nroot_x = 0.0\nroot_y = (great_apes_y + gm_ancestor_y) / 2\ntree_segments.append([(root_x, great_apes_y), (great_apes_x, great_apes_y)])\ntree_segments.append([(root_x, gm_ancestor_y), (gm_ancestor_x, gm_ancestor_y)])\ntree_segments.append([(root_x, great_apes_y), (root_x, gm_ancestor_y)])\n\n# Colorblind-friendly palette for species markers\nspecies_colors = [\"#E63946\", \"#457B9D\", \"#2A9D8F\", \"#E9C46A\", \"#F4A261\", \"#9C6644\"]\n\n# Branch color - pyplots blue\nbranch_color = \"#306998\"\n\n# Custom style for pyplots - larger fonts for 4800x2700 canvas\ncustom_style = Style(\n    background=\"white\",\n    plot_background=\"white\",\n    foreground=\"#333\",\n    foreground_strong=\"#333\",\n    foreground_subtle=\"#999\",\n    colors=(branch_color,),\n    title_font_size=56,\n    label_font_size=44,\n    major_label_font_size=40,\n    legend_font_size=44,\n    value_font_size=36,\n    tooltip_font_size=28,\n    stroke_width=6,\n    opacity=1.0,\n    guide_stroke_color=\"#ddd\",\n)\n\n# Create XY chart for phylogenetic tree\nchart = pygal.XY(\n    width=4800,\n    height=2700,\n    style=custom_style,\n    title=\"Primate Evolution · tree-phylogenetic · pygal · pyplots.ai\",\n    x_title=\"Evolutionary Distance (substitutions per site)\",\n    y_title=\"\",\n    show_legend=False,\n    show_dots=False,\n    stroke_style={\"width\": 6},\n    fill=False,\n    show_x_guides=False,\n    show_y_guides=False,\n    show_y_labels=False,\n    range=(-1.5, 10),\n    xrange=(-0.05, 1.45),\n    print_values=False,\n)\n\n# Add all tree branches as unnamed series\nfor seg in tree_segments:\n    chart.add(None, seg, show_dots=False, stroke_style={\"width\": 6})\n\n# Add species markers (dots only, labels added via SVG)\nfor i, (_name, x_pos, y_pos) in enumerate(species_data):\n    color = species_colors[i % len(species_colors)]\n    chart.add(\n        None, [{\"value\": (x_pos, y_pos), \"color\": color}], show_dots=True, dots_size=28, stroke_style={\"width\": 0}\n    )\n\n# Render to SVG string first\nsvg_content = chart.render().decode(\"utf-8\")\n\n# Calculate pixel positions for species labels\n# Plot area bounds for coordinate conversion\nplot_x_min, plot_x_max = 180, 4620\nplot_y_min, plot_y_max = 100, 2500\ndata_x_min, data_x_max = -0.05, 1.45\ndata_y_min, data_y_max = -1.5, 10\n\n# Generate species label SVG elements positioned directly next to leaf nodes\nspecies_labels_svg = '<g class=\"species-labels\">\\n'\nfor i, (name, x_pos, y_pos) in enumerate(species_data):\n    # Inline coordinate conversion (data to pixel)\n    px = plot_x_min + (x_pos - data_x_min) / (data_x_max - data_x_min) * (plot_x_max - plot_x_min)\n    py = plot_y_max - (y_pos - data_y_min) / (data_y_max - data_y_min) * (plot_y_max - plot_y_min)\n    color = species_colors[i % len(species_colors)]\n    # Position label to the right of the marker\n    species_labels_svg += f'  <text x=\"{px + 50}\" y=\"{py + 12}\" font-size=\"42\" fill=\"{color}\" '\n    species_labels_svg += f'font-family=\"sans-serif\" font-weight=\"bold\">{name}</text>\\n'\nspecies_labels_svg += \"</g>\\n\"\n\n# Add scale bar with label - inline coordinate conversion\nscale_x_data, scale_y_data = 0.0, -1.0\nscale_px = plot_x_min + (scale_x_data - data_x_min) / (data_x_max - data_x_min) * (plot_x_max - plot_x_min)\nscale_py = plot_y_max - (scale_y_data - data_y_min) / (data_y_max - data_y_min) * (plot_y_max - plot_y_min)\nscale_end_x_data = 0.1\nscale_end_px = plot_x_min + (scale_end_x_data - data_x_min) / (data_x_max - data_x_min) * (plot_x_max - plot_x_min)\nscale_width = scale_end_px - scale_px\n\nscale_bar_svg = f\"\"\"\n<g class=\"scale-bar\">\n  <line x1=\"{scale_px}\" y1=\"{scale_py}\" x2=\"{scale_end_px}\" y2=\"{scale_py}\" stroke=\"#333\" stroke-width=\"10\"/>\n  <text x=\"{scale_px + scale_width / 2}\" y=\"{scale_py + 55}\" text-anchor=\"middle\" font-size=\"40\" fill=\"#333\" font-family=\"sans-serif\">0.1 substitutions/site</text>\n</g>\n\"\"\"\n\n# Insert labels and scale bar before closing </svg> tag\nsvg_content = svg_content.replace(\"</svg>\", species_labels_svg + scale_bar_svg + \"</svg>\")\n\n# Save SVG\nwith open(\"plot.svg\", \"w\") as f:\n    f.write(svg_content)\n\n# For HTML, use the modified SVG\nhtml_content = f\"\"\"<!DOCTYPE html>\n<html>\n<head>\n    <meta charset=\"utf-8\">\n    <title>Phylogenetic Tree - pygal</title>\n    <style>body {{ margin: 0; display: flex; justify-content: center; align-items: center; min-height: 100vh; }}</style>\n</head>\n<body>\n{svg_content}\n</body>\n</html>\"\"\"\nwith open(\"plot.html\", \"w\") as f:\n    f.write(html_content)\n\n# Convert to PNG using cairosvg\ncairosvg.svg2png(bytestring=svg_content.encode(\"utf-8\"), write_to=\"plot.png\")\n"}