Added logic for zooming in the main chart and change between different perspectives
This commit is contained in:
parent
ee7bdd560f
commit
31bb648d1c
5 changed files with 464 additions and 35 deletions
|
|
@ -102,11 +102,15 @@ def calculate_report_data(model: models.ExposureModel):
|
|||
for interval in model.short_range.presence:
|
||||
short_range_intervals.append(list(interval.boundaries()))
|
||||
|
||||
concentrations = [
|
||||
short_range_concentrations = [
|
||||
np.array(model.concentration(float(time))).mean()
|
||||
for time in times
|
||||
]
|
||||
highest_const = max(concentrations)
|
||||
concentrations = [
|
||||
np.array(model.concentration_model.concentration(float(time))).mean()
|
||||
for time in times
|
||||
]
|
||||
highest_const = max(short_range_concentrations)
|
||||
prob = np.array(model.infection_probability()).mean()
|
||||
er = np.array(model.concentration_model.infected.emission_rate_when_present()).mean()
|
||||
exposed_occupants = model.exposed.number
|
||||
|
|
@ -121,6 +125,7 @@ def calculate_report_data(model: models.ExposureModel):
|
|||
"short_range_intervals": short_range_intervals,
|
||||
"exposed_presence_intervals": [list(interval) for interval in model.exposed.presence.boundaries()],
|
||||
"cumulative_doses": list(cumulative_doses),
|
||||
"short_range_concentrations": short_range_concentrations,
|
||||
"concentrations": concentrations,
|
||||
"highest_const": highest_const,
|
||||
"prob_inf": prob,
|
||||
|
|
|
|||
|
|
@ -522,8 +522,9 @@ function validateLunchTime(obj) {
|
|||
}
|
||||
|
||||
function overlapped_times(obj, start_time, finish_time) {
|
||||
removeErrorFor($(".short_range_option"));
|
||||
$(".short_range_option").removeClass("red_border");
|
||||
removeErrorFor($(obj));
|
||||
$(obj).removeClass("red_border");
|
||||
|
||||
|
||||
let simulation_start = parseTimeToMins($("#exposed_start").val())
|
||||
let simulation_finish = parseTimeToMins($("#exposed_finish").val())
|
||||
|
|
@ -856,7 +857,7 @@ $(document).ready(function () {
|
|||
});
|
||||
|
||||
// Validate row button (Save button)
|
||||
$("body").on("click", ".validate_node_btn_frm_field", function(e) {
|
||||
$("body").on("click", ".validate_node_btn_frm_field", function() {
|
||||
let index = $(this).attr('id').split('_').slice(-1)[0];
|
||||
let activity = validate_sr_parameter('#sr_activity_no_' + String(index)[0], "You must specify the activity type.");
|
||||
let start = validate_sr_parameter('#sr_start_no_' + String(index)[0], "You must specify the start time.");
|
||||
|
|
@ -889,33 +890,14 @@ $(document).ready(function () {
|
|||
$(this).closest(".form_field_outer_row").remove();
|
||||
});
|
||||
|
||||
//Short range modal - close button
|
||||
//Short range modal - close and save button
|
||||
$("body").on("click", ".close_btn_frm_field", function() {
|
||||
// var last_element = $(".form_field_outer").find(".form_field_outer_row").last().find(".short_range_option").prop("id");
|
||||
// if (!last_element) {
|
||||
// $('#short_range_dialog').modal('hide');
|
||||
// $("input[type=radio][id=short_range_no]").prop("checked", true);
|
||||
// on_short_range_option_change();
|
||||
// }
|
||||
// else {
|
||||
// let index = last_element.split("_").slice(-1)[0];
|
||||
// let activity = validate_sr_parameter('#sr_activity_no_' + String(index)[0], "You must specify the activity type.");
|
||||
// let start = validate_sr_parameter('#sr_start_no_' + String(index)[0], "You must specify the start time.");
|
||||
// let duration = validate_sr_parameter('#sr_duration_no_' + String(index)[0], "You must specify the duration.");
|
||||
// if (activity && start && duration) {
|
||||
// document.getElementById('sr_activity_no_' + String(index)).disabled = true;
|
||||
// document.getElementById('sr_start_no_' + String(index)).disabled = true;
|
||||
// document.getElementById('sr_duration_no_' + String(index)).disabled = true;
|
||||
// document.getElementById('edit_row_no_' + String(index)).style.cssText = 'display:inline !important';
|
||||
// document.getElementById('validate_row_no_' + String(index)).style.cssText = 'display: none !important';
|
||||
// $("#sr_interactions").text($(".form_field_outer").find(".form_field_outer_row").length);
|
||||
// $('#short_range_dialog').modal('hide');
|
||||
// }
|
||||
// }
|
||||
$("#sr_interactions").text($(".form_field_outer").find(".form_field_outer_row.row_validated").length);
|
||||
$(".form_field_outer_row").not(".row_validated").remove();
|
||||
$('#short_range_dialog').modal('hide');
|
||||
|
||||
$(".validate_node_btn_frm_field").click();
|
||||
if ($(".form_field_outer").find(".form_field_outer_row.row_validated").length == $(".form_field_outer").find(".form_field_outer_row").length) {
|
||||
$("#sr_interactions").text($(".form_field_outer").find(".form_field_outer_row.row_validated").length);
|
||||
$(".form_field_outer_row").not(".row_validated").remove();
|
||||
$('#short_range_dialog').modal('hide');
|
||||
}
|
||||
});
|
||||
|
||||
//Short range modal - reset button
|
||||
|
|
|
|||
|
|
@ -397,6 +397,445 @@ function draw_concentration_plot(svg_id, times, concentrations, cumulative_doses
|
|||
|
||||
}
|
||||
|
||||
function draw_plot(svg_id, times, concentrations, short_range_concentrations, cumulative_doses) {
|
||||
|
||||
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.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');
|
||||
|
||||
var time_format = d3.timeFormat('%H:%M');
|
||||
// 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(),
|
||||
yCumulativeRange = d3.scaleLinear().domain([0., Math.max(...cumulative_doses)*1.1]),
|
||||
|
||||
yAxis = d3.axisLeft();
|
||||
yCumulativeAxis = d3.axisRight(yCumulativeRange).ticks(4);
|
||||
|
||||
// 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');
|
||||
|
||||
// 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')
|
||||
.attr('stroke', '#1f77b4')
|
||||
.attr('stroke-width', 2)
|
||||
.attr('fill', 'none');
|
||||
|
||||
// Line representing the cumulative concentration.
|
||||
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] = draw_line.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] = draw_line.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);
|
||||
|
||||
var brush = d3.brushY();
|
||||
|
||||
function update_concentration_plot(data, data_for_graphs) {
|
||||
yRange.domain([0., Math.max(...data)]);
|
||||
yAxisEl.transition().duration(1000).call(yAxis);
|
||||
|
||||
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'))
|
||||
.transition()
|
||||
.duration(1000)
|
||||
.attr("d", lineFunc(data_for_graphs));
|
||||
|
||||
// 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.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.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);
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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));
|
||||
|
||||
// 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.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.filter(d => {
|
||||
return d.time >= b[0] && d.time <= b[1]
|
||||
})));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
// 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]);
|
||||
yRange.range([graph_height - margins.bottom, margins.top]);
|
||||
yCumulativeRange.range([graph_height - margins.bottom, margins.top]);
|
||||
|
||||
// Axis.
|
||||
var xAxis = d3.axisBottom(xRange).tickFormat(d => time_format(d));
|
||||
yAxis.scale(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)');
|
||||
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);
|
||||
}
|
||||
|
||||
// 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));
|
||||
}
|
||||
|
||||
document.getElementById("button_full_exposure").addEventListener("click", () => {
|
||||
update_concentration_plot(short_range_concentrations, data_for_graphs.short_range_concentrations);
|
||||
});
|
||||
document.getElementById("button_long_exposure").addEventListener("click", () => {
|
||||
update_concentration_plot(concentrations, data_for_graphs.concentrations);
|
||||
});
|
||||
|
||||
// If user double click, reinitialize the chart
|
||||
vis.on("dblclick",function(){
|
||||
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, data_for_graphs.short_range_concentrations);
|
||||
|
||||
// Redraw based on the new size whenever the browser window is resized.
|
||||
window.addEventListener("resize", redraw);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Generate the alternative scenarios plot using d3 library.
|
||||
// 'alternative_scenarios' is a dictionary with all the alternative scenarios
|
||||
// 'times' is a list of times for all the scenarios
|
||||
|
|
|
|||
|
|
@ -377,7 +377,7 @@
|
|||
|
||||
<div id="DIVsr_interactions" class="none">
|
||||
<div class="d-flex">
|
||||
<button type="button" class="btn btn-primary btn-sm" data-toggle="modal" data-target="#short_range_dialog">Set interactions</button>
|
||||
<button type="button" class="btn btn-primary btn-sm" data-toggle="modal" data-target="#short_range_dialog" data-keyboard="false" data-backdrop="static">Set interactions</button>
|
||||
<p class="align-self-center pl-4"><b id="sr_interactions">0</b> short range interactions.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -399,7 +399,7 @@
|
|||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary dismiss_btn_frm_field" data-dismiss="modal">Reset</button>
|
||||
<button type="button" class="btn btn-primary close_btn_frm_field">Close</button>
|
||||
<button type="button" class="btn btn-primary close_btn_frm_field">Save all</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -89,15 +89,18 @@
|
|||
{% endblock report_summary_footnote %}
|
||||
</div>
|
||||
<p id="section1">* The results are based on the parameters and assumptions published in the CERN Open Report <a href="https://cds.cern.ch/record/2756083"> CERN-OPEN-2021-004</a>.</p>
|
||||
|
||||
<button class="btn btn-sm btn-primary" id="button_full_exposure">Full exposure</button>
|
||||
<button class="btn btn-sm btn-primary" id="button_long_exposure">Background concentration</button>
|
||||
|
||||
<div id="concentration_plot" style="height: 400px"></div>
|
||||
<script type="application/javascript">
|
||||
var times = {{ times | JSONify }}
|
||||
var short_range_concentrations = {{ short_range_concentrations | JSONify }}
|
||||
var concentrations = {{ concentrations | JSONify }}
|
||||
var cumulative_doses = {{ cumulative_doses | JSONify }}
|
||||
var exposed_presence_intervals = {{ exposed_presence_intervals | JSONify }}
|
||||
var short_range_intervals = {{ short_range_intervals | JSONify }}
|
||||
draw_concentration_plot("concentration_plot", times, concentrations, cumulative_doses, exposed_presence_intervals, short_range_intervals);
|
||||
draw_plot("concentration_plot", times, concentrations, short_range_concentrations, cumulative_doses)
|
||||
</script>
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in a new issue