Skip to content
GitLab
Explore
Sign in
Register
Primary navigation
Search or go to…
Project
dccviewer-js
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package registry
Container registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
DigitalDynamicMeasurement
dccviewer-js
Commits
4403465f
Commit
4403465f
authored
4 months ago
by
Benedikt
Browse files
Options
Downloads
Patches
Plain Diff
added color coding
parent
53d7f02a
No related branches found
No related tags found
No related merge requests found
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
src/renderers/MeasurementRenderer.js
+174
-51
174 additions, 51 deletions
src/renderers/MeasurementRenderer.js
with
174 additions
and
51 deletions
src/renderers/MeasurementRenderer.js
+
174
−
51
View file @
4403465f
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
valu
es
// Build table headers and
arrays for values, comments, conformity, uncertainti
es
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
+
'
)
'
,
x
axis
:
{
title
:
xHeader
},
yaxis
:
{
title
:
unit
}
,
hovermode
:
'
closest
'
xaxis
:
{
title
:
{
text
:
xHeader
,
font
:
{
size
:
16
,
weight
:
'
bold
'
}
}
}
,
y
axis
:
{
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
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment