Skip to content

Commit dec029f

Browse files
committed
Improve stability of annotation selection events
1 parent 4e5090c commit dec029f

File tree

1 file changed

+121
-99
lines changed

1 file changed

+121
-99
lines changed

src/viewer.js

Lines changed: 121 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -2994,11 +2994,6 @@ class VolumeImageViewer {
29942994
color: this[_options].primaryColor
29952995
}
29962996

2997-
// We need to bind those variables to constants for the loader function
2998-
const client = this[_options].client
2999-
const pyramid = this[_pyramid].metadata
3000-
const affineInverse = this[_affineInverse]
3001-
const container = this[_map].getTargetElement()
30022997
const _getROIFromFeature = (feature) => {
30032998
const roi = this._getROIFromFeature(
30042999
feature,
@@ -3009,92 +3004,109 @@ class VolumeImageViewer {
30093004
const annotationGroupMetadata = metadata.AnnotationGroupSequence.find(
30103005
item => item.AnnotationGroupUID === annotationGroupUID
30113006
)
3007+
if (annotationGroupUID == null || annotationGroupMetadata == null) {
3008+
throw new Error(
3009+
'Could not obtain information of annotation from ' +
3010+
`annotation group "${annotationGroupUID}".`
3011+
)
3012+
}
30123013

3013-
const findingCategory = (
3014-
annotationGroupMetadata
3015-
.AnnotationPropertyCategoryCodeSequence[0]
3016-
)
3017-
roi.addEvaluation(
3018-
new dcmjs.sr.valueTypes.CodeContentItem({
3019-
name: new dcmjs.sr.coding.CodedConcept({
3020-
value: '276214006',
3021-
meaning: 'Finding category',
3022-
schemeDesignator: 'SCT'
3023-
}),
3024-
value: new dcmjs.sr.coding.CodedConcept({
3025-
value: findingCategory.CodeValue,
3026-
meaning: findingCategory.CodeMeaning,
3027-
schemeDesignator: findingCategory.CodingSchemeDesignator
3028-
}),
3029-
relationshipType: dcmjs.sr.valueTypes.RelationshipTypes.HAS_CONCEPT_MOD
3030-
})
3031-
)
3032-
const findingType = (
3033-
annotationGroupMetadata
3034-
.AnnotationPropertyTypeCodeSequence[0]
3035-
)
3036-
roi.addEvaluation(
3037-
new dcmjs.sr.valueTypes.CodeContentItem({
3038-
name: new dcmjs.sr.coding.CodedConcept({
3039-
value: '121071',
3040-
meaning: 'Finding',
3041-
schemeDesignator: 'DCM'
3042-
}),
3043-
value: new dcmjs.sr.coding.CodedConcept({
3044-
value: findingType.CodeValue,
3045-
meaning: findingType.CodeMeaning,
3046-
schemeDesignator: findingType.CodingSchemeDesignator
3047-
}),
3048-
relationshipType: dcmjs.sr.valueTypes.RelationshipTypes.HAS_CONCEPT_MOD
3049-
})
3050-
)
3051-
3052-
annotationGroupMetadata.MeasurementsSequence.forEach(
3053-
(measurementItem, measurementIndex) => {
3054-
const key = `measurementValue${measurementIndex.toString()}`
3055-
const value = feature.get(key)
3056-
const name = measurementItem.ConceptNameCodeSequence[0]
3057-
const unit = measurementItem.MeasurementUnitsCodeSequence[0]
3058-
3059-
const measurement = new dcmjs.sr.valueTypes.NumContentItem({
3060-
value: Number(value),
3014+
if (annotationGroupMetadata.AnnotationPropertyCategoryCodeSequence != null) {
3015+
const findingCategory = (
3016+
annotationGroupMetadata.AnnotationPropertyCategoryCodeSequence[0]
3017+
)
3018+
roi.addEvaluation(
3019+
new dcmjs.sr.valueTypes.CodeContentItem({
30613020
name: new dcmjs.sr.coding.CodedConcept({
3062-
value: name.CodeValue,
3063-
meaning: name.CodeMeaning,
3064-
schemeDesignator: name.CodingSchemeDesignator
3021+
value: '276214006',
3022+
meaning: 'Finding category',
3023+
schemeDesignator: 'SCT'
30653024
}),
3066-
unit: new dcmjs.sr.coding.CodedConcept({
3067-
value: unit.CodeValue,
3068-
meaning: unit.CodeMeaning,
3069-
schemeDesignator: unit.CodingSchemeDesignator
3025+
value: new dcmjs.sr.coding.CodedConcept({
3026+
value: findingCategory.CodeValue,
3027+
meaning: findingCategory.CodeMeaning,
3028+
schemeDesignator: findingCategory.CodingSchemeDesignator
30703029
}),
3071-
relationshipType: dcmjs.sr.valueTypes.RelationshipTypes.CONTAINS
3030+
relationshipType:
3031+
dcmjs.sr.valueTypes.RelationshipTypes.HAS_CONCEPT_MOD
30723032
})
3073-
if (measurementItem.ReferencedImageSequence != null) {
3074-
const ref = measurementItem.ReferencedImageSequence[0]
3075-
const image = new dcmjs.sr.valueTypes.ImageContentItem({
3033+
)
3034+
}
3035+
if (annotationGroupMetadata.AnnotationPropertyTypeCodeSequence != null) {
3036+
const findingType = (
3037+
annotationGroupMetadata.AnnotationPropertyTypeCodeSequence[0]
3038+
)
3039+
roi.addEvaluation(
3040+
new dcmjs.sr.valueTypes.CodeContentItem({
3041+
name: new dcmjs.sr.coding.CodedConcept({
3042+
value: '121071',
3043+
meaning: 'Finding',
3044+
schemeDesignator: 'DCM'
3045+
}),
3046+
value: new dcmjs.sr.coding.CodedConcept({
3047+
value: findingType.CodeValue,
3048+
meaning: findingType.CodeMeaning,
3049+
schemeDesignator: findingType.CodingSchemeDesignator
3050+
}),
3051+
relationshipType:
3052+
dcmjs.sr.valueTypes.RelationshipTypes.HAS_CONCEPT_MOD
3053+
})
3054+
)
3055+
}
3056+
3057+
if (annotationGroupMetadata.MeasurementsSequence != null) {
3058+
annotationGroupMetadata.MeasurementsSequence.forEach(
3059+
(measurementItem, measurementIndex) => {
3060+
const key = `measurementValue${measurementIndex.toString()}`
3061+
const value = feature.get(key)
3062+
const name = measurementItem.ConceptNameCodeSequence[0]
3063+
const unit = measurementItem.MeasurementUnitsCodeSequence[0]
3064+
3065+
const measurement = new dcmjs.sr.valueTypes.NumContentItem({
3066+
value: Number(value),
30763067
name: new dcmjs.sr.coding.CodedConcept({
3077-
value: '121112',
3078-
meaning: 'Source of Measurement',
3079-
schemeDesignator: 'DCM'
3068+
value: name.CodeValue,
3069+
meaning: name.CodeMeaning,
3070+
schemeDesignator: name.CodingSchemeDesignator
30803071
}),
3081-
referencedSOPClassUID: ref.ReferencedSOPClassUID,
3082-
referencedSOPInstanceUID: ref.ReferencedSOPInstanceUID
3072+
unit: new dcmjs.sr.coding.CodedConcept({
3073+
value: unit.CodeValue,
3074+
meaning: unit.CodeMeaning,
3075+
schemeDesignator: unit.CodingSchemeDesignator
3076+
}),
3077+
relationshipType: dcmjs.sr.valueTypes.RelationshipTypes.CONTAINS
30833078
})
3084-
if (ref.ReferencedOpticalPathIdentifier != null) {
3085-
image.ReferencedSOPSequence[0].ReferencedOpticalPathIdentifier = (
3086-
ref.ReferencedOpticalPathIdentifier
3087-
)
3079+
if (measurementItem.ReferencedImageSequence != null) {
3080+
const ref = measurementItem.ReferencedImageSequence[0]
3081+
const image = new dcmjs.sr.valueTypes.ImageContentItem({
3082+
name: new dcmjs.sr.coding.CodedConcept({
3083+
value: '121112',
3084+
meaning: 'Source of Measurement',
3085+
schemeDesignator: 'DCM'
3086+
}),
3087+
referencedSOPClassUID: ref.ReferencedSOPClassUID,
3088+
referencedSOPInstanceUID: ref.ReferencedSOPInstanceUID
3089+
})
3090+
if (ref.ReferencedOpticalPathIdentifier != null) {
3091+
image.ReferencedSOPSequence[0].ReferencedOpticalPathIdentifier = (
3092+
ref.ReferencedOpticalPathIdentifier
3093+
)
3094+
}
3095+
measurement.ContentSequence = [image]
30883096
}
3089-
measurement.ContentSequence = [image]
3097+
roi.addMeasurement(measurement)
30903098
}
3091-
roi.addMeasurement(measurement)
3092-
}
3093-
)
3099+
)
3100+
}
30943101

30953102
return roi
30963103
}
30973104

3105+
// We need to bind those variables to constants for the loader function
3106+
const client = this[_options].client
3107+
const pyramid = this[_pyramid].metadata
3108+
const affineInverse = this[_affineInverse]
3109+
30983110
metadata.AnnotationGroupSequence.forEach((item, index) => {
30993111
const annotationGroupUID = item.AnnotationGroupUID
31003112
const algorithm = item.AnnotationGroupAlgorithmIdentificationSequence[0]
@@ -3291,28 +3303,33 @@ class VolumeImageViewer {
32913303

32923304
let selectedAnnotation = null
32933305
this[_map].on('singleclick', (e) => {
3294-
if (selectedAnnotation !== null) {
3295-
selectedAnnotation.set('selected', 0)
3296-
selectedAnnotation = null
3297-
}
3298-
3299-
this[_map].forEachFeatureAtPixel(
3300-
e.pixel,
3301-
(feature) => {
3302-
feature.set('selected', 1)
3303-
selectedAnnotation = feature
3304-
publish(
3305-
container,
3306-
EVENT.ROI_SELECTED,
3307-
_getROIFromFeature(feature)
3308-
)
3309-
return true
3310-
},
3311-
{
3312-
hitTolerance: 1,
3313-
layerFilter: (layer) => (layer instanceof PointsLayer)
3306+
if (e != null) {
3307+
if (selectedAnnotation != null) {
3308+
selectedAnnotation.set('selected', 0)
3309+
selectedAnnotation = null
33143310
}
3315-
)
3311+
const container = this[_map].getTargetElement()
3312+
this[_map].forEachFeatureAtPixel(
3313+
e.pixel,
3314+
(feature) => {
3315+
if (feature != null) {
3316+
feature.set('selected', 1)
3317+
selectedAnnotation = feature
3318+
publish(
3319+
container,
3320+
EVENT.ROI_SELECTED,
3321+
_getROIFromFeature(feature)
3322+
)
3323+
return true
3324+
}
3325+
return false
3326+
},
3327+
{
3328+
hitTolerance: 1,
3329+
layerFilter: (layer) => (layer instanceof PointsLayer)
3330+
}
3331+
)
3332+
}
33163333
})
33173334
}
33183335

@@ -3767,7 +3784,12 @@ class VolumeImageViewer {
37673784
colormap: segment.style.paletteColorLookupTable.data
37683785
}),
37693786
useInterimTilesOnError: false,
3770-
cacheSize: this[_options].tilesCacheSize
3787+
cacheSize: this[_options].tilesCacheSize,
3788+
minResolution: (
3789+
minZoomLevel > 0
3790+
? this[_pyramid].resolutions[minZoomLevel]
3791+
: undefined
3792+
)
37713793
})
37723794
segment.layer.on('error', (event) => {
37733795
console.error(`error rendering segment "${segmentUID}"`, event)

0 commit comments

Comments
 (0)