[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:
James Petts 2020-09-24 11:34:22 +01:00 committed by GitHub
parent 6cf72d1757
commit ccdfcd4a85
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 187 additions and 78 deletions

View File

@ -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,

View File

@ -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 {

View File

@ -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);

View File

@ -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;

View File

@ -55,7 +55,7 @@ const appProps = {
OHIFDicomSegmentationExtension, OHIFDicomSegmentationExtension,
OHIFDicomRtExtension, OHIFDicomRtExtension,
//[OHIFDebuggingExtension, { mailTo: 'support@canceridc.dev' }], //[OHIFDebuggingExtension, { mailTo: 'support@canceridc.dev' }],
//OHIFDicomTagBrowserExtension, OHIFDicomTagBrowserExtension,
], ],
}; };