diff --git a/src/components/plots/AnalysisWG.tsx b/src/components/plots/AnalysisWG.tsx index b2e73559..44ec6263 100644 --- a/src/components/plots/AnalysisWG.tsx +++ b/src/components/plots/AnalysisWG.tsx @@ -43,10 +43,10 @@ type Operation = keyof typeof ShaderMap; const AnalysisWG = ({ setTexture, ZarrDS }: { setTexture: React.Dispatch>, ZarrDS: ZarrDataset }) => { // Global state hooks remain the same - const { strides, dataShape, valueScales, isFlat, setIsFlat, setDownloading, setShowLoading, setValueScales } = useGlobalStore(useShallow(state => ({ + const { strides, dataShape, valueScales, isFlat, setIsFlat, setStatus, setValueScales } = useGlobalStore(useShallow(state => ({ strides: state.strides, dataShape: state.dataShape, valueScales: state.valueScales, - isFlat: state.isFlat, setIsFlat: state.setIsFlat, setDownloading: state.setDownloading, - setShowLoading: state.setShowLoading, setValueScales: state.setValueScales, + isFlat: state.isFlat, setIsFlat: state.setIsFlat, setStatus: state.setStatus, + setValueScales: state.setValueScales, }))); const setPlotType = usePlotStore(state => state.setPlotType); @@ -73,7 +73,7 @@ const AnalysisWG = ({ setTexture, ZarrDS }: { setTexture: React.Dispatch { - setShowLoading(true); + setStatus("Computing..."); const currentOperation = operation == 'Convolution' ? kernelOperation as Operation : operation as Operation; // If it's convolution use the kernelOperation let newArray: Float16Array | Float32Array | undefined; let is3DResult = !isFlat; // Assume the result's dimensionality until determined @@ -81,13 +81,13 @@ const AnalysisWG = ({ setTexture, ZarrDS }: { setTexture: React.Dispatch{ const Plot = ({ZarrDS}:{ZarrDS: ZarrDataset}) => { const { setShape, setDataShape, setFlipY, setValueScales, setMetadata, setDimArrays, - setDimNames, setDimUnits, setPlotOn, setShowLoading} = useGlobalStore( + setDimNames, setDimUnits, setPlotOn, setStatus} = useGlobalStore( useShallow(state => ({ //UseShallow for object returns setShape:state.setShape, setDataShape: state.setDataShape, @@ -87,7 +87,7 @@ const Plot = ({ZarrDS}:{ZarrDS: ZarrDataset}) => { setDimNames:state.setDimNames, setDimUnits:state.setDimUnits, setPlotOn: state.setPlotOn, - setShowLoading: state.setShowLoading + setStatus: state.setStatus } ))) const {colormap, variable, isFlat, metadata, valueScales, is4D, setIsFlat} = useGlobalStore(useShallow(state=>({ @@ -126,7 +126,6 @@ const Plot = ({ZarrDS}:{ZarrDS: ZarrDataset}) => { //DATA LOADING useEffect(() => { if (variable != "Default") { - setShowLoading(true); setShow(false) try{ if (textures) { @@ -160,10 +159,10 @@ const Plot = ({ZarrDS}:{ZarrDS: ZarrDataset}) => { setDataShape(result.shape) setShow(true) setPlotOn(true) - setShowLoading(false) + setStatus(null) }) }catch{ - setShowLoading(false); + setStatus(null); return; } //Get Metadata diff --git a/src/components/ui/Loading.tsx b/src/components/ui/Loading.tsx index 4d86340f..0c4b7a7d 100644 --- a/src/components/ui/Loading.tsx +++ b/src/components/ui/Loading.tsx @@ -4,18 +4,16 @@ import { useShallow } from "zustand/shallow" export function Loading(){ - const {progress, showLoading, downloading, decompressing} = useGlobalStore(useShallow(state => ({ + const {progress, status} = useGlobalStore(useShallow(state => ({ progress: state.progress, - showLoading: state.showLoading, - downloading: state.downloading, - decompressing: state.decompressing + status: state.status }))) return ( - showLoading && + status &&
- {downloading ? 'Downloading' : decompressing ? 'Unpacking' : 'Building'} + {status}
- ) } \ No newline at end of file diff --git a/src/components/ui/MainPanel/Dataset.tsx b/src/components/ui/MainPanel/Dataset.tsx index a726a7f4..70070562 100644 --- a/src/components/ui/MainPanel/Dataset.tsx +++ b/src/components/ui/MainPanel/Dataset.tsx @@ -212,7 +212,9 @@ const Dataset = ({setOpenVariables} : {setOpenVariables: React.Dispatch - diff --git a/src/components/ui/MainPanel/LocalZarr.tsx b/src/components/ui/MainPanel/LocalZarr.tsx index a036d369..9ef6a2bc 100644 --- a/src/components/ui/MainPanel/LocalZarr.tsx +++ b/src/components/ui/MainPanel/LocalZarr.tsx @@ -1,7 +1,7 @@ "use client"; import React, {useState, useEffect, ChangeEvent} from 'react' import * as zarr from 'zarrita' -import { useZarrStore, useErrorStore } from '@/utils/GlobalStates'; +import { useZarrStore, useErrorStore, useGlobalStore } from '@/utils/GlobalStates'; import { Input } from '../input'; import ZarrParser from '@/components/zarr/ZarrParser'; @@ -13,16 +13,13 @@ interface LocalZarrType { const LocalZarr = ({setShowLocal, setOpenVariables, setInitStore}:LocalZarrType) => { const setCurrentStore = useZarrStore(state => state.setCurrentStore) - + const {setStatus} = useGlobalStore.getState() const handleFileSelect = async (event: ChangeEvent) => { const files = event.target.files; - if (!files || files.length === 0) { + setStatus(null) return; } - if (files.length > 1000){ - // useErrorStore.getState().setError('filecount') - } // Create a Map to hold the Zarr store data const fileMap = new Map(); @@ -53,12 +50,14 @@ const LocalZarr = ({setShowLocal, setOpenVariables, setInitStore}:LocalZarrType) // Metadata is missing. We will need to parse variables here. store = await ZarrParser(files, customStore) } - const gs = zarr.open(store, {kind: 'group'}); - gs.then(e=>{setCurrentStore(e)}) - setShowLocal(false) - setOpenVariables(true) + const gs = await zarr.open(store, {kind: 'group'}); + setCurrentStore(gs); + setShowLocal(false); + setOpenVariables(true); setInitStore(`local_${baseDir}`) + setStatus(null) } catch (error) { + setStatus(null) if (error instanceof Error) { console.log(`Error opening Zarr store: ${error.message}`); } else { @@ -66,18 +65,16 @@ const LocalZarr = ({setShowLocal, setOpenVariables, setInitStore}:LocalZarrType) } } }; - return (
- + className='hover:drop-shadow-md hover:scale-[110%]' + style={{width:'200px', cursor:'pointer'}} + // @ts-expect-error `webkitdirectory` is non-standard attribute. TS doesn't know about it. It's used for cross-browser compatibility. + directory='' + webkitdirectory='true' + onChange={handleFileSelect} + />
) } diff --git a/src/components/ui/MainPanel/MetaDataInfo.tsx b/src/components/ui/MainPanel/MetaDataInfo.tsx index 76ba7685..4dcf6cf9 100644 --- a/src/components/ui/MainPanel/MetaDataInfo.tsx +++ b/src/components/ui/MainPanel/MetaDataInfo.tsx @@ -368,42 +368,42 @@ const MetaDataInfo = ({ meta, setShowMeta, setOpenVariables }: { meta: any, setS } }
- {cached && -
- {cachedChunks ? - {cachedChunks} chunks already cached. : - This data is already cached. - } -
- } - {tooBig && -
- - Not only will this certainly not fit in memory, but it also won't fit in a single shader call. You are wild for this one. Textures: {texCount}/14 - +
+ {cached && +
+ {cachedChunks ? + {cachedChunks} chunks already cached. : + This data is already cached. + }
- } - {true && } + } + {tooBig && +
+ + Not only will this certainly not fit in memory, but it also won't fit in a single shader call. You are wild for this one. Textures: {texCount}/14 + +
+ } + {true && } +
) } diff --git a/src/components/zarr/ZarrLoaderLRU.ts b/src/components/zarr/ZarrLoaderLRU.ts index 071d29d5..4a7dfb1d 100644 --- a/src/components/zarr/ZarrLoaderLRU.ts +++ b/src/components/zarr/ZarrLoaderLRU.ts @@ -145,19 +145,21 @@ const maxRetries = 10; const retryDelay = 500; // 0.5 seconds in milliseconds export async function GetStore(storePath: string): Promise> | undefined>{ + const {setStatus} = useGlobalStore.getState(); for (let attempt = 0; attempt <= maxRetries; attempt++) { try { const d_store = zarr.tryWithConsolidated( new zarr.FetchStore(storePath) ); const gs = await d_store.then(store => zarr.open(store, {kind: 'group'})); + setStatus(null) return gs; } catch (error) { // If this is the final attempt, handle the error if (attempt === maxRetries) { if (storePath.slice(0,5) != 'local'){ useErrorStore.getState().setError('zarrFetch') - useGlobalStore.getState().setShowLoading(false) + setStatus(null) } throw new ZarrError(`Failed to initialize store at ${storePath}`, error); } @@ -202,7 +204,7 @@ export class ZarrDataset{ this.chunkIDs = []; } async GetArray(variable: string, slices: Slices){ - const {is4D, idx4D, initStore, setProgress, setStrides, setDownloading} = useGlobalStore.getState(); + const {is4D, idx4D, initStore, setProgress, setStrides, setStatus} = useGlobalStore.getState(); const {compress, setCurrentChunks, setArraySize} = useZarrStore.getState() const {cache} = useCacheStore.getState(); const {xSlice, ySlice, zSlice} = slices @@ -235,7 +237,7 @@ export class ZarrDataset{ let shape; let scalingFactor = null; if (totalSize < 50e6 || !hasTimeChunks){ // Check if total is less than 50MB or no chunks along time - setDownloading(true) + setStatus("Downloading...") for (let attempt = 0; attempt <= maxRetries; attempt++) { try { chunk = is4D ? await zarr.get(outVar, [idx4D, null , null, null]) : await zarr.get(outVar); @@ -244,8 +246,7 @@ export class ZarrDataset{ // If this is the final attempt, handle the error if (attempt === maxRetries) { useErrorStore.getState().setError('zarrFetch') - useGlobalStore.getState().setShowLoading(false) - setDownloading(false) + setStatus(null) throw new ZarrError(`Failed to fetch variable ${variable}`, error); } @@ -271,8 +272,8 @@ export class ZarrDataset{ } cache.set(is4D ? `${initStore}_${idx4D}_${variable}` : `${initStore}_${variable}`, cacheChunk) } - setDownloading(false) - } else if (outVar.shape.length == 2){ + setStatus(null) + } else if (outVar.shape.length == 2){ // 2D downloading/Slicing const totalYChunks = Math.ceil(outVar.shape[0]/chunkShape[0]) const totalXChunks = Math.ceil(outVar.shape[1]/chunkShape[1]) @@ -320,10 +321,9 @@ export class ZarrDataset{ } catch (error) { // If this is the final attempt, handle the error if (attempt === maxRetries) { - useErrorStore.getState().setError('zarrFetch') - useGlobalStore.getState().setShowLoading(false) - setDownloading(false) - setProgress(0) + useErrorStore.getState().setError('zarrFetch'); + setStatus(null); + setProgress(0); throw new ZarrError(`Failed to fetch chunk ${chunkID} for variable ${variable}`, error); } // Wait before retrying (except on the last attempt which we've already handled above) @@ -375,10 +375,10 @@ export class ZarrDataset{ } } } - setDownloading(false) + setStatus(null); setProgress(0) } else { - setDownloading(true) + setStatus("Downloading..."); setProgress(0) const totalZChunks = Math.ceil(outVar.shape[0+(is4D ? 1 : 0)]/chunkShape[0]) const totalYChunks = Math.ceil(outVar.shape[1+(is4D ? 1 : 0)]/chunkShape[1]) @@ -439,8 +439,7 @@ export class ZarrDataset{ // If this is the final attempt, handle the error if (attempt === maxRetries) { useErrorStore.getState().setError('zarrFetch') - useGlobalStore.getState().setShowLoading(false) - setDownloading(false) + setStatus(null); setProgress(0) throw new ZarrError(`Failed to fetch chunk ${chunkID} for variable ${variable}`, error); } @@ -494,7 +493,6 @@ export class ZarrDataset{ } } } - setDownloading(false) setProgress(0) // Reset progress for next load } return { diff --git a/src/utils/GlobalStates.ts b/src/utils/GlobalStates.ts index e1a9eb35..b1662fd5 100644 --- a/src/utils/GlobalStates.ts +++ b/src/utils/GlobalStates.ts @@ -25,7 +25,6 @@ type StoreState = { colormap: THREE.DataTexture; timeSeries: Record>; strides: number[]; - showLoading: boolean; metadata: Record | null; zMeta: object[]; dimArrays: number[][]; @@ -39,9 +38,9 @@ type StoreState = { variables: string[]; plotOn: boolean; isFlat: boolean; + status: string | null; progress: number; - downloading: boolean; - decompressing: boolean; + is4D: boolean; idx4D: number | null; titleDescription: { title: string | null; description: string | null }; @@ -56,7 +55,6 @@ type StoreState = { setTimeSeries: (timeSeries: Record>) => void; updateTimeSeries: (newEntries: Record>) => void; setStrides: (strides: number[]) => void; - setShowLoading: (showLoading: boolean) => void; setMetadata: (metadata: object | null) => void; setZMeta: (zMeta: object[]) => void; setDimArrays: (dimArrays: number[][]) => void; @@ -72,8 +70,7 @@ type StoreState = { setPlotOn: (plotOn: boolean) => void; setIsFlat: (isFlat: boolean) => void; setProgress: (progress: number) => void; - setDownloading: (downloading: boolean) => void; - setDecompressing: (decompressing: boolean) => void; + setStatus: (status: string | null) => void; setIs4D: (is4D: boolean) => void; setIdx4D: (idx4D: number | null) => void; setTitleDescription: (titleDescription: { title: string | null; description: string | null }) => void; @@ -88,7 +85,6 @@ export const useGlobalStore = create((set, get) => ({ colormap: GetColorMapTexture(), timeSeries: {}, strides: [10368,144,1], - showLoading: false, metadata: null, zMeta: [{}], dimArrays: [[0], [0], [0]], @@ -103,8 +99,7 @@ export const useGlobalStore = create((set, get) => ({ plotOn: false, isFlat:false, progress: 0, - downloading: false, - decompressing: false, + status: null, is4D: false, idx4D: null, titleDescription: {title:null, description: null}, @@ -127,7 +122,7 @@ export const useGlobalStore = create((set, get) => ({ set({ timeSeries: limitedTimeSeries }); }, setStrides: (strides) => set({ strides }), - setShowLoading: (showLoading) => set({ showLoading }), + setStatus: (status) => set({ status }), setMetadata: (metadata) => set({ metadata }), setZMeta: (zMeta) => set({ zMeta }), setDimArrays: (dimArrays) => set({ dimArrays }), @@ -153,8 +148,6 @@ export const useGlobalStore = create((set, get) => ({ setPlotOn: (plotOn) => set({ plotOn }), setIsFlat: (isFlat) => set({ isFlat }), setProgress: (progress) => set({ progress }), - setDownloading: (downloading) => set({ downloading }), - setDecompressing: (decompressing) => set({ decompressing }), setIs4D: (is4D) => set({ is4D }), setIdx4D: (idx4D) => set({ idx4D }), setTitleDescription: (titleDescription) => set({ titleDescription }), diff --git a/src/utils/HelperFuncs.ts b/src/utils/HelperFuncs.ts index 6c2daf1e..4ca27693 100644 --- a/src/utils/HelperFuncs.ts +++ b/src/utils/HelperFuncs.ts @@ -229,7 +229,7 @@ function DecompressArray(compressed : Uint8Array){ } export function GetCurrentArray(overrideStore?:string){ - const { variable, is4D, idx4D, initStore, strides, dataShape, setDecompressing }= useGlobalStore.getState() + const { variable, is4D, idx4D, initStore, strides, dataShape, setStatus }= useGlobalStore.getState() const { arraySize, currentChunks } = useZarrStore.getState() const {cache} = useCacheStore.getState(); const store = overrideStore ? overrideStore : initStore @@ -237,9 +237,9 @@ export function GetCurrentArray(overrideStore?:string){ if (cache.has(is4D ? `${store}_${idx4D}_${variable}` : `${store}_${variable}`)){ const chunk = cache.get(is4D ? `${store}_${idx4D}_${variable}` : `${store}_${variable}`) const compressed = chunk.compressed - setDecompressing(compressed) + setStatus(compressed ? "Decompressing data..." : null) const thisData = compressed ? DecompressArray(chunk.data) : chunk.data - setDecompressing(false) + setStatus(null) return thisData } else{ @@ -274,7 +274,7 @@ export function GetCurrentArray(overrideStore?:string){ } } } - setDecompressing(false) + setStatus(null) return typedArray } }