Removed the zooming property and added back the tooltip

This commit is contained in:
Luis Aleixo 2022-03-15 14:22:51 +00:00
parent 1c3d2ce38c
commit ebad26fa8b

View file

@ -1,402 +1,4 @@
/* Generate the concentration plot using d3 library. */
function draw_concentration_plot(svg_id, times, concentrations, cumulative_doses, exposed_presence_intervals, short_range_intervals) {
var time_format = d3.timeFormat('%H:%M');
var data_for_graphs = {
'concentrations': [],
'cumulative_doses': [],
}
times.map((time, index) => data_for_graphs.concentrations.push({ 'time': time, 'hour': new Date().setHours(Math.trunc(time), (time - Math.trunc(time)) * 60), 'concentration': concentrations[index]}));
times.map((time, index) => data_for_graphs.cumulative_doses.push({ 'time': time, 'hour': new Date().setHours(Math.trunc(time), (time - Math.trunc(time)) * 60), 'concentration': cumulative_doses[index]}));
// Add main SVG element
var plot_div = document.getElementById(svg_id);
var vis = d3.select(plot_div).append('svg');
// H:M time format for x axis.
xRange = d3.scaleTime().domain([data_for_graphs.concentrations[0].hour, data_for_graphs.concentrations[data_for_graphs.concentrations.length - 1].hour]),
xTimeRange = d3.scaleLinear().domain([data_for_graphs.concentrations[0].time, data_for_graphs.concentrations[data_for_graphs.concentrations.length - 1].time]),
bisecHour = d3.bisector((d) => { return d.hour; }).left,
yRange = d3.scaleLinear().domain([0., Math.max(...concentrations)]),
yCumulativeRange = d3.scaleLinear().domain([0., Math.max(...cumulative_doses)*1.1]),
xAxis = d3.axisBottom(xRange).tickFormat(d => time_format(d)),
yAxis = d3.axisLeft(yRange).ticks(4),
yCumulativeAxis = d3.axisRight(yCumulativeRange).ticks(4);
// Line representing the mean concentration.
var lineFunc = d3.line();
var draw_line = vis.append('svg:path')
.attr('stroke', '#1f77b4')
.attr('stroke-width', 2)
.attr('fill', 'none');
var lineCumulative = d3.line();
var draw_cumulative_line = vis.append('svg:path')
.attr('stroke', '#1f77b4')
.attr('stroke-width', 2)
.style("stroke-dasharray", "5 5")
.attr('fill', 'none');
// Area representing the presence of exposed person(s).
var exposedArea = {};
var drawArea = {};
exposed_presence_intervals.forEach((b, index) => {
exposedArea[index] = d3.area();
drawArea[index] = vis.append('svg:path')
.attr('fill', '#1f77b4')
.attr('fill-opacity', '0.1');
});
// Area representing the short range interaction(s).
var shortRangeArea = {};
var drawShortRangeArea = {};
short_range_intervals.forEach((b, index) => {
shortRangeArea[index] = d3.area();
drawShortRangeArea[index] = vis.append('svg:path')
.attr('fill', '#1f00b4')
.attr('fill-opacity', '0.1');
});
// X axis declaration.
var xAxisEl = vis.append('svg:g')
.attr('class', 'x axis');
// X axis label.
var xAxisLabelEl = vis.append('text')
.attr('class', 'x label')
.attr('fill', 'black')
.attr('text-anchor', 'middle')
.text('Time of day')
// Y axis declaration.
var yAxisEl = vis.append('svg:g')
.attr('class', 'y axis');
// Y axis label.
var yAxisLabelEl = vis.append('svg:text')
.attr('class', 'y label')
.attr('fill', 'black')
.attr('text-anchor', 'middle')
.text('Mean concentration (virions/m³)');
// Y cumulative concentration axis declaration.
var yAxisCumEl = vis.append('svg:g')
.attr('class', 'y axis')
.style('font-size', 14)
.style("stroke-dasharray", "5 5");
// Y cumulated concentration axis label.
var yAxisCumLabelEl = vis.append('svg:text')
.attr('class', 'y label')
.attr('fill', 'black')
.attr('text-anchor', 'middle')
.text('Mean cumulative dose (infectious virus)');
// Legend for the plot elements - line and area.
var legendLineIcon = vis.append('rect')
.attr('width', 20)
.attr('height', 3)
.style('fill', '#1f77b4');
var legendCumulativeIcon = vis.append('line')
.style("stroke-dasharray", "5 5") //dashed array for line
.attr('stroke-width', '2')
.style("stroke", '#1f77b4');
var legendAreaIcon = vis.append('rect')
.attr('width', 20)
.attr('height', 15)
.attr('fill', '#1f77b4')
.attr('fill-opacity', '0.1');
var legendShortRangeAreaIcon = vis.append('rect')
.attr('width', 20)
.attr('height', 15)
.attr('fill', '#1f00b4')
.attr('fill-opacity', '0.1');
var legendLineText = vis.append('text')
.text('Mean concentration')
.style('font-size', '15px')
.attr('alignment-baseline', 'central');
var legendCumutiveText = vis.append('text')
.text('Cumulative dose')
.style('font-size', '15px')
.attr('alignment-baseline', 'central');
var legendAreaText = vis.append('text')
.text('Presence of exposed person(s)')
.style('font-size', '15px')
.attr('alignment-baseline', 'central');
var legendShortRangeText = vis.append('text')
.text('Short range interaction(s)')
.style('font-size', '15px')
.attr('alignment-baseline', 'central');
// Legend bounding
var legendBBox = vis.append('rect')
.attr('width', 255)
.attr('height', 90)
.attr('stroke', 'lightgrey')
.attr('stroke-width', '2')
.attr('rx', '5px')
.attr('ry', '5px')
.attr('stroke-linejoin', 'round')
.attr('fill', 'none');
// Tooltip.
var focus = {}, tooltip_rect = {}, tooltip_time = {}, tooltip_concentration = {}, toolBox = {};
for (const [concentration, data] of Object.entries(data_for_graphs)) {
focus[concentration] = vis.append('svg:g')
.style('display', 'none');
focus[concentration].append('circle')
.attr('r', 3);
tooltip_rect[concentration] = focus[concentration].append('rect')
.attr('fill', 'white')
.attr('stroke', '#000')
.attr('width', 85)
.attr('height', 50)
.attr('x', 10)
.attr('y', -22)
.attr('rx', 4)
.attr('ry', 4);
tooltip_time[concentration] = focus[concentration].append('text')
.attr('id', 'tooltip-time')
.attr('x', 18)
.attr('y', -2);
tooltip_concentration[concentration] = focus[concentration].append('text')
.attr('id', 'tooltip-concentration')
.attr('x', 18)
.attr('y', 18);
toolBox[concentration] = vis.append('rect')
.attr('fill', 'none')
.attr('pointer-events', 'all')
.on('mouseover', () => { for (const [concentration, data] of Object.entries(focus)) focus[concentration].style('display', null); })
.on('mouseout', () => { for (const [concentration, data] of Object.entries(focus)) focus[concentration].style('display', 'none'); })
.on('mousemove', mousemove);
}
var graph_width;
var graph_height;
function redraw() {
// Define width and height according to the screen size.
var div_width = plot_div.clientWidth;
var div_height = plot_div.clientHeight;
graph_width = div_width;
graph_height = div_height
if (div_width >= 900) { // For screens with width > 900px legend can be on the graph's right side.
var margins = { top: 30, right: 20, bottom: 50, left: 60 };
div_width = 900;
graph_width = div_width * (2/3);
const svg_margins = {'margin-left': '0rem', 'margin-top': '0rem'};
Object.entries(svg_margins).forEach(([prop,val]) => vis.style(prop,val));
}
else {
var margins = { top: 30, right: 20, bottom: 50, left: 40 };
div_width = div_width * 1.1
graph_width = div_width * .9;
graph_height = div_height * 0.65; // On mobile screen sizes we want the legend to be on the bottom of the graph.
const svg_margins = {'margin-left': '-1rem', 'margin-top': '3rem'};
Object.entries(svg_margins).forEach(([prop,val]) => vis.style(prop,val));
};
// Use the extracted size to set the size of the SVG element.
vis.attr("width", div_width)
.attr('height', div_height);
// SVG components according to the width and height.
// Axis ranges.
xRange.range([margins.left, graph_width - margins.right]);
xTimeRange.range([margins.left, graph_width - margins.right]);
yRange.range([graph_height - margins.bottom, margins.top]);
yCumulativeRange.range([graph_height - margins.bottom, margins.top]);
// Line.
lineFunc.defined(d => !isNaN(d.concentration))
.x(d => xTimeRange(d.time))
.y(d => yRange(d.concentration));
draw_line.attr("d", lineFunc(data_for_graphs.concentrations));
// Cumulative line.
lineCumulative.defined(d => !isNaN(d.concentration))
.x(d => xTimeRange(d.time))
.y(d => yCumulativeRange(d.concentration));
draw_cumulative_line.attr("d", lineCumulative(data_for_graphs.cumulative_doses));
// Area.
exposed_presence_intervals.forEach((b, index) => {
exposedArea[index].x(d => xTimeRange(d.time))
.y0(graph_height - margins.bottom)
.y1(d => yRange(d.concentration));
drawArea[index].attr('d', exposedArea[index](data_for_graphs.concentrations.filter(d => {
return d.time >= b[0] && d.time <= b[1]
})));
});
// Short Range Area.
short_range_intervals.forEach((b, index) => {
shortRangeArea[index].x(d => xTimeRange(d.time))
.y0(graph_height - margins.bottom)
.y1(d => yRange(d.concentration));
drawShortRangeArea[index].attr('d', shortRangeArea[index](data_for_graphs.concentrations.filter(d => {
return d.time >= b[0] && d.time <= b[1]
})))
})
// Axis.
var xAxis = d3.axisBottom(xRange).tickFormat(d => time_format(d));
var yAxis = d3.axisLeft(yRange);
xAxisEl.attr('transform', 'translate(0,' + (graph_height - margins.bottom) + ')')
.call(xAxis);
xAxisLabelEl.attr('x', (graph_width + margins.right) / 2)
.attr('y', graph_height * 0.97);
yAxisEl.attr('transform', 'translate(' + margins.left + ',0)').call(yAxis);
yAxisLabelEl.attr('x', (graph_height * 0.9 + margins.bottom) / 2)
.attr('y', (graph_height + margins.left) * 0.9)
.attr('transform', 'rotate(-90, 0,' + graph_height + ')');
yAxisCumEl.attr('transform', 'translate(' + (graph_width - margins.right) + ',0)').call(yCumulativeAxis);
yAxisCumLabelEl.attr('transform', 'rotate(-90, 0,' + graph_height + ')')
.attr('x', (graph_height + margins.bottom) / 2);
if (plot_div.clientWidth >= 900) {
yAxisCumLabelEl.attr('transform', 'rotate(-90, 0,' + graph_height + ')')
.attr('x', (graph_height + margins.bottom) / 2)
.attr('y', 1.71 * graph_width);
}
else {
yAxisCumLabelEl.attr('transform', 'rotate(-90, 0,' + graph_height + ')')
.attr('x', (graph_height + margins.bottom * 0.55) / 2)
.attr('y', graph_width + 290);
}
// Legend on right side.
const size = 20;
if (plot_div.clientWidth >= 900) {
legendLineIcon.attr('x', graph_width + size * 2.5)
.attr('y', margins.top + size);
legendLineText.attr('x', graph_width + 4 * size)
.attr('y', margins.top + size);
legendCumulativeIcon.attr("x1", graph_width + size + 30)
.attr("x2", graph_width + 2 * size + 32)
.attr("y1", 3.5 * size)
.attr("y2", 3.5 * size);
legendCumutiveText.attr('x', graph_width + 2.5 * size + 30)
.attr('y', margins.top + 2 * size);
legendAreaIcon.attr('x', graph_width + size * 2.5)
.attr('y', margins.top + 2.6 * size);
legendAreaText.attr('x', graph_width + 4 * size)
.attr('y', margins.top + 3 * size);
legendShortRangeAreaIcon.attr('x', graph_width + size * 2.5)
.attr('y', margins.top + 3.6 * size);
legendShortRangeText.attr('x', graph_width + 4 * size)
.attr('y', margins.top + 4 * size);
legendBBox.attr('x', graph_width * 1.07)
.attr('y', margins.top * 1.2);
}
// Legend on the bottom.
else {
legendLineIcon.attr('x', size * 0.5)
.attr('y', graph_height * 1.05);
legendLineText.attr('x', 2 * size)
.attr('y', graph_height * 1.05);
legendCumulativeIcon.attr("x1", size * 0.5)
.attr("x2", size * 1.55)
.attr("y1", graph_height * 1.05 + size)
.attr("y2", graph_height * 1.05 + size);
legendCumutiveText.attr('x', 2 * size)
.attr('y', graph_height + 1.65 * size);
legendAreaIcon.attr('x', size * 0.50)
.attr('y', graph_height * 1.09 + size);
legendAreaText.attr('x', 2 * size)
.attr('y', graph_height + 2.6 * size);
legendShortRangeAreaIcon.attr('x', size * 0.50)
.attr('y', graph_height * 1.175 + size);
legendShortRangeText.attr('x', 2 * size)
.attr('y', graph_height + 3.65 * size);
legendBBox.attr('x', 1)
.attr('y', graph_height);
}
// ToolBox.
for (const [concentration, data] of Object.entries(data_for_graphs)) {
toolBox[concentration].attr('width', graph_width - margins.right)
.attr('height', graph_height);
}
}
// Draw for the first time to initialize.
redraw();
function mousemove() {
for (const [scenario, data] of Object.entries(data_for_graphs)) {
if (d3.pointer(event)[0] < graph_width / 2) {
tooltip_rect[scenario].attr('x', 10)
tooltip_time[scenario].attr('x', 18)
tooltip_concentration[scenario].attr('x', 18);
}
else {
tooltip_rect[scenario].attr('x', -90)
tooltip_time[scenario].attr('x', -82)
tooltip_concentration[scenario].attr('x', -82)
}
}
// Concentration line
var x0 = xRange.invert(d3.pointer(event, this)[0]),
i = bisecHour(data_for_graphs.concentrations, x0, 1),
d0 = data_for_graphs.concentrations[i - 1],
d1 = data_for_graphs.concentrations[i];
if (d1) {
var d = x0 - d0.hour > d1.hour - x0 ? d1 : d0;
focus.concentrations.attr('transform', 'translate(' + xRange(d.hour) + ',' + yRange(d.concentration) + ')');
focus.concentrations.select('#tooltip-time').text('x = ' + time_format(d.hour));
focus.concentrations.select('#tooltip-concentration').text('y = ' + d.concentration.toFixed(2));
}
// Cumulative line
var x0 = xRange.invert(d3.pointer(event, this)[0]),
i = bisecHour(data_for_graphs.cumulative_doses, x0, 1),
d0 = data_for_graphs.cumulative_doses[i - 1],
d1 = data_for_graphs.cumulative_doses[i];
if (d1 && d1.concentration) {
var d = x0 - d0.hour > d1.hour - x0 ? d1 : d0;
focus.cumulative_doses.attr('transform', 'translate(' + xRange(d.hour) + ',' + yCumulativeRange(d.concentration) + ')');
focus.cumulative_doses.select('#tooltip-time').text('x = ' + time_format(d.hour));
focus.cumulative_doses.select('#tooltip-concentration').text('y = ' + d.concentration.toFixed(2));
}
}
// Redraw based on the new size whenever the browser window is resized.
window.addEventListener("resize", redraw);
}
function draw_plot(svg_id, times, concentrations, short_range_concentrations, cumulative_doses) {
// Used for controlling the short range interactions
@ -406,11 +8,9 @@ function draw_plot(svg_id, times, concentrations, short_range_concentrations, cu
var data_for_graphs = {
'concentrations': [],
'short_range_concentrations': [],
'cumulative_doses': [],
}
times.map((time, index) => data_for_graphs.concentrations.push({ 'time': time, 'hour': new Date().setHours(Math.trunc(time), (time - Math.trunc(time)) * 60), 'concentration': concentrations[index]}));
times.map((time, index) => data_for_graphs.short_range_concentrations.push({ 'time': time, 'hour': new Date().setHours(Math.trunc(time), (time - Math.trunc(time)) * 60), 'concentration': short_range_concentrations[index]}));
times.map((time, index) => data_for_graphs.concentrations.push({ 'time': time, 'hour': new Date().setHours(Math.trunc(time), (time - Math.trunc(time)) * 60), 'concentration': short_range_concentrations[index]}));
times.map((time, index) => data_for_graphs.cumulative_doses.push({ 'time': time, 'hour': new Date().setHours(Math.trunc(time), (time - Math.trunc(time)) * 60), 'concentration': cumulative_doses[index]}));
// Add main SVG element
@ -526,10 +126,7 @@ function draw_plot(svg_id, times, concentrations, short_range_concentrations, cu
// Line representing the mean concentration.
var lineFunc = d3.line();
var draw_line = vis.append('g')
.attr('clip-path', 'url(#clip)');
draw_line.append('svg:path')
.attr('class', 'line')
var draw_line = vis.append('svg:path')
.attr('stroke', '#1f77b4')
.attr('stroke-width', 2)
.attr('fill', 'none');
@ -547,7 +144,7 @@ function draw_plot(svg_id, times, concentrations, short_range_concentrations, cu
var drawArea = {};
exposed_presence_intervals.forEach((b, index) => {
exposedArea[index] = d3.area();
drawArea[index] = draw_line.append('svg:path')
drawArea[index] = vis.append('svg:path')
.attr('fill', '#1f77b4')
.attr('fill-opacity', '0.1');
});
@ -557,18 +154,45 @@ function draw_plot(svg_id, times, concentrations, short_range_concentrations, cu
var drawShortRangeArea = {};
short_range_intervals.forEach((b, index) => {
shortRangeArea[index] = d3.area();
drawShortRangeArea[index] = draw_line.append('svg:path')
drawShortRangeArea[index] = vis.append('svg:path')
.attr('fill', '#1f00b4')
.attr('fill-opacity', '0.1');
});
var clip = vis.append("defs").append("svg:clipPath")
.attr("id", "clip")
.append("svg:rect")
.attr("x", 0)
.attr("y", 30);
// Tooltip.
var focus = {}, tooltip_rect = {}, tooltip_time = {}, tooltip_concentration = {}, toolBox = {};
for (const [concentration, data] of Object.entries(data_for_graphs)) {
var brush = d3.brushY();
focus[concentration] = vis.append('svg:g')
.style('display', 'none');
focus[concentration].append('circle')
.attr('r', 3);
tooltip_rect[concentration] = focus[concentration].append('rect')
.attr('fill', 'white')
.attr('stroke', '#000')
.attr('width', 85)
.attr('height', 50)
.attr('x', 10)
.attr('y', -22)
.attr('rx', 4)
.attr('ry', 4);
tooltip_time[concentration] = focus[concentration].append('text')
.attr('id', 'tooltip-time')
.attr('x', 18)
.attr('y', -2);
tooltip_concentration[concentration] = focus[concentration].append('text')
.attr('id', 'tooltip-concentration')
.attr('x', 18)
.attr('y', 18);
toolBox[concentration] = vis.append('rect')
.attr('fill', 'none')
.attr('pointer-events', 'all');
}
function update_concentration_plot(data) {
yRange.domain([0., Math.max(...data)]);
@ -577,12 +201,11 @@ function draw_plot(svg_id, times, concentrations, short_range_concentrations, cu
lineFunc.defined(d => !isNaN(d.concentration))
.x(d => xTimeRange(d.time))
.y(d => yRange(d.concentration));
draw_line.select('.line')
.enter()
.merge(draw_line.select('.line'))
draw_line.enter()
.merge(draw_line)
.transition()
.duration(1000)
.attr("d", lineFunc(data_for_graphs.short_range_concentrations));
.attr("d", lineFunc(data_for_graphs.concentrations));
// Area.
exposed_presence_intervals.forEach((b, index) => {
@ -590,7 +213,7 @@ function draw_plot(svg_id, times, concentrations, short_range_concentrations, cu
.y0(graph_height - 50)
.y1(d => yRange(d.concentration)
);
drawArea[index].transition().duration(1000).attr('d', exposedArea[index](data_for_graphs.short_range_concentrations.filter(d => {
drawArea[index].transition().duration(1000).attr('d', exposedArea[index](data_for_graphs.concentrations.filter(d => {
return d.time >= b[0] && d.time <= b[1]
})));
});
@ -601,67 +224,54 @@ function draw_plot(svg_id, times, concentrations, short_range_concentrations, cu
.y0(graph_height - 50)
.y1(d => yRange(d.concentration));
drawShortRangeArea[index].transition().duration(1000).attr('d', shortRangeArea[index](data_for_graphs.short_range_concentrations.filter(d => {
drawShortRangeArea[index].transition().duration(1000).attr('d', shortRangeArea[index](data_for_graphs.concentrations.filter(d => {
return d.time >= b[0] && d.time <= b[1]
})));
});
brush.on("end", updateChart); // Each time the brush selection changes, trigger the 'updateChart' function
// Brushing
draw_line.append("svg:g")
.attr("class", "brush")
.call(brush);
// Tooltip.
for (const [concentration, data] of Object.entries(data_for_graphs)) {
toolBox[concentration]
.on('mouseover', () => { for (const [concentration, data] of Object.entries(focus)) focus[concentration].style('display', null); })
.on('mouseout', () => { for (const [concentration, data] of Object.entries(focus)) focus[concentration].style('display', 'none'); })
.on('mousemove', mousemove);
}
// A function that set idleTimeOut to null
var idleTimeout
function idled() { idleTimeout = null; }
// A function that updates the chart for given boundaries
function updateChart(event,d) {
// What are the selected boundaries?
extent = event.selection
// If no selection, back to initial coordinate. Otherwise, update Y axis domain
if(!extent) {
if (!idleTimeout) return idleTimeout = setTimeout(idled, 350); // This allows to wait a little bit
}
else {
yRange.domain([ yRange.invert(extent[1]), yRange.invert(extent[0]) ])
draw_line.select(".brush").call(brush.move, null) // This remove the grey brush area as soon as the selection has been done
function mousemove() {
for (const [scenario, data] of Object.entries(data_for_graphs)) {
if (d3.pointer(event)[0] < graph_width / 2) {
tooltip_rect[scenario].attr('x', 10)
tooltip_time[scenario].attr('x', 18)
tooltip_concentration[scenario].attr('x', 18);
}
else {
tooltip_rect[scenario].attr('x', -90)
tooltip_time[scenario].attr('x', -82)
tooltip_concentration[scenario].attr('x', -82)
}
}
// Concentration line
var x0 = xRange.invert(d3.pointer(event, this)[0]),
i = bisecHour(data_for_graphs.concentrations, x0, 1),
d0 = data_for_graphs.concentrations[i - 1],
d1 = data_for_graphs.concentrations[i];
if (d1) {
var d = x0 - d0.hour > d1.hour - x0 ? d1 : d0;
focus.concentrations.attr('transform', 'translate(' + xRange(d.hour) + ',' + yRange(d.concentration) + ')');
focus.concentrations.select('#tooltip-time').text('x = ' + time_format(d.hour));
focus.concentrations.select('#tooltip-concentration').text('y = ' + d.concentration.toFixed(2));
}
// Cumulative line
var x0 = xRange.invert(d3.pointer(event, this)[0]),
i = bisecHour(data_for_graphs.cumulative_doses, x0, 1),
d0 = data_for_graphs.cumulative_doses[i - 1],
d1 = data_for_graphs.cumulative_doses[i];
if (d1 && d1.concentration) {
var d = x0 - d0.hour > d1.hour - x0 ? d1 : d0;
focus.cumulative_doses.attr('transform', 'translate(' + xRange(d.hour) + ',' + yCumulativeRange(d.concentration) + ')');
focus.cumulative_doses.select('#tooltip-time').text('x = ' + time_format(d.hour));
focus.cumulative_doses.select('#tooltip-concentration').text('y = ' + d.concentration.toFixed(2));
}
// Update axis and line position
yAxisEl.transition().duration(1000).call(d3.axisLeft(yRange))
lineFunc.defined(d => !isNaN(d.concentration))
.x(d => xTimeRange(d.time))
.y(d => yRange(d.concentration));
draw_line
.select('.line')
.transition()
.duration(1000)
.attr("d", lineFunc(data_for_graphs.short_range_concentrations));
// Area.
exposed_presence_intervals.forEach((b, index) => {
exposedArea[index].x(d => xTimeRange(d.time))
.y0(graph_height - 50)
.y1(d => yRange(d.concentration));
drawArea[index].transition().duration(1000).attr('d', exposedArea[index](data_for_graphs.short_range_concentrations.filter(d => {
return d.time >= b[0] && d.time <= b[1]
})));
});
// Short Range Area.
short_range_intervals.forEach((b, index) => {
shortRangeArea[index].x(d => xTimeRange(d.time))
.y0(graph_height - 50)
.y1(d => yRange(d.concentration));
drawShortRangeArea[index].transition().duration(1000).attr('d', shortRangeArea[index](data_for_graphs.short_range_concentrations.filter(d => {
return d.time >= b[0] && d.time <= b[1]
})));
});
}
}
@ -694,13 +304,6 @@ function draw_plot(svg_id, times, concentrations, short_range_concentrations, cu
// SVG components according to the width and height.
// clipPath: everything out of this area won't be drawn.
clip.attr("width", graph_width - margins.right)
.attr("height", graph_height - margins.top - margins.bottom);
// Add brushing
brush.extent([[margins.left, margins.top],[graph_width - margins.right, graph_height - margins.bottom ]]);
// Axis ranges.
xRange.range([margins.left, graph_width - margins.right]);
xTimeRange.range([margins.left, graph_width - margins.right]);
@ -796,6 +399,12 @@ function draw_plot(svg_id, times, concentrations, short_range_concentrations, cu
.attr('y', graph_height);
}
// ToolBox.
for (const [concentration, data] of Object.entries(data_for_graphs)) {
toolBox[concentration].attr('width', graph_width - margins.right)
.attr('height', graph_height);
}
// Cumulative line.
lineCumulative.defined(d => !isNaN(d.concentration))
.x(d => xTimeRange(d.time))
@ -818,41 +427,6 @@ function draw_plot(svg_id, times, concentrations, short_range_concentrations, cu
});
}
// If user double click, reinitialize the chart
vis.on("dblclick",function(){
if (button_full_exposure || button_long_exposure) {
button_full_exposure.disabled = true;
button_long_exposure.disabled = false;
}
yRange.domain([0., Math.max(...short_range_concentrations)])
yAxisEl.transition().call(d3.axisLeft(yRange))
lineFunc.defined(d => !isNaN(d.concentration))
.x(d => xTimeRange(d.time))
.y(d => yRange(d.concentration));
draw_line
.select('.line')
.transition()
.attr("d", lineFunc(data_for_graphs.short_range_concentrations));
exposed_presence_intervals.forEach((b, index) => {
exposedArea[index].x(d => xTimeRange(d.time))
.y0(graph_height - 50)
.y1(d => yRange(d.concentration)
);
drawArea[index].transition().duration(1000).attr('d', exposedArea[index](data_for_graphs.short_range_concentrations.filter(d => {
return d.time >= b[0] && d.time <= b[1]
})));
});
short_range_intervals.forEach((b, index) => {
shortRangeArea[index].x(d => xTimeRange(d.time))
.y0(graph_height - 50)
.y1(d => yRange(d.concentration));
drawShortRangeArea[index].transition().duration(1000).attr('d', shortRangeArea[index](data_for_graphs.short_range_concentrations.filter(d => {
return d.time >= b[0] && d.time <= b[1]
})));
});
});
// Draw for the first time to initialize.
redraw();
update_concentration_plot(short_range_concentrations);