@abi-software/flatmapvuer 1.17.1-demo.0 → 1.18.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.
- package/README.md +28 -6
- package/cypress.config.js +19 -19
- package/dist/flatmapvuer.js +2684 -3322
- package/dist/flatmapvuer.umd.cjs +10 -18
- package/dist/index.html +9 -6
- package/dist/style.css +1 -1
- package/eslint.config.js +43 -9
- package/package.json +9 -3
- package/public/index.html +9 -6
- package/reporter-config.json +1 -1
- package/src/App.vue +57 -89
- package/src/assets/_variables.scss +12 -10
- package/src/components/EventBus.js +3 -3
- package/src/components/FlatmapError.vue +38 -38
- package/src/components/FlatmapVuer.vue +891 -946
- package/src/components/MultiFlatmapVuer.vue +257 -158
- package/src/components/SelectionsGroup.vue +66 -82
- package/src/components/index.js +2 -2
- package/src/components/legends/DynamicLegends.vue +5 -10
- package/src/components/legends/LegendItem.vue +32 -35
- package/src/components/legends/SvgLegends.vue +1 -1
- package/src/components/utilities.js +3 -7
- package/src/components.d.ts +1 -0
- package/src/icons/flatmap-marker.js +1 -1
- package/src/icons/mapicon-species-style.css +10 -8
- package/src/icons/yellowstar.js +1 -1
- package/src/main.js +12 -14
- package/src/services/flatmapKnowledge.js +7 -11
- package/src/services/flatmapQueries.js +282 -279
- package/src/store/index.js +7 -7
- package/vite.config.js +33 -34
- package/vite.static-build.js +4 -4
- package/vuese-generator.js +47 -56
- package/src/services/somaLocationMarkers.js +0 -125
|
@@ -1,21 +1,18 @@
|
|
|
1
|
-
/* eslint-disable no-alert, no-console */
|
|
2
1
|
import { querySingleConnectivityList } from '@abi-software/map-utilities';
|
|
3
2
|
|
|
4
3
|
// remove duplicates by stringifying the objects
|
|
5
4
|
const removeDuplicates = function (arrayOfAnything) {
|
|
6
|
-
if (!arrayOfAnything) return []
|
|
7
|
-
return [...new Set(arrayOfAnything.map((e) => JSON.stringify(e)))].map((e) =>
|
|
8
|
-
|
|
9
|
-
)
|
|
10
|
-
}
|
|
5
|
+
if (!arrayOfAnything) return [];
|
|
6
|
+
return [...new Set(arrayOfAnything.map((e) => JSON.stringify(e)))].map((e) => JSON.parse(e));
|
|
7
|
+
};
|
|
11
8
|
|
|
12
9
|
// remove duplicates for terms with id (UBERON or ILX) and name properties
|
|
13
10
|
const removeDuplicateTerms = function (arrayOfTerms) {
|
|
14
|
-
if (!arrayOfTerms) return []
|
|
11
|
+
if (!arrayOfTerms) return [];
|
|
15
12
|
const seen = new Set();
|
|
16
13
|
const filteredTerms = [];
|
|
17
14
|
arrayOfTerms.forEach((term) => {
|
|
18
|
-
const {id, name} = term;
|
|
15
|
+
const { id, name } = term;
|
|
19
16
|
const identifier = `${id.toLowerCase()}:${name.toLowerCase()}`;
|
|
20
17
|
if (!seen.has(identifier)) {
|
|
21
18
|
seen.add(identifier);
|
|
@@ -23,14 +20,14 @@ const removeDuplicateTerms = function (arrayOfTerms) {
|
|
|
23
20
|
}
|
|
24
21
|
});
|
|
25
22
|
return filteredTerms;
|
|
26
|
-
}
|
|
23
|
+
};
|
|
27
24
|
|
|
28
|
-
const cachedLabels = {}
|
|
25
|
+
const cachedLabels = {};
|
|
29
26
|
const cachedTaxonLabels = [];
|
|
30
27
|
|
|
31
28
|
const findTaxonomyLabel = async function (flatmapAPI, taxonomy) {
|
|
32
|
-
if (cachedLabels &&
|
|
33
|
-
return cachedLabels[taxonomy]
|
|
29
|
+
if (cachedLabels && Object.hasOwn(cachedLabels, taxonomy)) {
|
|
30
|
+
return cachedLabels[taxonomy];
|
|
34
31
|
}
|
|
35
32
|
|
|
36
33
|
return new Promise((resolve) => {
|
|
@@ -39,32 +36,32 @@ const findTaxonomyLabel = async function (flatmapAPI, taxonomy) {
|
|
|
39
36
|
})
|
|
40
37
|
.then((response) => response.json())
|
|
41
38
|
.then((data) => {
|
|
42
|
-
let label = data.label
|
|
39
|
+
let label = data.label;
|
|
43
40
|
if (label === 'Mammalia') {
|
|
44
|
-
label = 'Mammalia not otherwise specified'
|
|
41
|
+
label = 'Mammalia not otherwise specified';
|
|
45
42
|
}
|
|
46
|
-
cachedLabels[taxonomy] = label
|
|
47
|
-
resolve(label)
|
|
43
|
+
cachedLabels[taxonomy] = label;
|
|
44
|
+
resolve(label);
|
|
48
45
|
})
|
|
49
46
|
.catch((error) => {
|
|
50
|
-
console.error('Error:', error)
|
|
51
|
-
cachedLabels[taxonomy] = taxonomy
|
|
52
|
-
resolve(taxonomy)
|
|
53
|
-
})
|
|
54
|
-
})
|
|
55
|
-
}
|
|
47
|
+
console.error('Error:', error);
|
|
48
|
+
cachedLabels[taxonomy] = taxonomy;
|
|
49
|
+
resolve(taxonomy);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
};
|
|
56
53
|
|
|
57
54
|
const findTaxonomyLabels = async function (mapImp, taxonomies) {
|
|
58
55
|
const intersectionTaxonomies = taxonomies.filter((taxonomy) =>
|
|
59
|
-
cachedTaxonLabels.some((obj) => obj.taxon === taxonomy)
|
|
56
|
+
cachedTaxonLabels.some((obj) => obj.taxon === taxonomy),
|
|
60
57
|
);
|
|
61
58
|
|
|
62
59
|
const foundCachedTaxonLabels = cachedTaxonLabels.filter((obj) =>
|
|
63
|
-
intersectionTaxonomies.includes(obj.taxon)
|
|
60
|
+
intersectionTaxonomies.includes(obj.taxon),
|
|
64
61
|
);
|
|
65
62
|
|
|
66
|
-
const leftoverTaxonomies = taxonomies.filter(
|
|
67
|
-
!intersectionTaxonomies.includes(taxonomy)
|
|
63
|
+
const leftoverTaxonomies = taxonomies.filter(
|
|
64
|
+
(taxonomy) => !intersectionTaxonomies.includes(taxonomy),
|
|
68
65
|
);
|
|
69
66
|
|
|
70
67
|
if (!leftoverTaxonomies.length) {
|
|
@@ -75,7 +72,7 @@ const findTaxonomyLabels = async function (mapImp, taxonomies) {
|
|
|
75
72
|
entityLabels.forEach((entityLabel) => {
|
|
76
73
|
let { entity: taxon, label } = entityLabel;
|
|
77
74
|
if (label === 'Mammalia') {
|
|
78
|
-
label = 'Mammalia not otherwise specified'
|
|
75
|
+
label = 'Mammalia not otherwise specified';
|
|
79
76
|
}
|
|
80
77
|
const item = { taxon, label };
|
|
81
78
|
foundCachedTaxonLabels.push(item);
|
|
@@ -84,14 +81,14 @@ const findTaxonomyLabels = async function (mapImp, taxonomies) {
|
|
|
84
81
|
return foundCachedTaxonLabels;
|
|
85
82
|
}
|
|
86
83
|
}
|
|
87
|
-
}
|
|
84
|
+
};
|
|
88
85
|
|
|
89
86
|
const inArray = function (ar1, ar2) {
|
|
90
|
-
if (!ar1 || !ar2) return false
|
|
91
|
-
let as1 = JSON.stringify(ar1)
|
|
92
|
-
let as2 = JSON.stringify(ar2)
|
|
93
|
-
return as1.indexOf(as2) !== -1
|
|
94
|
-
}
|
|
87
|
+
if (!ar1 || !ar2) return false;
|
|
88
|
+
let as1 = JSON.stringify(ar1);
|
|
89
|
+
let as2 = JSON.stringify(ar2);
|
|
90
|
+
return as1.indexOf(as2) !== -1;
|
|
91
|
+
};
|
|
95
92
|
|
|
96
93
|
const compareNames = (a, b) => {
|
|
97
94
|
// to make it work for both string name and obj.name
|
|
@@ -109,43 +106,40 @@ const compareNames = (a, b) => {
|
|
|
109
106
|
}
|
|
110
107
|
|
|
111
108
|
return 0;
|
|
112
|
-
}
|
|
109
|
+
};
|
|
113
110
|
|
|
114
111
|
let FlatmapQueries = function () {
|
|
115
112
|
this.initialise = function (flatmapAPI) {
|
|
116
|
-
this.flatmapAPI = flatmapAPI
|
|
117
|
-
this.destinations = []
|
|
118
|
-
this.origins = []
|
|
119
|
-
this.components = []
|
|
120
|
-
this.destinationsWithDatasets = []
|
|
121
|
-
this.originsWithDatasets = []
|
|
122
|
-
this.componentsWithDatasets = []
|
|
123
|
-
this.destinationsCombinations = []
|
|
124
|
-
this.originsCombinations = []
|
|
125
|
-
this.componentsCombinations = []
|
|
126
|
-
this.singleConnectivityList = []
|
|
127
|
-
this.hasSingleConnectivityList = false
|
|
128
|
-
this.rawURLs = []
|
|
129
|
-
this.controller = undefined
|
|
130
|
-
this.uberons = []
|
|
131
|
-
this.lookUp = []
|
|
132
|
-
this.connectivitySource = 'map' // 'sckan'
|
|
133
|
-
this.noMapConnectivity = false
|
|
134
|
-
}
|
|
113
|
+
this.flatmapAPI = flatmapAPI;
|
|
114
|
+
this.destinations = [];
|
|
115
|
+
this.origins = [];
|
|
116
|
+
this.components = [];
|
|
117
|
+
this.destinationsWithDatasets = [];
|
|
118
|
+
this.originsWithDatasets = [];
|
|
119
|
+
this.componentsWithDatasets = [];
|
|
120
|
+
this.destinationsCombinations = [];
|
|
121
|
+
this.originsCombinations = [];
|
|
122
|
+
this.componentsCombinations = [];
|
|
123
|
+
this.singleConnectivityList = [];
|
|
124
|
+
this.hasSingleConnectivityList = false;
|
|
125
|
+
this.rawURLs = [];
|
|
126
|
+
this.controller = undefined;
|
|
127
|
+
this.uberons = [];
|
|
128
|
+
this.lookUp = [];
|
|
129
|
+
this.connectivitySource = 'map'; // 'sckan'
|
|
130
|
+
this.noMapConnectivity = false;
|
|
131
|
+
};
|
|
135
132
|
|
|
136
133
|
this.createTooltipData = async function (mapImp, eventData) {
|
|
137
|
-
let hyperlinks
|
|
138
|
-
if (
|
|
139
|
-
eventData.feature.hyperlinks
|
|
140
|
-
eventData.feature.hyperlinks.length > 0
|
|
141
|
-
) {
|
|
142
|
-
hyperlinks = eventData.feature.hyperlinks
|
|
134
|
+
let hyperlinks;
|
|
135
|
+
if (eventData.feature.hyperlinks && eventData.feature.hyperlinks.length > 0) {
|
|
136
|
+
hyperlinks = eventData.feature.hyperlinks;
|
|
143
137
|
} else {
|
|
144
138
|
hyperlinks = this.rawURLs;
|
|
145
139
|
}
|
|
146
|
-
let taxonomyLabel = undefined
|
|
140
|
+
let taxonomyLabel = undefined;
|
|
147
141
|
if (eventData.provenanceTaxonomy) {
|
|
148
|
-
taxonomyLabel = []
|
|
142
|
+
taxonomyLabel = [];
|
|
149
143
|
try {
|
|
150
144
|
const entityLabels = await findTaxonomyLabels(mapImp, eventData.provenanceTaxonomy);
|
|
151
145
|
if (entityLabels.length) {
|
|
@@ -155,7 +149,7 @@ let FlatmapQueries = function () {
|
|
|
155
149
|
});
|
|
156
150
|
}
|
|
157
151
|
} catch (error) {
|
|
158
|
-
console.log(error)
|
|
152
|
+
console.log(error);
|
|
159
153
|
}
|
|
160
154
|
}
|
|
161
155
|
|
|
@@ -177,9 +171,9 @@ let FlatmapQueries = function () {
|
|
|
177
171
|
provenanceTaxonomyLabel: taxonomyLabel,
|
|
178
172
|
connectivitySource: this.connectivitySource,
|
|
179
173
|
noMapConnectivity: this.noMapConnectivity,
|
|
180
|
-
}
|
|
181
|
-
return tooltipData
|
|
182
|
-
}
|
|
174
|
+
};
|
|
175
|
+
return tooltipData;
|
|
176
|
+
};
|
|
183
177
|
|
|
184
178
|
this.updateTooltipData = function (tooltipEntry) {
|
|
185
179
|
return {
|
|
@@ -196,144 +190,146 @@ let FlatmapQueries = function () {
|
|
|
196
190
|
connectivitySource: this.connectivitySource,
|
|
197
191
|
noMapConnectivity: this.noMapConnectivity,
|
|
198
192
|
};
|
|
199
|
-
}
|
|
193
|
+
};
|
|
200
194
|
|
|
201
195
|
this.createComponentsLabelList = function (components, lookUp) {
|
|
202
|
-
let labelList = []
|
|
196
|
+
let labelList = [];
|
|
203
197
|
components.forEach((n) => {
|
|
204
|
-
labelList.push(this.createLabelFromNeuralNode(n[0]), lookUp)
|
|
198
|
+
labelList.push(this.createLabelFromNeuralNode(n[0]), lookUp);
|
|
205
199
|
if (n.length === 2) {
|
|
206
|
-
labelList.push(this.createLabelFromNeuralNode(n[1]), lookUp)
|
|
200
|
+
labelList.push(this.createLabelFromNeuralNode(n[1]), lookUp);
|
|
207
201
|
}
|
|
208
|
-
})
|
|
209
|
-
return labelList
|
|
210
|
-
}
|
|
202
|
+
});
|
|
203
|
+
return labelList;
|
|
204
|
+
};
|
|
211
205
|
|
|
212
|
-
this.createLabelLookup = function (mapImp, uberons) {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
name: label,
|
|
224
|
-
})
|
|
206
|
+
this.createLabelLookup = async function (mapImp, uberons) {
|
|
207
|
+
let uberonMap = {};
|
|
208
|
+
this.uberons = [];
|
|
209
|
+
const entityLabels = await findTaxonomyLabels(mapImp, uberons);
|
|
210
|
+
if (entityLabels.length) {
|
|
211
|
+
entityLabels.forEach((entityLabel) => {
|
|
212
|
+
const { taxon: entity, label } = entityLabel;
|
|
213
|
+
uberonMap[entity] = label;
|
|
214
|
+
this.uberons.push({
|
|
215
|
+
id: entity,
|
|
216
|
+
name: label,
|
|
225
217
|
});
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
}
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
return uberonMap;
|
|
221
|
+
};
|
|
230
222
|
|
|
231
223
|
this.buildConnectivitySqlStatement = function (keastIds) {
|
|
232
|
-
let sql = 'select knowledge from knowledge where entity in ('
|
|
224
|
+
let sql = 'select knowledge from knowledge where entity in (';
|
|
233
225
|
if (keastIds.length === 1) {
|
|
234
|
-
sql += `'${keastIds[0]}')
|
|
226
|
+
sql += `'${keastIds[0]}')`;
|
|
235
227
|
} else if (keastIds.length > 1) {
|
|
236
228
|
for (let i in keastIds) {
|
|
237
|
-
sql += `'${keastIds[i]}'${i >= keastIds.length - 1 ? ')' : ','}
|
|
229
|
+
sql += `'${keastIds[i]}'${i >= keastIds.length - 1 ? ')' : ','} `;
|
|
238
230
|
}
|
|
239
231
|
}
|
|
240
|
-
return sql
|
|
241
|
-
}
|
|
232
|
+
return sql;
|
|
233
|
+
};
|
|
242
234
|
|
|
243
235
|
this.buildLabelSqlStatement = function (uberons) {
|
|
244
|
-
let sql = 'select entity, label from labels where entity in ('
|
|
236
|
+
let sql = 'select entity, label from labels where entity in (';
|
|
245
237
|
if (uberons.length === 1) {
|
|
246
|
-
sql += `'${uberons[0]}')
|
|
238
|
+
sql += `'${uberons[0]}')`;
|
|
247
239
|
} else if (uberons.length > 1) {
|
|
248
240
|
for (let i in uberons) {
|
|
249
|
-
sql += `'${uberons[i]}'${i >= uberons.length - 1 ? ')' : ','}
|
|
241
|
+
sql += `'${uberons[i]}'${i >= uberons.length - 1 ? ')' : ','} `;
|
|
250
242
|
}
|
|
251
243
|
}
|
|
252
|
-
return sql
|
|
253
|
-
}
|
|
244
|
+
return sql;
|
|
245
|
+
};
|
|
254
246
|
|
|
255
247
|
this.findAllIdsFromConnectivity = function (connectivity) {
|
|
256
|
-
let dnodes = connectivity.connectivity.flat() // get nodes from edgelist
|
|
257
|
-
let nodes = [...new Set(dnodes)] // remove duplicates
|
|
258
|
-
let found = []
|
|
248
|
+
let dnodes = connectivity.connectivity.flat(); // get nodes from edgelist
|
|
249
|
+
let nodes = [...new Set(dnodes)]; // remove duplicates
|
|
250
|
+
let found = [];
|
|
259
251
|
nodes.forEach((n) => {
|
|
260
252
|
if (Array.isArray(n)) {
|
|
261
|
-
found.push(n.flat())
|
|
253
|
+
found.push(n.flat());
|
|
262
254
|
} else {
|
|
263
|
-
found.push(n)
|
|
255
|
+
found.push(n);
|
|
264
256
|
}
|
|
265
|
-
})
|
|
266
|
-
return [...new Set(found.flat())]
|
|
267
|
-
}
|
|
257
|
+
});
|
|
258
|
+
return [...new Set(found.flat())];
|
|
259
|
+
};
|
|
268
260
|
|
|
269
261
|
this.flattenConnectivity = function (connectivity) {
|
|
270
|
-
let dnodes = connectivity.flat() // get nodes from edgelist
|
|
271
|
-
let nodes = [...new Set(dnodes)] // remove duplicates
|
|
272
|
-
let found = []
|
|
262
|
+
let dnodes = connectivity.flat(); // get nodes from edgelist
|
|
263
|
+
let nodes = [...new Set(dnodes)]; // remove duplicates
|
|
264
|
+
let found = [];
|
|
273
265
|
nodes.forEach((n) => {
|
|
274
266
|
if (Array.isArray(n)) {
|
|
275
|
-
found.push(n.flat())
|
|
267
|
+
found.push(n.flat());
|
|
276
268
|
} else {
|
|
277
|
-
found.push(n)
|
|
269
|
+
found.push(n);
|
|
278
270
|
}
|
|
279
|
-
})
|
|
280
|
-
return found.flat()
|
|
281
|
-
}
|
|
271
|
+
});
|
|
272
|
+
return found.flat();
|
|
273
|
+
};
|
|
282
274
|
|
|
283
275
|
this.findComponents = function (connectivity, axons, dendrites, somas) {
|
|
284
|
-
let dnodes = connectivity.connectivity.flat() // get nodes from edgelist
|
|
285
|
-
let nodes = removeDuplicates(dnodes)
|
|
276
|
+
let dnodes = connectivity.connectivity.flat(); // get nodes from edgelist
|
|
277
|
+
let nodes = removeDuplicates(dnodes);
|
|
286
278
|
|
|
287
|
-
let found = []
|
|
288
|
-
let terminal = false
|
|
279
|
+
let found = [];
|
|
280
|
+
let terminal = false;
|
|
289
281
|
nodes.forEach((node) => {
|
|
290
|
-
terminal = false
|
|
282
|
+
terminal = false;
|
|
291
283
|
// Check if the node is an destination or origin (note that they are labelled dendrite and axon as opposed to origin and destination)
|
|
292
284
|
if (inArray(axons, node)) {
|
|
293
|
-
terminal = true
|
|
285
|
+
terminal = true;
|
|
294
286
|
}
|
|
295
287
|
if (somas && inArray(somas, node)) {
|
|
296
|
-
terminal = true
|
|
288
|
+
terminal = true;
|
|
297
289
|
}
|
|
298
290
|
if (inArray(dendrites, node)) {
|
|
299
|
-
terminal = true
|
|
291
|
+
terminal = true;
|
|
300
292
|
}
|
|
301
293
|
if (!terminal) {
|
|
302
|
-
found.push(node)
|
|
294
|
+
found.push(node);
|
|
303
295
|
}
|
|
304
|
-
})
|
|
296
|
+
});
|
|
305
297
|
|
|
306
|
-
return found
|
|
307
|
-
}
|
|
298
|
+
return found;
|
|
299
|
+
};
|
|
308
300
|
|
|
309
301
|
this.retrieveFlatmapKnowledgeForEvent = async function (mapImp, eventData) {
|
|
310
302
|
// check if there is an existing query
|
|
311
|
-
if (this.controller) this.controller.abort()
|
|
303
|
+
if (this.controller) this.controller.abort();
|
|
312
304
|
|
|
313
305
|
// set up the abort controller
|
|
314
|
-
this.controller = new AbortController()
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
this.
|
|
319
|
-
this.
|
|
320
|
-
this.
|
|
321
|
-
this.
|
|
322
|
-
this.
|
|
323
|
-
this.
|
|
324
|
-
this.
|
|
325
|
-
this.
|
|
326
|
-
this.
|
|
327
|
-
this.
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
let
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
}
|
|
306
|
+
this.controller = new AbortController();
|
|
307
|
+
|
|
308
|
+
const keastIds = eventData.resource;
|
|
309
|
+
this.destinations = [];
|
|
310
|
+
this.origins = [];
|
|
311
|
+
this.components = [];
|
|
312
|
+
this.destinationsWithDatasets = [];
|
|
313
|
+
this.originsWithDatasets = [];
|
|
314
|
+
this.componentsWithDatasets = [];
|
|
315
|
+
this.destinationsCombinations = [];
|
|
316
|
+
this.originsCombinations = [];
|
|
317
|
+
this.componentsCombinations = [];
|
|
318
|
+
this.singleConnectivityList = [];
|
|
319
|
+
this.rawURLs = [];
|
|
320
|
+
if (!keastIds || keastIds.length === 0 || !keastIds[0]) return;
|
|
321
|
+
|
|
322
|
+
let prom1 = await this.queryForConnectivityNew(mapImp, keastIds[0]); // This on returns a promise so dont need 'await'
|
|
323
|
+
let results = await Promise.all([prom1]);
|
|
324
|
+
return results;
|
|
325
|
+
};
|
|
335
326
|
|
|
336
|
-
this.queryForConnectivityNew = async function (
|
|
327
|
+
this.queryForConnectivityNew = async function (
|
|
328
|
+
mapImp,
|
|
329
|
+
keastId,
|
|
330
|
+
connectivitySource = 'map',
|
|
331
|
+
processConnectivity = true,
|
|
332
|
+
) {
|
|
337
333
|
// The new service, CQ27, is available for SCKAN knowledge sources from 2026 onward.
|
|
338
334
|
const sourceMatch = /^sckan-(\d{4})/.exec(mapImp.knowledgeSource || '');
|
|
339
335
|
const sourceYear = sourceMatch ? Number(sourceMatch[1]) : 0;
|
|
@@ -347,9 +343,10 @@ let FlatmapQueries = function () {
|
|
|
347
343
|
}
|
|
348
344
|
|
|
349
345
|
return new Promise((resolve) => {
|
|
350
|
-
const queryAPI =
|
|
351
|
-
|
|
352
|
-
|
|
346
|
+
const queryAPI =
|
|
347
|
+
this.connectivitySource === 'map'
|
|
348
|
+
? this.queryMapConnectivity(mapImp.mapMetadata.uuid, keastId)
|
|
349
|
+
: mapImp.queryKnowledge(keastId);
|
|
353
350
|
|
|
354
351
|
queryAPI
|
|
355
352
|
.then((response) => {
|
|
@@ -365,17 +362,17 @@ let FlatmapQueries = function () {
|
|
|
365
362
|
// with publications from both PubMed and Others
|
|
366
363
|
this.rawURLs = [...response.references];
|
|
367
364
|
}
|
|
368
|
-
resolve(processedConnectivity)
|
|
369
|
-
})
|
|
370
|
-
}
|
|
371
|
-
else resolve(connectivity)
|
|
365
|
+
resolve(processedConnectivity);
|
|
366
|
+
});
|
|
367
|
+
} else resolve(connectivity);
|
|
372
368
|
} else if (this.connectivitySource === 'map') {
|
|
373
369
|
// switch to SCKAN
|
|
374
370
|
// when there has no connectivity data from Map
|
|
375
371
|
// and add the noMapConnectivity flag to disable the option
|
|
376
372
|
this.connectivitySource = 'sckan';
|
|
377
373
|
this.noMapConnectivity = true;
|
|
378
|
-
mapImp
|
|
374
|
+
mapImp
|
|
375
|
+
.queryKnowledge(keastId)
|
|
379
376
|
.then((fallbackResponse) => {
|
|
380
377
|
if (this.checkConnectivityExists(fallbackResponse)) {
|
|
381
378
|
let connectivity = fallbackResponse;
|
|
@@ -386,17 +383,16 @@ let FlatmapQueries = function () {
|
|
|
386
383
|
// with publications from both PubMed and Others
|
|
387
384
|
this.rawURLs = [...fallbackResponse.references];
|
|
388
385
|
}
|
|
389
|
-
resolve(processedConnectivity)
|
|
390
|
-
})
|
|
391
|
-
}
|
|
392
|
-
else resolve(connectivity)
|
|
386
|
+
resolve(processedConnectivity);
|
|
387
|
+
});
|
|
388
|
+
} else resolve(connectivity);
|
|
393
389
|
} else {
|
|
394
|
-
resolve(false)
|
|
390
|
+
resolve(false);
|
|
395
391
|
}
|
|
396
392
|
})
|
|
397
393
|
.catch(() => resolve(false));
|
|
398
394
|
} else {
|
|
399
|
-
resolve(false)
|
|
395
|
+
resolve(false);
|
|
400
396
|
}
|
|
401
397
|
})
|
|
402
398
|
.catch((error) => {
|
|
@@ -405,12 +401,12 @@ let FlatmapQueries = function () {
|
|
|
405
401
|
} else {
|
|
406
402
|
// console.error('Error:', error)
|
|
407
403
|
// TODO: to update after queryKnowledge method update
|
|
408
|
-
console.warn(`Unable to get the knowledge for the entity ${keastId}.`)
|
|
404
|
+
console.warn(`Unable to get the knowledge for the entity ${keastId}.`);
|
|
409
405
|
}
|
|
410
|
-
resolve(false)
|
|
411
|
-
})
|
|
412
|
-
})
|
|
413
|
-
}
|
|
406
|
+
resolve(false);
|
|
407
|
+
});
|
|
408
|
+
});
|
|
409
|
+
};
|
|
414
410
|
|
|
415
411
|
this.queryMapConnectivity = async function (mapuuid, pathId) {
|
|
416
412
|
const url = this.flatmapAPI + `flatmap/${mapuuid}/connectivity/${pathId}`;
|
|
@@ -423,12 +419,12 @@ let FlatmapQueries = function () {
|
|
|
423
419
|
|
|
424
420
|
return await response.json();
|
|
425
421
|
} catch (error) {
|
|
426
|
-
throw new Error(error);
|
|
422
|
+
throw new Error(error, { cause: error });
|
|
427
423
|
}
|
|
428
|
-
}
|
|
424
|
+
};
|
|
429
425
|
|
|
430
|
-
this.queryForConnectivity = function (mapImp, keastIds, signal, processConnectivity=true) {
|
|
431
|
-
const data = { sql: this.buildConnectivitySqlStatement(keastIds) }
|
|
426
|
+
this.queryForConnectivity = function (mapImp, keastIds, signal, processConnectivity = true) {
|
|
427
|
+
const data = { sql: this.buildConnectivitySqlStatement(keastIds) };
|
|
432
428
|
const headers = {
|
|
433
429
|
method: 'POST',
|
|
434
430
|
headers: {
|
|
@@ -436,33 +432,32 @@ let FlatmapQueries = function () {
|
|
|
436
432
|
},
|
|
437
433
|
body: JSON.stringify(data),
|
|
438
434
|
...(signal ? { signal: signal } : {}), // add signal to header if it exists
|
|
439
|
-
}
|
|
435
|
+
};
|
|
440
436
|
return new Promise((resolve) => {
|
|
441
437
|
fetch(`${this.flatmapAPI}knowledge/query/`, headers)
|
|
442
438
|
.then((response) => response.json())
|
|
443
439
|
.then((data) => {
|
|
444
440
|
if (this.connectivityExists(data)) {
|
|
445
|
-
let connectivity = JSON.parse(data.values[0][0])
|
|
441
|
+
let connectivity = JSON.parse(data.values[0][0]);
|
|
446
442
|
if (processConnectivity) {
|
|
447
443
|
this.processConnectivity(mapImp, connectivity).then((processedConnectivity) => {
|
|
448
|
-
resolve(processedConnectivity)
|
|
449
|
-
})
|
|
450
|
-
}
|
|
451
|
-
else resolve(connectivity)
|
|
444
|
+
resolve(processedConnectivity);
|
|
445
|
+
});
|
|
446
|
+
} else resolve(connectivity);
|
|
452
447
|
} else {
|
|
453
|
-
resolve(false)
|
|
448
|
+
resolve(false);
|
|
454
449
|
}
|
|
455
450
|
})
|
|
456
451
|
.catch((error) => {
|
|
457
452
|
if (error.name === 'AbortError') {
|
|
458
453
|
// This error is from AbortController's abort method.
|
|
459
454
|
} else {
|
|
460
|
-
console.error('Error:', error)
|
|
455
|
+
console.error('Error:', error);
|
|
461
456
|
}
|
|
462
|
-
resolve(false)
|
|
463
|
-
})
|
|
464
|
-
})
|
|
465
|
-
}
|
|
457
|
+
resolve(false);
|
|
458
|
+
});
|
|
459
|
+
});
|
|
460
|
+
};
|
|
466
461
|
|
|
467
462
|
this.checkConnectivityExists = function (data) {
|
|
468
463
|
return data && data.connectivity?.length;
|
|
@@ -475,67 +470,68 @@ let FlatmapQueries = function () {
|
|
|
475
470
|
JSON.parse(data.values[0][0]).connectivity &&
|
|
476
471
|
JSON.parse(data.values[0][0]).connectivity.length > 0
|
|
477
472
|
) {
|
|
478
|
-
return true
|
|
473
|
+
return true;
|
|
479
474
|
} else {
|
|
480
|
-
return false
|
|
475
|
+
return false;
|
|
481
476
|
}
|
|
482
|
-
}
|
|
477
|
+
};
|
|
483
478
|
|
|
484
479
|
// This function is used to determine if a node is a single node or a node with multiple children
|
|
485
480
|
// Returns the id of the node if it is a single node, otherwise returns false
|
|
486
481
|
this.findIfNodeIsSingle = function (node) {
|
|
487
|
-
if (node.length === 1) {
|
|
488
|
-
|
|
489
|
-
|
|
482
|
+
if (node.length === 1) {
|
|
483
|
+
// If the node is in the form [id]
|
|
484
|
+
console.error('Server returns a single node', node);
|
|
485
|
+
return node[0];
|
|
490
486
|
} else {
|
|
491
|
-
if (node.length === 2 && node[1].length === 0) {
|
|
492
|
-
|
|
487
|
+
if (node.length === 2 && node[1].length === 0) {
|
|
488
|
+
// If the node is in the form [id, []]
|
|
489
|
+
return node[0];
|
|
493
490
|
} else {
|
|
494
|
-
return false // If the node is in the form [id, [id1, id2]]
|
|
491
|
+
return false; // If the node is in the form [id, [id1, id2]]
|
|
495
492
|
}
|
|
496
493
|
}
|
|
497
|
-
}
|
|
494
|
+
};
|
|
498
495
|
|
|
499
496
|
this.createLabelFromNeuralNode = function (node, lookUp) {
|
|
500
|
-
|
|
501
497
|
// Check if the node is a single node or a node with multiple children
|
|
502
|
-
let nodeIsSingle = this.findIfNodeIsSingle(node)
|
|
498
|
+
let nodeIsSingle = this.findIfNodeIsSingle(node);
|
|
503
499
|
|
|
504
500
|
// Case where node is in the form [id]
|
|
505
501
|
if (nodeIsSingle) {
|
|
506
|
-
return lookUp[nodeIsSingle]
|
|
502
|
+
return lookUp[nodeIsSingle];
|
|
507
503
|
}
|
|
508
504
|
|
|
509
505
|
// Case where node is in the form [id, [id1 (,id2)]]
|
|
510
|
-
let label = lookUp[node[0]]
|
|
506
|
+
let label = lookUp[node[0]];
|
|
511
507
|
if (node.length === 2 && node[1].length > 0) {
|
|
512
508
|
node[1].forEach((n) => {
|
|
513
509
|
if (lookUp[n] == undefined) {
|
|
514
|
-
label += `, ${n}
|
|
510
|
+
label += `, ${n}`;
|
|
515
511
|
} else {
|
|
516
|
-
label += `, ${lookUp[n]}
|
|
512
|
+
label += `, ${lookUp[n]}`;
|
|
517
513
|
}
|
|
518
|
-
})
|
|
514
|
+
});
|
|
519
515
|
}
|
|
520
|
-
return label
|
|
521
|
-
}
|
|
516
|
+
return label;
|
|
517
|
+
};
|
|
522
518
|
|
|
523
519
|
this.flattenAndFindDatasets = function (dendrites, components, axons) {
|
|
524
520
|
// process the nodes for finding datasets (Note this is not critical to the tooltip, only for the 'search on components' button)
|
|
525
|
-
let dendritesFlat = this.flattenConnectivity(dendrites)
|
|
526
|
-
let componentsFlat = this.flattenConnectivity(components)
|
|
527
|
-
let axonsFlat = this.flattenConnectivity(axons)
|
|
521
|
+
let dendritesFlat = this.flattenConnectivity(dendrites);
|
|
522
|
+
let componentsFlat = this.flattenConnectivity(components);
|
|
523
|
+
let axonsFlat = this.flattenConnectivity(axons);
|
|
528
524
|
|
|
529
525
|
// Filter for the anatomy which is annotated on datasets
|
|
530
|
-
this.originsWithDatasets = this.uberons
|
|
531
|
-
(ub) => dendritesFlat.indexOf(ub.id) !== -1
|
|
532
|
-
|
|
533
|
-
this.componentsWithDatasets = this.uberons
|
|
534
|
-
(ub) => componentsFlat.indexOf(ub.id) !== -1
|
|
535
|
-
|
|
536
|
-
this.destinationsWithDatasets = this.uberons
|
|
537
|
-
(ub) => axonsFlat.indexOf(ub.id) !== -1
|
|
538
|
-
|
|
526
|
+
this.originsWithDatasets = this.uberons
|
|
527
|
+
.filter((ub) => dendritesFlat.indexOf(ub.id) !== -1)
|
|
528
|
+
.sort(compareNames);
|
|
529
|
+
this.componentsWithDatasets = this.uberons
|
|
530
|
+
.filter((ub) => componentsFlat.indexOf(ub.id) !== -1)
|
|
531
|
+
.sort(compareNames);
|
|
532
|
+
this.destinationsWithDatasets = this.uberons
|
|
533
|
+
.filter((ub) => axonsFlat.indexOf(ub.id) !== -1)
|
|
534
|
+
.sort(compareNames);
|
|
539
535
|
|
|
540
536
|
// Get combinations of SCKAN and Map
|
|
541
537
|
if (this.singleConnectivityList.length > 0) {
|
|
@@ -543,110 +539,117 @@ let FlatmapQueries = function () {
|
|
|
543
539
|
const { sckanNodeId, sckanNodeLabel, mapNodeId, mapNodeLabel } = value;
|
|
544
540
|
|
|
545
541
|
// The base ID is SCKAN ID
|
|
546
|
-
if (axons.find(axon => JSON.stringify(axon) === JSON.stringify(sckanNodeId))) {
|
|
542
|
+
if (axons.find((axon) => JSON.stringify(axon) === JSON.stringify(sckanNodeId))) {
|
|
547
543
|
this.destinationsCombinations.push({
|
|
548
544
|
sckanId: sckanNodeId,
|
|
549
545
|
sckanLabel: sckanNodeLabel,
|
|
550
546
|
mapId: mapNodeId,
|
|
551
547
|
mapLabel: mapNodeLabel,
|
|
552
|
-
})
|
|
548
|
+
});
|
|
553
549
|
mapNodeId.forEach((id, i) => {
|
|
554
550
|
const stringId = typeof id === 'string' ? id : id[0];
|
|
555
551
|
if (stringId) {
|
|
556
552
|
this.destinationsWithDatasets.push({
|
|
557
553
|
id: stringId,
|
|
558
|
-
name: mapNodeLabel.split(',')[i]
|
|
554
|
+
name: mapNodeLabel.split(',')[i],
|
|
559
555
|
});
|
|
560
556
|
}
|
|
561
557
|
});
|
|
562
558
|
}
|
|
563
|
-
if (
|
|
559
|
+
if (
|
|
560
|
+
dendrites.find((dendrite) => JSON.stringify(dendrite) === JSON.stringify(sckanNodeId))
|
|
561
|
+
) {
|
|
564
562
|
this.originsCombinations.push({
|
|
565
563
|
sckanId: sckanNodeId,
|
|
566
564
|
sckanLabel: sckanNodeLabel,
|
|
567
565
|
mapId: mapNodeId,
|
|
568
566
|
mapLabel: mapNodeLabel,
|
|
569
|
-
})
|
|
567
|
+
});
|
|
570
568
|
mapNodeId.forEach((id, i) => {
|
|
571
569
|
const stringId = typeof id === 'string' ? id : id[0];
|
|
572
570
|
if (stringId) {
|
|
573
571
|
this.originsWithDatasets.push({
|
|
574
572
|
id: stringId,
|
|
575
|
-
name: mapNodeLabel.split(',')[i]
|
|
573
|
+
name: mapNodeLabel.split(',')[i],
|
|
576
574
|
});
|
|
577
575
|
}
|
|
578
576
|
});
|
|
579
577
|
}
|
|
580
|
-
if (
|
|
578
|
+
if (
|
|
579
|
+
components.find((component) => JSON.stringify(component) === JSON.stringify(sckanNodeId))
|
|
580
|
+
) {
|
|
581
581
|
this.componentsCombinations.push({
|
|
582
582
|
sckanId: sckanNodeId,
|
|
583
583
|
sckanLabel: sckanNodeLabel,
|
|
584
584
|
mapId: mapNodeId,
|
|
585
585
|
mapLabel: mapNodeLabel,
|
|
586
|
-
})
|
|
586
|
+
});
|
|
587
587
|
mapNodeId.forEach((id, i) => {
|
|
588
588
|
const stringId = typeof id === 'string' ? id : id[0];
|
|
589
589
|
if (stringId) {
|
|
590
590
|
this.componentsWithDatasets.push({
|
|
591
591
|
id: stringId,
|
|
592
|
-
name: mapNodeLabel.split(',')[i]
|
|
592
|
+
name: mapNodeLabel.split(',')[i],
|
|
593
593
|
});
|
|
594
594
|
}
|
|
595
595
|
});
|
|
596
596
|
}
|
|
597
597
|
});
|
|
598
598
|
}
|
|
599
|
-
}
|
|
599
|
+
};
|
|
600
600
|
|
|
601
601
|
this.processConnectivity = function (mapImp, connectivity) {
|
|
602
602
|
return new Promise((resolve) => {
|
|
603
|
-
let dendrites = []
|
|
604
|
-
let axons = []
|
|
605
|
-
let somas = undefined
|
|
606
|
-
if (connectivity && connectivity[
|
|
607
|
-
const sourceKey = [
|
|
608
|
-
const destinationKey = [
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
603
|
+
let dendrites = [];
|
|
604
|
+
let axons = [];
|
|
605
|
+
let somas = undefined;
|
|
606
|
+
if (connectivity && connectivity['node-phenotypes']) {
|
|
607
|
+
const sourceKey = ['ilxtr:hasSomaLocatedIn'];
|
|
608
|
+
const destinationKey = [
|
|
609
|
+
'ilxtr:hasAxonPresynapticElementIn',
|
|
610
|
+
'ilxtr:hasAxonSensorySubcellularElementIn',
|
|
611
|
+
];
|
|
612
|
+
sourceKey.forEach((key) => {
|
|
613
|
+
if (key in connectivity['node-phenotypes']) {
|
|
614
|
+
dendrites.push(...connectivity['node-phenotypes'][key]);
|
|
612
615
|
}
|
|
613
|
-
})
|
|
614
|
-
dendrites = removeDuplicates(dendrites)
|
|
615
|
-
destinationKey.forEach((key)=>{
|
|
616
|
-
if (key in connectivity[
|
|
617
|
-
axons.push(...connectivity[
|
|
616
|
+
});
|
|
617
|
+
dendrites = removeDuplicates(dendrites);
|
|
618
|
+
destinationKey.forEach((key) => {
|
|
619
|
+
if (key in connectivity['node-phenotypes']) {
|
|
620
|
+
axons.push(...connectivity['node-phenotypes'][key]);
|
|
618
621
|
}
|
|
619
|
-
})
|
|
620
|
-
axons = removeDuplicates(axons)
|
|
622
|
+
});
|
|
623
|
+
axons = removeDuplicates(axons);
|
|
621
624
|
} else {
|
|
622
625
|
// Remove duplicates
|
|
623
|
-
axons = removeDuplicates(connectivity.axons)
|
|
626
|
+
axons = removeDuplicates(connectivity.axons);
|
|
624
627
|
//Somas will become part of origins, support this for future proof
|
|
625
628
|
if (connectivity.somas && connectivity.somas.length > 0) {
|
|
626
|
-
dendrites.push(...connectivity.somas)
|
|
629
|
+
dendrites.push(...connectivity.somas);
|
|
627
630
|
}
|
|
628
631
|
if (connectivity.dendrites && connectivity.dendrites.length > 0) {
|
|
629
|
-
dendrites.push(...connectivity.dendrites)
|
|
632
|
+
dendrites.push(...connectivity.dendrites);
|
|
630
633
|
}
|
|
631
|
-
dendrites = removeDuplicates(dendrites)
|
|
632
|
-
somas = connectivity.somas
|
|
634
|
+
dendrites = removeDuplicates(dendrites);
|
|
635
|
+
somas = connectivity.somas;
|
|
633
636
|
}
|
|
634
637
|
|
|
635
|
-
const components = this.findComponents(connectivity, axons, dendrites, somas)
|
|
638
|
+
const components = this.findComponents(connectivity, axons, dendrites, somas);
|
|
636
639
|
// Create list of ids to get labels for
|
|
637
|
-
const conIds = this.findAllIdsFromConnectivity(connectivity)
|
|
640
|
+
const conIds = this.findAllIdsFromConnectivity(connectivity);
|
|
638
641
|
// Create readable labels from the nodes. Setting this to 'this.origins' updates the display
|
|
639
642
|
this.createLabelLookup(mapImp, conIds).then((lookUp) => {
|
|
640
|
-
this.origins = dendrites
|
|
641
|
-
this.createLabelFromNeuralNode(d, lookUp)
|
|
642
|
-
|
|
643
|
-
this.components = components
|
|
644
|
-
this.createLabelFromNeuralNode(c, lookUp)
|
|
645
|
-
|
|
646
|
-
this.destinations = axons
|
|
647
|
-
this.createLabelFromNeuralNode(a, lookUp)
|
|
648
|
-
|
|
649
|
-
this.flattenAndFindDatasets(dendrites, components, axons)
|
|
643
|
+
this.origins = dendrites
|
|
644
|
+
.map((d) => this.createLabelFromNeuralNode(d, lookUp))
|
|
645
|
+
.sort(compareNames);
|
|
646
|
+
this.components = components
|
|
647
|
+
.map((c) => this.createLabelFromNeuralNode(c, lookUp))
|
|
648
|
+
.sort(compareNames);
|
|
649
|
+
this.destinations = axons
|
|
650
|
+
.map((a) => this.createLabelFromNeuralNode(a, lookUp))
|
|
651
|
+
.sort(compareNames);
|
|
652
|
+
this.flattenAndFindDatasets(dendrites, components, axons);
|
|
650
653
|
resolve({
|
|
651
654
|
ids: {
|
|
652
655
|
dendrites: dendrites,
|
|
@@ -657,27 +660,27 @@ let FlatmapQueries = function () {
|
|
|
657
660
|
origins: this.origins,
|
|
658
661
|
components: this.components,
|
|
659
662
|
destinations: this.destinations,
|
|
660
|
-
}
|
|
661
|
-
})
|
|
662
|
-
})
|
|
663
|
-
})
|
|
664
|
-
}
|
|
663
|
+
},
|
|
664
|
+
});
|
|
665
|
+
});
|
|
666
|
+
});
|
|
667
|
+
};
|
|
665
668
|
|
|
666
669
|
this.buildPubmedSqlStatement = function (keastIds) {
|
|
667
|
-
let sql = 'select distinct publication from publications where entity in ('
|
|
670
|
+
let sql = 'select distinct publication from publications where entity in (';
|
|
668
671
|
if (keastIds.length === 1) {
|
|
669
|
-
sql += `'${keastIds[0]}')
|
|
672
|
+
sql += `'${keastIds[0]}')`;
|
|
670
673
|
} else if (keastIds.length > 1) {
|
|
671
674
|
for (let i in keastIds) {
|
|
672
|
-
sql += `'${keastIds[i]}'${i >= keastIds.length - 1 ? ')' : ','}
|
|
675
|
+
sql += `'${keastIds[i]}'${i >= keastIds.length - 1 ? ')' : ','} `;
|
|
673
676
|
}
|
|
674
677
|
}
|
|
675
|
-
return sql
|
|
676
|
-
}
|
|
678
|
+
return sql;
|
|
679
|
+
};
|
|
677
680
|
|
|
678
681
|
this.buildPubmedSqlStatementForModels = function (model) {
|
|
679
|
-
return `select distinct publication from publications where entity = '${model}'
|
|
680
|
-
}
|
|
682
|
+
return `select distinct publication from publications where entity = '${model}'`;
|
|
683
|
+
};
|
|
681
684
|
|
|
682
685
|
this.queryKnowledge = async (sql, params) => {
|
|
683
686
|
const url = `${this.flatmapAPI}knowledge/query/`;
|
|
@@ -685,19 +688,19 @@ let FlatmapQueries = function () {
|
|
|
685
688
|
const response = await fetch(url, {
|
|
686
689
|
method: 'POST',
|
|
687
690
|
headers: {
|
|
688
|
-
|
|
691
|
+
Accept: 'application/json',
|
|
689
692
|
},
|
|
690
693
|
body: JSON.stringify(query),
|
|
691
|
-
})
|
|
694
|
+
});
|
|
692
695
|
if (!response.ok) {
|
|
693
|
-
|
|
696
|
+
throw new Error(`Cannot access ${url}`);
|
|
694
697
|
}
|
|
695
698
|
const data = await response.json();
|
|
696
699
|
if ('error' in data) {
|
|
697
|
-
|
|
700
|
+
throw new TypeError(data.error);
|
|
698
701
|
}
|
|
699
|
-
return data.values
|
|
700
|
-
}
|
|
701
|
-
}
|
|
702
|
+
return data.values;
|
|
703
|
+
};
|
|
704
|
+
};
|
|
702
705
|
|
|
703
|
-
export { FlatmapQueries, findTaxonomyLabel, findTaxonomyLabels }
|
|
706
|
+
export { FlatmapQueries, findTaxonomyLabel, findTaxonomyLabels };
|