Skip to content

Commit

Permalink
feature(viewer.js): Polygon/Point/Ellipse/Rectangle 2D and 3D bulk an…
Browse files Browse the repository at this point in the history
…notations support (#170)

* Bump package versions and update logging

* Improve logs and bump java heap memory

* Add new annotation property

* Add proxy script to start dev viewer

* Address multiple calls to populate viewports

* Rollback populate viewports call to did mount

* Bump mem size for larger annotations

* Add 2d support

* Add firebase config

* Remove fixed zoom level

* Cleanup on unmount

* Improve error logging

* Add warnings to debug toolbar button and disable strict mode

* Run lint

* Adjustments to error handling middlware

* fix: filter ann groups per series (#215)

* fix: filter ann groups per series

* fix: change to referencedSeriesInstanceUID

* feat: add tooltip on hover (#217)

* Lint

* Lint

* Update dicomweb action

* Update dcm4chee response

* Update docker version

---------

Co-authored-by: Pedro H. Köhler <[email protected]>
  • Loading branch information
igoroctaviano and pedrokohler authored Jul 12, 2024
1 parent ec94737 commit 2c58820
Show file tree
Hide file tree
Showing 16 changed files with 412 additions and 99 deletions.
10 changes: 8 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version: '3.7'
version: "3.7"

volumes:
db_data: {}
Expand Down Expand Up @@ -62,9 +62,15 @@ services:
- 12575
env_file: docker-compose.env
environment:
# Used to set the initial and maximal Java heap size to avoid
# problems retrieving large WSI bulk data annotations.
#
# The default is "-Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m"
# Reference: https://github.com/dcm4che-dockerfiles/dcm4chee-arc-psql
JBOSS_JAVA_SIZING: "-Xms64m -Xmx4096m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=4096m"
WILDFLY_CHOWN: /opt/wildfly/standalone /storage
WILDFLY_WAIT_FOR: ldap:389 db:5432
HTTP_PROXY_ADDRESS_FORWARDING: 'true'
HTTP_PROXY_ADDRESS_FORWARDING: "true"
ARCHIVE_HOST: localhost
depends_on:
- ldap
Expand Down
19 changes: 19 additions & 0 deletions firebase.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,25 @@
{
"hosting": {
"public": "build",
"headers": [
{
"source": "/**",
"headers": [
{
"key": "Cache-Control",
"value": "no-cache, no-store, must-revalidate"
},
{
"key" : "Cross-Origin-Opener-Policy",
"value" : "same-origin"
},
{
"key" : "Cross-Origin-Embedder-Policy",
"value" : "require-corp"
}
]
}
],
"ignore": [
"firebase.json",
"**/.*",
Expand Down
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
"version": "0.31.3",
"homepage": "https://github.com/imagingdatacommons/slim",
"private": true,
"author": "ImagingDataCommons",
"proxy": "http://localhost:8008",
"scripts": {
"start": "rm -rf ./node_modules/.cache/default-development && craco start",
"build": "craco build",
"build:firebase": "REACT_APP_CONFIG=gcp PUBLIC_URL=/ craco build",
"lint": "ts-standard --env jest 'src/**/*.{tsx,ts}'",
"fmt": "ts-standard --env jest 'src/**/*.{tsx,ts}' --fix",
"test": "ts-standard --env jest 'src/**/*.{tsx,ts}' && craco test --setupFiles ./src/setupTests.tsx --watchAll=false",
Expand Down Expand Up @@ -47,10 +50,10 @@
"classnames": "^2.2.6",
"copy-webpack-plugin": "^10.2.4",
"craco-less": "^2.0.0",
"dcmjs": "^0.19.1",
"dcmjs": "^0.29.8",
"detect-browser": "^5.2.1",
"dicom-microscopy-viewer": "^0.45.1",
"dicomweb-client": "^0.8.4",
"dicomweb-client": "^0.10.3",
"gh-pages": "^5.0.0",
"oidc-client": "^1.11.5",
"react": "^18.2.0",
Expand Down
2 changes: 1 addition & 1 deletion public/config/preview.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,6 @@ window.config = {
color: [255, 255, 255, 0.2]
}
}
}
},
]
}
25 changes: 17 additions & 8 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,18 @@ class App extends React.Component<AppProps, AppState> {
'User is not authorized to access DICOMweb resources.')
)
}

const logServerError = (): void => {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
NotificationMiddleware.onError(
NotificationMiddlewareContext.DICOMWEB,
new CustomError(
errorTypes.COMMUNICATION,
'An unexpected server error occured.'
)
)
}

if (serverSettings.errorMessages !== undefined) {
serverSettings.errorMessages.forEach((setting: ErrorMessageSettings) => {
if (error.status === setting.status) {
Expand All @@ -193,15 +205,11 @@ class App extends React.Component<AppProps, AppState> {
}
})
} else if (error.status === 500) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
NotificationMiddleware.onError(
NotificationMiddlewareContext.DICOMWEB,
new CustomError(
errorTypes.COMMUNICATION,
'An unexpected server error occured.')
)
logServerError()
}
})
} else if (error.status === 500) {
logServerError()
}
}

Expand Down Expand Up @@ -350,7 +358,8 @@ class App extends React.Component<AppProps, AppState> {
isLoading: false,
wasAuthSuccessful: true
})
}).catch(() => {
}).catch((error) => {
console.error(error)
// eslint-disable-next-line @typescript-eslint/no-floating-promises
NotificationMiddleware.onError(
NotificationMiddlewareContext.AUTH,
Expand Down
4 changes: 4 additions & 0 deletions src/components/AnnotationGroupItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,10 @@ class AnnotationGroupItem extends React.Component<AnnotationGroupItemProps, Anno
{
name: 'Graphic type',
value: item.GraphicType
},
{
name: 'Annotation coordinate type',
value: this.props.metadata.AnnotationCoordinateType
}
]

Expand Down
3 changes: 2 additions & 1 deletion src/components/CaseViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,8 @@ class Viewer extends React.Component<ViewerProps, ViewerState> {
isLoading: false
})
}
).catch(() => {
).catch((error) => {
console.error(error)
// eslint-disable-next-line @typescript-eslint/no-floating-promises
NotificationMiddleware.onError(
NotificationMiddlewareContext.SLIM,
Expand Down
80 changes: 64 additions & 16 deletions src/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,17 @@ interface HeaderProps extends RouteComponentProps {
showServerSelectionButton: boolean
}

interface ExtendedCustomError extends CustomError {
source: string
}

interface HeaderState {
selectedServerUrl?: string
isServerSelectionModalVisible: boolean
isServerSelectionDisabled: boolean
errorObj: CustomError[]
errorObj: ExtendedCustomError[]
errorCategory: string[]
warnings: string[]
}

/**
Expand All @@ -66,23 +71,49 @@ class Header extends React.Component<HeaderProps, HeaderState> {
isServerSelectionModalVisible: false,
isServerSelectionDisabled: true,
errorObj: [],
errorCategory: []
errorCategory: [],
warnings: []
}

const onErrorHandler = ({ error }: {
category: string
const onErrorHandler = ({ source, error }: {
source: string
error: CustomError
}): void => {
this.setState({
errorObj: [...this.state.errorObj, error],
errorCategory: [...this.state.errorCategory, error.type]
})
this.setState(state => ({
...state,
errorObj: [...state.errorObj, { ...error, source }],
errorCategory: [...state.errorCategory, error.type]
}))
}

const onWarningHandler = (warning: string): void => {
this.setState(state => ({
...state,
warnings: [...state.warnings, warning]
}))
}

NotificationMiddleware.subscribe(
NotificationMiddlewareEvents.OnError,
onErrorHandler
)

NotificationMiddleware.subscribe(
NotificationMiddlewareEvents.OnWarning,
onWarningHandler
)
}

componentDidUpdate (prevProps: Readonly<HeaderProps>, prevState: Readonly<HeaderState>): void {
if (((prevState.warnings.length > 0) || (prevState.errorObj.length > 0)) && this.props.location.pathname !== prevProps.location.pathname) {
this.setState({
isServerSelectionModalVisible: false,
isServerSelectionDisabled: true,
errorObj: [],
errorCategory: [],
warnings: []
})
}
}

handleInfoButtonClick = (): void => {
Expand Down Expand Up @@ -163,7 +194,7 @@ class Header extends React.Component<HeaderProps, HeaderState> {
if (errorNum > 0) {
for (let i = 0; i < errorNum; i++) {
const category = this.state.errorCategory[i] as ObjectKey
errorMsgs[category].push(this.state.errorObj[i].message)
errorMsgs[category].push(`${this.state.errorObj[i].message as string} (Source: ${this.state.errorObj[i].source})`)
}
}

Expand All @@ -173,6 +204,10 @@ class Header extends React.Component<HeaderProps, HeaderState> {
<Badge count={errcount} />
)

const showWarningCount = (warncount: number): JSX.Element => (
<Badge color='green' count={warncount} />
)

Modal.info({
title: 'Debug Information\n (Check console for more information)',
width: 800,
Expand Down Expand Up @@ -222,6 +257,17 @@ class Header extends React.Component<HeaderProps, HeaderState> {
))}
</ol>
</Panel>
<Panel
header='Warning'
key='warning'
extra={showWarningCount(this.state.warnings.length)}
>
<ol>
{this.state.warnings.map(warning => (
<li key={uuidv4()}>{warning}</li>
))}
</ol>
</Panel>
</Collapse>
),
onOk (): void {}
Expand Down Expand Up @@ -280,11 +326,13 @@ class Header extends React.Component<HeaderProps, HeaderState> {

const debugButton = (
<Badge count={this.state.errorObj.length}>
<Button
icon={SettingOutlined}
tooltip='Debug info'
onClick={this.handleDebugButtonClick}
/>
<Badge color='green' count={this.state.warnings.length}>
<Button
icon={SettingOutlined}
tooltip='Debug info'
onClick={this.handleDebugButtonClick}
/>
</Badge>
</Badge>
)

Expand Down Expand Up @@ -318,15 +366,15 @@ class Header extends React.Component<HeaderProps, HeaderState> {
})
}

const handleServerSelectionCancellation = (event: any): void => {
const handleServerSelectionCancellation = (): void => {
this.setState({
selectedServerUrl: undefined,
isServerSelectionModalVisible: false,
isServerSelectionDisabled: true
})
}

const handleServerSelection = (event: any): void => {
const handleServerSelection = (): void => {
const url = this.state.selectedServerUrl
let closeModal = false
if (url != null && url !== '') {
Expand Down
33 changes: 33 additions & 0 deletions src/components/HoveredRoiTooltip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const HoveredRoiTooltip = ({
xPosition,
yPosition,
attributes
}: {
xPosition: number
yPosition: number
attributes: Array<{ name: string, value: string }>
}): JSX.Element => {
return (
<div
style={{
position: 'fixed',
top: `${yPosition}px`,
left: `${xPosition}px`,
backgroundColor: 'rgba(230, 230, 230, 0.65)',
minWidth: '150px',
minHeight: '60px',
padding: '20px',
fontWeight: 'bold',
pointerEvents: 'none'
}}
>
{attributes.map((attr) => (
<div key={attr.name}>
{attr.name}: <span style={{ fontWeight: 500 }}>{attr.value}</span>
</div>
))}
</div>
)
}

export default HoveredRoiTooltip
3 changes: 2 additions & 1 deletion src/components/SlideItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,12 @@ class SlideItem extends React.Component<SlideItemProps, SlideItemState> {
],
metadata: metadata,
resizeFactor: 1,
errorInterceptor: (error: CustomError) =>
errorInterceptor: (error: CustomError) => {
NotificationMiddleware.onError(
NotificationMiddlewareContext.DMV,
error
)
}
})
this.overviewViewer.render({
container: this.overviewViewportRef.current
Expand Down
Loading

0 comments on commit 2c58820

Please sign in to comment.