diff --git a/extensions/dicom-html/src/OHIFDicomHtmlSopClassHandler.js b/extensions/dicom-html/src/OHIFDicomHtmlSopClassHandler.js
index cf3ecccf5..f94ea055c 100644
--- a/extensions/dicom-html/src/OHIFDicomHtmlSopClassHandler.js
+++ b/extensions/dicom-html/src/OHIFDicomHtmlSopClassHandler.js
@@ -22,6 +22,12 @@ const OHIFDicomHtmlSopClassHandler = {
getDisplaySetFromSeries(series, study, dicomWebClient, authorizationHeaders) {
const instance = series.getFirstInstance();
+ const {
+ SeriesDate,
+ SeriesTime,
+ SeriesNumber,
+ } = instance._instance.metadata;
+
return {
plugin: 'html',
Modality: 'SR',
@@ -31,6 +37,9 @@ const OHIFDicomHtmlSopClassHandler = {
SOPInstanceUID: instance.getSOPInstanceUID(),
SeriesInstanceUID: series.getSeriesInstanceUID(),
StudyInstanceUID: study.getStudyInstanceUID(),
+ SeriesDate,
+ SeriesTime,
+ SeriesNumber,
authorizationHeaders,
};
},
diff --git a/extensions/dicom-microscopy/src/DicomMicroscopySopClassHandler.js b/extensions/dicom-microscopy/src/DicomMicroscopySopClassHandler.js
index cef4e49d1..909e39bf2 100644
--- a/extensions/dicom-microscopy/src/DicomMicroscopySopClassHandler.js
+++ b/extensions/dicom-microscopy/src/DicomMicroscopySopClassHandler.js
@@ -12,6 +12,12 @@ const DicomMicroscopySopClassHandler = {
getDisplaySetFromSeries(series, study, dicomWebClient) {
const instance = series.getFirstInstance();
+ const {
+ ContentDate,
+ ContentTime,
+ SeriesNumber,
+ } = instance._instance.metadata;
+
// Note: We are passing the dicomweb client into each viewport!
return {
@@ -22,6 +28,9 @@ const DicomMicroscopySopClassHandler = {
SOPInstanceUID: instance.getSOPInstanceUID(),
SeriesInstanceUID: series.getSeriesInstanceUID(),
StudyInstanceUID: study.getStudyInstanceUID(),
+ SeriesDate: ContentDate, // Map ContentDate/Time to SeriesTime for series list sorting.
+ SeriesTime: ContentTime,
+ SeriesNumber,
};
},
};
diff --git a/extensions/dicom-pdf/src/OHIFDicomPDFSopClassHandler.js b/extensions/dicom-pdf/src/OHIFDicomPDFSopClassHandler.js
index c07f1c39c..9550ed80f 100644
--- a/extensions/dicom-pdf/src/OHIFDicomPDFSopClassHandler.js
+++ b/extensions/dicom-pdf/src/OHIFDicomPDFSopClassHandler.js
@@ -12,6 +12,12 @@ const OHIFDicomPDFSopClassHandler = {
getDisplaySetFromSeries(series, study, dicomWebClient, authorizationHeaders) {
const instance = series.getFirstInstance();
+ const {
+ ContentDate,
+ ContentTime,
+ SeriesNumber,
+ } = instance._instance.metadata;
+
return {
plugin: 'pdf',
Modality: 'DOC',
@@ -21,6 +27,9 @@ const OHIFDicomPDFSopClassHandler = {
SOPInstanceUID: instance.getSOPInstanceUID(),
SeriesInstanceUID: series.getSeriesInstanceUID(),
StudyInstanceUID: study.getStudyInstanceUID(),
+ SeriesDate: ContentDate, // Map ContentDate/Time to SeriesTime for series list sorting.
+ SeriesTime: ContentTime,
+ SeriesNumber,
authorizationHeaders: authorizationHeaders,
};
},
diff --git a/extensions/dicom-rt/src/OHIFDicomRTStructSopClassHandler.js b/extensions/dicom-rt/src/OHIFDicomRTStructSopClassHandler.js
index 5095e82bf..0aae17761 100644
--- a/extensions/dicom-rt/src/OHIFDicomRTStructSopClassHandler.js
+++ b/extensions/dicom-rt/src/OHIFDicomRTStructSopClassHandler.js
@@ -27,6 +27,7 @@ const OHIFDicomRTStructSopClassHandler = {
const {
SeriesDate,
SeriesTime,
+ SeriesNumber,
SeriesDescription,
FrameOfReferenceUID,
SOPInstanceUID,
@@ -53,6 +54,7 @@ const OHIFDicomRTStructSopClassHandler = {
isLoaded: false,
SeriesDate,
SeriesTime,
+ SeriesNumber,
SeriesDescription,
};
diff --git a/extensions/dicom-segmentation/src/getOHIFDicomSegSopClassHandler.js b/extensions/dicom-segmentation/src/getOHIFDicomSegSopClassHandler.js
index a396ad92d..83f84ed42 100644
--- a/extensions/dicom-segmentation/src/getOHIFDicomSegSopClassHandler.js
+++ b/extensions/dicom-segmentation/src/getOHIFDicomSegSopClassHandler.js
@@ -28,6 +28,7 @@ export default function getSopClassHandlerModule({ servicesManager }) {
const {
SeriesDate,
SeriesTime,
+ SeriesNumber,
SeriesDescription,
FrameOfReferenceUID,
SOPInstanceUID,
@@ -52,6 +53,7 @@ export default function getSopClassHandlerModule({ servicesManager }) {
isLoaded: false,
SeriesDate,
SeriesTime,
+ SeriesNumber,
SeriesDescription,
};
diff --git a/platform/core/src/classes/OHIFStudyMetadataSource.js b/platform/core/src/classes/OHIFStudyMetadataSource.js
index 4e81f4113..8c7e82b79 100644
--- a/platform/core/src/classes/OHIFStudyMetadataSource.js
+++ b/platform/core/src/classes/OHIFStudyMetadataSource.js
@@ -54,9 +54,6 @@ export class OHIFStudyMetadataSource extends StudyMetadataSource {
// Get Study display sets
const displaySets = studyMetadata.createDisplaySets();
- // Set studyMetadata display sets
- studyMetadata.setDisplaySets(displaySets);
-
OHIFStudyMetadataSource._updateStudyCollections(studyMetadata);
resolve(studyMetadata);
})
diff --git a/platform/core/src/classes/metadata/StudyMetadata.js b/platform/core/src/classes/metadata/StudyMetadata.js
index c8d02cac7..06810710b 100644
--- a/platform/core/src/classes/metadata/StudyMetadata.js
+++ b/platform/core/src/classes/metadata/StudyMetadata.js
@@ -10,8 +10,8 @@ import { api } from 'dicomweb-client';
// - createStacks
import { isImage } from '../../utils/isImage';
import isDisplaySetReconstructable from '../../utils/isDisplaySetReconstructable';
-import isLowPriorityModality from '../../utils/isLowPriorityModality';
import errorHandler from '../../errorHandler';
+import isLowPriorityModality from '../../utils/isLowPriorityModality';
export class StudyMetadata extends Metadata {
constructor(data, uid) {
@@ -308,14 +308,10 @@ export class StudyMetadata extends Metadata {
series
);
- displaySets.push(...displaySetsForSeries);
+ displaySetsForSeries.forEach(ds => this._insertDisplaySet(ds));
});
- return sortDisplaySetList(displaySets);
- }
-
- sortDisplaySets() {
- sortDisplaySetList(this._displaySets);
+ return this._displaySets;
}
/**
@@ -346,25 +342,9 @@ export class StudyMetadata extends Metadata {
this.addDisplaySet(displaySet);
});
- this.sortDisplaySets();
-
return true;
}
- /**
- * Set display sets
- * @param {Array} displaySets Array of display sets (ImageSet[])
- */
- setDisplaySets(displaySets) {
- if (Array.isArray(displaySets) && displaySets.length > 0) {
- // TODO: This is weird, can we just switch it to writable: true?
- this._displaySets.splice(0);
-
- displaySets.forEach(displaySet => this.addDisplaySet(displaySet));
- this.sortDisplaySets();
- }
- }
-
/**
* Add a single display set to the list
* @param {Object} displaySet Display set object
@@ -372,7 +352,7 @@ export class StudyMetadata extends Metadata {
*/
addDisplaySet(displaySet) {
if (displaySet instanceof ImageSet || displaySet.sopClassModule) {
- this._displaySets.push(displaySet);
+ this._insertDisplaySet(displaySet);
return true;
}
return false;
@@ -393,6 +373,96 @@ export class StudyMetadata extends Metadata {
}
}
+ /**
+ * Insert the displaySet so that the list has an increasing SeriesNumber,
+ * with the most recent series first for displaySets with the same SeriesNumber.
+ *
+ * If the displaySet is low priority, the same logic is applied, but is sorted within a sub list
+ * At the end of the list, where all low priority data is found.
+ */
+ _insertDisplaySet(displaySet) {
+ const { SeriesNumber } = displaySet;
+ const displaySets = this._displaySets;
+ let insertIndex = displaySets.length;
+ let firstIndexWithSameSeriesNumber;
+
+ // If low priority, start search from next low priority.
+ if (isLowPriorityModality(displaySet.Modality)) {
+ let startingIndex;
+
+ // Find where the first low priority displaySet is.
+ for (let i = 0; i < displaySets.length; i++) {
+ if (isLowPriorityModality(displaySets[i].Modality)) {
+ startingIndex = i;
+ break;
+ }
+ }
+
+ if (!startingIndex) {
+ startingIndex = displaySets.length;
+ }
+
+ // Find the correct SeriesNumber location to insert within the low priority
+ // Modality displaySets
+ for (let i = startingIndex; i < displaySets.length; i++) {
+ if (
+ displaySets[i].SeriesNumber === SeriesNumber &&
+ !firstIndexWithSameSeriesNumber
+ ) {
+ firstIndexWithSameSeriesNumber = i;
+ }
+
+ if (displaySets[i].SeriesNumber > SeriesNumber) {
+ insertIndex = i;
+ break;
+ }
+ }
+ } else {
+ // Find correct SeriesNumber to insert or where the low priority modalities start.
+ for (let i = 0; i < displaySets.length; i++) {
+ if (
+ displaySets[i].SeriesNumber === SeriesNumber &&
+ !firstIndexWithSameSeriesNumber
+ ) {
+ firstIndexWithSameSeriesNumber = i;
+ }
+
+ if (
+ displaySets[i].SeriesNumber > SeriesNumber ||
+ isLowPriorityModality(displaySets[i].Modality)
+ ) {
+ insertIndex = i;
+ break;
+ }
+ }
+ }
+
+ // If we have multiple displaySets with the same series number, find the insert position based on
+ // SeriesDate and SeriesTime.
+ if (firstIndexWithSameSeriesNumber !== undefined) {
+ // If no SeriesDate, is just a placeholder displaySet, just insert anywhere, it will be re-added later.
+ if (displaySet.SeriesDate) {
+ const seriesDateTime = `${displaySet.SeriesDate}${displaySet.SeriesTime}`;
+
+ for (let i = firstIndexWithSameSeriesNumber; i < insertIndex; i++) {
+ const displaySetI = displaySets[i];
+
+ if (
+ displaySetI.SeriesDate &&
+ `${displaySetI.SeriesDate}${displaySetI.SeriesTime}` <
+ seriesDateTime
+ ) {
+ insertIndex = i;
+ break;
+ }
+ }
+ }
+ }
+
+ this._displaySets.splice(insertIndex, 0, displaySet);
+ this.displaySets = this._displaySets;
+ }
+
/**
* Search the associated display sets using the supplied callback as criteria. The callback is passed
* two arguments: display set (an ImageSet instance) and index (the integer
@@ -548,49 +618,6 @@ export class StudyMetadata extends Metadata {
return this._series.indexOf(series);
}
- /**
- * It sorts the series based on display sets order. Each series must be an instance
- * of SeriesMetadata and each display sets must be an instance of ImageSet.
- * Useful example of usage:
- * Study data provided by backend does not sort series at all and client-side
- * needs series sorted by the same criteria used for sorting display sets.
- */
- sortSeriesByDisplaySets() {
- // Object for mapping display sets' index by SeriesInstanceUID
- const displaySetsMapping = {};
-
- // Loop through each display set to create the mapping
- this.forEachDisplaySet((displaySet, index) => {
- if (!(displaySet instanceof ImageSet)) {
- throw new OHIFError(
- `StudyMetadata::sortSeriesByDisplaySets display set at index ${index} is not an instance of ImageSet`
- );
- }
-
- // In case of multiframe studies, just get the first index occurence
- if (displaySetsMapping[displaySet.SeriesInstanceUID] === void 0) {
- displaySetsMapping[displaySet.SeriesInstanceUID] = index;
- }
- });
-
- // Clone of actual series
- const actualSeries = this.getSeries();
-
- actualSeries.forEach((series, index) => {
- if (!(series instanceof SeriesMetadata)) {
- throw new OHIFError(
- `StudyMetadata::sortSeriesByDisplaySets series at index ${index} is not an instance of SeriesMetadata`
- );
- }
-
- // Get the new series index
- const seriesIndex = displaySetsMapping[series.getSeriesInstanceUID()];
-
- // Update the series object with the new series position
- this._series[seriesIndex] = series;
- });
- }
-
/**
* Compares the current study instance with another one.
* @param {StudyMetadata} study An instance of the StudyMetadata class.
@@ -865,53 +892,3 @@ function _getDisplaySetFromSopClassModule(
}
return displaySet;
}
-
-/**
- * Sort series primarily by Modality (i.e., series with references to other
- * series like SEG, KO or PR are grouped in the end of the list) and then by
- * series number:
- *
- * --------
- * | CT #3 |
- * | CT #4 |
- * | CT #5 |
- * --------
- * | SEG #1 |
- * | SEG #2 |
- * --------
- *
- * @param {*} a - DisplaySet
- * @param {*} b - DisplaySet
- */
-
-function seriesSortingCriteria(a, b) {
- const isLowPriorityA = isLowPriorityModality(a.Modality);
- const isLowPriorityB = isLowPriorityModality(b.Modality);
- if (!isLowPriorityA && isLowPriorityB) {
- return -1;
- }
- if (isLowPriorityA && !isLowPriorityB) {
- return 1;
- }
- return sortBySeriesNumber(a, b);
-}
-
-/**
- * Sort series by series number. Series with low
- * @param {*} a - DisplaySet
- * @param {*} b - DisplaySet
- */
-function sortBySeriesNumber(a, b) {
- const seriesNumberAIsGreaterOrUndefined =
- a.SeriesNumber > b.SeriesNumber || (!a.SeriesNumber && b.SeriesNumber);
-
- return seriesNumberAIsGreaterOrUndefined ? 1 : -1;
-}
-
-/**
- * Sorts a list of display set objects
- * @param {Array} list A list of display sets to be sorted
- */
-function sortDisplaySetList(list) {
- return list.sort(seriesSortingCriteria);
-}
diff --git a/platform/core/src/utils/isLowPriorityModality.js b/platform/core/src/utils/isLowPriorityModality.js
index d2794a8ce..42a5c3040 100644
--- a/platform/core/src/utils/isLowPriorityModality.js
+++ b/platform/core/src/utils/isLowPriorityModality.js
@@ -1,4 +1,11 @@
-const LOW_PRIORITY_MODALITIES = Object.freeze(['SEG', 'KO', 'PR']);
+const LOW_PRIORITY_MODALITIES = Object.freeze([
+ 'SEG',
+ 'DOC',
+ 'RTSTRUCT',
+ 'SR',
+ 'KO',
+ 'PR',
+]);
export default function isLowPriorityModality(Modality) {
return LOW_PRIORITY_MODALITIES.includes(Modality);
diff --git a/platform/viewer/src/connectedComponents/Viewer.js b/platform/viewer/src/connectedComponents/Viewer.js
index 632d2909b..08d70d741 100644
--- a/platform/viewer/src/connectedComponents/Viewer.js
+++ b/platform/viewer/src/connectedComponents/Viewer.js
@@ -19,6 +19,7 @@ import UserManagerContext from '../context/UserManagerContext';
import AppContext from '../context/AppContext';
import './Viewer.css';
+import { finished } from 'stream';
class Viewer extends Component {
static propTypes = {
@@ -189,6 +190,7 @@ class Viewer extends Component {
currentTimepointId,
]);
}
+
this.setState({
thumbnails: _mapStudiesToThumbnails(studies),
});
@@ -197,6 +199,7 @@ class Viewer extends Component {
componentDidUpdate(prevProps) {
const { studies, isStudyLoaded } = this.props;
+
if (studies !== prevProps.studies) {
this.setState({
thumbnails: _mapStudiesToThumbnails(studies),
@@ -355,7 +358,6 @@ export default withDialog(Viewer);
* a mapping layer?
*
* TODO[react]:
- * - Add sorting of display sets
* - Add showStackLoadingProgressBar option
*
* @param {Study[]} studies
@@ -369,9 +371,9 @@ const _mapStudiesToThumbnails = function(studies) {
const {
displaySetInstanceUID,
SeriesDescription,
- SeriesNumber,
InstanceNumber,
numImageFrames,
+ SeriesNumber,
} = displaySet;
let imageId;
@@ -395,9 +397,9 @@ const _mapStudiesToThumbnails = function(studies) {
altImageText,
displaySetInstanceUID,
SeriesDescription,
- SeriesNumber,
InstanceNumber,
numImageFrames,
+ SeriesNumber,
};
});
diff --git a/platform/viewer/src/connectedComponents/ViewerLocalFileData.js b/platform/viewer/src/connectedComponents/ViewerLocalFileData.js
index c0e93d979..9d211f2d8 100644
--- a/platform/viewer/src/connectedComponents/ViewerLocalFileData.js
+++ b/platform/viewer/src/connectedComponents/ViewerLocalFileData.js
@@ -77,7 +77,8 @@ class ViewerLocalFileData extends Component {
study.displaySets =
study.displaySets ||
studyMetadata.createDisplaySets(sopClassHandlerModules);
- studyMetadata.setDisplaySets(study.displaySets);
+
+ debugger;
studyMetadata.forEachDisplaySet(displayset => {
displayset.localFile = true;
diff --git a/platform/viewer/src/connectedComponents/ViewerRetrieveStudyData.js b/platform/viewer/src/connectedComponents/ViewerRetrieveStudyData.js
index 5a4646ff0..1d003cf9e 100644
--- a/platform/viewer/src/connectedComponents/ViewerRetrieveStudyData.js
+++ b/platform/viewer/src/connectedComponents/ViewerRetrieveStudyData.js
@@ -47,7 +47,6 @@ const _promoteList = (study, studyMetadata, filters, isFilterStrategy) => {
let promoted = false;
// Promote only if no filter should be applied
if (!isFilterStrategy) {
- _sortStudyDisplaySet(study, studyMetadata);
promoted = _promoteStudyDisplaySet(study, studyMetadata, filters);
}
@@ -193,12 +192,6 @@ const _updateStudyDisplaySets = (study, studyMetadata) => {
if (study.derivedDisplaySets) {
studyMetadata._addDerivedDisplaySets(study.derivedDisplaySets);
}
-
- studyMetadata.setDisplaySets(study.displaySets);
-};
-
-const _sortStudyDisplaySet = (study, studyMetadata) => {
- studyMetadata.sortDisplaySets(study.displaySets);
};
const _thinStudyData = study => {
diff --git a/platform/viewer/src/routes/StandaloneRouting.js b/platform/viewer/src/routes/StandaloneRouting.js
index b2a3038fa..82ee9fa54 100644
--- a/platform/viewer/src/routes/StandaloneRouting.js
+++ b/platform/viewer/src/routes/StandaloneRouting.js
@@ -194,7 +194,6 @@ const _mapStudiesToNewFormat = studies => {
study.displaySets =
study.displaySets ||
studyMetadata.createDisplaySets(sopClassHandlerModules);
- studyMetadata.setDisplaySets(study.displaySets);
studyMetadataManager.add(studyMetadata);
uniqueStudyUIDs.add(study.StudyInstanceUID);