chart = {
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height])
.attr("font-family", "sans-serif")
.attr("font-size", 10)
.attr("stroke-miterlimit", 1);
const gradient = DOM.uid();
svg.append("linearGradient")
.attr("id", gradient.id)
.attr("gradientUnits", "userSpaceOnUse")
.attr("x1", 0)
.attr("y1", 0)
.attr("x2", 0)
.attr("y2", height)
.selectAll("stop")
.data([
{offset: 0, color: negativeColor},
{offset: y(threshold) / height, color: negativeColor},
{offset: y(threshold) / height, color: positiveColor},
{offset: 1, color: positiveColor}
])
.join("stop")
.attr("offset", d => d.offset)
.attr("stop-color", d => d.color);
svg.append("g")
.call(xAxis)
.selectAll("text")
.attr("color", "gray")
.attr("font-size", 12);
svg.append("g")
.call(yAxis)
.selectAll("text")
.attr("color", "gray")
.attr("font-size", 18);
svg.append("g")
.call(grid);
svg.append("path")
.attr("fill", gradient)
.attr("fill-opacity", 0.1) // opacity of confidence level (CL)
.attr("d", area(data));
svg.append("path")
.attr("fill", "none")
.attr("stroke", gradient)
.attr("stroke-width", 1.5)
.attr("d", line(data.slice(1)));
// svg.append("line")
// .attr("id", "followline")
// .attr("x1", x(Date.parse("2020-01-20"))) //Date.parse("2020-01-20")
// .attr("x2", x(Date.parse("2020-01-20")))
// .attr("y1", 0)
// .attr("y2", height - margin.top)
// .attr("stroke-dasharray", "3,3")
// .style("stroke", "#ccc")
// .style("stroke-width", 1.5)
// const circles = svg.append("g")
// .attr("stroke", gradient)
// .attr("stroke-opacity", 1)
// .selectAll("circle")
// .data(data.slice(1))
// .join("circle")
// .attr("cx", d => x(d.date))
// .attr("cy", d => y(d.ml))
// .attr("fill", gradient)
// .attr("fill-opacity", 0.1)
// .attr("r", 5)
if (!mutable lookup) mutable lookup = new Date(data[0].date);
const bar = svg
.append("line") // vertical moving line
.attr("style", "stroke-width:1")
.attr('stroke', 'blue')
// .attr("style", 'gray')
.attr("y2", height*0.94)
.attr("x1", x(mutable lookup))
.attr("x2", x(mutable lookup));
const legendData = svg
.append("text")
.style("font", "22px sans-serif")
.attr("x", 10)
.attr("y", 100);
const legendR = svg
.append("text")
.style("font", "22px sans-serif")
.attr("x", 10)
.attr("y", 125);
const marker = svg
.append("circle")
.attr("r", 3)
.attr("cx", -100)
.attr("fill", "black")
.attr("fill-opacity", 0)//circle hidden
const formatTime = d3.utcFormat("%d/%m/%Y");
function update(date) {
const i = bisect.right(data, date);
if (i < data.length && i > 0) {
legendData.text(`${formatTime(data[i].date)}`);
legendR.text(`R: ${data[i].ml}`);
marker.attr("cx", x(data[i].date)).attr("cy", y(data[i].ml));
bar.attr("stroke", (data[i].ml > 1) ? negativeColor: positiveColor);
legendData.attr("x", (i > data.length/2)? x(data[i].date)-117 : x(data[i].date))
legendR.attr("x", (i > data.length/2)? x(data[i].date)-117 : x(data[i].date))
}
mutable lookup = new Date(date);
bar.attr("x1", x(mutable lookup)).attr("x2", x(mutable lookup));
//https://observablehq.com/@d3/index-chart --> line with range
}
svg.on("mousemove click touchmove", function() {
const m = d3.mouse(this);
update(x.invert(m[0]));
});
update(mutable lookup);
return svg.node();
}