From 53d7f02a1f24990bd9ad02dc7c6180a9c693afe7 Mon Sep 17 00:00:00 2001
From: Benedikt Seeger <benedikt.seeger@ptb.de>
Date: Mon, 24 Feb 2025 17:43:33 +0100
Subject: [PATCH] aded first viewer

---
 .idea/.gitignore                             |   8 +
 .idea/dccviewertypescript.iml                |  12 +
 .idea/inspectionProfiles/Project_Default.xml |  15 +
 .idea/modules.xml                            |   8 +
 .idea/vcs.xml                                |   6 +
 dcc-viewer/.idea/workspace.xml               |  91 +++++
 project_structure.json                       |  10 +-
 src/app.js                                   |  17 +-
 src/main.js                                  |   2 +-
 src/renderers/AdminRenderer.js               |   4 -
 src/renderers/MeasurementRenderer.js         | 343 ++++++++++++++++---
 src/renderers/SelectRenderer.js              |  16 +-
 src/styles.css                               |  16 +-
 vite.config.js                               |   2 -
 14 files changed, 468 insertions(+), 82 deletions(-)
 create mode 100644 .idea/.gitignore
 create mode 100644 .idea/dccviewertypescript.iml
 create mode 100644 .idea/inspectionProfiles/Project_Default.xml
 create mode 100644 .idea/modules.xml
 create mode 100644 .idea/vcs.xml
 create mode 100644 dcc-viewer/.idea/workspace.xml

diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..13566b8
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/dccviewertypescript.iml b/.idea/dccviewertypescript.iml
new file mode 100644
index 0000000..24643cc
--- /dev/null
+++ b/.idea/dccviewertypescript.iml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="WEB_MODULE" version="4">
+  <component name="NewModuleRootManager">
+    <content url="file://$MODULE_DIR$">
+      <excludeFolder url="file://$MODULE_DIR$/.tmp" />
+      <excludeFolder url="file://$MODULE_DIR$/temp" />
+      <excludeFolder url="file://$MODULE_DIR$/tmp" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..798a81f
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,15 @@
+<component name="InspectionProjectProfileManager">
+  <profile version="1.0">
+    <option name="myName" value="Project Default" />
+    <inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
+    <inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
+      <option name="ignoredPackages">
+        <value>
+          <list size="1">
+            <item index="0" class="java.lang.String" itemvalue="bokeh" />
+          </list>
+        </value>
+      </option>
+    </inspection_tool>
+  </profile>
+</component>
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..53f01cf
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/.idea/dccviewertypescript.iml" filepath="$PROJECT_DIR$/.idea/dccviewertypescript.iml" />
+    </modules>
+  </component>
+</project>
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="" vcs="Git" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/dcc-viewer/.idea/workspace.xml b/dcc-viewer/.idea/workspace.xml
new file mode 100644
index 0000000..cf5d6f0
--- /dev/null
+++ b/dcc-viewer/.idea/workspace.xml
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="AutoImportSettings">
+    <option name="autoReloadType" value="SELECTIVE" />
+  </component>
+  <component name="ChangeListManager">
+    <list default="true" id="c3d759c9-e9f8-41d4-b9b5-ef12db68e70c" name="Changes" comment="">
+      <change beforePath="$PROJECT_DIR$/../src/app.js" beforeDir="false" afterPath="$PROJECT_DIR$/../src/app.js" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/../src/main.js" beforeDir="false" afterPath="$PROJECT_DIR$/../src/main.js" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/../src/renderers/AdminRenderer.js" beforeDir="false" afterPath="$PROJECT_DIR$/../src/renderers/AdminRenderer.js" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/../src/renderers/MeasurementRenderer.js" beforeDir="false" afterPath="$PROJECT_DIR$/../src/renderers/MeasurementRenderer.js" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/../src/renderers/SelectRenderer.js" beforeDir="false" afterPath="$PROJECT_DIR$/../src/renderers/SelectRenderer.js" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/../src/styles.css" beforeDir="false" afterPath="$PROJECT_DIR$/../src/styles.css" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/../vite.config.js" beforeDir="false" afterPath="$PROJECT_DIR$/../vite.config.js" afterDir="false" />
+    </list>
+    <option name="SHOW_DIALOG" value="false" />
+    <option name="HIGHLIGHT_CONFLICTS" value="true" />
+    <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
+    <option name="LAST_RESOLUTION" value="IGNORE" />
+  </component>
+  <component name="FileTemplateManagerImpl">
+    <option name="RECENT_TEMPLATES">
+      <list>
+        <option value="JavaScript File" />
+      </list>
+    </option>
+  </component>
+  <component name="Git.Settings">
+    <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$/.." />
+  </component>
+  <component name="ProjectColorInfo">{
+  &quot;associatedIndex&quot;: 6
+}</component>
+  <component name="ProjectId" id="2tLWdqlIz5X4SCSwK5EjWvfiZtG" />
+  <component name="ProjectViewState">
+    <option name="hideEmptyMiddlePackages" value="true" />
+    <option name="showLibraryContents" value="true" />
+  </component>
+  <component name="PropertiesComponent"><![CDATA[{
+  "keyToString": {
+    "JavaScript Debug.Debug Vite.executor": "Run",
+    "JavaScript Debug.dccViewer.executor": "Debug",
+    "JavaScript Debug.index.html.executor": "Run",
+    "Node.js.vite.config.js.executor": "Run",
+    "RunOnceActivity.ShowReadmeOnStart": "true",
+    "RunOnceActivity.git.unshallow": "true",
+    "git-widget-placeholder": "main",
+    "last_opened_file_path": "/home/seeger01/repos/dccviewertypescript",
+    "node.js.detected.package.eslint": "true",
+    "node.js.detected.package.tslint": "true",
+    "node.js.selected.package.eslint": "(autodetect)",
+    "node.js.selected.package.tslint": "(autodetect)",
+    "nodejs_package_manager_path": "npm",
+    "settings.editor.selected.configurable": "http.proxy",
+    "ts.external.directory.path": "/home/seeger01/Downloads/WebStorm-243.24978.60/plugins/javascript-plugin/jsLanguageServicesImpl/external",
+    "vue.rearranger.settings.migration": "true"
+  }
+}]]></component>
+  <component name="RunManager">
+    <configuration name="dccViewer" type="JavascriptDebugType" uri="http://localhost:5173/">
+      <method v="2" />
+    </configuration>
+  </component>
+  <component name="SharedIndexes">
+    <attachedChunks>
+      <set>
+        <option value="bundled-js-predefined-d6986cc7102b-76f8388c3a79-JavaScript-WS-243.24978.60" />
+      </set>
+    </attachedChunks>
+  </component>
+  <component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
+  <component name="TaskManager">
+    <task active="true" id="Default" summary="Default task">
+      <changelist id="c3d759c9-e9f8-41d4-b9b5-ef12db68e70c" name="Changes" comment="" />
+      <created>1740131925655</created>
+      <option name="number" value="Default" />
+      <option name="presentableId" value="Default" />
+      <updated>1740131925655</updated>
+      <workItem from="1740131927264" duration="102000" />
+      <workItem from="1740132052064" duration="10000" />
+      <workItem from="1740132146315" duration="24000" />
+      <workItem from="1740132175271" duration="89000" />
+      <workItem from="1740158794192" duration="1155000" />
+      <workItem from="1740380748268" duration="5753000" />
+    </task>
+    <servers />
+  </component>
+  <component name="TypeScriptGeneratedFilesManager">
+    <option name="version" value="3" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/project_structure.json b/project_structure.json
index 746ffdf..55cbd1e 100644
--- a/project_structure.json
+++ b/project_structure.json
@@ -1,15 +1,7 @@
 {
-  "package.json": "{\n  \"name\": \"dcc-viewer\",\n  \"version\": \"1.0.0\",\n  \"description\": \"Digital Calibration Certificate Viewer\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"vite build\",\n    \"preview\": \"vite preview\"\n  },\n  \"dependencies\": {\n    \"xml2js\": \"^0.4.23\",\n    \"jsoneditor\": \"^9.5.6\",\n    \"plotly.js-dist\": \"^2.18.2\",\n    \"events\": \"^3.3.0\"\n  },\n  \"devDependencies\": {\n    \"vite\": \"^4.0.0\"\n  }\n}\n",
-  "vite.config.js": "import { defineConfig } from 'vite';\n\nexport default defineConfig({\n  resolve: {\n    alias: {\n      events: 'events'\n    }\n  },\n  optimizeDeps: {\n    esbuildOptions: {\n      define: { global: 'globalThis' }\n    }\n  }\n});\n",
-  "index.html": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"UTF-8\">\n  <title>DCC Viewer</title>\n  <link rel=\"stylesheet\" href=\"/src/styles.css\">\n</head>\n<body>\n  <div id=\"app\"></div>\n  <script type=\"module\" src=\"/src/main.js\"></script>\n</body>\n</html>\n",
   "src": {
-    "main.js": "import { initApp } from './app.js';\n\n// Initialize after DOM loads\ndocument.addEventListener('DOMContentLoaded', () => {\n  initApp();\n});\n",
-    "app.js": "import { parseString } from 'xml2js';\n\n// Import our interactive measurement renderer\nimport { renderMeasurementResults } from './renderers/MeasurementRenderer.js';\nimport { renderAdminData } from './renderers/AdminRenderer.js';\n\n// Global configuration\nconst selectableLanguages = ['en', 'de', 'fr', 'es'];\nlet selectedLanguage = 'en';\nlet dccData = null;\n\nexport function initApp() {\n  const appContainer = document.getElementById('app');\n\n  // Create language selection dropdown\n  const langSelect = document.createElement('select');\n  selectableLanguages.forEach(lang => {\n    const option = document.createElement('option');\n    option.value = lang;\n    option.textContent = lang;\n    if (lang === selectedLanguage) option.selected = true;\n    langSelect.appendChild(option);\n  });\n  langSelect.addEventListener('change', (e) => {\n    selectedLanguage = e.target.value;\n    renderAll();\n  });\n  appContainer.appendChild(langSelect);\n\n  // Create containers for admin data and measurement results\n  const adminContainer = document.createElement('div');\n  adminContainer.id = 'adminData';\n  appContainer.appendChild(adminContainer);\n\n  const measContainer = document.createElement('div');\n  measContainer.id = 'measurementResults';\n  appContainer.appendChild(measContainer);\n\n  // Load the XML file from the data folder\n  fetch('/data/sin_acceleration_example_dcc_WithExampleConformatyStatment.xml')\n    .then(response => response.text())\n    .then(xmlText => {\n      parseString(xmlText, { explicitArray: false }, (err, result) => {\n        if (err) {\n          console.error('Error parsing XML:', err);\n          return;\n        }\n        dccData = result;\n        renderAll();\n      });\n    })\n    .catch(err => console.error('Error loading XML file:', err));\n}\n\nfunction renderAll() {\n  // Clear containers\n  document.getElementById('adminData').innerHTML = '';\n  document.getElementById('measurementResults').innerHTML = '';\n\n  if (dccData && dccData['dcc:digitalCalibrationCertificate']) {\n    const cert = dccData['dcc:digitalCalibrationCertificate'];\n    if (cert['dcc:administrativeData']) {\n      renderAdminData(cert['dcc:administrativeData'], selectedLanguage);\n    }\n    if (cert['dcc:measurementResults']) {\n      renderMeasurementResults(cert['dcc:measurementResults'], selectedLanguage);\n    }\n  }\n}\n",
-    "styles.css": "body {\n  font-family: Arial, sans-serif;\n  margin: 20px;\n}\n\nh2, h3 {\n  color: #333;\n}\n\n#adminData, #measurementResults {\n  margin-top: 20px;\n  border: 1px solid #ccc;\n  padding: 10px;\n}\n\ntable {\n  width: 100%;\n  border-collapse: collapse;\n}\n\ntable, th, td {\n  border: 1px solid #ccc;\n}\n\nth, td {\n  padding: 4px;\n  text-align: center;\n}\n\n.plot-and-table, .plot-table {\n  margin: 10px 0;\n  padding: 10px;\n  border: 1px dashed #999;\n}\n",
     "renderers": {
-      "AdminRenderer.js": "import JSONEditor from 'jsoneditor';\nimport 'jsoneditor/dist/jsoneditor.css';\n\nexport function renderAdminData(adminData, language) {\n  const container = document.getElementById('adminData');\n  const title = document.createElement('h2');\n  title.textContent = 'Administrative Data (' + language + ')';\n  container.appendChild(title);\n\n  const options = {\n    mode: 'view',\n    mainMenuBar: false,\n    navigationBar: false,\n    statusBar: false\n  };\n  const editor = new JSONEditor(container, options);\n  editor.set(adminData);\n}\n",
-      "SelectRenderer.js": "export function selectRenderer(json, language) {\n  // This function is not used in the interactive measurement renderer\n  const container = document.createElement('div');\n  container.textContent = 'Interactive Measurement Renderer in use.';\n  return container;\n}\n",
-      "MeasurementRenderer.js": "import Plotly from 'plotly.js-dist';\n\nexport function renderMeasurementResults(measurementResults, language) {\n  const container = document.getElementById('measurementResults');\n  container.innerHTML = '';\n\n  // Use the first measurementResult for this example\n  let result = measurementResults['dcc:measurementResult'];\n  if (!Array.isArray(result)) {\n    result = [result];\n  }\n  result = result[0];\n\n  // Use dcc:name content for the tab title\n  let resultName = 'Measurement Result';\n  if (result['dcc:name'] && result['dcc:name']['dcc:content']) {\n    let content = result['dcc:name']['dcc:content'];\n    if (Array.isArray(content)) {\n      const match = content.find(item => item.$ && item.$.lang === language) || content[0];\n      resultName = match._ || match;\n    } else {\n      resultName = content._ || content;\n    }\n  }\n  const tabTitle = document.createElement('h2');\n  tabTitle.textContent = resultName + ' (' + language + ')';\n  container.appendChild(tabTitle);\n\n  // Process the measurement data from dcc:data -> dcc:list\n  if (!result['dcc:data'] || !result['dcc:data']['dcc:list']) return;\n  const listData = result['dcc:data']['dcc:list'];\n\n  // Flatten all quantities from the list\n  let quantities = [];\n  if (listData['dcc:quantity']) {\n    quantities = Array.isArray(listData['dcc:quantity']) ? listData['dcc:quantity'] : [listData['dcc:quantity']];\n  }\n  // Also add quantities from measurementMetaData\n  if (listData['dcc:measurementMetaData'] && listData['dcc:measurementMetaData']['dcc:metaData']) {\n    let metaData = listData['dcc:measurementMetaData']['dcc:metaData'];\n    if (!Array.isArray(metaData)) metaData = [metaData];\n    metaData.forEach(md => {\n      if (md['dcc:data'] && md['dcc:data']['dcc:quantity']) {\n        let qs = md['dcc:data']['dcc:quantity'];\n        if (!Array.isArray(qs)) qs = [qs];\n        quantities = quantities.concat(qs);\n      }\n    });\n  }\n\n  // Separate index quantities and data quantities\n  const indexQuantities = [];\n  const dataQuantities = [];\n  quantities.forEach(q => {\n    if (q.$ && q.$.refType && q.$.refType.match(/basic_tableIndex/)) {\n      indexQuantities.push(q);\n    } else {\n      dataQuantities.push(q);\n    }\n  });\n\n  // Create radio buttons for X-axis selection (only from indexQuantities)\n  const xAxisContainer = document.createElement('div');\n  xAxisContainer.innerHTML = '<strong>Select X-Axis:</strong> ';\n  indexQuantities.forEach((q, idx) => {\n    let nameStr = 'Index ' + idx;\n    if (q['dcc:name'] && q['dcc:name']['dcc:content']) {\n      let content = q['dcc:name']['dcc:content'];\n      if (Array.isArray(content)) {\n        const match = content.find(item => item.$ && item.$.lang === language) || content[0];\n        nameStr = match._ || match;\n      } else {\n        nameStr = content._ || content;\n      }\n    }\n    const radio = document.createElement('input');\n    radio.type = 'radio';\n    radio.name = 'xAxisSelect';\n    radio.value = idx;\n    if (idx === 0) radio.checked = true;\n    const label = document.createElement('label');\n    label.textContent = nameStr;\n    label.style.marginRight = '10px';\n    xAxisContainer.appendChild(radio);\n    xAxisContainer.appendChild(label);\n  });\n  container.appendChild(xAxisContainer);\n\n  // Optional tolerance toggle (placeholder for future implementation)\n  const toleranceToggle = document.createElement('input');\n  toleranceToggle.type = 'checkbox';\n  toleranceToggle.id = 'toleranceToggle';\n  const tolLabel = document.createElement('label');\n  tolLabel.htmlFor = 'toleranceToggle';\n  tolLabel.textContent = ' Enable tolerance markings';\n  const tolContainer = document.createElement('div');\n  tolContainer.appendChild(toleranceToggle);\n  tolContainer.appendChild(tolLabel);\n  container.appendChild(tolContainer);\n\n  // Create containers for plots and table\n  const plotsContainer = document.createElement('div');\n  plotsContainer.id = 'plotsContainer';\n  container.appendChild(plotsContainer);\n  const tableContainer = document.createElement('div');\n  tableContainer.id = 'tableContainer';\n  container.appendChild(tableContainer);\n\n  // Function to update the visualization\n  function updateVisualization() {\n    // Determine selected X-axis (default index0)\n    const selectedIndex = document.querySelector('input[name=\"xAxisSelect\"]:checked').value;\n    const xQuantity = indexQuantities[selectedIndex];\n    let xValues = [];\n    if (xQuantity && xQuantity['si:realListXMLList'] && xQuantity['si:realListXMLList']['si:valueXMLList']) {\n      xValues = xQuantity['si:realListXMLList']['si:valueXMLList'].trim().split(/\\s+/).map(v => parseFloat(v));\n    }\n\n    // Build table data\n    const headers = [];\n    // Header for X-axis\n    let xHeader = 'X-Axis';\n    if (xQuantity['dcc:name'] && xQuantity['dcc:name']['dcc:content']) {\n      let content = xQuantity['dcc:name']['dcc:content'];\n      if (Array.isArray(content)) {\n        const match = content.find(item => item.$ && item.$.lang === language) || content[0];\n        xHeader = match._ || match;\n      } else {\n        xHeader = content._ || content;\n      }\n    }\n    headers.push(xHeader);\n\n    const dataValues = [];\n    dataQuantities.forEach(q => {\n      let header = 'Data';\n      if (q['dcc:name'] && q['dcc:name']['dcc:content']) {\n        let content = q['dcc:name']['dcc:content'];\n        if (Array.isArray(content)) {\n          const match = content.find(item => item.$ && item.$.lang === language) || content[0];\n          header = match._ || match;\n        } else {\n          header = content._ || content;\n        }\n      }\n      headers.push(header);\n      let values = [];\n      if (q['si:realListXMLList'] && q['si:realListXMLList']['si:valueXMLList']) {\n        values = q['si:realListXMLList']['si:valueXMLList'].trim().split(/\\s+/).map(v => parseFloat(v));\n      }\n      if (values.length === 1 && xValues.length > 1) {\n        values = new Array(xValues.length).fill(values[0]);\n      }\n      dataValues.push(values);\n    });\n\n    // Build table rows\n    const tableData = [headers];\n    for (let i = 0; i < xValues.length; i++) {\n      const row = [];\n      row.push(xValues[i]);\n      dataValues.forEach(values => {\n        row.push(values[i] !== undefined ? values[i] : '');\n      });\n      tableData.push(row);\n    }\n    renderTable(tableData);\n\n    // Group data quantities by unit for plotting\n    const unitGroups = {};\n    dataQuantities.forEach((q, idx) => {\n      let unit = '';\n      if (q['si:realListXMLList'] && q['si:realListXMLList']['si:unitXMLList']) {\n        unit = q['si:realListXMLList']['si:unitXMLList'].trim();\n      }\n      if (!unitGroups[unit]) {\n        unitGroups[unit] = [];\n      }\n      let header = headers[idx + 1];\n      let values = [];\n      if (q['si:realListXMLList'] && q['si:realListXMLList']['si:valueXMLList']) {\n        values = q['si:realListXMLList']['si:valueXMLList'].trim().split(/\\s+/).map(v => parseFloat(v));\n      }\n      if (values.length === 1 && xValues.length > 1) {\n        values = new Array(xValues.length).fill(values[0]);\n      }\n      unitGroups[unit].push({ name: header, y: values });\n    });\n\n    // Clear plots container\n    plotsContainer.innerHTML = '';\n    Object.keys(unitGroups).forEach(unit => {\n      const graphDiv = document.createElement('div');\n      graphDiv.style.width = '100%';\n      graphDiv.style.height = '300px';\n      plotsContainer.appendChild(graphDiv);\n\n      const traces = unitGroups[unit].map(trace => {\n        return {\n          x: xValues,\n          y: trace.y,\n          type: 'scatter',\n          mode: 'lines+markers',\n          name: trace.name\n        };\n      });\n      const layout = {\n        title: 'Plot (' + unit + ')',\n        xaxis: { title: xHeader },\n        yaxis: { title: unit },\n        hovermode: 'closest'\n      };\n      Plotly.newPlot(graphDiv, traces, layout);\n\n      // Coupled mouseover events\n      graphDiv.on('plotly_hover', function(data) {\n        if (data.points && data.points.length > 0) {\n          const pointIndex = data.points[0].pointIndex + 1; // offset for header row\n          highlightTableRow(pointIndex);\n        }\n      });\n      graphDiv.on('plotly_unhover', function() {\n        clearTableRowHighlights();\n      });\n    });\n  }\n\n  // Render table from 2D array\n  function renderTable(tableData) {\n    tableContainer.innerHTML = '';\n    const table = document.createElement('table');\n    tableData.forEach((rowData, rowIndex) => {\n      const tr = document.createElement('tr');\n      rowData.forEach(cellData => {\n        const cell = document.createElement(rowIndex === 0 ? 'th' : 'td');\n        cell.textContent = cellData;\n        cell.style.padding = '4px';\n        cell.style.border = '1px solid #ccc';\n        tr.appendChild(cell);\n      });\n      tr.addEventListener('mouseover', () => { tr.style.backgroundColor = '#eef'; });\n      tr.addEventListener('mouseout', () => { tr.style.backgroundColor = ''; });\n      table.appendChild(tr);\n    });\n    tableContainer.appendChild(table);\n  }\n\n  // Placeholder functions for coupled table row highlights\n  function highlightTableRow(rowIndex) {\n    const rows = tableContainer.querySelectorAll('tr');\n    if (rows[rowIndex]) {\n      rows[rowIndex].style.backgroundColor = '#fee';\n    }\n  }\n  function clearTableRowHighlights() {\n    const rows = tableContainer.querySelectorAll('tr');\n    rows.forEach(row => row.style.backgroundColor = '');\n  }\n\n  // Initial update\n  updateVisualization();\n\n  // Update visualization on X-axis selection change\n  const radios = document.querySelectorAll('input[name=\"xAxisSelect\"]');\n  radios.forEach(radio => {\n    radio.addEventListener('change', updateVisualization);\n  });\n\n  // Tolerance toggle event (placeholder)\n  toleranceToggle.addEventListener('change', () => {\n    console.log('Tolerance toggle:', toleranceToggle.checked);\n    // Future implementation: update plot/table for tolerance markings and color coding\n  });\n}\n"
+      "MeasurementRenderer.js": "import Plotly from 'plotly.js-dist';\n\nexport function renderMeasurementResults(measurementResults, language) {\n  console.debug('renderMeasurementResults called with:', measurementResults);\n  const container = document.getElementById('measurementResults');\n  container.innerHTML = '';\n\n  if (!measurementResults) {\n    console.error('No measurementResults provided.');\n    return;\n  }\n\n  let result = measurementResults['dcc:measurementResult'];\n  console.debug('Raw dcc:measurementResult:', result);\n\n  if (!result) {\n    console.error(\"Missing 'dcc:measurementResult' in measurementResults\");\n    return;\n  }\n\n  if (!Array.isArray(result)) {\n    result = [result];\n  }\n  console.debug('Processed measurement result array:', result);\n\n  // Use the first measurement result for rendering\n  result = result[0];\n\n  // Get the measurement result name from dcc:name\n  let resultName = 'Measurement Result';\n  if (result['dcc:name'] && result['dcc:name']['dcc:content']) {\n    let content = result['dcc:name']['dcc:content'];\n    if (Array.isArray(content)) {\n      const match = content.find(item => item.$ && item.$.lang === language) || content[0];\n      resultName = match._ || match;\n    } else {\n      resultName = content._ || content;\n    }\n  }\n  console.debug('Result name:', resultName);\n\n  const tabTitle = document.createElement('h2');\n  tabTitle.textContent = resultName + ' (' + language + ')';\n  container.appendChild(tabTitle);\n\n  if (!result['dcc:data'] || !result['dcc:data']['dcc:list']) {\n    console.error(\"Missing 'dcc:data' or 'dcc:list' in measurement result:\", result);\n    return;\n  }\n  const listData = result['dcc:data']['dcc:list'];\n  console.debug('List data:', listData);\n\n  // Flatten all quantities from the list\n  let quantities = [];\n  if (listData['dcc:quantity']) {\n    quantities = Array.isArray(listData['dcc:quantity']) ? listData['dcc:quantity'] : [listData['dcc:quantity']];\n  }\n  console.debug('Quantities from list:', quantities);\n\n  // Also add quantities from measurementMetaData\n  if (listData['dcc:measurementMetaData'] && listData['dcc:measurementMetaData']['dcc:metaData']) {\n    let metaData = listData['dcc:measurementMetaData']['dcc:metaData'];\n    if (!Array.isArray(metaData)) metaData = [metaData];\n    metaData.forEach(md => {\n      if (md['dcc:data'] && md['dcc:data']['dcc:quantity']) {\n        let qs = md['dcc:data']['dcc:quantity'];\n        if (!Array.isArray(qs)) qs = [qs];\n        quantities = quantities.concat(qs);\n      }\n    });\n  }\n  console.debug('Combined quantities:', quantities);\n\n  // Separate index quantities and data quantities\n  const indexQuantities = [];\n  const dataQuantities = [];\n  quantities.forEach(q => {\n    if (q.$ && q.$.refType && q.$.refType.match(/basic_tableIndex/)) {\n      indexQuantities.push(q);\n    } else {\n      dataQuantities.push(q);\n    }\n  });\n  console.debug('Index Quantities:', indexQuantities);\n  console.debug('Data Quantities:', dataQuantities);\n\n  // Create radio buttons for X-axis selection (from indexQuantities)\n  const xAxisContainer = document.createElement('div');\n  xAxisContainer.innerHTML = '<strong>Select X-Axis:</strong> ';\n  indexQuantities.forEach((q, idx) => {\n    let nameStr = 'Index ' + idx;\n    if (q['dcc:name'] && q['dcc:name']['dcc:content']) {\n      let content = q['dcc:name']['dcc:content'];\n      if (Array.isArray(content)) {\n        const match = content.find(item => item.$ && item.$.lang === language) || content[0];\n        nameStr = match._ || match;\n      } else {\n        nameStr = content._ || content;\n      }\n    }\n    const radio = document.createElement('input');\n    radio.type = 'radio';\n    radio.name = 'xAxisSelect';\n    radio.value = idx;\n    if (idx === 0) radio.checked = true;\n    const label = document.createElement('label');\n    label.textContent = nameStr;\n    label.style.marginRight = '10px';\n    xAxisContainer.appendChild(radio);\n    xAxisContainer.appendChild(label);\n  });\n  container.appendChild(xAxisContainer);\n\n  // Tolerance toggle (placeholder)\n  const toleranceToggle = document.createElement('input');\n  toleranceToggle.type = 'checkbox';\n  toleranceToggle.id = 'toleranceToggle';\n  const tolLabel = document.createElement('label');\n  tolLabel.htmlFor = 'toleranceToggle';\n  tolLabel.textContent = ' Enable tolerance markings';\n  const tolContainer = document.createElement('div');\n  tolContainer.appendChild(toleranceToggle);\n  tolContainer.appendChild(tolLabel);\n  container.appendChild(tolContainer);\n\n  // Create containers for plots and table\n  const plotsContainer = document.createElement('div');\n  plotsContainer.id = 'plotsContainer';\n  container.appendChild(plotsContainer);\n  const tableContainer = document.createElement('div');\n  tableContainer.id = 'tableContainer';\n  container.appendChild(tableContainer);\n\n  // Function to update the visualization\n  function updateVisualization() {\n    const selectedRadio = document.querySelector('input[name=\"xAxisSelect\"]:checked');\n    if (!selectedRadio) {\n      console.error('No X-Axis selection found.');\n      return;\n    }\n    const selectedIndex = selectedRadio.value;\n    const xQuantity = indexQuantities[selectedIndex];\n    let xValues = [];\n    if (xQuantity && xQuantity['si:realListXMLList'] && xQuantity['si:realListXMLList']['si:valueXMLList']) {\n      xValues = xQuantity['si:realListXMLList']['si:valueXMLList'].trim().split(/\\s+/).map(v => parseFloat(v));\n    }\n    console.debug('Selected X-Axis values:', xValues);\n\n    // Build table headers and values\n    const headers = [];\n    let xHeader = 'X-Axis';\n    if (xQuantity['dcc:name'] && xQuantity['dcc:name']['dcc:content']) {\n      let content = xQuantity['dcc:name']['dcc:content'];\n      if (Array.isArray(content)) {\n        const match = content.find(item => item.$ && item.$.lang === language) || content[0];\n        xHeader = match._ || match;\n      } else {\n        xHeader = content._ || content;\n      }\n    }\n    headers.push(xHeader);\n\n    const dataValues = [];\n    dataQuantities.forEach(q => {\n      let header = 'Data';\n      if (q['dcc:name'] && q['dcc:name']['dcc:content']) {\n        let content = q['dcc:name']['dcc:content'];\n        if (Array.isArray(content)) {\n          const match = content.find(item => item.$ && item.$.lang === language) || content[0];\n          header = match._ || match;\n        } else {\n          header = content._ || content;\n        }\n      }\n      headers.push(header);\n      let values = [];\n      if (q['si:realListXMLList'] && q['si:realListXMLList']['si:valueXMLList']) {\n        values = q['si:realListXMLList']['si:valueXMLList'].trim().split(/\\s+/).map(v => parseFloat(v));\n      }\n      if (values.length === 1 && xValues.length > 1) {\n        values = new Array(xValues.length).fill(values[0]);\n      }\n      dataValues.push(values);\n    });\n\n    // Build table rows\n    const tableData = [headers];\n    for (let i = 0; i < xValues.length; i++) {\n      const row = [];\n      row.push(xValues[i]);\n      dataValues.forEach(values => {\n        row.push(values[i] !== undefined ? values[i] : '');\n      });\n      tableData.push(row);\n    }\n    console.debug('Table data:', tableData);\n    renderTable(tableData);\n\n    // Group data quantities by unit for plotting\n    const unitGroups = {};\n    dataQuantities.forEach((q, idx) => {\n      let unit = '';\n      if (q['si:realListXMLList'] && q['si:realListXMLList']['si:unitXMLList']) {\n        unit = q['si:realListXMLList']['si:unitXMLList'].trim();\n      }\n      if (!unitGroups[unit]) {\n        unitGroups[unit] = [];\n      }\n      let header = headers[idx + 1];\n      let values = [];\n      if (q['si:realListXMLList'] && q['si:realListXMLList']['si:valueXMLList']) {\n        values = q['si:realListXMLList']['si:valueXMLList'].trim().split(/\\s+/).map(v => parseFloat(v));\n      }\n      if (values.length === 1 && xValues.length > 1) {\n        values = new Array(xValues.length).fill(values[0]);\n      }\n      unitGroups[unit].push({ name: header, y: values });\n    });\n    console.debug('Unit groups for plots:', unitGroups);\n\n    // Clear and render plots\n    plotsContainer.innerHTML = '';\n    Object.keys(unitGroups).forEach(unit => {\n      const graphDiv = document.createElement('div');\n      graphDiv.style.width = '100%';\n      graphDiv.style.height = '300px';\n      plotsContainer.appendChild(graphDiv);\n\n      const traces = unitGroups[unit].map(trace => {\n        return {\n          x: xValues,\n          y: trace.y,\n          type: 'scatter',\n          mode: 'lines+markers',\n          name: trace.name\n        };\n      });\n      const layout = {\n        title: 'Plot (' + unit + ')',\n        xaxis: { title: xHeader },\n        yaxis: { title: unit },\n        hovermode: 'closest'\n      };\n      Plotly.newPlot(graphDiv, traces, layout).then(() => {\n        console.debug('Plot rendered for unit:', unit);\n      });\n\n      // Coupled mouseover events\n      graphDiv.on('plotly_hover', function(data) {\n        if (data.points && data.points.length > 0) {\n          const pointIndex = data.points[0].pointIndex + 1; // offset for header row\n          highlightTableRow(pointIndex);\n        }\n      });\n      graphDiv.on('plotly_unhover', function() {\n        clearTableRowHighlights();\n      });\n    });\n  }\n\n  // Render table from a 2D array\n  function renderTable(tableData) {\n    tableContainer.innerHTML = '';\n    const table = document.createElement('table');\n    tableData.forEach((rowData, rowIndex) => {\n      const tr = document.createElement('tr');\n      rowData.forEach(cellData => {\n        const cell = document.createElement(rowIndex === 0 ? 'th' : 'td');\n        cell.textContent = cellData;\n        cell.style.padding = '4px';\n        cell.style.border = '1px solid #ccc';\n        tr.appendChild(cell);\n      });\n      tr.addEventListener('mouseover', () => { tr.style.backgroundColor = '#eef'; });\n      tr.addEventListener('mouseout', () => { tr.style.backgroundColor = ''; });\n      table.appendChild(tr);\n    });\n    tableContainer.appendChild(table);\n  }\n\n  // Functions for coupled table row highlights\n  function highlightTableRow(rowIndex) {\n    const rows = tableContainer.querySelectorAll('tr');\n    if (rows[rowIndex]) {\n      rows[rowIndex].style.backgroundColor = '#fee';\n    }\n  }\n  function clearTableRowHighlights() {\n    const rows = tableContainer.querySelectorAll('tr');\n    rows.forEach(row => row.style.backgroundColor = '');\n  }\n\n  // Initial update\n  updateVisualization();\n\n  // Update visualization when X-axis selection changes\n  const radios = document.querySelectorAll('input[name=\"xAxisSelect\"]');\n  radios.forEach(radio => {\n    radio.addEventListener('change', updateVisualization);\n  });\n\n  // Tolerance toggle event (placeholder)\n  toleranceToggle.addEventListener('change', () => {\n    console.log('Tolerance toggle:', toleranceToggle.checked);\n    // Future: update plot/table for tolerance markings and color coding\n  });\n}\n"
     }
   }
 }
diff --git a/src/app.js b/src/app.js
index 95e25b5..6a2a9c9 100644
--- a/src/app.js
+++ b/src/app.js
@@ -1,9 +1,8 @@
 import { parseString } from 'xml2js';
-import JSONEditor from 'jsoneditor';
 
-// Import renderer functions
-import { renderAdminData } from './renderers/AdminRenderer.js';
+// Import our interactive measurement renderer
 import { renderMeasurementResults } from './renderers/MeasurementRenderer.js';
+import { renderAdminData } from './renderers/AdminRenderer.js';
 
 // Global configuration
 const selectableLanguages = ['en', 'de', 'fr', 'es'];
@@ -28,7 +27,7 @@ export function initApp() {
   });
   appContainer.appendChild(langSelect);
 
-  // Create containers for administrative data and measurement results
+  // Create containers for admin data and measurement results
   const adminContainer = document.createElement('div');
   adminContainer.id = 'adminData';
   appContainer.appendChild(adminContainer);
@@ -37,11 +36,10 @@ export function initApp() {
   measContainer.id = 'measurementResults';
   appContainer.appendChild(measContainer);
 
-  // Load the embedded XML file (for testing) from the data folder
+  // Load the XML file from the data folder
   fetch('/data/sin_acceleration_example_dcc_WithExampleConformatyStatment.xml')
     .then(response => response.text())
     .then(xmlText => {
-      // Convert XML to JSON using xml2js
       parseString(xmlText, { explicitArray: false }, (err, result) => {
         if (err) {
           console.error('Error parsing XML:', err);
@@ -56,12 +54,9 @@ export function initApp() {
 
 function renderAll() {
   // Clear containers
-  const adminContainer = document.getElementById('adminData');
-  adminContainer.innerHTML = '';
-  const measContainer = document.getElementById('measurementResults');
-  measContainer.innerHTML = '';
+  document.getElementById('adminData').innerHTML = '';
+  document.getElementById('measurementResults').innerHTML = '';
 
-  // Render the content if XML has been loaded and converted
   if (dccData && dccData['dcc:digitalCalibrationCertificate']) {
     const cert = dccData['dcc:digitalCalibrationCertificate'];
     if (cert['dcc:administrativeData']) {
diff --git a/src/main.js b/src/main.js
index 122f3f4..06d04af 100644
--- a/src/main.js
+++ b/src/main.js
@@ -1,6 +1,6 @@
 import { initApp } from './app.js';
 
-// Wait for the DOM to be loaded before initializing
+// Initialize after DOM loads
 document.addEventListener('DOMContentLoaded', () => {
   initApp();
 });
diff --git a/src/renderers/AdminRenderer.js b/src/renderers/AdminRenderer.js
index 82ffa9b..83d4e93 100644
--- a/src/renderers/AdminRenderer.js
+++ b/src/renderers/AdminRenderer.js
@@ -3,20 +3,16 @@ import 'jsoneditor/dist/jsoneditor.css';
 
 export function renderAdminData(adminData, language) {
   const container = document.getElementById('adminData');
-  // Title for admin data
   const title = document.createElement('h2');
   title.textContent = 'Administrative Data (' + language + ')';
   container.appendChild(title);
 
-  // JSONEditor options (read-only view)
   const options = {
     mode: 'view',
     mainMenuBar: false,
     navigationBar: false,
     statusBar: false
   };
-
-  // Create a new JSONEditor instance to display the adminData
   const editor = new JSONEditor(container, options);
   editor.set(adminData);
 }
diff --git a/src/renderers/MeasurementRenderer.js b/src/renderers/MeasurementRenderer.js
index fa583a0..4cf1abc 100644
--- a/src/renderers/MeasurementRenderer.js
+++ b/src/renderers/MeasurementRenderer.js
@@ -1,57 +1,320 @@
-import { selectRenderer } from './SelectRenderer.js';
+import Plotly from 'plotly.js-dist';
 
 export function renderMeasurementResults(measurementResults, language) {
+  console.debug('renderMeasurementResults called with:', measurementResults);
   const container = document.getElementById('measurementResults');
-  const title = document.createElement('h2');
-  title.textContent = 'Measurement Results (' + language + ')';
-  container.appendChild(title);
+  container.innerHTML = '';
+
+  if (!measurementResults) {
+    console.error('No measurementResults provided.');
+    return;
+  }
 
-  // measurementResult can be an object or an array
   let results = measurementResults['dcc:measurementResult'];
+  console.debug('Raw dcc:measurementResult:', results);
+
+  if (!results) {
+    console.error("Missing 'dcc:measurementResult' in measurementResults");
+    return;
+  }
+
   if (!Array.isArray(results)) {
     results = [results];
   }
+  console.debug('Processed measurement result array:', results);
 
-  results.forEach(result => {
-    // Render the measurement result title
-    const resultTitle = document.createElement('h3');
-    if (result['dcc:name'] && result['dcc:name']['dcc:content']) {
-      let nameContent = result['dcc:name']['dcc:content'];
-      if (Array.isArray(nameContent)) {
-        // Find the content matching the selected language or fallback
-        const match = nameContent.find(item => item.$ && item.$.lang === language) || nameContent[0];
-        resultTitle.textContent = match._ || match;
-      } else {
-        resultTitle.textContent = nameContent._ || nameContent;
+  // Use the first measurementResult object
+  const measurementResult = results[0];
+  console.debug('Using measurementResult:', measurementResult);
+
+  // Extract the result object from dcc:results -> dcc:result
+  let resultObj = measurementResult['dcc:results'] && measurementResult['dcc:results']['dcc:result'];
+  if (!resultObj) {
+    console.error("Missing 'dcc:results' or 'dcc:result' in measurementResult:", measurementResult);
+    return;
+  }
+  if (Array.isArray(resultObj)) {
+    resultObj = resultObj[0];
+  }
+  console.debug('Using result object:', resultObj);
+
+  // Get the measurement result name from dcc:name (from measurementResult)
+  let resultName = 'Measurement Result';
+  if (measurementResult['dcc:name'] && measurementResult['dcc:name']['dcc:content']) {
+    let content = measurementResult['dcc:name']['dcc:content'];
+    if (Array.isArray(content)) {
+      const match = content.find(item => item.$ && item.$.lang === language) || content[0];
+      resultName = match._ || match;
+    } else {
+      resultName = content._ || content;
+    }
+  }
+  console.debug('Result name:', resultName);
+
+  const tabTitle = document.createElement('h2');
+  tabTitle.textContent = resultName + ' (' + language + ')';
+  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;
+  }
+  const listData = resultObj['dcc:data']['dcc:list'];
+  console.debug('List data:', listData);
+
+  // Flatten all quantities from the list
+  let quantities = [];
+  if (listData['dcc:quantity']) {
+    quantities = Array.isArray(listData['dcc:quantity']) ? listData['dcc:quantity'] : [listData['dcc:quantity']];
+  }
+  console.debug('Quantities from list:', quantities);
+
+  // Also add quantities from measurementMetaData if available
+  if (listData['dcc:measurementMetaData'] && listData['dcc:measurementMetaData']['dcc:metaData']) {
+    let metaData = listData['dcc:measurementMetaData']['dcc:metaData'];
+    if (!Array.isArray(metaData)) metaData = [metaData];
+    metaData.forEach(md => {
+      if (md['dcc:data'] && md['dcc:data']['dcc:quantity']) {
+        let qs = md['dcc:data']['dcc:quantity'];
+        if (!Array.isArray(qs)) qs = [qs];
+        quantities = quantities.concat(qs);
       }
+    });
+  }
+  console.debug('Combined quantities:', quantities);
+
+  // Separate index quantities and data quantities
+  const indexQuantities = [];
+  const dataQuantities = [];
+  quantities.forEach(q => {
+    if (q.$ && q.$.refType && q.$.refType.match(/basic_tableIndex/)) {
+      indexQuantities.push(q);
     } else {
-      resultTitle.textContent = 'Measurement Result';
-    }
-    container.appendChild(resultTitle);
-
-    // Render results using dcc:results -> dcc:result
-    if (result['dcc:results'] && result['dcc:results']['dcc:result']) {
-      let resItems = result['dcc:results']['dcc:result'];
-      if (!Array.isArray(resItems)) {
-        resItems = [resItems];
-      }
-      resItems.forEach(res => {
-        // Check if the result contains a dcc:data with a dcc:list that should be rendered as plot/table
-        if (res['dcc:data'] && res['dcc:data']['dcc:list']) {
-          const dataContainer = document.createElement('div');
-          dataContainer.className = 'plot-table';
-          // Use the global selectRenderer to decide how to render this data
-          const renderedContent = selectRenderer(res['dcc:data']['dcc:list'], language);
-          dataContainer.appendChild(renderedContent);
-          container.appendChild(dataContainer);
+      dataQuantities.push(q);
+    }
+  });
+  console.debug('Index Quantities:', indexQuantities);
+  console.debug('Data Quantities:', dataQuantities);
+
+  // Create radio buttons for X-axis selection (from indexQuantities)
+  const xAxisContainer = document.createElement('div');
+  xAxisContainer.innerHTML = '<strong>Select X-Axis:</strong> ';
+  indexQuantities.forEach((q, idx) => {
+    let nameStr = 'Index ' + idx;
+    if (q['dcc:name'] && q['dcc:name']['dcc:content']) {
+      let content = q['dcc:name']['dcc:content'];
+      if (Array.isArray(content)) {
+        const match = content.find(item => item.$ && item.$.lang === language) || content[0];
+        nameStr = match._ || match;
+      } else {
+        nameStr = content._ || content;
+      }
+    }
+    const radio = document.createElement('input');
+    radio.type = 'radio';
+    radio.name = 'xAxisSelect';
+    radio.value = idx;
+    if (idx === 0) radio.checked = true;
+    const label = document.createElement('label');
+    label.textContent = nameStr;
+    label.style.marginRight = '10px';
+    xAxisContainer.appendChild(radio);
+    xAxisContainer.appendChild(label);
+  });
+  container.appendChild(xAxisContainer);
+
+  // Tolerance toggle (placeholder)
+  const toleranceToggle = document.createElement('input');
+  toleranceToggle.type = 'checkbox';
+  toleranceToggle.id = 'toleranceToggle';
+  const tolLabel = document.createElement('label');
+  tolLabel.htmlFor = 'toleranceToggle';
+  tolLabel.textContent = ' Enable tolerance markings';
+  const tolContainer = document.createElement('div');
+  tolContainer.appendChild(toleranceToggle);
+  tolContainer.appendChild(tolLabel);
+  container.appendChild(tolContainer);
+
+  // Create containers for plots and table
+  const plotsContainer = document.createElement('div');
+  plotsContainer.id = 'plotsContainer';
+  container.appendChild(plotsContainer);
+  const tableContainer = document.createElement('div');
+  tableContainer.id = 'tableContainer';
+  container.appendChild(tableContainer);
+
+  // Function to update the visualization
+  function updateVisualization() {
+    const selectedRadio = document.querySelector('input[name="xAxisSelect"]:checked');
+    if (!selectedRadio) {
+      console.error('No X-Axis selection found.');
+      return;
+    }
+    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));
+    }
+    console.debug('Selected X-Axis values:', xValues);
+
+    // Build table headers and values
+    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;
+      }
+    }
+    headers.push(xHeader);
+
+    const dataValues = [];
+    dataQuantities.forEach(q => {
+      let header = 'Data';
+      if (q['dcc:name'] && q['dcc:name']['dcc:content']) {
+        let content = q['dcc:name']['dcc:content'];
+        if (Array.isArray(content)) {
+          const match = content.find(item => item.$ && item.$.lang === language) || content[0];
+          header = match._ || match;
         } else {
-          // Fallback: simple placeholder
-          const defaultContainer = document.createElement('div');
-          defaultContainer.className = 'default-render';
-          defaultContainer.textContent = 'Default rendering for measurement result.';
-          container.appendChild(defaultContainer);
+          header = content._ || content;
         }
+      }
+      headers.push(header);
+      let values = [];
+      if (q['si:realListXMLList'] && q['si:realListXMLList']['si:valueXMLList']) {
+        values = q['si:realListXMLList']['si:valueXMLList'].trim().split(/\s+/).map(v => parseFloat(v));
+      }
+      if (values.length === 1 && xValues.length > 1) {
+        values = new Array(xValues.length).fill(values[0]);
+      }
+      dataValues.push(values);
+    });
+
+    // 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] : '');
       });
+      tableData.push(row);
     }
+    console.debug('Table data:', tableData);
+    renderTable(tableData);
+
+    // Group data quantities by unit for plotting
+    const unitGroups = {};
+    dataQuantities.forEach((q, idx) => {
+      let unit = '';
+      if (q['si:realListXMLList'] && q['si:realListXMLList']['si:unitXMLList']) {
+        unit = q['si:realListXMLList']['si:unitXMLList'].trim();
+      }
+      if (!unitGroups[unit]) {
+        unitGroups[unit] = [];
+      }
+      let header = headers[idx + 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));
+      }
+      if (values.length === 1 && xValues.length > 1) {
+        values = new Array(xValues.length).fill(values[0]);
+      }
+      unitGroups[unit].push({ name: header, y: values });
+    });
+    console.debug('Unit groups for plots:', unitGroups);
+
+    // Clear and render plots
+    plotsContainer.innerHTML = '';
+    Object.keys(unitGroups).forEach(unit => {
+      const graphDiv = document.createElement('div');
+      graphDiv.style.width = '100%';
+      graphDiv.style.height = '300px';
+      plotsContainer.appendChild(graphDiv);
+
+      const traces = unitGroups[unit].map(trace => {
+        return {
+          x: xValues,
+          y: trace.y,
+          type: 'scatter',
+          mode: 'lines+markers',
+          name: trace.name
+        };
+      });
+      const layout = {
+        title: 'Plot (' + unit + ')',
+        xaxis: { title: xHeader },
+        yaxis: { title: unit },
+        hovermode: 'closest'
+      };
+      Plotly.newPlot(graphDiv, traces, layout).then(() => {
+        console.debug('Plot rendered for unit:', unit);
+      });
+
+      // 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
+          highlightTableRow(pointIndex);
+        }
+      });
+      graphDiv.on('plotly_unhover', function() {
+        clearTableRowHighlights();
+      });
+    });
+  }
+
+  // Render table from a 2D array
+  function renderTable(tableData) {
+    tableContainer.innerHTML = '';
+    const table = document.createElement('table');
+    tableData.forEach((rowData, rowIndex) => {
+      const tr = document.createElement('tr');
+      rowData.forEach(cellData => {
+        const cell = document.createElement(rowIndex === 0 ? 'th' : 'td');
+        cell.textContent = cellData;
+        cell.style.padding = '4px';
+        cell.style.border = '1px solid #ccc';
+        tr.appendChild(cell);
+      });
+      tr.addEventListener('mouseover', () => { tr.style.backgroundColor = '#eef'; });
+      tr.addEventListener('mouseout', () => { tr.style.backgroundColor = ''; });
+      table.appendChild(tr);
+    });
+    tableContainer.appendChild(table);
+  }
+
+  // Functions for coupled table row highlights
+  function highlightTableRow(rowIndex) {
+    const rows = tableContainer.querySelectorAll('tr');
+    if (rows[rowIndex]) {
+      rows[rowIndex].style.backgroundColor = '#fee';
+    }
+  }
+  function clearTableRowHighlights() {
+    const rows = 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
   });
 }
diff --git a/src/renderers/SelectRenderer.js b/src/renderers/SelectRenderer.js
index f593395..3927995 100644
--- a/src/renderers/SelectRenderer.js
+++ b/src/renderers/SelectRenderer.js
@@ -1,18 +1,6 @@
 export function selectRenderer(json, language) {
-  // If the JSON node has a refType matching basic_\dIndexTable, render as plot and table
-  if (json && json.$ && json.$.refType && /^basic_\dIndexTable/.test(json.$.refType)) {
-    return renderPlotAndTable(json, language);
-  }
-  // Fallback: render a simple JSON string
-  const container = document.createElement('pre');
-  container.textContent = JSON.stringify(json, null, 2);
-  return container;
-}
-
-function renderPlotAndTable(json, language) {
-  // Placeholder implementation for plot and table rendering
+  // This function is not used in the interactive measurement renderer
   const container = document.createElement('div');
-  container.className = 'plot-and-table';
-  container.textContent = 'Plot and Table renderer placeholder for language: ' + language;
+  container.textContent = 'Interactive Measurement Renderer in use.';
   return container;
 }
diff --git a/src/styles.css b/src/styles.css
index e10ba0b..a5dc683 100644
--- a/src/styles.css
+++ b/src/styles.css
@@ -13,7 +13,21 @@ h2, h3 {
   padding: 10px;
 }
 
-.plot-table, .plot-and-table {
+table {
+  width: 100%;
+  border-collapse: collapse;
+}
+
+table, th, td {
+  border: 1px solid #ccc;
+}
+
+th, td {
+  padding: 4px;
+  text-align: center;
+}
+
+.plot-and-table, .plot-table {
   margin: 10px 0;
   padding: 10px;
   border: 1px dashed #999;
diff --git a/vite.config.js b/vite.config.js
index be1e181..527552c 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -3,13 +3,11 @@ import { defineConfig } from 'vite';
 export default defineConfig({
   resolve: {
     alias: {
-      // Ensure that Node's events module is polyfilled
       events: 'events'
     }
   },
   optimizeDeps: {
     esbuildOptions: {
-      // Define global as globalThis to help with some Node packages
       define: { global: 'globalThis' }
     }
   }
-- 
GitLab