diff --git a/src/renderers/MeasurementRenderer.js b/src/renderers/MeasurementRenderer.js index 8befef26c7920c077bf80f8fac575b7471bb1d7e..97430b66e1d39f5399bf108ff1e5a6a05b330d5b 100644 --- a/src/renderers/MeasurementRenderer.js +++ b/src/renderers/MeasurementRenderer.js @@ -38,7 +38,6 @@ export function renderMeasurementResults(measurementResults, language) { } let results = measurementResults['dcc:measurementResult']; - console.debug('Raw dcc:measurementResult:', results); if (!results) { console.error("Missing 'dcc:measurementResult' in measurementResults"); return; @@ -46,10 +45,7 @@ export function renderMeasurementResults(measurementResults, language) { if (!Array.isArray(results)) { results = [results]; } - console.debug('Processed measurement result array:', results); - const measurementResult = results[0]; - console.debug('Using measurementResult:', measurementResult); let resultObj = measurementResult['dcc:results'] && measurementResult['dcc:results']['dcc:result']; if (!resultObj) { @@ -59,7 +55,6 @@ export function renderMeasurementResults(measurementResults, language) { if (Array.isArray(resultObj)) { resultObj = resultObj[0]; } - console.debug('Using result object:', resultObj); let resultName = 'Measurement Result'; if (measurementResult['dcc:name'] && measurementResult['dcc:name']['dcc:content']) { @@ -71,7 +66,6 @@ export function renderMeasurementResults(measurementResults, language) { resultName = content._ || content; } } - console.debug('Result name:', resultName); const tabTitle = document.createElement('h2'); tabTitle.textContent = resultName; container.appendChild(tabTitle); @@ -81,7 +75,6 @@ export function renderMeasurementResults(measurementResults, language) { return; } const listData = resultObj['dcc:data']['dcc:list']; - console.debug('List data:', listData); let quantityJSONs = []; if (listData['dcc:quantity']) { @@ -100,7 +93,6 @@ export function renderMeasurementResults(measurementResults, language) { } }); } - console.debug('Combined quantity JSONs:', quantityJSONs); const indexQuantities = []; const dataQuantities = []; @@ -138,9 +130,37 @@ export function renderMeasurementResults(measurementResults, language) { extraInfo.push({ uncertainty, comment, conformity }); } }); - console.debug('Index Quantities (objects):', indexQuantities); - console.debug('Data Quantities (objects):', dataQuantities); - console.debug('Extra info for data quantities:', extraInfo); + + // Build headers for data quantities + const dataHeaders = dataQuantities.map(q => { + let header = q.getName(language); + let unit = q.getUnit(); + if (!header.toLowerCase().includes(" in ")) { + header = header + " in " + unit; + } + return header; + }); + + // Create log scaling toggles + const scalingContainer = document.createElement('div'); + scalingContainer.innerHTML = '<strong>Scaling:</strong> '; + const logXToggle = document.createElement('input'); + logXToggle.type = 'checkbox'; + logXToggle.id = 'logXToggle'; + const logXLabel = document.createElement('label'); + logXLabel.htmlFor = 'logXToggle'; + logXLabel.textContent = 'Log X'; + scalingContainer.appendChild(logXToggle); + scalingContainer.appendChild(logXLabel); + const logYToggle = document.createElement('input'); + logYToggle.type = 'checkbox'; + logYToggle.id = 'logYToggle'; + const logYLabel = document.createElement('label'); + logYLabel.htmlFor = 'logYToggle'; + logYLabel.textContent = 'Log Y'; + scalingContainer.appendChild(logYToggle); + scalingContainer.appendChild(logYLabel); + container.appendChild(scalingContainer); const xAxisContainer = document.createElement('div'); xAxisContainer.innerHTML = '<strong>Select X-Axis:</strong> '; @@ -195,28 +215,19 @@ export function renderMeasurementResults(measurementResults, language) { headers.push(xHeader); const dataValues = []; - const commentsArray = []; - const conformityArray = []; const uncertaintiesArray = []; + const conformityArray = []; dataQuantities.forEach((q, idx) => { - let header = q.getName(language); - let unit = q.getUnit(); - if (!header.toLowerCase().includes(" in ")) { - header = header + " in " + unit; - } - headers.push(header); - headers.push('Comments'); - headers.push(extraInfo[idx] && extraInfo[idx].conformity ? 'Conformity' : ''); - let values = q.getValues(); - if (values.length === 1 && xValues.length > 1) { - values = new Array(xValues.length).fill(values[0]); + headers.push(dataHeaders[idx]); + if (extraInfo[idx] && extraInfo[idx].conformity) { + headers.push('Conformity'); } - dataValues.push(values); - uncertaintiesArray.push(extraInfo[idx] ? extraInfo[idx].uncertainty : null); - commentsArray.push(extraInfo[idx] ? extraInfo[idx].comment : ''); + dataValues.push(q.getValues()); + uncertaintiesArray.push(extraInfo[idx] ? extraInfo[idx].uncertainty : []); conformityArray.push(extraInfo[idx] ? extraInfo[idx].conformity : []); }); - console.debug('Table data arrays:', { dataValues, commentsArray, conformityArray, uncertaintiesArray }); + headers.push('Comments'); + const tableData = [headers]; for (let i = 0; i < xValues.length; i++) { const row = []; @@ -227,12 +238,16 @@ export function renderMeasurementResults(measurementResults, language) { cellValue = cellValue + ' ± ' + uncertaintiesArray[idx][i]; } row.push(cellValue); - row.push(commentsArray[idx] || ''); - row.push(conformityArray[idx] && conformityArray[idx][i] ? conformityArray[idx][i] : ''); + if (extraInfo[idx] && extraInfo[idx].conformity && extraInfo[idx].conformity[i]) { + row.push(extraInfo[idx].conformity[i]); + } else if (extraInfo[idx] && extraInfo[idx].conformity) { + row.push(''); + } }); + let globalComment = extraInfo.map(e => e.comment).filter(c => c).join(' ; '); + row.push(globalComment); tableData.push(row); } - console.debug('Final table data:', tableData); renderTable(tableData); const unitGroups = {}; @@ -241,64 +256,86 @@ export function renderMeasurementResults(measurementResults, language) { if (!unitGroups[unit]) { unitGroups[unit] = []; } - let header = headers[idx * 3 + 1]; + const header = dataHeaders[idx]; let values = q.getValues(); - if (values.length === 1 && xValues.length > 1) { - values = new Array(xValues.length).fill(values[0]); - } const uncertainty = uncertaintiesArray[idx]; const conformity = conformityArray[idx]; - const traceColor = palette[idx % palette.length]; - unitGroups[unit].push({ name: header, y: values, uncertainty, conformity, color: traceColor, index: idx }); + const defaultColor = palette[idx % palette.length]; + const markerColorArray = values.map((val, i) => { + if (conformity && conformity[i] && conformity[i].toLowerCase().includes('fail')) { + return '#d62728'; + } + return defaultColor; + }); + unitGroups[unit].push({ name: header, y: values, uncertainty, conformity, defaultColor, markerColor: markerColorArray, index: idx }); }); - console.debug('Unit groups for plots:', unitGroups); subplotsContainer.innerHTML = ''; const unitKeys = Object.keys(unitGroups); + const plotDivs = []; unitKeys.forEach((unit, groupIdx) => { const group = unitGroups[unit]; const graphDiv = document.createElement('div'); graphDiv.style.width = '100%'; graphDiv.style.height = '300px'; subplotsContainer.appendChild(graphDiv); - + plotDivs.push(graphDiv); const groupTraces = group.map(trace => { - const hovertemplate = 'X: %{x} ' + xUnit + ' | ' + trace.name + ': %{y} ± %{customdata_unc} ' + unit + ' | Conformity: %{customdata}<extra></extra>'; + let tooltip = 'X: %{x} ' + xUnit + ' | ' + trace.name + ': %{y}'; + if (trace.conformity && trace.conformity.length > 0) { + tooltip += ' | Conformity: %{customdata}'; + } + tooltip += '<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 }, + error_y: { + type: 'data', + array: (trace.uncertainty && trace.uncertainty.length === xValues.length) + ? trace.uncertainty + : new Array(xValues.length).fill(0), + visible: true, + color: trace.defaultColor + }, type: 'scatter', mode: 'lines+markers', name: trace.name, - marker: { color: trace.color }, - hovertemplate: hovertemplate, - customdata: (trace.conformity && trace.conformity.length === xValues.length) ? trace.conformity : new Array(xValues.length).fill(''), - customdata_unc: (trace.uncertainty && trace.uncertainty.length === xValues.length) ? trace.uncertainty : new Array(xValues.length).fill('0') + marker: { color: trace.markerColor }, + line: { color: trace.defaultColor }, + hovertemplate: tooltip, + customdata: (trace.conformity && trace.conformity.length === xValues.length) + ? trace.conformity + : new Array(xValues.length).fill('') }; }); - let xaxisTitle = ''; if (groupIdx === unitKeys.length - 1) { let xLabel = 'X: ' + xQuantity.getName(language); - xaxisTitle = xLabel + ' in ' + xUnit; + xaxisTitle = '<b>' + xLabel + ' in ' + xUnit + '</b>'; } + const logX = document.getElementById('logXToggle').checked; + const logY = document.getElementById('logYToggle').checked; const layout = { - xaxis: { title: { text: xaxisTitle, font: { size: 18, family: 'Arial', color: 'black' } }, tickfont: { family: 'Arial', size: 14, color: 'black' } }, - yaxis: { title: { text: unit, font: { size: 18, family: 'Arial', color: 'black' } }, tickfont: { family: 'Arial', size: 14, color: 'black' } }, + xaxis: { + title: { text: xaxisTitle, font: { size: 36, family: 'Arial', color: 'black' } }, + tickfont: { family: 'Arial', size: 14, color: 'black' }, + type: logX ? 'log' : 'linear' + }, + yaxis: { + title: { text: unit, font: { size: 18, family: 'Arial', color: 'black' } }, + tickfont: { family: 'Arial', size: 14, color: 'black' }, + type: logY ? 'log' : 'linear' + }, hovermode: 'closest', margin: { t: 20, b: 40 } }; - Plotly.newPlot(graphDiv, groupTraces, layout).then(() => { - console.debug('Plot rendered for unit:', unit); const caption = document.createElement('div'); - caption.innerHTML = '<b>' + group[0].name +'</b>'; + caption.innerHTML = '<b>' + group[0].name + '</b>'; caption.style.textAlign = 'center'; caption.style.marginBottom = '5px'; graphDiv.parentNode.insertBefore(caption, graphDiv); }); - graphDiv.on('plotly_hover', function(data) { if (data.points && data.points.length > 0) { const pointIndex = data.points[0].pointIndex + 1; @@ -308,6 +345,13 @@ export function renderMeasurementResults(measurementResults, language) { graphDiv.on('plotly_unhover', function() { clearTableRowHighlights(); }); + graphDiv.on('plotly_relayout', function(eventData) { + plotDivs.forEach(div => { + if (div !== graphDiv && eventData['xaxis.range[0]'] && eventData['xaxis.range[1]']) { + Plotly.relayout(div, { 'xaxis.range': [eventData['xaxis.range[0]'], eventData['xaxis.range[1]']] }); + } + }); + }); }); } @@ -319,18 +363,17 @@ export function renderMeasurementResults(measurementResults, language) { const tr = document.createElement('tr'); rowData.forEach((cellData, cellIndex) => { const cell = document.createElement(rowIndex === 0 ? 'th' : 'td'); - // For headers, allow HTML rendering (dsiUnits output) by setting innerHTML. if (rowIndex === 0) { cell.innerHTML = cellData; + if (cellData !== 'Comments' && cellIndex > 0) { + const qtyIndex = Math.floor((cellIndex - 1) / 2); + cell.style.backgroundColor = lightPalette[qtyIndex % lightPalette.length]; + } } else { 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); - cell.style.backgroundColor = lightPalette[dataIndex % lightPalette.length]; - } tr.appendChild(cell); }); tr.addEventListener('mouseover', () => { tr.style.backgroundColor = '#eef'; }); @@ -357,6 +400,8 @@ export function renderMeasurementResults(measurementResults, language) { radios.forEach(radio => { radio.addEventListener('change', updateVisualization); }); + document.getElementById('logXToggle').addEventListener('change', updateVisualization); + document.getElementById('logYToggle').addEventListener('change', updateVisualization); toleranceToggle.addEventListener('change', () => { console.log('Tolerance toggle:', toleranceToggle.checked); });