@@ -8,7 +8,8 @@ import { SeriesMetadata } from './SeriesMetadata';
88// - createStacks
99import { api } from 'dicomweb-client' ;
1010// - createStacks
11- import { isImage } from './../../utils/isImage' ;
11+ import { isImage } from '../../utils/isImage' ;
12+ import isLowPriorityModality from '../../utils/isLowPriorityModality' ;
1213
1314export class StudyMetadata extends Metadata {
1415 constructor ( data , uid ) {
@@ -88,6 +89,100 @@ export class StudyMetadata extends Metadata {
8889 return this . _displaySets . slice ( ) ;
8990 }
9091
92+ /**
93+ * Split a series metadata object into display sets
94+ * @param {Array } sopClassHandlerModules List of SOP Class Modules
95+ * @param {SeriesMetadata } series The series metadata object from which the display sets will be created
96+ * @param {Array } [givenDisplaySets] An optional list to which the display sets will be appended
97+ * @returns {Array } The list of display sets created for the given series object
98+ */
99+ _createDisplaySetsForSeries (
100+ sopClassHandlerModules ,
101+ series ,
102+ givenDisplaySets
103+ ) {
104+ const study = this ;
105+ const displaySets = Array . isArray ( givenDisplaySets ) ? givenDisplaySets : [ ] ;
106+ const anyInstances = series . getInstanceCount ( ) > 0 ;
107+
108+ if ( ! anyInstances ) {
109+ return ;
110+ }
111+
112+ const sopClassUids = getSopClassUids ( series ) ;
113+
114+ if ( sopClassHandlerModules && sopClassHandlerModules . length > 0 ) {
115+ const displaySet = _getDisplaySetFromSopClassModule (
116+ sopClassHandlerModules ,
117+ series ,
118+ study ,
119+ sopClassUids
120+ ) ;
121+ if ( displaySet ) {
122+ displaySet . sopClassModule = true ;
123+ displaySets . push ( displaySet ) ;
124+ return ;
125+ }
126+ }
127+
128+ // WE NEED A BETTER WAY TO NOTE THAT THIS IS THE DEFAULT BEHAVIOR FOR LOADING
129+ // A DISPLAY SET IF THERE IS NO MATCHING SOP CLASS PLUGIN
130+
131+ // Search through the instances (InstanceMetadata object) of this series
132+ // Split Multi-frame instances and Single-image modalities
133+ // into their own specific display sets. Place the rest of each
134+ // series into another display set.
135+ const stackableInstances = [ ] ;
136+ series . forEachInstance ( instance => {
137+ // All imaging modalities must have a valid value for sopClassUid (x00080016) or rows (x00280010)
138+ if (
139+ ! isImage ( instance . getRawValue ( 'x00080016' ) ) &&
140+ ! instance . getRawValue ( 'x00280010' )
141+ ) {
142+ return ;
143+ }
144+
145+ let displaySet ;
146+
147+ if ( isMultiFrame ( instance ) ) {
148+ displaySet = makeDisplaySet ( series , [ instance ] ) ;
149+ displaySet . setAttributes ( {
150+ sopClassUids,
151+ isClip : true ,
152+ seriesInstanceUid : series . getSeriesInstanceUID ( ) ,
153+ studyInstanceUid : study . getStudyInstanceUID ( ) , // Include the study instance Uid for drag/drop purposes
154+ numImageFrames : instance . getRawValue ( 'x00280008' ) , // Override the default value of instances.length
155+ instanceNumber : instance . getRawValue ( 'x00200013' ) , // Include the instance number
156+ acquisitionDatetime : instance . getRawValue ( 'x0008002a' ) , // Include the acquisition datetime
157+ } ) ;
158+ displaySets . push ( displaySet ) ;
159+ } else if ( isSingleImageModality ( instance . modality ) ) {
160+ displaySet = makeDisplaySet ( series , [ instance ] ) ;
161+ displaySet . setAttributes ( {
162+ sopClassUids,
163+ studyInstanceUid : study . getStudyInstanceUID ( ) , // Include the study instance Uid
164+ seriesInstanceUid : series . getSeriesInstanceUID ( ) ,
165+ instanceNumber : instance . getRawValue ( 'x00200013' ) , // Include the instance number
166+ acquisitionDatetime : instance . getRawValue ( 'x0008002a' ) , // Include the acquisition datetime
167+ } ) ;
168+ displaySets . push ( displaySet ) ;
169+ } else {
170+ stackableInstances . push ( instance ) ;
171+ }
172+ } ) ;
173+
174+ if ( stackableInstances . length ) {
175+ const displaySet = makeDisplaySet ( series , stackableInstances ) ;
176+ displaySet . setAttribute ( 'studyInstanceUid' , study . getStudyInstanceUID ( ) ) ;
177+ displaySet . setAttributes ( {
178+ sopClassUids,
179+ } ) ;
180+ displaySets . push ( displaySet ) ;
181+ }
182+
183+ return displaySets ;
184+ }
185+
91186 /**
92187 * Creates a set of series to be placed in the Study Metadata
93188 * The series that appear in the Study Metadata must represent
@@ -101,113 +196,55 @@ export class StudyMetadata extends Metadata {
101196 * @returns {Array } An array of series to be placed in the Study Metadata
102197 */
103198 createDisplaySets ( sopClassHandlerModules ) {
104- const study = this ;
105199 const displaySets = [ ] ;
106- const anyDisplaySets = study . getSeriesCount ( ) ;
107- const anySopClassHandlerModules =
108- sopClassHandlerModules && sopClassHandlerModules . length > 0 ;
200+ const anyDisplaySets = this . getSeriesCount ( ) ;
109201
110202 if ( ! anyDisplaySets ) {
111203 return displaySets ;
112204 }
113205
114206 // Loop through the series (SeriesMetadata)
115- this . forEachSeries ( series => {
116- const anyInstances = series . getInstanceCount ( ) > 0 ;
117- if ( ! anyInstances ) {
118- return ;
119- }
120-
121- const sopClassUids = getSopClassUids ( series ) ;
122-
123- if ( anySopClassHandlerModules ) {
124- const displaySet = _getDisplaySetFromSopClassModule (
207+ this . forEachSeries (
208+ series =>
209+ void this . _createDisplaySetsForSeries (
125210 sopClassHandlerModules ,
126211 series ,
127- study ,
128- sopClassUids
129- ) ;
130-
131- if ( displaySet ) {
132- displaySet . sopClassModule = true ;
133- displaySets . push ( displaySet ) ;
134-
135- return ;
136- }
137- }
138-
139- // WE NEED A BETTER WAY TO NOTE THAT THIS IS THE DEFAULT BEHAVIOR FOR LOADING
140- // A DISPLAY SET IF THERE IS NO MATCHING SOP CLASS PLUGIN
141-
142- // Search through the instances (InstanceMetadata object) of this series
143- // Split Multi-frame instances and Single-image modalities
144- // into their own specific display sets. Place the rest of each
145- // series into another display set.
146- const stackableInstances = [ ] ;
147- series . forEachInstance ( instance => {
148- // All imaging modalities must have a valid value for sopClassUid (x00080016) or rows (x00280010)
149- if (
150- ! isImage ( instance . getRawValue ( 'x00080016' ) ) &&
151- ! instance . getRawValue ( 'x00280010' )
152- ) {
153- return ;
154- }
155-
156- let displaySet ;
157-
158- if ( isMultiFrame ( instance ) ) {
159- displaySet = makeDisplaySet ( series , [ instance ] ) ;
160- displaySet . setAttributes ( {
161- sopClassUids,
162- isClip : true ,
163- seriesInstanceUid : series . getSeriesInstanceUID ( ) ,
164- studyInstanceUid : study . getStudyInstanceUID ( ) , // Include the study instance Uid for drag/drop purposes
165- numImageFrames : instance . getRawValue ( 'x00280008' ) , // Override the default value of instances.length
166- instanceNumber : instance . getRawValue ( 'x00200013' ) , // Include the instance number
167- acquisitionDatetime : instance . getRawValue ( 'x0008002a' ) , // Include the acquisition datetime
168- } ) ;
169- displaySets . push ( displaySet ) ;
170- } else if ( isSingleImageModality ( instance . modality ) ) {
171- displaySet = makeDisplaySet ( series , [ instance ] ) ;
172- displaySet . setAttributes ( {
173- sopClassUids,
174- studyInstanceUid : study . getStudyInstanceUID ( ) , // Include the study instance Uid
175- seriesInstanceUid : series . getSeriesInstanceUID ( ) ,
176- instanceNumber : instance . getRawValue ( 'x00200013' ) , // Include the instance number
177- acquisitionDatetime : instance . getRawValue ( 'x0008002a' ) , // Include the acquisition datetime
178- } ) ;
179- displaySets . push ( displaySet ) ;
180- } else {
181- stackableInstances . push ( instance ) ;
182- }
183- } ) ;
212+ displaySets
213+ )
214+ ) ;
184215
185- if ( stackableInstances . length ) {
186- const displaySet = makeDisplaySet ( series , stackableInstances ) ;
187- displaySet . setAttribute (
188- 'studyInstanceUid' ,
189- study . getStudyInstanceUID ( )
190- ) ;
191- displaySet . setAttributes ( {
192- sopClassUids,
193- } ) ;
194- displaySets . push ( displaySet ) ;
195- }
196- } ) ;
216+ return sortDisplaySetList ( displaySets ) ;
217+ }
197218
198- // TODO
199- displaySets . sort ( _sortBySeriesNumber ) ;
200- displaySets . sort ( _sortSegToEndOfList ) ;
219+ sortDisplaySets ( ) {
220+ sortDisplaySetList ( this . _displaySets ) ;
221+ }
201222
202- return displaySets ;
223+ /**
224+ * Method to append display sets from a given series to the internal list of display sets
225+ * @param {Array } sopClassHandlerModules A list of SOP Class Handler Modules
226+ * @param {SeriesMetadata } series The series metadata object from which the display sets will be created
227+ * @returns {boolean } Returns true on success or false on failure (e.g., the series does not belong to this study)
228+ */
229+ createAndAddDisplaySetsForSeries ( sopClassHandlerModules , series ) {
230+ if ( this . containsSeries ( series ) ) {
231+ this . setDisplaySets (
232+ this . _createDisplaySetsForSeries ( sopClassHandlerModules , series )
233+ ) ;
234+ return true ;
235+ }
236+ return false ;
203237 }
204238
205239 /**
206240 * Set display sets
207241 * @param {Array } displaySets Array of display sets (ImageSet[])
208242 */
209243 setDisplaySets ( displaySets ) {
210- displaySets . forEach ( displaySet => this . addDisplaySet ( displaySet ) ) ;
244+ if ( Array . isArray ( displaySets ) && displaySets . length > 0 ) {
245+ displaySets . forEach ( displaySet => this . addDisplaySet ( displaySet ) ) ;
246+ this . sortDisplaySets ( ) ;
247+ }
211248 }
212249
213250 /**
@@ -321,6 +358,12 @@ export class StudyMetadata extends Metadata {
321358 return found ;
322359 }
323360
361+ containsSeries ( series ) {
362+ return (
363+ series instanceof SeriesMetadata && this . _series . indexOf ( series ) >= 0
364+ ) ;
365+ }
366+
324367 /**
325368 * Retrieve the number of series within the current study.
326369 * @returns {number } The number of series in the current study.
@@ -651,34 +694,51 @@ function _getDisplaySetFromSopClassModule(
651694}
652695
653696/**
697+ * Sort series primarily by modality (i.e., series with references to other
698+ * series like SEG, KO or PR are grouped in the end of the list) and then by
699+ * series number:
700+ *
701+ * --------
702+ * | CT #3 |
703+ * | CT #4 |
704+ * | CT #5 |
705+ * --------
706+ * | SEG #1 |
707+ * | SEG #2 |
708+ * --------
654709 *
655710 * @param {* } a - DisplaySet
656711 * @param {* } b - DisplaySet
657712 */
658- function _sortBySeriesNumber ( a , b ) {
659- const seriesNumberAIsGreaterOrUndefined =
660- a . seriesNumber > b . seriesNumber || ( ! a . seriesNumber && b . seriesNumber ) ;
661713
662- return seriesNumberAIsGreaterOrUndefined ? 1 : - 1 ;
714+ function seriesSortingCriteria ( a , b ) {
715+ const isLowPriorityA = isLowPriorityModality ( a . modality ) ;
716+ const isLowPriorityB = isLowPriorityModality ( b . modality ) ;
717+ if ( ! isLowPriorityA && isLowPriorityB ) {
718+ return - 1 ;
719+ }
720+ if ( isLowPriorityA && ! isLowPriorityB ) {
721+ return 1 ;
722+ }
723+ return sortBySeriesNumber ( a , b ) ;
663724}
664725
665726/**
666- * Move Segmentation modality files to the end of the list of
667- * display sets. This is a workaround to prevent issues when
668- * the referenced dataset's metadata is not yet available.
669- *
670- * It will be removed once proper SEG ingestion is added.
671- *
727+ * Sort series by series number. Series with low
672728 * @param {* } a - DisplaySet
673729 * @param {* } b - DisplaySet
674730 */
675- function _sortSegToEndOfList ( a , b ) {
676- const displaySetAIsSeg = a . modality === 'SEG' ;
677- const displaySetBIsSeg = b . modality === 'SEG' ;
731+ function sortBySeriesNumber ( a , b ) {
732+ const seriesNumberAIsGreaterOrUndefined =
733+ a . seriesNumber > b . seriesNumber || ( ! a . seriesNumber && b . seriesNumber ) ;
678734
679- if ( displaySetAIsSeg && displaySetBIsSeg ) {
680- return 0 ;
681- }
735+ return seriesNumberAIsGreaterOrUndefined ? 1 : - 1 ;
736+ }
682737
683- return displaySetAIsSeg ? 1 : - 1 ;
684- }
738+ /**
739+ * Sorts a list of display set objects
740+ * @param {Array } list A list of display sets to be sorted
741+ */
742+ function sortDisplaySetList ( list ) {
743+ return list . sort ( seriesSortingCriteria ) ;
744+ }
0 commit comments