diff --git a/extensions/dicom-sr/src/constants/scoordTypes.js b/extensions/dicom-sr/src/constants/scoordTypes.js new file mode 100644 index 000000000..9120d71d3 --- /dev/null +++ b/extensions/dicom-sr/src/constants/scoordTypes.js @@ -0,0 +1,7 @@ +export default { + POINT: 'POINT', + MULTIPOINT: 'MULTIPOINT', + POLYLINE: 'POLYLINE', + CIRCLE: 'CIRCLE', + ELLIPSE: 'ELLIPSE', +}; diff --git a/extensions/dicom-sr/src/constants/toolNames.js b/extensions/dicom-sr/src/constants/toolNames.js new file mode 100644 index 000000000..ba11ac0ef --- /dev/null +++ b/extensions/dicom-sr/src/constants/toolNames.js @@ -0,0 +1,5 @@ +const TOOL_NAMES = { + DICOM_SR_DISPLAY_TOOL: 'DICOMSRDisplayTool', +}; + +export default TOOL_NAMES; diff --git a/extensions/dicom-sr/src/getSopClassHandlerModule.js b/extensions/dicom-sr/src/getSopClassHandlerModule.js index 7947531c9..c8ff65d73 100644 --- a/extensions/dicom-sr/src/getSopClassHandlerModule.js +++ b/extensions/dicom-sr/src/getSopClassHandlerModule.js @@ -1,5 +1,8 @@ import id from './id'; -import { utils } from '@ohif/core'; +import { utils, classes } from '@ohif/core'; +import addMeasurement from './utils/addMeasurement.js'; + +const { ImageSet } = classes; const sopClassHandlerName = 'dicom-sr'; @@ -9,8 +12,6 @@ const sopClassUids = [ '1.2.840.10008.5.1.4.1.1.88.33', //COMPREHENSIVE_SR: ]; -const scoordTypes = ['POINT', 'MULTIPOINT', 'POLYLINE', 'CIRCLE', 'ELLIPSE']; - const CodeNameCodeSequenceValues = { ImagingMeasurementReport: '126000', ImageLibrary: '111028', @@ -20,6 +21,10 @@ const CodeNameCodeSequenceValues = { TrackingUniqueIdentifier: '112040', }; +const RELATIONSHIP_TYPE = { + INFERRED_FROM: 'INFERRED FROM', +}; + /** * Basic SOPClassHandler: * - For all Image types that are stackable, create @@ -29,16 +34,23 @@ const CodeNameCodeSequenceValues = { * @param {SeriesMetadata} series The series metadata object from which the display sets will be created * @returns {Array} The list of display sets created for the given series object */ -function getDisplaySetsFromSeries(instances) { +function _getDisplaySetsFromSeries( + instances, + servicesManager, + extensionManager +) { // If the series has no instances, stop here if (!instances || !instances.length) { throw new Error('No instances were provided'); } + const { DisplaySetService } = servicesManager.services; + const dataSources = extensionManager.getDataSources(); + const dataSource = dataSources[0]; + const instance = instances[0]; const { StudyInstanceUID, SeriesInstanceUID, SOPInstanceUID } = instance; - const { ConceptNameCodeSequence, ContentSequence } = instance; if ( @@ -51,10 +63,6 @@ function getDisplaySetsFromSeries(instances) { return []; } - const referencedImages = _getReferencedImagesList(ContentSequence); - - const measurements = _getMeasurements(ContentSequence, SOPInstanceUID); - const displaySet = { plugin: id, Modality: 'SR', @@ -63,13 +71,141 @@ function getDisplaySetsFromSeries(instances) { SeriesInstanceUID, StudyInstanceUID, SOPClassHandlerId: `${id}.sopClassHandlerModule.${sopClassHandlerName}`, - referencedImages, + referencedImages: _getReferencedImagesList(ContentSequence), + measurements: _getMeasurements(ContentSequence), }; + console.log(DisplaySetService); + + // Check currently added displaySets and add measurements if the sources exist. + DisplaySetService.activeDisplaySets.forEach(activeDisplaySet => { + _checkIfCanAddMeasurementsToDisplaySet( + displaySet, + activeDisplaySet, + dataSource + ); + }); + + // Subscribe to new displaySets as the source may come in after. + DisplaySetService.subscribe( + DisplaySetService.EVENTS.DISPLAY_SETS_ADDED, + newDisplaySets => { + // If there are still some measurements that have not yet been loaded into cornerstone, + // See if we can load them onto any of the new displaySets. + newDisplaySets.forEach(newDisplaySet => { + _checkIfCanAddMeasurementsToDisplaySet( + displaySet, + newDisplaySet, + dataSource + ); + }); + } + ); + return [displaySet]; } -function getSopClassHandlerModule() { +function _checkIfCanAddMeasurementsToDisplaySet( + srDisplaySet, + newDisplaySet, + dataSource +) { + let unloadedMeasurements = srDisplaySet.measurements.filter( + measurement => measurement.loaded === false + ); + + if (unloadedMeasurements.length === 0) { + // All already loaded! + return; + } + + if (!newDisplaySet instanceof ImageSet) { + // This also filters out _this_ displaySet, as it is not an ImageSet. + return; + } + + const { sopClassUids, images } = newDisplaySet; + + // Check if any have the newDisplaySet is the correct SOPClass. + unloadedMeasurements = unloadedMeasurements.filter(measurement => + measurement.coords.some(coord => + sopClassUids.includes(coord.ReferencedSOPSequence.ReferencedSOPClassUID) + ) + ); + + if (unloadedMeasurements.length === 0) { + // New displaySet isn't the correct SOPClass, so can't contain the referenced images. + return; + } + + const SOPInstanceUIDs = []; + + unloadedMeasurements.forEach(measurement => { + const { coords } = measurement; + + coords.forEach(coord => { + const SOPInstanceUID = + coord.ReferencedSOPSequence.ReferencedSOPInstanceUID; + + if (!SOPInstanceUIDs.includes(SOPInstanceUID)) { + SOPInstanceUIDs.push(SOPInstanceUID); + } + }); + }); + + const imageIdsForDisplaySet = dataSource.getImageIdsForDisplaySet( + newDisplaySet + ); + + for (let i = 0; i < images.length; i++) { + if (!unloadedMeasurements.length) { + // All measurements loaded. + break; + } + + const image = images[i]; + const { SOPInstanceUID } = image; + if (SOPInstanceUIDs.includes(SOPInstanceUID)) { + const imageId = imageIdsForDisplaySet[i]; + + for (let j = unloadedMeasurements.length - 1; j >= 0; j--) { + const measurement = unloadedMeasurements[j]; + if (_measurmentReferencesSOPInstanceUID(measurement, SOPInstanceUID)) { + addMeasurement( + measurement, + imageId, + newDisplaySet.displaySetInstanceUID + ); + + unloadedMeasurements.splice(j, 1); + } + } + } + } +} + +function _measurmentReferencesSOPInstanceUID(measurement, SOPInstanceUID) { + const { coords } = measurement; + + for (let j = 0; j < coords.length; j++) { + const coord = coords[j]; + const { ReferencedSOPInstanceUID } = coord.ReferencedSOPSequence; + + if (ReferencedSOPInstanceUID === SOPInstanceUID) { + return true; + } + } +} + +function getSopClassHandlerModule({ servicesManager, extensionManager }) { + const getDisplaySetsFromSeries = instances => { + return _getDisplaySetsFromSeries( + instances, + servicesManager, + extensionManager + ); + }; + return [ { name: sopClassHandlerName, @@ -79,10 +215,7 @@ function getSopClassHandlerModule() { ]; } -function _getMeasurements( - ImagingMeasurementReportContentSequence, - SOPInstanceUID -) { +function _getMeasurements(ImagingMeasurementReportContentSequence) { const ImagingMeasurements = ImagingMeasurementReportContentSequence.find( item => item.ConceptNameCodeSequence.CodeValue === @@ -101,7 +234,7 @@ function _getMeasurements( MeasurementGroups ); - let allMeasurements = []; + let measurements = []; Object.keys(mergedContentSequencesByTrackingUniqueIdentifiers).forEach( trackingUniqueIdentifier => { @@ -110,13 +243,15 @@ function _getMeasurements( trackingUniqueIdentifier ]; - const measurements = _processMeasurement(mergedContentSequence); + const measurement = _processMeasurement(mergedContentSequence); - if (measurements) { - allMeasurements = allMeasurements.concat(measurements); + if (measurement) { + measurements.push(measurement); } } ); + + return measurements; } function _getMergedContentSequencesByTrackingUniqueIdentifiers( @@ -172,8 +307,6 @@ function _getMergedContentSequencesByTrackingUniqueIdentifiers( } function _processMeasurement(mergedContentSequence) { - debugger; - if ( mergedContentSequence.some( group => group.ValueType === 'SCOORD' || group.ValueType === 'SCOORD3D' @@ -182,34 +315,134 @@ function _processMeasurement(mergedContentSequence) { return _processTID1410Measurement(mergedContentSequence); } + return _processNonGeometricallyDefinedMeasurement(mergedContentSequence); +} + +function _processTID1410Measurement(mergedContentSequence) { + // Need to deal with TID 1410 style measurements, which will have a SCOORD or SCOORD3D at the top level, + // And non-geometric representations where each NUM has "INFERRED FROM" SCOORD/SCOORD3D + // TODO -> Look at RelationshipType => Contains means + + const graphicItem = mergedContentSequence.find( + group => group.ValueType === 'SCOORD' + ); + + if (!graphicItem) { + console.warn( + `graphic ValueType ${graphicItem.ValueType} not currently supported, skipping annotation.` + ); + return; + } + const NUMContentItems = mergedContentSequence.filter( group => group.ValueType === 'NUM' ); - const measurements = []; + const measurement = { + loaded: false, + labels: [], + coords: [_getCoordsFromSCOORDOrSCOORD3D(graphicItem)], + }; NUMContentItems.forEach(item => { - debugger; + const { ConceptNameCodeSequence, MeasuredValueSequence } = item; - const { ContentSequence, MeasuredValueSequence } = item; - - // GET -> REPRESENTATION, REFERNECED IMAGE, measurement value + if (MeasuredValueSequence) { + measurement.labels.push( + _getLabelFromMeasuredValueSequence( + ConceptNameCodeSequence, + MeasuredValueSequence + ) + ); + } }); - // Need to deal with TID 1410 style measurements, which will have a SCOORD or SCOORD3D at the top level, - // And non-geometric representations where each NUM has "INFERRED FROM" SCOORD/SCOORD3D - - // TODO -> Look at RelationshipType => Contains means - - return []; + return measurement; } -function _processTID1410Measurement(mergedContentSequence) { - // TODO - console.error('TODO => Process TID1410Measurement'); - debugger; +function _processNonGeometricallyDefinedMeasurement(mergedContentSequence) { + const NUMContentItems = mergedContentSequence.filter( + group => group.ValueType === 'NUM' + ); - return [ß]; + const measurement = { + loaded: false, + labels: [], + coords: [], + }; + + NUMContentItems.forEach(item => { + const { + ConceptNameCodeSequence, + ContentSequence, + MeasuredValueSequence, + } = item; + + const { ValueType } = ContentSequence; + + if (!ValueType === 'SCOORD') { + console.warn( + `Graphic ${ValueType} not currently supported, skipping annotation.` + ); + + return; + } + + const coords = _getCoordsFromSCOORDOrSCOORD3D(ContentSequence); + + if (coords) { + measurement.coords.push(coords); + } + + if (MeasuredValueSequence) { + measurement.labels.push( + _getLabelFromMeasuredValueSequence( + ConceptNameCodeSequence, + MeasuredValueSequence + ) + ); + } + }); + + return measurement; +} + +function _getCoordsFromSCOORDOrSCOORD3D(item) { + const { ValueType, RelationshipType, GraphicType, GraphicData } = item; + + if (RelationshipType !== RELATIONSHIP_TYPE.INFERRED_FROM) { + console.warn( + `Relationshiptype === ${RelationshipType}. Cannot deal with NON TID-1400 SCOORD group with RelationshipType !== "INFERRED FROM."` + ); + + return; + } + + const coords = { ValueType, GraphicType, GraphicData }; + + // ContentSequence has length of 1 as RelationshipType === 'INFERRED FROM' + if (ValueType === 'SCOORD') { + const { ReferencedSOPSequence } = item.ContentSequence; + + coords.ReferencedSOPSequence = ReferencedSOPSequence; + } else if (ValueType === 'SCOORD3D') { + const { ReferencedFrameOfReferenceSequence } = item.ContentSequence; + + coords.ReferencedFrameOfReferenceSequence = ReferencedFrameOfReferenceSequence; + } + + return coords; +} + +function _getLabelFromMeasuredValueSequence( + ConceptNameCodeSequence, + MeasuredValueSequence +) { + const { CodeMeaning } = ConceptNameCodeSequence; + const { NumericValue, MeasurementUnitsCodeSequence } = MeasuredValueSequence; + const { CodeValue } = MeasurementUnitsCodeSequence; + + return { label: CodeMeaning, value: `${NumericValue} ${CodeValue}` }; // E.g. Long Axis: 31.0 mm } function _getReferencedImagesList(ImagingMeasurementReportContentSequence) { diff --git a/extensions/dicom-sr/src/index.js b/extensions/dicom-sr/src/index.js index 533c06381..17e7a43a1 100644 --- a/extensions/dicom-sr/src/index.js +++ b/extensions/dicom-sr/src/index.js @@ -1,6 +1,7 @@ import React from 'react'; import getSopClassHandlerModule from './getSopClassHandlerModule'; import id from './id.js'; +import init from './init'; const Component = React.lazy(() => { return import('./OHIFCornerstoneSRViewport'); @@ -23,6 +24,10 @@ export default { */ id, + preRegistration({ servicesManager, configuration = {} }) { + init({ servicesManager, configuration }); + }, + /** * * diff --git a/extensions/dicom-sr/src/init.js b/extensions/dicom-sr/src/init.js new file mode 100644 index 000000000..5734103f0 --- /dev/null +++ b/extensions/dicom-sr/src/init.js @@ -0,0 +1,21 @@ +import cornerstoneTools from 'cornerstone-tools'; +import DICOMSRDisplayTool from './tools/DICOMSRDisplayTool'; + +import TOOL_NAMES from './constants/toolNames'; + +const defaultConfig = { + TOOL_NAMES: { + DICOM_SR_DISPLAY_TOOL: 'DICOMSRDisplayTool', + }, +}; + +/** + * @param {object} configuration + */ +export default function init({ configuration = {} }) { + const conifg = Object.assign({}, defaultConfig, configuration); + + TOOL_NAMES.DICOM_SR_DISPLAY_TOOL = conifg.TOOL_NAMES.DICOM_SR_DISPLAY_TOOL; + + cornerstoneTools.addTool(DICOMSRDisplayTool); +} diff --git a/extensions/dicom-sr/src/tools/DICOMSRDisplayTool.js b/extensions/dicom-sr/src/tools/DICOMSRDisplayTool.js new file mode 100644 index 000000000..ff0a2eab6 --- /dev/null +++ b/extensions/dicom-sr/src/tools/DICOMSRDisplayTool.js @@ -0,0 +1,123 @@ +import { importInternal, getToolState, toolColors } from 'cornerstone-tools'; + +import TOOL_NAMES from '../constants/toolNames'; +import SCOORD_TYPES from '../constants/scoordTypes'; + +// Cornerstone 3rd party dev kit imports +const draw = importInternal('drawing/draw'); +const drawJoinedLines = importInternal('drawing/drawJoinedLines'); +const drawCircle = importInternal('drawing/drawCircle'); +const drawEllipse = importInternal('drawing/drawEllipse'); +const getNewContext = importInternal('drawing/getNewContext'); +const BaseTool = importInternal('base/BaseTool'); + +/** + * @class DICOMSRDisplayTool - Renders DICOMSR data in a read only manner (i.e. as an overlay). + * @extends cornerstoneTools.BaseTool + */ +export default class DICOMSRDisplayTool extends BaseTool { + constructor(props = {}) { + const defaultProps = { + mixins: ['enabledOrDisabledBinaryTool'], + name: TOOL_NAMES.DICOM_SR_DISPLAY_TOOL, + }; + + const initialProps = Object.assign(defaultProps, props); + + super(initialProps); + } + + renderToolData(evt) { + const eventData = evt.detail; + const { element } = eventData; + + const toolState = getToolState(element, this.name); + + if (!toolState) { + return; + } + + const options = { + color: toolColors.setToolColor(), + lineWidth: 2, + handleRadius: 6, + }; + + for (let i = 0; i < toolState.data.length; i++) { + const data = toolState.data[i]; + + Object.keys(data).forEach(GraphicType => { + const renderableData = data[GraphicType]; + + switch (GraphicType) { + case SCOORD_TYPES.POINT: + case SCOORD_TYPES.MULTIPOINT: + renderPointOrMultipoint(renderableData, eventData, options); + break; + case SCOORD_TYPES.POLYLINE: + renderPolyLine(renderableData, eventData, options); + break; + case SCOORD_TYPES.CIRCLE: + renderCircle(renderableData, eventData, options); + break; + case SCOORD_TYPES.ELLIPSE: + renderEllipse(renderableData, eventData, options); + break; + } + }); + } + } + + renderPolyLine(renderableData, eventData, options) { + const { element } = eventData; + const context = getNewContext(eventData.canvasContext.canvas); + + renderableData.forEach(points => { + draw(context, context => { + drawJoinedLines(context, element, points[0], points, options); + }); + }); + } + + renderPointOrMultipoint(renderableData, eventData, options) { + const context = getNewContext(eventData.canvasContext.canvas); + + renderableData.forEach(points => { + draw(context, context => { + drawHandles(context, eventData, points, options); + }); + }); + } + + renderCircle(renderableData, eventData, options) { + const { element } = eventData; + + const context = getNewContext(eventData.canvasContext.canvas); + + renderableData.forEach(circle => { + const { center, radius } = circle; + + drawCircle(context, element, center, radius, options); + }); + } + + renderEllipse(renderableData, eventData, options) { + const { element } = eventData; + + const context = getNewContext(eventData.canvasContext.canvas); + + renderableData.forEach(ellipse => { + const { corner1, corner2 } = ellipse; + + drawEllipse( + context, + element, + corner1, + corner2, + options, + 'pixel', + 0 // TODO -> Work our the initial rotation and add it here so we render appropriately rotated ellipses. + ); + }); + } +} diff --git a/extensions/dicom-sr/src/utils/addMeasurement.js b/extensions/dicom-sr/src/utils/addMeasurement.js new file mode 100644 index 000000000..402082113 --- /dev/null +++ b/extensions/dicom-sr/src/utils/addMeasurement.js @@ -0,0 +1,135 @@ +import cornerstoneTools from 'cornerstone-tools'; +import TOOL_NAMES from '../constants/toolNames'; +import SCOORD_TYPES from '../constants/scoordTypes'; +import { cornerstone } from '../../../../platform/core/src'; + +const globalImageIdSpecificToolStateManager = + cornerstoneTools.globalImageIdSpecificToolStateManager; + +export default function addMeasurement( + measurement, + imageId, + displaySetInstanceUID +) { + console.log('== ADD MEASUREMENT TO CST =='); + console.log(measurement); + console.log(imageId); + console.log('============================'); + + // TODO -> GET TRACKING UID AND ADD IT TO TOOL DATA AND ALSO THE MEASUREMENT ENTRY. + // TODO -> Render rotated ellipse . + + debugger; + + const toolName = TOOL_NAMES.DICOM_SR_DISPLAY_TOOL; + + const measurementData = {}; + + measurement.coords.forEach(coord => { + const { GraphicType, GraphicData } = coord; + + if (measurementData[GraphicType] === undefined) { + measurementData[GraphicType] = []; + } + + const renderableData = _getRenderableData(GraphicType, GraphicData); + + measurementData[GraphicType].push(renderableData); + }); + + const toolState = globalImageIdSpecificToolStateManager.saveToolState(); + + if (toolState[imageId] === undefined) { + toolState[imageId] = {}; + } + + const imageIdToolState = toolState[imageId]; + + // If we don't have tool state for this type of tool, add an empty object + if (imageIdToolState[toolName] === undefined) { + imageIdToolState[toolName] = { + data: [], + }; + } + + const toolData = imageIdToolState[toolName]; + + toolData.data.push(measurementData); + + measurement.loaded = true; + measurement.imageId = imageId; + measurement.displaySetInstanceUID = displaySetInstanceUID; + debugger; +} + +function _getRenderableData(GraphicType, GraphicData) { + let renderableData; + + switch (GraphicType) { + case SCOORD_TYPES.POINT: + case SCOORD_TYPES.MULTIPOINT: + case SCOORD_TYPES.POLYLINE: + renderableData = []; + + for (let i = 0; i < GraphicData.length; i += 2) { + renderableData.push({ x: GraphicData[i], y: GraphicData[i + 1] }); + } + break; + case SCOORD_TYPES.CIRCLE: + const center = { x: GraphicData[0], y: GraphicData[1] }; + const onPerimeter = { x: GraphicData[2], y: GraphicData[3] }; + + const radius = cornerstoneMath.point.distance(center, onPerimeter); + + renderableData = { + center, + radius, + }; + break; + case SCOORD_TYPES.ELLIPSE: + console.warn('ROTATED ELLIPSE NOT YET SUPPORTED!'); + + const majorAxis = [ + { x: GraphicData[0], y: GraphicData[1] }, + { x: GraphicData[2], y: GraphicData[3] }, + ]; + const minorAxis = [ + { x: GraphicData[4], y: GraphicData[5] }, + { x: GraphicData[6], y: GraphicData[7] }, + ]; + + // Calculate two opposite corners of box defined by two axes. + + const minorAxisLength = cornerstoneMath.point.distance( + minorAxis[0], + minorAxis[1] + ); + + const minorAxisDirection = { + x: (minorAxis[1].x - minorAxis[0].x) / minorAxisLength, + y: (minorAxis[1].y - minorAxis[0].y) / minorAxisLength, + }; + + const halfMinorAxisLength = minorAxisLength / 2; + + // First end point of major axis + half minor axis vector + const corner1 = { + x: majorAxis[0].x + minorAxisDirection.x * halfMinorAxisLength, + y: majorAxis[0].y + minorAxisDirection.y * halfMinorAxisLength, + }; + + // Second end point of major axis - half of minor axis vector + const corner2 = { + x: majorAxis[1].x - minorAxisDirection.x * halfMinorAxisLength, + y: majorAxis[1].y - minorAxisDirection.y * halfMinorAxisLength, + }; + + renderableData = { + corner1, + corner2, + }; + break; + } + + return renderableData; +} diff --git a/platform/core/src/extensions/ExtensionManager.js b/platform/core/src/extensions/ExtensionManager.js index 4e0fda85f..af11f1cf4 100644 --- a/platform/core/src/extensions/ExtensionManager.js +++ b/platform/core/src/extensions/ExtensionManager.js @@ -17,9 +17,12 @@ export default class ExtensionManager { this.modules[moduleType] = []; }); this.dataSourceMap = {}; + this.defaultDataSourceName = appConfig.defaultDataSourceName; + this.activeDataSource = undefined; + } - console.log('modules map'); - console.log(this.modulesMap); + setActiveDataSource(dataSourceName) { + this.activeDataSource = dataSourceName; } /** @@ -137,6 +140,10 @@ export default class ExtensionManager { }; getDataSources = dataSourceName => { + if (dataSourceName === undefined) { + dataSourceName = this.activeDataSource; + } + // Note: this currently uses the data source name, which feels weird... return this.dataSourceMap[dataSourceName]; }; diff --git a/platform/viewer/public/config/default.js b/platform/viewer/public/config/default.js index 1b22769f0..4aa619bd2 100644 --- a/platform/viewer/public/config/default.js +++ b/platform/viewer/public/config/default.js @@ -23,4 +23,5 @@ window.config = { }, }, ], + defaultDataSourceName: 'dicomweb', }; diff --git a/platform/viewer/src/appInit.js b/platform/viewer/src/appInit.js index 973bb7603..f553eaa22 100644 --- a/platform/viewer/src/appInit.js +++ b/platform/viewer/src/appInit.js @@ -13,6 +13,8 @@ import { // redux as reduxOHIF, } from '@ohif/core'; +// TODO -> this feels bad. + /** * @param {object|func} appConfigOrFunc - application configuration, or a function that returns application configuration * @param {object[]} defaultExtensions - array of extension objects diff --git a/platform/viewer/src/routes/Mode/Mode.jsx b/platform/viewer/src/routes/Mode/Mode.jsx index 50d29fb1e..371aa4c0d 100644 --- a/platform/viewer/src/routes/Mode/Mode.jsx +++ b/platform/viewer/src/routes/Mode/Mode.jsx @@ -27,12 +27,15 @@ export default function ModeRoute({ : [StudyInstanceUIDs]; const { extensions, sopClassHandlers } = mode; - // TODO: - // - Check query/params for specific dataSource - // - If provided, query for that dataSource instance - // - If not provided, select default datasource - // - Update `extensionManager` to have a method to retrieve the default source - const dataSources = extensionManager.getDataSources(dataSourceName); + + if (dataSourceName === undefined) { + dataSourceName = extensionManager.defaultDataSourceName; + } + + extensionManager.setActiveDataSource(dataSourceName); + + const dataSources = extensionManager.getDataSources(); + const dataSource = dataSources[0]; const route = mode.routes[0]; diff --git a/platform/viewer/src/routes/buildModeRoutes.js b/platform/viewer/src/routes/buildModeRoutes.js index a4822e4cd..bd63e3076 100644 --- a/platform/viewer/src/routes/buildModeRoutes.js +++ b/platform/viewer/src/routes/buildModeRoutes.js @@ -68,6 +68,30 @@ export default function buildModeRoutes( exact: true, }); }); + + const defaultDataSourceName = extensionManager.defaultDataSourceName; + + // Add default DataSource route. + const path = `/${mode.id}`; + + // TODO move up. + const component = ({ location }) => ( + + + + ); + + routes.push({ + path, + component, + exact: true, + }); }); return routes; diff --git a/platform/viewer/src/routes/index.js b/platform/viewer/src/routes/index.js index 8869837e9..8ba005b10 100644 --- a/platform/viewer/src/routes/index.js +++ b/platform/viewer/src/routes/index.js @@ -29,8 +29,7 @@ const createRoutes = ( const routes = buildModeRoutes(modes, dataSources, extensionManager, servicesManager) || []; - // TODO: Shouldn't need to guard input routes with an empty array? - const allRoutes = [...(routes || []), ...bakedInRoutes]; + const allRoutes = [...routes, ...bakedInRoutes]; console.log( 'Creating Routes: ',