@@ -676,14 +676,17 @@ function _getColorPaletteStyleForPointLayer ({
676676 const expression = [
677677 'palette' ,
678678 indexExpression ,
679- colormap . map ( c => rgb2hex ( c ) )
679+ colormap
680680 ]
681681
682682 return { color : expression }
683683}
684684
685685const _affine = Symbol ( 'affine' )
686686const _affineInverse = Symbol ( 'affineInverse' )
687+ const _annotationManager = Symbol ( 'annotationManager' )
688+ const _annotationGroups = Symbol ( 'annotationGroups' )
689+ const _areIccProfilesFetched = Symbol ( 'areIccProfilesFetched' )
687690const _controls = Symbol ( 'controls' )
688691const _drawingLayer = Symbol ( 'drawingLayer' )
689692const _drawingSource = Symbol ( 'drawingSource' )
@@ -693,17 +696,15 @@ const _interactions = Symbol('interactions')
693696const _map = Symbol ( 'map' )
694697const _mappings = Symbol ( 'mappings' )
695698const _metadata = Symbol ( 'metadata' )
696- const _areIccProfilesFetched = Symbol ( 'areIccProfilesFetched ' )
699+ const _opticalPaths = Symbol ( 'opticalPaths ' )
697700const _options = Symbol ( 'options' )
701+ const _overlays = Symbol ( 'overlays' )
702+ const _overviewMap = Symbol ( 'overviewMap' )
703+ const _projection = Symbol ( 'projection' )
698704const _pyramid = Symbol ( 'pyramid' )
699705const _segments = Symbol ( 'segments' )
700- const _opticalPaths = Symbol ( 'opticalPaths' )
701706const _rotation = Symbol ( 'rotation' )
702- const _projection = Symbol ( 'projection' )
703707const _tileGrid = Symbol ( 'tileGrid' )
704- const _annotationManager = Symbol ( 'annotationManager' )
705- const _annotationGroups = Symbol ( 'annotationGroups' )
706- const _overviewMap = Symbol ( 'overviewMap' )
707708const _updateOverviewMapSize = Symbol ( 'updateOverviewMapSize' )
708709
709710/**
@@ -731,6 +732,10 @@ class VolumeImageViewer {
731732 * turned on (e.g., display of tile boundaries)
732733 * @param {number } [options.tilesCacheSize=1000] - Number of tiles that should
733734 * be cached to avoid repeated retrieval for the DICOMweb server
735+ * @param {number[] } [options.primaryColor=[0, 126, 163]] - Primary color of
736+ * the application
737+ * @param {number[] } [options.highlightColor=[140, 184, 198]] - Color that
738+ * should be used to highlight things that get selected by the user
734739 */
735740 constructor ( options ) {
736741 this [ _options ] = options
@@ -756,6 +761,13 @@ class VolumeImageViewer {
756761 }
757762 this [ _options ] . controls = new Set ( this [ _options ] . controls )
758763
764+ if ( this [ _options ] . primaryColor == null ) {
765+ this [ _options ] . primaryColor = [ 0 , 126 , 163 ]
766+ }
767+ if ( this [ _options ] . highlightColor == null ) {
768+ this [ _options ] . highlightColor = [ 140 , 184 , 198 ]
769+ }
770+
759771 // Collection of Openlayers "TileLayer" instances
760772 this [ _segments ] = { }
761773 this [ _mappings ] = { }
@@ -1473,8 +1485,8 @@ class VolumeImageViewer {
14731485 opticalPath . overviewLayer . setStyle ( style )
14741486 } else {
14751487 const styleVariables = {
1476- windowCenter : windowCenter ,
1477- windowWidth : windowWidth ,
1488+ windowCenter,
1489+ windowWidth,
14781490 red : opticalPath . style . color [ 0 ] ,
14791491 green : opticalPath . style . color [ 1 ] ,
14801492 blue : opticalPath . style . color [ 2 ]
@@ -2950,7 +2962,108 @@ class VolumeImageViewer {
29502962
29512963 const defaultAnnotationGroupStyle = {
29522964 opacity : 1.0 ,
2953- color : [ 2 , 126 , 163 ]
2965+ color : this [ _options ] . primaryColor
2966+ }
2967+
2968+ // We need to bind those variables to constants for the loader function
2969+ const client = this [ _options ] . client
2970+ const pyramid = this [ _pyramid ] . metadata
2971+ const affineInverse = this [ _affineInverse ]
2972+ const container = this [ _map ] . getTargetElement ( )
2973+ const _getROIFromFeature = ( feature ) => {
2974+ const roi = this . _getROIFromFeature (
2975+ feature ,
2976+ this [ _pyramid ] . metadata ,
2977+ this [ _affine ]
2978+ )
2979+ const annotationGroupUID = feature . get ( 'annotationGroupUID' )
2980+ const annotationGroupMetadata = metadata . AnnotationGroupSequence . find (
2981+ item => item . AnnotationGroupUID === annotationGroupUID
2982+ )
2983+
2984+ const findingCategory = (
2985+ annotationGroupMetadata
2986+ . AnnotationPropertyCategoryCodeSequence [ 0 ]
2987+ )
2988+ roi . addEvaluation (
2989+ new dcmjs . sr . valueTypes . CodeContentItem ( {
2990+ name : new dcmjs . sr . coding . CodedConcept ( {
2991+ value : '276214006' ,
2992+ meaning : 'Finding category' ,
2993+ schemeDesignator : 'SCT'
2994+ } ) ,
2995+ value : new dcmjs . sr . coding . CodedConcept ( {
2996+ value : findingCategory . CodeValue ,
2997+ meaning : findingCategory . CodeMeaning ,
2998+ schemeDesignator : findingCategory . CodingSchemeDesignator
2999+ } ) ,
3000+ relationshipType : dcmjs . sr . valueTypes . RelationshipTypes . HAS_CONCEPT_MOD
3001+ } )
3002+ )
3003+ const findingType = (
3004+ annotationGroupMetadata
3005+ . AnnotationPropertyTypeCodeSequence [ 0 ]
3006+ )
3007+ roi . addEvaluation (
3008+ new dcmjs . sr . valueTypes . CodeContentItem ( {
3009+ name : new dcmjs . sr . coding . CodedConcept ( {
3010+ value : '121071' ,
3011+ meaning : 'Finding' ,
3012+ schemeDesignator : 'DCM'
3013+ } ) ,
3014+ value : new dcmjs . sr . coding . CodedConcept ( {
3015+ value : findingType . CodeValue ,
3016+ meaning : findingType . CodeMeaning ,
3017+ schemeDesignator : findingType . CodingSchemeDesignator
3018+ } ) ,
3019+ relationshipType : dcmjs . sr . valueTypes . RelationshipTypes . HAS_CONCEPT_MOD
3020+ } )
3021+ )
3022+
3023+ annotationGroupMetadata . MeasurementsSequence . forEach (
3024+ ( measurementItem , measurementIndex ) => {
3025+ const key = `measurementValue${ measurementIndex . toString ( ) } `
3026+ const value = feature . get ( key )
3027+ const name = measurementItem . ConceptNameCodeSequence [ 0 ]
3028+ const unit = measurementItem . MeasurementUnitsCodeSequence [ 0 ]
3029+
3030+ const measurement = new dcmjs . sr . valueTypes . NumContentItem ( {
3031+ value : Number ( value ) ,
3032+ name : new dcmjs . sr . coding . CodedConcept ( {
3033+ value : name . CodeValue ,
3034+ meaning : name . CodeMeaning ,
3035+ schemeDesignator : name . CodingSchemeDesignator
3036+ } ) ,
3037+ unit : new dcmjs . sr . coding . CodedConcept ( {
3038+ value : unit . CodeValue ,
3039+ meaning : unit . CodeMeaning ,
3040+ schemeDesignator : unit . CodingSchemeDesignator
3041+ } ) ,
3042+ relationshipType : dcmjs . sr . valueTypes . RelationshipTypes . CONTAINS
3043+ } )
3044+ if ( measurementItem . ReferencedImageSequence != null ) {
3045+ const ref = measurementItem . ReferencedImageSequence [ 0 ]
3046+ const image = new dcmjs . sr . valueTypes . ImageContentItem ( {
3047+ name : new dcmjs . sr . coding . CodedConcept ( {
3048+ value : '121112' ,
3049+ meaning : 'Source of Measurement' ,
3050+ schemeDesignator : 'DCM'
3051+ } ) ,
3052+ referencedSOPClassUID : ref . ReferencedSOPClassUID ,
3053+ referencedSOPInstanceUID : ref . ReferencedSOPInstanceUID
3054+ } )
3055+ if ( ref . ReferencedOpticalPathIdentifier != null ) {
3056+ image . ReferencedSOPSequence [ 0 ] . ReferencedOpticalPathIdentifier = (
3057+ ref . ReferencedOpticalPathIdentifier
3058+ )
3059+ }
3060+ measurement . ContentSequence = [ image ]
3061+ }
3062+ roi . addMeasurement ( measurement )
3063+ }
3064+ )
3065+
3066+ return roi
29543067 }
29553068
29563069 metadata . AnnotationGroupSequence . forEach ( ( item , index ) => {
@@ -2971,7 +3084,7 @@ class VolumeImageViewer {
29713084 } ) ,
29723085 style : { ...defaultAnnotationGroupStyle } ,
29733086 defaultStyle : defaultAnnotationGroupStyle ,
2974- metadata : metadata
3087+ metadata
29753088 }
29763089
29773090 if ( item . GraphicType === 'POLYLINE' ) {
@@ -2986,11 +3099,6 @@ class VolumeImageViewer {
29863099 return
29873100 }
29883101
2989- // We need to bind those variables to constants for the loader function
2990- const client = this [ _options ] . client
2991- const pyramid = this [ _pyramid ] . metadata
2992- const affineInverse = this [ _affineInverse ]
2993-
29943102 /**
29953103 * In the loader function "this" is bound to the vector source.
29963104 */
@@ -3053,13 +3161,16 @@ class VolumeImageViewer {
30533161 const feature = new Feature ( {
30543162 geometry : new PointGeometry ( coordinates )
30553163 } )
3056- const properties = { }
3164+
3165+ feature . set ( 'annotationGroupUID' , annotationGroupUID , true )
30573166 measurements . forEach ( ( measurementItem , measurementIndex ) => {
3167+ const key = `measurementValue${ measurementIndex . toString ( ) } `
30583168 const value = measurementItem . values [ i ]
3059- properties [ measurementIndex ] = value
3169+ // Needed for the WebGL renderer
3170+ feature . set ( key , value , true )
30603171 } )
3061- feature . setProperties ( properties , true )
3062- feature . setId ( i + 1 )
3172+ const uid = _generateUID ( ` ${ annotationGroupUID } - ${ i } ` )
3173+ feature . setId ( uid )
30633174 features . push ( feature )
30643175 }
30653176
@@ -3086,7 +3197,8 @@ class VolumeImageViewer {
30863197 ( a , b ) => Math . max ( a , b ) ,
30873198 - Infinity
30883199 )
3089- properties [ measurementIndex ] = { min, max }
3200+ const key = `measurementValue${ measurementIndex . toString ( ) } `
3201+ properties [ key ] = { min, max }
30903202 } )
30913203 this . setProperties ( properties , true )
30923204 success ( features )
@@ -3139,13 +3251,40 @@ class VolumeImageViewer {
31393251 }
31403252 annotationGroup . layer = new PointsLayer ( {
31413253 source,
3142- style
3254+ style,
3255+ disableHitDetection : false
31433256 } )
31443257 annotationGroup . layer . setVisible ( false )
31453258
31463259 this [ _map ] . addLayer ( annotationGroup . layer )
31473260 this [ _annotationGroups ] [ annotationGroupUID ] = annotationGroup
31483261 } )
3262+
3263+ let selectedAnnotation = null
3264+ this [ _map ] . on ( 'singleclick' , ( e ) => {
3265+ if ( selectedAnnotation !== null ) {
3266+ selectedAnnotation . set ( 'selected' , 0 )
3267+ selectedAnnotation = null
3268+ }
3269+
3270+ this [ _map ] . forEachFeatureAtPixel (
3271+ e . pixel ,
3272+ ( feature ) => {
3273+ feature . set ( 'selected' , 1 )
3274+ selectedAnnotation = feature
3275+ publish (
3276+ container ,
3277+ EVENT . ROI_SELECTED ,
3278+ _getROIFromFeature ( feature )
3279+ )
3280+ return true
3281+ } ,
3282+ {
3283+ hitTolerance : 1 ,
3284+ layerFilter : ( layer ) => ( layer instanceof PointsLayer )
3285+ }
3286+ )
3287+ } )
31493288 }
31503289
31513290 /**
@@ -3286,11 +3425,8 @@ class VolumeImageViewer {
32863425 )
32873426 }
32883427 const properties = source . getProperties ( )
3289- if ( properties [ measurementIndex ] ) {
3290- const colormap = createColormap ( {
3291- name : ColormapNames . VIRIDIS ,
3292- bins : 50
3293- } )
3428+ const key = `measurementValue${ measurementIndex . toString ( ) } `
3429+ if ( properties [ key ] ) {
32943430 const style = {
32953431 symbol : {
32963432 symbolType : 'circle' ,
@@ -3306,19 +3442,23 @@ class VolumeImageViewer {
33063442 opacity : annotationGroup . style . opacity
33073443 }
33083444 }
3445+ const colormap = createColormap ( {
3446+ name : ColormapNames . VIRIDIS ,
3447+ bins : 50
3448+ } )
33093449 Object . assign (
3310- style ,
3450+ style . symbol ,
33113451 _getColorPaletteStyleForPointLayer ( {
3312- key : measurementIndex ,
3313- minValue : properties [ measurementIndex ] . min ,
3314- maxValue : properties [ measurementIndex ] . max ,
3452+ key,
3453+ minValue : properties [ key ] . min ,
3454+ maxValue : properties [ key ] . max ,
33153455 colormap
33163456 } )
33173457 )
33183458 const newLayer = new PointsLayer ( {
33193459 source,
33203460 style,
3321- disableHitDetection : true ,
3461+ disableHitDetection : false ,
33223462 visible : false
33233463 } )
33243464 this [ _map ] . addLayer ( newLayer )
@@ -3339,14 +3479,20 @@ class VolumeImageViewer {
33393479 this [ _pyramid ] . metadata . length ,
33403480 15
33413481 ] ,
3342- color : rgb2hex ( annotationGroup . style . color ) ,
3482+ color : [
3483+ 'match' ,
3484+ [ 'get' , 'selected' ] ,
3485+ 1 ,
3486+ rgb2hex ( this [ _options ] . highlightColor ) ,
3487+ rgb2hex ( annotationGroup . style . color )
3488+ ] ,
33433489 opacity : annotationGroup . style . opacity
33443490 }
33453491 }
33463492 const newLayer = new PointsLayer ( {
33473493 source,
33483494 style,
3349- disableHitDetection : true ,
3495+ disableHitDetection : false ,
33503496 visible : false
33513497 } )
33523498 this [ _map ] . addLayer ( newLayer )
0 commit comments