From 5e3d363f704d1200d848896fb8bb721a648fdfa5 Mon Sep 17 00:00:00 2001 From: Nicholas Lee Date: Wed, 4 Dec 2024 23:34:02 -0500 Subject: [PATCH] feat: added nimare file parsing --- .../Visualizer/NiiVueVisualizer.tsx | 8 +- .../components/DisplayMetaAnalysisResults.tsx | 76 ++++++++++++----- .../components/DisplayParsedNiMareFile.tsx | 85 +++++++++++++++++++ 3 files changed, 145 insertions(+), 24 deletions(-) create mode 100644 compose/neurosynth-frontend/src/pages/MetaAnalysis/components/DisplayParsedNiMareFile.tsx diff --git a/compose/neurosynth-frontend/src/components/Visualizer/NiiVueVisualizer.tsx b/compose/neurosynth-frontend/src/components/Visualizer/NiiVueVisualizer.tsx index c2f1ecf3..9c441b2f 100644 --- a/compose/neurosynth-frontend/src/components/Visualizer/NiiVueVisualizer.tsx +++ b/compose/neurosynth-frontend/src/components/Visualizer/NiiVueVisualizer.tsx @@ -6,7 +6,7 @@ let niivue: Niivue; const NiiVueVisualizer: React.FC<{ imageURL: string }> = ({ imageURL }) => { const canvasRef = useRef(null); - const [softThreshold, setSoftThresold] = useState(false); + const [softThreshold, setSoftThresold] = useState(true); const [showNegatives, setShowNegatives] = useState(false); const [showCrosshairs, setShowCrosshairs] = useState(true); const [threshold, setThreshold] = useState<{ @@ -104,8 +104,8 @@ const NiiVueVisualizer: React.FC<{ imageURL: string }> = ({ imageURL }) => { niivue.attachToCanvas(canvasRef.current); niivue.addVolumesFromUrl(volumes).then(() => { - niivue.volumes[1].alphaThreshold = 0; - niivue.overlayOutlineWidth = 0; + niivue.overlayOutlineWidth = 2; + niivue.volumes[1].alphaThreshold = 5; niivue.volumes[0].colorbarVisible = false; niivue.volumes[1].colormapNegative = ''; @@ -119,7 +119,7 @@ const NiiVueVisualizer: React.FC<{ imageURL: string }> = ({ imageURL }) => { setThreshold({ min: 0, - max: largestAbsoluteValue + 0.1, + max: Math.round((largestAbsoluteValue + 0.1) * 100) / 100, value: startingValue, }); niivue.volumes[1].cal_min = startingValue; diff --git a/compose/neurosynth-frontend/src/pages/MetaAnalysis/components/DisplayMetaAnalysisResults.tsx b/compose/neurosynth-frontend/src/pages/MetaAnalysis/components/DisplayMetaAnalysisResults.tsx index 25583427..eaa1a56f 100644 --- a/compose/neurosynth-frontend/src/pages/MetaAnalysis/components/DisplayMetaAnalysisResults.tsx +++ b/compose/neurosynth-frontend/src/pages/MetaAnalysis/components/DisplayMetaAnalysisResults.tsx @@ -1,12 +1,14 @@ -import { OpenInNew } from '@mui/icons-material'; +import { Download, OpenInNew } from '@mui/icons-material'; import { Box, Button, Link, List, ListItemButton, ListItemText, Typography } from '@mui/material'; import NiiVueVisualizer from 'components/Visualizer/NiiVueVisualizer'; import { MetaAnalysisReturn, NeurovaultFile, ResultReturn } from 'neurosynth-compose-typescript-sdk'; import MetaAnalysisResultStatusAlert from './MetaAnalysisResultStatusAlert'; import useGetMetaAnalysisResultById from 'hooks/metaAnalyses/useGetMetaAnalysisResultById'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import StateHandlerComponent from 'components/StateHandlerComponent/StateHandlerComponent'; import useGetNeurovaultImages, { INeurovault } from 'hooks/metaAnalyses/useGetNeurovault'; +import DisplayParsedNiMareFile from './DisplayParsedNiMareFile'; +import ImageIcon from '@mui/icons-material/Image'; const DisplayMetaAnalysisResults: React.FC<{ metaAnalysis: MetaAnalysisReturn | undefined; @@ -28,17 +30,21 @@ const DisplayMetaAnalysisResults: React.FC<{ isLoading: neurovaultFilesIsLoading, isError: neurovaultFilesIsError, } = useGetNeurovaultImages(neurovaultFileURLs); - console.log({ neurovaultFiles }); const [selectedNeurovaultImage, setSelectedNeurovaultImage] = useState(); + useEffect(() => { + if (!neurovaultFiles) return; + setSelectedNeurovaultImage(neurovaultFiles[0]); + }, [neurovaultFiles]); + return ( - - + + {(neurovaultFiles || []).map((neurovaultFile) => ( + + + {selectedNeurovaultImage?.file ? ( - + <> + + + + + + + + + ) : ( No image selected )} - ); }; diff --git a/compose/neurosynth-frontend/src/pages/MetaAnalysis/components/DisplayParsedNiMareFile.tsx b/compose/neurosynth-frontend/src/pages/MetaAnalysis/components/DisplayParsedNiMareFile.tsx new file mode 100644 index 00000000..87633fb4 --- /dev/null +++ b/compose/neurosynth-frontend/src/pages/MetaAnalysis/components/DisplayParsedNiMareFile.tsx @@ -0,0 +1,85 @@ +import { HelpOutline } from '@mui/icons-material'; +import { Box, Icon, Paper, Tooltip, Typography } from '@mui/material'; +import { useMemo } from 'react'; + +const nimareOutputs = { + // possible value types + z: 'Z-statistic', + t: 'T-statistic', + p: 'p-value', + logp: 'Negative base-ten logarithm of p-value', + chi2: 'Chi-squared value', + prob: 'Probability value', + stat: 'Test value of meta-analytic algorithm (e.g., ALE values for ALE, OF values for MKDA)', + est: 'Parameter estimate (IBMA only)', + se: 'Standard error of the parameter estimate (IBMA only)', + tau2: 'Estimated between-study variance (IBMA only)', + sigma2: 'Estimated within-study variance (IBMA only)', + label: 'Label map', + // methods of meta analysis + desc: 'Description of the data type. Only used when multiple maps with the same data type are produced by the same method.', + level: 'Level of multiple comparisons correction. Either cluster or voxel.', + corr: 'Type of multiple comparisons correction. Either FWE (familywise error rate) or FDR (false discovery rate).', + method: 'Name of the method used for multiple comparisons correction (e.g., “montecarlo” for a Monte Carlo procedure).', + diag: 'Type of diagnostic. Either Jackknife (jackknife analysis) or FocusCounter (focus-count analysis).', + tab: 'Type of table. Either clust (clusters table) or counts (contribution table).', + tail: 'Sign of the tail for label maps. Either positive or negative.', +}; + +const parseSegment = (segment: string): { key: string; keyDesc: string; value: string } => { + const [key, value] = segment.split('-'); + if (value === undefined) { + // not a method + return { + key: 'type', + keyDesc: 'The type of data in the map.', + value: `${nimareOutputs[key as keyof typeof nimareOutputs]}`, + }; + } else { + return { + key: key, + keyDesc: nimareOutputs[key as keyof typeof nimareOutputs], + value: value, + }; + } +}; + +const DisplayParsedNiMareFile: React.FC<{ nimareFileName: string | undefined }> = (props) => { + const fileNameSegments = useMemo(() => { + if (!props.nimareFileName) return []; + const segments = props.nimareFileName.replace('.nii.gz', '').split('_'); + return segments.map(parseSegment); + }, [props.nimareFileName]); + + return ( + + {fileNameSegments.map((segment) => ( + + + + {segment.key} + + {segment.keyDesc}} placement="top"> + + + + + + {segment.value} + + ))} + + ); +}; + +export default DisplayParsedNiMareFile;