feat(measurement-table): Improve measurement table support. Still missing labelling

This commit is contained in:
Erik Ziegler 2019-02-13 16:18:46 +01:00
parent becdfee1ed
commit 113c20c346
12 changed files with 99 additions and 186 deletions

View File

@ -18,7 +18,6 @@ import {
createUserManager, createUserManager,
reducer as oidcReducer reducer as oidcReducer
} from 'redux-oidc'; } from 'redux-oidc';
import timepointManager from './redux/timepointManager';
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader'; import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
const { ExtensionManager } = OHIF.extensions; const { ExtensionManager } = OHIF.extensions;
@ -28,7 +27,6 @@ const Icons = 'icons.svg';
const { reducers, localStorage } = OHIF.redux; const { reducers, localStorage } = OHIF.redux;
reducers.ui = ui; reducers.ui = ui;
reducers.oidc = oidcReducer; reducers.oidc = oidcReducer;
reducers.timepointManager = timepointManager;
const combined = combineReducers(reducers); const combined = combineReducers(reducers);
const store = createStore(combined, localStorage.loadState()); const store = createStore(combined, localStorage.loadState());

View File

@ -1,7 +1,100 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { MeasurementTable } from 'react-viewerbase'; import { MeasurementTable } from 'react-viewerbase';
import OHIF from 'ohif-core';
import moment from 'moment'; import moment from 'moment';
function groupBy(list, props) {
return list.reduce((a, b) => {
(a[b[props]] = a[b[props]] || []).push(b);
return a;
}, {});
}
function getAllTools() {
const config = OHIF.measurements.MeasurementApi.getConfiguration();
let tools = [];
config.measurementTools.forEach( toolGroup =>
tools = tools.concat(toolGroup.childTools)
);
return tools;
}
function getMeasurementText(measurementData) {
const { location, description} = measurementData;
let text = '...';
if (location) {
text = location;
if (description) {
text += `(${description})`;
}
}
return text;
}
function getDataForEachMeasurementNumber(measurementNumberList, timepoints, displayFunction) {
const data = [];
// on each measurement number we should get each measurement data by available timepoint
measurementNumberList.forEach( measurement => {
timepoints.forEach( timepoint => {
const eachData = {
displayText: '...'
}
if (measurement.timepointId === timepoint.timepointId) {
eachData.displayText = displayFunction(measurement);
}
data.push(eachData);
});
});
return data;
}
function convertMeasurementsToTableData(toolCollections, timepoints) {
const config = OHIF.measurements.MeasurementApi.getConfiguration();
const toolGroups = config.measurementTools;
const tools = getAllTools();
const tableMeasurements = toolGroups.map( toolGroup => {
return {
groupName: toolGroup.name,
groupId: toolGroup.id,
measurements: []
}
});
Object.keys(toolCollections).forEach(toolId => {
const toolMeasurements = toolCollections[toolId];
const tool = tools.find(tool => tool.id === toolId);
const { displayFunction } = tool.options.measurementTable;
// Group by measurementNumber so we can display then all in the same line
const groupedMeasurements = groupBy(toolMeasurements, 'measurementNumber');
Object.keys(groupedMeasurements).forEach( groupedMeasurementsIndex => {
const measurementNumberList = groupedMeasurements[groupedMeasurementsIndex];
//check if all measurements with same measurementNumber will have same LABEL
const tableMeasurement = {
label: getMeasurementText(measurementNumberList[0]),
hasWarnings: false, //TODO
warningTitle: '', //TODO
isSplitLesion: false, //TODO
warningList: [], //TODO
data: getDataForEachMeasurementNumber(measurementNumberList, timepoints, displayFunction)
};
// find the group object for the tool
const toolGroupMeasurements = tableMeasurements.find( group => {
return group.groupId === tool.toolGroup;
});
// inject the new measurement for this measurementNumer
toolGroupMeasurements.measurements.push(tableMeasurement);
});
});
return tableMeasurements;
}
function convertTimepointsToTableData(timepoints) { function convertTimepointsToTableData(timepoints) {
if (!timepoints || !timepoints.length) { if (!timepoints || !timepoints.length) {
return []; return [];
@ -15,41 +108,11 @@ function convertTimepointsToTableData(timepoints) {
]; ];
} }
function convertMeasurementsToTableData(measurements) {
const tableData = [
{
groupName: 'Measurements',
measurements: [],
measurements1: measurements
}
];
if (measurements && measurements.allTools) {
measurements.allTools.forEach(measurement => {
const tableMeasurement = {
label: '...',
hasWarnings: false,
warningTitle: '',
isSplitLesion: false,
warningList: [],
data: [
{
displayText: '...'
}
]
};
tableData[0].measurements.push(tableMeasurement);
});
}
return tableData;
}
const mapStateToProps = state => { const mapStateToProps = state => {
const { timepoints, measurements } = state.timepointManager; const { timepoints, measurements } = state.timepointManager;
return { return {
timepoints: convertTimepointsToTableData(timepoints), timepoints: convertTimepointsToTableData(timepoints),
measurementCollection: convertMeasurementsToTableData(measurements) measurementCollection: convertMeasurementsToTableData(measurements, timepoints)
}; };
}; };

View File

@ -1,14 +1,16 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import Viewer from './Viewer.js'; import Viewer from './Viewer.js';
import actions from '../redux/actions.js'; import OHIF from 'ohif-core';
const { setTimepoints, setMeasurements } = OHIF.redux.actions;
const mapDispatchToProps = dispatch => { const mapDispatchToProps = dispatch => {
return { return {
onTimepointsUpdated: timepoints => { onTimepointsUpdated: timepoints => {
dispatch(actions.setTimepoints(timepoints)); dispatch(setTimepoints(timepoints));
}, },
onMeasurementsUpdated: measurements => { onMeasurementsUpdated: measurements => {
dispatch(actions.setMeasurements(measurements)); dispatch(setMeasurements(measurements));
} }
}; };
}; };

View File

@ -9,7 +9,6 @@ import WhiteLabellingContext from '../WhiteLabellingContext.js';
import ConnectedHeader from './ConnectedHeader.js'; import ConnectedHeader from './ConnectedHeader.js';
import ConnectedFlexboxLayout from './ConnectedFlexboxLayout.js'; import ConnectedFlexboxLayout from './ConnectedFlexboxLayout.js';
import ConnectedToolbarRow from './ConnectedToolbarRow.js'; import ConnectedToolbarRow from './ConnectedToolbarRow.js';
import measurementTools from '../measurementTools';
import './Viewer.css'; import './Viewer.css';
/** /**
* Inits OHIF Hanging Protocol's onReady. * Inits OHIF Hanging Protocol's onReady.
@ -57,9 +56,7 @@ class Viewer extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
OHIF.measurements.MeasurementApi.setConfiguration({ OHIF.measurements.MeasurementApi.setConfiguration({
measurementTools,
dataExchange: { dataExchange: {
retrieve: this.retrieveMeasurements, retrieve: this.retrieveMeasurements,
store: this.storeMeasurements store: this.storeMeasurements
@ -169,10 +166,7 @@ class Viewer extends Component {
const patientId = studies[0] && studies[0].patientId; const patientId = studies[0] && studies[0].patientId;
timepointApi.retrieveTimepoints({ patientId }); timepointApi.retrieveTimepoints({ patientId });
// TODO: Retrieve measurements and sync them with tool data
measurementApi.retrieveMeasurements(patientId, [currentTimepointId]); measurementApi.retrieveMeasurements(patientId, [currentTimepointId]);
//measurementApi.syncMeasurementsAndToolData();
} }
render() { render() {

View File

@ -1,15 +0,0 @@
const displayFunction = data => {
return data.text || '';
};
export default {
id: 'ArrowAnnotate',
name: 'ArrowAnnotate',
toolGroup: 'allTools',
cornerstoneToolType: 'ArrowAnnotate',
options: {
measurementTable: {
displayFunction
}
}
};

View File

@ -1,22 +0,0 @@
const displayFunction = data => {
let meanValue = '';
if (data.meanStdDev && data.meanStdDev.mean) {
meanValue = data.meanStdDev.mean.toFixed(2) + ' HU';
}
return meanValue;
// let meanValue = data.meanStdDev && data.meanStdDev.mean || 0;
// return numberWithCommas(meanValue).toFixed(2) + ' HU';
//return data.meanStdDev.mean.toFixed(2);
};
export default {
id: 'EllipticalRoi',
name: 'Ellipse',
toolGroup: 'allTools',
cornerstoneToolType: 'EllipticalRoi',
options: {
measurementTable: {
displayFunction
}
}
};

View File

@ -1,19 +0,0 @@
const displayFunction = data => {
let lengthValue = '';
if (data.length) {
lengthValue = data.length.toFixed(2) + ' mm';
}
return lengthValue;
};
export default {
id: 'Length',
name: 'Length',
toolGroup: 'allTools',
cornerstoneToolType: 'Length',
options: {
measurementTable: {
displayFunction
}
}
};

View File

@ -1,19 +0,0 @@
const displayFunction = data => {
let text = '';
if (data.rAngle) {
text = data.rAngle.toFixed(2) + String.fromCharCode(parseInt('00B0', 16));
}
return text;
};
export default {
id: 'SimpleAngle',
name: 'SimpleAngle',
toolGroup: 'allTools',
cornerstoneToolType: 'SimpleAngle',
options: {
measurementTable: {
displayFunction
}
}
};

View File

@ -1,21 +0,0 @@
import Length from './Length';
import EllipticalRoi from './EllipticalRoi';
import RectangleRoi from './rectangleRoi';
import SimpleAngle from './SimpleAngle';
import ArrowAnnotate from './ArrowAnnotate';
const trackedTools = [
Length,
EllipticalRoi,
RectangleRoi,
SimpleAngle,
ArrowAnnotate
];
export default [
{
id: 'allTools',
name: 'Measurements',
childTools: trackedTools
}
];

View File

@ -1,19 +0,0 @@
const displayFunction = data => {
let meanValue = '';
if (data.meanStdDev && data.meanStdDev.mean) {
meanValue = data.meanStdDev.mean.toFixed(2) + ' HU';
}
return meanValue;
};
export default {
id: 'RectangleRoi',
name: 'Rectangle',
toolGroup: 'allTools',
cornerstoneToolType: 'RectangleRoi',
options: {
measurementTable: {
displayFunction
}
}
};

View File

@ -13,22 +13,10 @@ export const setUserPreferencesModalOpen = state => ({
state state
}); });
export const setTimepoints = state => ({
type: 'SET_TIMEPOINTS',
state
});
export const setMeasurements = state => ({
type: 'SET_MEASUREMENTS',
state
});
const actions = { const actions = {
setLeftSidebarOpen, setLeftSidebarOpen,
setRightSidebarOpen, setRightSidebarOpen,
setUserPreferencesModalOpen, setUserPreferencesModalOpen,
setTimepoints,
setMeasurements
}; };
export default actions; export default actions;

View File

@ -1,17 +0,0 @@
const defaultState = {
timepoints: [],
measurements: []
};
const timepointManager = (state = defaultState, action) => {
switch (action.type) {
case 'SET_TIMEPOINTS':
return Object.assign({}, state, { timepoints: action.state });
case 'SET_MEASUREMENTS':
return Object.assign({}, state, { measurements: action.state });
default:
return state;
}
};
export default timepointManager;