From 7b7b63de93a5a0538ff593929c5365bb8f53d096 Mon Sep 17 00:00:00 2001 From: "James A. Petts" Date: Thu, 25 Jun 2020 11:15:33 +0100 Subject: [PATCH 1/4] WIP Measurements service. --- extensions/cornerstone/src/init.js | 47 +++-- .../ArrowAnnotate.js | 49 +++++ .../Bidirectional.js | 50 +++++ .../EllipticalRoi.js | 74 +++++++ .../measurementServiceMappings/Length.js | 79 ++++++++ .../constants/supportedTools.js | 1 + .../measurementServiceMappingsFactory.js | 190 +++++++----------- .../utils/getHandlesFromPoints.js | 5 + .../utils/getPointsFromHandles.js | 12 ++ .../utils/getSOPInstanceAttributes.js | 14 ++ .../PanelMeasurementTableTracking/index.js | 64 ++++-- .../MeasurementService/MeasurementService.js | 5 + .../MeasurementTable/MeasurementTable.jsx | 14 +- 13 files changed, 453 insertions(+), 151 deletions(-) create mode 100644 extensions/cornerstone/src/utils/measurementServiceMappings/ArrowAnnotate.js create mode 100644 extensions/cornerstone/src/utils/measurementServiceMappings/Bidirectional.js create mode 100644 extensions/cornerstone/src/utils/measurementServiceMappings/EllipticalRoi.js create mode 100644 extensions/cornerstone/src/utils/measurementServiceMappings/Length.js create mode 100644 extensions/cornerstone/src/utils/measurementServiceMappings/constants/supportedTools.js create mode 100644 extensions/cornerstone/src/utils/measurementServiceMappings/utils/getHandlesFromPoints.js create mode 100644 extensions/cornerstone/src/utils/measurementServiceMappings/utils/getPointsFromHandles.js create mode 100644 extensions/cornerstone/src/utils/measurementServiceMappings/utils/getSOPInstanceAttributes.js diff --git a/extensions/cornerstone/src/init.js b/extensions/cornerstone/src/init.js index 48809b2b4..5f80a9d6f 100644 --- a/extensions/cornerstone/src/init.js +++ b/extensions/cornerstone/src/init.js @@ -90,6 +90,7 @@ export default function init({ servicesManager, configuration }) { /* Add extension tools configuration here. */ const internalToolsConfig = { + /* TODO ArrowAnnotate input ArrowAnnotate: { configuration: { getTextCallback: (callback, eventDetails) => @@ -98,6 +99,7 @@ export default function init({ servicesManager, configuration }) { callInputDialog(data, eventDetails, callback), }, }, + */ }; /* Abstract tools configuration using extension configuration. */ @@ -166,27 +168,48 @@ export default function init({ servicesManager, configuration }) { const _initMeasurementService = measurementService => { /* Initialization */ - const { toAnnotation, toMeasurement } = measurementServiceMappingsFactory( - measurementService - ); + const { + Length, + Bidirectional, + EllipticalRoi, + ArrowAnnotate, + } = measurementServiceMappingsFactory(measurementService); const csToolsVer4MeasurementSource = measurementService.createSource( 'CornerstoneTools', '4' ); - /* Matching Criterias */ - const matchingCriteria = { - valueType: measurementService.VALUE_TYPES.POLYLINE, - points: 2, - }; - /* Mappings */ measurementService.addMapping( csToolsVer4MeasurementSource, 'Length', - matchingCriteria, - toAnnotation, - toMeasurement + Length.matchingCriteria, + Length.toAnnotation, + Length.toMeasurement + ); + + measurementService.addMapping( + csToolsVer4MeasurementSource, + 'Bidirectional', + Bidirectional.matchingCriteria, + Bidirectional.toAnnotation, + Bidirectional.toMeasurement + ); + + measurementService.addMapping( + csToolsVer4MeasurementSource, + 'EllipticalRoi', + EllipticalRoi.matchingCriteria, + EllipticalRoi.toAnnotation, + EllipticalRoi.toMeasurement + ); + + measurementService.addMapping( + csToolsVer4MeasurementSource, + 'ArrowAnnotate', + ArrowAnnotate.matchingCriteria, + ArrowAnnotate.toAnnotation, + ArrowAnnotate.toMeasurement ); return csToolsVer4MeasurementSource; diff --git a/extensions/cornerstone/src/utils/measurementServiceMappings/ArrowAnnotate.js b/extensions/cornerstone/src/utils/measurementServiceMappings/ArrowAnnotate.js new file mode 100644 index 000000000..b1d9f2f44 --- /dev/null +++ b/extensions/cornerstone/src/utils/measurementServiceMappings/ArrowAnnotate.js @@ -0,0 +1,49 @@ +import SUPPORTED_TOOLS from './constants/supportedTools'; +import getHandlesFromPoints from './utils/getHandlesFromPoints'; +import getPointsFromHandles from './utils/getPointsFromHandles'; +import getSOPInstanceAttributes from './utils/getSOPInstanceAttributes'; + +const ArrowAnnotate = { + toAnnotation: (measurement, definition) => { + // TODO -> Implement when this is needed. + }, + toMeasurement: (csToolsAnnotation, getValueTypeFromToolType) => { + const { element, measurementData } = csToolsAnnotation; + const tool = + csToolsAnnotation.toolType || + csToolsAnnotation.toolName || + measurementData.toolType; + + const validToolType = toolName => SUPPORTED_TOOLS.includes(toolName); + + if (!validToolType(tool)) { + throw new Error('Tool not supported'); + } + + const { + SOPInstanceUID, + FrameOfReferenceUID, + SeriesInstanceUID, + StudyInstanceUID, + } = getSOPInstanceAttributes(element); + + const points = []; + points.push(measurementData.handles); + + return { + id: measurementData._measurementServiceId, + SOPInstanceUID: SOPInstanceUID, + FrameOfReferenceUID, + referenceSeriesUID: SeriesInstanceUID, + referenceStudyUID: StudyInstanceUID, + label: measurementData.text, + description: measurementData.description, + unit: measurementData.unit, + text: measurementData.text, + type: getValueTypeFromToolType(tool), + points: getPointsFromHandles(measurementData.handles), + }; + }, +}; + +export default ArrowAnnotate; diff --git a/extensions/cornerstone/src/utils/measurementServiceMappings/Bidirectional.js b/extensions/cornerstone/src/utils/measurementServiceMappings/Bidirectional.js new file mode 100644 index 000000000..5b6f3e893 --- /dev/null +++ b/extensions/cornerstone/src/utils/measurementServiceMappings/Bidirectional.js @@ -0,0 +1,50 @@ +import SUPPORTED_TOOLS from './constants/supportedTools'; +import getSOPInstanceAttributes from './utils/getSOPInstanceAttributes'; + +const Bidirectional = { + toAnnotation: (measurement, definition) => { + // TODO -> Implement when this is needed. + }, + toMeasurement: (csToolsAnnotation, getValueTypeFromToolType) => { + const { element, measurementData } = csToolsAnnotation; + const tool = + csToolsAnnotation.toolType || + csToolsAnnotation.toolName || + measurementData.toolType; + + const validToolType = toolName => SUPPORTED_TOOLS.includes(toolName); + + if (!validToolType(tool)) { + throw new Error('Tool not supported'); + } + + const { + SOPInstanceUID, + FrameOfReferenceUID, + SeriesInstanceUID, + StudyInstanceUID, + } = getSOPInstanceAttributes(element); + + const { handles } = measurementData; + + const longAxis = [handles.start, handles.end]; + const shortAxis = [handles.perpendicularStart, handles.perpendicularEnd]; + + return { + id: measurementData._measurementServiceId, + SOPInstanceUID: SOPInstanceUID, + FrameOfReferenceUID, + referenceSeriesUID: SeriesInstanceUID, + referenceStudyUID: StudyInstanceUID, + label: measurementData.text, + description: measurementData.description, + unit: measurementData.unit, + shortestDiameter: measurementData.shortestDiameter, + longestDiameter: measurementData.longestDiameter, + type: getValueTypeFromToolType(tool), + points: { longAxis, shortAxis }, + }; + }, +}; + +export default Bidirectional; diff --git a/extensions/cornerstone/src/utils/measurementServiceMappings/EllipticalRoi.js b/extensions/cornerstone/src/utils/measurementServiceMappings/EllipticalRoi.js new file mode 100644 index 000000000..dbd4d2403 --- /dev/null +++ b/extensions/cornerstone/src/utils/measurementServiceMappings/EllipticalRoi.js @@ -0,0 +1,74 @@ +import SUPPORTED_TOOLS from './constants/supportedTools'; +import getSOPInstanceAttributes from './utils/getSOPInstanceAttributes'; + +const EllipticalRoi = { + toAnnotation: (measurement, definition) => { + // TODO -> Implement when this is needed. + }, + toMeasurement: (csToolsAnnotation, getValueTypeFromToolType) => { + const { element, measurementData } = csToolsAnnotation; + const tool = + csToolsAnnotation.toolType || + csToolsAnnotation.toolName || + measurementData.toolType; + + const validToolType = toolName => SUPPORTED_TOOLS.includes(toolName); + + if (!validToolType(tool)) { + throw new Error('Tool not supported'); + } + + const { + SOPInstanceUID, + FrameOfReferenceUID, + SeriesInstanceUID, + StudyInstanceUID, + } = getSOPInstanceAttributes(element); + + const { start, end } = measurementData.handles; + + const halfXLength = Math.abs(start.x - end.x) / 2; + const halfYLength = Math.abs(start.y - end.y) / 2; + + const points = []; + const center = { x: (start.x + end.x) / 2, y: (start.y + end.y) / 2 }; + + // To store similar to SR. + if (halfXLength > halfYLength) { + // X-axis major + // Major axis + points.push({ x: center.x - halfXLength, y: center.y }); + points.push({ x: center.x + halfXLength, y: center.y }); + // Minor axis + points.push({ x: center.x, y: center.y - halfYLength }); + points.push({ x: center.x, y: center.y + halfYLength }); + } else { + // Y-axis major + // Major axis + points.push({ x: center.x, y: center.y - halfYLength }); + points.push({ x: center.x, y: center.y + halfYLength }); + // Minor axis + points.push({ x: center.x - halfXLength, y: center.y }); + points.push({ x: center.x + halfXLength, y: center.y }); + } + + return { + id: measurementData._measurementServiceId, + SOPInstanceUID: SOPInstanceUID, + FrameOfReferenceUID, + referenceSeriesUID: SeriesInstanceUID, + referenceStudyUID: StudyInstanceUID, + label: measurementData.text, + description: measurementData.description, + unit: measurementData.unit, + area: + measurementData.cachedStats && + measurementData.cachedStats + .area /* TODO: Add concept names instead (descriptor) */, + type: getValueTypeFromToolType(tool), + points, + }; + }, +}; + +export default EllipticalRoi; diff --git a/extensions/cornerstone/src/utils/measurementServiceMappings/Length.js b/extensions/cornerstone/src/utils/measurementServiceMappings/Length.js new file mode 100644 index 000000000..5f9eda649 --- /dev/null +++ b/extensions/cornerstone/src/utils/measurementServiceMappings/Length.js @@ -0,0 +1,79 @@ +import SUPPORTED_TOOLS from './constants/supportedTools'; +import getHandlesFromPoints from './utils/getHandlesFromPoints'; +import getPointsFromHandles from './utils/getPointsFromHandles'; +import getSOPInstanceAttributes from './utils/getSOPInstanceAttributes'; + +const Length = { + toAnnotation: (measurement, definition) => { + const { + id, + label, + description, + points, + unit, + SOPInstanceUID, + FrameOfReferenceUID, + referenceSeriesUID, + } = measurement; + + return { + toolName: definition, + measurementData: { + sopInstanceUid: SOPInstanceUID, + frameOfReferenceUID: FrameOfReferenceUID, + SeriesInstanceUID: referenceSeriesUID, + unit, + text: label, + description, + handles: getHandlesFromPoints(points), + _measurementServiceId: id, + }, + }; + }, + + /** + * Maps cornerstone annotation event data to measurement service format. + * + * @param {Object} cornerstone Cornerstone event data + * @return {Measurement} Measurement instance + */ + toMeasurement: (csToolsAnnotation, getValueTypeFromToolType) => { + const { element, measurementData } = csToolsAnnotation; + const tool = + csToolsAnnotation.toolType || + csToolsAnnotation.toolName || + measurementData.toolType; + + const validToolType = toolName => SUPPORTED_TOOLS.includes(toolName); + + if (!validToolType(tool)) { + throw new Error('Tool not supported'); + } + + const { + SOPInstanceUID, + FrameOfReferenceUID, + SeriesInstanceUID, + StudyInstanceUID, + } = getSOPInstanceAttributes(element); + + const points = []; + points.push(measurementData.handles); + + return { + id: measurementData._measurementServiceId, + SOPInstanceUID: SOPInstanceUID, + FrameOfReferenceUID, + referenceSeriesUID: SeriesInstanceUID, + referenceStudyUID: StudyInstanceUID, + label: measurementData.text, + description: measurementData.description, + unit: measurementData.unit, + length: measurementData.length, + type: getValueTypeFromToolType(tool), + points: getPointsFromHandles(measurementData.handles), + }; + }, +}; + +export default Length; diff --git a/extensions/cornerstone/src/utils/measurementServiceMappings/constants/supportedTools.js b/extensions/cornerstone/src/utils/measurementServiceMappings/constants/supportedTools.js new file mode 100644 index 000000000..a1ca5e83e --- /dev/null +++ b/extensions/cornerstone/src/utils/measurementServiceMappings/constants/supportedTools.js @@ -0,0 +1 @@ +export default ['Length', 'EllipticalRoi', 'Bidirectional', 'ArrowAnnotate']; diff --git a/extensions/cornerstone/src/utils/measurementServiceMappings/measurementServiceMappingsFactory.js b/extensions/cornerstone/src/utils/measurementServiceMappings/measurementServiceMappingsFactory.js index f279eaec0..6e325fb19 100644 --- a/extensions/cornerstone/src/utils/measurementServiceMappings/measurementServiceMappingsFactory.js +++ b/extensions/cornerstone/src/utils/measurementServiceMappings/measurementServiceMappingsFactory.js @@ -1,11 +1,7 @@ -import cornerstone from 'cornerstone-core'; - -const SUPPORTED_TOOLS = [ - 'Length', - 'EllipticalRoi', - 'RectangleRoi', - 'ArrowAnnotate', -]; +import Length from './Length'; +import Bidirectional from './Bidirectional'; +import ArrowAnnotate from './ArrowAnnotate'; +import EllipticalRoi from './EllipticalRoi'; const measurementServiceMappingsFactory = measurementService => { /** @@ -15,129 +11,87 @@ const measurementServiceMappingsFactory = measurementService => { * @param {string} definition The source definition * @return {Object} Cornerstone annotation data */ - const toAnnotation = (measurement, definition) => { - const { - id, - label, - description, - points, - unit, - SOPInstanceUID, - FrameOfReferenceUID, - referenceSeriesUID, - } = measurement; - - return { - toolName: definition, - measurementData: { - sopInstanceUid: SOPInstanceUID, - frameOfReferenceUID: FrameOfReferenceUID, - SeriesInstanceUID: referenceSeriesUID, - unit, - text: label, - description, - handles: _getHandlesFromPoints(points), - _measurementServiceId: id, - }, - }; - }; - - /** - * Maps cornerstone annotation event data to measurement service format. - * - * @param {Object} cornerstone Cornerstone event data - * @return {Measurement} Measurement instance - */ - const toMeasurement = csToolsAnnotation => { - const { element, measurementData } = csToolsAnnotation; - const tool = - csToolsAnnotation.toolType || - csToolsAnnotation.toolName || - measurementData.toolType; - - const validToolType = toolName => SUPPORTED_TOOLS.includes(toolName); - - if (!validToolType(tool)) { - throw new Error('Tool not supported'); - } - - const { - SOPInstanceUID, - FrameOfReferenceUID, - SeriesInstanceUID, - StudyInstanceUID, - } = _getAttributes(element); - - const points = []; - points.push(measurementData.handles); - - return { - id: measurementData._measurementServiceId, - SOPInstanceUID: SOPInstanceUID, - FrameOfReferenceUID, - referenceSeriesUID: SeriesInstanceUID, - referenceStudyUID: StudyInstanceUID, - label: measurementData.text, - description: measurementData.description, - unit: measurementData.unit, - area: - measurementData.cachedStats && - measurementData.cachedStats - .area /* TODO: Add concept names instead (descriptor) */, - type: _getValueTypeFromToolType(tool), - points: _getPointsFromHandles(measurementData.handles), - }; - }; - - const _getAttributes = element => { - const enabledElement = cornerstone.getEnabledElement(element); - const imageId = enabledElement.image.imageId; - const instance = cornerstone.metaData.get('instance', imageId); - - return { - SOPInstanceUID: instance.SOPInstanceUID, - FrameOfReferenceUID: instance.FrameOfReferenceUID, - SeriesInstanceUID: instance.SeriesInstanceUID, - StudyInstanceUID: instance.StudyInstanceUID, - }; - }; const _getValueTypeFromToolType = toolType => { - const { POLYLINE, ELLIPSE, POINT } = measurementService.VALUE_TYPES; + const { + POLYLINE, + ELLIPSE, + POINT, + BIDIRECTIONAL, + } = measurementService.VALUE_TYPES; - /* TODO: Relocate static value types */ + // TODO -> I get why this was attemped, but its not nearly flexible enough. + // A single measurement may have an ellipse + a bidirectional measurement, for instances. + // You can't define a bidirectional tool as a single type.. const TOOL_TYPE_TO_VALUE_TYPE = { Length: POLYLINE, EllipticalRoi: ELLIPSE, - RectangleRoi: POLYLINE, + Bidirectional: BIDIRECTIONAL, ArrowAnnotate: POINT, }; return TOOL_TYPE_TO_VALUE_TYPE[toolType]; }; - const _getPointsFromHandles = handles => { - let points = []; - Object.keys(handles).map(handle => { - if (['start', 'end'].includes(handle)) { - let point = {}; - if (handles[handle].x) point.x = handles[handle].x; - if (handles[handle].y) point.y = handles[handle].y; - points.push(point); - } - }); - return points; - }; - - const _getHandlesFromPoints = points => { - return points - .map((p, i) => (i % 10 === 0 ? { start: p } : { end: p })) - .reduce((obj, item) => Object.assign(obj, { ...item }), {}); - }; - return { - toAnnotation, - toMeasurement, + Length: { + toAnnotation: Length.toAnnotation, + toMeasurement: csToolsAnnotation => + Length.toMeasurement(csToolsAnnotation, _getValueTypeFromToolType), + matchingCriteria: [ + { + valueType: measurementService.VALUE_TYPES.POLYLINE, + points: 2, + }, + ], + }, + Bidirectional: { + toAnnotation: Bidirectional.toAnnotation, + toMeasurement: csToolsAnnotation => + Bidirectional.toMeasurement( + csToolsAnnotation, + _getValueTypeFromToolType + ), + matchingCriteria: [ + // TODO -> We should eventually do something like shortAxis + longAxis, + // But its still a little unclear how these automatic interpretations will work. + { + valueType: measurementService.VALUE_TYPES.POLYLINE, + points: 2, + }, + { + valueType: measurementService.VALUE_TYPES.POLYLINE, + points: 2, + }, + ], + }, + ArrowAnnotate: { + toAnnotation: ArrowAnnotate.toAnnotation, + toMeasurement: csToolsAnnotation => + ArrowAnnotate.toMeasurement( + csToolsAnnotation, + _getValueTypeFromToolType + ), + matchingCriteria: [ + { + valueType: measurementService.VALUE_TYPES.POINT, + points: 1, + }, + ], + }, + EllipticalRoi: { + toAnnotation: EllipticalRoi.toAnnotation, + toMeasurement: csToolsAnnotation => + EllipticalRoi.toMeasurement( + csToolsAnnotation, + _getValueTypeFromToolType + ), + matchingCriteria: [ + { + valueType: measurementService.VALUE_TYPES.ELLIPSE, + }, + ], + }, }; }; diff --git a/extensions/cornerstone/src/utils/measurementServiceMappings/utils/getHandlesFromPoints.js b/extensions/cornerstone/src/utils/measurementServiceMappings/utils/getHandlesFromPoints.js new file mode 100644 index 000000000..1440405bc --- /dev/null +++ b/extensions/cornerstone/src/utils/measurementServiceMappings/utils/getHandlesFromPoints.js @@ -0,0 +1,5 @@ +export default function getHandlesFromPoints(points) { + return points + .map((p, i) => (i % 10 === 0 ? { start: p } : { end: p })) + .reduce((obj, item) => Object.assign(obj, { ...item }), {}); +} diff --git a/extensions/cornerstone/src/utils/measurementServiceMappings/utils/getPointsFromHandles.js b/extensions/cornerstone/src/utils/measurementServiceMappings/utils/getPointsFromHandles.js new file mode 100644 index 000000000..dfd4d5092 --- /dev/null +++ b/extensions/cornerstone/src/utils/measurementServiceMappings/utils/getPointsFromHandles.js @@ -0,0 +1,12 @@ +export default function getPointsFromHandles(handles) { + let points = []; + Object.keys(handles).map(handle => { + if (['start', 'end'].includes(handle)) { + let point = {}; + if (handles[handle].x) point.x = handles[handle].x; + if (handles[handle].y) point.y = handles[handle].y; + points.push(point); + } + }); + return points; +} diff --git a/extensions/cornerstone/src/utils/measurementServiceMappings/utils/getSOPInstanceAttributes.js b/extensions/cornerstone/src/utils/measurementServiceMappings/utils/getSOPInstanceAttributes.js new file mode 100644 index 000000000..01ca8ffd9 --- /dev/null +++ b/extensions/cornerstone/src/utils/measurementServiceMappings/utils/getSOPInstanceAttributes.js @@ -0,0 +1,14 @@ +import cornerstone from 'cornerstone-core'; + +export default function getSOPInstanceAttributes(element) { + const enabledElement = cornerstone.getEnabledElement(element); + const imageId = enabledElement.image.imageId; + const instance = cornerstone.metaData.get('instance', imageId); + + return { + SOPInstanceUID: instance.SOPInstanceUID, + FrameOfReferenceUID: instance.FrameOfReferenceUID, + SeriesInstanceUID: instance.SeriesInstanceUID, + StudyInstanceUID: instance.StudyInstanceUID, + }; +} diff --git a/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking/index.js b/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking/index.js index ece666e80..edfd44aa1 100644 --- a/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking/index.js +++ b/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking/index.js @@ -42,7 +42,7 @@ function PanelMeasurementTableTracking({ servicesManager, commandsManager }) { trackedSeries.includes(m.referenceSeriesUID) ); const mappedMeasurements = filteredMeasurements.map((m, index) => - _mapMeasurementToDisplay(m, index) + _mapMeasurementToDisplay(m, index, MeasurementService.VALUE_TYPES) ); setDisplayMeasurements(mappedMeasurements); // eslint-ignore-next-line @@ -130,7 +130,7 @@ function PanelMeasurementTableTracking({ servicesManager, commandsManager }) { PanelMeasurementTableTracking.propTypes = {}; // TODO: This could be a MeasurementService mapper -function _mapMeasurementToDisplay(measurement, index) { +function _mapMeasurementToDisplay(measurement, index, types) { const { id, label, @@ -153,12 +153,14 @@ function _mapMeasurementToDisplay(measurement, index) { return { id: index + 1, label: '(empty)', // 'Label short description', - displayText: _getDisplayText( - measurement.points, - PixelSpacing, - SeriesNumber, - InstanceNumber - ), + displayText: + _getDisplayText( + measurement, + PixelSpacing, + SeriesNumber, + InstanceNumber, + types + ) || [], // TODO: handle one layer down isActive: false, // activeMeasurementItem === i + 1, }; @@ -169,7 +171,13 @@ function _mapMeasurementToDisplay(measurement, index) { * @param {*} points * @param {*} pixelSpacing */ -function _getDisplayText(points, pixelSpacing, seriesNumber, instanceNumber) { +function _getDisplayText( + measurement, + pixelSpacing, + seriesNumber, + instanceNumber, + types +) { // TODO: determination of shape influences text // Length: 'xx.x unit (S:x, I:x)' // Rectangle: 'xx.x x xx.x unit (S:x, I:x)', @@ -177,6 +185,8 @@ function _getDisplayText(points, pixelSpacing, seriesNumber, instanceNumber) { // Bidirectional? // Freehand? + const { type, points } = measurement; + const hasPixelSpacing = pixelSpacing !== undefined && Array.isArray(pixelSpacing) && @@ -186,13 +196,37 @@ function _getDisplayText(points, pixelSpacing, seriesNumber, instanceNumber) { : [1, 1]; const unit = hasPixelSpacing ? 'mm' : 'px'; - const { x: x1, y: y1 } = points[0]; - const { x: x2, y: y2 } = points[1]; - const dx = (x2 - x1) * colPixelSpacing; - const dy = (y2 - y1) * rowPixelSpacing; - const length = _round(Math.sqrt(dx * dx + dy * dy), 1); + switch (type) { + case types.POLYLINE: + const { length } = measurement; - return `${length} ${unit} (S:${seriesNumber}, I:${instanceNumber})`; + const roundedLength = _round(length, 1); + + return [ + `${roundedLength} ${unit} (S:${seriesNumber}, I:${instanceNumber})`, + ]; + + case types.BIDIRECTIONAL: + const { shortestDiameter, longestDiameter } = measurement; + + const roundedShortestDiameter = _round(shortestDiameter, 1); + const roundedLongestDiameter = _round(longestDiameter, 1); + + return [ + `l: ${roundedLongestDiameter} ${unit} (S:${seriesNumber}, I:${instanceNumber})`, + `s: ${roundedShortestDiameter} ${unit}`, + ]; + case types.ELLIPSE: + const { area } = measurement; + + const roundedArea = _round(area, 1); + return [ + `${roundedArea} ${unit}2 (S:${seriesNumber}, I:${instanceNumber})`, + ]; + case types.POINT: + const { text } = measurement; + return [`${text} (S:${seriesNumber}, I:${instanceNumber})`]; + } } function _round(value, decimals) { diff --git a/platform/core/src/services/MeasurementService/MeasurementService.js b/platform/core/src/services/MeasurementService/MeasurementService.js index 4688a9849..ba9ddcf61 100644 --- a/platform/core/src/services/MeasurementService/MeasurementService.js +++ b/platform/core/src/services/MeasurementService/MeasurementService.js @@ -40,6 +40,10 @@ const MEASUREMENT_SCHEMA_KEYS = [ 'type', 'unit', 'area', // TODO: Add concept names instead (descriptor) + 'length', + 'shortestDiameter', + 'longestDiameter', + 'text', // NOTE: There is nothing like this in SR. 'points', 'source', ]; @@ -53,6 +57,7 @@ const EVENTS = { const VALUE_TYPES = { POLYLINE: 'value_type::polyline', POINT: 'value_type::point', + BIDIRECTIONAL: 'value_type::shortAxisLongAxis', // TODO -> Discuss with Danny. => just using SCOORD values isn't enough here. ELLIPSE: 'value_type::ellipse', MULTIPOINT: 'value_type::multipoint', CIRCLE: 'value_type::circle', diff --git a/platform/ui/src/components/MeasurementTable/MeasurementTable.jsx b/platform/ui/src/components/MeasurementTable/MeasurementTable.jsx index 95ba02982..9898c504d 100644 --- a/platform/ui/src/components/MeasurementTable/MeasurementTable.jsx +++ b/platform/ui/src/components/MeasurementTable/MeasurementTable.jsx @@ -14,7 +14,7 @@ const MeasurementTable = ({ data, title, amount, onClick, onEdit }) => {
{!!data.length && - data.map((measurementItem) => { + data.map(measurementItem => { const { id, label, displayText, isActive } = measurementItem; return (
{ {label} - - {displayText} - + {displayText.map(line => ( + + {line} + + ))} { right: 4, transform: isActive ? '' : 'translateX(100%)', }} - onClick={(e) => { + onClick={e => { // stopPropagation needed to avoid disable the current active item e.stopPropagation(); onEdit(id); @@ -108,7 +110,7 @@ MeasurementTable.propTypes = { PropTypes.shape({ id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), label: PropTypes.string, - displayText: PropTypes.string, + displayText: PropTypes.arrayOf(PropTypes.string), isActive: PropTypes.bool, }) ), From 74c14622715acb36ec95141250742afc3511c0b1 Mon Sep 17 00:00:00 2001 From: "James A. Petts" Date: Thu, 25 Jun 2020 17:21:03 +0100 Subject: [PATCH 2/4] Working local download, STOW to PACS an view in SR Viewport. --- extensions/cornerstone/package.json | 2 +- .../ArrowAnnotate.js | 1 - extensions/default/package.json | 2 +- .../default/src/DicomWebDataSource/index.js | 33 ++++- .../src/Panels/WrappedPanelStudyBrowser.jsx | 2 +- extensions/dicom-html/package.json | 2 +- extensions/dicom-rt/package.json | 2 +- extensions/dicom-segmentation/package.json | 2 +- extensions/dicom-sr/package.json | 2 +- extensions/measurement-tracking/package.json | 5 +- .../ActionButtons.jsx | 16 ++- .../PanelMeasurementTableTracking/index.js | 43 ++++++- extensions/vtk/package.json | 2 +- platform/core/package.json | 2 +- platform/core/src/DICOMSR/dataExchange.js | 116 +++++++++++++++++- platform/core/src/DICOMSR/index.js | 7 +- .../core/src/DataSources/IWebApiDataSource.js | 12 +- platform/viewer/package.json | 2 +- yarn.lock | 8 +- 19 files changed, 232 insertions(+), 29 deletions(-) diff --git a/extensions/cornerstone/package.json b/extensions/cornerstone/package.json index 4dd657d40..b09d30d55 100644 --- a/extensions/cornerstone/package.json +++ b/extensions/cornerstone/package.json @@ -34,7 +34,7 @@ "cornerstone-core": "^2.3.0", "cornerstone-math": "^0.1.8", "cornerstone-tools": "4.15.1", - "dcmjs": "^0.12.3", + "dcmjs": "0.14.0", "cornerstone-wado-image-loader": "^3.1.2", "dicom-parser": "^1.8.3", "hammerjs": "^2.0.8", diff --git a/extensions/cornerstone/src/utils/measurementServiceMappings/ArrowAnnotate.js b/extensions/cornerstone/src/utils/measurementServiceMappings/ArrowAnnotate.js index b1d9f2f44..7d2c1c3b2 100644 --- a/extensions/cornerstone/src/utils/measurementServiceMappings/ArrowAnnotate.js +++ b/extensions/cornerstone/src/utils/measurementServiceMappings/ArrowAnnotate.js @@ -1,5 +1,4 @@ import SUPPORTED_TOOLS from './constants/supportedTools'; -import getHandlesFromPoints from './utils/getHandlesFromPoints'; import getPointsFromHandles from './utils/getPointsFromHandles'; import getSOPInstanceAttributes from './utils/getSOPInstanceAttributes'; diff --git a/extensions/default/package.json b/extensions/default/package.json index e9633cdc1..09342a611 100644 --- a/extensions/default/package.json +++ b/extensions/default/package.json @@ -32,7 +32,7 @@ "react": "^16.13.1", "react-dom": "^16.13.1", "webpack": "^4.0.0", - "dcmjs": "^0.12.4" + "dcmjs": "0.14.0" }, "dependencies": { "@babel/runtime": "7.7.6" diff --git a/extensions/default/src/DicomWebDataSource/index.js b/extensions/default/src/DicomWebDataSource/index.js index c832ee4a9..33bce395d 100644 --- a/extensions/default/src/DicomWebDataSource/index.js +++ b/extensions/default/src/DicomWebDataSource/index.js @@ -12,9 +12,14 @@ import getImageId from './utils/getImageId'; import * as dcmjs from 'dcmjs'; import { retrieveStudyMetadata } from './retrieveStudyMetadata.js'; -const { naturalizeDataset } = dcmjs.data.DicomMetaDictionary; +const { DicomMetaDictionary, DicomDict } = dcmjs.data; + +const { naturalizeDataset, denaturalizeDataset } = DicomMetaDictionary; const { urlUtil } = utils; +const VERSION_NAME = 'OHIF-0.1'; +const SR_TRANSFER_SYNTAX_UID = '1.2.840.10008.1.2.1'; + /** * * @param {string} name - Data source name @@ -128,6 +133,32 @@ function createDicomWebApi(dicomWebConfig) { }, }, }, + store: { + dicom: async dataset => { + const meta = { + FileMetaInformationVersion: + dataset._meta.FileMetaInformationVersion.Value, + MediaStorageSOPClassUID: dataset.SOPClassUID, + MediaStorageSOPInstanceUID: dataset.SOPInstanceUID, + TransferSyntaxUID: SR_TRANSFER_SYNTAX_UID, + ImplementationClassUID: DicomMetaDictionary.uid(), + ImplementationVersionName: VERSION_NAME, + }; + + const denaturalized = denaturalizeDataset(meta); + const dicomDict = new DicomDict(denaturalized); + + dicomDict.dict = denaturalizeDataset(dataset); + + const part10Buffer = dicomDict.write(); + + const options = { + datasets: [part10Buffer], + }; + + await wadoDicomWebClient.storeInstances(options); + }, + }, retrieveSeriesMetadata: async ({ StudyInstanceUID } = {}) => { if (!StudyInstanceUID) { throw new Error( diff --git a/extensions/default/src/Panels/WrappedPanelStudyBrowser.jsx b/extensions/default/src/Panels/WrappedPanelStudyBrowser.jsx index c3bb27d01..e79deea68 100644 --- a/extensions/default/src/Panels/WrappedPanelStudyBrowser.jsx +++ b/extensions/default/src/Panels/WrappedPanelStudyBrowser.jsx @@ -20,7 +20,7 @@ function WrappedPanelStudyBrowser({ }) { // TODO: This should be made available a different way; route should have // already determined our datasource - const dataSource = extensionManager.getDataSources('dicomweb')[0]; + const dataSource = extensionManager.getDataSources()[0]; const _getStudiesForPatientByStudyInstanceUID = getStudiesForPatientByStudyInstanceUID.bind( null, dataSource diff --git a/extensions/dicom-html/package.json b/extensions/dicom-html/package.json index d76e6fa11..eca2b16a3 100644 --- a/extensions/dicom-html/package.json +++ b/extensions/dicom-html/package.json @@ -28,7 +28,7 @@ }, "peerDependencies": { "@ohif/core": "^0.50.0", - "dcmjs": "^0.12.3", + "dcmjs": "0.14.0", "prop-types": "^15.6.2", "react": "^16.11.0", "react-dom": "^16.11.0" diff --git a/extensions/dicom-rt/package.json b/extensions/dicom-rt/package.json index fb67f9acf..55ce43430 100644 --- a/extensions/dicom-rt/package.json +++ b/extensions/dicom-rt/package.json @@ -31,7 +31,7 @@ "@ohif/core": "^0.50.0", "cornerstone-core": "^2.2.8", "cornerstone-tools": "4.15.1", - "dcmjs": "^0.12.3", + "dcmjs": "0.14.0", "prop-types": "^15.6.2", "react": "^16.8.6", "react-dom": "^16.8.6" diff --git a/extensions/dicom-segmentation/package.json b/extensions/dicom-segmentation/package.json index ede02d636..e9214776f 100644 --- a/extensions/dicom-segmentation/package.json +++ b/extensions/dicom-segmentation/package.json @@ -31,7 +31,7 @@ "@ohif/core": "^0.50.0", "cornerstone-core": "^2.2.8", "cornerstone-tools": "4.15.1", - "dcmjs": "^0.12.2", + "dcmjs": "0.14.0", "prop-types": "^15.6.2", "react": "^16.8.6", "react-dom": "^16.8.6" diff --git a/extensions/dicom-sr/package.json b/extensions/dicom-sr/package.json index 6b14984c9..c8d6e6839 100644 --- a/extensions/dicom-sr/package.json +++ b/extensions/dicom-sr/package.json @@ -34,7 +34,7 @@ "cornerstone-core": "^2.3.0", "cornerstone-math": "^0.1.8", "cornerstone-tools": "4.15.1", - "dcmjs": "^0.12.3", + "dcmjs": "0.14.0", "cornerstone-wado-image-loader": "^3.1.2", "dicom-parser": "^1.8.3", "hammerjs": "^2.0.8", diff --git a/extensions/measurement-tracking/package.json b/extensions/measurement-tracking/package.json index 7d08ecdbd..86bad62c0 100644 --- a/extensions/measurement-tracking/package.json +++ b/extensions/measurement-tracking/package.json @@ -28,11 +28,12 @@ }, "peerDependencies": { "@ohif/core": "^0.50.0", - "dcmjs": "^0.12.4", + "dcmjs": "0.14.0", "prop-types": "^15.6.2", "react": "^16.13.1", "react-dom": "^16.13.1", - "webpack": "^4.0.0" + "webpack": "^4.0.0", + "cornerstone-tools": "4.15.1" }, "dependencies": { "@babel/runtime": "7.7.6", diff --git a/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking/ActionButtons.jsx b/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking/ActionButtons.jsx index 0e3e8a3b6..6d239cf81 100644 --- a/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking/ActionButtons.jsx +++ b/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking/ActionButtons.jsx @@ -2,10 +2,10 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Button, ButtonGroup, Icon, IconButton } from '@ohif/ui'; -function ActionButtons() { +function ActionButtons({ onExportClick, onCreateReportClick }) { return ( - alert('Export')}> + @@ -34,4 +34,14 @@ function ActionButtons() { ); } +ActionButtons.propTypes = { + onExportClick: PropTypes.func, + onCreateReportClick: PropTypes.func, +}; + +ActionButtons.defaultProps = { + onExportClick: () => alert('Export'), + onCreateReportClick: () => alert('Create Report'), +}; + export default ActionButtons; diff --git a/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking/index.js b/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking/index.js index edfd44aa1..a37240549 100644 --- a/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking/index.js +++ b/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking/index.js @@ -1,10 +1,13 @@ import React, { useEffect, useState } from 'react'; import PropTypes from 'prop-types'; import { StudySummary, MeasurementTable } from '@ohif/ui'; -import { DicomMetadataStore } from '@ohif/core'; +import { DicomMetadataStore, DICOMSR } from '@ohif/core'; import { useDebounce } from '@hooks'; import ActionButtons from './ActionButtons'; import { useTrackedMeasurements } from '../../getContextModule'; +import cornerstoneTools from 'cornerstone-tools'; +import cornerstone from 'cornerstone-core'; +import dcmjs from 'dcmjs'; const DISPLAY_STUDY_SUMMARY_INITIAL_VALUE = { key: undefined, // @@ -13,7 +16,7 @@ const DISPLAY_STUDY_SUMMARY_INITIAL_VALUE = { description: undefined, // 'CHEST/ABD/PELVIS W CONTRAST', }; -function PanelMeasurementTableTracking({ servicesManager, commandsManager }) { +function PanelMeasurementTableTracking({ servicesManager, extensionManager }) { const [measurementChangeTimestamp, setMeasurementsUpdated] = useState( Date.now().toString() ); @@ -102,6 +105,37 @@ function PanelMeasurementTableTracking({ servicesManager, commandsManager }) { const activeMeasurementItem = 0; + const onExportClick = () => { + const measurements = MeasurementService.getMeasurements(); + const trackedMeasurements = measurements.filter( + m => + trackedStudy === m.referenceStudyUID && + trackedSeries.includes(m.referenceSeriesUID) + ); + + // TODO -> local download. + DICOMSR.downloadReport(trackedMeasurements, dataSource); + }; + + const onCreateReportClick = () => { + const measurements = MeasurementService.getMeasurements(); + const trackedMeasurements = measurements.filter( + m => + trackedStudy === m.referenceStudyUID && + trackedSeries.includes(m.referenceSeriesUID) + ); + + console.log(servicesManager); + console.log(extensionManager); + + const dataSources = extensionManager.getDataSources(); + // TODO -> Eventually deal with multiple dataSources. + // Would need some way of saying which one is the "push" dataSource + const dataSource = dataSources[0]; + + DICOMSR.storeMeasurements(trackedMeasurements, dataSource); + }; + return ( <>
@@ -121,7 +155,10 @@ function PanelMeasurementTableTracking({ servicesManager, commandsManager }) { />
- +
); diff --git a/extensions/vtk/package.json b/extensions/vtk/package.json index 422d61c6f..bbe03c172 100644 --- a/extensions/vtk/package.json +++ b/extensions/vtk/package.json @@ -33,7 +33,7 @@ "@ohif/ui": "^2.0.0", "cornerstone-core": "^2.3.0", "cornerstone-wado-image-loader": "^3.1.2", - "dcmjs": "0.12.2", + "dcmjs": "0.14.0", "dicom-parser": "^1.8.3", "i18next": "^17.0.3", "i18next-browser-languagedetector": "^3.0.1", diff --git a/platform/core/package.json b/platform/core/package.json index 83320ad2f..608077d64 100644 --- a/platform/core/package.json +++ b/platform/core/package.json @@ -38,7 +38,7 @@ "dependencies": { "@babel/runtime": "7.7.6", "ajv": "^6.10.0", - "dcmjs": "^0.12.4", + "dcmjs": "0.14.0", "dicomweb-client": "^0.6.0", "immer": "6.0.2", "isomorphic-base64": "^1.0.2", diff --git a/platform/core/src/DICOMSR/dataExchange.js b/platform/core/src/DICOMSR/dataExchange.js index aab594da3..dac463a12 100644 --- a/platform/core/src/DICOMSR/dataExchange.js +++ b/platform/core/src/DICOMSR/dataExchange.js @@ -6,6 +6,10 @@ import { stowSRFromMeasurements, } from './handleStructuredReport'; import findMostRecentStructuredReport from './utils/findMostRecentStructuredReport'; +import cornerstoneTools from 'cornerstone-tools'; +import dcmjs from 'dcmjs'; + +const { MeasurementReport } = dcmjs.adapters.Cornerstone; /** * @@ -47,7 +51,7 @@ const retrieveMeasurements = server => { * @param {serverType} server * @returns {Object} With message to be displayed on success */ -const storeMeasurements = async (measurementData, filter, server) => { +const storeMeasurementsOld = async (measurementData, filter, server) => { log.info('[DICOMSR] storeMeasurements'); if (!server || server.type !== 'dicomWeb') { @@ -78,4 +82,112 @@ const storeMeasurements = async (measurementData, filter, server) => { } }; -export { retrieveMeasurements, storeMeasurements }; +const downloadReport = measurementData => { + const srDataset = generateReport(measurementData); + const reportBlob = dcmjs.data.datasetToBlob(srDataset); + + //Create a URL for the binary. + var objectUrl = URL.createObjectURL(reportBlob); + window.location.assign(objectUrl); +}; + +const generateReport = measurementData => { + const ids = measurementData.map(md => md.id); + const filteredToolState = _getFilteredCornerstoneToolState(ids); + + const report = MeasurementReport.generateReport( + filteredToolState, + cornerstone.metaData + ); + + return report.dataset; +}; + +const storeMeasurements = async (measurementData, dataSource) => { + // TODO -> Eventually use the measurements directly and not the dcmjs adapter, + // But it is good enough for now whilst we only have cornerstone as a datasource. + log.info('[DICOMSR] storeMeasurements'); + + if (!dataSource || !dataSource.store || !dataSource.store.dicom) { + log.error('[DICOMSR] datasource has no dataSource.store.dicom endpoint!'); + return Promise.reject({}); + } + + const naturalizedReport = generateReport(measurementData); + const { StudyInstanceUID } = naturalizedReport; + + try { + await dataSource.store.dicom(naturalizedReport); + + if (StudyInstanceUID) { + studies.deleteStudyMetadataPromise(StudyInstanceUID); + } + + return { + message: 'Measurements saved successfully', + }; + } catch (error) { + log.error( + `[DICOMSR] Error while saving the measurements: ${error.message}` + ); + throw new Error('Error while saving the measurements.'); + } +}; + +function _getFilteredCornerstoneToolState(uidFilter) { + const globalToolState = cornerstoneTools.globalImageIdSpecificToolStateManager.saveToolState(); + const filteredToolState = {}; + + function addToFilteredToolState(imageId, toolType, toolDataI) { + if (!filteredToolState[imageId]) { + filteredToolState[imageId] = {}; + } + + const imageIdSpecificToolState = filteredToolState[imageId]; + + if (!imageIdSpecificToolState[toolType]) { + imageIdSpecificToolState[toolType] = { + data: [], + }; + } + + const toolData = imageIdSpecificToolState[toolType].data; + + toolData.push(toolDataI); + } + + const uids = uidFilter.slice(); + const imageIds = Object.keys(globalToolState); + + for (let i = 0; i < imageIds.length; i++) { + const imageId = imageIds[i]; + const imageIdSpecificToolState = globalToolState[imageId]; + + const toolTypes = Object.keys(imageIdSpecificToolState); + + for (let j = 0; j < toolTypes.length; j++) { + const toolType = toolTypes[j]; + const toolData = imageIdSpecificToolState[toolType].data; + + if (toolData) { + for (let k = 0; k < toolData.length; k++) { + const toolDataK = toolData[k]; + const uidIndex = uids.findIndex(uid => uid === toolDataK.id); + + if (uidIndex !== -1) { + addToFilteredToolState(imageId, toolType, toolDataK); + uids.splice(uidIndex, 1); + + if (!uids.length) { + return filteredToolState; + } + } + } + } + } + } + + return filteredToolState; +} + +export { retrieveMeasurements, storeMeasurements, downloadReport }; diff --git a/platform/core/src/DICOMSR/index.js b/platform/core/src/DICOMSR/index.js index 786202642..759f26cb1 100644 --- a/platform/core/src/DICOMSR/index.js +++ b/platform/core/src/DICOMSR/index.js @@ -1,9 +1,14 @@ -import { retrieveMeasurements, storeMeasurements } from './dataExchange'; +import { + retrieveMeasurements, + storeMeasurements, + downloadReport, +} from './dataExchange'; import isToolSupported from './utils/isToolSupported'; const DICOMSR = { retrieveMeasurements, storeMeasurements, + downloadReport, isToolSupported, }; diff --git a/platform/core/src/DataSources/IWebApiDataSource.js b/platform/core/src/DataSources/IWebApiDataSource.js index e10914e9e..10285d364 100644 --- a/platform/core/src/DataSources/IWebApiDataSource.js +++ b/platform/core/src/DataSources/IWebApiDataSource.js @@ -15,6 +15,7 @@ import { DicomMetadataStore } from '@ohif/core'; function create({ query, retrieve, + store, retrieveSeriesMetadata, getImageIdsForDisplaySet, }) { @@ -44,13 +45,20 @@ function create({ series: {}, }; + const defaultStore = { + dicom: async naturalizedDataset => { + throw new Error( + 'store.dicom(naturalizedDicom, StudyInstanceUID) not implemented for dataSource.' + ); + }, + }; + return { query: query || defaultQuery, retrieve: retrieve || defaultRetrieve, + store: store || defaultStore, getImageIdsForDisplaySet, retrieveSeriesMetadata, - // then go get all series level metadata. - // Store this in the DICOM MetadataStore. }; } diff --git a/platform/viewer/package.json b/platform/viewer/package.json index 52c3ff3b7..de94e0357 100644 --- a/platform/viewer/package.json +++ b/platform/viewer/package.json @@ -69,7 +69,7 @@ "cornerstone-math": "^0.1.8", "cornerstone-tools": "4.15.1", "cornerstone-wado-image-loader": "^3.1.2", - "dcmjs": "^0.12.2", + "dcmjs": "0.14.0", "dicom-parser": "^1.8.3", "dicomweb-client": "^0.4.4", "dotenv-webpack": "^1.7.0", diff --git a/yarn.lock b/yarn.lock index eb88b95ac..71ee64509 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7353,10 +7353,10 @@ dateformat@^3.0.0: resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== -dcmjs@^0.12.2, dcmjs@^0.12.4: - version "0.12.4" - resolved "https://registry.yarnpkg.com/dcmjs/-/dcmjs-0.12.4.tgz#82c24abdc357ea5281b78eb2cae8b781f7392aa3" - integrity sha512-N1ZsXqZIysirqdytb7h572TyIjmxpvCjrzdjtQsuPN8gC2EpxsUHQ598CPzaJBpBy9i1kfKuq4h2Jwt99cr/QQ== +dcmjs@0.14.0: + version "0.14.0" + resolved "https://registry.yarnpkg.com/dcmjs/-/dcmjs-0.14.0.tgz#0dc6cb2d15ddcff759bc9002f2a9704537735d1c" + integrity sha512-VL/Ibxe5RDsc5j5SEv3aEqdlKuBXz81/bBuW59Or0cos9vgK3XnVl3rr0ct6DWXJGK8vGIUMBOguyd/NRvlN0w== dependencies: "@babel/polyfill" "^7.8.3" "@babel/runtime" "^7.8.4" From bb26b8696949d6996e4b4083093b6acb59495d05 Mon Sep 17 00:00:00 2001 From: Danny Brown Date: Thu, 25 Jun 2020 12:48:42 -0400 Subject: [PATCH 3/4] remove console log --- .../src/panels/PanelMeasurementTableTracking/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking/index.js b/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking/index.js index a37240549..16e506deb 100644 --- a/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking/index.js +++ b/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking/index.js @@ -126,7 +126,6 @@ function PanelMeasurementTableTracking({ servicesManager, extensionManager }) { ); console.log(servicesManager); - console.log(extensionManager); const dataSources = extensionManager.getDataSources(); // TODO -> Eventually deal with multiple dataSources. From 0ae95adf348a5cdcb35c61a010cace9888db3446 Mon Sep 17 00:00:00 2001 From: Danny Brown Date: Thu, 25 Jun 2020 12:49:05 -0400 Subject: [PATCH 4/4] Remove console log --- .../src/panels/PanelMeasurementTableTracking/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking/index.js b/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking/index.js index 16e506deb..10184832a 100644 --- a/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking/index.js +++ b/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking/index.js @@ -125,7 +125,6 @@ function PanelMeasurementTableTracking({ servicesManager, extensionManager }) { trackedSeries.includes(m.referenceSeriesUID) ); - console.log(servicesManager); const dataSources = extensionManager.getDataSources(); // TODO -> Eventually deal with multiple dataSources.