Right here
"... Includes Python Notebooks, d3.js, and straight React SVG. ..."
-- Tim, 2019
[
{
"name": "Calibrate the Kubernetes Cluster",
"points": 2,
"owner": "Heather",
"iterationStartDate": "2019-07-08",
"workStartDate": "2019-07-10",
"finishDate": "2019-07-10"
},
{
"name": "Reimplement the Cache",
"points": 5,
"owner": "Eli",
"iterationStartDate": "2019-07-08",
"workStartDate": "2019-07-09",
"finishDate": "2019-07-16"
},
{
"name": "Restore the Secret Store",
"points": 5,
"owner": "Heather",
"iterationStartDate": "2019-07-08",
"workStartDate": "2019-07-08",
"finishDate": "2019-07-10"
},
{
"name": "Calibrate the Feature Flag for B",
"points": 3,
"owner": "Heather",
"iterationStartDate": "2019-07-08",
"workStartDate": "2019-07-08",
"finishDate": "2019-07-08"
},
{
"name": "Address the Mongo Backend",
"points": 21,
"owner": "Eli",
"iterationStartDate": "2019-07-08",
"workStartDate": "2019-07-08",
"finishDate": "2019-07-31"
},
{
"name": "Fix the Azure Configuration",
"points": 2,
"owner": "Sam",
"iterationStartDate": "2019-07-08",
"workStartDate": "2019-07-08",
"finishDate": "2019-07-09"
}
]
Jupyter
SVG/d3.js
streamlit
ObservableHQ
<svg width="500" height="140">
<g>
<circle
cx="50" cy="50"
r="50"/>
</g>
</svg>
var width = window.innerWidth,
height = window.innerHeight
positionStrength = 0.025;
var margin = {
top: 50,
left: 100,
bottom: 50,
right: 50
}
function updateDOM() {
let dots = svg.selectAll('circle').data(workitems);
dots.enter()
.append('circle')
.style("stroke", currentStroke)
.style("fill", currentFill)
.attr("r", 5);
dots
.attr("cx", d => d.x)
.attr("cy", d => d.y)
let xaxis = d3.axisBottom(x ? x : d3.scaleLinear())
let xAxisG = svg.selectAll('g.xaxis').data(x ? [0] : [])
xAxisG.enter()
.append('g')
.classed('xaxis', true)
.call(xaxis)
.style("opacity", 0)
.attr("transform", `translate(0,${xAxisTransform})`)
.transition()
.delay(500)
.duration(750)
.style("opacity", 1);
xAxisG.exit().remove();
}
document.getElementById("first").onclick = function() {
x = d3.scaleTime()
.domain(d3.extent(workitems, d => d.finishDate))
.range([margin.left, width - margin.right]);
y = null;
simulation
.force("charge", d3.forceManyBody().distanceMax(20).strength(-15))
.force("center", null)
.force("x", d3.forceX(d => x(d.finishDate)).strength(positionStrength))
.force("y", d3.forceY(height / 2).strength(positionStrength))
.alpha(1)
.restart();
xAxisTransform = height / 2 + 50;
svg.selectAll('g.xaxis')
.transition()
.duration(500)
.attr("transform", `translate(0, ${xAxisTransform})`);
};
var simulation = d3.forceSimulation()
.alphaDecay( 1 - (0.001**(1 / 600)) )
.force("charge", d3.forceManyBody().distanceMax(20).strength(-15))
.force("collide", d3.forceCollide().radius(5))
.force("center", d3.forceCenter( width / 2, height / 2))
.nodes(workitems)
.on("tick", updateDOM);
...
// Later, in "1"
simulation
.force("charge", d3.forceManyBody().distanceMax(20).strength(-15))
.force("center", null)
.force("x", d3.forceX(d => x(d.finishDate)).strength(positionStrength))
.force("y", d3.forceY(height / 2).strength(positionStrength))
.alpha(1)
.restart();
...
// Later, in "2"
simulation
.force("charge", null)
.force("center", null)
.force("x", d3.forceX(d => x(d.finishDate)).strength(positionStrength))
.force("y", d3.forceY(d => y(d.iterationStartDate)).strength(positionStrength))
.alpha(2)
.restart();
(Last one, I promise)
"Graphical excellence is that which gives to the viewer the greatest number of ideas in the shortest time with the least ink in the smallest space."
Jupyter | Streamlit | d3.js | ObservableHQ | |
---|---|---|---|---|
Language | Python | Python | Javascript | Javascript |
Easy to Learn | ✓ | ✓ | ✖ | ✖ |
Interactive | ✖ | ✓ | ✓ | ✓ |
Custom Viz | ✖ | ✖ | ✓ | ✓ |
Run locally? | ✓ | ✓ | ✓ | ✖ |
Thank You!
Tim Garvin
@tcgarvin
Dedicated to Pete Garvin