chart = {
var container = d3.create('div');
var charts = [];
measures.forEach((value, key) => {
var measure = getMeasure(key).sort((a, b) => a.AVISITN - b.AVISITN);
var measureStatistics = visitStatistics[key];
var ids = getIds(measure);
var median = getMedian(measure);
var x = getX(measure);
var y = getY(measure);
var colorScaleLine = getColorScaleLine(y);
var svg = container
.append('svg')
.attr('viewBox', [0, 0, width, height])
.datum({ x, y });
var visitText = svg
.append('text')
.attr('x', width / 2)
.attr('y', margin.top)
.attr('text-anchor', 'center')
.text(`${visits[0]} (n=${ids.length})`);
var gx = svg
.append("g")
.classed('axis axis--x', true)
.call(xAxis);
var gy = svg
.append("g")
.classed('axis axis--y', true)
.call(yAxis);
svg
.append("text")
.attr('transform', `rotate(-90 10 ${(height - margin.top) / 2})`)
.attr("x", 10)
.attr("y", height / 2)
.attr("fill", "currentColor")
.attr("text-anchor", "middle")
.text(key);
var g = svg.append('g').classed('canvas', true);
var lines = g
.append('g')
.classed('lines canvas__lines', true)
.selectAll('line')
.data(ids, d => d[0])
.join('line')
.attr('stroke-opacity', 0);
var circles = g
.append('g')
.classed('circles canvas__circles', true)
.selectAll('circle')
.data(ids, d => d[0])
.join('circle')
.attr('cx', d => x(getValue(d, 0, 'ADY')))
.attr('cy', d => y(getValue(d, 0, 'AVAL')))
.attr('r', 1)
.attr('fill', colorScaleLine(0))
.attr('fill-opacity', .25)
.attr('stroke', colorScaleLine(0))
.attr('stroke-opacity', .5)
.style('display', d => (getDatum(d, 0) ? null : 'none'));
var statLine = g
.selectAll('line.stat')
.data(d3.pairs(measureStatistics))
.join('line')
.classed('stat', true)
.attr('x1', (d, i) => x(getStatistic(ids, i, 'ADY', 'mean')))
.attr('x2', (d, i) => x(getStatistic(ids, i, 'ADY', 'mean')))
.attr('y1', d => y(d[0]))
.attr('y2', d => y(d[0]))
.attr('stroke', d => colorScaleLine(d[1] - d[0]))
.attr('stroke-width', 3);
var statMark = g
.append('circle')
.datum(measureStatistics)
.attr('cx', x(getStatistic(ids, 0, 'ADY', 'mean')))
.attr('cy', d => y(d[0]))
.attr('r', 4)
.attr('fill', colorScaleLine(0))
.attr('fill-opacity', 1)
.attr('stroke', 'black') //colorScaleLine(0))
.attr('stroke-opacity', 1);
charts.push({
measure,
ids,
median,
x,
y,
colorScaleLine,
svg,
circles,
lines,
statLine,
statMark,
visitText,
g
});
});
let visitIndex = 1;
d3.interval(() => {
// reset visit index
if (visitIndex >= visits.length) {
//setTimeout(() => {
visitIndex = 0;
container.selectAll('circle.clone').remove();
container
.selectAll('line.stat')
.transition()
.duration(1000)
.delay(500)
.attr('x2', function() {
return this.getAttribute('x1');
})
.attr('y2', function() {
return this.getAttribute('y1');
});
//}, 5000);
} // else {
var visit1 = visits[visitIndex - 1];
var visit2 = visits[visitIndex];
charts.forEach(chart => {
// subset on visit
var subset = chart.measure.filter(d => d.AVISIT === visit2);
// update visit text
chart.visitText
.transition()
.delay(500)
.text(`${visit2} (n=${subset.length})`);
// Lines should move from the previous visit to the next visit and be colored by the size of the change.
updateLines(chart, visit2);
// Circles should move from the previous visit to the next visit.
updateCircles(chart, visit2);
// TODO: draw each line from the previous visit to the current visit, i.e. the first element in the datum to the second element in the datum, from the median study day of the previous visit to the median study day of the current visit
chart.statLine
.filter((d, i) => i === visitIndex - 1)
.transition()
.duration(1000)
.delay(500)
.attr('x2', (d, i) =>
chart.x(getStatistic(chart.ids, visitIndex, 'ADY', 'mean'))
)
.attr('y2', d => chart.y(d[1]));
chart.statMark
.transition()
.ease(d3.easeQuad)
.duration(1000)
.delay(1000)
.attr('cx', chart.x(getStatistic(chart.ids, visitIndex, 'ADY', 'mean')))
.attr('cy', d => chart.y(d[visitIndex]))
.attr('fill', d => chart.colorScaleLine(d[visitIndex] - d[0]));
// .attr('stroke', d => chart.colorScaleLine(d[visitIndex] - d[0]));
chart.statMark.clone().classed('clone', true);
});
// increment visit
visitIndex++;
//}
}, 2500);
return container.node();
}