diff --git a/Dockerfile b/Dockerfile index faaf1f14..f86a1a0e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,6 +16,8 @@ RUN echo -e " server {\n" \ " root /app;\n" \ " index index.html;\n" \ " try_files \$uri \$uri/ /index.html;\n" \ + " add_header 'Cross-Origin-Embedder-Policy' 'require-corp';\n" \ + " add_header 'Cross-Origin-Opener-Policy' 'same-origin';\n" \ " }\n" \ " }\n" \ > /etc/nginx/conf.d/datalab.conf diff --git a/package-lock.json b/package-lock.json index 34f02b80..a9a0e9c6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,7 @@ "vue": "^3.5.9", "vue-router": "^4.2.5", "vue3-carousel": "^0.3.1", - "vuetify": "^3.6.0", + "vuetify": "^3.7.4", "webfontloader": "^1.0.0" }, "devDependencies": { @@ -12726,9 +12726,9 @@ } }, "node_modules/vuetify": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-3.6.0.tgz", - "integrity": "sha512-ic7VrB+nOTo8F7APhcKPjtDEO3yBCK5CJ2LIQ/4oAC/aaAKtuGuNMBUiUVitDKQjr0tcnDgy9Ar1CrHU5d28IA==", + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-3.7.4.tgz", + "integrity": "sha512-Y8UU5wUDQXC3oz2uumPb8IOdvB4XMCxtxnmqdOc+LihNuPlkSgxIwf92ndRzbOtJFKHsggFUxpyLqpQp+A+5kg==", "engines": { "node": "^12.20 || >=14.13" }, @@ -12740,7 +12740,6 @@ "typescript": ">=4.7", "vite-plugin-vuetify": ">=1.0.0", "vue": "^3.3.0", - "vue-i18n": "^9.0.0", "webpack-plugin-vuetify": ">=2.0.0" }, "peerDependenciesMeta": { @@ -12750,9 +12749,6 @@ "vite-plugin-vuetify": { "optional": true }, - "vue-i18n": { - "optional": true - }, "webpack-plugin-vuetify": { "optional": true } diff --git a/package.json b/package.json index 393e5634..f5a0c48f 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "vue": "^3.5.9", "vue-router": "^4.2.5", "vue3-carousel": "^0.3.1", - "vuetify": "^3.6.0", + "vuetify": "^3.7.4", "webfontloader": "^1.0.0" }, "devDependencies": { diff --git a/src/assets/MockData.JSON b/src/assets/MockData.JSON new file mode 100644 index 00000000..e0a37b05 --- /dev/null +++ b/src/assets/MockData.JSON @@ -0,0 +1,195 @@ +[ + { + "basefile_name": "aro1-sq002ms-20231210-00000001", + "time": "2023-12-11 00:15", + "object": "M33", + "image": "images/image1.png" + }, + { + "basefile_name": "mrc1-ec002cs-20231210-00000002", + "time": "2023-12-11 00:30", + "object": "NGC 224", + "image": "images/image2.png" + }, + { + "basefile_name": "mrc2-sq003cm-20231210-00000003", + "time": "2023-12-11 00:45", + "object": "NGC 253", + "image": "images/image3.png" + }, + { + "basefile_name": "sro2-sq002ms-20231210-00000004", + "time": "2023-12-11 01:00", + "object": "M81", + "image": "images/image4.png" + }, + { + "basefile_name": "eco1-ec002cs-20231210-00000005", + "time": "2023-12-11 01:15", + "object": "M82", + "image": "images/image5.png" + }, + { + "basefile_name": "eco2-sq003cm-20231210-00000006", + "time": "2023-12-11 01:30", + "object": "M87", + "image": "images/image6.png" + }, + { + "basefile_name": "aro1-sq002ms-20231210-00000007", + "time": "2023-12-11 01:45", + "object": "NGC 5128", + "image": "images/image7.png" + }, + { + "basefile_name": "mrc1-ec002cs-20231210-00000008", + "time": "2023-12-11 02:00", + "object": "NGC 4736", + "image": "images/image8.png" + }, + { + "basefile_name": "mrc2-sq003cm-20231210-00000009", + "time": "2023-12-11 02:15", + "object": "NGC 4826", + "image": "images/image9.png" + }, + { + "basefile_name": "sro2-sq002ms-20231210-00000010", + "time": "2023-12-11 02:30", + "object": "NGC 5055", + "image": "images/image10.png" + }, + { + "basefile_name": "eco1-ec002cs-20231210-00000011", + "time": "2023-12-11 02:45", + "object": "NGC 6946", + "image": "images/image11.png" + }, + { + "basefile_name": "eco2-sq003cm-20231210-00000012", + "time": "2023-12-11 03:00", + "object": "NGC 5194", + "image": "images/image12.png" + }, + { + "basefile_name": "aro1-sq002ms-20231210-00000013", + "time": "2023-12-11 03:15", + "object": "NGC 5236", + "image": "images/image13.png" + }, + { + "basefile_name": "mrc1-ec002cs-20231210-00000014", + "time": "2023-12-11 03:30", + "object": "NGC 5457", + "image": "images/image14.png" + }, + { + "basefile_name": "mrc2-sq003cm-20231210-00000015", + "time": "2023-12-11 03:45", + "object": "NGC 3031", + "image": "images/image15.png" + }, + { + "basefile_name": "eco1-ec002cs-20231210-00000016", + "time": "2023-12-11 04:00", + "object": "NGC 5195", + "image": "images/image16.png" + }, + { + "basefile_name": "eco2-sq003cm-20231210-00000017", + "time": "2023-12-11 04:15", + "object": "IC 342", + "image": "images/image17.png" + }, + { + "basefile_name": "aro1-sq002ms-20231210-00000018", + "time": "2023-12-11 04:30", + "object": "NGC 2403", + "image": "images/image18.png" + }, + { + "basefile_name": "mrc1-ec002cs-20231210-00000019", + "time": "2023-12-11 04:45", + "object": "NGC 2903", + "image": "images/image19.png" + }, + { + "basefile_name": "mrc2-sq003cm-20231210-00000020", + "time": "2023-12-11 05:00", + "object": "NGC 3184", + "image": "images/image20.png" + }, + { + "basefile_name": "sro2-sq002ms-20231210-00000021", + "time": "2023-12-11 05:15", + "object": "NGC 3344", + "image": "images/image21.png" + }, + { + "basefile_name": "eco1-ec002cs-20231210-00000022", + "time": "2023-12-11 05:30", + "object": "NGC 3621", + "image": "images/image22.png" + }, + { + "basefile_name": "eco2-sq003cm-20231210-00000023", + "time": "2023-12-11 05:45", + "object": "NGC 3627", + "image": "images/image23.png" + }, + { + "basefile_name": "aro1-sq002ms-20231210-00000024", + "time": "2023-12-11 06:00", + "object": "NGC 4244", + "image": "images/image24.png" + }, + { + "basefile_name": "mrc1-ec002cs-20231210-00000025", + "time": "2023-12-11 06:15", + "object": "NGC 4258", + "image": "images/image25.png" + }, + { + "basefile_name": "mrc2-sq003cm-20231210-00000026", + "time": "2023-12-11 06:30", + "object": "NGC 4559", + "image": "images/image26.png" + }, + { + "basefile_name": "sro2-sq002ms-20231210-00000027", + "time": "2023-12-11 06:45", + "object": "NGC 4565", + "image": "images/image27.png" + }, + { + "basefile_name": "eco1-ec002cs-20231210-00000028", + "time": "2023-12-11 07:00", + "object": "NGC 4631", + "image": "images/image28.png" + }, + { + "basefile_name": "eco2-sq003cm-20231210-00000029", + "time": "2023-12-11 07:15", + "object": "NGC 4725", + "image": "images/image29.png" + }, + { + "basefile_name": "aro1-sq002ms-20231210-00000030", + "time": "2023-12-11 07:30", + "object": "NGC 4736", + "image": "images/image30.png" + }, + { + "basefile_name": "mrc1-ec002cs-20231210-00000031", + "time": "2023-12-11 07:45", + "object": "NGC 4826", + "image": "images/image31.png" + }, + { + "basefile_name": "mrc2-sq003cm-20231210-00000032", + "time": "2023-12-11 08:00", + "object": "NGC 5055", + "image": "images/image32.png" + } +] + diff --git a/src/assets/images/image1.png b/src/assets/images/image1.png new file mode 100644 index 00000000..7c1cb866 Binary files /dev/null and b/src/assets/images/image1.png differ diff --git a/src/assets/images/image10.png b/src/assets/images/image10.png new file mode 100644 index 00000000..b3c9e1fc Binary files /dev/null and b/src/assets/images/image10.png differ diff --git a/src/assets/images/image11.png b/src/assets/images/image11.png new file mode 100644 index 00000000..a0b76eb3 Binary files /dev/null and b/src/assets/images/image11.png differ diff --git a/src/assets/images/image12.png b/src/assets/images/image12.png new file mode 100644 index 00000000..c6df68aa Binary files /dev/null and b/src/assets/images/image12.png differ diff --git a/src/assets/images/image13.png b/src/assets/images/image13.png new file mode 100644 index 00000000..da1dbef2 Binary files /dev/null and b/src/assets/images/image13.png differ diff --git a/src/assets/images/image14.png b/src/assets/images/image14.png new file mode 100644 index 00000000..828c0fa6 Binary files /dev/null and b/src/assets/images/image14.png differ diff --git a/src/assets/images/image15.png b/src/assets/images/image15.png new file mode 100644 index 00000000..ddae93c3 Binary files /dev/null and b/src/assets/images/image15.png differ diff --git a/src/assets/images/image16.png b/src/assets/images/image16.png new file mode 100644 index 00000000..b7f32c93 Binary files /dev/null and b/src/assets/images/image16.png differ diff --git a/src/assets/images/image17.png b/src/assets/images/image17.png new file mode 100644 index 00000000..d44e2664 Binary files /dev/null and b/src/assets/images/image17.png differ diff --git a/src/assets/images/image18.png b/src/assets/images/image18.png new file mode 100644 index 00000000..74d133e8 Binary files /dev/null and b/src/assets/images/image18.png differ diff --git a/src/assets/images/image19.png b/src/assets/images/image19.png new file mode 100644 index 00000000..27d6ec0a Binary files /dev/null and b/src/assets/images/image19.png differ diff --git a/src/assets/images/image2.png b/src/assets/images/image2.png new file mode 100644 index 00000000..7bc7c9fc Binary files /dev/null and b/src/assets/images/image2.png differ diff --git a/src/assets/images/image20.png b/src/assets/images/image20.png new file mode 100644 index 00000000..108afc17 Binary files /dev/null and b/src/assets/images/image20.png differ diff --git a/src/assets/images/image21.png b/src/assets/images/image21.png new file mode 100644 index 00000000..534ddbf5 Binary files /dev/null and b/src/assets/images/image21.png differ diff --git a/src/assets/images/image22.png b/src/assets/images/image22.png new file mode 100644 index 00000000..a813e7ce Binary files /dev/null and b/src/assets/images/image22.png differ diff --git a/src/assets/images/image23.png b/src/assets/images/image23.png new file mode 100644 index 00000000..8206687e Binary files /dev/null and b/src/assets/images/image23.png differ diff --git a/src/assets/images/image24.png b/src/assets/images/image24.png new file mode 100644 index 00000000..84e9ebd2 Binary files /dev/null and b/src/assets/images/image24.png differ diff --git a/src/assets/images/image25.png b/src/assets/images/image25.png new file mode 100644 index 00000000..6881c473 Binary files /dev/null and b/src/assets/images/image25.png differ diff --git a/src/assets/images/image26.png b/src/assets/images/image26.png new file mode 100644 index 00000000..8e9e146f Binary files /dev/null and b/src/assets/images/image26.png differ diff --git a/src/assets/images/image27.png b/src/assets/images/image27.png new file mode 100644 index 00000000..0fa50bed Binary files /dev/null and b/src/assets/images/image27.png differ diff --git a/src/assets/images/image28.png b/src/assets/images/image28.png new file mode 100644 index 00000000..659b83b1 Binary files /dev/null and b/src/assets/images/image28.png differ diff --git a/src/assets/images/image29.png b/src/assets/images/image29.png new file mode 100644 index 00000000..796d9329 Binary files /dev/null and b/src/assets/images/image29.png differ diff --git a/src/assets/images/image3.png b/src/assets/images/image3.png new file mode 100644 index 00000000..f8422e53 Binary files /dev/null and b/src/assets/images/image3.png differ diff --git a/src/assets/images/image30.png b/src/assets/images/image30.png new file mode 100644 index 00000000..f0a63008 Binary files /dev/null and b/src/assets/images/image30.png differ diff --git a/src/assets/images/image31.png b/src/assets/images/image31.png new file mode 100644 index 00000000..84545e31 Binary files /dev/null and b/src/assets/images/image31.png differ diff --git a/src/assets/images/image32.png b/src/assets/images/image32.png new file mode 100644 index 00000000..cffdc0b0 Binary files /dev/null and b/src/assets/images/image32.png differ diff --git a/src/assets/images/image4.png b/src/assets/images/image4.png new file mode 100644 index 00000000..a0419e69 Binary files /dev/null and b/src/assets/images/image4.png differ diff --git a/src/assets/images/image5.png b/src/assets/images/image5.png new file mode 100644 index 00000000..8ee95c30 Binary files /dev/null and b/src/assets/images/image5.png differ diff --git a/src/assets/images/image6.png b/src/assets/images/image6.png new file mode 100644 index 00000000..d548f68b Binary files /dev/null and b/src/assets/images/image6.png differ diff --git a/src/assets/images/image7.png b/src/assets/images/image7.png new file mode 100644 index 00000000..f4e5a764 Binary files /dev/null and b/src/assets/images/image7.png differ diff --git a/src/assets/images/image8.png b/src/assets/images/image8.png new file mode 100644 index 00000000..8f97619b Binary files /dev/null and b/src/assets/images/image8.png differ diff --git a/src/assets/images/image9.png b/src/assets/images/image9.png new file mode 100644 index 00000000..2bb84677 Binary files /dev/null and b/src/assets/images/image9.png differ diff --git a/src/components/DataSession/OperationWizard.vue b/src/components/DataSession/OperationWizard.vue index 505a7f52..2af91d0c 100644 --- a/src/components/DataSession/OperationWizard.vue +++ b/src/components/DataSession/OperationWizard.vue @@ -3,7 +3,7 @@ import { ref, onMounted, computed, defineEmits, defineProps} from 'vue' import { fetchApiCall, handleError } from '@/utils/api' import { calculateColumnSpan } from '@/utils/common' import ImageGrid from '../Global/ImageGrid' -import ImageScalingGroup from '../Global/ImageScalingGroup' +import ImageScalingGroup from '../Global/Scaling/ImageScalingGroup' import { useConfigurationStore } from '@/stores/configuration' import { useAlertsStore } from '@/stores/alerts' diff --git a/src/components/Global/CompositeImage.vue b/src/components/Global/Scaling/CompositeImage.vue similarity index 100% rename from src/components/Global/CompositeImage.vue rename to src/components/Global/Scaling/CompositeImage.vue diff --git a/src/components/Global/Scaling/HistogramSlider.vue b/src/components/Global/Scaling/HistogramSlider.vue new file mode 100644 index 00000000..41d23238 --- /dev/null +++ b/src/components/Global/Scaling/HistogramSlider.vue @@ -0,0 +1,229 @@ + + + diff --git a/src/components/Global/ImageScaler.vue b/src/components/Global/Scaling/ImageScaler.vue similarity index 58% rename from src/components/Global/ImageScaler.vue rename to src/components/Global/Scaling/ImageScaler.vue index c4b04bd2..75f51444 100644 --- a/src/components/Global/ImageScaler.vue +++ b/src/components/Global/Scaling/ImageScaler.vue @@ -3,6 +3,7 @@ import { ref, computed, onMounted, defineEmits, defineProps } from 'vue' import { useConfigurationStore } from '@/stores/configuration' import { fetchApiCall } from '@/utils/api' import RawScaledImage from './RawScaledImage.vue' +import HistogramSlider from './HistogramSlider.vue' // This component gets the raw image data from the server for an image // and then displays the image and the scaling controls under it @@ -33,7 +34,7 @@ const isLoading = ref(true) const errorReason = ref('') const rawData = ref({}) const sliderRange = ref([0, 65535]) -var zScaleValues = null +const zScaleValues = ref([0, 65535]) const titleText = computed(() => { return props.imageName.replace('_', ' ') @@ -49,24 +50,29 @@ const maxPixelValue = computed(() => { } }) -function updateLowerScale(value) { - sliderRange.value = [Number(value), sliderRange.value[1]] - emit('updateScaling', props.imageName, sliderRange.value[0], sliderRange.value[1]) -} - -function updateUpperScale(value) { - sliderRange.value = [sliderRange.value[0], Number(value)] - emit('updateScaling', props.imageName, sliderRange.value[0], sliderRange.value[1]) -} +const histogram = computed(() => { + if (rawData.value && rawData.value.histogram) { + return rawData.value.histogram + } + else { + return [0, 10, 10, 5, 4, 3, 2, 1] + } +}) -function zScaleImage() { - if (zScaleValues){ - sliderRange.value = [...zScaleValues] +const bins = computed(() => { + if (rawData.value && rawData.value.bins) { + return rawData.value.bins + } + else { + return [0, 1, 2, 3, 4, 5, 6, 7] } +}) + +function updateScaleRange(lowerValue, upperValue) { + sliderRange.value = [Number(lowerValue), Number(upperValue)] emit('updateScaling', props.imageName, sliderRange.value[0], sliderRange.value[1]) } - onMounted(async () => { const url = dataSessionsUrl + 'analysis/raw-data/' const body = { @@ -77,8 +83,7 @@ onMounted(async () => { fetchApiCall({url: url, method: 'POST', body: body, successCallback: (response) => { rawData.value = response - zScaleValues = [response.zmin, response.zmax] - zScaleImage() + zScaleValues.value = [response.zmin, response.zmax] isLoading.value = false }, failCallback: (error) => { @@ -106,46 +111,17 @@ onMounted(async () => { - - - - + + + + diff --git a/src/components/Global/ImageScalingGroup.vue b/src/components/Global/Scaling/ImageScalingGroup.vue similarity index 98% rename from src/components/Global/ImageScalingGroup.vue rename to src/components/Global/Scaling/ImageScalingGroup.vue index 80b3370c..21c848f4 100644 --- a/src/components/Global/ImageScalingGroup.vue +++ b/src/components/Global/Scaling/ImageScalingGroup.vue @@ -69,7 +69,6 @@ watch( :key="groupName" :value="groupName" > - - diff --git a/src/components/Global/RawScaledImage.vue b/src/components/Global/Scaling/RawScaledImage.vue similarity index 100% rename from src/components/Global/RawScaledImage.vue rename to src/components/Global/Scaling/RawScaledImage.vue diff --git a/src/utils/imageLoader.js b/src/utils/imageLoader.js new file mode 100644 index 00000000..b0e60631 --- /dev/null +++ b/src/utils/imageLoader.js @@ -0,0 +1,83 @@ +import { fetchApiCall } from '@/utils/api' +import { useConfigurationStore } from '@/stores/configuration' + +const cacheOptions = { 'ignoreVary': true, 'ignoreMethod': true, 'ignoreSearch': true } + + +async function getAllCacheKeys(size) { + const cacheName = size + 'Thumbnails' + return caches.open(cacheName).then((cache) => { + return cache.keys().then((keys) => { + return keys + }) + }) +} + +async function getImageFromBasename(size, archive, url, imageBasename) { + // Clean up the -large or -small on the basename from ptr archive images + imageBasename = imageBasename.replace('-small', '').replace('-large', '') + const cacheName = size + 'Thumbnails' + return caches.open(cacheName).then((cache) => { + return cache.match(imageBasename, cacheOptions).then(function (response) { + // If not found in cache, then fetch image thumbnail from url or from archive and cache the result and return it + return response || cacheImageFromBasename(size, archive, url, imageBasename) + }) + }) +} + +async function deleteCachedImageFromBasename(size, imageBasename) { + const cacheName = size + 'Thumbnails' + return caches.open(cacheName).then((cache) => { + return cache.delete(imageBasename, cacheOptions) + }) +} + +async function cacheImageFromUrl(size, url, imageBasename) { + const imageResponse = await fetch(url, { method: 'GET', mode: 'cors' }) + const cacheName = size + 'Thumbnails' + caches.open(cacheName).then((cache) => { + cache.put(imageBasename, imageResponse.clone()) + }) + return imageResponse.clone() +} + +async function cacheImageFromBasename(size, archive, url, imageBasename) { + // Right now we only have a ptr archive, but planning to maybe use this to differentiate later + // If url exists, it will use that to directly download and cache image. If not it will use + // the archive to decide how to fetch the image based on its basename and archive. + if (url) { + return cacheImageFromUrl(size, url, imageBasename) + } + else { + const store = useConfigurationStore() + if (archive == 'ptr') { + const thumbnailBasename = imageBasename + '-' + size + const frameUrl = store.datalabArchiveApiUrl + 'frames/?basename_exact=' + thumbnailBasename + const responseData = await fetchApiCall({ url: frameUrl, method: 'GET', mode: 'cors' }) + if (responseData && responseData.results) { + url = responseData.results[0].url + } + } + else if (archive == 'lco') { + // Use the lco thumbnail service to get the image thumbnail given its basename + var width = 200 + if (size == 'large') { + width = 1000 + } + // Thumbnail service needs to set width and height or sometimes it creates a smaller thumbnail then requested + const thumbnailServiceUrl = store.thumbnailServiceUrl + imageBasename + '/?width=' + width + '&height=' + width + const responseData = await fetchApiCall({ url: thumbnailServiceUrl, method: 'GET'}) + if (responseData && responseData.url) { + url = responseData.url + } + } + if (!url) { + console.error('Failed to get frame data for image ' + imageBasename) + } + else { + return cacheImageFromUrl(size, url, imageBasename) + } + } +} + +export { getImageFromBasename, deleteCachedImageFromBasename, getAllCacheKeys }