feat(metadata): metadata access improvement (#5292)
* feat(metadata): metadata access improvement * feat: Make a few fields commonly used at the instance level inherit multiframe
This commit is contained in:
parent
9413219206
commit
16233d980f
@ -2,6 +2,7 @@ import dcmjs from 'dcmjs';
|
|||||||
|
|
||||||
import pubSubServiceInterface from '../_shared/pubSubServiceInterface';
|
import pubSubServiceInterface from '../_shared/pubSubServiceInterface';
|
||||||
import createStudyMetadata from './createStudyMetadata';
|
import createStudyMetadata from './createStudyMetadata';
|
||||||
|
import addProxyFields from '../../utils/addProxyFields';
|
||||||
|
|
||||||
const EVENTS = {
|
const EVENTS = {
|
||||||
STUDY_ADDED: 'event::dicomMetadataStore:studyAdded',
|
STUDY_ADDED: 'event::dicomMetadataStore:studyAdded',
|
||||||
@ -70,7 +71,7 @@ function _getStudy(StudyInstanceUID) {
|
|||||||
* @returns {*} A series object
|
* @returns {*} A series object
|
||||||
*/
|
*/
|
||||||
function _getSeries(StudyInstanceUID, SeriesInstanceUID) {
|
function _getSeries(StudyInstanceUID, SeriesInstanceUID) {
|
||||||
if(!StudyInstanceUID) {
|
if (!StudyInstanceUID) {
|
||||||
const series = _model.studies.map(study => study.series).flat();
|
const series = _model.studies.map(study => study.series).flat();
|
||||||
return series.find(aSeries => aSeries.SeriesInstanceUID === SeriesInstanceUID);
|
return series.find(aSeries => aSeries.SeriesInstanceUID === SeriesInstanceUID);
|
||||||
}
|
}
|
||||||
@ -176,7 +177,7 @@ const BaseImplementation = {
|
|||||||
naturalizedDataset = dicomJSONDataset;
|
naturalizedDataset = dicomJSONDataset;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { StudyInstanceUID } = naturalizedDataset;
|
const { StudyInstanceUID, NumberOfFrames: numberOfFrames } = naturalizedDataset;
|
||||||
|
|
||||||
let study = _model.studies.find(study => study.StudyInstanceUID === StudyInstanceUID);
|
let study = _model.studies.find(study => study.StudyInstanceUID === StudyInstanceUID);
|
||||||
|
|
||||||
@ -185,10 +186,19 @@ const BaseImplementation = {
|
|||||||
study = _model.studies[_model.studies.length - 1];
|
study = _model.studies[_model.studies.length - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
naturalizedDataset = numberOfFrames > 1 ? addProxyFields(naturalizedDataset) : naturalizedDataset;
|
||||||
|
|
||||||
study.addInstanceToSeries(naturalizedDataset);
|
study.addInstanceToSeries(naturalizedDataset);
|
||||||
},
|
},
|
||||||
addInstances(instances, madeInClient = false) {
|
addInstances(instances, madeInClient = false) {
|
||||||
const { StudyInstanceUID, SeriesInstanceUID } = instances[0];
|
const { StudyInstanceUID, SeriesInstanceUID, NumberOfFrames } = instances[0];
|
||||||
|
// For multiframe images (NumberOfFrames > 1), wrap each instance with a metadata proxy
|
||||||
|
// to enable fallback access for key image plane fields. This allows fields like
|
||||||
|
// ImagePositionPatient, ImageOrientationPatient, and PixelSpacing to be transparently
|
||||||
|
// resolved from parent or shared metadata if they are not directly present on the instance directly
|
||||||
|
if (NumberOfFrames > 1) {
|
||||||
|
instances = instances.map(instance => addProxyFields(instance));
|
||||||
|
}
|
||||||
|
|
||||||
let study = _model.studies.find(study => study.StudyInstanceUID === StudyInstanceUID);
|
let study = _model.studies.find(study => study.StudyInstanceUID === StudyInstanceUID);
|
||||||
|
|
||||||
|
|||||||
57
platform/core/src/utils/addProxyFields.js
Normal file
57
platform/core/src/utils/addProxyFields.js
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/**
|
||||||
|
* fieldProxies has the set of fields which are proxied on a per-field basis
|
||||||
|
* to the child element.
|
||||||
|
*/
|
||||||
|
export const fieldProxies = [
|
||||||
|
'ImagePositionPatient',
|
||||||
|
'ImageOrientationPatient',
|
||||||
|
'PixelSpacing',
|
||||||
|
// more fields as necessary
|
||||||
|
];
|
||||||
|
|
||||||
|
const METADATA_PROXY_FLAG = Symbol('isMetadataProxy');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds proxy properties for a limited number of values which are sometimes
|
||||||
|
* defined in multiframe instance data.
|
||||||
|
*
|
||||||
|
* If a requested field is in `metadataFieldsToWrap`:
|
||||||
|
* - It first tries to return the value from the current instance.
|
||||||
|
* - If not found, it attempts to retrieve the value from the `_parentInstance`.
|
||||||
|
* - If still not found, it checks `_parentInstance._shared`.
|
||||||
|
* - If none exist, it returns `undefined`.
|
||||||
|
*
|
||||||
|
* For all other properties, it behaves like a regular property access.
|
||||||
|
*
|
||||||
|
* This allows graceful fallback access for DICOM metadata values that might be spread
|
||||||
|
* across nested or shared metadata structures.
|
||||||
|
*
|
||||||
|
* @param {Object} instance - The target instance object to wrap.
|
||||||
|
* @returns {Proxy} A proxy-wrapped instance with custom field resolution behavior.
|
||||||
|
*/
|
||||||
|
export function addProxyFields(instance) {
|
||||||
|
// Skip wrapping if already wrapped
|
||||||
|
if (!instance || instance[METADATA_PROXY_FLAG]) {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const fieldProxy of fieldProxies) {
|
||||||
|
if( fieldProxy in instance) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Object.defineProperty(instance,fieldProxy, {
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true,
|
||||||
|
get: () => {
|
||||||
|
return instance._parentInstance?.[fieldProxy] ?? instance._parentInstance?._shared?.[fieldProxy];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark this proxy to avoid double wrapping
|
||||||
|
Object.defineProperty(instance,METADATA_PROXY_FLAG, { value: true });
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default addProxyFields;
|
||||||
Loading…
Reference in New Issue
Block a user