@abi-software/flatmapvuer 1.4.1 → 1.4.3-image.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -81,6 +81,9 @@
81
81
  style="height: 100%"
82
82
  :flatmapAPI="flatmapAPI"
83
83
  :sparcAPI="sparcAPI"
84
+ @imageThumbnailDisplay="onImageThumbnailDisplay"
85
+ :imageThumbnailSidebar="imageThumbnailSidebar"
86
+ @image-thumbnail-open="onImageThumbnailOpen"
84
87
  />
85
88
  </div>
86
89
  </template>
@@ -264,6 +267,12 @@ export default {
264
267
  onConnectivityInfoOpen: function (entryData) {
265
268
  this.$emit('connectivity-info-open', entryData);
266
269
  },
270
+ onImageThumbnailDisplay: function (payload) {
271
+ this.$emit('imageThumbnailDisplay', payload);
272
+ },
273
+ onImageThumbnailOpen: function (payload) {
274
+ this.$emit('image-thumbnail-open', payload);
275
+ },
267
276
  onSelectionsDataChanged: function (data) {
268
277
  this.$emit('pathway-selection-changed', data);
269
278
  },
@@ -700,7 +709,7 @@ export default {
700
709
  */
701
710
  sparcAPI: {
702
711
  type: String,
703
- default: 'https://api.sparc.science/',
712
+ default: '',
704
713
  },
705
714
  /**
706
715
  * Flag to disable UIs on Map
@@ -716,6 +725,13 @@ export default {
716
725
  type: Boolean,
717
726
  default: false,
718
727
  },
728
+ /**
729
+ * The option to show image thumbnail in sidebar
730
+ */
731
+ imageThumbnailSidebar: {
732
+ type: Boolean,
733
+ default: false,
734
+ },
719
735
  },
720
736
  data: function () {
721
737
  return {
package/src/main.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { createApp } from 'vue'
2
2
  import { createPinia } from 'pinia'
3
3
  import App from './App.vue'
4
- import { useMainStore } from '@/store/index'
4
+ import { useMainStore } from '@/stores/index'
5
5
 
6
6
  const pinia = createPinia()
7
7
  const app = createApp(App)
@@ -0,0 +1,116 @@
1
+ export default {
2
+ methods: {
3
+ populateMapWithImages: function (mapImp, images, type) {
4
+ this.downloadImageKeys = []; // to count downloaded images
5
+ for (const [key, list] of Object.entries(images)) {
6
+ // Exclude empty list
7
+ if (list.length) {
8
+ this.downloadImageThumbnail(mapImp, key, list, type);
9
+ }
10
+ }
11
+ },
12
+ downloadImageThumbnail: function (mapImp, key, list, type) {
13
+ // This inner list count will be updated by failed download
14
+ const count = list.length;
15
+
16
+ if (!this.downloadImageKeys.includes(key)) {
17
+ this.downloadImageKeys.push(key);
18
+ }
19
+
20
+ if (count > 0) {
21
+ //Pick a random image
22
+ const index = Math.floor(Math.random() * count);
23
+ const thumbnail = list[index].thumbnail;
24
+ this.getThumbnail(thumbnail, type)
25
+ .then((wrapperElement) => {
26
+ this.downloadImageKeys = this.downloadImageKeys.filter((item) => item !== key);
27
+ this.addImageThumbnailMarker(mapImp, key, wrapperElement);
28
+
29
+ // All images are downloaded
30
+ if (!this.downloadImageKeys.length) {
31
+ this.imagesDownloading = false;
32
+ }
33
+ })
34
+ .catch(() => {
35
+ //Failed to download, pick another one
36
+ list.splice(index);
37
+ this.downloadImageThumbnail(mapImp, key, list, type);
38
+ });
39
+ } else {
40
+ if (this.downloadImageKeys.includes(key)) {
41
+ this.downloadImageKeys = this.downloadImageKeys.filter((item) => item !== key);
42
+
43
+ // Failed to download, the last item
44
+ if (!this.downloadImageKeys.length) {
45
+ this.imagesDownloading = false;
46
+ }
47
+ }
48
+ }
49
+ },
50
+ getThumbnail: async function (url, type) {
51
+ return new Promise((resolve, reject) => {
52
+ if (type === "Image" || type === "Segmentation") {
53
+ this.getBinaryThumbnail(url)
54
+ .then((response) => resolve(response))
55
+ .catch((response) => reject(response));
56
+ } else {
57
+ this.getGenericThumbnail(url)
58
+ .then((response) => resolve(response))
59
+ .catch((response) => reject(response));
60
+ }
61
+ });
62
+ },
63
+ getBinaryThumbnail: async function (url) {
64
+ return new Promise((resolve, reject) => {
65
+ fetch(url)
66
+ .then((response) => {
67
+ if (response.status >= 200 && response.status < 300) {
68
+ return response.text();
69
+ } else {
70
+ reject();
71
+ }
72
+ })
73
+ .then((data) => {
74
+ if (data) {
75
+ let img = new Image();
76
+ let wrapperElement = document.createElement("div");
77
+ img.style = "height: auto;width: 50px;margin-right: 80px;";
78
+ img.onload = function () {
79
+ wrapperElement.appendChild(img);
80
+ resolve(wrapperElement);
81
+ };
82
+ img.onerror = function () {
83
+ reject(new Error("Failed to load image at " + url));
84
+ };
85
+ img.src = `data:'image/png';base64,${data}`;
86
+ } else {
87
+ reject(new Error("Failed to load image at " + url));
88
+ }
89
+ });
90
+ });
91
+ },
92
+ getGenericThumbnail: async function (url) {
93
+ return new Promise((resolve, reject) => {
94
+ let img = new Image();
95
+ let wrapperElement = document.createElement("div");
96
+ img.style = "height: auto;width: 50px;margin-right: 80px;";
97
+ img.onload = function () {
98
+ wrapperElement.appendChild(img);
99
+ resolve(wrapperElement);
100
+ };
101
+ img.onerror = function () {
102
+ reject(new Error("Failed to load image at " + url));
103
+ };
104
+ img.src = url;
105
+ });
106
+ },
107
+ addImageThumbnailMarker: function (mapImp, id, wrapperElement) {
108
+ const markerIdentifier = mapImp.addMarker(id, {
109
+ element: wrapperElement,
110
+ className: "highlight-marker",
111
+ cluster: false,
112
+ type: "image",
113
+ });
114
+ },
115
+ },
116
+ };
@@ -256,7 +256,11 @@ let FlatmapQueries = function () {
256
256
  }
257
257
  })
258
258
  .catch((error) => {
259
- console.error('Error:', error)
259
+ if (error.name === 'AbortError') {
260
+ // This error is from AbortController's abort method.
261
+ } else {
262
+ console.error('Error:', error)
263
+ }
260
264
  resolve(false)
261
265
  })
262
266
  })
@@ -0,0 +1,280 @@
1
+ const getOrganCuries = async (sparcApi, filetypes = [], species = []) => {
2
+ let params = new URLSearchParams();
3
+ filetypes.forEach((type) => {
4
+ params.append("filetypes", type);
5
+ });
6
+ species.forEach((name) => {
7
+ params.append("species", name);
8
+ });
9
+
10
+ const response = await fetch(`${sparcApi}/get-organ-curies/?${params}`);
11
+ const data = await response.json();
12
+
13
+ let organCuries = [];
14
+ data.uberon.array.forEach((pair) => {
15
+ const organCurie = {
16
+ id: pair.id.toUpperCase(),
17
+ name: pair.name,
18
+ };
19
+ organCuries.push(organCurie);
20
+ });
21
+
22
+ return organCuries;
23
+ };
24
+
25
+ const getFilesInfoForCuries = async (sparcApi, organCuries, filetypes) => {
26
+ const response = await fetch(`${sparcApi}/get-files-info-for-curies`, {
27
+ method: "POST",
28
+ body: JSON.stringify({
29
+ curies: organCuries.map((item) => item.id),
30
+ filetypes: filetypes,
31
+ }),
32
+ headers: {
33
+ "Content-Type": "application/json",
34
+ },
35
+ });
36
+ const data = await response.json();
37
+
38
+ return data;
39
+ };
40
+
41
+ const getFileNameFromPath = (filePath) => {
42
+ return filePath.substring(filePath.lastIndexOf("/") + 1);
43
+ };
44
+
45
+ const getBiolucidaThumbnailURL = (sparcApi, biolucidaId) => {
46
+ return `${sparcApi}/thumbnail/${biolucidaId}`;
47
+ };
48
+
49
+ const getBiolucidaThumbnails = async (sparcApi, organCuries, type) => {
50
+ try {
51
+ const data = await getFilesInfoForCuries(sparcApi, organCuries, [
52
+ "biolucida-2d",
53
+ "biolucida-3d",
54
+ ]);
55
+
56
+ if (data["files_info"]) {
57
+ let images = {};
58
+ for (const [key, value] of Object.entries(data["files_info"])) {
59
+ if (value.length > 0) {
60
+ let list = [];
61
+ value.forEach((entry) => {
62
+ const thumbnailURL = getBiolucidaThumbnailURL(
63
+ sparcApi,
64
+ entry.biolucida_id
65
+ );
66
+ if (entry.biolucida_id) {
67
+ let image = {
68
+ thumbnail: thumbnailURL,
69
+ resource: entry.file_path,
70
+ id: entry.id,
71
+ title: getFileNameFromPath(entry.file_path),
72
+ type: type,
73
+ link: thumbnailURL,
74
+ mimetype: entry.mimetype,
75
+ species: entry.species,
76
+ version: entry.version,
77
+ };
78
+ list.push(image);
79
+ }
80
+ });
81
+ images[key] = list;
82
+ }
83
+ }
84
+ return images;
85
+ }
86
+ } catch (error) {
87
+ console.error("Error:", error);
88
+ }
89
+ return {};
90
+ };
91
+
92
+ const getSegmentationThumbnailURL = (
93
+ sparcApi,
94
+ datasetId,
95
+ datasetVersion,
96
+ filePath
97
+ ) => {
98
+ return `${sparcApi}/thumbnail/neurolucida?datasetId=${datasetId}&version=${datasetVersion}&path=files/${filePath}`;
99
+ };
100
+
101
+ const getSegmentationThumbnails = async (sparcApi, organCuries, type) => {
102
+ try {
103
+ const data = await getFilesInfoForCuries(sparcApi, organCuries, [
104
+ "mbf-segmentation",
105
+ ]);
106
+
107
+ if (data["files_info"]) {
108
+ let images = {};
109
+ for (const [key, value] of Object.entries(data["files_info"])) {
110
+ if (value.length > 0) {
111
+ let list = [];
112
+ value.forEach((entry) => {
113
+ const thumbnailURL = getSegmentationThumbnailURL(
114
+ sparcApi,
115
+ entry.id,
116
+ entry.version,
117
+ entry.file_path
118
+ );
119
+ let image = {
120
+ thumbnail: thumbnailURL,
121
+ resource: entry.file_path,
122
+ id: entry.id,
123
+ title: getFileNameFromPath(entry.file_path),
124
+ type: type,
125
+ link: thumbnailURL,
126
+ mimetype: entry.mimetype,
127
+ species: entry.species,
128
+ version: entry.version,
129
+ };
130
+ list.push(image);
131
+ });
132
+ images[key] = list;
133
+ }
134
+ }
135
+ return images;
136
+ }
137
+ } catch (error) {
138
+ console.error("Error:", error);
139
+ }
140
+ return {};
141
+ };
142
+
143
+ const findEntryWithPathInArray = (entry, list, type) => {
144
+ if (entry && list) {
145
+ for (let i = 0; i < entry.isSourceOf.length; i++) {
146
+ for (let l = 0; l < list.length; l++) {
147
+ const item = list[l];
148
+ if (
149
+ entry.id === item.id &&
150
+ (!type || item.type === type) &&
151
+ entry.isSourceOf[i] === item.file_path
152
+ ) {
153
+ return item;
154
+ }
155
+ }
156
+ }
157
+ }
158
+ return undefined;
159
+ };
160
+
161
+ const getScaffoldThumbnailURL = (sparcApi, entry, list) => {
162
+ const viewEntry = findEntryWithPathInArray(
163
+ entry,
164
+ list,
165
+ "abi-scaffold-view-file"
166
+ );
167
+ const thumbnailEntry = findEntryWithPathInArray(
168
+ viewEntry,
169
+ list,
170
+ "abi-thumbnail"
171
+ );
172
+ if (thumbnailEntry) {
173
+ return `${sparcApi}/s3-resource/${thumbnailEntry.id}/files/${thumbnailEntry.file_path}`;
174
+ }
175
+ return undefined;
176
+ };
177
+
178
+ const getScaffoldThumbnails = async (sparcApi, organCuries, type) => {
179
+ try {
180
+ const data = await getFilesInfoForCuries(sparcApi, organCuries, [
181
+ "abi-thumbnail",
182
+ "abi-scaffold-metadata-file",
183
+ "abi-scaffold-view-file",
184
+ ]);
185
+
186
+ if (data["files_info"]) {
187
+ let images = {};
188
+ for (const [key, value] of Object.entries(data["files_info"])) {
189
+ if (value.length > 0) {
190
+ let list = [];
191
+ value.forEach((entry) => {
192
+ if (entry.type === "abi-scaffold-metadata-file") {
193
+ const thumbnailURL = getScaffoldThumbnailURL(
194
+ sparcApi,
195
+ entry,
196
+ value
197
+ );
198
+ if (thumbnailURL) {
199
+ let image = {
200
+ thumbnail: thumbnailURL,
201
+ resource: entry.file_path,
202
+ id: entry.id,
203
+ title: getFileNameFromPath(entry.file_path),
204
+ type: type,
205
+ link: thumbnailURL,
206
+ species: entry.species,
207
+ version: entry.version,
208
+ };
209
+ list.push(image);
210
+ }
211
+ }
212
+ });
213
+ images[key] = list;
214
+ }
215
+ }
216
+ return images;
217
+ }
218
+ } catch (error) {
219
+ console.error("Error:", error);
220
+ }
221
+ return {};
222
+ };
223
+
224
+ const getPlotThumbnailURL = (sparcApi, entry) => {
225
+ //None of the thumbnail for plot is properly annotated.
226
+ //We will use the first in is source of for testing.
227
+ if (entry.isSourceOf.length > 0) {
228
+ return `${sparcApi}/s3-resource/${entry.id}/files/${entry.isSourceOf[0]}`;
229
+ }
230
+ return undefined;
231
+ };
232
+
233
+ const getPlotThumbnails = async (sparcApi, organCuries, type) => {
234
+ try {
235
+ const data = await getFilesInfoForCuries(sparcApi, organCuries, [
236
+ "abi-plot",
237
+ "abi-thumbnail",
238
+ ]);
239
+
240
+ if (data["files_info"]) {
241
+ let images = {};
242
+ for (const [key, value] of Object.entries(data["files_info"])) {
243
+ if (value.length > 0) {
244
+ let list = [];
245
+ value.forEach((entry) => {
246
+ if (entry.type === "abi-plot") {
247
+ const thumbnailURL = getPlotThumbnailURL(sparcApi, entry);
248
+ if (thumbnailURL) {
249
+ let image = {
250
+ thumbnail: thumbnailURL,
251
+ resource: entry.file_path,
252
+ id: entry.id,
253
+ title: getFileNameFromPath(entry.file_path),
254
+ type: type,
255
+ link: thumbnailURL,
256
+ species: entry.species,
257
+ version: entry.version,
258
+ };
259
+ list.push(image);
260
+ }
261
+ }
262
+ });
263
+ images[key] = list;
264
+ }
265
+ }
266
+ return images;
267
+ }
268
+ } catch (error) {
269
+ console.error("Error:", error);
270
+ }
271
+ return {};
272
+ };
273
+
274
+ export {
275
+ getOrganCuries,
276
+ getBiolucidaThumbnails,
277
+ getSegmentationThumbnails,
278
+ getScaffoldThumbnails,
279
+ getPlotThumbnails,
280
+ };
@@ -0,0 +1,46 @@
1
+ import { defineStore } from "pinia";
2
+
3
+ /* eslint-disable no-alert, no-console */
4
+
5
+ export const useSettingsStore = defineStore("settings", {
6
+ state: () => {
7
+ return {
8
+ organCuries: [],
9
+ imageTypes: [],
10
+ imageThumbnails: {},
11
+ };
12
+ },
13
+ getters: {
14
+ imageTypeCached: (state) => (imageType) => {
15
+ return state.imageTypes.includes(imageType);
16
+ },
17
+ getImageThumbnails:
18
+ (state) =>
19
+ (imageType, uberonIds = undefined) => {
20
+ if (uberonIds) {
21
+ let thumbnails = {};
22
+ Object.entries(state.imageThumbnails).forEach(([key, value]) => {
23
+ if (uberonIds.includes(key)) {
24
+ thumbnails[key] = value.filter((item) => item.type === imageType);
25
+ }
26
+ });
27
+ return thumbnails;
28
+ }
29
+ return state.imageThumbnails;
30
+ },
31
+ },
32
+ actions: {
33
+ updateOrganCuries(organCuries) {
34
+ this.organCuries = organCuries;
35
+ },
36
+ updateImageThumbnails(imageType, imageThumbnails) {
37
+ this.imageTypes.push(imageType);
38
+ Object.keys(imageThumbnails).forEach((key) => {
39
+ if (!(key in this.imageThumbnails)) {
40
+ this.imageThumbnails[key] = [];
41
+ }
42
+ this.imageThumbnails[key].push(...imageThumbnails[key]);
43
+ });
44
+ },
45
+ },
46
+ });
File without changes