Update viewer to use prior studies for Hanging Protocols initialization and using those studies in HP Match Image process (added getStudyPriors and getStudyPriorsMap)
This commit is contained in:
parent
b9a3cf9da3
commit
4df18233c4
@ -28,8 +28,11 @@ const initHangingProtocol = () => {
|
|||||||
// Instantiate StudyMetadataSource: necessary for Hanging Protocol to get study metadata
|
// Instantiate StudyMetadataSource: necessary for Hanging Protocol to get study metadata
|
||||||
const studyMetadataSource = new StudyList.classes.OHIFStudyMetadataSource();
|
const studyMetadataSource = new StudyList.classes.OHIFStudyMetadataSource();
|
||||||
|
|
||||||
|
// Get prior studies map
|
||||||
|
const studyPriorsMap = StudyList.functions.getStudyPriorsMap(studyMetadataList);
|
||||||
|
|
||||||
// Creates Protocol Engine object with required arguments
|
// Creates Protocol Engine object with required arguments
|
||||||
const ProtocolEngine = new HP.ProtocolEngine(layoutManager, studyMetadataList, [], studyMetadataSource);
|
const ProtocolEngine = new HP.ProtocolEngine(layoutManager, studyMetadataList, studyPriorsMap, studyMetadataSource);
|
||||||
|
|
||||||
// Sets up Hanging Protocol engine
|
// Sets up Hanging Protocol engine
|
||||||
HP.setEngine(ProtocolEngine);
|
HP.setEngine(ProtocolEngine);
|
||||||
@ -132,7 +135,7 @@ Template.viewer.onRendered(function() {
|
|||||||
|
|
||||||
// To avoid first run
|
// To avoid first run
|
||||||
if (isOHIFViewerMainRendered) {
|
if (isOHIFViewerMainRendered) {
|
||||||
// To run only when ViewerMainRendered dependency has changed.
|
// To run only when OHIFViewerMainRendered dependency has changed.
|
||||||
// because initHangingProtocol can have other reactive components
|
// because initHangingProtocol can have other reactive components
|
||||||
Tracker.nonreactive(initHangingProtocol);
|
Tracker.nonreactive(initHangingProtocol);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,13 @@ import { _ } from 'meteor/underscore';
|
|||||||
import { OHIF } from 'meteor/ohif:core';
|
import { OHIF } from 'meteor/ohif:core';
|
||||||
import 'meteor/ohif:viewerbase';
|
import 'meteor/ohif:viewerbase';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Import Constants
|
||||||
|
*/
|
||||||
|
|
||||||
|
const StudyMetadata = OHIF.viewerbase.metadata.StudyMetadata;
|
||||||
|
const StudySummary = OHIF.viewerbase.metadata.StudySummary;
|
||||||
|
|
||||||
// Define a global variable that will be used to refer to the Protocol Engine
|
// Define a global variable that will be used to refer to the Protocol Engine
|
||||||
// It must be populated by HP.setEngine when the Viewer is initialized and a ProtocolEngine
|
// It must be populated by HP.setEngine when the Viewer is initialized and a ProtocolEngine
|
||||||
// is instantiated on top of the LayoutManager. If the global ProtocolEngine variable remains
|
// is instantiated on top of the LayoutManager. If the global ProtocolEngine variable remains
|
||||||
@ -171,18 +178,30 @@ HP.ProtocolEngine = class ProtocolEngine {
|
|||||||
* @param {Array} relatedStudies Array of related studies
|
* @param {Array} relatedStudies Array of related studies
|
||||||
* @param {Object} studyMedadataSource Instance of StudyMetadataSource (ohif-viewerbase) Object to get study metadata
|
* @param {Object} studyMedadataSource Instance of StudyMetadataSource (ohif-viewerbase) Object to get study metadata
|
||||||
*/
|
*/
|
||||||
constructor(LayoutManager, studies, relatedStudies, studyMetadataSource) {
|
constructor(layoutManager, studies, priorStudies, studyMetadataSource) {
|
||||||
OHIF.log.info('ProtocolEngine::constructor');
|
|
||||||
|
// -----------
|
||||||
|
// Validations
|
||||||
|
|
||||||
|
if (!(layoutManager instanceof OHIF.viewerbase.LayoutManager)) {
|
||||||
|
throw new OHIF.viewerbase.OHIFError('ProtocolEngine::constructor layoutManager is not an instance of LayoutManager');
|
||||||
|
}
|
||||||
|
|
||||||
if (!(studyMetadataSource instanceof OHIF.viewerbase.StudyMetadataSource)) {
|
if (!(studyMetadataSource instanceof OHIF.viewerbase.StudyMetadataSource)) {
|
||||||
throw new OHIF.viewerbase.OHIFError('ProtocolEngine::constructor studyMetadataSource is not an instance of StudyMetadataSource');
|
throw new OHIF.viewerbase.OHIFError('ProtocolEngine::constructor studyMetadataSource is not an instance of StudyMetadataSource');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.LayoutManager = LayoutManager;
|
// @TODO: Validate "studies" parameter
|
||||||
|
|
||||||
|
// --------------
|
||||||
|
// Initialization
|
||||||
|
|
||||||
|
this.LayoutManager = layoutManager;
|
||||||
this.studies = studies;
|
this.studies = studies;
|
||||||
this.relatedStudies = relatedStudies;
|
this.priorStudies = priorStudies instanceof Map ? priorStudies : new Map();
|
||||||
this.studyMetadataSource = studyMetadataSource;
|
this.studyMetadataSource = studyMetadataSource;
|
||||||
|
|
||||||
|
// Put protocol engine in a known states
|
||||||
this.reset();
|
this.reset();
|
||||||
|
|
||||||
// Create an array for new stage ids to be stored
|
// Create an array for new stage ids to be stored
|
||||||
@ -221,7 +240,7 @@ HP.ProtocolEngine = class ProtocolEngine {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
study.numberOfPriorsReferenced = this.getNumberOfAvailablePriors(study);
|
study.numberOfPriorsReferenced = this.getNumberOfAvailablePriors(study.getStudyInstanceUID());
|
||||||
const rule = new HP.ProtocolMatchingRule('numberOfPriorsReferenced', {
|
const rule = new HP.ProtocolMatchingRule('numberOfPriorsReferenced', {
|
||||||
numericality: {
|
numericality: {
|
||||||
greaterThanOrEqualTo: protocol.numberOfPriorsReferenced
|
greaterThanOrEqualTo: protocol.numberOfPriorsReferenced
|
||||||
@ -312,69 +331,25 @@ HP.ProtocolEngine = class ProtocolEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates the number of previous studies in the cached StudyList that
|
* Get the number of prior studies supplied in the priorStudies map property.
|
||||||
* have the same patientId and an earlier study date
|
|
||||||
*
|
*
|
||||||
* @param study The input study
|
* @param {String} studyInstanceUID The StudyInstanceUID of the study whose priors are needed
|
||||||
* @returns {any|*} The number of available prior studies with the same patientId
|
* @returns {number} The number of available prior studies with the same PatientID
|
||||||
*/
|
*/
|
||||||
getNumberOfAvailablePriors(study) {
|
getNumberOfAvailablePriors(studyInstanceUID) {
|
||||||
const instance = study.getFirstInstance();
|
const priors = this.getAvailableStudyPriors(studyInstanceUID);
|
||||||
const studies = StudyListStudies.find({
|
return priors.length;
|
||||||
patientId: instance.getRawValue('x00100020'), // PatientID,
|
|
||||||
studyDate: {
|
|
||||||
$lt: instance.getRawValue('x00080020') // StudyDate
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return studies.count();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
findRelatedStudies(protocol, study) {
|
/**
|
||||||
if (!protocol.protocolMatchingRules) {
|
* Get the array prior studies from a specific study.
|
||||||
return;
|
*
|
||||||
}
|
* @param {String} studyInstanceUID The StudyInstanceUID of the study whose priors are needed
|
||||||
|
* @returns {Array} The array of available priors or an empty array
|
||||||
var studies = StudyListStudies.find({
|
*/
|
||||||
patientId: study.patientId,
|
getAvailableStudyPriors(studyInstanceUID) {
|
||||||
studyDate: {
|
const priors = this.priorStudies.get(studyInstanceUID);
|
||||||
$lt: study.studyDate
|
return priors instanceof Array ? priors : [];
|
||||||
}
|
|
||||||
}, {
|
|
||||||
sort: {
|
|
||||||
studyDate: -1
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var related = [];
|
|
||||||
var currentDate = moment(study.studyDate, 'YYYYMMDD');
|
|
||||||
|
|
||||||
studies.forEach(function(priorStudy, priorIndex) {
|
|
||||||
// Calculate an abstract prior value for the study in question
|
|
||||||
if (priorIndex === (studies.length - 1)) {
|
|
||||||
priorStudy.abstractPriorValue = -1;
|
|
||||||
} else {
|
|
||||||
// Abstract prior index starts from 1 in the DICOM standard
|
|
||||||
priorStudy.abstractPriorValue = priorIndex + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate the relative time using Moment.js
|
|
||||||
var priorDate = moment(priorStudy.studyDate, 'YYYYMMDD');
|
|
||||||
priorStudy.relativeTime = currentDate.diff(priorDate);
|
|
||||||
|
|
||||||
var details = HP.match(priorStudy, protocol.protocolMatchingRules);
|
|
||||||
if (details.score) {
|
|
||||||
related.push({
|
|
||||||
score: details.score,
|
|
||||||
study: priorStudy
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
sortByScore(related);
|
|
||||||
return related.map(function(v) {
|
|
||||||
return v.study;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Match images given a list of Studies and a Viewport's image matching reqs
|
// Match images given a list of Studies and a Viewport's image matching reqs
|
||||||
@ -403,17 +378,7 @@ HP.ProtocolEngine = class ProtocolEngine {
|
|||||||
abstractPriorValue = parseInt(abstractPriorValue, 10);
|
abstractPriorValue = parseInt(abstractPriorValue, 10);
|
||||||
// TODO: Restrict or clarify validators for abstractPriorValue?
|
// TODO: Restrict or clarify validators for abstractPriorValue?
|
||||||
|
|
||||||
// @TODO replace for this.relatedStudies (TypeSafeCollection of StudyMetadatas)
|
const studies = this.getAvailableStudyPriors(currentStudy.getStudyInstanceUID());
|
||||||
const studies = StudyListStudies.find({
|
|
||||||
patientId: currentStudy.patientId,
|
|
||||||
studyDate: {
|
|
||||||
$lt: currentStudy.studyDate
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
sort: {
|
|
||||||
studyDate: -1
|
|
||||||
}
|
|
||||||
}).fetch();
|
|
||||||
|
|
||||||
// TODO: Revisit this later: What about two studies with the same
|
// TODO: Revisit this later: What about two studies with the same
|
||||||
// study date?
|
// study date?
|
||||||
@ -426,25 +391,40 @@ HP.ProtocolEngine = class ProtocolEngine {
|
|||||||
priorStudy = studies[studyIndex];
|
priorStudy = studies[studyIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!priorStudy) {
|
if (!(priorStudy instanceof StudyMetadata) && !(priorStudy instanceof StudySummary)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @TODO: Make sure this study is already loaded into the viewer (OHIF.viewer.Studies)
|
||||||
// @TypeSafeStudies
|
// @TypeSafeStudies
|
||||||
|
const priorStudyInstanceUID = priorStudy.getStudyInstanceUID();
|
||||||
const alreadyLoaded = OHIF.viewer.Studies.findBy({
|
const alreadyLoaded = OHIF.viewer.Studies.findBy({
|
||||||
studyInstanceUid: priorStudy.studyInstanceUid
|
studyInstanceUid: priorStudyInstanceUID
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!alreadyLoaded) {
|
if (!alreadyLoaded) {
|
||||||
this.studyMetadataSource.getByInstanceUID(priorStudy.studyInstanceUid, study => {
|
this.studyMetadataSource.getByInstanceUID(priorStudyInstanceUID).then(study => {
|
||||||
study.abstractPriorValue = abstractPriorValue;
|
study.abstractPriorValue = abstractPriorValue;
|
||||||
study.displaySets = OHIF.viewerbase.sortingManager.getDisplaySets(study);
|
|
||||||
OHIF.viewer.Studies.insert(study);
|
|
||||||
|
|
||||||
this.studies.push(study);
|
const studyMetadata = new OHIF.metadata.StudyMetadata(study);
|
||||||
this.matchImages(viewport);
|
const displaySets = OHIF.viewerbase.sortingManager.getDisplaySets(studyMetadata);
|
||||||
this.updateViewports();
|
|
||||||
});
|
studyMetadata.setDisplaySets(displaySets);
|
||||||
|
|
||||||
|
study.selected = true;
|
||||||
|
study.displaySets = displaySets;
|
||||||
|
OHIF.viewer.Studies.insert(study);
|
||||||
|
OHIF.viewer.StudyMetadataList.insert(studyMetadata);
|
||||||
|
}, error => { console.alert(error) });
|
||||||
|
// this.studyMetadataSource.getByInstanceUID(priorStudy.studyInstanceUid).then(, study => {
|
||||||
|
// study.abstractPriorValue = abstractPriorValue;
|
||||||
|
// study.displaySets = OHIF.viewerbase.sortingManager.getDisplaySets(study);
|
||||||
|
// OHIF.viewer.Studies.insert(study);
|
||||||
|
|
||||||
|
// this.studies.push(study);
|
||||||
|
// this.matchImages(viewport);
|
||||||
|
// this.updateViewports();
|
||||||
|
// });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO: Add relative Date / time
|
// TODO: Add relative Date / time
|
||||||
|
|||||||
12
Packages/ohif-study-list/client/lib/OHIFStudySummary.js
Normal file
12
Packages/ohif-study-list/client/lib/OHIFStudySummary.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { OHIF } from 'meteor/ohif:core';
|
||||||
|
import 'meteor/ohif:viewerbase';
|
||||||
|
|
||||||
|
export class OHIFStudySummary extends OHIF.viewerbase.metadata.StudySummary {
|
||||||
|
|
||||||
|
// @Override
|
||||||
|
addTags(tagMap) {
|
||||||
|
// @TODO: Map OHIF tag names to standard DICOM tag names
|
||||||
|
super.addTags(tagMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
52
Packages/ohif-study-list/client/lib/getStudyPriors.js
Normal file
52
Packages/ohif-study-list/client/lib/getStudyPriors.js
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import { OHIF } from 'meteor/ohif:core';
|
||||||
|
import 'meteor/ohif:viewerbase';
|
||||||
|
// Local Dependencies
|
||||||
|
import { OHIFStudySummary } from './OHIFStudySummary';
|
||||||
|
|
||||||
|
const StudyMetadata = OHIF.viewerbase.metadata.StudyMetadata;
|
||||||
|
const StudySummary = OHIF.viewerbase.metadata.StudySummary;
|
||||||
|
const PatientID = 'x00100020';
|
||||||
|
const StudyDate = 'x00080020';
|
||||||
|
|
||||||
|
const getStudyPriors = study => {
|
||||||
|
|
||||||
|
const priorStudies = [];
|
||||||
|
let patientID;
|
||||||
|
let studyDate;
|
||||||
|
|
||||||
|
if (study instanceof StudyMetadata) {
|
||||||
|
const instance = study.getFirstInstance();
|
||||||
|
patientID = instance.getRawValue(PatientID); // PatientID
|
||||||
|
studyDate = instance.getRawValue(StudyDate); // StudyDate
|
||||||
|
} else if (study instanceof StudySummary) {
|
||||||
|
patientID = study.getTagValue(PatientID); // PatientID
|
||||||
|
studyDate = study.getTagValue(StudyDate); // StudyDate
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find prior studies in global StudyListStudies Minimongo collection...
|
||||||
|
const cursor = StudyListStudies.find({
|
||||||
|
patientId: patientID,
|
||||||
|
studyDate: {
|
||||||
|
$lt: studyDate
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
sort: {
|
||||||
|
studyDate: 'desc'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create an OHIFStudySummary object for each prior study found...
|
||||||
|
cursor.forEach(study => {
|
||||||
|
const summary = new OHIFStudySummary();
|
||||||
|
summary.addTags(study);
|
||||||
|
priorStudies.push(summary);
|
||||||
|
console.log('@StudyListStudies');
|
||||||
|
console.log(study);
|
||||||
|
console.log(study);
|
||||||
|
});
|
||||||
|
|
||||||
|
return priorStudies;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
export { getStudyPriors };
|
||||||
33
Packages/ohif-study-list/client/lib/getStudyPriorsMap.js
Normal file
33
Packages/ohif-study-list/client/lib/getStudyPriorsMap.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { OHIF } from 'meteor/ohif:core';
|
||||||
|
import 'meteor/ohif:viewerbase';
|
||||||
|
// Local Dependencies
|
||||||
|
import { getStudyPriors } from './getStudyPriors';
|
||||||
|
|
||||||
|
const StudyMetadata = OHIF.viewerbase.metadata.StudyMetadata;
|
||||||
|
const StudySummary = OHIF.viewerbase.metadata.StudySummary;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a Map of study priors where the key of each entry is the StudyInstanceUID and its value is an array of StudySummary instances.
|
||||||
|
* @returns {Map} The study map.
|
||||||
|
*/
|
||||||
|
const getStudyPriorsMap = studies => {
|
||||||
|
|
||||||
|
const priorsMap = new Map();
|
||||||
|
|
||||||
|
if (studies instanceof Array) {
|
||||||
|
studies.forEach(study => {
|
||||||
|
if (study instanceof StudyMetadata || study instanceof StudySummary) {
|
||||||
|
const studyInstanceUID = study.getStudyInstanceUID();
|
||||||
|
if (studyInstanceUID) {
|
||||||
|
const priors = getStudyPriors(study);
|
||||||
|
priorsMap.set(studyInstanceUID, priors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return priorsMap;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
export { getStudyPriorsMap };
|
||||||
@ -1,11 +1,20 @@
|
|||||||
import { Viewerbase } from 'meteor/ohif:viewerbase';
|
import { Viewerbase } from 'meteor/ohif:viewerbase';
|
||||||
|
// Classes
|
||||||
import { OHIFStudyMetadataSource } from './OHIFStudyMetadataSource';
|
import { OHIFStudyMetadataSource } from './OHIFStudyMetadataSource';
|
||||||
|
import { OHIFStudySummary } from './OHIFStudySummary';
|
||||||
|
// Functions
|
||||||
|
import { getStudyPriors } from './getStudyPriors';
|
||||||
|
import { getStudyPriorsMap } from './getStudyPriorsMap';
|
||||||
|
|
||||||
StudyList = {
|
StudyList = {
|
||||||
functions: {},
|
functions: {
|
||||||
|
getStudyPriors,
|
||||||
|
getStudyPriorsMap
|
||||||
|
},
|
||||||
callbacks: {},
|
callbacks: {},
|
||||||
classes: {
|
classes: {
|
||||||
OHIFStudyMetadataSource
|
OHIFStudyMetadataSource,
|
||||||
|
OHIFStudySummary
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -188,7 +188,8 @@ Viewerbase.ResizeViewportManager = ResizeViewportManager;
|
|||||||
import { StudyMetadata } from './lib/classes/metadata/StudyMetadata';
|
import { StudyMetadata } from './lib/classes/metadata/StudyMetadata';
|
||||||
import { SeriesMetadata } from './lib/classes/metadata/SeriesMetadata';
|
import { SeriesMetadata } from './lib/classes/metadata/SeriesMetadata';
|
||||||
import { InstanceMetadata } from './lib/classes/metadata/InstanceMetadata';
|
import { InstanceMetadata } from './lib/classes/metadata/InstanceMetadata';
|
||||||
Viewerbase.metadata = { StudyMetadata, SeriesMetadata, InstanceMetadata };
|
import { StudySummary } from './lib/classes/metadata/StudySummary';
|
||||||
|
Viewerbase.metadata = { StudyMetadata, SeriesMetadata, InstanceMetadata, StudySummary };
|
||||||
|
|
||||||
// TypeSafeCollection
|
// TypeSafeCollection
|
||||||
import { TypeSafeCollection } from './lib/classes/TypeSafeCollection';
|
import { TypeSafeCollection } from './lib/classes/TypeSafeCollection';
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
|
|
||||||
const NUMBER = 'number';
|
const NUMBER = 'number';
|
||||||
const STRING = 'string';
|
const STRING = 'string';
|
||||||
|
const REGEX_TAG = /^x[0-9a-fx]{8}$/;
|
||||||
|
|
||||||
const DICOMTagDescriptions = Object.create(Object.prototype, {
|
const DICOMTagDescriptions = Object.create(Object.prototype, {
|
||||||
_descriptions: {
|
_descriptions: {
|
||||||
@ -9,22 +10,94 @@ const DICOMTagDescriptions = Object.create(Object.prototype, {
|
|||||||
writable: false,
|
writable: false,
|
||||||
value: Object.create(null)
|
value: Object.create(null)
|
||||||
},
|
},
|
||||||
|
tagNumberToString: {
|
||||||
|
configurable: false,
|
||||||
|
enumerable: true,
|
||||||
|
writable: false,
|
||||||
|
value: function tagNumberToString(tag) {
|
||||||
|
let string; // by default, undefined is returned...
|
||||||
|
if (this.isValidTagNumber(tag)) {
|
||||||
|
// if it's a number, build its hexadecimal representation...
|
||||||
|
string = 'x' + ('00000000' + tag.toString(16)).substr(-8);
|
||||||
|
}
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isValidTagNumber: {
|
||||||
|
configurable: false,
|
||||||
|
enumerable: true,
|
||||||
|
writable: false,
|
||||||
|
value: function isValidTagNumber(tag) {
|
||||||
|
return (typeof tag === NUMBER && tag >= 0 && tag <= 0xFFFFFFFF);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isValidTag: {
|
||||||
|
configurable: false,
|
||||||
|
enumerable: true,
|
||||||
|
writable: false,
|
||||||
|
value: function isValidTag(tag) {
|
||||||
|
return (typeof tag === STRING ? REGEX_TAG.test(tag) : this.isValidTagNumber(tag));
|
||||||
|
}
|
||||||
|
},
|
||||||
find: {
|
find: {
|
||||||
configurable: false,
|
configurable: false,
|
||||||
enumerable: true,
|
enumerable: true,
|
||||||
writable: false,
|
writable: false,
|
||||||
value: function find(name) {
|
value: function find(name) {
|
||||||
let description; // by default, undefined...
|
let description; // by default, undefined is returned...
|
||||||
if (typeof name === NUMBER) {
|
if (typeof name !== STRING) {
|
||||||
// if it's a number, build its hexadecimal representation...
|
// if it's a number, a tag string will be returned...
|
||||||
name = 'x' + ('00000000' + name.toString(16)).substr(-8);
|
name = this.tagNumberToString(name);
|
||||||
}
|
}
|
||||||
if (typeof name === STRING) {
|
if (typeof name === STRING) {
|
||||||
description = this._descriptions[name];
|
description = this._descriptions[name];
|
||||||
}
|
}
|
||||||
return description;
|
return description;
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
init: {
|
||||||
|
configurable: false,
|
||||||
|
enumerable: true,
|
||||||
|
writable: false,
|
||||||
|
value: function init(descriptionMap) {
|
||||||
|
const _hasOwn = Object.prototype.hasOwnProperty;
|
||||||
|
const _descriptions = this._descriptions;
|
||||||
|
for (let tag in descriptionMap) {
|
||||||
|
if (_hasOwn.call(descriptionMap, tag)) {
|
||||||
|
if (!this.isValidTag(tag)) {
|
||||||
|
// Skip in case tag is not valid...
|
||||||
|
console.info(`DICOMTagDescriptions: Invalid tag "${tag}"...`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (tag in _descriptions) {
|
||||||
|
// Skip in case the tag is duplicated...
|
||||||
|
console.info(`DICOMTagDescriptions: Duplicated tag "${tag}"...`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Save keyword...
|
||||||
|
const keyword = descriptionMap[tag];
|
||||||
|
// Create a description entry and freeze it...
|
||||||
|
const entry = Object.create(null);
|
||||||
|
entry.tag = tag;
|
||||||
|
entry.keyword = keyword;
|
||||||
|
Object.freeze(entry);
|
||||||
|
// Add tag references to entry...
|
||||||
|
_descriptions[tag] = entry;
|
||||||
|
// Add keyword references to entry (if not present already)...
|
||||||
|
if (keyword in _descriptions) {
|
||||||
|
const currentEntry = _descriptions[keyword];
|
||||||
|
console.info(`DICOMTagDescriptions: Using <${currentEntry.tag},${currentEntry.keyword}> instead of <${entry.tag},${entry.keyword}> for keyword "${keyword}"...`);
|
||||||
|
} else {
|
||||||
|
_descriptions[keyword] = entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Freeze internal description map...
|
||||||
|
Object.freeze(_descriptions);
|
||||||
|
// Freeze itself...
|
||||||
|
Object.freeze(this);
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -3179,41 +3252,7 @@ let initialTagDescriptionMap = {
|
|||||||
xfffee0dd: 'EndOfSequence'
|
xfffee0dd: 'EndOfSequence'
|
||||||
};
|
};
|
||||||
|
|
||||||
// Safely populate DICOMTagDescriptions object
|
DICOMTagDescriptions.init(initialTagDescriptionMap);
|
||||||
(function(initialTagDescriptionMap) {
|
|
||||||
const _hasOwn = Object.prototype.hasOwnProperty;
|
|
||||||
const descriptionMap = DICOMTagDescriptions._descriptions;
|
|
||||||
for (let tag in initialTagDescriptionMap) {
|
|
||||||
if (_hasOwn.call(initialTagDescriptionMap, tag)) {
|
|
||||||
if (tag in descriptionMap) {
|
|
||||||
// Skip in case the tag is duplicated...
|
|
||||||
console.info(`DICOMTagDescriptions: Duplicated tag ${tag}...`);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Save keyword...
|
|
||||||
const keyword = initialTagDescriptionMap[tag];
|
|
||||||
// Create a description entry and freeze it...
|
|
||||||
const entry = Object.create(null);
|
|
||||||
entry.tag = tag;
|
|
||||||
entry.keyword = keyword;
|
|
||||||
Object.freeze(entry);
|
|
||||||
// Add tag references to entry...
|
|
||||||
descriptionMap[tag] = entry;
|
|
||||||
// Add keyword references to entry (if not present already)...
|
|
||||||
if (keyword in descriptionMap) {
|
|
||||||
const currentEntry = descriptionMap[keyword];
|
|
||||||
console.info(`DICOMTagDescriptions: Using <${currentEntry.tag},${currentEntry.keyword}> instead of <${entry.tag},${entry.keyword}> for keyword "${keyword}"...`);
|
|
||||||
} else {
|
|
||||||
descriptionMap[keyword] = entry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Freeze internal description map (nothing should be changed in this object afterwords)...
|
|
||||||
Object.freeze(descriptionMap);
|
|
||||||
}(initialTagDescriptionMap));
|
|
||||||
|
|
||||||
// Freeze exported object...
|
|
||||||
Object.freeze(DICOMTagDescriptions);
|
|
||||||
|
|
||||||
// Discard original map...
|
// Discard original map...
|
||||||
initialTagDescriptionMap = null;
|
initialTagDescriptionMap = null;
|
||||||
|
|||||||
@ -4,7 +4,6 @@ import { OHIFError } from '../OHIFError';
|
|||||||
const UNDEFINED = 'undefined';
|
const UNDEFINED = 'undefined';
|
||||||
const NUMBER = 'number';
|
const NUMBER = 'number';
|
||||||
const STRING = 'string';
|
const STRING = 'string';
|
||||||
const REGEX_TAG = /^x[0-9a-fx]{8}$/;
|
|
||||||
|
|
||||||
export class InstanceMetadata extends Metadata {
|
export class InstanceMetadata extends Metadata {
|
||||||
|
|
||||||
|
|||||||
@ -1,42 +1,94 @@
|
|||||||
|
import { DICOMTagDescriptions } from '../../DICOMTagDescriptions';
|
||||||
|
|
||||||
class StudySummary {
|
const StudyInstanceUID = 'x0020000D';
|
||||||
|
|
||||||
constructor(propertyMap) {
|
export class StudySummary {
|
||||||
|
|
||||||
|
constructor(tagMap, propertyMap) {
|
||||||
|
|
||||||
// Define the main immutable "_data" private property.
|
// Define the main immutable "_data" private property.
|
||||||
Object.defineProperty(this, '_data', {
|
Object.defineProperties(this, {
|
||||||
configurable: false,
|
_tags: {
|
||||||
enumerable: false,
|
configurable: false,
|
||||||
writable: false,
|
enumerable: false,
|
||||||
value: Object.create(null)
|
writable: false,
|
||||||
|
value: Object.create(null)
|
||||||
|
},
|
||||||
|
_properties: {
|
||||||
|
configurable: false,
|
||||||
|
enumerable: false,
|
||||||
|
writable: false,
|
||||||
|
value: Object.create(null)
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const hasOwn = Object.prototype.hasOwnProperty;
|
// Initialize internal tag map if first argument is given.
|
||||||
const data = this._data;
|
if (tagMap !== void 0) {
|
||||||
for (let property in propertyMap) {
|
this.addTags(tagMap);
|
||||||
if (hasOwn.call(propertyMap, property)) {
|
}
|
||||||
data[property] = propertyMap[property];
|
|
||||||
}
|
// Initialize internal property map if second argument is given.
|
||||||
|
if (propertyMap !== void 0) {
|
||||||
|
this.addProperties(addProperties);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getStudyInstanceUID() {
|
||||||
|
// This method should return null if StudyInstanceUID is not available to keep compatibility StudyMetadata API
|
||||||
|
return this.getTagValue(StudyInstanceUID) || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
addProperties(propertyMap) {
|
||||||
|
const _hasOwn = Object.prototype.hasOwnProperty;
|
||||||
|
const _properties = this._properties;
|
||||||
|
for (let property in propertyMap) {
|
||||||
|
if (_hasOwn.call(propertyMap, property)) {
|
||||||
|
_properties[property] = propertyMap[property];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addTags(tagMap) {
|
||||||
|
const _hasOwn = Object.prototype.hasOwnProperty;
|
||||||
|
const _tags = this._tags;
|
||||||
|
for (let tag in tagMap) {
|
||||||
|
if (_hasOwn.call(tagMap, tag)) {
|
||||||
|
const description = DICOMTagDescriptions.find(tag);
|
||||||
|
// When a description is available, use its tag as internal key...
|
||||||
|
if (description) {
|
||||||
|
_tags[description.tag] = tagMap[tag];
|
||||||
|
} else {
|
||||||
|
_tags[tag] = tagMap[tag];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tagExists(tagName) {
|
||||||
|
const _tags = this._tags;
|
||||||
|
const description = DICOMTagDescriptions.find(tagName);
|
||||||
|
if (description) {
|
||||||
|
return (description.tag in _tags);
|
||||||
|
}
|
||||||
|
return (tagName in _tags);
|
||||||
|
}
|
||||||
|
|
||||||
|
getTagValue(tagName) {
|
||||||
|
const _tags = this._tags;
|
||||||
|
const description = DICOMTagDescriptions.find(tagName);
|
||||||
|
if (description) {
|
||||||
|
return _tags[description.tag];
|
||||||
|
}
|
||||||
|
return _tags[tagName];
|
||||||
|
}
|
||||||
|
|
||||||
propertyExists(propertyName) {
|
propertyExists(propertyName) {
|
||||||
const internalPropertyName = this.getInternalPropertyName(propertyName);
|
return (propertyName in this._properties);
|
||||||
return (internalPropertyName in this._data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getPropertyValue(propertyName) {
|
getPropertyValue(propertyName) {
|
||||||
const internalPropertyName = this.getInternalPropertyName(propertyName);
|
return this._properties[propertyName];
|
||||||
return this._data[internalPropertyName];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function is supposed to translate external property names into internal property names.
|
|
||||||
* By default it implements the simplest translation possible.
|
|
||||||
*/
|
|
||||||
getInternalPropertyName(propertyName) {
|
|
||||||
return propertyName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user