Skip to content

Commit 6aa74c6

Browse files
authored
Merge pull request #97 from LCOGT/feature/rgb-composite
Feature/rgb composite
2 parents f48e930 + c932996 commit 6aa74c6

File tree

8 files changed

+149
-68
lines changed

8 files changed

+149
-68
lines changed

src/components/DataSession/OperationWizard.vue

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { fetchApiCall, handleError } from '@/utils/api'
44
import { calculateColumnSpan } from '@/utils/common'
55
import ImageGrid from '../Global/ImageGrid'
66
import { useConfigurationStore } from '@/stores/configuration'
7+
import { useAlertsStore } from '@/stores/alerts'
78
89
const props = defineProps({
910
images: {
@@ -13,6 +14,7 @@ const props = defineProps({
1314
})
1415
1516
const store = useConfigurationStore()
17+
const alert = useAlertsStore()
1618
const emit = defineEmits(['closeWizard', 'addOperation'])
1719
const dataSessionsUrl = store.datalabApiBaseUrl
1820
@@ -91,6 +93,13 @@ const wizardTitle = computed(() => {
9193
9294
function goForward() {
9395
if (page.value == 'select') {
96+
// if there are no images for a filter required by the operation, do not proceed
97+
for (const inputKey in selectedOperationInputs.value) {
98+
const inputField = selectedOperationInputs.value[inputKey]
99+
if (inputField.type == 'file' && imagesWithFilter(inputField.filter).length == 0){
100+
return
101+
}
102+
}
94103
page.value = 'configure'
95104
}
96105
else {
@@ -101,23 +110,42 @@ function goForward() {
101110
}
102111
}
103112
for (const inputKey in selectedImages.value) {
104-
let selected = []
113+
let input = []
114+
const filter = selectedOperationInputs.value[inputKey].filter
105115
selectedImages.value[inputKey].forEach(index => {
106-
selected.push(props.images[index])
116+
input.push(imagesWithFilter(filter)[index])
107117
})
108-
operationDefinition.input_data[inputKey] = selected
118+
operationDefinition.input_data[inputKey] = input
109119
}
110120
emit('addOperation', operationDefinition)
111121
emit('closeWizard')
112122
}
113123
}
114124
125+
function imagesWithFilter(filters) {
126+
if(!filters) {
127+
return props.images
128+
}
129+
const imagesFiltered = props.images.filter((image) => filters.includes(image.filter))
130+
if (imagesFiltered.length == 0) {
131+
alert.setAlert('warning', 'Operation requires images with filter ' + filters.join(', '))
132+
}
133+
return imagesFiltered
134+
}
135+
115136
function selectImage(inputKey, imageIndex) {
116-
if (selectedImages.value[inputKey].includes(imageIndex)) {
117-
selectedImages.value[inputKey].splice(selectedImages.value[inputKey].indexOf(imageIndex), 1)
137+
const inputImages = selectedImages.value[inputKey]
138+
const maxImages = selectedOperationInputs.value[inputKey].maximum
139+
140+
if (inputImages.includes(imageIndex)) {
141+
inputImages.splice(inputImages.indexOf(imageIndex), 1)
118142
}
119-
else {
120-
selectedImages.value[inputKey].push(imageIndex)
143+
else if (!maxImages || inputImages.length < maxImages){
144+
inputImages.push(imageIndex)
145+
}
146+
else{
147+
alert.setAlert('warning', 'Maximum number of images selected')
148+
console.log('Maximum number of images selected in input', inputKey)
121149
}
122150
}
123151
@@ -192,7 +220,8 @@ function selectImage(inputKey, imageIndex) {
192220
{{ inputDescription.name }}
193221
</div>
194222
<image-grid
195-
:images="images"
223+
:images="imagesWithFilter(inputDescription.filter)"
224+
:selected-images="selectedImages[inputKey]"
196225
:column-span="calculateColumnSpan(images.length, imagesPerRow)"
197226
class="wizard-images"
198227
:allow-selection="true"

src/components/Global/AlertToast.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ watch(() => alertStore.alertText, () => {showAlert.value = true})
2323
position: absolute;
2424
bottom: 0;
2525
left: 0;
26-
z-index: 1000;
26+
/* to show above the v-overlay whose z-index is 2400 */
27+
z-index: 2401;
2728
padding: 10px;
2829
margin: 2rem;
2930
}

src/components/Global/FilterBadge.vue

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<script setup>
2+
import { computed } from 'vue'
3+
const props = defineProps({
4+
filter: {
5+
type: String,
6+
required: true
7+
}
8+
})
9+
10+
const badgeColor = computed(() => {
11+
switch (props.filter) {
12+
case 'R': case 'rp':
13+
return 'red'
14+
case 'V': case 'gp':
15+
return 'green'
16+
case 'B':
17+
return 'blue'
18+
default:
19+
return 'var(--metal)'
20+
}
21+
})
22+
</script>
23+
<template>
24+
<p
25+
class="filter-badge"
26+
:style="{ backgroundColor: badgeColor}"
27+
>
28+
{{ props.filter }}
29+
</p>
30+
</template>
31+
<style scoped>
32+
.filter-badge {
33+
border-radius: 5px;
34+
color: var(--tan);
35+
padding: 5px 10px;
36+
display: inline-block;
37+
}
38+
</style>

src/components/Global/ImageGrid.vue

Lines changed: 15 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
<script setup>
2-
import { defineProps, ref, defineEmits, watch } from 'vue'
2+
import { ref, watch } from 'vue'
33
import { useThumbnailsStore } from '@/stores/thumbnails'
44
import { useConfigurationStore } from '@/stores/configuration'
5+
import FilterBadge from './FilterBadge.vue'
56
67
const props = defineProps({
78
images: {
89
type: Array,
910
required: true
1011
},
12+
selectedImages: {
13+
type: Array,
14+
default: () => []
15+
},
1116
columnSpan: {
1217
type: Number,
1318
required: true
@@ -19,30 +24,16 @@ const props = defineProps({
1924
})
2025
2126
let imageDetails = ref({})
22-
let selectedImages = ref([])
2327
const configurationStore = useConfigurationStore()
2428
const thumbnailsStore = useThumbnailsStore()
2529
const emit = defineEmits(['selectImage'])
2630
2731
2832
const isSelected = (index) => {
29-
return selectedImages.value.includes(index)
30-
}
31-
32-
const onImageClick = (index) => {
33-
if (props.allowSelection) {
34-
if (selectedImages.value.includes(index)) {
35-
selectedImages.value.splice(selectedImages.value.indexOf(index), 1)
36-
}
37-
else {
38-
selectedImages.value.push(index)
39-
}
40-
emit('selectImage', index)
41-
}
33+
return props.selectedImages.includes(index)
4234
}
4335
4436
watch(() => props.images, () => {
45-
selectedImages.value = []
4637
props.images.forEach(image => {
4738
if (image.basename && !(image.basename in imageDetails.value)) {
4839
imageDetails.value[image.basename] = ref('')
@@ -62,6 +53,7 @@ watch(() => props.images, () => {
6253
v-for="(image, index) in props.images"
6354
:key="index"
6455
:cols="columnSpan"
56+
class="image-grid-col"
6557
>
6658
<v-img
6759
v-if="image.basename in imageDetails && imageDetails[image.basename]"
@@ -70,9 +62,12 @@ watch(() => props.images, () => {
7062
cover
7163
:class="{ 'selected-image': isSelected(index) }"
7264
aspect-ratio="1"
73-
class="image-grid"
74-
@click="onImageClick(index)"
65+
@click="emit('selectImage', index)"
7566
>
67+
<filter-badge
68+
v-if="image.filter"
69+
:filter="image.filter"
70+
/>
7671
<span
7772
v-if="'operationIndex' in image"
7873
class="image-text-overlay"
@@ -83,29 +78,17 @@ watch(() => props.images, () => {
8378
</template>
8479

8580
<style scoped>
86-
.image-grid-container {
87-
display: flex;
88-
max-width: 200px;
89-
max-height: 200px;
90-
}
9181
.selected-image {
9282
border: 0.3rem solid var(--dark-green);
9383
}
9484
9585
.image-text-overlay {
96-
color: white;
86+
color: var(--tan);
9787
font-weight: bold;
9888
margin-right: 5px;
9989
float: right;
10090
}
101-
.image-grid {
91+
.image-grid-col {
10292
max-width: 200px;
103-
height: auto;
104-
}
105-
@media (max-width: 900px) {
106-
.image-grid {
107-
width: 20vw;
108-
height: auto;
109-
}
11093
}
11194
</style>

src/components/Project/ImageAnalysis/ImageAnalyzer.vue

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { fetchApiCall } from '../../../utils/api'
44
import { useConfigurationStore } from '@/stores/configuration'
55
import ImageViewer from './ImageViewer.vue'
66
import LinePlot from './LinePlot.vue'
7+
import FilterBadge from '@/components/Global/FilterBadge.vue'
78
89
// eslint-disable-next-line no-unused-vars
910
const props = defineProps(['modelValue', 'image'])
@@ -69,9 +70,7 @@ function handleAnalysisOutput(response, action){
6970
:download="image.basename"
7071
target="_blank"
7172
>
72-
<v-btn
73-
icon="mdi-download"
74-
/>
73+
<v-icon icon="mdi-download" />
7574
</a>
7675
<v-btn
7776
icon="mdi-close"
@@ -92,6 +91,12 @@ function handleAnalysisOutput(response, action){
9291
<p>Site: {{ image.site_id }}</p>
9392
<p>Telescope: {{ image.telescope_id }}</p>
9493
<p>Instrument: {{ image.instrument_id }}</p>
94+
<span>Filter:
95+
<filter-badge
96+
v-if="image.FILTER"
97+
:filter="image.FILTER"
98+
/>
99+
</span>
95100
</v-sheet>
96101
<line-plot
97102
:y-axis-luminosity="lineProfile"
@@ -105,6 +110,9 @@ function handleAnalysisOutput(response, action){
105110
</v-dialog>
106111
</template>
107112
<style scoped>
113+
a{
114+
color: var(--tan);
115+
}
108116
.analysis-sheet{
109117
background-color: var(--dark-blue);
110118
font-family: 'Open Sans', sans-serif;

src/components/Project/ImageCarousel.vue

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { useThumbnailsStore } from '@/stores/thumbnails'
77
import { Carousel, Slide } from 'vue3-carousel'
88
import 'vue3-carousel/dist/carousel.css'
99
import ImageAnalyzer from './ImageAnalysis/ImageAnalyzer.vue'
10+
import FilterBadge from '@/components/Global/FilterBadge.vue'
1011
1112
const props = defineProps({
1213
data: {
@@ -109,22 +110,34 @@ watch(() => props.data, (newVal) => {
109110
</Slide>
110111
</Carousel>
111112
<Carousel
112-
class="mt-4"
113113
id="thumbnails"
114+
ref="carousel"
115+
v-model="currentSlide"
116+
class="mt-4"
114117
:wrap-around="false"
115118
:breakpoints="breakpoints"
116-
v-model="currentSlide"
117-
ref="carousel">
118-
<Slide v-for="(item, index) in data" :key="index">
119-
<div class="carousel__item" @click="handleThumbnailClick(item, index)">
119+
>
120+
<Slide
121+
v-for="(item, index) in data"
122+
:key="index"
123+
>
124+
<div
125+
class="carousel__item"
126+
@click="handleThumbnailClick(item, index)"
127+
>
120128
<v-img
121129
:src="item.smallCachedUrl"
122130
loading="lazy"
123131
cover
124132
class="thumbnail__item"
125133
:class="{'selected-thumbnail': store.isSelected(item)}"
126134
:alt="item.OBJECT"
127-
/>
135+
>
136+
<filter-badge
137+
v-if="item.FILTER"
138+
:filter="item.FILTER"
139+
/>
140+
</v-img>
128141
</div>
129142
</Slide>
130143
</Carousel>

src/components/Project/ImageList.vue

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
<script setup>
33
import { ref, onMounted, defineProps } from 'vue'
44
import { useSettingsStore } from '@/stores/settings'
5+
import FilterBadge from '@/components/Global/FilterBadge.vue'
56
67
const store = useSettingsStore()
78
// eslint-disable-next-line no-unused-vars
@@ -11,7 +12,7 @@ const props = defineProps(['data'])
1112
let headers = ref([
1213
{ title: 'IMAGE NAME', align: 'start', sortable: true, key: 'basename' },
1314
{ title: 'TIME', align: 'start', sortable: true, key: 'observation_date' },
14-
{ title: 'OBJECT', align: 'start', sortable: true, key: 'OBJECT' },
15+
{ title: 'FILTER', align: 'start', sortable: true, key: 'FILTER' },
1516
{ title: 'IMAGE', align: 'start', sortable: false, key: 'url' },
1617
])
1718
let itemsPerPage = ref(15)
@@ -38,11 +39,16 @@ onMounted ( () => {
3839
item-value="basename"
3940
:return-object="true"
4041
show-select
41-
density="compact"
4242
:hover="true"
4343
class="data_table"
4444
@update:model-value="select($event)"
4545
>
46+
<template #[`item.FILTER`]="{ item }">
47+
<filter-badge
48+
v-if="item.FILTER"
49+
:filter="item.FILTER"
50+
/>
51+
</template>
4652
<template #[`item.url`]="{ item }">
4753
<v-img
4854
:src="item.smallCachedUrl"
@@ -64,6 +70,8 @@ onMounted ( () => {
6470
color: var(--tan);
6571
font-size: 1.4rem;
6672
background-color: var(--metal);
73+
overflow-y: scroll;
74+
max-height: 80vh;
6775
}
6876
.list_image{
6977
height: 8vh;

0 commit comments

Comments
 (0)