[IDC-2049] Sort Tags in Tag browser split items in sequences, add indent after space. (#2053)
* Sort tag browser, add items, add indent. * Remove debugger.
This commit is contained in:
parent
6cf72d1757
commit
ccdfcd4a85
@ -39,6 +39,7 @@ const dicomBrowserSelectStyles = {
|
|||||||
}),
|
}),
|
||||||
option: (base, state) => ({
|
option: (base, state) => ({
|
||||||
...base,
|
...base,
|
||||||
|
width: '100%',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
'&:first-of-type': {
|
'&:first-of-type': {
|
||||||
borderTopLeftRadius: 5,
|
borderTopLeftRadius: 5,
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: start;
|
justify-content: start;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dcmseg-segmentation-item .segmentation-meta {
|
.dcmseg-segmentation-item .segmentation-meta {
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dicom-tag-browser-table td {
|
.dicom-tag-browser-table tr {
|
||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
padding-right: 10px;
|
padding-right: 10px;
|
||||||
color: var(--table-text-primary-color);
|
color: var(--table-text-primary-color);
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { classes } from '@ohif/core';
|
import { classes } from '@ohif/core';
|
||||||
import dcmjs from 'dcmjs';
|
import dcmjs from 'dcmjs';
|
||||||
import DicomBrowserSelect from './DicomBrowserSelect';
|
import DicomBrowserSelect from './DicomBrowserSelect';
|
||||||
@ -16,73 +16,96 @@ const DicomTagBrowser = ({ displaySets, displaySetInstanceUID }) => {
|
|||||||
setActiveDisplaySetInstanceUID,
|
setActiveDisplaySetInstanceUID,
|
||||||
] = useState(displaySetInstanceUID);
|
] = useState(displaySetInstanceUID);
|
||||||
const [activeInstance, setActiveInstance] = useState(0);
|
const [activeInstance, setActiveInstance] = useState(0);
|
||||||
|
const [tags, setTags] = useState([]);
|
||||||
|
const [instanceList, setInstanceList] = useState([]);
|
||||||
|
const [displaySetList, setDisplaySetList] = useState([]);
|
||||||
|
const [isImageStack, setIsImageStack] = useState(false);
|
||||||
|
|
||||||
const activeDisplaySet = displaySets.find(
|
useEffect(() => {
|
||||||
ds => ds.displaySetInstanceUID === activeDisplaySetInstanceUID
|
const activeDisplaySet = displaySets.find(
|
||||||
);
|
ds => ds.displaySetInstanceUID === activeDisplaySetInstanceUID
|
||||||
|
);
|
||||||
|
|
||||||
const displaySetList = displaySets.map(displaySet => {
|
const newDisplaySetList = displaySets.map(displaySet => {
|
||||||
const {
|
const {
|
||||||
displaySetInstanceUID,
|
displaySetInstanceUID,
|
||||||
SeriesDate,
|
SeriesDate,
|
||||||
SeriesTime,
|
SeriesTime,
|
||||||
SeriesNumber,
|
SeriesNumber,
|
||||||
SeriesDescription,
|
SeriesDescription,
|
||||||
Modality,
|
Modality,
|
||||||
} = displaySet;
|
} = displaySet;
|
||||||
|
|
||||||
/* Map to display representation */
|
/* Map to display representation */
|
||||||
const dateStr = `${SeriesDate}:${SeriesTime}`.split('.')[0];
|
const dateStr = `${SeriesDate}:${SeriesTime}`.split('.')[0];
|
||||||
const date = moment(dateStr, 'YYYYMMDD:HHmmss');
|
const date = moment(dateStr, 'YYYYMMDD:HHmmss');
|
||||||
const displayDate = date.format('ddd, MMM Do YYYY');
|
const displayDate = date.format('ddd, MMM Do YYYY');
|
||||||
|
|
||||||
return {
|
|
||||||
value: displaySetInstanceUID,
|
|
||||||
title: `${SeriesNumber} (${Modality}): ${SeriesDescription}`,
|
|
||||||
description: displayDate,
|
|
||||||
onClick: () => {
|
|
||||||
setActiveDisplaySetInstanceUID(displaySetInstanceUID);
|
|
||||||
setActiveInstance(0);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
let metadata;
|
|
||||||
const isImageStack = activeDisplaySet instanceof ImageSet;
|
|
||||||
|
|
||||||
let selectedInstanceValue;
|
|
||||||
let instanceList;
|
|
||||||
|
|
||||||
if (isImageStack) {
|
|
||||||
const { images } = activeDisplaySet;
|
|
||||||
const image = images[activeInstance];
|
|
||||||
|
|
||||||
instanceList = images.map((image, index) => {
|
|
||||||
const metadata = image.getData().metadata;
|
|
||||||
|
|
||||||
const { InstanceNumber } = metadata;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
value: index,
|
value: displaySetInstanceUID,
|
||||||
title: `Instance Number: ${InstanceNumber}`,
|
title: `${SeriesNumber} (${Modality}): ${SeriesDescription}`,
|
||||||
description: '',
|
description: displayDate,
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
setActiveInstance(index);
|
setActiveDisplaySetInstanceUID(displaySetInstanceUID);
|
||||||
|
setActiveInstance(0);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
selectedInstanceValue = instanceList[activeInstance];
|
let metadata;
|
||||||
|
const isImageStack = activeDisplaySet instanceof ImageSet;
|
||||||
|
|
||||||
metadata = image.getData().metadata;
|
let selectedInstanceValue;
|
||||||
} else {
|
let instanceList;
|
||||||
metadata = activeDisplaySet.metadata;
|
|
||||||
}
|
if (isImageStack) {
|
||||||
|
const { images } = activeDisplaySet;
|
||||||
|
const image = images[activeInstance];
|
||||||
|
|
||||||
|
instanceList = images.map((image, index) => {
|
||||||
|
const metadata = image.getData().metadata;
|
||||||
|
|
||||||
|
const { InstanceNumber } = metadata;
|
||||||
|
|
||||||
|
return {
|
||||||
|
value: index,
|
||||||
|
title: `Instance Number: ${InstanceNumber}`,
|
||||||
|
description: '',
|
||||||
|
onClick: () => {
|
||||||
|
setActiveInstance(index);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
metadata = image.getData().metadata;
|
||||||
|
} else {
|
||||||
|
metadata = activeDisplaySet.metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
setTags(getSortedTags(metadata));
|
||||||
|
setInstanceList(instanceList);
|
||||||
|
setDisplaySetList(newDisplaySetList);
|
||||||
|
setIsImageStack(isImageStack);
|
||||||
|
}, [activeDisplaySetInstanceUID, activeInstance]);
|
||||||
|
|
||||||
const selectedDisplaySetValue = displaySetList.find(
|
const selectedDisplaySetValue = displaySetList.find(
|
||||||
ds => ds.value === activeDisplaySetInstanceUID
|
ds => ds.value === activeDisplaySetInstanceUID
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let instanceSelectList = null;
|
||||||
|
|
||||||
|
if (isImageStack) {
|
||||||
|
const selectedInstanceValue = instanceList[activeInstance];
|
||||||
|
|
||||||
|
instanceSelectList = (
|
||||||
|
<DicomBrowserSelect
|
||||||
|
value={selectedInstanceValue}
|
||||||
|
formatOptionLabel={DicomBrowserSelectItem}
|
||||||
|
options={instanceList}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<DicomBrowserSelect
|
<DicomBrowserSelect
|
||||||
@ -90,20 +113,14 @@ const DicomTagBrowser = ({ displaySets, displaySetInstanceUID }) => {
|
|||||||
formatOptionLabel={DicomBrowserSelectItem}
|
formatOptionLabel={DicomBrowserSelectItem}
|
||||||
options={displaySetList}
|
options={displaySetList}
|
||||||
/>
|
/>
|
||||||
{isImageStack ? (
|
{instanceSelectList}
|
||||||
<DicomBrowserSelect
|
<DicomTagTable tags={tags}></DicomTagTable>
|
||||||
value={selectedInstanceValue}
|
|
||||||
formatOptionLabel={DicomBrowserSelectItem}
|
|
||||||
options={instanceList}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
<DicomTagTable instanceMetadata={metadata}></DicomTagTable>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
function DicomTagTable({ instanceMetadata }) {
|
function DicomTagTable({ tags }) {
|
||||||
const rows = getRows(instanceMetadata);
|
const rows = getFormattedRowsFromTags(tags);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@ -114,19 +131,71 @@ function DicomTagTable({ instanceMetadata }) {
|
|||||||
<th className="dicom-tag-browser-table-left">Keyword</th>
|
<th className="dicom-tag-browser-table-left">Keyword</th>
|
||||||
<th className="dicom-tag-browser-table-left">Value</th>
|
<th className="dicom-tag-browser-table-left">Value</th>
|
||||||
</tr>
|
</tr>
|
||||||
{rows.map(row => (
|
{rows.map(row => {
|
||||||
<tr>
|
const className = row.className ? row.className : null;
|
||||||
<td>{row[0]}</td>
|
|
||||||
<td>{row[1]}</td>
|
return (
|
||||||
<td>{row[2]}</td>
|
<tr className={className}>
|
||||||
<td>{row[3]}</td>
|
<td>{row[0]}</td>
|
||||||
</tr>
|
<td className="dicom-tag-browser-table-center">{row[1]}</td>
|
||||||
))}
|
<td>{row[2]}</td>
|
||||||
|
<td>{row[3]}</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getFormattedRowsFromTags(tags) {
|
||||||
|
const rows = [];
|
||||||
|
|
||||||
|
tags.forEach(tagInfo => {
|
||||||
|
if (tagInfo.vr === 'SQ') {
|
||||||
|
rows.push([
|
||||||
|
`${tagInfo.tagIndent}${tagInfo.tag}`,
|
||||||
|
tagInfo.vr,
|
||||||
|
tagInfo.keyword,
|
||||||
|
'',
|
||||||
|
]);
|
||||||
|
|
||||||
|
const { values } = tagInfo;
|
||||||
|
|
||||||
|
values.forEach((item, index) => {
|
||||||
|
const formatedRowsFromTags = getFormattedRowsFromTags(item);
|
||||||
|
|
||||||
|
rows.push([
|
||||||
|
`${item[0].tagIndent}(FFFE,E000)`,
|
||||||
|
'',
|
||||||
|
`Item #${index}`,
|
||||||
|
'',
|
||||||
|
]);
|
||||||
|
|
||||||
|
rows.push(...formatedRowsFromTags);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
rows.push([
|
||||||
|
`${tagInfo.tagIndent}${tagInfo.tag}`,
|
||||||
|
tagInfo.vr,
|
||||||
|
tagInfo.keyword,
|
||||||
|
tagInfo.value,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSortedTags(metadata) {
|
||||||
|
const tagList = getRows(metadata);
|
||||||
|
|
||||||
|
// Sort top level tags, sequence groups are sorted when created.
|
||||||
|
_sortTagList(tagList);
|
||||||
|
|
||||||
|
return tagList;
|
||||||
|
}
|
||||||
|
|
||||||
function getRows(metadata, depth = 0) {
|
function getRows(metadata, depth = 0) {
|
||||||
// Tag, Type, Value, Keyword
|
// Tag, Type, Value, Keyword
|
||||||
|
|
||||||
@ -138,6 +207,10 @@ function getRows(metadata, depth = 0) {
|
|||||||
tagIndent += '>';
|
tagIndent += '>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (depth > 0) {
|
||||||
|
tagIndent += ' '; // If indented, add a space after the indents.
|
||||||
|
}
|
||||||
|
|
||||||
const rows = [];
|
const rows = [];
|
||||||
|
|
||||||
for (let i = 0; i < keywords.length; i++) {
|
for (let i = 0; i < keywords.length; i++) {
|
||||||
@ -155,7 +228,16 @@ function getRows(metadata, depth = 0) {
|
|||||||
const sequenceAsArray = toArray(value);
|
const sequenceAsArray = toArray(value);
|
||||||
|
|
||||||
// Push line defining the sequence
|
// Push line defining the sequence
|
||||||
rows.push([`${tagIndent}${tagInfo.tag}`, tagInfo.vr, keyword, '']);
|
|
||||||
|
const sequence = {
|
||||||
|
tag: tagInfo.tag,
|
||||||
|
tagIndent,
|
||||||
|
vr: tagInfo.vr,
|
||||||
|
keyword,
|
||||||
|
values: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
rows.push(sequence);
|
||||||
|
|
||||||
if (value === null) {
|
if (value === null) {
|
||||||
// Type 2 Sequence
|
// Type 2 Sequence
|
||||||
@ -165,9 +247,10 @@ function getRows(metadata, depth = 0) {
|
|||||||
sequenceAsArray.forEach(item => {
|
sequenceAsArray.forEach(item => {
|
||||||
const sequenceRows = getRows(item, depth + 1);
|
const sequenceRows = getRows(item, depth + 1);
|
||||||
|
|
||||||
sequenceRows.forEach(row => {
|
// Sort the sequence group.
|
||||||
rows.push(row);
|
_sortTagList(sequenceRows);
|
||||||
});
|
|
||||||
|
sequence.values.push(sequenceRows);
|
||||||
});
|
});
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
@ -205,16 +288,30 @@ function getRows(metadata, depth = 0) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// tag / vr/ keyword/ value
|
||||||
|
|
||||||
// Remove retired tags
|
// Remove retired tags
|
||||||
keyword = keyword.replace('RETIRED_', '');
|
keyword = keyword.replace('RETIRED_', '');
|
||||||
|
|
||||||
if (tagInfo) {
|
if (tagInfo) {
|
||||||
rows.push([`${tagIndent}${tagInfo.tag}`, tagInfo.vr, keyword, value]);
|
rows.push({
|
||||||
|
tag: tagInfo.tag,
|
||||||
|
tagIndent,
|
||||||
|
vr: tagInfo.vr,
|
||||||
|
keyword,
|
||||||
|
value,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
// Private tag
|
// Private tag
|
||||||
const tag = `(${keyword.substring(0, 4)},${keyword.substring(4, 8)})`;
|
const tag = `(${keyword.substring(0, 4)},${keyword.substring(4, 8)})`;
|
||||||
|
|
||||||
rows.push([`${tagIndent}${tag}`, '', 'Private Tag', value]);
|
rows.push({
|
||||||
|
tag,
|
||||||
|
tagIndent,
|
||||||
|
vr: '',
|
||||||
|
keyword: 'Private Tag',
|
||||||
|
value,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -225,4 +322,14 @@ function toArray(objectOrArray) {
|
|||||||
return Array.isArray(objectOrArray) ? objectOrArray : [objectOrArray];
|
return Array.isArray(objectOrArray) ? objectOrArray : [objectOrArray];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _sortTagList(tagList) {
|
||||||
|
tagList.sort((a, b) => {
|
||||||
|
if (a.tag < b.tag) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export default DicomTagBrowser;
|
export default DicomTagBrowser;
|
||||||
|
|||||||
@ -55,7 +55,7 @@ const appProps = {
|
|||||||
OHIFDicomSegmentationExtension,
|
OHIFDicomSegmentationExtension,
|
||||||
OHIFDicomRtExtension,
|
OHIFDicomRtExtension,
|
||||||
//[OHIFDebuggingExtension, { mailTo: 'support@canceridc.dev' }],
|
//[OHIFDebuggingExtension, { mailTo: 'support@canceridc.dev' }],
|
||||||
//OHIFDicomTagBrowserExtension,
|
OHIFDicomTagBrowserExtension,
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user