import SUPPORTED_TOOLS from './constants/supportedTools'; import getSOPInstanceAttributes from './utils/getSOPInstanceAttributes'; import { utils } from '@ohif/core'; const CobbAngle = { toAnnotation: measurement => {}, /** * Maps cornerstone annotation event data to measurement service format. * * @param {Object} cornerstone Cornerstone event data * @return {Measurement} Measurement instance */ toMeasurement: ( csToolsEventDetail, displaySetService, CornerstoneViewportService, getValueTypeFromToolType ) => { const { annotation, viewportId } = csToolsEventDetail; const { metadata, data, annotationUID } = annotation; if (!metadata || !data) { console.warn('Cobb Angle tool: Missing metadata or data'); return null; } const { toolName, referencedImageId, FrameOfReferenceUID } = metadata; const validToolType = SUPPORTED_TOOLS.includes(toolName); if (!validToolType) { throw new Error('Tool not supported'); } const { SOPInstanceUID, SeriesInstanceUID, StudyInstanceUID } = getSOPInstanceAttributes( referencedImageId, CornerstoneViewportService, viewportId ); let displaySet; if (SOPInstanceUID) { displaySet = displaySetService.getDisplaySetForSOPInstanceUID( SOPInstanceUID, SeriesInstanceUID ); } else { displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID); } const { points } = data.handles; const mappedAnnotations = getMappedAnnotations(annotation, displaySetService); const displayText = getDisplayText(mappedAnnotations, displaySet); const getReport = () => _getReport(mappedAnnotations, points, FrameOfReferenceUID); return { uid: annotationUID, SOPInstanceUID, FrameOfReferenceUID, points, metadata, referenceSeriesUID: SeriesInstanceUID, referenceStudyUID: StudyInstanceUID, frameNumber: mappedAnnotations?.[0]?.frameNumber || 1, toolName: metadata.toolName, displaySetInstanceUID: displaySet.displaySetInstanceUID, label: data.label, displayText: displayText, data: data.cachedStats, type: getValueTypeFromToolType(toolName), getReport, }; }, }; function getMappedAnnotations(annotation, DisplaySetService) { const { metadata, data } = annotation; const { cachedStats } = data; const { referencedImageId } = metadata; const targets = Object.keys(cachedStats); if (!targets.length) { return; } const annotations = []; Object.keys(cachedStats).forEach(targetId => { const targetStats = cachedStats[targetId]; if (!referencedImageId) { throw new Error('Non-acquisition plane measurement mapping not supported'); } const { SOPInstanceUID, SeriesInstanceUID, frameNumber } = getSOPInstanceAttributes(referencedImageId); const displaySet = DisplaySetService.getDisplaySetForSOPInstanceUID( SOPInstanceUID, SeriesInstanceUID, frameNumber ); const { SeriesNumber } = displaySet; const { angle } = targetStats; const unit = '\u00B0'; annotations.push({ SeriesInstanceUID, SOPInstanceUID, SeriesNumber, frameNumber, unit, angle, }); }); return annotations; } /* This function is used to convert the measurement data to a format that is suitable for the report generation (e.g. for the csv report). The report returns a list of columns and corresponding values. */ function _getReport(mappedAnnotations, points, FrameOfReferenceUID) { const columns = []; const values = []; // Add Type columns.push('AnnotationType'); values.push('Cornerstone:CobbAngle'); mappedAnnotations.forEach(annotation => { const { angle, unit } = annotation; columns.push(`Angle (${unit})`); values.push(angle); }); if (FrameOfReferenceUID) { columns.push('FrameOfReferenceUID'); values.push(FrameOfReferenceUID); } if (points) { columns.push('points'); // points has the form of [[x1, y1, z1], [x2, y2, z2], ...] // convert it to string of [[x1 y1 z1];[x2 y2 z2];...] // so that it can be used in the csv report values.push(points.map(p => p.join(' ')).join(';')); } return { columns, values, }; } function getDisplayText(mappedAnnotations, displaySet) { if (!mappedAnnotations || !mappedAnnotations.length) { return ''; } const displayText = []; // Area is the same for all series const { angle, unit, SeriesNumber, SOPInstanceUID, frameNumber } = mappedAnnotations[0]; const instance = displaySet.images.find(image => image.SOPInstanceUID === SOPInstanceUID); let InstanceNumber; if (instance) { InstanceNumber = instance.InstanceNumber; } const instanceText = InstanceNumber ? ` I: ${InstanceNumber}` : ''; const frameText = displaySet.isMultiFrame ? ` F: ${frameNumber}` : ''; if (angle === undefined) { return displayText; } const roundedAngle = utils.roundNumber(angle, 2); displayText.push(`${roundedAngle} ${unit} (S: ${SeriesNumber}${instanceText}${frameText})`); return displayText; } export default CobbAngle;