Skip to content

Commit

Permalink
Implement CSV export for Matrix vis
Browse files Browse the repository at this point in the history
  • Loading branch information
axelboc committed Jul 6, 2021
1 parent 17df791 commit a4d9dde
Show file tree
Hide file tree
Showing 9 changed files with 101 additions and 3 deletions.
32 changes: 32 additions & 0 deletions src/h5web/toolbar/MatrixToolbar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { FiDownload } from 'react-icons/fi';
import { useMatrixConfig } from '../vis-packs/core/matrix/config';
import { sliceToCsv } from '../vis-packs/core/matrix/utils';
import DownloadBtn from './controls/DownloadBtn';
import Toolbar from './Toolbar';

function MatrixToolbar() {
const currentSlice = useMatrixConfig((state) => state.currentSlice);
if (currentSlice && currentSlice.shape.length > 2) {
throw new Error('Expected current slice to have at most two dimensions');
}

return (
<Toolbar>
{currentSlice && (
<DownloadBtn
icon={FiDownload}
label="CSV"
filename="export.csv"
getDownloadUrl={() => {
const data = sliceToCsv(currentSlice);
return URL.createObjectURL(
new Blob([data], { type: 'text/csv;charset=utf-8' })
);
}}
/>
)}
</Toolbar>
);
}

export default MatrixToolbar;
2 changes: 1 addition & 1 deletion src/h5web/toolbar/Toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import OverflowMenu from './OverflowMenu';
import MeasuredControl from './MeasuredControl';

interface Props {
children?: (ReactElement | undefined)[];
children?: ReactElement | (ReactElement | undefined)[];
}

function Toolbar(props: Props) {
Expand Down
1 change: 0 additions & 1 deletion src/h5web/toolbar/controls/DownloadBtn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ function DownloadBtn(props: Props) {
return;
}

// Let link open download URL in new tab/window
evt.currentTarget.setAttribute('href', url);
}}
>
Expand Down
1 change: 1 addition & 0 deletions src/h5web/toolbar/toolbars.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { default as MatrixToolbar } from './MatrixToolbar';
export { default as LineToolbar } from './LineToolbar';
export { default as HeatmapToolbar } from './HeatmapToolbar';
export { default as ComplexToolbar } from './ComplexToolbar';
Expand Down
1 change: 1 addition & 0 deletions src/h5web/vis-packs/core/configs.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { MatrixConfigProvider } from './matrix/config';
export { LineConfigProvider } from './line/config';
export { HeatmapConfigProvider } from './heatmap/config';
export { ComplexConfigProvider } from './complex/config';
Expand Down
9 changes: 8 additions & 1 deletion src/h5web/vis-packs/core/matrix/MappedMatrixVis.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import type { DimensionMapping } from '../../../dimension-mapper/models';
import type { Primitive } from '../../../providers/models';
import { useMappedArray, useSlicedDimsAndMapping } from '../hooks';
import type { PrintableType } from '../models';
import { useMatrixConfig } from './config';
import { useEffect } from 'react';

interface Props {
value: Primitive<PrintableType>[];
Expand All @@ -16,9 +18,14 @@ function MappedMatrixVis(props: Props) {
const { value, dims, dimMapping, formatter, cellWidth } = props;

const [slicedDims, slicedMapping] = useSlicedDimsAndMapping(dims, dimMapping);

const [mappedArray] = useMappedArray(value, slicedDims, slicedMapping);

const setCurrentSlice = useMatrixConfig((state) => state.setCurrentSlice);

useEffect(() => {
setCurrentSlice(mappedArray);
}, [mappedArray, setCurrentSlice]);

return (
<MatrixVis
dataArray={mappedArray}
Expand Down
26 changes: 26 additions & 0 deletions src/h5web/vis-packs/core/matrix/config.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { NdArray } from 'ndarray';
import create from 'zustand';
import createContext from 'zustand/context';
import type { Primitive } from '../../../providers/models';
import type { ConfigProviderProps } from '../../models';
import type { PrintableType } from '../models';

interface MatrixConfig {
currentSlice: NdArray<Primitive<PrintableType>> | undefined;
setCurrentSlice: (slice: NdArray<Primitive<PrintableType>>) => void;
}

function createStore() {
return create<MatrixConfig>((set) => ({
currentSlice: undefined,
setCurrentSlice: (slice) => set({ currentSlice: slice }),
}));
}

const { Provider, useStore } = createContext<MatrixConfig>();
export const useMatrixConfig = useStore;

export function MatrixConfigProvider(props: ConfigProviderProps) {
const { children } = props;
return <Provider createStore={createStore}>{children}</Provider>;
}
28 changes: 28 additions & 0 deletions src/h5web/vis-packs/core/matrix/utils.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import type { NdArray } from 'ndarray';
import { hasComplexType, hasNumericType } from '../../../guards';
import { renderComplex } from '../../../metadata-viewer/utils';
import type {
ArrayShape,
Dataset,
H5WebComplex,
Primitive,
} from '../../../providers/models';
import type { PrintableType, ValueFormatter } from '../models';
import { formatNumber } from '../utils';
Expand All @@ -21,3 +23,29 @@ export function getFormatter(

return (val) => (val as string).toString();
}

export function sliceToCsv(slice: NdArray<Primitive<PrintableType>>): string {
let csv = '';

if (slice.shape.length === 1) {
for (let i = 0; i < slice.shape[0]; i++) {
csv += `${slice.get(i).toString()}\n`; // complex numbers are stringifyied as two values
}

return csv;
}

if (slice.shape.length === 2) {
for (let i = 0; i < slice.shape[0]; i++) {
let line = '';
for (let j = 0; j < slice.shape[1]; j++) {
line += `${slice.get(i, j).toString()},`; // complex numbers are stringifyied as two values
}
csv += `${line.replace(/,$/u, '')}\n`;
}

return csv;
}

throw new Error('Expected at most 2 dimensions');
}
4 changes: 4 additions & 0 deletions src/h5web/vis-packs/core/visualizations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,15 @@ import {
RgbVisContainer,
} from './containers';
import {
MatrixToolbar,
LineToolbar,
HeatmapToolbar,
ComplexToolbar,
ComplexLineToolbar,
RgbToolbar,
} from '../../toolbar/toolbars';
import {
MatrixConfigProvider,
LineConfigProvider,
HeatmapConfigProvider,
ComplexConfigProvider,
Expand Down Expand Up @@ -78,7 +80,9 @@ export const CORE_VIS: Record<Vis, CoreVisDef> = {
[Vis.Matrix]: {
name: Vis.Matrix,
Icon: FiGrid,
Toolbar: MatrixToolbar,
Container: MatrixVisContainer,
ConfigProvider: MatrixConfigProvider,
supportsDataset: (dataset) => {
return hasPrintableType(dataset) && hasArrayShape(dataset);
},
Expand Down

0 comments on commit a4d9dde

Please sign in to comment.