forked from OHIF/Viewers
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: 🎸 MeasurementService (OHIF#1314)
* feat: 🎸 MeasurementService Work in progress... * Remove cornerstone tools import * Second iteration * CR Update: Add update / added events example * Add new props to measurements * Update event log * Add new measurementid to annotation * Add context support * Add value types * Add area * Add todo * Wip measurement to annotation map * Change points representation * Add props to annotation mapping * Add tests * Extract formatter from init and add tests * Sketch matchers * Fix events and valuetypes imports * Remove context support * Rename formatter to mappings * Sketching source and source definitions * Adjust matching criteria in addOrUpdate * CR Updates: Extract private functions and rename variables * Fix broken tests * Add more measurement service tests * Update broken mapping tests * Update test description * Update getAnnotation to get mapping based on def and source Co-authored-by: Danny Brown <[email protected]>
- Loading branch information
1 parent
e8d56c1
commit 0c37a40
Showing
14 changed files
with
1,255 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
module.exports = require("../../babel.config.js"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
const base = require('../../jest.config.base.js'); | ||
const pkg = require('./package'); | ||
|
||
module.exports = { | ||
...base, | ||
name: pkg.name, | ||
displayName: pkg.name, | ||
// rootDir: "../.." | ||
// testMatch: [ | ||
// //`<rootDir>/platform/${pack.name}/**/*.spec.js` | ||
// "<rootDir>/platform/viewer/**/*.test.js" | ||
// ] | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
133 changes: 133 additions & 0 deletions
133
...ons/cornerstone/src/utils/measurementServiceMappings/measurementServiceMappingsFactory.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
import cornerstone from 'cornerstone-core'; | ||
|
||
const SUPPORTED_TOOLS = ['Length', 'EllipticalRoi', 'RectangleRoi', 'ArrowAnnotate']; | ||
|
||
const measurementServiceMappingsFactory = measurementService => { | ||
/** | ||
* Maps measurement service format object to cornerstone annotation object. | ||
* | ||
* @param {Measurement} measurement The measurement instance | ||
* @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, | ||
} = _getAttributes(element); | ||
|
||
const points = []; | ||
points.push(measurementData.handles); | ||
|
||
return { | ||
id: measurementData._measurementServiceId, | ||
sopInstanceUID: sopInstanceUid, | ||
frameOfReferenceUID: frameOfReferenceUid, | ||
referenceSeriesUID: seriesInstanceUid, | ||
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 sopInstance = cornerstone.metaData.get('instance', imageId); | ||
const sopInstanceUid = sopInstance.sopInstanceUid; | ||
const frameOfReferenceUid = sopInstance.frameOfReferenceUID; | ||
const series = cornerstone.metaData.get('series', imageId); | ||
const seriesInstanceUid = series.seriesInstanceUid; | ||
|
||
return { sopInstanceUid, frameOfReferenceUid, seriesInstanceUid }; | ||
}; | ||
|
||
const _getValueTypeFromToolType = toolType => { | ||
const { POLYLINE, ELLIPSE, POINT } = measurementService.VALUE_TYPES; | ||
|
||
/* TODO: Relocate static value types */ | ||
const TOOL_TYPE_TO_VALUE_TYPE = { | ||
Length: POLYLINE, | ||
EllipticalRoi: ELLIPSE, | ||
RectangleRoi: POLYLINE, | ||
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, | ||
}; | ||
}; | ||
|
||
export default measurementServiceMappingsFactory; |
80 changes: 80 additions & 0 deletions
80
...ornerstone/src/utils/measurementServiceMappings/measurementServiceMappingsFactory.test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import measurementServiceMappingsFactory from './measurementServiceMappingsFactory'; | ||
|
||
jest.mock('cornerstone-core', () => ({ | ||
...jest.requireActual('cornerstone-core'), | ||
getEnabledElement: () => ({ | ||
image: { imageId: 123 }, | ||
}), | ||
metaData: { | ||
...jest.requireActual('cornerstone-core').metaData, | ||
get: () => ({ | ||
sopInstanceUid: "123", | ||
frameOfReferenceUID: "123", | ||
seriesInstanceUid: "123", | ||
}), | ||
}, | ||
})); | ||
|
||
describe('measurementServiceMappings.js', () => { | ||
let mappings; | ||
let handles; | ||
let points; | ||
let csToolsAnnotation; | ||
let measurement; | ||
let measurementServiceMock; | ||
let definition = 'Length'; | ||
|
||
beforeEach(() => { | ||
measurementServiceMock = { | ||
VALUE_TYPES: { | ||
POLYLINE: 'value_type::polyline', | ||
POINT: 'value_type::point', | ||
ELLIPSE: 'value_type::ellipse', | ||
MULTIPOINT: 'value_type::multipoint', | ||
CIRCLE: 'value_type::circle', | ||
}, | ||
}; | ||
mappings = measurementServiceMappingsFactory(measurementServiceMock); | ||
handles = { start: { x: 1, y: 2 }, end: { x: 1, y: 2 } }; | ||
points = [{ x: 1, y: 2 }, { x: 1, y: 2 }]; | ||
csToolsAnnotation = { | ||
toolName: definition, | ||
measurementData: { | ||
_measurementServiceId: 1, | ||
sopInstanceUid: '123', | ||
frameOfReferenceUid: '123', | ||
seriesInstanceUid: '123', | ||
handles, | ||
text: 'Test', | ||
description: 'Test', | ||
unit: 'mm', | ||
}, | ||
}; | ||
measurement = { | ||
id: 1, | ||
sopInstanceUID: '123', | ||
frameOfReferenceUID: '123', | ||
referenceSeriesUID: '123', | ||
label: 'Test', | ||
description: 'Test', | ||
unit: 'mm', | ||
type: measurementServiceMock.VALUE_TYPES.POLYLINE, | ||
points: points, | ||
}; | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
describe('toAnnotation()', () => { | ||
it('map measurement service format to annotation', async () => { | ||
const mappedMeasurement = await mappings.toAnnotation(measurement, definition); | ||
expect(mappedMeasurement).toEqual(csToolsAnnotation); | ||
}); | ||
}); | ||
|
||
describe('toMeasurement()', () => { | ||
it('map annotation to measurement service format', async () => { | ||
const mappedAnnotation = await mappings.toMeasurement(csToolsAnnotation); | ||
expect(mappedAnnotation).toEqual(measurement); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.