@abi-software/flatmapvuer 0.3.14 → 0.3.15
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/LICENSE +201 -201
- package/README.md +105 -105
- package/babel.config.js +14 -14
- package/dist/flatmapvuer.common.js +1344 -985
- package/dist/flatmapvuer.common.js.map +1 -1
- package/dist/flatmapvuer.css +1 -1
- package/dist/flatmapvuer.umd.js +1344 -985
- package/dist/flatmapvuer.umd.js.map +1 -1
- package/dist/flatmapvuer.umd.min.js +1 -1
- package/dist/flatmapvuer.umd.min.js.map +1 -1
- package/package-lock.json +14210 -14210
- package/package.json +71 -71
- package/public/index.html +17 -17
- package/src/App.vue +162 -162
- package/src/assets/_variables.scss +43 -43
- package/src/assets/styles.scss +7 -7
- package/src/components/EventBus.js +2 -2
- package/src/components/FlatmapVuer.vue +1634 -1636
- package/src/components/MultiFlatmapVuer.vue +509 -511
- package/src/components/PubmedViewer.vue +149 -149
- package/src/components/Tooltip.vue +594 -594
- package/src/components/index.js +9 -9
- package/src/components/legends/Legends.vue +66 -66
- package/src/icons/fonts/mapicon-species.eot +0 -0
- package/src/icons/fonts/mapicon-species.svg +14 -14
- package/src/icons/fonts/mapicon-species.ttf +0 -0
- package/src/icons/fonts/mapicon-species.woff +0 -0
- package/src/icons/mapicon-species-style.css +42 -42
- package/src/legends/legend.svg +25 -25
- package/src/main.js +8 -8
- package/vue.config.js +31 -31
- package/src/nerve-map.js +0 -99
|
@@ -1,594 +1,594 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div class="tooltip-container">
|
|
3
|
-
<el-main v-if="content" class="main" v-loading="loading">
|
|
4
|
-
<div class="block" v-if="content.title">
|
|
5
|
-
<span class="title">{{capitalise(content.title)}}</span>
|
|
6
|
-
</div>
|
|
7
|
-
<div class="block" v-else>
|
|
8
|
-
<span class="title">{{content.featureId}}</span>
|
|
9
|
-
</div>
|
|
10
|
-
<div class="content-container scrollbar">
|
|
11
|
-
{{content.paths}}
|
|
12
|
-
<div v-if="this.origins" class="block">
|
|
13
|
-
<div>
|
|
14
|
-
<span class="attribute-title">Origin</span>
|
|
15
|
-
<el-popover
|
|
16
|
-
width="250"
|
|
17
|
-
trigger="hover"
|
|
18
|
-
:append-to-body=false
|
|
19
|
-
popper-class="popover-origin-help"
|
|
20
|
-
>
|
|
21
|
-
<i slot="reference" class="el-icon-warning-outline info"/>
|
|
22
|
-
<span style="word-break: keep-all;">
|
|
23
|
-
<i>Origin</i> {{originDescription}}
|
|
24
|
-
</span>
|
|
25
|
-
</el-popover>
|
|
26
|
-
</div>
|
|
27
|
-
<div v-for="(origin, i) in origins" class="attribute-content" :key="origin">
|
|
28
|
-
{{ capitalise(origin) }}
|
|
29
|
-
<div v-if="i != origins.length - 1" class="seperator"></div>
|
|
30
|
-
</div>
|
|
31
|
-
<el-button v-show="originsWithDatasets.length > 0" class="button" @click="openDendrites">
|
|
32
|
-
Explore origin data
|
|
33
|
-
</el-button>
|
|
34
|
-
</div>
|
|
35
|
-
<div v-if="this.components" class="block">
|
|
36
|
-
<div class="attribute-title">Components</div>
|
|
37
|
-
<div v-for="(component, i) in components" class="attribute-content" :key="component">
|
|
38
|
-
{{ capitalise(component) }}
|
|
39
|
-
<div v-if="i != components.length - 1" class="seperator"></div>
|
|
40
|
-
</div>
|
|
41
|
-
</div>
|
|
42
|
-
<div v-if="this.destinations" class="block">
|
|
43
|
-
<div>
|
|
44
|
-
<span class="attribute-title">Destination</span>
|
|
45
|
-
<el-popover
|
|
46
|
-
width="250"
|
|
47
|
-
trigger="hover"
|
|
48
|
-
:append-to-body=false
|
|
49
|
-
popper-class="popover-origin-help"
|
|
50
|
-
>
|
|
51
|
-
<i slot="reference" class="el-icon-warning-outline info"/>
|
|
52
|
-
<span style="word-break: keep-all;">
|
|
53
|
-
<i>Destination</i> is where the axons terminate
|
|
54
|
-
</span>
|
|
55
|
-
</el-popover>
|
|
56
|
-
</div>
|
|
57
|
-
<div v-for="(destination, i) in destinations" class="attribute-content" :key="destination">
|
|
58
|
-
{{ capitalise(destination) }}
|
|
59
|
-
<div v-if="i != destinations.length - 1" class="seperator"></div>
|
|
60
|
-
</div>
|
|
61
|
-
<el-button v-show="destinationsWithDatasets.length > 0" class="button" @click="openAxons">
|
|
62
|
-
Explore destination data
|
|
63
|
-
</el-button>
|
|
64
|
-
</div>
|
|
65
|
-
|
|
66
|
-
<el-button v-show="components.length > 0" class="button" @click="openAll">
|
|
67
|
-
Search for data on components
|
|
68
|
-
</el-button>
|
|
69
|
-
|
|
70
|
-
<!-- pubmed-viewer is just used for processing pubmed requests (no display) -->
|
|
71
|
-
<pubmed-viewer v-if="content.featureIds" v-show="false" :entry="content" @pubmedSearchUrl="pubmedSearchUrlUpdate"/>
|
|
72
|
-
<el-button v-if="pubmedSearchUrl != ''" class="button" icon="el-icon-notebook-2" @click="openUrl(pubmedSearchUrl)">
|
|
73
|
-
Open publications in pubmed
|
|
74
|
-
</el-button>
|
|
75
|
-
</div>
|
|
76
|
-
</el-main>
|
|
77
|
-
</div>
|
|
78
|
-
</template>
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
<script>
|
|
82
|
-
/* eslint-disable no-alert, no-console */
|
|
83
|
-
import Vue from "vue";
|
|
84
|
-
import {
|
|
85
|
-
Button,
|
|
86
|
-
Container,
|
|
87
|
-
Header,
|
|
88
|
-
Icon,
|
|
89
|
-
Main
|
|
90
|
-
} from "element-ui";
|
|
91
|
-
import lang from "element-ui/lib/locale/lang/en";
|
|
92
|
-
import locale from "element-ui/lib/locale";
|
|
93
|
-
locale.use(lang);
|
|
94
|
-
Vue.use(Button);
|
|
95
|
-
Vue.use(Container);
|
|
96
|
-
Vue.use(Header);
|
|
97
|
-
Vue.use(Icon);
|
|
98
|
-
Vue.use(Main);
|
|
99
|
-
|
|
100
|
-
// pubmedviewer is currently not in use, but still under review so not ready to delete yet
|
|
101
|
-
import PubmedViewer from './PubmedViewer.vue'
|
|
102
|
-
import EventBus from './EventBus'
|
|
103
|
-
|
|
104
|
-
const titleCase = (str) => {
|
|
105
|
-
return str.replace(/\w\S*/g, (t) => { return t.charAt(0).toUpperCase() + t.substr(1).toLowerCase() });
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const inArray = function(ar1, ar2){
|
|
109
|
-
let as1 = JSON.stringify(ar1)
|
|
110
|
-
let as2 = JSON.stringify(ar2)
|
|
111
|
-
return as1.indexOf(as2) !== -1
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// remove duplicates by stringifying the objects
|
|
115
|
-
const removeDuplicates = function(arrayOfAnything){
|
|
116
|
-
return [...new Set(arrayOfAnything.map(e => JSON.stringify(e)))].map(e => JSON.parse(e))
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const capitalise = function(str){
|
|
120
|
-
if (str)
|
|
121
|
-
return str.charAt(0).toUpperCase() + str.slice(1)
|
|
122
|
-
return ""
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
export default {
|
|
126
|
-
components: { PubmedViewer },
|
|
127
|
-
name: "Tooltip",
|
|
128
|
-
props: {
|
|
129
|
-
visible: {
|
|
130
|
-
type: Boolean,
|
|
131
|
-
default: false
|
|
132
|
-
},
|
|
133
|
-
content: {
|
|
134
|
-
type: Object,
|
|
135
|
-
default: undefined
|
|
136
|
-
},
|
|
137
|
-
},
|
|
138
|
-
data: function() {
|
|
139
|
-
return {
|
|
140
|
-
activeSpecies: undefined,
|
|
141
|
-
appendToBody: false,
|
|
142
|
-
pubmedSearchUrl: '',
|
|
143
|
-
loading: false,
|
|
144
|
-
destinations: [],
|
|
145
|
-
origins: [],
|
|
146
|
-
components: [],
|
|
147
|
-
destinationsWithDatasets: [],
|
|
148
|
-
originsWithDatasets: [],
|
|
149
|
-
originDescriptions: {
|
|
150
|
-
'motor': 'is the location of the initial cell body of the circuit',
|
|
151
|
-
'sensory': 'is the location of the initial cell body in the PNS circuit'
|
|
152
|
-
},
|
|
153
|
-
componentsWithDatasets: [],
|
|
154
|
-
uberons: [{id: undefined, name: undefined}]
|
|
155
|
-
};
|
|
156
|
-
},
|
|
157
|
-
inject: ['sparcAPI', 'flatmapAPI'],
|
|
158
|
-
watch: {
|
|
159
|
-
'content.featureIds': {
|
|
160
|
-
handler: function(){
|
|
161
|
-
this.pathwayQuery(this.content.featureIds)
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
},
|
|
165
|
-
mounted: function(){
|
|
166
|
-
this.getOrganCuries()
|
|
167
|
-
},
|
|
168
|
-
computed: {
|
|
169
|
-
originDescription: function(){
|
|
170
|
-
if(this.content && this.content.title && this.content.title.toLowerCase().includes('motor')){
|
|
171
|
-
return this.originDescriptions.motor
|
|
172
|
-
} else {
|
|
173
|
-
return this.originDescriptions.sensory
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
},
|
|
177
|
-
methods: {
|
|
178
|
-
resourceSelected: function(action) {
|
|
179
|
-
this.$emit("resource-selected", action);
|
|
180
|
-
},
|
|
181
|
-
titleCase: function(title){
|
|
182
|
-
return titleCase(title)
|
|
183
|
-
},
|
|
184
|
-
capitalise: function(text){
|
|
185
|
-
return capitalise(text)
|
|
186
|
-
},
|
|
187
|
-
onClose: function() {
|
|
188
|
-
this.$emit("onClose")
|
|
189
|
-
},
|
|
190
|
-
openUrl: function(url){
|
|
191
|
-
window.open(url, '_blank')
|
|
192
|
-
},
|
|
193
|
-
openAll: function(){
|
|
194
|
-
EventBus.$emit('onActionClick', {type:'Facets', labels: this.components})
|
|
195
|
-
},
|
|
196
|
-
openAxons: function(){
|
|
197
|
-
EventBus.$emit('onActionClick', {type:'Facets', labels: this.destinationsWithDatasets.map(a=>a.name)})
|
|
198
|
-
},
|
|
199
|
-
openDendrites: function(){
|
|
200
|
-
EventBus.$emit('onActionClick', {type:'Facets', labels: this.originsWithDatasets.map(a=>a.name)})
|
|
201
|
-
},
|
|
202
|
-
pubmedSearchUrlUpdate: function (val){
|
|
203
|
-
this.pubmedSearchUrl = val
|
|
204
|
-
},
|
|
205
|
-
findAllIdsFromConnectivity(connectivity){
|
|
206
|
-
let dnodes = connectivity.connectivity.flat() // get nodes from edgelist
|
|
207
|
-
let nodes = [...new Set(dnodes)] // remove duplicates
|
|
208
|
-
let found = []
|
|
209
|
-
nodes.forEach(n=>{
|
|
210
|
-
if (Array.isArray(n)){
|
|
211
|
-
found.push(n.flat())
|
|
212
|
-
} else {
|
|
213
|
-
found.push(n)
|
|
214
|
-
}
|
|
215
|
-
})
|
|
216
|
-
return [... new Set(found.flat())]
|
|
217
|
-
},
|
|
218
|
-
flattenConntectivity(connectivity){
|
|
219
|
-
let dnodes = connectivity.flat() // get nodes from edgelist
|
|
220
|
-
let nodes = [...new Set(dnodes)] // remove duplicates
|
|
221
|
-
let found = []
|
|
222
|
-
nodes.forEach(n=>{
|
|
223
|
-
if (Array.isArray(n)){
|
|
224
|
-
found.push(n.flat())
|
|
225
|
-
} else {
|
|
226
|
-
found.push(n)
|
|
227
|
-
}
|
|
228
|
-
})
|
|
229
|
-
return found.flat()
|
|
230
|
-
},
|
|
231
|
-
findComponents: function(connectivity){
|
|
232
|
-
let dnodes = connectivity.connectivity.flat() // get nodes from edgelist
|
|
233
|
-
let nodes = removeDuplicates(dnodes)
|
|
234
|
-
|
|
235
|
-
let found = []
|
|
236
|
-
let terminal = false
|
|
237
|
-
nodes.forEach(node=>{
|
|
238
|
-
terminal = false
|
|
239
|
-
// Check if the node is an destination or origin (note that they are labelled dendrite and axon as opposed to origin and destination)
|
|
240
|
-
if(inArray(connectivity.axons,node)){
|
|
241
|
-
terminal = true
|
|
242
|
-
}
|
|
243
|
-
if(inArray(connectivity.dendrites, node)){
|
|
244
|
-
terminal = true
|
|
245
|
-
}
|
|
246
|
-
if (!terminal){
|
|
247
|
-
found.push(node)
|
|
248
|
-
}
|
|
249
|
-
})
|
|
250
|
-
|
|
251
|
-
return found
|
|
252
|
-
},
|
|
253
|
-
getOrganCuries: function(){
|
|
254
|
-
fetch(`${this.sparcAPI}get-organ-curies/`)
|
|
255
|
-
.then(response=>response.json())
|
|
256
|
-
.then(data=>{
|
|
257
|
-
this.uberons = data.uberon.array
|
|
258
|
-
})
|
|
259
|
-
},
|
|
260
|
-
buildConnectivitySqlStatement: function(keastIds) {
|
|
261
|
-
let sql = 'select knowledge from knowledge where entity in ('
|
|
262
|
-
if (keastIds.length === 1) {
|
|
263
|
-
sql += `'${keastIds[0]}')`
|
|
264
|
-
} else if (keastIds.length > 1) {
|
|
265
|
-
for (let i in keastIds) {
|
|
266
|
-
sql += `'${keastIds[i]}'${i >= keastIds.length - 1 ? ')' : ','} `
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
return sql
|
|
270
|
-
},
|
|
271
|
-
buildLabelSqlStatement: function(uberons) {
|
|
272
|
-
let sql = 'select entity, label from labels where entity in ('
|
|
273
|
-
if (uberons.length === 1) {
|
|
274
|
-
sql += `'${uberons[0]}')`
|
|
275
|
-
} else if (uberons.length > 1) {
|
|
276
|
-
for (let i in uberons) {
|
|
277
|
-
sql += `'${uberons[i]}'${i >= uberons.length - 1 ? ')' : ','} `
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
return sql
|
|
281
|
-
},
|
|
282
|
-
createLabelLookup: function(uberons) {
|
|
283
|
-
return new Promise(resolve=> {
|
|
284
|
-
let uberonMap = {}
|
|
285
|
-
const data = { sql: this.buildLabelSqlStatement(uberons)}
|
|
286
|
-
fetch(`${this.flatmapAPI}knowledge/query/`, {
|
|
287
|
-
method: 'POST',
|
|
288
|
-
headers: {
|
|
289
|
-
'Content-Type': 'application/json',
|
|
290
|
-
},
|
|
291
|
-
body: JSON.stringify(data),
|
|
292
|
-
})
|
|
293
|
-
.then(response => response.json())
|
|
294
|
-
.then(payload => {
|
|
295
|
-
const entity = payload.keys.indexOf("entity");
|
|
296
|
-
const label = payload.keys.indexOf("label");
|
|
297
|
-
if (entity > -1 && label > -1) {
|
|
298
|
-
payload.values.forEach(pair => {
|
|
299
|
-
uberonMap[pair[entity]] = pair[label];
|
|
300
|
-
});
|
|
301
|
-
}
|
|
302
|
-
resolve(uberonMap)
|
|
303
|
-
})
|
|
304
|
-
})
|
|
305
|
-
},
|
|
306
|
-
createComponentsLabelList: function(components, lookUp){
|
|
307
|
-
let labelList = []
|
|
308
|
-
components.forEach(n=>{
|
|
309
|
-
labelList.push(this.createLabelFromNeuralNode(n[0]), lookUp)
|
|
310
|
-
if (n.length === 2){
|
|
311
|
-
labelList.push(this.createLabelFromNeuralNode(n[1]), lookUp)
|
|
312
|
-
}
|
|
313
|
-
})
|
|
314
|
-
return labelList
|
|
315
|
-
},
|
|
316
|
-
createLabelFromNeuralNode: function(node, lookUp){
|
|
317
|
-
let label = lookUp[node[0]]
|
|
318
|
-
if (node.length === 2 && node[1].length > 0){
|
|
319
|
-
node[1].forEach(n=>{
|
|
320
|
-
if (lookUp[n] == undefined){
|
|
321
|
-
label += `, ${n}`
|
|
322
|
-
} else {
|
|
323
|
-
label += `, ${lookUp[n]}`
|
|
324
|
-
}
|
|
325
|
-
})
|
|
326
|
-
}
|
|
327
|
-
return label
|
|
328
|
-
},
|
|
329
|
-
processConnectivity(connectivity){
|
|
330
|
-
// Filter the origin and destinations from components
|
|
331
|
-
let components = this.findComponents(connectivity)
|
|
332
|
-
|
|
333
|
-
// Remove duplicates
|
|
334
|
-
let axons = removeDuplicates(connectivity.axons)
|
|
335
|
-
let dendrites = removeDuplicates(connectivity.dendrites)
|
|
336
|
-
|
|
337
|
-
// Create list of ids to get labels for
|
|
338
|
-
let conIds = this.findAllIdsFromConnectivity(connectivity)
|
|
339
|
-
|
|
340
|
-
// Create readable labels from the nodes. Setting this to 'this.origins' updates the display
|
|
341
|
-
this.createLabelLookup(conIds).then(lookUp=>{
|
|
342
|
-
this.destinations = axons.map(a=>this.createLabelFromNeuralNode(a,lookUp))
|
|
343
|
-
this.origins = dendrites.map(d=>this.createLabelFromNeuralNode(d,lookUp))
|
|
344
|
-
this.components = components.map(c=>this.createLabelFromNeuralNode(c, lookUp))
|
|
345
|
-
})
|
|
346
|
-
|
|
347
|
-
this.flattenAndFindDatasets(components, axons, dendrites)
|
|
348
|
-
},
|
|
349
|
-
flattenAndFindDatasets(components, axons, dendrites){
|
|
350
|
-
|
|
351
|
-
// process the nodes for finding datasets (Note this is not critical to the tooltip, only for the 'search on components' button)
|
|
352
|
-
let componentsFlat = this.flattenConntectivity(components)
|
|
353
|
-
let axonsFlat = this.flattenConntectivity(axons)
|
|
354
|
-
let dendritesFlat = this.flattenConntectivity(dendrites)
|
|
355
|
-
|
|
356
|
-
// Filter for the anatomy which is annotated on datasets
|
|
357
|
-
this.destinationsWithDatasets = this.uberons.filter(ub => axonsFlat.indexOf(ub.id) !== -1)
|
|
358
|
-
this.originsWithDatasets = this.uberons.filter(ub => dendritesFlat.indexOf(ub.id) !== -1)
|
|
359
|
-
this.componentsWithDatasets = this.uberons.filter(ub => componentsFlat.indexOf(ub.id) !== -1)
|
|
360
|
-
},
|
|
361
|
-
pathwayQuery: function(keastIds){
|
|
362
|
-
this.destinations = []
|
|
363
|
-
this.origins = []
|
|
364
|
-
this.components = []
|
|
365
|
-
this.loading = true
|
|
366
|
-
if (!keastIds || keastIds.length == 0) return
|
|
367
|
-
const data = { sql: this.buildConnectivitySqlStatement(keastIds)};
|
|
368
|
-
fetch(`${this.flatmapAPI}knowledge/query/`, {
|
|
369
|
-
method: 'POST',
|
|
370
|
-
headers: {
|
|
371
|
-
'Content-Type': 'application/json',
|
|
372
|
-
},
|
|
373
|
-
body: JSON.stringify(data),
|
|
374
|
-
})
|
|
375
|
-
.then(response => response.json())
|
|
376
|
-
.then(data => {
|
|
377
|
-
let connectivity = JSON.parse(data.values[0][0])
|
|
378
|
-
this.processConnectivity(connectivity)
|
|
379
|
-
this.loading = false
|
|
380
|
-
})
|
|
381
|
-
.catch((error) => {
|
|
382
|
-
console.error('Error:', error);
|
|
383
|
-
})
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
};
|
|
387
|
-
</script>
|
|
388
|
-
|
|
389
|
-
<style scoped lang="scss">
|
|
390
|
-
@import "~element-ui/packages/theme-chalk/src/button";
|
|
391
|
-
@import "~element-ui/packages/theme-chalk/src/container";
|
|
392
|
-
@import "~element-ui/packages/theme-chalk/src/header";
|
|
393
|
-
@import "~element-ui/packages/theme-chalk/src/main";
|
|
394
|
-
|
|
395
|
-
.tooltip-container {
|
|
396
|
-
text-align:justify;
|
|
397
|
-
border-radius: 4px;
|
|
398
|
-
box-shadow: 0 1px 2px rgba(0,0,0,.1);
|
|
399
|
-
pointer-events: auto;
|
|
400
|
-
background: #fff;
|
|
401
|
-
border: 1px solid $app-primary-color;
|
|
402
|
-
display: flex;
|
|
403
|
-
justify-content: center;
|
|
404
|
-
align-items: center;
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
.display {
|
|
408
|
-
width: 44px;
|
|
409
|
-
word-break: normal;
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
.title {
|
|
413
|
-
text-align: left;
|
|
414
|
-
width: 16em;
|
|
415
|
-
line-height: 1.5em !important;
|
|
416
|
-
font-size: 1em;
|
|
417
|
-
font-family: Helvetica;
|
|
418
|
-
font-weight: 500;
|
|
419
|
-
/* font-weight: bold; */
|
|
420
|
-
padding-bottom: 8px;
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
.block {
|
|
424
|
-
margin-bottom: 1.5em;
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
.pub {
|
|
428
|
-
width: 16rem;
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
.icon {
|
|
432
|
-
right: 0px;
|
|
433
|
-
position: absolute;
|
|
434
|
-
top: 10px;
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
.icon:hover {
|
|
438
|
-
cursor: pointer;
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
.popover-origin-help {
|
|
442
|
-
text-transform: none !important; // need to overide the tooltip text transform
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
.info{
|
|
446
|
-
transform: rotate(180deg);
|
|
447
|
-
color: #8300bf;
|
|
448
|
-
margin-left: 8px;
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
.seperator {
|
|
452
|
-
width:90%;
|
|
453
|
-
height:0.5px;
|
|
454
|
-
background-color:#bfbec2;
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
.main {
|
|
458
|
-
font-size: 14px;
|
|
459
|
-
text-align: left;
|
|
460
|
-
line-height: 1.5em;
|
|
461
|
-
font-family: Helvetica;
|
|
462
|
-
font-weight: 400;
|
|
463
|
-
/* outline: thin red solid; */
|
|
464
|
-
padding: 1em !important;
|
|
465
|
-
overflow: hidden;
|
|
466
|
-
min-width: 16rem;
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
.title{
|
|
470
|
-
font-size: 18px;
|
|
471
|
-
font-weight: 500;
|
|
472
|
-
font-weight: bold;
|
|
473
|
-
padding-bottom: 8px;
|
|
474
|
-
color: rgb(131, 0, 191);
|
|
475
|
-
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
.attribute-title{
|
|
479
|
-
font-size: 16px;
|
|
480
|
-
font-weight: 600;
|
|
481
|
-
/* font-weight: bold; */
|
|
482
|
-
text-transform: uppercase;
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
.attribute-content{
|
|
486
|
-
font-size: 14px;
|
|
487
|
-
font-weight: 500;
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
.popover-container {
|
|
491
|
-
height: 100%;
|
|
492
|
-
width: 100%;
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
.main {
|
|
496
|
-
.el-button.is-round{
|
|
497
|
-
border-radius: 4px;
|
|
498
|
-
padding: 9px 20px 10px 20px;
|
|
499
|
-
display: flex;
|
|
500
|
-
height: 36px;
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
.button {
|
|
505
|
-
margin-left: 0px !important;
|
|
506
|
-
margin-top: 0px !important;
|
|
507
|
-
font-size: 14px !important;
|
|
508
|
-
background-color: $app-primary-color;
|
|
509
|
-
color: #fff;
|
|
510
|
-
&+.button {
|
|
511
|
-
margin-top: 10px !important;
|
|
512
|
-
}
|
|
513
|
-
&:hover {
|
|
514
|
-
color: #fff !important;
|
|
515
|
-
background: #ac76c5 !important;
|
|
516
|
-
border: 1px solid #ac76c5 !important;
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
.tooltip-container{
|
|
521
|
-
&::after, &::before {
|
|
522
|
-
content: '';
|
|
523
|
-
display: block;
|
|
524
|
-
position: absolute;
|
|
525
|
-
width: 0;
|
|
526
|
-
height: 0;
|
|
527
|
-
border-style: solid;
|
|
528
|
-
flex-shrink: 0;
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
.mapboxgl-popup-anchor-bottom {
|
|
533
|
-
.tooltip-container {
|
|
534
|
-
&::after, &::before {
|
|
535
|
-
top: 100%;
|
|
536
|
-
border-width: 12px;
|
|
537
|
-
}
|
|
538
|
-
&::after {
|
|
539
|
-
margin-top:-1px;
|
|
540
|
-
border-color: rgb(255, 255, 255) transparent transparent transparent ;
|
|
541
|
-
}
|
|
542
|
-
&::before {
|
|
543
|
-
margin: 0 auto;
|
|
544
|
-
border-color: $app-primary-color transparent transparent transparent ;
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
.mapboxgl-popup-anchor-top {
|
|
550
|
-
.tooltip-container {
|
|
551
|
-
&::after, &::before {
|
|
552
|
-
top: -24px;
|
|
553
|
-
border-width: 12px;
|
|
554
|
-
}
|
|
555
|
-
&::after {
|
|
556
|
-
margin-top: 1px;
|
|
557
|
-
border-color: transparent transparent rgb(255, 255, 255) transparent ;
|
|
558
|
-
}
|
|
559
|
-
&::before {
|
|
560
|
-
margin: 0 auto;
|
|
561
|
-
border-color: transparent transparent $app-primary-color transparent ;
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
.content-container {
|
|
567
|
-
overflow-y: scroll;
|
|
568
|
-
scrollbar-width: thin !important;
|
|
569
|
-
height: 200px;
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
.scrollbar::-webkit-scrollbar-track {
|
|
573
|
-
border-radius: 10px;
|
|
574
|
-
background-color: #f5f5f5;
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
.scrollbar::-webkit-scrollbar {
|
|
578
|
-
width: 12px;
|
|
579
|
-
right: -12px;
|
|
580
|
-
background-color: #f5f5f5;
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
.scrollbar::-webkit-scrollbar-thumb {
|
|
584
|
-
border-radius: 4px;
|
|
585
|
-
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.06);
|
|
586
|
-
background-color: #979797;
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
/* Fix for chrome bug where under triangle pops up above one on top of it */
|
|
591
|
-
.selector:not(*:root), .tooltip-container::after{
|
|
592
|
-
top: 99.4%;
|
|
593
|
-
}
|
|
594
|
-
</style>
|
|
1
|
+
<template>
|
|
2
|
+
<div class="tooltip-container">
|
|
3
|
+
<el-main v-if="content" class="main" v-loading="loading">
|
|
4
|
+
<div class="block" v-if="content.title">
|
|
5
|
+
<span class="title">{{capitalise(content.title)}}</span>
|
|
6
|
+
</div>
|
|
7
|
+
<div class="block" v-else>
|
|
8
|
+
<span class="title">{{content.featureId}}</span>
|
|
9
|
+
</div>
|
|
10
|
+
<div class="content-container scrollbar">
|
|
11
|
+
{{content.paths}}
|
|
12
|
+
<div v-if="this.origins" class="block">
|
|
13
|
+
<div>
|
|
14
|
+
<span class="attribute-title">Origin</span>
|
|
15
|
+
<el-popover
|
|
16
|
+
width="250"
|
|
17
|
+
trigger="hover"
|
|
18
|
+
:append-to-body=false
|
|
19
|
+
popper-class="popover-origin-help"
|
|
20
|
+
>
|
|
21
|
+
<i slot="reference" class="el-icon-warning-outline info"/>
|
|
22
|
+
<span style="word-break: keep-all;">
|
|
23
|
+
<i>Origin</i> {{originDescription}}
|
|
24
|
+
</span>
|
|
25
|
+
</el-popover>
|
|
26
|
+
</div>
|
|
27
|
+
<div v-for="(origin, i) in origins" class="attribute-content" :key="origin">
|
|
28
|
+
{{ capitalise(origin) }}
|
|
29
|
+
<div v-if="i != origins.length - 1" class="seperator"></div>
|
|
30
|
+
</div>
|
|
31
|
+
<el-button v-show="originsWithDatasets.length > 0" class="button" @click="openDendrites">
|
|
32
|
+
Explore origin data
|
|
33
|
+
</el-button>
|
|
34
|
+
</div>
|
|
35
|
+
<div v-if="this.components" class="block">
|
|
36
|
+
<div class="attribute-title">Components</div>
|
|
37
|
+
<div v-for="(component, i) in components" class="attribute-content" :key="component">
|
|
38
|
+
{{ capitalise(component) }}
|
|
39
|
+
<div v-if="i != components.length - 1" class="seperator"></div>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
<div v-if="this.destinations" class="block">
|
|
43
|
+
<div>
|
|
44
|
+
<span class="attribute-title">Destination</span>
|
|
45
|
+
<el-popover
|
|
46
|
+
width="250"
|
|
47
|
+
trigger="hover"
|
|
48
|
+
:append-to-body=false
|
|
49
|
+
popper-class="popover-origin-help"
|
|
50
|
+
>
|
|
51
|
+
<i slot="reference" class="el-icon-warning-outline info"/>
|
|
52
|
+
<span style="word-break: keep-all;">
|
|
53
|
+
<i>Destination</i> is where the axons terminate
|
|
54
|
+
</span>
|
|
55
|
+
</el-popover>
|
|
56
|
+
</div>
|
|
57
|
+
<div v-for="(destination, i) in destinations" class="attribute-content" :key="destination">
|
|
58
|
+
{{ capitalise(destination) }}
|
|
59
|
+
<div v-if="i != destinations.length - 1" class="seperator"></div>
|
|
60
|
+
</div>
|
|
61
|
+
<el-button v-show="destinationsWithDatasets.length > 0" class="button" @click="openAxons">
|
|
62
|
+
Explore destination data
|
|
63
|
+
</el-button>
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
<el-button v-show="components.length > 0" class="button" @click="openAll">
|
|
67
|
+
Search for data on components
|
|
68
|
+
</el-button>
|
|
69
|
+
|
|
70
|
+
<!-- pubmed-viewer is just used for processing pubmed requests (no display) -->
|
|
71
|
+
<pubmed-viewer v-if="content.featureIds" v-show="false" :entry="content" @pubmedSearchUrl="pubmedSearchUrlUpdate"/>
|
|
72
|
+
<el-button v-if="pubmedSearchUrl != ''" class="button" icon="el-icon-notebook-2" @click="openUrl(pubmedSearchUrl)">
|
|
73
|
+
Open publications in pubmed
|
|
74
|
+
</el-button>
|
|
75
|
+
</div>
|
|
76
|
+
</el-main>
|
|
77
|
+
</div>
|
|
78
|
+
</template>
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
<script>
|
|
82
|
+
/* eslint-disable no-alert, no-console */
|
|
83
|
+
import Vue from "vue";
|
|
84
|
+
import {
|
|
85
|
+
Button,
|
|
86
|
+
Container,
|
|
87
|
+
Header,
|
|
88
|
+
Icon,
|
|
89
|
+
Main
|
|
90
|
+
} from "element-ui";
|
|
91
|
+
import lang from "element-ui/lib/locale/lang/en";
|
|
92
|
+
import locale from "element-ui/lib/locale";
|
|
93
|
+
locale.use(lang);
|
|
94
|
+
Vue.use(Button);
|
|
95
|
+
Vue.use(Container);
|
|
96
|
+
Vue.use(Header);
|
|
97
|
+
Vue.use(Icon);
|
|
98
|
+
Vue.use(Main);
|
|
99
|
+
|
|
100
|
+
// pubmedviewer is currently not in use, but still under review so not ready to delete yet
|
|
101
|
+
import PubmedViewer from './PubmedViewer.vue'
|
|
102
|
+
import EventBus from './EventBus'
|
|
103
|
+
|
|
104
|
+
const titleCase = (str) => {
|
|
105
|
+
return str.replace(/\w\S*/g, (t) => { return t.charAt(0).toUpperCase() + t.substr(1).toLowerCase() });
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const inArray = function(ar1, ar2){
|
|
109
|
+
let as1 = JSON.stringify(ar1)
|
|
110
|
+
let as2 = JSON.stringify(ar2)
|
|
111
|
+
return as1.indexOf(as2) !== -1
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// remove duplicates by stringifying the objects
|
|
115
|
+
const removeDuplicates = function(arrayOfAnything){
|
|
116
|
+
return [...new Set(arrayOfAnything.map(e => JSON.stringify(e)))].map(e => JSON.parse(e))
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const capitalise = function(str){
|
|
120
|
+
if (str)
|
|
121
|
+
return str.charAt(0).toUpperCase() + str.slice(1)
|
|
122
|
+
return ""
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export default {
|
|
126
|
+
components: { PubmedViewer },
|
|
127
|
+
name: "Tooltip",
|
|
128
|
+
props: {
|
|
129
|
+
visible: {
|
|
130
|
+
type: Boolean,
|
|
131
|
+
default: false
|
|
132
|
+
},
|
|
133
|
+
content: {
|
|
134
|
+
type: Object,
|
|
135
|
+
default: undefined
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
data: function() {
|
|
139
|
+
return {
|
|
140
|
+
activeSpecies: undefined,
|
|
141
|
+
appendToBody: false,
|
|
142
|
+
pubmedSearchUrl: '',
|
|
143
|
+
loading: false,
|
|
144
|
+
destinations: [],
|
|
145
|
+
origins: [],
|
|
146
|
+
components: [],
|
|
147
|
+
destinationsWithDatasets: [],
|
|
148
|
+
originsWithDatasets: [],
|
|
149
|
+
originDescriptions: {
|
|
150
|
+
'motor': 'is the location of the initial cell body of the circuit',
|
|
151
|
+
'sensory': 'is the location of the initial cell body in the PNS circuit'
|
|
152
|
+
},
|
|
153
|
+
componentsWithDatasets: [],
|
|
154
|
+
uberons: [{id: undefined, name: undefined}]
|
|
155
|
+
};
|
|
156
|
+
},
|
|
157
|
+
inject: ['sparcAPI', 'flatmapAPI'],
|
|
158
|
+
watch: {
|
|
159
|
+
'content.featureIds': {
|
|
160
|
+
handler: function(){
|
|
161
|
+
this.pathwayQuery(this.content.featureIds)
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
mounted: function(){
|
|
166
|
+
this.getOrganCuries()
|
|
167
|
+
},
|
|
168
|
+
computed: {
|
|
169
|
+
originDescription: function(){
|
|
170
|
+
if(this.content && this.content.title && this.content.title.toLowerCase().includes('motor')){
|
|
171
|
+
return this.originDescriptions.motor
|
|
172
|
+
} else {
|
|
173
|
+
return this.originDescriptions.sensory
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
methods: {
|
|
178
|
+
resourceSelected: function(action) {
|
|
179
|
+
this.$emit("resource-selected", action);
|
|
180
|
+
},
|
|
181
|
+
titleCase: function(title){
|
|
182
|
+
return titleCase(title)
|
|
183
|
+
},
|
|
184
|
+
capitalise: function(text){
|
|
185
|
+
return capitalise(text)
|
|
186
|
+
},
|
|
187
|
+
onClose: function() {
|
|
188
|
+
this.$emit("onClose")
|
|
189
|
+
},
|
|
190
|
+
openUrl: function(url){
|
|
191
|
+
window.open(url, '_blank')
|
|
192
|
+
},
|
|
193
|
+
openAll: function(){
|
|
194
|
+
EventBus.$emit('onActionClick', {type:'Facets', labels: this.components})
|
|
195
|
+
},
|
|
196
|
+
openAxons: function(){
|
|
197
|
+
EventBus.$emit('onActionClick', {type:'Facets', labels: this.destinationsWithDatasets.map(a=>a.name)})
|
|
198
|
+
},
|
|
199
|
+
openDendrites: function(){
|
|
200
|
+
EventBus.$emit('onActionClick', {type:'Facets', labels: this.originsWithDatasets.map(a=>a.name)})
|
|
201
|
+
},
|
|
202
|
+
pubmedSearchUrlUpdate: function (val){
|
|
203
|
+
this.pubmedSearchUrl = val
|
|
204
|
+
},
|
|
205
|
+
findAllIdsFromConnectivity(connectivity){
|
|
206
|
+
let dnodes = connectivity.connectivity.flat() // get nodes from edgelist
|
|
207
|
+
let nodes = [...new Set(dnodes)] // remove duplicates
|
|
208
|
+
let found = []
|
|
209
|
+
nodes.forEach(n=>{
|
|
210
|
+
if (Array.isArray(n)){
|
|
211
|
+
found.push(n.flat())
|
|
212
|
+
} else {
|
|
213
|
+
found.push(n)
|
|
214
|
+
}
|
|
215
|
+
})
|
|
216
|
+
return [... new Set(found.flat())]
|
|
217
|
+
},
|
|
218
|
+
flattenConntectivity(connectivity){
|
|
219
|
+
let dnodes = connectivity.flat() // get nodes from edgelist
|
|
220
|
+
let nodes = [...new Set(dnodes)] // remove duplicates
|
|
221
|
+
let found = []
|
|
222
|
+
nodes.forEach(n=>{
|
|
223
|
+
if (Array.isArray(n)){
|
|
224
|
+
found.push(n.flat())
|
|
225
|
+
} else {
|
|
226
|
+
found.push(n)
|
|
227
|
+
}
|
|
228
|
+
})
|
|
229
|
+
return found.flat()
|
|
230
|
+
},
|
|
231
|
+
findComponents: function(connectivity){
|
|
232
|
+
let dnodes = connectivity.connectivity.flat() // get nodes from edgelist
|
|
233
|
+
let nodes = removeDuplicates(dnodes)
|
|
234
|
+
|
|
235
|
+
let found = []
|
|
236
|
+
let terminal = false
|
|
237
|
+
nodes.forEach(node=>{
|
|
238
|
+
terminal = false
|
|
239
|
+
// Check if the node is an destination or origin (note that they are labelled dendrite and axon as opposed to origin and destination)
|
|
240
|
+
if(inArray(connectivity.axons,node)){
|
|
241
|
+
terminal = true
|
|
242
|
+
}
|
|
243
|
+
if(inArray(connectivity.dendrites, node)){
|
|
244
|
+
terminal = true
|
|
245
|
+
}
|
|
246
|
+
if (!terminal){
|
|
247
|
+
found.push(node)
|
|
248
|
+
}
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
return found
|
|
252
|
+
},
|
|
253
|
+
getOrganCuries: function(){
|
|
254
|
+
fetch(`${this.sparcAPI}get-organ-curies/`)
|
|
255
|
+
.then(response=>response.json())
|
|
256
|
+
.then(data=>{
|
|
257
|
+
this.uberons = data.uberon.array
|
|
258
|
+
})
|
|
259
|
+
},
|
|
260
|
+
buildConnectivitySqlStatement: function(keastIds) {
|
|
261
|
+
let sql = 'select knowledge from knowledge where entity in ('
|
|
262
|
+
if (keastIds.length === 1) {
|
|
263
|
+
sql += `'${keastIds[0]}')`
|
|
264
|
+
} else if (keastIds.length > 1) {
|
|
265
|
+
for (let i in keastIds) {
|
|
266
|
+
sql += `'${keastIds[i]}'${i >= keastIds.length - 1 ? ')' : ','} `
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
return sql
|
|
270
|
+
},
|
|
271
|
+
buildLabelSqlStatement: function(uberons) {
|
|
272
|
+
let sql = 'select entity, label from labels where entity in ('
|
|
273
|
+
if (uberons.length === 1) {
|
|
274
|
+
sql += `'${uberons[0]}')`
|
|
275
|
+
} else if (uberons.length > 1) {
|
|
276
|
+
for (let i in uberons) {
|
|
277
|
+
sql += `'${uberons[i]}'${i >= uberons.length - 1 ? ')' : ','} `
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return sql
|
|
281
|
+
},
|
|
282
|
+
createLabelLookup: function(uberons) {
|
|
283
|
+
return new Promise(resolve=> {
|
|
284
|
+
let uberonMap = {}
|
|
285
|
+
const data = { sql: this.buildLabelSqlStatement(uberons)}
|
|
286
|
+
fetch(`${this.flatmapAPI}knowledge/query/`, {
|
|
287
|
+
method: 'POST',
|
|
288
|
+
headers: {
|
|
289
|
+
'Content-Type': 'application/json',
|
|
290
|
+
},
|
|
291
|
+
body: JSON.stringify(data),
|
|
292
|
+
})
|
|
293
|
+
.then(response => response.json())
|
|
294
|
+
.then(payload => {
|
|
295
|
+
const entity = payload.keys.indexOf("entity");
|
|
296
|
+
const label = payload.keys.indexOf("label");
|
|
297
|
+
if (entity > -1 && label > -1) {
|
|
298
|
+
payload.values.forEach(pair => {
|
|
299
|
+
uberonMap[pair[entity]] = pair[label];
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
resolve(uberonMap)
|
|
303
|
+
})
|
|
304
|
+
})
|
|
305
|
+
},
|
|
306
|
+
createComponentsLabelList: function(components, lookUp){
|
|
307
|
+
let labelList = []
|
|
308
|
+
components.forEach(n=>{
|
|
309
|
+
labelList.push(this.createLabelFromNeuralNode(n[0]), lookUp)
|
|
310
|
+
if (n.length === 2){
|
|
311
|
+
labelList.push(this.createLabelFromNeuralNode(n[1]), lookUp)
|
|
312
|
+
}
|
|
313
|
+
})
|
|
314
|
+
return labelList
|
|
315
|
+
},
|
|
316
|
+
createLabelFromNeuralNode: function(node, lookUp){
|
|
317
|
+
let label = lookUp[node[0]]
|
|
318
|
+
if (node.length === 2 && node[1].length > 0){
|
|
319
|
+
node[1].forEach(n=>{
|
|
320
|
+
if (lookUp[n] == undefined){
|
|
321
|
+
label += `, ${n}`
|
|
322
|
+
} else {
|
|
323
|
+
label += `, ${lookUp[n]}`
|
|
324
|
+
}
|
|
325
|
+
})
|
|
326
|
+
}
|
|
327
|
+
return label
|
|
328
|
+
},
|
|
329
|
+
processConnectivity(connectivity){
|
|
330
|
+
// Filter the origin and destinations from components
|
|
331
|
+
let components = this.findComponents(connectivity)
|
|
332
|
+
|
|
333
|
+
// Remove duplicates
|
|
334
|
+
let axons = removeDuplicates(connectivity.axons)
|
|
335
|
+
let dendrites = removeDuplicates(connectivity.dendrites)
|
|
336
|
+
|
|
337
|
+
// Create list of ids to get labels for
|
|
338
|
+
let conIds = this.findAllIdsFromConnectivity(connectivity)
|
|
339
|
+
|
|
340
|
+
// Create readable labels from the nodes. Setting this to 'this.origins' updates the display
|
|
341
|
+
this.createLabelLookup(conIds).then(lookUp=>{
|
|
342
|
+
this.destinations = axons.map(a=>this.createLabelFromNeuralNode(a,lookUp))
|
|
343
|
+
this.origins = dendrites.map(d=>this.createLabelFromNeuralNode(d,lookUp))
|
|
344
|
+
this.components = components.map(c=>this.createLabelFromNeuralNode(c, lookUp))
|
|
345
|
+
})
|
|
346
|
+
|
|
347
|
+
this.flattenAndFindDatasets(components, axons, dendrites)
|
|
348
|
+
},
|
|
349
|
+
flattenAndFindDatasets(components, axons, dendrites){
|
|
350
|
+
|
|
351
|
+
// process the nodes for finding datasets (Note this is not critical to the tooltip, only for the 'search on components' button)
|
|
352
|
+
let componentsFlat = this.flattenConntectivity(components)
|
|
353
|
+
let axonsFlat = this.flattenConntectivity(axons)
|
|
354
|
+
let dendritesFlat = this.flattenConntectivity(dendrites)
|
|
355
|
+
|
|
356
|
+
// Filter for the anatomy which is annotated on datasets
|
|
357
|
+
this.destinationsWithDatasets = this.uberons.filter(ub => axonsFlat.indexOf(ub.id) !== -1)
|
|
358
|
+
this.originsWithDatasets = this.uberons.filter(ub => dendritesFlat.indexOf(ub.id) !== -1)
|
|
359
|
+
this.componentsWithDatasets = this.uberons.filter(ub => componentsFlat.indexOf(ub.id) !== -1)
|
|
360
|
+
},
|
|
361
|
+
pathwayQuery: function(keastIds){
|
|
362
|
+
this.destinations = []
|
|
363
|
+
this.origins = []
|
|
364
|
+
this.components = []
|
|
365
|
+
this.loading = true
|
|
366
|
+
if (!keastIds || keastIds.length == 0) return
|
|
367
|
+
const data = { sql: this.buildConnectivitySqlStatement(keastIds)};
|
|
368
|
+
fetch(`${this.flatmapAPI}knowledge/query/`, {
|
|
369
|
+
method: 'POST',
|
|
370
|
+
headers: {
|
|
371
|
+
'Content-Type': 'application/json',
|
|
372
|
+
},
|
|
373
|
+
body: JSON.stringify(data),
|
|
374
|
+
})
|
|
375
|
+
.then(response => response.json())
|
|
376
|
+
.then(data => {
|
|
377
|
+
let connectivity = JSON.parse(data.values[0][0])
|
|
378
|
+
this.processConnectivity(connectivity)
|
|
379
|
+
this.loading = false
|
|
380
|
+
})
|
|
381
|
+
.catch((error) => {
|
|
382
|
+
console.error('Error:', error);
|
|
383
|
+
})
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
</script>
|
|
388
|
+
|
|
389
|
+
<style scoped lang="scss">
|
|
390
|
+
@import "~element-ui/packages/theme-chalk/src/button";
|
|
391
|
+
@import "~element-ui/packages/theme-chalk/src/container";
|
|
392
|
+
@import "~element-ui/packages/theme-chalk/src/header";
|
|
393
|
+
@import "~element-ui/packages/theme-chalk/src/main";
|
|
394
|
+
|
|
395
|
+
.tooltip-container {
|
|
396
|
+
text-align:justify;
|
|
397
|
+
border-radius: 4px;
|
|
398
|
+
box-shadow: 0 1px 2px rgba(0,0,0,.1);
|
|
399
|
+
pointer-events: auto;
|
|
400
|
+
background: #fff;
|
|
401
|
+
border: 1px solid $app-primary-color;
|
|
402
|
+
display: flex;
|
|
403
|
+
justify-content: center;
|
|
404
|
+
align-items: center;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
.display {
|
|
408
|
+
width: 44px;
|
|
409
|
+
word-break: normal;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
.title {
|
|
413
|
+
text-align: left;
|
|
414
|
+
width: 16em;
|
|
415
|
+
line-height: 1.5em !important;
|
|
416
|
+
font-size: 1em;
|
|
417
|
+
font-family: Helvetica;
|
|
418
|
+
font-weight: 500;
|
|
419
|
+
/* font-weight: bold; */
|
|
420
|
+
padding-bottom: 8px;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
.block {
|
|
424
|
+
margin-bottom: 1.5em;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
.pub {
|
|
428
|
+
width: 16rem;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
.icon {
|
|
432
|
+
right: 0px;
|
|
433
|
+
position: absolute;
|
|
434
|
+
top: 10px;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
.icon:hover {
|
|
438
|
+
cursor: pointer;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
.popover-origin-help {
|
|
442
|
+
text-transform: none !important; // need to overide the tooltip text transform
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
.info{
|
|
446
|
+
transform: rotate(180deg);
|
|
447
|
+
color: #8300bf;
|
|
448
|
+
margin-left: 8px;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
.seperator {
|
|
452
|
+
width:90%;
|
|
453
|
+
height:0.5px;
|
|
454
|
+
background-color:#bfbec2;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
.main {
|
|
458
|
+
font-size: 14px;
|
|
459
|
+
text-align: left;
|
|
460
|
+
line-height: 1.5em;
|
|
461
|
+
font-family: Helvetica;
|
|
462
|
+
font-weight: 400;
|
|
463
|
+
/* outline: thin red solid; */
|
|
464
|
+
padding: 1em !important;
|
|
465
|
+
overflow: hidden;
|
|
466
|
+
min-width: 16rem;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
.title{
|
|
470
|
+
font-size: 18px;
|
|
471
|
+
font-weight: 500;
|
|
472
|
+
font-weight: bold;
|
|
473
|
+
padding-bottom: 8px;
|
|
474
|
+
color: rgb(131, 0, 191);
|
|
475
|
+
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
.attribute-title{
|
|
479
|
+
font-size: 16px;
|
|
480
|
+
font-weight: 600;
|
|
481
|
+
/* font-weight: bold; */
|
|
482
|
+
text-transform: uppercase;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
.attribute-content{
|
|
486
|
+
font-size: 14px;
|
|
487
|
+
font-weight: 500;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
.popover-container {
|
|
491
|
+
height: 100%;
|
|
492
|
+
width: 100%;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
.main {
|
|
496
|
+
.el-button.is-round{
|
|
497
|
+
border-radius: 4px;
|
|
498
|
+
padding: 9px 20px 10px 20px;
|
|
499
|
+
display: flex;
|
|
500
|
+
height: 36px;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
.button {
|
|
505
|
+
margin-left: 0px !important;
|
|
506
|
+
margin-top: 0px !important;
|
|
507
|
+
font-size: 14px !important;
|
|
508
|
+
background-color: $app-primary-color;
|
|
509
|
+
color: #fff;
|
|
510
|
+
&+.button {
|
|
511
|
+
margin-top: 10px !important;
|
|
512
|
+
}
|
|
513
|
+
&:hover {
|
|
514
|
+
color: #fff !important;
|
|
515
|
+
background: #ac76c5 !important;
|
|
516
|
+
border: 1px solid #ac76c5 !important;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
.tooltip-container{
|
|
521
|
+
&::after, &::before {
|
|
522
|
+
content: '';
|
|
523
|
+
display: block;
|
|
524
|
+
position: absolute;
|
|
525
|
+
width: 0;
|
|
526
|
+
height: 0;
|
|
527
|
+
border-style: solid;
|
|
528
|
+
flex-shrink: 0;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
.mapboxgl-popup-anchor-bottom {
|
|
533
|
+
.tooltip-container {
|
|
534
|
+
&::after, &::before {
|
|
535
|
+
top: 100%;
|
|
536
|
+
border-width: 12px;
|
|
537
|
+
}
|
|
538
|
+
&::after {
|
|
539
|
+
margin-top:-1px;
|
|
540
|
+
border-color: rgb(255, 255, 255) transparent transparent transparent ;
|
|
541
|
+
}
|
|
542
|
+
&::before {
|
|
543
|
+
margin: 0 auto;
|
|
544
|
+
border-color: $app-primary-color transparent transparent transparent ;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
.mapboxgl-popup-anchor-top {
|
|
550
|
+
.tooltip-container {
|
|
551
|
+
&::after, &::before {
|
|
552
|
+
top: -24px;
|
|
553
|
+
border-width: 12px;
|
|
554
|
+
}
|
|
555
|
+
&::after {
|
|
556
|
+
margin-top: 1px;
|
|
557
|
+
border-color: transparent transparent rgb(255, 255, 255) transparent ;
|
|
558
|
+
}
|
|
559
|
+
&::before {
|
|
560
|
+
margin: 0 auto;
|
|
561
|
+
border-color: transparent transparent $app-primary-color transparent ;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
.content-container {
|
|
567
|
+
overflow-y: scroll;
|
|
568
|
+
scrollbar-width: thin !important;
|
|
569
|
+
height: 200px;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
.scrollbar::-webkit-scrollbar-track {
|
|
573
|
+
border-radius: 10px;
|
|
574
|
+
background-color: #f5f5f5;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
.scrollbar::-webkit-scrollbar {
|
|
578
|
+
width: 12px;
|
|
579
|
+
right: -12px;
|
|
580
|
+
background-color: #f5f5f5;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
.scrollbar::-webkit-scrollbar-thumb {
|
|
584
|
+
border-radius: 4px;
|
|
585
|
+
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.06);
|
|
586
|
+
background-color: #979797;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
|
|
590
|
+
/* Fix for chrome bug where under triangle pops up above one on top of it */
|
|
591
|
+
.selector:not(*:root), .tooltip-container::after{
|
|
592
|
+
top: 99.4%;
|
|
593
|
+
}
|
|
594
|
+
</style>
|