Public
Edited
May 20
Fork of Tree of Life
Insert cell
Insert cell
Insert cell
chart = {
const root = d3
.hierarchy(data, (d) => d.branchset)
.sum((d) => (d.branchset ? 0 : 1))
.sort(
(a, b) => a.value - b.value || d3.ascending(a.data.length, b.data.length)
);

cluster(root);
setRadius(root, (root.data.length = 0), innerRadius / maxLength(root));
setColor(root);

const svg = d3
.create("svg")
.attr("viewBox", [-outerRadius, -outerRadius, width, width])
.attr("font-family", "sans-serif")
.attr("font-size", 9);

svg.append("g").call(legend);

svg.append("style").text(`

.link--active {
stroke: #000 !important;
stroke-width: 1.5px;
}

.link-extension--active {
stroke-opacity: .6;
}

.label--active {
font-weight: bold;
}

`);

const linkExtension = svg
.append("g")
.attr("fill", "none")
.attr("stroke", "#000")
.attr("stroke-opacity", 0.25)
.selectAll("path")
.data(root.links().filter((d) => !d.target.children))
.join("path")
.each(function (d) {
d.target.linkExtensionNode = this;
})
.attr("stroke-width", "0.5")
.attr("d", linkExtensionConstant);

const link = svg
.append("g")
.attr("fill", "none")
.attr("stroke", "#000")
.selectAll("path")
.data(root.links())
.join("path")
.each(function (d) {
d.target.linkNode = this;
})
.attr("d", linkConstant)
.attr("stroke-width", "1.5")
.attr("stroke", (d) => d.target.color);

svg
.append("g")
.selectAll("text")
.data(root.leaves())
.join("text")
.attr("dy", ".31em")
.attr(
"transform",
(d) =>
`rotate(${d.x - 90}) translate(${innerRadius + 4},0)${
d.x < 180 ? "" : " rotate(180)"
}`
)
.attr("text-anchor", (d) => (d.x < 180 ? "start" : "end"))
.attr("font-style", "italic")
.text((d) => d.data.name.replace(/_/g, " "))
.on("mouseover", mouseovered(true))
.on("mouseout", mouseovered(false));

function update(checked) {
const t = d3.transition().duration(750);
linkExtension
.transition(t)
.attr("d", checked ? linkExtensionVariable : linkExtensionConstant);
link.transition(t).attr("d", checked ? linkVariable : linkConstant);
}

function mouseovered(active) {
return function (event, d) {
d3.select(this).classed("label--active", active);
d3.select(d.linkExtensionNode)
.classed("link-extension--active", active)
.raise();
do d3.select(d.linkNode).classed("link--active", active).raise();
while ((d = d.parent));
};
}

return Object.assign(svg.node(), { update });
}
Insert cell
update = chart.update(showLength)
Insert cell
cluster = d3.cluster()
.size([360, innerRadius])
.separation((a, b) => 1)
Insert cell
color = d3
.scaleOrdinal()
.domain(["Non-sedge flowering plants", "Sedges", "Mosses"])
.range(d3.schemeCategory10)
Insert cell
// Compute the maximum cumulative length of any node in the tree.
function maxLength(d) {
return d.data.length + (d.children ? d3.max(d.children, maxLength) : 0);
}
Insert cell
// Set the radius of each node by recursively summing and scaling the distance from the root.
function setRadius(d, y0, k) {
d.radius = (y0 += d.data.length) * k;
if (d.children) d.children.forEach(d => setRadius(d, y0, k));
}
Insert cell
// Set the color of each node by recursively inheriting.
function setColor(d) {
if (!d.children || d.children.length === 0) {
// Leaf node
const name = d.data.name;
d.group = nameToGroup[name] || null;
} else {
const childGroups = new Set();
d.children.forEach(child => {
setColor(child);
if (child.group) childGroups.add(child.group);
});

// If all children share the same group, assign it to the parent
if (childGroups.size === 1) {
d.group = [...childGroups][0];
} else {
d.group = null; // Mixed group — do not assign
}
}

d.color = d.group ? color(d.group) : (d.parent ? d.parent.color : null);
}

Insert cell
function linkVariable(d) {
return linkStep(d.source.x, d.source.radius, d.target.x, d.target.radius);
}
Insert cell
function linkConstant(d) {
return linkStep(d.source.x, d.source.y, d.target.x, d.target.y);
}
Insert cell
function linkExtensionVariable(d) {
return linkStep(d.target.x, d.target.radius, d.target.x, innerRadius);
}
Insert cell
function linkExtensionConstant(d) {
return linkStep(d.target.x, d.target.y, d.target.x, innerRadius);
}
Insert cell
function linkStep(startAngle, startRadius, endAngle, endRadius) {
const c0 = Math.cos(startAngle = (startAngle - 90) / 180 * Math.PI);
const s0 = Math.sin(startAngle);
const c1 = Math.cos(endAngle = (endAngle - 90) / 180 * Math.PI);
const s1 = Math.sin(endAngle);
return "M" + startRadius * c0 + "," + startRadius * s0
+ (endAngle === startAngle ? "" : "A" + startRadius + "," + startRadius + " 0 0 " + (endAngle > startAngle ? 1 : 0) + " " + startRadius * c1 + "," + startRadius * s1)
+ "L" + endRadius * c1 + "," + endRadius * s1;
}
Insert cell
legend = (svg) => {
const g = svg
.selectAll("g")
.data(color.domain())
.join("g")
.attr(
"transform",
(d, i) => `translate(${-outerRadius},${-outerRadius + i * 30})`
);

g.append("rect").attr("width", 24).attr("height", 24).attr("fill", color);

g.append("text")
.attr("x", 30)
.attr("y", 9)
.attr("dy", "0.5em")
.attr("font-size", "20px")
.text((d) => d);
}
Insert cell
data = parseNewick(await FileAttachment("concatenated.contree").text())
Insert cell
width = 954
Insert cell
outerRadius = width / 2
Insert cell
innerRadius = outerRadius - 170
Insert cell
// https://github.com/jasondavies/newick.js
function parseNewick(a){for(var e=[],r={},s=a.split(/\s*(;|\(|\)|,|:)\s*/),t=0;t<s.length;t++){var n=s[t];switch(n){case"(":var c={};r.branchset=[c],e.push(r),r=c;break;case",":var c={};e[e.length-1].branchset.push(c),r=c;break;case")":r=e.pop();break;case":":break;default:var h=s[t-1];")"==h||"("==h||","==h?r.name=n:":"==h&&(r.length=parseFloat(n))}}return r}
Insert cell
d3 = require("d3@6")
Insert cell
Dicots = "Non-sedge flowering plants"
Insert cell
Mosses = "Mosses"
Insert cell
Sedges = "Sedges"
Insert cell
nameToGroup = ({
Acaena_ovalifolia: Dicots,
Acer_campestre: Dicots,
Adonis_annua: Dicots,
Aesculus_hippocastanum: Dicots,
Agrostemma_githago: Dicots,
Ailanthus_altissima: Dicots,
Ajuga_chamaepitys: Dicots,
Alisma_plantago_aquatica: Dicots,
Alnus_glutinosa: Dicots,
Alnus_incana: Dicots,
Ammi_majus: Dicots,
Antitrichia_curtipendula: Mosses,
Aponogeton_distachyos: Dicots,
Aquilegia_vulgaris: Dicots,
Arabidopsis_thaliana: Dicots,
Arabis_hirsuta: Dicots,
Arctium_minus: Dicots,
Arenaria_serpyllifolia: Dicots,
Astragalus_alpinus: Dicots,
Astragalus_glycyphyllos: Dicots,
Ballota_nigra: Dicots,
Barbarea_vulgaris: Dicots,
Berberis_vulgaris: Dicots,
Blitum_bonus_henricus: Dicots,
Brassica_nigra: Dicots,
Bromus_sterilis: Dicots,
Bryoerythrophyllum_caledonicum: Mosses,
Buxus_sempervirens: Dicots,
Calendula_officinalis: Dicots,
Calliergon_cordifolium: Mosses,
Callitriche_obtusangula: Dicots,
Calluna_vulgaris: Dicots,
Calystegia_soldanella: Dicots,
Cardamine_flexuosa: Dicots,
Cardamine_hirsuta: Dicots,
Carduus_nutans: Dicots,
Carex_acutiformis: Sedges,
Carex_arenaria: Sedges,
Carex_caryophyllea: Sedges,
Carex_depauperata: Sedges,
Carex_distans: Sedges,
Carex_divulsa: Sedges,
Carex_extensa: Sedges,
Carex_hirta: Sedges,
Carex_laevigata: Sedges,
Carex_nigra: Sedges,
Carex_pendula: Sedges,
Carex_riparia: Sedges,
Carex_rostrata: Sedges,
Carex_sylvatica: Sedges,
Carlina_vulgaris: Dicots,
Chamaenerion_angustifolium: Dicots,
Chenopodium_album: Dicots,
Cirsium_eriophorum: Dicots,
Climacium_dendroides: Mosses,
Comarum_palustre: Dicots,
Convolvulus_arvensis: Dicots,
Cornus_suecica: Dicots,
Corylus_avellana: Dicots,
Crataegus_laevigata: Dicots,
Cratoneuron_filicinum: Mosses,
Cruciata_laevipes: Dicots,
Cyperus_fuscus: Sedges,
Digitaria_sanguinalis: Dicots,
Draba_incana: Dicots,
Draba_verna: Dicots,
Dryas_octopetala: Dicots,
Eleocharis_quinqueflora: Sedges,
Epilobium_hirsutum: Dicots,
Eriophorum_vaginatum: Sedges,
Erodium_maritimum: Dicots,
Erodium_moschatum: Dicots,
Euonymus_europaeus: Dicots,
Euphorbia_lathyris: Dicots,
Euphorbia_maculata: Dicots,
Euphorbia_peplus: Dicots,
Euphrasia_confusa: Dicots,
Fagus_sylvatica: Dicots,
Fallopia_convolvulus: Dicots,
Filago_lutescens: Dicots,
Filipendula_ulmaria: Dicots,
Fragaria_moschata: Dicots,
Fragaria_vesca: Dicots,
Frangula_alnus: Dicots,
Galeopsis_tetrahit: Dicots,
Galium_aparine: Dicots,
Geranium_rotundifolium: Dicots,
Geum_rivale: Dicots,
Geum_urbanum: Dicots,
Gnaphalium_supinum: Dicots,
Gnaphalium_uliginosum: Dicots,
Hedera_helix: Dicots,
Herbertus_hutchinsiae: Mosses,
Heterocladium_heteropterum: Mosses,
Hibiscus_tridactylites: Dicots,
Hibiscus_verdcourtii: Dicots,
Humulus_lupulus: Dicots,
Hydrocotyle_vulgaris: Dicots,
Hyocomium_armoricum: Mosses,
Hypericum_androsaemum: Dicots,
Hypericum_hirsutum: Dicots,
Hypericum_pulchrum: Dicots,
Hypnum_cupressiforme: Mosses,
Ilex_aquifolium: Dicots,
Impatiens_glandulifera: Dicots,
Impatiens_parviflora: Dicots,
Iris_foetidissima: Dicots,
Isopterygiella_pulchella: Mosses,
Isothecium_myosuroides: Mosses,
Jacobaea_maritima: Dicots,
Jacobaea_vulgaris: Dicots,
Jasione_montana: Dicots,
Juncus_effusus: Dicots,
Juncus_squarrosus: Dicots,
Laburnum_anagyroides: Dicots,
Lamium_amplexicaule: Dicots,
Lamium_purpureum: Dicots,
Lapsana_communis: Dicots,
Lathyrus_aphaca: Dicots,
Lathyrus_linifolius: Dicots,
Leersia_oryzoides: Dicots,
Lemna_minuta: Dicots,
Lepidium_didymum: Dicots,
Ligustrum_vulgare: Dicots,
Linaria_vulgaris: Dicots,
Logfia_minima: Dicots,
Lunularia_cruciata: Mosses,
Luzula_pallescens: Dicots,
Luzula_sylvatica: Dicots,
Lycopus_europaeus: Dicots,
Malus_domestica: Dicots,
Malus_sylvestris: Dicots,
Malus_x_robusta: Dicots,
Malva_neglecta: Dicots,
Matricaria_chamomilla: Dicots,
Matricaria_discoidea: Dicots,
Matthiola_sinuata: Dicots,
Medicago_arabica: Dicots,
Medicago_lupulina: Dicots,
Melampyrum_sylvaticum: Dicots,
Melittis_melissophyllum: Dicots,
Mercurialis_annua: Dicots,
Misopates_orontium: Dicots,
Moehringia_trinervia: Dicots,
Myrica_gale: Dicots,
Myriophyllum_alterniflorum: Dicots,
Narthecium_ossifragum: Dicots,
Nasturtium_officinale: Dicots,
Neckera_pumila: Mosses,
Ononis_reclinata: Dicots,
Orthothecium_rufescens: Mosses,
Oxyria_digyna: Dicots,
Parietaria_judaica: Dicots,
Pentaglottis_sempervirens: Dicots,
Persicaria_maculosa: Dicots,
Petasites_albus: Dicots,
Petasites_hybridus: Dicots,
Phragmites_australis: Dicots,
Plagiothecium_denticulatum: Mosses,
Poa_chaixii: Dicots,
Polygonum_aviculare: Dicots,
Polygonum_maritimum: Dicots,
Polypogon_viridis: Dicots,
Polytrichastrum_sexangulare: Mosses,
Polytrichum_commune: Mosses,
Polytrichum_formosum: Mosses,
Populus_nigra: Dicots,
Populus_tremula: Dicots,
Potamogeton_natans: Dicots,
Potentilla_anserina: Dicots,
Potentilla_indica: Dicots,
Potentilla_sterilis: Dicots,
Primula_veris: Dicots,
Primula_vulgaris: Dicots,
Prunella_vulgaris: Dicots,
Prunus_padus: Dicots,
Pseudognaphalium_luteoalbum: Dicots,
Pseudoscleropodium_purum: Mosses,
Pseudotaxiphyllum_elegans: Mosses,
Pulicaria_dysenterica: Dicots,
Pyrus_communis: Dicots,
Quercus_cerris: Dicots,
Quercus_ilex: Dicots,
Quercus_petraea: Dicots,
Quercus_robur: Dicots,
Ranunculus_sardous: Dicots,
Ranunculus_sceleratus: Dicots,
Rhinanthus_minor: Dicots,
Rhynchostegiella_tenella: Mosses,
Rhynchostegium_megapolitanum: Mosses,
Rhytidiadelphus_loreus: Mosses,
Ribes_rubrum: Dicots,
Ribes_uva_crispa: Dicots,
Rosa_agrestis: Dicots,
Rosa_canina: Dicots,
Rosa_rugosa: Dicots,
Rosa_spinosissima: Dicots,
Rubus_caesius: Dicots,
Rubus_idaeus: Dicots,
Salicornia_ramosissima: Dicots,
Salix_caprea: Dicots,
Salix_cinerea: Dicots,
Salix_repens: Dicots,
Salix_viminalis: Dicots,
Sambucus_nigra: Dicots,
Samolus_valerandi: Dicots,
Schoenoplectus_lacustris: Sedges,
Schoenoplectus_triqueter: Sedges,
Scirpus_sylvaticus: Sedges,
Scutellaria_galericulata: Dicots,
Scutellaria_minor: Dicots,
Sherardia_arvensis: Dicots,
Solanum_dulcamara: Dicots,
Solanum_nigrum: Dicots,
Sparganium_emersum: Dicots,
Spergula_arvensis: Dicots,
Spergularia_bocconei: Dicots,
Spergularia_marina: Dicots,
Sphagnum_contortum: Mosses,
Sphagnum_girgensohnii: Mosses,
Sphagnum_tenellum: Mosses,
Straminergon_stramineum: Mosses,
Syringa_vulgaris: Dicots,
Tamarix_gallica: Dicots,
Tanacetum_parthenium: Dicots,
Tanacetum_vulgare: Dicots,
Thalictrum_minus: Dicots,
Thuidium_tamariscinum: Mosses,
Torilis_japonica: Dicots,
Tragopogon_pratensis: Dicots,
Trichophorum_cespitosum: Sedges,
Trifolium_dubium: Dicots,
Trifolium_fragiferum: Dicots,
Trifolium_occidentale: Dicots,
Trifolium_striatum: Dicots,
Trifolium_subterraneum: Dicots,
Triglochin_maritima: Dicots,
Triglochin_palustris: Dicots,
Tripolium_pannonicum: Dicots,
Tussilago_farfara: Dicots,
Ulmus_glabra: Dicots,
Ulota_crispa: Mosses,
Umbilicus_rupestris: Dicots,
Urtica_dioica: Dicots,
Urtica_urens: Dicots,
Vaccinium_vitis_idaea: Dicots,
Verbascum_nigrum: Dicots,
Verbascum_thapsus: Dicots,
Verbena_officinalis: Dicots,
Veronica_anagallis_aquatica: Dicots,
Veronica_arvensis: Dicots,
Veronica_scutellata: Dicots,
Veronica_serpyllifolia: Dicots,
Veronica_triphyllos: Dicots,
Veronica_verna: Dicots,
Vicia_sativa: Dicots,
Viola_odorata: Dicots,
Viscum_album: Dicots
})
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more