feat: Support for local DICOM drag and drop (#2445)
* Added drag and drop from local and local data source * fix: dicom SR drag and drop from local
This commit is contained in:
1 parent
99b8dc4759
commit
fdb6ca41d6
23 files changed
+656
-89
No files matched your search
@@ -17,7 +17,7 @@ function create({
|
||||
retrieve,
|
||||
store,
|
||||
reject,
|
||||
parseRouteParams,
|
||||
initialize,
|
||||
retrieveSeriesMetadata,
|
||||
deleteStudyMetadataPromise,
|
||||
getImageIdsForDisplaySet,
|
||||
@@ -38,7 +38,7 @@ function create({
|
||||
* @param {number} params.resultsPerPage
|
||||
*/
|
||||
mapParams: params => params,
|
||||
requestResults: () => {},
|
||||
requestResults: () => { },
|
||||
processResults: results => results,
|
||||
},
|
||||
series: {},
|
||||
@@ -63,7 +63,7 @@ function create({
|
||||
retrieve: retrieve || defaultRetrieve,
|
||||
reject: reject || defaultReject,
|
||||
store: store || defaultStore,
|
||||
parseRouteParams,
|
||||
initialize,
|
||||
retrieveSeriesMetadata,
|
||||
deleteStudyMetadataPromise,
|
||||
getImageIdsForDisplaySet,
|
||||
|
||||
@@ -71,47 +71,47 @@ class MetadataProvider {
|
||||
this.imageIdToUIDs.set(imageId, uids);
|
||||
}
|
||||
|
||||
_getAndCacheStudy(StudyInstanceUID) {
|
||||
const studies = this.studies;
|
||||
// _getAndCacheStudy(StudyInstanceUID) {
|
||||
// const studies = this.studies;
|
||||
|
||||
let study = studies.get(StudyInstanceUID);
|
||||
// let study = studies.get(StudyInstanceUID);
|
||||
|
||||
if (!study) {
|
||||
study = { series: new Map() };
|
||||
studies.set(StudyInstanceUID, study);
|
||||
}
|
||||
// if (!study) {
|
||||
// study = { series: new Map() };
|
||||
// studies.set(StudyInstanceUID, study);
|
||||
// }
|
||||
|
||||
return study;
|
||||
}
|
||||
_getAndCacheSeriesFromStudy(study, SeriesInstanceUID) {
|
||||
let series = study.series.get(SeriesInstanceUID);
|
||||
// return study;
|
||||
// }
|
||||
// _getAndCacheSeriesFromStudy(study, SeriesInstanceUID) {
|
||||
// let series = study.series.get(SeriesInstanceUID);
|
||||
|
||||
if (!series) {
|
||||
series = { instances: new Map() };
|
||||
study.series.set(SeriesInstanceUID, series);
|
||||
}
|
||||
// if (!series) {
|
||||
// series = { instances: new Map() };
|
||||
// study.series.set(SeriesInstanceUID, series);
|
||||
// }
|
||||
|
||||
return series;
|
||||
}
|
||||
// return series;
|
||||
// }
|
||||
|
||||
_getAndCacheInstanceFromStudy(series, SOPInstanceUID) {
|
||||
let instance = series.instances.get(SOPInstanceUID);
|
||||
// _getAndCacheInstanceFromStudy(series, SOPInstanceUID) {
|
||||
// let instance = series.instances.get(SOPInstanceUID);
|
||||
|
||||
if (!instance) {
|
||||
instance = {};
|
||||
series.instances.set(SOPInstanceUID, instance);
|
||||
}
|
||||
// if (!instance) {
|
||||
// instance = {};
|
||||
// series.instances.set(SOPInstanceUID, instance);
|
||||
// }
|
||||
|
||||
return instance;
|
||||
}
|
||||
// return instance;
|
||||
// }
|
||||
|
||||
async _checkBulkDataAndInlineBinaries(instance, server) {
|
||||
await fetchOverlayData(instance, server);
|
||||
// async _checkBulkDataAndInlineBinaries(instance, server) {
|
||||
// await fetchOverlayData(instance, server);
|
||||
|
||||
if (instance.PhotometricInterpretation === 'PALETTE COLOR') {
|
||||
await fetchPaletteColorLookupTableData(instance, server);
|
||||
}
|
||||
}
|
||||
// if (instance.PhotometricInterpretation === 'PALETTE COLOR') {
|
||||
// await fetchPaletteColorLookupTableData(instance, server);
|
||||
// }
|
||||
// }
|
||||
|
||||
_getInstance(imageId) {
|
||||
const uids = this._getUIDsFromImageID(imageId);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import dcmjs from 'dcmjs'
|
||||
|
||||
import pubSubServiceInterface from '../_shared/pubSubServiceInterface';
|
||||
import createStudyMetadata from './createStudyMetadata';
|
||||
import EVENTS from './EVENTS';
|
||||
@@ -24,6 +26,10 @@ const _model = {
|
||||
// }]
|
||||
};
|
||||
|
||||
function _getStudyInstanceUIDs() {
|
||||
return _model.studies.map(aStudy => aStudy.StudyInstanceUID)
|
||||
}
|
||||
|
||||
function _getStudy(StudyInstanceUID) {
|
||||
return _model.studies.find(
|
||||
aStudy => aStudy.StudyInstanceUID === StudyInstanceUID
|
||||
@@ -69,6 +75,41 @@ function _getInstanceFromImageId(imageId) {
|
||||
const BaseImplementation = {
|
||||
EVENTS,
|
||||
listeners: {},
|
||||
addInstance(dicomJSONDatasetOrP10ArrayBuffer) {
|
||||
let dicomJSONDataset;
|
||||
|
||||
// If Arraybuffer, parse to DICOMJSON before naturalizing.
|
||||
if (dicomJSONDatasetOrP10ArrayBuffer instanceof ArrayBuffer) {
|
||||
const dicomData = dcmjs.data.DicomMessage.readFile(dicomJSONDatasetOrP10ArrayBuffer);
|
||||
|
||||
dicomJSONDataset = dicomData.dict;
|
||||
} else {
|
||||
dicomJSONDataset = dicomJSONDatasetOrP10ArrayBuffer;
|
||||
}
|
||||
|
||||
let naturalizedDataset;
|
||||
|
||||
if (dicomJSONDataset['SeriesInstanceUID'] === undefined) {
|
||||
naturalizedDataset = dcmjs.data.DicomMetaDictionary.naturalizeDataset(
|
||||
dicomJSONDataset
|
||||
);
|
||||
} else {
|
||||
naturalizedDataset = dicomJSONDataset;
|
||||
}
|
||||
|
||||
const { StudyInstanceUID } = naturalizedDataset;
|
||||
|
||||
let study = _model.studies.find(
|
||||
study => study.StudyInstanceUID === StudyInstanceUID
|
||||
);
|
||||
|
||||
if (!study) {
|
||||
_model.studies.push(createStudyMetadata(StudyInstanceUID));
|
||||
study = _model.studies[_model.studies.length - 1];
|
||||
}
|
||||
|
||||
study.addInstanceToSeries(naturalizedDataset);
|
||||
},
|
||||
addInstances(instances, madeInClient = false) {
|
||||
const { StudyInstanceUID, SeriesInstanceUID } = instances[0];
|
||||
|
||||
@@ -134,6 +175,7 @@ const BaseImplementation = {
|
||||
_model.studies.push(newStudy);
|
||||
}
|
||||
},
|
||||
getStudyInstanceUIDs: _getStudyInstanceUIDs,
|
||||
getStudy: _getStudy,
|
||||
getSeries: _getSeries,
|
||||
getInstance: _getInstance,
|
||||
@@ -146,8 +188,6 @@ const DicomMetadataStore = Object.assign(
|
||||
pubSubServiceInterface
|
||||
);
|
||||
|
||||
// TODO => Add instances
|
||||
//_addInstance(input) // arraybuffer, or other stuff
|
||||
|
||||
export { DicomMetadataStore };
|
||||
export default DicomMetadataStore;
|
||||
@@ -5,13 +5,31 @@ function createStudyMetadata(StudyInstanceUID) {
|
||||
StudyInstanceUID,
|
||||
isLoaded: false,
|
||||
series: [],
|
||||
/**
|
||||
*
|
||||
* @param {object} instance
|
||||
* @returns {bool} true if series were added; false if series already exist
|
||||
*/
|
||||
addInstanceToSeries: function (instance) {
|
||||
const { SeriesInstanceUID } = instance;
|
||||
const existingSeries = this.series.find(
|
||||
s => s.SeriesInstanceUID === SeriesInstanceUID
|
||||
);
|
||||
|
||||
if (existingSeries) {
|
||||
existingSeries.instances.push(instance);
|
||||
} else {
|
||||
const series = createSeriesMetadata([instance]);
|
||||
this.series.push(series);
|
||||
}
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {object[]} instances
|
||||
* @param {string} instances[].SeriesInstanceUID
|
||||
* @returns {bool} true if series were added; false if series already exist
|
||||
*/
|
||||
addInstancesToSeries: function(instances) {
|
||||
addInstancesToSeries: function (instances) {
|
||||
const { SeriesInstanceUID } = instances[0];
|
||||
const existingSeries = this.series.find(
|
||||
s => s.SeriesInstanceUID === SeriesInstanceUID
|
||||
@@ -25,7 +43,7 @@ function createStudyMetadata(StudyInstanceUID) {
|
||||
}
|
||||
},
|
||||
|
||||
setSeriesMetadata: function(SeriesInstanceUID, seriesMetadata) {
|
||||
setSeriesMetadata: function (SeriesInstanceUID, seriesMetadata) {
|
||||
let existingSeries = this.series.find(
|
||||
s => s.SeriesInstanceUID === SeriesInstanceUID
|
||||
);
|
||||
|
||||
@@ -179,7 +179,7 @@ export function ViewportGridProvider({ children, service }) {
|
||||
]);
|
||||
|
||||
const api = {
|
||||
// getState,
|
||||
getState,
|
||||
setActiveViewportIndex,
|
||||
setDisplaysetForViewport,
|
||||
setLayout,
|
||||
|
||||
@@ -68,6 +68,7 @@
|
||||
"moment": "^2.24.0",
|
||||
"prop-types": "^15.7.2",
|
||||
"query-string": "^6.12.1",
|
||||
"react-dropzone": "^10.1.7",
|
||||
"react-i18next": "^10.11.0",
|
||||
"react-resize-detector": "^4.2.0",
|
||||
"react-router": "^5.2.0",
|
||||
|
||||
@@ -24,6 +24,20 @@ window.config = {
|
||||
supportsWildcard: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
friendlyName: 'dicom json',
|
||||
namespace: 'org.ohif.default.dataSourcesModule.dicomjson',
|
||||
sourceName: 'dicomjson',
|
||||
configuration: {
|
||||
name: 'json',
|
||||
},
|
||||
},
|
||||
{
|
||||
friendlyName: 'dicom local',
|
||||
namespace: 'org.ohif.default.dataSourcesModule.dicomlocal',
|
||||
sourceName: 'dicomlocal',
|
||||
configuration: {},
|
||||
},
|
||||
],
|
||||
// whiteLabeling: {
|
||||
// /* Optional: Should return a React component to be rendered in the "Logo" section of the application's Top Navigation bar */
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
<title>OHIF Viewer</title>
|
||||
|
||||
<!-- WEB FONTS -->
|
||||
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700&display=swap" rel="stylesheet" />
|
||||
<link href="https://fonts.googleapis.com/css?family=Lato:100,300,400,500,700&display=swap" rel="stylesheet" />
|
||||
|
||||
<!-- EXTENSIONS -->
|
||||
<!-- <script type="text/javascript" src="path/to/some-extension.js"></script>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* CSS Grid Reference: http://grid.malven.co/
|
||||
*/
|
||||
import React, { useEffect } from 'react';
|
||||
import React, { useEffect, useCallback } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { ViewportGrid, ViewportPane, useViewportGrid } from '@ohif/ui';
|
||||
import EmptyViewport from './EmptyViewport';
|
||||
@@ -27,53 +27,69 @@ function ViewerViewportGrid(props) {
|
||||
} = servicesManager.services;
|
||||
|
||||
|
||||
// Using Hanging protocol engine to match the displaysets
|
||||
const updateDisplaysetForViewports = useCallback(
|
||||
(displaySets) => {
|
||||
const [
|
||||
matchDetails,
|
||||
hpAlreadyApplied,
|
||||
] = HangingProtocolService.getState();
|
||||
|
||||
if (!matchDetails.length) return;
|
||||
// Match each viewport individually
|
||||
|
||||
const numViewports = viewportGrid.numRows * viewportGrid.numCols;
|
||||
for (let i = 0; i < numViewports; i++) {
|
||||
if (hpAlreadyApplied[i] === true) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// if current viewport doesn't have a match
|
||||
if (matchDetails[i] === undefined) return
|
||||
|
||||
const { SeriesInstanceUID } = matchDetails[i];
|
||||
const matchingDisplaySet = displaySets.find(ds => {
|
||||
return ds.SeriesInstanceUID === SeriesInstanceUID;
|
||||
});
|
||||
|
||||
if (!matchingDisplaySet) {
|
||||
continue;
|
||||
}
|
||||
|
||||
viewportGridService.setDisplaysetForViewport({
|
||||
viewportIndex: i,
|
||||
displaySetInstanceUID: matchingDisplaySet.displaySetInstanceUID,
|
||||
});
|
||||
|
||||
HangingProtocolService.setHangingProtocolAppliedForViewport(i);
|
||||
}
|
||||
},
|
||||
[viewportGrid, numRows, numCols],
|
||||
)
|
||||
|
||||
// Using Hanging protocol engine to match the displaySets
|
||||
useEffect(() => {
|
||||
const { unsubscribe } = DisplaySetService.subscribe(
|
||||
DisplaySetService.EVENTS.DISPLAY_SETS_ADDED,
|
||||
eventData => {
|
||||
const { displaySetsAdded } = eventData;
|
||||
|
||||
const [
|
||||
matchDetails,
|
||||
hpAlreadyApplied,
|
||||
] = HangingProtocolService.getState();
|
||||
|
||||
if (!matchDetails.length) return;
|
||||
// Match each viewport individually
|
||||
|
||||
const numViewports = viewportGrid.numRows * viewportGrid.numCols;
|
||||
for (let i = 0; i < numViewports; i++) {
|
||||
if (hpAlreadyApplied[i] === true) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// if current viewport doesn't have a match
|
||||
if (matchDetails[i] === undefined) return
|
||||
|
||||
const { SeriesInstanceUID } = matchDetails[i];
|
||||
const matchingDisplaySet = displaySetsAdded.find(ds => {
|
||||
return ds.SeriesInstanceUID === SeriesInstanceUID;
|
||||
});
|
||||
|
||||
if (!matchingDisplaySet) {
|
||||
continue;
|
||||
}
|
||||
|
||||
viewportGridService.setDisplaysetForViewport({
|
||||
viewportIndex: i,
|
||||
displaySetInstanceUID: matchingDisplaySet.displaySetInstanceUID,
|
||||
});
|
||||
|
||||
HangingProtocolService.setHangingProtocolAppliedForViewport(i);
|
||||
}
|
||||
updateDisplaysetForViewports(displaySetsAdded)
|
||||
}
|
||||
);
|
||||
|
||||
return () => {
|
||||
unsubscribe();
|
||||
};
|
||||
}, [numRows, numCols]);
|
||||
}, [viewportGrid]);
|
||||
|
||||
|
||||
|
||||
// Changing the Hanging protocol while viewing
|
||||
useEffect(() => {
|
||||
const displaySets = DisplaySetService.getActiveDisplaySets();
|
||||
updateDisplaysetForViewports(displaySets)
|
||||
}, [viewportGrid])
|
||||
|
||||
|
||||
|
||||
|
||||
// Layout change based on hanging protocols
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
import React, { useEffect, useRef } from 'react'
|
||||
import classnames from 'classnames'
|
||||
import { MODULE_TYPES } from '@ohif/core'
|
||||
|
||||
import PropTypes from 'prop-types'
|
||||
import Dropzone from 'react-dropzone'
|
||||
import filesToStudies from './filesToStudies'
|
||||
|
||||
import { extensionManager } from '../../App.jsx'
|
||||
|
||||
import { Icon, Button } from '@ohif/ui'
|
||||
|
||||
const getLoadButton = (onDrop, text, isDir) => {
|
||||
return (
|
||||
<Dropzone onDrop={onDrop} noDrag>
|
||||
{({ getRootProps, getInputProps }) => (
|
||||
<div {...getRootProps()}>
|
||||
<Button
|
||||
rounded="full"
|
||||
variant="contained" // outlined
|
||||
disabled={false}
|
||||
endIcon={<Icon name="launch-arrow" />} // launch-arrow | launch-info
|
||||
className={classnames('font-bold', 'ml-2')}
|
||||
onClick={() => { }}
|
||||
>
|
||||
{text}
|
||||
{isDir ? (
|
||||
<input
|
||||
{...getInputProps()}
|
||||
webkitdirectory="true"
|
||||
mozdirectory="true"
|
||||
/>
|
||||
) : (
|
||||
<input {...getInputProps()} />
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</Dropzone>)
|
||||
}
|
||||
|
||||
function Local(props) {
|
||||
const { history } = props
|
||||
const dropzoneRef = useRef()
|
||||
|
||||
// Initializing the dicom local dataSource
|
||||
const dataSourceModules = extensionManager.modules[MODULE_TYPES.DATA_SOURCE]
|
||||
const localDataSources = dataSourceModules.reduce((acc, curr) => {
|
||||
const mods = []
|
||||
curr.module.forEach((mod) => {
|
||||
if (mod.type === 'localApi') {
|
||||
mods.push(mod)
|
||||
}
|
||||
})
|
||||
return acc.concat(mods)
|
||||
}, [])
|
||||
|
||||
const fistLocalDataSource = localDataSources[0]
|
||||
const dataSource = fistLocalDataSource.createDataSource({})
|
||||
|
||||
const onDrop = async (acceptedFiles) => {
|
||||
const studies = await filesToStudies(acceptedFiles, dataSource)
|
||||
// Todo: navigate to work list and let user select a mode
|
||||
history.push(`/viewer/dicomlocal?StudyInstanceUIDs=${studies[0]}`)
|
||||
}
|
||||
|
||||
// Set body style
|
||||
useEffect(() => {
|
||||
document.body.classList.add('bg-black')
|
||||
return () => {
|
||||
document.body.classList.remove('bg-black')
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Dropzone ref={dropzoneRef} onDrop={onDrop} noClick>
|
||||
{({ getRootProps, getInputProps }) => (
|
||||
<div {...getRootProps()} style={{ width: '100%', height: '100%' }}>
|
||||
<div className="h-screen w-screen flex justify-center items-center ">
|
||||
<div className="py-8 px-8 mx-auto bg-secondary-dark shadow-md space-y-2 rounded-lg">
|
||||
<img
|
||||
className="block mx-auto h-10"
|
||||
src="./customLogo.svg"
|
||||
alt="OHIF"
|
||||
/>
|
||||
<div className="text-center space-y-2 pt-4">
|
||||
<div className="space-y-2">
|
||||
<p className="text-blue-300 text-base">
|
||||
Note: You data is not uploaded to any server, it will stay
|
||||
in your local browser application
|
||||
</p>
|
||||
<p className="text-xg text-primary-active font-semibold pt-8">
|
||||
Drag and Drop DICOM files here to load them in the Viewer
|
||||
</p>
|
||||
<p className="text-blue-300 text-lg">Or click to </p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-around pt-4">
|
||||
{getLoadButton(onDrop, 'Load files', false)}
|
||||
{getLoadButton(onDrop, 'Load folders', true)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Dropzone>
|
||||
)
|
||||
}
|
||||
|
||||
Local.propTypes = {
|
||||
history: PropTypes.shape({
|
||||
push: PropTypes.func,
|
||||
}).isRequired,
|
||||
};
|
||||
|
||||
export default Local
|
||||
@@ -0,0 +1,29 @@
|
||||
import dcmjs from 'dcmjs';
|
||||
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
|
||||
import FileLoader from './fileLoader';
|
||||
|
||||
|
||||
const DICOMFileLoader = new (class extends FileLoader {
|
||||
fileType = 'application/dicom';
|
||||
loadFile(file, imageId) {
|
||||
return cornerstoneWADOImageLoader.wadouri.loadFileRequest(imageId);
|
||||
}
|
||||
|
||||
getDataset(image, imageId) {
|
||||
const dicomData = dcmjs.data.DicomMessage.readFile(image);
|
||||
|
||||
const dataset = dcmjs.data.DicomMetaDictionary.naturalizeDataset(
|
||||
dicomData.dict
|
||||
);
|
||||
|
||||
dataset.url = imageId
|
||||
|
||||
dataset._meta = dcmjs.data.DicomMetaDictionary.namifyDataset(
|
||||
dicomData.meta
|
||||
);
|
||||
|
||||
return dataset
|
||||
}
|
||||
})();
|
||||
|
||||
export default DICOMFileLoader;
|
||||
@@ -0,0 +1,6 @@
|
||||
export default class FileLoader {
|
||||
fileType;
|
||||
loadFile(file, imageId) { }
|
||||
getDataset(image, imageId) { }
|
||||
getStudies(dataset, imageId) { }
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
|
||||
import { DicomMetadataStore } from '@ohif/core';
|
||||
|
||||
import FileLoader from './fileLoader';
|
||||
import PDFFileLoader from './pdfFileLoader';
|
||||
import DICOMFileLoader from './dicomFileLoader';
|
||||
|
||||
class FileLoaderService extends FileLoader {
|
||||
fileType;
|
||||
loader;
|
||||
constructor(file) {
|
||||
super();
|
||||
const fileType = file && file.type;
|
||||
this.loader = this.getLoader(fileType);
|
||||
this.fileType = this.loader.fileType;
|
||||
}
|
||||
|
||||
addFile(file) {
|
||||
return cornerstoneWADOImageLoader.wadouri.fileManager.add(file);
|
||||
}
|
||||
|
||||
loadFile(file, imageId) {
|
||||
return this.loader.loadFile(file, imageId);
|
||||
}
|
||||
|
||||
getDataset(image, imageId) {
|
||||
return this.loader.getDataset(image, imageId);
|
||||
}
|
||||
|
||||
getLoader(fileType) {
|
||||
if (fileType === 'application/pdf') {
|
||||
return PDFFileLoader;
|
||||
} else {
|
||||
// Default to dicom loader
|
||||
return DICOMFileLoader;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default FileLoaderService;
|
||||
@@ -0,0 +1,26 @@
|
||||
import FileLoaderService from './fileLoaderService';
|
||||
import { DicomMetadataStore } from '@ohif/core';
|
||||
|
||||
const processFile = async file => {
|
||||
try {
|
||||
const fileLoaderService = new FileLoaderService(file);
|
||||
const imageId = fileLoaderService.addFile(file);
|
||||
const image = await fileLoaderService.loadFile(file, imageId);
|
||||
const dicomJSONDataset = await fileLoaderService.getDataset(image, imageId);
|
||||
|
||||
DicomMetadataStore.addInstance(dicomJSONDataset);
|
||||
} catch (error) {
|
||||
console.log(
|
||||
error.name,
|
||||
':Error when trying to load and process local files:',
|
||||
error.message
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default async function filesToStudies(files) {
|
||||
const processFilesPromises = files.map(processFile);
|
||||
await Promise.all(processFilesPromises);
|
||||
|
||||
return DicomMetadataStore.getStudyInstanceUIDs();
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from './Local';
|
||||
@@ -0,0 +1,17 @@
|
||||
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
|
||||
import FileLoader from './fileLoader';
|
||||
|
||||
const PDFFileLoader = new (class extends FileLoader {
|
||||
fileType = 'application/pdf';
|
||||
loadFile(file, imageId) {
|
||||
return cornerstoneWADOImageLoader.wadouri.loadFileRequest(imageId);
|
||||
}
|
||||
|
||||
getDataset(image, imageId) {
|
||||
const dataset = {};
|
||||
dataset.imageId = image.imageId || imageId;
|
||||
return dataset;
|
||||
}
|
||||
})();
|
||||
|
||||
export default PDFFileLoader;
|
||||
@@ -19,7 +19,7 @@ async function defaultRouteInit({
|
||||
} = servicesManager.services;
|
||||
|
||||
const unsubscriptions = [];
|
||||
// TODO: This should be baked into core, not manuall?
|
||||
// TODO: This should be baked into core, not manual?
|
||||
// DisplaySetService would wire this up?
|
||||
const {
|
||||
unsubscribe: instanceAddedUnsubscribe,
|
||||
@@ -37,10 +37,6 @@ async function defaultRouteInit({
|
||||
|
||||
unsubscriptions.push(instanceAddedUnsubscribe);
|
||||
|
||||
studyInstanceUIDs.forEach(StudyInstanceUID => {
|
||||
dataSource.retrieveSeriesMetadata({ StudyInstanceUID });
|
||||
});
|
||||
|
||||
const { unsubscribe: seriesAddedUnsubscribe } = DicomMetadataStore.subscribe(
|
||||
DicomMetadataStore.EVENTS.SERIES_ADDED,
|
||||
({ StudyInstanceUID }) => {
|
||||
@@ -50,6 +46,10 @@ async function defaultRouteInit({
|
||||
);
|
||||
unsubscriptions.push(seriesAddedUnsubscribe);
|
||||
|
||||
studyInstanceUIDs.forEach(StudyInstanceUID => {
|
||||
dataSource.retrieveSeriesMetadata({ StudyInstanceUID });
|
||||
});
|
||||
|
||||
return unsubscriptions;
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ export default function ModeRoute({
|
||||
useEffect(() => {
|
||||
// Todo: this should not be here, data source should not care about params
|
||||
const initializeDataSource = async (params, query) => {
|
||||
const studyInstanceUIDs = await dataSource.parseRouteParams({
|
||||
const studyInstanceUIDs = await dataSource.initialize({
|
||||
params,
|
||||
query,
|
||||
});
|
||||
@@ -261,7 +261,7 @@ export default function ModeRoute({
|
||||
<ImageViewerProvider
|
||||
// initialState={{ StudyInstanceUIDs: StudyInstanceUIDs }}
|
||||
StudyInstanceUIDs={studyInstanceUIDs}
|
||||
// reducer={reducer}
|
||||
// reducer={reducer}
|
||||
>
|
||||
<CombinedContextProvider>
|
||||
<DragAndDropProvider>
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Switch, Route } from 'react-router-dom';
|
||||
// Route Components
|
||||
import DataSourceWrapper from './DataSourceWrapper';
|
||||
import WorkList from './WorkList';
|
||||
import Local from './Local';
|
||||
import NotFound from './NotFound';
|
||||
import buildModeRoutes from './buildModeRoutes';
|
||||
import { ErrorBoundary } from '@ohif/ui';
|
||||
@@ -17,6 +18,11 @@ const bakedInRoutes = [
|
||||
component: DataSourceWrapper,
|
||||
props: { children: WorkList },
|
||||
},
|
||||
{
|
||||
path: '/local',
|
||||
exact: true,
|
||||
component: Local,
|
||||
},
|
||||
// NOT FOUND (404)
|
||||
{ component: NotFound },
|
||||
];
|
||||
|
||||
Reference in new issue
Block a user