Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 61 additions & 63 deletions client/src/components/Dragger.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,20 @@ import { Button, Input, message, Modal, Space, Upload } from 'antd'
import { InboxOutlined } from '@ant-design/icons'
import { AppContext } from '../contexts/GlobalContext'
import { DEFAULT_IMAGE } from '../utils/utils'
import UTIF from 'utif';
import UTIF from 'utif'

const path = require('path')

export function Dragger () {
const context = useContext(AppContext)

const getBase64 = (file) =>
new Promise((resolve, reject) => {
const reader = new FileReader()
reader.readAsDataURL(file)
reader.onload = () => resolve(reader.result)
reader.onerror = (error) => reject(error)
})
// const getBase64 = (file) =>
// new Promise((resolve, reject) => {
// const reader = new FileReader()
// reader.readAsDataURL(file)
// reader.onload = () => resolve(reader.result)
// reader.onerror = (error) => reject(error)
// })

const onChange = (info) => {
const { status } = info.file
Expand Down Expand Up @@ -118,69 +118,68 @@ export function Dragger () {
const handleCancel = () => setPreviewOpen(false)
// Function to generate preview for TIFF files
const generateTiffPreview = (file, callback) => {
const reader = new FileReader();
reader.onload = function(event) {
const reader = new FileReader()
reader.onload = function (event) {
try {
const buffer = new Uint8Array(event.target.result);
console.log('Buffer length: ', buffer.length); //Log buffer length in bytes
const tiffPages = UTIF.decode(buffer);
//Check if tiffPages array is not empty
if (tiffPages.length === 0) throw new Error('No TIFF pages found');
const firstPage = tiffPages[0];
console.log('First page before decoding:', firstPage); // Log first page object before decoding
const buffer = new Uint8Array(event.target.result)
console.log('Buffer length: ', buffer.length) // Log buffer length in bytes

const tiffPages = UTIF.decode(buffer)

// Check if tiffPages array is not empty
if (tiffPages.length === 0) throw new Error('No TIFF pages found')

const firstPage = tiffPages[0]
console.log('First page before decoding:', firstPage) // Log first page object before decodin

// Ensure the firstPage has necessary tags before decoding
if (!firstPage.t256 || !firstPage.t257) throw new Error('First page is missing essential tags (width and height)');
UTIF.decodeImage(buffer, firstPage); // firstPage before and after decoding, the result is same.
console.log('TIFF first page after decoding: ', firstPage); //Log the first page object
if (!firstPage.t256 || !firstPage.t257) throw new Error('First page is missing essential tags (width and height)')

UTIF.decodeImage(buffer, firstPage) // firstPage before and after decoding, the result is same.
console.log('TIFF first page after decoding: ', firstPage) // Log the first page object

// Extract width and height from the TIFF tags
const width = firstPage.t256 ? firstPage.t256[0] : 0;
const height = firstPage.t257 ? firstPage.t257[0] : 0;
const width = firstPage.t256 ? firstPage.t256[0] : 0
const height = firstPage.t257 ? firstPage.t257[0] : 0

// Check if width and height are valid
if (width > 0 && height > 0) {
const rgba = UTIF.toRGBA8(firstPage); // Uint8Array with RGBA pixels
const rgba = UTIF.toRGBA8(firstPage) // Uint8Array with RGBA pixels

// Create a canvas to draw the TIFF image
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = width;
canvas.height = height;
const imageData = ctx.createImageData(width, height);
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
canvas.width = width
canvas.height = height
const imageData = ctx.createImageData(width, height)

imageData.data.set(rgba);
ctx.putImageData(imageData, 0, 0);
imageData.data.set(rgba)
ctx.putImageData(imageData, 0, 0)

const dataURL = canvas.toDataURL();
console.log('Canvas data URL:', dataURL);

callback(dataURL);
const dataURL = canvas.toDataURL()
console.log('Canvas data URL:', dataURL)

callback(dataURL)
} else {
console.error('TIFF image has invalid dimensions:', { width, height });
message.error('TIFF image has invalid dimensions.');
setPreviewImage(DEFAULT_IMAGE); // Fallback to default image
}
console.error('TIFF image has invalid dimensions:', { width, height })
message.error('TIFF image has invalid dimensions.')
setPreviewImage(DEFAULT_IMAGE) // Fallback to default image
}
} catch (error) {
console.error('Failed to generate TIFF preview:', error);
message.error('Failed to generate TIFF preview.');
setPreviewImage(DEFAULT_IMAGE); // Fallback to default image
}
};
reader.readAsArrayBuffer(file);
};
console.error('Failed to generate TIFF preview:', error)
message.error('Failed to generate TIFF preview.')
setPreviewImage(DEFAULT_IMAGE) // Fallback to default image
}
}
reader.readAsArrayBuffer(file)
}

//When click preview eye icon, implement handlePreview function
// When click preview eye icon, implement handlePreview function
const handlePreview = async (file) => {
setFileUID(file.uid);
setPreviewOpen(true);
setPreviewImage(file.thumbUrl);
setPreviewTitle(file.name || file.url.substring(file.url.lastIndexOf('/') + 1));
setFileUID(file.uid)
setPreviewOpen(true)
setPreviewImage(file.thumbUrl)
setPreviewTitle(file.name || file.url.substring(file.url.lastIndexOf('/') + 1))
if (
context.files.find(targetFile => targetFile.uid === file.uid) &&
context.files.find(targetFile => targetFile.uid === file.uid).folderPath) {
Expand All @@ -201,24 +200,24 @@ export function Dragger () {
// when click or drag file to this area to upload, below function will be deployed.
const handleBeforeUpload = (file) => {
// Create a URL for the thumbnail using object URL
if (file.type === "image/tiff" || file.type === "image/tif") {
if (file.type === 'image/tiff' || file.type === 'image/tif') {
return new Promise((resolve) => {
generateTiffPreview(file, (dataURL) => {
file.thumbUrl = dataURL;
console.log('file thumbUrl inside callback is', file.thumbUrl);
file.thumbUrl = dataURL
console.log('file thumbUrl inside callback is', file.thumbUrl)
resolve(file)
});
console.log('file thumbUrl is', file.thumbUrl);
})
console.log('file thumbUrl is', file.thumbUrl)
})
} else {
file.thumbUrl = URL.createObjectURL(file);
file.thumbUrl = URL.createObjectURL(file)
}
return true // Allow the upload
}

return (
<>
<Upload.Dragger
<Upload.Dragger
multiple
onChange={onChange}
customRequest={uploadImage}
Expand Down Expand Up @@ -276,4 +275,3 @@ export function Dragger () {
</>
)
}

4 changes: 2 additions & 2 deletions client/src/utils/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ function handleError (error) {
}
export async function makeApiRequest (url, method, data = null) {
try {
// const fullUrl = `${process.env.REACT_APP_API_PROTOCOL}://${process.env.REACT_APP_API_URL}/${url}`;
const res = await axios[method](url, data)
const fullUrl = `${process.env.REACT_APP_API_PROTOCOL}://${process.env.REACT_APP_API_URL}/${url}`
const res = await axios[method](fullUrl, data)
return res.data
} catch (error) {
handleError(error)
Expand Down
2 changes: 1 addition & 1 deletion client/src/views/DataLoader.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useContext, useEffect, useState } from 'react'
import {Dragger} from '../components/Dragger'
import { Dragger } from '../components/Dragger'
import { Button, Input, Select, Space, Typography } from 'antd'
import { ArrowRightOutlined } from '@ant-design/icons'
import { AppContext } from '../contexts/GlobalContext'
Expand Down
100 changes: 57 additions & 43 deletions client/src/views/Views.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const { Content, Sider } = Layout
function Views () {
const [current, setCurrent] = useState('visualization')
const [viewers, setViewers] = useState([])
const [isLoading, setIsLoading] = useState(false)
console.log(viewers)

const onClick = (e) => {
Expand Down Expand Up @@ -44,31 +45,38 @@ function Views () {
currentLabel,
scales
) => {
setIsLoading(true)
try {
const viewerId = currentImage.uid + currentLabel.uid + JSON.stringify(scales)
let updatedViewers = viewers
const exists = viewers.find(
(viewer) => viewer.key === currentImage.uid + currentLabel.uid
// (viewer) => viewer.key === currentImage.uid + currentLabel.uid
(viewer) => viewer.key === viewerId
)
console.log(exists, viewers)
if (!exists) {
const res = await getNeuroglancerViewer(
currentImage,
currentLabel,
scales
)
const newUrl = res.replace(/\/\/[^:/]+/, '//localhost')
console.log('Viewer at ', newUrl)

setViewers([
...viewers,
{
key: currentImage.uid + currentLabel.uid,
title: currentImage.name + ' & ' + currentLabel.name,
viewer: newUrl
}
])
// console.log(exists, viewers)
if (exists) {
updatedViewers = viewers.filter((viewer) => viewer.key !== viewerId)
}
const res = await getNeuroglancerViewer(
currentImage,
currentLabel,
scales
)
const newUrl = res.replace(/\/\/[^:/]+/, '//localhost')
console.log('Current Viewer at ', newUrl)

setViewers([
...updatedViewers,
{
key: viewerId,
title: currentImage.name + ' & ' + currentLabel.name,
viewer: newUrl
}
])
setIsLoading(false)
} catch (e) {
console.log(e)
setIsLoading(false)
}
}

Expand All @@ -79,30 +87,36 @@ function Views () {
minWidth: '90vw'
}}
>
<Sider
// collapsible
collapsed={collapsed}
onCollapse={(value) => setCollapsed(value)}
theme='light'
collapsedWidth='0'
>
<DataLoader fetchNeuroglancerViewer={fetchNeuroglancerViewer} />
</Sider>
<Layout className='site-layout'>
<Content
style={{
margin: '0 16px'
}}
>
<Menu
onClick={onClick}
selectedKeys={[current]}
mode='horizontal'
items={items}
/>
{renderMenu()}
</Content>
</Layout>
{isLoading ? (
<div>Loading the viewer ...</div>
) : (
<React.Fragment>
<Sider
// collapsible
collapsed={collapsed}
onCollapse={(value) => setCollapsed(value)}
theme='light'
collapsedWidth='0'
>
<DataLoader fetchNeuroglancerViewer={fetchNeuroglancerViewer} />
</Sider>
<Layout className='site-layout'>
<Content
style={{
margin: '0 16px'
}}
>
<Menu
onClick={onClick}
selectedKeys={[current]}
mode='horizontal'
items={items}
/>
{renderMenu()}
</Content>
</Layout>
</React.Fragment>
) }
</Layout>
)
}
Expand Down
29 changes: 27 additions & 2 deletions client/src/views/Visualization.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import {
ArrowRightOutlined,
DownOutlined,
EyeOutlined,
InboxOutlined
InboxOutlined,
ReloadOutlined
} from '@ant-design/icons'

function Visualization (props) {
Expand Down Expand Up @@ -42,6 +43,20 @@ function Visualization (props) {
setActiveKey(newActiveKey)
}

const refreshViewer = (key) => {
// This function refreshes the viewer specified by the key
const updatedViewers = viewers.map((viewer) => {
if (viewer.key === key) {
// Refresh the viewer URL by adding a refresh request token to it
// The refresh request token is only for node.js to force refresh the element
// The appended token will be ignored when rendering
return {...viewer, viewer: viewer.viewer + '?refresh=' + new Date().getTime() }
}
return viewer
})
setViewers(updatedViewers)
}

return (
<div style={{ marginTop: '20px' }}>
{viewers.length > 0
Expand All @@ -54,7 +69,17 @@ function Visualization (props) {
activeKey={activeKey}
onChange={handleChange}
items={viewers.map((viewer) => ({
label: viewer.title,
label: (
<span>
{viewer.title}
<Button
type='link'
icon={<ReloadOutlined />}
onClick={() => refreshViewer(viewer.key)}
/>

</span>
),
key: viewer.key,
children: (
<iframe
Expand Down