Published
Edited
Jan 5, 2022
1 fork
1 star
Insert cell
Insert cell
margins=({top:10,bottom:60,left:30,right:50});
Insert cell
height=width/2
Insert cell
colorScale = d3.scaleOrdinal(d3.schemeTableau10).domain(tree.annotations.location.values)
Insert cell
tree.annotations.location.values
Insert cell
Insert cell
swooshColor = "#B0B0B0"
Insert cell
branchColor="#B0B0B0"
Insert cell
Insert cell
Insert cell
Insert cell
viewof loadedFile = html`<input type=file accept="text/*">`
Insert cell
Insert cell
tree = FileAttachment("tree.nexus").text()
//Files.text(loadedFile) // uncomment this line and comment the line above if choosing a file form above
.then(t=> ft.Tree.parseNexus(t,{datePrefix:"|",dateFormat:"%Y-%m-%d"})[0])//parse nexus returns an array of trees. Here the tip dates are taken from the tip labels that look like "Tip1|2020-03-11"
.then(tree=>{
tree.orderByNodeDensity(true);
collapse(tree, 0.5);
return tree;
})

Insert cell
Insert cell
figtree = {
//dependencies
xAxis;
bars;
setupSVG(d3.select(document.getElementById("tree"))) // add gradients and white background to
return new ft.FigTree(document.getElementById("tree"), margins, tree)
.layout(ft.rectangularLayout)
.nodes(
ft.coalescentEvent()
.filter(d => d.children && d.children.length>2 ) // only nodes with children and more than 2
.attr("fill", "url(#nodeGrad)") // here is the fill gradient we set in the chart cell
.attr('pointer-events', "none")
.setting("slope", 0.5),
ft.circle() // circles on tips
.filter(v => !v.children )
.attr("fill", n => colorScale(n.annotations.location))
.attr("r", 5) // radius
.hilightOnHover(8),
)
.nodeBackgrounds(
ft.circle().filter(v => !v.children).attr("r", 6),) // we could give the nodes a "stroke" but putting a slightly bigger cirlce behind the node hilights clusters that might be over plotted
.branches(
ft.branch()
.filter(n=> n.parent && n.parent.children.length>2)
.attr("stroke", n => "url(#strokegrad-internal)")// here is the branch gradient we set in the chart cell
.attr("stroke-width", 2),
ft.branch()
.filter(n=> n.parent && n.parent.children.length<=2)
.attr("stroke",n=>branchColor)
.attr("stroke-width", 2),
)
.feature(xAxis)
.feature(bars)
.feature(ft.legend()
.scale(colorScale)
.size(10)
.x(100)
.y(100)
);
}
Insert cell
tree.getExternalNode("virusA|1975").children
Insert cell
Insert cell
xAxis = {
chart;// this just makes this cell rerun after the chart is rerendered
const maxDate = d3.max(tree.externalNodes, d => d.annotations.date)
return ft.axis()
.location("bottom")
.y(height - margins.top - margins.bottom + 5)
.x(0)
.title({
text: "",
yPadding: 30
})
.tickFormat(customFormat("%Y")) //defined below for dates
.ticks(6)
.origin(maxDate);
}
Insert cell
Insert cell
Insert cell
collapse =(tree,cutoff)=>{
for(const node of tree.preorder()){
if(node.annotations.posterior<cutoff){
tree.removeNode(node);
}
}
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function setupSVG(svg){
//clear anything that was on the svg. This is hack to work in observable where cell will rerun and keep appending to an svg.
svg.selectAll("g").remove()
svg.selectAll("defs").remove()
svg.selectAll("rect").remove()

svg.append("rect")
.attr("x",0)
.attr("y",0)
.attr("width",width)
.attr("height",height)
.attr("fill","white")

const defs = svg
.append("defs");

// This sets ups the node/swoosh gradients
// if you put this in a for loop and loop over mulitple colors be sure to give each on a different id
// so it can be
const nodeGrad = defs
.append("linearGradient")
.attr("id","nodeGrad") // give this a different id for each color/value
nodeGrad.append('stop')
.attr('offset', '0')
.style("stop-color",swooshColor) //727a98
.style("stop-opacity", "100%")
nodeGrad.append('stop')
.attr('offset', '0.25')
.style("stop-color",swooshColor)
.style("stop-opacity", "0%")
nodeGrad.append('stop')
.attr('offset', '0.5')
.style("stop-color",swooshColor)
.style("stop-opacity", "0%")

nodeGrad.append('stop')
.attr('offset', '1')
.style("stop-color",swooshColor)
.style("stop-opacity", "0%")
// Gradients for the branches that emerge from the swooshes. The same comments as above apply here for looping over different colores.
const branchGrad=defs
.append("linearGradient")
.attr("id",`strokegrad-internal`);

branchGrad.append("stop")
.style("stop-opacity","0%")
.style("stop-color",branchColor)
.attr("offset","0");

branchGrad.append("stop")
.style("stop-opacity","0%")
.style("stop-color",branchColor)
.attr("offset","0.5");
branchGrad.append("stop")
.style("stop-opacity","100%")
.style("stop-color",branchColor)
.attr("offset","1");
}
Insert cell
Insert cell
Insert cell
Insert cell
## css styles for this page
Insert cell
styles=html `
<style>
body {
font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
font-weight: 300;
}
.legend text{
font-size: 12px;
}
.axis text{
font-size: 14px;
}
.branch{
stroke-linejoin:round;
}
.node text{
display:none
}
.node.hovered text{
display:inline
}

</style>
`
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more