Skip to content

Commit 2b63804

Browse files
authored
descriptions for large image imports (#1700)
1 parent 3cdd8f3 commit 2b63804

6 files changed

Lines changed: 121 additions & 36 deletions

File tree

client/dive-common/components/ImportButton.vue

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ export default defineComponent({
3535
type: Boolean,
3636
default: false,
3737
},
38+
tooltip: {
39+
type: String,
40+
default: '',
41+
},
3842
},
3943
setup() {
4044
return {
@@ -44,15 +48,48 @@ export default defineComponent({
4448
</script>
4549

4650
<template>
47-
<div>
51+
<div class="import-button-root">
4852
<v-menu
4953
offset-y
5054
offset-x
5155
nudge-left="180"
5256
max-width="180"
5357
>
5458
<template #activator="{ on }">
59+
<v-tooltip
60+
v-if="tooltip"
61+
bottom
62+
max-width="360"
63+
open-delay="50"
64+
>
65+
<template #activator="{ on: tooltipOn, attrs }">
66+
<v-btn
67+
v-bind="{ ...buttonAttrs, ...attrs }"
68+
:large="!small"
69+
:small="small"
70+
class="px-0 import-button"
71+
v-on="tooltipOn"
72+
@click="$emit('open', openType)"
73+
>
74+
<div class="col-11">
75+
{{ name }}
76+
<v-icon class="ml-2">
77+
{{ icon }}
78+
</v-icon>
79+
</div>
80+
<v-icon
81+
v-if="multiCamImport"
82+
class="button-dropdown col-1"
83+
v-on="on"
84+
>
85+
mdi-chevron-down
86+
</v-icon>
87+
</v-btn>
88+
</template>
89+
<span>{{ tooltip }}</span>
90+
</v-tooltip>
5591
<v-btn
92+
v-else
5693
v-bind="buttonAttrs"
5794
:large="!small"
5895
:small="small"
@@ -141,6 +178,14 @@ export default defineComponent({
141178
</template>
142179

143180
<style scoped lang="scss">
181+
.import-button-root {
182+
width: 100%;
183+
}
184+
185+
.import-button {
186+
width: 100%;
187+
}
188+
144189
.button-dropdown {
145190
height: 44px;
146191
border-left: 1px solid white;

client/dive-common/constants.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,12 +82,52 @@ const largeImageTypes = [
8282
'image/ntf',
8383
];
8484

85+
/** Extension-only formats for large-image uploads (aligned with server validLargeImageFormats). */
86+
const largeImageFileExtensions = [
87+
'nitf',
88+
'tif',
89+
'tiff',
90+
'ntf',
91+
'vrt',
92+
'r0',
93+
'r1',
94+
'r2',
95+
'r3',
96+
'r4',
97+
'r5',
98+
'r6',
99+
];
100+
101+
/** Desktop Electron open-dialog extensions (GeoTIFF/TIFF only; tiles served via geotiff.js). */
85102
const largeImageDesktopTypes = [
86103
'geotiff',
87104
'tiff',
88105
'tif',
89106
];
90107

108+
/** MIME types and dotted extensions for HTML file input accept on web. */
109+
const largeImageWebAccept = [
110+
...largeImageTypes,
111+
...largeImageFileExtensions.map((ext) => `.${ext}`),
112+
].join(',');
113+
114+
/** Dotted extensions for desktop-mode file inputs in the web upload UI. */
115+
const largeImageDesktopAccept = largeImageDesktopTypes.map((ext) => `.${ext}`).join(',');
116+
117+
function getLargeImageFileAccept(): string {
118+
if (typeof navigator !== 'undefined' && navigator.userAgent.includes('Electron')) {
119+
return largeImageDesktopAccept;
120+
}
121+
return largeImageWebAccept;
122+
}
123+
124+
function getLargeImageAllowedExtensions(): string[] {
125+
if (typeof navigator !== 'undefined' && navigator.userAgent.includes('Electron')) {
126+
return largeImageDesktopTypes;
127+
}
128+
return largeImageFileExtensions;
129+
}
130+
91131
const websafeImageTypes = [
92132
// 'image/apng',
93133
// 'image/bmp',
@@ -162,7 +202,12 @@ export {
162202
websafeVideoTypes,
163203
inputAnnotationTypes,
164204
largeImageTypes,
205+
largeImageFileExtensions,
165206
largeImageDesktopTypes,
207+
largeImageWebAccept,
208+
largeImageDesktopAccept,
209+
getLargeImageFileAccept,
210+
getLargeImageAllowedExtensions,
166211
inputAnnotationFileTypes,
167212
listFileTypes,
168213
zipFileTypes,

client/platform/desktop/frontend/api.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import type {
99
import {
1010
fileVideoTypes, calibrationFileTypes,
1111
inputAnnotationFileTypes, listFileTypes,
12+
largeImageDesktopTypes,
1213
} from 'dive-common/constants';
1314
import {
1415
DesktopMetadata, NvidiaSmiReply,
@@ -40,8 +41,6 @@ function joinPath(dir: string, filename: string) {
4041
* Native functions that run entirely in the renderer
4142
*/
4243

43-
const largeImageFileExtensions = ['tif', 'tiff', 'geotiff'];
44-
4544
async function openFromDisk(datasetType: DatasetType | 'bulk' | 'calibration' | 'annotation' | 'text', directory = false) {
4645
let filters: FileFilter[] = [];
4746
const allFiles = { name: 'All Files', extensions: ['*'] };
@@ -53,7 +52,7 @@ async function openFromDisk(datasetType: DatasetType | 'bulk' | 'calibration' |
5352
}
5453
if (datasetType === 'large-image') {
5554
filters = [
56-
{ name: 'GeoTIFF / TIFF', extensions: largeImageFileExtensions },
55+
{ name: 'GeoTIFF / TIFF', extensions: largeImageDesktopTypes },
5756
];
5857
}
5958
if (datasetType === 'calibration') {
@@ -88,7 +87,7 @@ async function openFromDisk(datasetType: DatasetType | 'bulk' | 'calibration' |
8887
}
8988
}
9089
if (datasetType === 'large-image') {
91-
const allowed = new Set(largeImageFileExtensions.map((ext) => ext.toLowerCase()));
90+
const allowed = new Set(largeImageDesktopTypes.map((ext) => ext.toLowerCase()));
9291
if (!results.filePaths.every(
9392
(item) => allowed.has(getExtension(item)),
9493
)) {

client/platform/desktop/frontend/components/Recent.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -427,10 +427,11 @@ export default defineComponent({
427427
@multi-cam="openMultiCamDialog"
428428
/>
429429
<ImportButton
430-
name="Open Large Image (TIFF)"
430+
name="Open Tiled GeoTIFF / TIFF"
431431
icon="mdi-map"
432432
open-type="large-image"
433433
class="my-3"
434+
tooltip="Open a high-resolution geospatial image for tiled viewing. Supported formats: .tif, .tiff, .geotiff. Files should include internal pyramid overviews (COG recommended) for best performance."
434435
@open="open($event)"
435436
/>
436437
</v-col>

client/platform/web-girder/utils.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { UploadManager, Location } from '@girder/components/src';
22
import {
33
calibrationFileTypes, inputAnnotationFileTypes, inputAnnotationTypes,
4+
getLargeImageAllowedExtensions, getLargeImageFileAccept,
45
otherImageTypes, otherVideoTypes, websafeImageTypes, websafeVideoTypes, zipFileTypes,
56
} from 'dive-common/constants';
67
import { DatasetType } from 'dive-common/apispec';
@@ -56,6 +57,8 @@ async function openFromDisk(
5657
input.accept = '';
5758
} else if (datasetType === 'video') {
5859
input.accept = baseTypes.concat(websafeVideoTypes).concat(otherVideoTypes).join(',');
60+
} else if (datasetType === 'large-image') {
61+
input.accept = getLargeImageFileAccept();
5962
} else if (datasetType === 'calibration') {
6063
input.accept = calibrationFileTypes.map((item) => `.${item}`).join(',');
6164
} else if (datasetType === 'annotation') {
@@ -78,6 +81,14 @@ async function openFromDisk(
7881
if (!fileList.every((item) => inputAnnotationTypes.includes(item.type))) {
7982
reject(new Error('File Types did not match JSON or CSV'));
8083
}
84+
} else if (datasetType === 'large-image') {
85+
const allowed = new Set(getLargeImageAllowedExtensions().map((ext) => ext.toLowerCase()));
86+
if (!fileList.every((item) => {
87+
const ext = item.name.split('.').pop()?.toLowerCase();
88+
return ext && allowed.has(ext);
89+
})) {
90+
reject(new Error('File types did not match tiled image formats'));
91+
}
8192
}
8293
const filePaths = fileList.map(
8394
(item) => item.webkitRelativePath || item.name,

client/platform/web-girder/views/Upload.vue

Lines changed: 14 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { useRouter } from 'vue-router/composables';
77
import {
88
ImageSequenceType, VideoType, DefaultVideoFPS, FPSOptions,
99
inputAnnotationFileTypes, websafeVideoTypes, otherVideoTypes,
10-
websafeImageTypes, otherImageTypes, JsonMetaRegEx, largeImageTypes, largeImageDesktopTypes, LargeImageType,
10+
websafeImageTypes, otherImageTypes, JsonMetaRegEx, getLargeImageFileAccept, LargeImageType,
1111
} from 'dive-common/constants';
1212
1313
import {
@@ -164,7 +164,6 @@ export default defineComponent({
164164
};
165165
166166
onBeforeUnmount(clearMulticamUploadProgressTimer);
167-
const isDesktopMode = navigator.userAgent.includes('Electron');
168167
const { prompt } = usePrompt();
169168
const router = useRouter();
170169
@@ -329,10 +328,7 @@ export default defineComponent({
329328
} if (type === 'video') {
330329
return websafeVideoTypes.concat(otherVideoTypes);
331330
} if (type === 'large-image') {
332-
if (isDesktopMode) {
333-
return largeImageDesktopTypes.map((item) => `.${item}`).join(',');
334-
}
335-
return largeImageTypes;
331+
return getLargeImageFileAccept();
336332
}
337333
return websafeImageTypes.concat(otherImageTypes);
338334
};
@@ -867,30 +863,18 @@ export default defineComponent({
867863
@multi-cam="openMultiCamDialog"
868864
/>
869865
</v-list-item>
870-
<v-tooltip
871-
open-delay="50"
872-
top
873-
max-width="400"
874-
>
875-
<template #activator="{ on }">
876-
<v-list-item v-on="on">
877-
<import-button
878-
:name="`Add ${pendingUploads.length ? 'Another ' : ''}Tiled Images`"
879-
icon="mdi-folder-open"
880-
open-type="large-image"
881-
class="grow my-2"
882-
:small="!!pendingUploads.length"
883-
:button-attrs="buttonAttrs"
884-
@open="openImport($event)"
885-
/>
886-
</v-list-item>
887-
</template>
888-
<b>
889-
Allows for a single or sequence of geospatial
890-
large images for use in a tile server
891-
with formats such as: .tiff, .nitf, .ntf, .tif
892-
</b>
893-
</v-tooltip>
866+
<v-list-item>
867+
<import-button
868+
:name="`Add ${pendingUploads.length ? 'Another ' : ''}Tiled TIFF / NITF`"
869+
icon="mdi-folder-open"
870+
open-type="large-image"
871+
class="grow my-2"
872+
:small="!!pendingUploads.length"
873+
:button-attrs="buttonAttrs"
874+
tooltip="Upload tiled geospatial images for the large-image viewer. Supports TIFF (.tif, .tiff), NITF (.nitf, .ntf), and other tiled raster data with internal pyramid overviews."
875+
@open="openImport($event)"
876+
/>
877+
</v-list-item>
894878
<v-list-item>
895879
<import-button
896880
:name="`Add ${pendingUploads.length ? 'Another ' : ''}Zip File`"

0 commit comments

Comments
 (0)