fix(bugs): and replace seriesInstanceUID and seriesInstanceUIDs URL with seriesInstanceUIDs (#4049)
This commit is contained in:
parent
058da93863
commit
da7c1a5d8c
@ -4,7 +4,7 @@ import PanelGenerateImage from './PanelGenerateImage';
|
|||||||
function DynamicDataPanel({ servicesManager, commandsManager }) {
|
function DynamicDataPanel({ servicesManager, commandsManager }) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="flex flex-auto flex-col text-white"
|
className="flex flex-col text-white"
|
||||||
data-cy={'dynamic-volume-panel'}
|
data-cy={'dynamic-volume-panel'}
|
||||||
>
|
>
|
||||||
<PanelGenerateImage
|
<PanelGenerateImage
|
||||||
|
|||||||
@ -61,6 +61,24 @@ const cornerstoneExtension: Types.Extensions.Extension = {
|
|||||||
*/
|
*/
|
||||||
id,
|
id,
|
||||||
|
|
||||||
|
onModeEnter: ({ servicesManager }): void => {
|
||||||
|
const { cornerstoneViewportService, toolbarService, segmentationService } =
|
||||||
|
servicesManager.services;
|
||||||
|
toolbarService.registerEventForToolbarUpdate(cornerstoneViewportService, [
|
||||||
|
cornerstoneViewportService.EVENTS.VIEWPORT_DATA_CHANGED,
|
||||||
|
]);
|
||||||
|
|
||||||
|
toolbarService.registerEventForToolbarUpdate(segmentationService, [
|
||||||
|
segmentationService.EVENTS.SEGMENTATION_ADDED,
|
||||||
|
segmentationService.EVENTS.SEGMENTATION_REMOVED,
|
||||||
|
segmentationService.EVENTS.SEGMENTATION_UPDATED,
|
||||||
|
]);
|
||||||
|
|
||||||
|
toolbarService.registerEventForToolbarUpdate(cornerstone.eventTarget, [
|
||||||
|
cornerstoneTools.Enums.Events.TOOL_ACTIVATED,
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
|
||||||
onModeExit: ({ servicesManager }): void => {
|
onModeExit: ({ servicesManager }): void => {
|
||||||
const { cineService } = servicesManager.services;
|
const { cineService } = servicesManager.services;
|
||||||
// Empty out the image load and retrieval pools to prevent memory leaks
|
// Empty out the image load and retrieval pools to prevent memory leaks
|
||||||
|
|||||||
@ -99,26 +99,10 @@ export default async function init({
|
|||||||
uiNotificationService,
|
uiNotificationService,
|
||||||
cornerstoneViewportService,
|
cornerstoneViewportService,
|
||||||
hangingProtocolService,
|
hangingProtocolService,
|
||||||
toolbarService,
|
|
||||||
viewportGridService,
|
viewportGridService,
|
||||||
stateSyncService,
|
stateSyncService,
|
||||||
segmentationService,
|
|
||||||
} = servicesManager.services as CornerstoneServices;
|
} = servicesManager.services as CornerstoneServices;
|
||||||
|
|
||||||
toolbarService.registerEventForToolbarUpdate(cornerstoneViewportService, [
|
|
||||||
cornerstoneViewportService.EVENTS.VIEWPORT_DATA_CHANGED,
|
|
||||||
]);
|
|
||||||
|
|
||||||
toolbarService.registerEventForToolbarUpdate(segmentationService, [
|
|
||||||
segmentationService.EVENTS.SEGMENTATION_ADDED,
|
|
||||||
segmentationService.EVENTS.SEGMENTATION_REMOVED,
|
|
||||||
segmentationService.EVENTS.SEGMENTATION_UPDATED,
|
|
||||||
]);
|
|
||||||
|
|
||||||
toolbarService.registerEventForToolbarUpdate(eventTarget, [
|
|
||||||
cornerstoneTools.Enums.Events.TOOL_ACTIVATED,
|
|
||||||
]);
|
|
||||||
|
|
||||||
window.services = servicesManager.services;
|
window.services = servicesManager.services;
|
||||||
window.extensionManager = extensionManager;
|
window.extensionManager = extensionManager;
|
||||||
window.commandsManager = commandsManager;
|
window.commandsManager = commandsManager;
|
||||||
|
|||||||
@ -98,7 +98,21 @@ class CornerstoneCacheService {
|
|||||||
const volume = cs3DCache.getVolume(volumeId);
|
const volume = cs3DCache.getVolume(volumeId);
|
||||||
|
|
||||||
if (volume) {
|
if (volume) {
|
||||||
cs3DCache.removeVolumeLoadObject(volumeId);
|
if (volume.imageIds) {
|
||||||
|
// also for each imageId in the volume, remove the imageId from the cache
|
||||||
|
// since that will hold the old metadata as well
|
||||||
|
|
||||||
|
volume.imageIds.forEach(imageId => {
|
||||||
|
if (cs3DCache.getImageLoadObject(imageId)) {
|
||||||
|
cs3DCache.removeImageLoadObject(imageId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// this shouldn't be via removeVolumeLoadObject, since that will
|
||||||
|
// remove the texture as well, but here we really just need a remove
|
||||||
|
// from registry so that we load it again
|
||||||
|
cs3DCache._volumeCache.delete(volumeId);
|
||||||
this.volumeImageIds.delete(volumeId);
|
this.volumeImageIds.delete(volumeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1147,6 +1147,10 @@ class SegmentationService extends PubSubService {
|
|||||||
|
|
||||||
displaySet.isHydrated = isHydrated;
|
displaySet.isHydrated = isHydrated;
|
||||||
displaySetService.setDisplaySetMetadataInvalidated(displaySetUID, false);
|
displaySetService.setDisplaySetMetadataInvalidated(displaySetUID, false);
|
||||||
|
|
||||||
|
this._broadcastEvent(this.EVENTS.SEGMENTATION_UPDATED, {
|
||||||
|
segmentation: this.getSegmentation(displaySetUID),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _highlightLabelmap(
|
private _highlightLabelmap(
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import SUPPORTED_TOOLS from './constants/supportedTools';
|
|||||||
import { getDisplayUnit } from './utils';
|
import { getDisplayUnit } from './utils';
|
||||||
import getSOPInstanceAttributes from './utils/getSOPInstanceAttributes';
|
import getSOPInstanceAttributes from './utils/getSOPInstanceAttributes';
|
||||||
import { utils } from '@ohif/core';
|
import { utils } from '@ohif/core';
|
||||||
|
import { getStatisticDisplayString } from './utils/getValueDisplayString';
|
||||||
|
|
||||||
const CircleROI = {
|
const CircleROI = {
|
||||||
toAnnotation: measurement => {},
|
toAnnotation: measurement => {},
|
||||||
@ -193,11 +194,7 @@ function getDisplayText(mappedAnnotations, displaySet, customizationService) {
|
|||||||
mappedAnnotations.forEach(mappedAnnotation => {
|
mappedAnnotations.forEach(mappedAnnotation => {
|
||||||
const { unit, max, SeriesNumber } = mappedAnnotation;
|
const { unit, max, SeriesNumber } = mappedAnnotation;
|
||||||
|
|
||||||
let maxStr = '';
|
const maxStr = getStatisticDisplayString(max, unit, 'max');
|
||||||
if (max) {
|
|
||||||
const roundedMax = utils.roundNumber(max, 2);
|
|
||||||
maxStr = `Max: ${roundedMax} <small>${getDisplayUnit(unit)}</small> `;
|
|
||||||
}
|
|
||||||
|
|
||||||
const str = `${maxStr}(S:${SeriesNumber}${instanceText}${frameText})`;
|
const str = `${maxStr}(S:${SeriesNumber}${instanceText}${frameText})`;
|
||||||
if (!displayText.includes(str)) {
|
if (!displayText.includes(str)) {
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import SUPPORTED_TOOLS from './constants/supportedTools';
|
|||||||
import { getDisplayUnit } from './utils';
|
import { getDisplayUnit } from './utils';
|
||||||
import getSOPInstanceAttributes from './utils/getSOPInstanceAttributes';
|
import getSOPInstanceAttributes from './utils/getSOPInstanceAttributes';
|
||||||
import { utils } from '@ohif/core';
|
import { utils } from '@ohif/core';
|
||||||
|
import { getStatisticDisplayString } from './utils/getValueDisplayString';
|
||||||
|
|
||||||
const EllipticalROI = {
|
const EllipticalROI = {
|
||||||
toAnnotation: measurement => {},
|
toAnnotation: measurement => {},
|
||||||
@ -192,12 +193,7 @@ function getDisplayText(mappedAnnotations, displaySet, customizationService) {
|
|||||||
mappedAnnotations.forEach(mappedAnnotation => {
|
mappedAnnotations.forEach(mappedAnnotation => {
|
||||||
const { unit, max, SeriesNumber } = mappedAnnotation;
|
const { unit, max, SeriesNumber } = mappedAnnotation;
|
||||||
|
|
||||||
let maxStr = '';
|
const maxStr = getStatisticDisplayString(max, unit, 'max');
|
||||||
if (max) {
|
|
||||||
const roundedMax = utils.roundNumber(max, 2);
|
|
||||||
maxStr = `Max: ${roundedMax} <small>${getDisplayUnit(unit)}</small> `;
|
|
||||||
}
|
|
||||||
|
|
||||||
const str = `${maxStr}(S:${SeriesNumber}${instanceText}${frameText})`;
|
const str = `${maxStr}(S:${SeriesNumber}${instanceText}${frameText})`;
|
||||||
if (!displayText.includes(str)) {
|
if (!displayText.includes(str)) {
|
||||||
displayText.push(str);
|
displayText.push(str);
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import SUPPORTED_TOOLS from './constants/supportedTools';
|
|||||||
import { getDisplayUnit } from './utils';
|
import { getDisplayUnit } from './utils';
|
||||||
import getSOPInstanceAttributes from './utils/getSOPInstanceAttributes';
|
import getSOPInstanceAttributes from './utils/getSOPInstanceAttributes';
|
||||||
import { utils } from '@ohif/core';
|
import { utils } from '@ohif/core';
|
||||||
|
import { getStatisticDisplayString } from './utils/getValueDisplayString';
|
||||||
|
|
||||||
const RectangleROI = {
|
const RectangleROI = {
|
||||||
toAnnotation: measurement => {},
|
toAnnotation: measurement => {},
|
||||||
@ -193,11 +194,7 @@ function getDisplayText(mappedAnnotations, displaySet, customizationService) {
|
|||||||
mappedAnnotations.forEach(mappedAnnotation => {
|
mappedAnnotations.forEach(mappedAnnotation => {
|
||||||
const { unit, max, SeriesNumber } = mappedAnnotation;
|
const { unit, max, SeriesNumber } = mappedAnnotation;
|
||||||
|
|
||||||
let maxStr = '';
|
const maxStr = getStatisticDisplayString(max, unit, 'max');
|
||||||
if (max) {
|
|
||||||
const roundedMax = utils.roundNumber(max, 2);
|
|
||||||
maxStr = `Max: ${roundedMax} <small>${getDisplayUnit(unit)}</small> `;
|
|
||||||
}
|
|
||||||
|
|
||||||
const str = `${maxStr}(S:${SeriesNumber}${instanceText}${frameText})`;
|
const str = `${maxStr}(S:${SeriesNumber}${instanceText}${frameText})`;
|
||||||
if (!displayText.includes(str)) {
|
if (!displayText.includes(str)) {
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import SUPPORTED_TOOLS from './constants/supportedTools';
|
import SUPPORTED_TOOLS from './constants/supportedTools';
|
||||||
import getSOPInstanceAttributes from './utils/getSOPInstanceAttributes';
|
import getSOPInstanceAttributes from './utils/getSOPInstanceAttributes';
|
||||||
import { getDisplayUnit } from './utils';
|
|
||||||
import { utils } from '@ohif/core';
|
import { utils } from '@ohif/core';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -0,0 +1,12 @@
|
|||||||
|
import { utils } from '@ohif/core';
|
||||||
|
import getDisplayUnit from './getDisplayUnit';
|
||||||
|
|
||||||
|
export const getStatisticDisplayString = (numbers, unit, key) => {
|
||||||
|
if (Array.isArray(numbers) && numbers.length > 0) {
|
||||||
|
const results = numbers.map(number => utils.roundNumber(number, 2));
|
||||||
|
return `${key.charAt(0).toUpperCase() + key.slice(1)}: ${results.join(', ')} <small>${getDisplayUnit(unit)}</small>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = utils.roundNumber(numbers, 2);
|
||||||
|
return `${key.charAt(0).toUpperCase() + key.slice(1)}: ${result} <small>${getDisplayUnit(unit)}</small>`;
|
||||||
|
};
|
||||||
@ -55,10 +55,13 @@ function createTools(utilityModule) {
|
|||||||
parentTool: 'Brush',
|
parentTool: 'Brush',
|
||||||
configuration: {
|
configuration: {
|
||||||
activeStrategy: 'THRESHOLD_INSIDE_CIRCLE',
|
activeStrategy: 'THRESHOLD_INSIDE_CIRCLE',
|
||||||
preview: {
|
// preview: {
|
||||||
enabled: true,
|
// enabled: true,
|
||||||
},
|
// },
|
||||||
strategySpecificConfiguration: {
|
strategySpecificConfiguration: {
|
||||||
|
// to use the use the center segment index to determine
|
||||||
|
// if inside -> same segment, if outside -> eraser
|
||||||
|
// useCenterSegmentIndex: true,
|
||||||
THRESHOLD: {
|
THRESHOLD: {
|
||||||
isDynamic: true,
|
isDynamic: true,
|
||||||
dynamicRadius: 3,
|
dynamicRadius: 3,
|
||||||
|
|||||||
@ -112,21 +112,15 @@ const toolbarButtons: Button[] = [
|
|||||||
commands: {
|
commands: {
|
||||||
commandName: 'setBrushSize',
|
commandName: 'setBrushSize',
|
||||||
commandOptions: {
|
commandOptions: {
|
||||||
toolNames: ['ThresholdCircularBrush', 'ThresholdSphereBrush'],
|
toolNames: [
|
||||||
|
'ThresholdCircularBrush',
|
||||||
|
'ThresholdSphereBrush',
|
||||||
|
'ThresholdCircularBrushDynamic',
|
||||||
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: 'Shape',
|
|
||||||
type: 'radio',
|
|
||||||
id: 'eraser-mode',
|
|
||||||
value: 'ThresholdCircularBrush',
|
|
||||||
values: [
|
|
||||||
{ value: 'ThresholdCircularBrush', label: 'Circle' },
|
|
||||||
{ value: 'ThresholdSphereBrush', label: 'Sphere' },
|
|
||||||
],
|
|
||||||
commands: 'setToolActiveToolbar',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'Threshold',
|
name: 'Threshold',
|
||||||
type: 'radio',
|
type: 'radio',
|
||||||
@ -148,6 +142,19 @@ const toolbarButtons: Button[] = [
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'Shape',
|
||||||
|
type: 'radio',
|
||||||
|
id: 'eraser-mode',
|
||||||
|
value: 'ThresholdCircularBrush',
|
||||||
|
values: [
|
||||||
|
{ value: 'ThresholdCircularBrush', label: 'Circle' },
|
||||||
|
{ value: 'ThresholdSphereBrush', label: 'Sphere' },
|
||||||
|
],
|
||||||
|
condition: ({ options }) =>
|
||||||
|
options.find(option => option.id === 'dynamic-mode').value === 'ThresholdRange',
|
||||||
|
commands: 'setToolActiveToolbar',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'ThresholdRange',
|
name: 'ThresholdRange',
|
||||||
type: 'double-range',
|
type: 'double-range',
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { hotkeys, classes } from '@ohif/core';
|
import { hotkeys, classes } from '@ohif/core';
|
||||||
import toolbarButtons from './toolbarButtons.js';
|
import toolbarButtons from './toolbarButtons.js';
|
||||||
import { id } from './id.js';
|
import { id } from './id.js';
|
||||||
import initToolGroups, { toolGroupIds } from './initToolGroups.js';
|
import initToolGroups from './initToolGroups.js';
|
||||||
import setCrosshairsConfiguration from './utils/setCrosshairsConfiguration.js';
|
import setCrosshairsConfiguration from './utils/setCrosshairsConfiguration.js';
|
||||||
import setFusionActiveVolume from './utils/setFusionActiveVolume.js';
|
import setFusionActiveVolume from './utils/setFusionActiveVolume.js';
|
||||||
import i18n from 'i18next';
|
import i18n from 'i18next';
|
||||||
@ -112,19 +112,6 @@ function modeFactory({ modeConfiguration }) {
|
|||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// This is a hack and we need to find a better way to enable
|
|
||||||
// some tools that require the viewport to be ready
|
|
||||||
const { unsubscribe: unsub1 } = viewportGridService.subscribe(
|
|
||||||
viewportGridService.EVENTS.VIEWPORTS_READY,
|
|
||||||
() => {
|
|
||||||
commandsManager.run('setToolEnabled', {
|
|
||||||
toolName: 'OrientationMarker',
|
|
||||||
toolGroupId: 'mipToolGroup',
|
|
||||||
});
|
|
||||||
unsub1();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// For the hanging protocol we need to decide on the window level
|
// For the hanging protocol we need to decide on the window level
|
||||||
// based on whether the SUV is corrected or not, hence we can't hard
|
// based on whether the SUV is corrected or not, hence we can't hard
|
||||||
// code the window level in the hanging protocol but we add a custom
|
// code the window level in the hanging protocol but we add a custom
|
||||||
|
|||||||
@ -242,7 +242,7 @@ export default function ModeRoute({
|
|||||||
const lowerVal = val.toLowerCase();
|
const lowerVal = val.toLowerCase();
|
||||||
if (lowerVal !== 'studyinstanceuids') {
|
if (lowerVal !== 'studyinstanceuids') {
|
||||||
// Not sure why the case matters here - it doesn't in the URL
|
// Not sure why the case matters here - it doesn't in the URL
|
||||||
if (lowerVal === 'seriesinstanceuid') {
|
if (lowerVal === 'seriesinstanceuids') {
|
||||||
const seriesUIDs = getSplitParam(lowerVal, query);
|
const seriesUIDs = getSplitParam(lowerVal, query);
|
||||||
return {
|
return {
|
||||||
...acc,
|
...acc,
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import isSeriesFilterUsed from '../../utils/isSeriesFilterUsed';
|
|||||||
|
|
||||||
import { utils, Enums } from '@ohif/core';
|
import { utils, Enums } from '@ohif/core';
|
||||||
|
|
||||||
const { sortingCriteria } = utils;
|
const { sortingCriteria, getSplitParam } = utils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the route.
|
* Initialize the route.
|
||||||
@ -95,6 +95,18 @@ export async function defaultRouteInit(
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// is displaysets from URL and has initialSOPInstanceUID or initialSeriesInstanceUID
|
||||||
|
// then we need to wait for all display sets to be retrieved before applying the hanging protocol
|
||||||
|
const params = new URLSearchParams(window.location.search);
|
||||||
|
|
||||||
|
const initialSeriesInstanceUID = getSplitParam('initialseriesinstanceuid', params);
|
||||||
|
const initialSOPInstanceUID = getSplitParam('initialsopinstanceuid', params);
|
||||||
|
|
||||||
|
let displaySetFromUrl = false;
|
||||||
|
if (initialSeriesInstanceUID || initialSOPInstanceUID) {
|
||||||
|
displaySetFromUrl = true;
|
||||||
|
}
|
||||||
|
|
||||||
await Promise.allSettled(allRetrieves).then(async promises => {
|
await Promise.allSettled(allRetrieves).then(async promises => {
|
||||||
log.timeEnd(Enums.TimingEnum.STUDY_TO_DISPLAY_SETS);
|
log.timeEnd(Enums.TimingEnum.STUDY_TO_DISPLAY_SETS);
|
||||||
log.time(Enums.TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE);
|
log.time(Enums.TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE);
|
||||||
@ -109,7 +121,16 @@ export async function defaultRouteInit(
|
|||||||
|
|
||||||
promises.forEach(promise => {
|
promises.forEach(promise => {
|
||||||
const retrieveSeriesMetadataPromise = promise.value;
|
const retrieveSeriesMetadataPromise = promise.value;
|
||||||
if (Array.isArray(retrieveSeriesMetadataPromise)) {
|
if (!Array.isArray(retrieveSeriesMetadataPromise)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (displaySetFromUrl) {
|
||||||
|
const requiredSeriesPromises = retrieveSeriesMetadataPromise.map(promise =>
|
||||||
|
promise.start()
|
||||||
|
);
|
||||||
|
allPromises.push(Promise.allSettled(requiredSeriesPromises));
|
||||||
|
} else {
|
||||||
const { requiredSeries, remaining } = hangingProtocolService.filterSeriesRequiredForRun(
|
const { requiredSeries, remaining } = hangingProtocolService.filterSeriesRequiredForRun(
|
||||||
hangingProtocolId,
|
hangingProtocolId,
|
||||||
retrieveSeriesMetadataPromise
|
retrieveSeriesMetadataPromise
|
||||||
|
|||||||
@ -1314,19 +1314,18 @@ export default class HangingProtocolService extends PubSubService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Use the display set provided instead
|
// Use the display set provided instead
|
||||||
// Todo: find out what is wrong here
|
if (reuseDisplaySetUID) {
|
||||||
// if (reuseDisplaySetUID) {
|
if (viewportOptions.allowUnmatchedView !== true) {
|
||||||
// if (viewportOptions.allowUnmatchedView !== true) {
|
this.validateDisplaySetSelectMatch(viewportDisplaySet, id, reuseDisplaySetUID);
|
||||||
// this.validateDisplaySetSelectMatch(viewportDisplaySet, id, reuseDisplaySetUID);
|
}
|
||||||
// }
|
const displaySetInfo: HangingProtocol.DisplaySetInfo = {
|
||||||
// const displaySetInfo: HangingProtocol.DisplaySetInfo = {
|
displaySetInstanceUID: reuseDisplaySetUID,
|
||||||
// displaySetInstanceUID: reuseDisplaySetUID,
|
displaySetOptions,
|
||||||
// displaySetOptions,
|
};
|
||||||
// };
|
|
||||||
|
|
||||||
// displaySetsInfo.push(displaySetInfo);
|
displaySetsInfo.push(displaySetInfo);
|
||||||
// return;
|
return;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// Use the display set index to allow getting the "next" match, eg
|
// Use the display set index to allow getting the "next" match, eg
|
||||||
// matching all display sets, and get the matchedDisplaySetsIndex'th item
|
// matching all display sets, and get the matchedDisplaySetsIndex'th item
|
||||||
|
|||||||
@ -13,15 +13,16 @@ const isDisplaySetFromUrl = (displaySet): boolean => {
|
|||||||
if (!initialSeriesInstanceUID && !initialSOPInstanceUID) {
|
if (!initialSeriesInstanceUID && !initialSOPInstanceUID) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const isSeriesMatch =
|
|
||||||
!initialSeriesInstanceUID ||
|
const isSeriesMatch = initialSeriesInstanceUID?.some(
|
||||||
initialSeriesInstanceUID.some(seriesUID => displaySet.SeriesInstanceUID === seriesUID);
|
seriesUID => displaySet.SeriesInstanceUID === seriesUID
|
||||||
const isSopMatch =
|
);
|
||||||
!initialSOPInstanceUID ||
|
|
||||||
displaySet.instances?.some?.(instance =>
|
const isSopMatch = initialSOPInstanceUID?.some(sopUID =>
|
||||||
initialSOPInstanceUID.some(sopUID => sopUID === instance.SOPInstanceUID)
|
displaySet.instances?.some(instance => sopUID === instance.SOPInstanceUID)
|
||||||
);
|
);
|
||||||
return isSeriesMatch && isSopMatch;
|
|
||||||
|
return isSeriesMatch || isSopMatch;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Returns the index location of the requested image, or the defaultValue in this.
|
/** Returns the index location of the requested image, or the defaultValue in this.
|
||||||
|
|||||||
@ -28,7 +28,6 @@ export default function getPixelSpacingInformation(instance) {
|
|||||||
PixelSpacingCalibrationType,
|
PixelSpacingCalibrationType,
|
||||||
PixelSpacingCalibrationDescription,
|
PixelSpacingCalibrationDescription,
|
||||||
EstimatedRadiographicMagnificationFactor,
|
EstimatedRadiographicMagnificationFactor,
|
||||||
SequenceOfUltrasoundRegions,
|
|
||||||
} = instance;
|
} = instance;
|
||||||
const isProjection = projectionRadiographSOPClassUIDs.includes(SOPClassUID);
|
const isProjection = projectionRadiographSOPClassUIDs.includes(SOPClassUID);
|
||||||
|
|
||||||
@ -90,21 +89,6 @@ export default function getPixelSpacingInformation(instance) {
|
|||||||
PixelSpacing: CorrectedImagerPixelSpacing,
|
PixelSpacing: CorrectedImagerPixelSpacing,
|
||||||
isProjection,
|
isProjection,
|
||||||
};
|
};
|
||||||
} else if (SequenceOfUltrasoundRegions && typeof SequenceOfUltrasoundRegions === 'object') {
|
|
||||||
const { PhysicalDeltaX, PhysicalDeltaY } = SequenceOfUltrasoundRegions;
|
|
||||||
const USPixelSpacing = [PhysicalDeltaX * 10, PhysicalDeltaY * 10];
|
|
||||||
|
|
||||||
return {
|
|
||||||
PixelSpacing: USPixelSpacing,
|
|
||||||
};
|
|
||||||
} else if (
|
|
||||||
SequenceOfUltrasoundRegions &&
|
|
||||||
Array.isArray(SequenceOfUltrasoundRegions) &&
|
|
||||||
SequenceOfUltrasoundRegions.length > 1
|
|
||||||
) {
|
|
||||||
log.warn(
|
|
||||||
'Sequence of Ultrasound Regions > one entry. This is not yet implemented, all measurements will be shown in pixels.'
|
|
||||||
);
|
|
||||||
} else if (isProjection === false && !ImagerPixelSpacing) {
|
} else if (isProjection === false && !ImagerPixelSpacing) {
|
||||||
// If only Pixel Spacing is present, and this is not a projection radiograph,
|
// If only Pixel Spacing is present, and this is not a projection radiograph,
|
||||||
// we can stop here
|
// we can stop here
|
||||||
|
|||||||
@ -120,47 +120,74 @@ values.
|
|||||||
:::
|
:::
|
||||||
|
|
||||||
|
|
||||||
### SeriesInstanceUID, SeriesInstanceUIDs and initialSeriesInstanceUID
|
### SeriesInstanceUIDs
|
||||||
|
|
||||||
Sometimes you need to only retrieve a specific series in a study, you can do
|
Sometimes you need to only retrieve a specific series in a study, you can do
|
||||||
that by providing series level QIDO query parameters in the URL such as
|
that by providing series level QIDO query parameters in the URL such as
|
||||||
SeriesInstanceUID or SeriesNumber. This does NOT work with instance or study
|
SeriesInstanceUIDs. This does NOT work with instance or study
|
||||||
level parameters. For example:
|
level parameters. For example:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
http://localhost:3000/viewer?StudyInstanceUIDs=1.3.6.1.4.1.25403.345050719074.3824.20170125113417.1&SeriesInstanceUID=1.3.6.1.4.1.25403.345050719074.3824.20170125113545.4
|
http://localhost:3000/viewer?StudyInstanceUIDs=1.3.6.1.4.1.25403.345050719074.3824.20170125095438.5&SeriesInstanceUIDs=1.3.6.1.4.1.25403.345050719074.3824.20170125095449.8
|
||||||
```
|
```
|
||||||
|
|
||||||
This will only open the viewer with one series (one displaySet) loaded, and no
|
This will only open the viewer with one series (one displaySet) loaded, and no
|
||||||
queries made for any other series.
|
queries made for any other series.
|
||||||
|
|
||||||
Sometimes you need to only retrieve a subset of series in a study, you can do
|
Sometimes you need to only retrieve a subset of series in a study, you can do
|
||||||
that by providing study level wado query parameters in the URL such as
|
that by providing more than one series, separated by commas. For example:
|
||||||
SeriesInstanceUIDs. For example:
|
|
||||||
|
|
||||||
```js
|
```js
|
||||||
http://localhost:3000/viewer?StudyInstanceUIDs=1.3.6.1.4.1.25403.345050719074.3824.20170125113417.1&SeriesInstanceUIDs=1.3.6.1.4.1.25403.345050719074.3824.20170125113545.4,1.3.6.1.4.1.25403.345050719074.3824.20170125113545.5
|
http://localhost:3000/viewer?StudyInstanceUIDs=1.3.6.1.4.1.25403.345050719074.3824.20170125095438.5&SeriesInstanceUIDs=1.3.6.1.4.1.25403.345050719074.3824.20170125095449.8,1.3.6.1.4.1.25403.345050719074.3824.20170125095506.10
|
||||||
```
|
```
|
||||||
|
|
||||||
This will only open the viewer with two series (two displaySets) loaded, and no
|
This will only open the viewer with two series (two displaySets) loaded, and no
|
||||||
queries made for any other series.
|
queries made for any other series.
|
||||||
|
|
||||||
Alternatively, sometimes you want to just open the study on a specified series
|
### initialSeriesInstanceUID
|
||||||
and/or display a particular sop instance, which you can accomplish using:
|
|
||||||
`initialSeriesInstanceUID` and/or `initialSOPInstanceUID`
|
Alternatively, sometimes you want to just open the study on a specified series, but allowing other
|
||||||
to select the series to open on, but allowing other
|
series to be present too. This is the same behavior can be
|
||||||
series to be present in the study browser panel. This is the same behaviour
|
achieved by using the `initialSeriesInstanceUID` parameter. For example:
|
||||||
as in OHIF 2.0, albeit on different URL parameters. For example:
|
|
||||||
|
|
||||||
```js
|
```js
|
||||||
http://localhost:3000/viewer?StudyInstanceUIDs=1.3.6.1.4.1.25403.345050719074.3824.20170125113417.1&initialSeriesInstanceUID=1.3.6.1.4.1.25403.345050719074.3824.20170125113545.4
|
http://localhost:3000/viewer?StudyInstanceUIDs=1.3.6.1.4.1.25403.345050719074.3824.20170125095438.5&initialSeriesInstanceUID=1.3.6.1.4.1.25403.345050719074.3824.20170125095449.8
|
||||||
```
|
```
|
||||||
|
|
||||||
|
This will open all the series in the study, but the viewer will start with the
|
||||||
|
series specified by the `initialSeriesInstanceUID` parameter.
|
||||||
|
|
||||||
|
|
||||||
Note that you can combine these, if you want to load a specific set of series
|
Note that you can combine these, if you want to load a specific set of series
|
||||||
plus show an initial one as the first one selected, for example:
|
plus show an initial one as the first one selected, for example:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
http://localhost:3000/viewer?StudyInstanceUIDs=1.3.6.1.4.1.25403.345050719074.3824.20170125113417.1&SeriesInstanceUID=1.3.6.1.4.1.25403.345050719074.3824.20170125113545.4&initialSopInstanceUID=1.3.6.1.4.1.25403.345050719074.3824.20170125113546.1
|
http://localhost:3000/viewer?StudyInstanceUIDs=1.3.6.1.4.1.25403.345050719074.3824.20170125095438.5&SeriesInstanceUIDs=1.3.6.1.4.1.25403.345050719074.3824.20170125095449.8,1.3.6.1.4.1.25403.345050719074.3824.20170125095506.10&initialSeriesInstanceUID=1.3.6.1.4.1.25403.345050719074.3824.20170125095506.10
|
||||||
|
```
|
||||||
|
|
||||||
|
### initialSopInstanceUID
|
||||||
|
|
||||||
|
You can also specify the initial SOP Instance to be displayed by using the
|
||||||
|
`initialSopInstanceUID` parameter. For example:
|
||||||
|
|
||||||
|
```js
|
||||||
|
http://localhost:3000/viewer?StudyInstanceUIDs=1.3.6.1.4.1.25403.345050719074.3824.20170125095438.5&SeriesInstanceUIDs=1.3.6.1.4.1.25403.345050719074.3824.20170125095449.8&initialSopInstanceUID=1.3.6.1.4.1.25403.345050719074.3824.20170125095501.9
|
||||||
|
```
|
||||||
|
|
||||||
|
This will open the study with the filtered series, and navigate to the slice 101
|
||||||
|
which happens to be the SOP Instance specified by the `initialSopInstanceUID`
|
||||||
|
|
||||||
|
Note: again you can mix and match
|
||||||
|
|
||||||
|
```js
|
||||||
|
http://localhost:3000/viewer?StudyInstanceUIDs=1.3.6.1.4.1.25403.345050719074.3824.20170125095438.5&SeriesInstanceUIDs=1.3.6.1.4.1.25403.345050719074.3824.20170125095449.8,1.3.6.1.4.1.25403.345050719074.3824.20170125095506.10&initialSeriesInstanceUID=1.3.6.1.4.1.25403.345050719074.3824.20170125095506.10&initialSopInstanceUID=1.3.6.1.4.1.25403.345050719074.3824.20170125095510.8
|
||||||
|
```
|
||||||
|
|
||||||
|
You can even load the whole study and only specify the initial SOP Instance to be displayed. Although
|
||||||
|
it will take more time to match, but it works as expected.
|
||||||
|
|
||||||
|
```js
|
||||||
|
http://localhost:3000/viewer?StudyInstanceUIDs=1.3.6.1.4.1.25403.345050719074.3824.20170125095438.5&initialSopInstanceUID=1.3.6.1.4.1.25403.345050719074.3824.20170125095510.8
|
||||||
```
|
```
|
||||||
|
|
||||||
### hangingProtocolId
|
### hangingProtocolId
|
||||||
@ -169,6 +196,11 @@ You can select the initial hanging protocol to apply by using the
|
|||||||
hangingProtocolId parameter. The selected parameter must be available in a
|
hangingProtocolId parameter. The selected parameter must be available in a
|
||||||
hangingProtocolModule registration, but does not have to be active.
|
hangingProtocolModule registration, but does not have to be active.
|
||||||
|
|
||||||
|
For instance for loading a specific study in mpr mode from start you can use:
|
||||||
|
|
||||||
|
```js
|
||||||
|
http://localhost:3000/viewer?StudyInstanceUIDs=1.3.6.1.4.1.25403.345050719074.3824.20170125095438.5&hangingProtocolId=@ohif/mnGrid
|
||||||
|
```
|
||||||
|
|
||||||
### token
|
### token
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user