Skip to content

Commit

Permalink
feat: added download and snapshot
Browse files Browse the repository at this point in the history
  • Loading branch information
nicoalee committed Jan 3, 2025
1 parent ff1e90e commit 675262a
Show file tree
Hide file tree
Showing 19 changed files with 543 additions and 293 deletions.
2 changes: 1 addition & 1 deletion compose/neurosynth-frontend/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

<link rel="preload" as="image" href="/static/brain-analysis.png" type="image/png">

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/handsontable/dist/handsontable.full.min.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/handsontable/dist/handsontable.full.min.css" crossorigin="anonymous" />

<link
rel="stylesheet"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import { Box, Checkbox, Slider, Typography } from '@mui/material';
import { Box, Button, Checkbox, Link, Slider, Typography } from '@mui/material';
import { ChangeEvent, useEffect, useRef, useState } from 'react';
import { Niivue, SHOW_RENDER } from '@niivue/niivue';
import { Download, OpenInNew } from '@mui/icons-material';
import ImageIcon from '@mui/icons-material/Image';

let niivue: Niivue;

const NiiVueVisualizer: React.FC<{ imageURL: string }> = ({ imageURL }) => {
const canvasRef = useRef(null);
const NiiVueVisualizer: React.FC<{ file: string; filename: string; neurovaultLink?: string }> = ({
file,
filename,
neurovaultLink,
}) => {
const canvasRef = useRef<HTMLCanvasElement>(null);
const [softThreshold, setSoftThresold] = useState(true);
const [showNegatives, setShowNegatives] = useState(false);
const [showCrosshairs, setShowCrosshairs] = useState(true);
Expand Down Expand Up @@ -84,7 +90,7 @@ const NiiVueVisualizer: React.FC<{ imageURL: string }> = ({ imageURL }) => {
opacity: 1,
},
{
url: imageURL,
url: file,
// url: 'https://niivue.github.io/niivue/images/fslt.nii.gz',
colorMap: 'warm',
cal_min: 0, // default
Expand Down Expand Up @@ -128,12 +134,17 @@ const NiiVueVisualizer: React.FC<{ imageURL: string }> = ({ imageURL }) => {
niivue.setInterpolation(true);
niivue.updateGLVolume();
});
}, [imageURL]);
}, [file]);

const handleDownloadImage = () => {
if (!niivue) return;
niivue.saveScene(filename + '.png');
};

return (
<Box>
<Box sx={{ marginBottom: '10px', display: 'flex', justifyContent: 'space-between' }}>
<Box width="300px">
<Box width="250px">
<Typography gutterBottom={false}>Threshold</Typography>
<Slider
valueLabelDisplay="auto"
Expand All @@ -144,22 +155,64 @@ const NiiVueVisualizer: React.FC<{ imageURL: string }> = ({ imageURL }) => {
value={threshold.value}
></Slider>
</Box>
<Box>
<Typography gutterBottom={false}>Soft Threshold</Typography>
<Checkbox checked={softThreshold} onChange={handleToggleSoftThreshold} />
</Box>
<Box>
<Typography gutterBottom={false}>Show Negatives</Typography>
<Checkbox checked={showNegatives} onChange={handleToggleNegatives} />
</Box>
<Box>
<Typography gutterBottom={false}>Show Crosshairs</Typography>
<Checkbox value={showCrosshairs} checked={showCrosshairs} onChange={handleToggleShowCrosshairs} />
<Box display="flex">
<Box width="100px">
<Typography gutterBottom={false}>Soft Threshold</Typography>
<Checkbox checked={softThreshold} onChange={handleToggleSoftThreshold} />
</Box>
<Box width="100px">
<Typography gutterBottom={false}>Show Negatives</Typography>
<Checkbox checked={showNegatives} onChange={handleToggleNegatives} />
</Box>
<Box width="100px">
<Typography gutterBottom={false}>Show Crosshairs</Typography>
<Checkbox
value={showCrosshairs}
checked={showCrosshairs}
onChange={handleToggleShowCrosshairs}
/>
</Box>
</Box>
</Box>
<Box sx={{ height: '300px' }}>
<canvas ref={canvasRef} />
</Box>
<Box display="flex" alignItems="center" justifyContent="space-between">
<Box>
<Button
size="small"
variant="contained"
endIcon={<Download />}
href={file}
sx={{ marginTop: '0.5rem', marginRight: '0.5rem' }}
>
Download NIfTI
</Button>
<Button
size="small"
variant="contained"
onClick={handleDownloadImage}
endIcon={<ImageIcon />}
sx={{ marginTop: '0.5rem' }}
>
Download image
</Button>
</Box>
{neurovaultLink && (
<Button
component={Link}
sx={{ marginTop: '0.5rem' }}
href={neurovaultLink.includes('/api') ? neurovaultLink.replace(/\/api/, '') : neurovaultLink}
rel="noreferrer"
size="small"
target="_blank"
disableElevation
>
Open in neurovault
<OpenInNew sx={{ marginLeft: '4px' }} fontSize="small" />
</Button>
)}
</Box>
</Box>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const MockNiiVueVisualizer: React.FC<{ imageURL: string }> = ({ imageURL }) => {
return (
<div>
<h1>Mocked NiiVue Visualizer</h1>
<span data-testid="imageURL">{imageURL}</span>
</div>
);
};

export default MockNiiVueVisualizer;
16 changes: 16 additions & 0 deletions compose/neurosynth-frontend/src/hooks/__mocks__/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import {
mockAnnotations,
mockBaseStudy,
mockConditions,
mockMetaAnalysisResult,
mockNeurovault,
mockProject,
mockStudy,
mockStudysetNested,
Expand Down Expand Up @@ -179,6 +181,18 @@ const useIsMounted = () => {

const useUserCanEdit = vi.fn().mockReturnValue(true);

const useGetMetaAnalysisResultById = vi.fn().mockReturnValue({
isLoading: false,
isError: false,
data: mockMetaAnalysisResult(),
});

const useGetNeurovaultImages = vi.fn().mockReturnValue({
isLoading: false,
isError: false,
data: mockNeurovault(),
});

export {
useCreateAnalysis,
useCreateCondition,
Expand Down Expand Up @@ -207,4 +221,6 @@ export {
useUpdateStudyset,
useUserCanEdit,
useGetProjectById,
useGetMetaAnalysisResultById,
useGetNeurovaultImages,
};
5 changes: 5 additions & 0 deletions compose/neurosynth-frontend/src/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import useCreateAlgorithmSpecification from './metaAnalyses/useCreateAlgorithmSp
import useGetMetaAnalysesByIds from './metaAnalyses/useGetMetaAnalysesByIds';
import useGetMetaAnalysisById from './metaAnalyses/useGetMetaAnalysisById';
import useGetMetaAnalysesPublic from './metaAnalyses/useGetMetaAnalysesPublic';
import useGetMetaAnalysisResultById from './metaAnalyses/useGetMetaAnalysisResultById';
import useGetAnnotationsByStudysetId from './analyses/useGetAnnotationsByStudysetId';
import useCreatePoint from './analyses/useCreatePoint';
import useUpdateStudy from './studies/useUpdateStudy';
Expand Down Expand Up @@ -38,6 +39,7 @@ import useGetFullText from './external/useGetFullText';
import useUserCanEdit from './useUserCanEdit';
import useGetBaseStudyById from './studies/useGetBaseStudyById';
import useGetProjectById from './projects/useGetProjectById';
import useGetNeurovaultImages from './metaAnalyses/useGetNeurovaultImages';

export {
useGetCurationSummary,
Expand All @@ -51,6 +53,7 @@ export {
useGetFullText,
useUserCanEdit,
useGetBaseStudyById,
useGetNeurovaultImages,
// STUDIES
useGetBaseStudies,
useGetStudyById,
Expand All @@ -61,6 +64,8 @@ export {
useGetMetaAnalysesByIds,
useGetMetaAnalysisById,
useGetMetaAnalysesPublic,
// META-ANALYSIS RESULTS
useGetMetaAnalysisResultById,
// STUDYSETS
useGetStudysets,
useGetStudysetById,
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import axios, { AxiosResponse } from 'axios';
import { useQuery } from 'react-query';

export interface INeurovault {
url: string | null;
id: number | null;
file: string | null;
collection: string | null;
collection_id: number | null;
file_size: number | null;
cognitive_paradigm_cogatlas: string | null;
cognitive_paradigm_cogatlas_id: string | null;
cognitive_contrast_cogatlas: string | null;
cognitive_contrast_cogatlas_id: string | null;
map_type: string | null;
analysis_level: string | null;
name: string | null;
description: string | null;
add_date: string | null;
modify_date: string | null;
is_valid: boolean;
surface_left_file: string | null;
surface_right_file: string | null;
data_origin: string | null;
target_template_image: string | null;
subject_species: string | null;
figure: string | null;
handedness: string | null;
age: string | null;
gender: string | null;
race: string | null;
ethnicity: string | null;
BMI: string | null;
fat_percentage: string | null;
waist_hip_ratio: string | null;
mean_PDS_score: string | null;
tanner_stage: string | null;
days_since_menstruation: string | null;
hours_since_last_meal: string | null;
bis_bas_score: string | null;
spsrq_score: string | null;
bis11_score: string | null;
thumbnail: string | null;
reduced_representation: string | null;
is_thresholded: boolean | null;
perc_bad_voxels: number | null;
not_mni: boolean | null;
brain_coverage: number | null;
perc_voxels_outside: number | null;
number_of_subjects: string | null;
modality: string | null;
statistic_parameters: string | null;
smoothness_fwhm: string | null;
contrast_definition: string | null;
contrast_definition_cogatlas: string | null;
cognitive_paradigm_description_url: string | null;
image_type: string | null;
}

function useGetNeurovaultImages(neurovaultImages: string[]) {
return useQuery({
queryKey: ['neurovault-images', ...neurovaultImages],
queryFn: async () => {
const res = await Promise.all<AxiosResponse<INeurovault>>(neurovaultImages.map((url) => axios.get(url)));
return res.map((x) => ({
...x.data,
file: (x.data.file || '').replace(/http/, 'https'), // without this, link will redirect but result in an error
}));
},
enabled: neurovaultImages.length > 0,
});
}

export default useGetNeurovaultImages;
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import { Box, Chip, Typography } from '@mui/material';
import NeurosynthBreadcrumbs from 'components/NeurosynthBreadcrumbs';
import StateHandlerComponent from 'components/StateHandlerComponent/StateHandlerComponent';
import TextEdit from 'components/TextEdit/TextEdit';
import { useGetMetaAnalysisById } from 'hooks';
import useGetMetaAnalysisResultById from 'hooks/metaAnalyses/useGetMetaAnalysisResultById';
import { useGetMetaAnalysisById, useGetMetaAnalysisResultById } from 'hooks';
import useGetSpecificationById from 'hooks/metaAnalyses/useGetSpecificationById';
import useUpdateMetaAnalysis from 'hooks/metaAnalyses/useUpdateMetaAnalysis';
import useUserCanEdit from 'hooks/useUserCanEdit';
Expand Down
Loading

0 comments on commit 675262a

Please sign in to comment.