Skip to content
Snippets Groups Projects
Commit 4403465f authored by Benedikt's avatar Benedikt
Browse files

added color coding

parent 53d7f02a
No related branches found
No related tags found
No related merge requests found
import Plotly from 'plotly.js-dist';
// Define Tab10 palette and lighter shades for table backgrounds
const palette = [
'#1f77b4',
'#ff7f0e',
'#2ca02c',
'#d62728',
'#9467bd',
'#8c564b',
'#e377c2',
'#7f7f7f',
'#bcbd22',
'#17becf'
];
const lightPalette = [
'#c6e2ff',
'#ffddbf',
'#c9e6c8',
'#f4c2c2',
'#dcd0ff',
'#e0cda9',
'#ffccff',
'#d3d3d3',
'#e6e6a9',
'#b3ffff'
];
export function renderMeasurementResults(measurementResults, language) {
console.debug('renderMeasurementResults called with:', measurementResults);
const container = document.getElementById('measurementResults');
......@@ -23,7 +50,6 @@ export function renderMeasurementResults(measurementResults, language) {
}
console.debug('Processed measurement result array:', results);
// Use the first measurementResult object
const measurementResult = results[0];
console.debug('Using measurementResult:', measurementResult);
......@@ -38,7 +64,7 @@ export function renderMeasurementResults(measurementResults, language) {
}
console.debug('Using result object:', resultObj);
// Get the measurement result name from dcc:name (from measurementResult)
// Get measurement result name without language tag
let resultName = 'Measurement Result';
if (measurementResult['dcc:name'] && measurementResult['dcc:name']['dcc:content']) {
let content = measurementResult['dcc:name']['dcc:content'];
......@@ -52,10 +78,9 @@ export function renderMeasurementResults(measurementResults, language) {
console.debug('Result name:', resultName);
const tabTitle = document.createElement('h2');
tabTitle.textContent = resultName + ' (' + language + ')';
tabTitle.textContent = resultName;
container.appendChild(tabTitle);
// Now extract dcc:data -> dcc:list from the resultObj
if (!resultObj['dcc:data'] || !resultObj['dcc:data']['dcc:list']) {
console.error("Missing 'dcc:data' or 'dcc:list' in result object:", resultObj);
return;
......@@ -84,20 +109,64 @@ export function renderMeasurementResults(measurementResults, language) {
}
console.debug('Combined quantities:', quantities);
// Separate index quantities and data quantities
// Separate index quantities and data quantities; also extract extra info (uncertainty, comment, conformity)
const indexQuantities = [];
const dataQuantities = [];
const extraInfo = [];
quantities.forEach(q => {
if (q.$ && q.$.refType && q.$.refType.match(/basic_tableIndex/)) {
indexQuantities.push(q);
} else {
dataQuantities.push(q);
let uncertainty = null;
if (q['si:measurementUncertaintyUnivariateXMLList'] &&
q['si:measurementUncertaintyUnivariateXMLList']['si:expandedMUXMLList'] &&
q['si:measurementUncertaintyUnivariateXMLList']['si:expandedMUXMLList']['si:valueExpandedMUXMLList']) {
const errStr = q['si:measurementUncertaintyUnivariateXMLList']['si:expandedMUXMLList']['si:valueExpandedMUXMLList'];
uncertainty = errStr.trim().split(/\s+/).map(v => parseFloat(v));
}
let comment = '';
let conformity = '';
if (q['dcc:measurementMetaData'] && q['dcc:measurementMetaData']['dcc:metaData']) {
let md = q['dcc:measurementMetaData']['dcc:metaData'];
if (Array.isArray(md)) {
md.forEach(item => {
if (item.$ && item.$.refType && item.$.refType.includes('basic_tableRowComment')) {
if (item['dcc:description'] && item['dcc:description']['dcc:content']) {
comment = Array.isArray(item['dcc:description']['dcc:content']) ?
(item['dcc:description']['dcc:content'][0]._ || item['dcc:description']['dcc:content'][0]) :
(item['dcc:description']['dcc:content']._ || item['dcc:description']['dcc:content']);
}
}
if (item.$ && item.$.refType && item.$.refType.includes('basic_conformity')) {
if (item['dcc:conformityXMLList']) {
conformity = item['dcc:conformityXMLList'].trim();
}
}
});
} else {
if (md.$ && md.$.refType && md.$.refType.includes('basic_tableRowComment')) {
if (md['dcc:description'] && md['dcc:description']['dcc:content']) {
comment = Array.isArray(md['dcc:description']['dcc:content']) ?
(md['dcc:description']['dcc:content'][0]._ || md['dcc:description']['dcc:content'][0]) :
(md['dcc:description']['dcc:content']._ || md['dcc:description']['dcc:content']);
}
}
if (md.$ && md.$.refType && md.$.refType.includes('basic_conformity')) {
if (md['dcc:conformityXMLList']) {
conformity = md['dcc:conformityXMLList'].trim();
}
}
}
}
extraInfo.push({ uncertainty, comment, conformity });
}
});
console.debug('Index Quantities:', indexQuantities);
console.debug('Data Quantities:', dataQuantities);
console.debug('Extra info for data quantities:', extraInfo);
// Create radio buttons for X-axis selection (from indexQuantities)
// Create radio buttons for X-axis selection
const xAxisContainer = document.createElement('div');
xAxisContainer.innerHTML = '<strong>Select X-Axis:</strong> ';
indexQuantities.forEach((q, idx) => {
......@@ -136,15 +205,17 @@ export function renderMeasurementResults(measurementResults, language) {
tolContainer.appendChild(tolLabel);
container.appendChild(tolContainer);
// Create containers for plots and table
const plotsContainer = document.createElement('div');
plotsContainer.id = 'plotsContainer';
container.appendChild(plotsContainer);
// Create container for subplots
const subplotsContainer = document.createElement('div');
subplotsContainer.id = 'subplotsContainer';
container.appendChild(subplotsContainer);
// Create container for table
const tableContainer = document.createElement('div');
tableContainer.id = 'tableContainer';
container.appendChild(tableContainer);
// Function to update the visualization
// Function to update visualization
function updateVisualization() {
const selectedRadio = document.querySelector('input[name="xAxisSelect"]:checked');
if (!selectedRadio) {
......@@ -154,28 +225,30 @@ export function renderMeasurementResults(measurementResults, language) {
const selectedIndex = selectedRadio.value;
const xQuantity = indexQuantities[selectedIndex];
let xValues = [];
if (xQuantity && xQuantity['si:realListXMLList'] && xQuantity['si:realListXMLList']['si:valueXMLList']) {
xValues = xQuantity['si:realListXMLList']['si:valueXMLList'].trim().split(/\s+/).map(v => parseFloat(v));
let xUnit = '';
if (xQuantity && xQuantity['si:realListXMLList']) {
if (xQuantity['si:realListXMLList']['si:valueXMLList']) {
xValues = xQuantity['si:realListXMLList']['si:valueXMLList'].trim().split(/\s+/).map(v => parseFloat(v));
}
if (xQuantity['si:realListXMLList']['si:unitXMLList']) {
xUnit = xQuantity['si:realListXMLList']['si:unitXMLList'].trim();
}
}
console.debug('Selected X-Axis values:', xValues);
console.debug('X-Axis unit:', xUnit);
// Build table headers and values
// Build table headers and arrays for values, comments, conformity, uncertainties
const headers = [];
let xHeader = 'X-Axis';
if (xQuantity['dcc:name'] && xQuantity['dcc:name']['dcc:content']) {
let content = xQuantity['dcc:name']['dcc:content'];
if (Array.isArray(content)) {
const match = content.find(item => item.$ && item.$.lang === language) || content[0];
xHeader = match._ || match;
} else {
xHeader = content._ || content;
}
}
let xHeader = 'X-Axis (' + xUnit + ')';
headers.push(xHeader);
const dataValues = [];
dataQuantities.forEach(q => {
const commentsArray = [];
const conformityArray = [];
const uncertaintiesArray = [];
dataQuantities.forEach((q, idx) => {
let header = 'Data';
let unit = '';
if (q['dcc:name'] && q['dcc:name']['dcc:content']) {
let content = q['dcc:name']['dcc:content'];
if (Array.isArray(content)) {
......@@ -185,7 +258,13 @@ export function renderMeasurementResults(measurementResults, language) {
header = content._ || content;
}
}
headers.push(header);
if (q['si:realListXMLList'] && q['si:realListXMLList']['si:unitXMLList']) {
unit = q['si:realListXMLList']['si:unitXMLList'].trim();
}
headers.push(header + ' (' + unit + ')');
headers.push('Comments');
headers.push('Conformity');
let values = [];
if (q['si:realListXMLList'] && q['si:realListXMLList']['si:valueXMLList']) {
values = q['si:realListXMLList']['si:valueXMLList'].trim().split(/\s+/).map(v => parseFloat(v));
......@@ -194,22 +273,38 @@ export function renderMeasurementResults(measurementResults, language) {
values = new Array(xValues.length).fill(values[0]);
}
dataValues.push(values);
let uncertainty = null;
if (extraInfo[idx] && extraInfo[idx].uncertainty) {
uncertainty = extraInfo[idx].uncertainty;
}
uncertaintiesArray.push(uncertainty);
let comment = extraInfo[idx] ? extraInfo[idx].comment : '';
let conformity = extraInfo[idx] ? extraInfo[idx].conformity : '';
commentsArray.push(comment);
conformityArray.push(conformity);
});
// Build table rows
const tableData = [headers];
for (let i = 0; i < xValues.length; i++) {
const row = [];
row.push(xValues[i]);
dataValues.forEach(values => {
row.push(values[i] !== undefined ? values[i] : '');
dataValues.forEach((values, idx) => {
let cellValue = values[i] !== undefined ? values[i] : '';
if (uncertaintiesArray[idx] && uncertaintiesArray[idx][i] !== undefined) {
cellValue = cellValue + ' ± ' + uncertaintiesArray[idx][i];
}
row.push(cellValue);
row.push(commentsArray[idx] || '');
row.push(conformityArray[idx] || '');
});
tableData.push(row);
}
console.debug('Table data:', tableData);
renderTable(tableData);
// Group data quantities by unit for plotting
// Group data quantities by unit for plotting and assign colors
const unitGroups = {};
dataQuantities.forEach((q, idx) => {
let unit = '';
......@@ -219,7 +314,7 @@ export function renderMeasurementResults(measurementResults, language) {
if (!unitGroups[unit]) {
unitGroups[unit] = [];
}
let header = headers[idx + 1];
let header = headers[idx * 3 + 1];
let values = [];
if (q['si:realListXMLList'] && q['si:realListXMLList']['si:valueXMLList']) {
values = q['si:realListXMLList']['si:valueXMLList'].trim().split(/\s+/).map(v => parseFloat(v));
......@@ -227,41 +322,66 @@ export function renderMeasurementResults(measurementResults, language) {
if (values.length === 1 && xValues.length > 1) {
values = new Array(xValues.length).fill(values[0]);
}
unitGroups[unit].push({ name: header, y: values });
let uncertainty = uncertaintiesArray[idx];
let conformity = conformityArray[idx];
// Assign color: use palette; override if conformity indicates pass/fail
let traceColor = palette[idx % palette.length];
if (conformity.toLowerCase().includes('pass')) {
traceColor = '#2ca02c';
} else if (conformity.toLowerCase().includes('fail')) {
traceColor = '#d62728';
}
unitGroups[unit].push({ name: header, y: values, uncertainty: uncertainty, conformity: conformity, color: traceColor });
});
console.debug('Unit groups for plots:', unitGroups);
// Clear and render plots
// Build a single Plotly figure with subplots (here, one plot per unit group)
const plotsContainer = document.getElementById('subplotsContainer');
plotsContainer.innerHTML = '';
Object.keys(unitGroups).forEach(unit => {
Object.keys(unitGroups).forEach((unit, idx) => {
const group = unitGroups[unit];
const graphDiv = document.createElement('div');
graphDiv.style.width = '100%';
graphDiv.style.height = '300px';
plotsContainer.appendChild(graphDiv);
const traces = unitGroups[unit].map(trace => {
// Build traces for this unit group
const groupTraces = group.map(trace => {
const hovertemplate = 'X-Axis: %{x} ' + xUnit + ' | ' + trace.name + ': %{y} ± %{error_y.array} (' + unit + ') | Conformity: <b style="color:' + (trace.conformity.toLowerCase().includes('pass') ? '#2ca02c' : (trace.conformity.toLowerCase().includes('fail') ? '#d62728' : '#000')) + '">' + trace.conformity + '</b><extra></extra>';
return {
x: xValues,
y: trace.y,
error_y: { type: 'data', array: trace.uncertainty || new Array(xValues.length).fill(0), visible: true, color: trace.color },
type: 'scatter',
mode: 'lines+markers',
name: trace.name
name: trace.name,
marker: { color: trace.color },
hovertemplate: hovertemplate,
customdata: trace.conformity
};
});
const layout = {
title: 'Plot (' + unit + ')',
xaxis: { title: xHeader },
yaxis: { title: unit },
hovermode: 'closest'
xaxis: { title: { text: xHeader, font: { size: 16, weight: 'bold' } } },
yaxis: { title: { text: unit, font: { size: 16, weight: 'bold' } } },
hovermode: 'closest',
margin: { t: 20, b: 40 }
};
Plotly.newPlot(graphDiv, traces, layout).then(() => {
Plotly.newPlot(graphDiv, groupTraces, layout).then(() => {
console.debug('Plot rendered for unit:', unit);
// Add caption below the plot: italic text "QuantityName in Unit"
const caption = document.createElement('div');
// Use the name of the first trace as QuantityName
caption.innerHTML = '<i>' + group[0].name + ' in ' + unit + '</i>';
caption.style.textAlign = 'center';
caption.style.fontStyle = 'italic';
graphDiv.parentNode.insertBefore(caption, graphDiv.nextSibling);
});
// Coupled mouseover events for table row highlighting
graphDiv.on('plotly_hover', function(data) {
if (data.points && data.points.length > 0) {
const pointIndex = data.points[0].pointIndex + 1; // offset for header row
const pointIndex = data.points[0].pointIndex + 1;
highlightTableRow(pointIndex);
}
});
......@@ -271,17 +391,23 @@ export function renderMeasurementResults(measurementResults, language) {
});
}
// Render table from a 2D array
// Render table from a 2D array with colored backgrounds for data columns
function renderTable(tableData) {
const tableContainer = document.getElementById('tableContainer');
tableContainer.innerHTML = '';
const table = document.createElement('table');
tableData.forEach((rowData, rowIndex) => {
const tr = document.createElement('tr');
rowData.forEach(cellData => {
rowData.forEach((cellData, cellIndex) => {
const cell = document.createElement(rowIndex === 0 ? 'th' : 'td');
cell.textContent = cellData;
cell.style.padding = '4px';
cell.style.border = '1px solid #ccc';
if (rowIndex === 0 && cellIndex > 0) {
const dataIndex = Math.floor((cellIndex - 1) / 3);
const color = palette[dataIndex % palette.length];
cell.style.backgroundColor = lightPalette[dataIndex % lightPalette.length];
}
tr.appendChild(cell);
});
tr.addEventListener('mouseover', () => { tr.style.backgroundColor = '#eef'; });
......@@ -291,28 +417,25 @@ export function renderMeasurementResults(measurementResults, language) {
tableContainer.appendChild(table);
}
// Functions for coupled table row highlights
function highlightTableRow(rowIndex) {
const rows = tableContainer.querySelectorAll('tr');
const rows = document.getElementById('tableContainer').querySelectorAll('tr');
if (rows[rowIndex]) {
rows[rowIndex].style.backgroundColor = '#fee';
}
}
function clearTableRowHighlights() {
const rows = tableContainer.querySelectorAll('tr');
const rows = document.getElementById('tableContainer').querySelectorAll('tr');
rows.forEach(row => row.style.backgroundColor = '');
}
// Initial update
updateVisualization();
// Update visualization when X-axis selection changes
const radios = document.querySelectorAll('input[name="xAxisSelect"]');
radios.forEach(radio => {
radio.addEventListener('change', updateVisualization);
});
// Tolerance toggle event (placeholder)
toleranceToggle.addEventListener('change', () => {
console.log('Tolerance toggle:', toleranceToggle.checked);
// Future: update plot/table for tolerance markings and color coding
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment