@abi-software/flatmapvuer 0.5.7 → 0.5.8

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.
Files changed (37) hide show
  1. package/CHANGELOG.md +399 -399
  2. package/LICENSE +201 -201
  3. package/README.md +105 -105
  4. package/babel.config.js +14 -14
  5. package/dist/flatmapvuer.common.js +139 -98
  6. package/dist/flatmapvuer.common.js.map +1 -1
  7. package/dist/flatmapvuer.css +1 -1
  8. package/dist/flatmapvuer.umd.js +139 -98
  9. package/dist/flatmapvuer.umd.js.map +1 -1
  10. package/dist/flatmapvuer.umd.min.js +2 -2
  11. package/dist/flatmapvuer.umd.min.js.map +1 -1
  12. package/package-lock.json +14399 -14399
  13. package/package.json +78 -78
  14. package/public/index.html +17 -17
  15. package/src/App.vue +226 -226
  16. package/src/assets/_variables.scss +43 -43
  17. package/src/assets/styles.scss +7 -7
  18. package/src/components/EventBus.js +2 -2
  19. package/src/components/ExternalResourceCard.vue +98 -98
  20. package/src/components/FlatmapVuer.vue +1841 -1841
  21. package/src/components/MultiFlatmapVuer.vue +529 -529
  22. package/src/components/SelectionsGroup.vue +249 -249
  23. package/src/components/Tooltip.vue +447 -417
  24. package/src/components/TreeControls.vue +231 -231
  25. package/src/components/index.js +9 -9
  26. package/src/components/legends/DynamicLegends.vue +112 -112
  27. package/src/components/legends/SvgLegends.vue +66 -66
  28. package/src/icons/fonts/mapicon-species.eot +0 -0
  29. package/src/icons/fonts/mapicon-species.svg +14 -14
  30. package/src/icons/fonts/mapicon-species.ttf +0 -0
  31. package/src/icons/fonts/mapicon-species.woff +0 -0
  32. package/src/icons/mapicon-species-style.css +42 -42
  33. package/src/legends/legend.svg +25 -25
  34. package/src/main.js +8 -8
  35. package/src/nerve-map.js +99 -0
  36. package/src/services/flatmapQueries.js +415 -415
  37. package/vue.config.js +31 -31
@@ -1,1841 +1,1841 @@
1
- <template>
2
- <div
3
- class="flatmap-container"
4
- ref="flatmapContainer"
5
- v-loading="loading"
6
- element-loading-text="Loading..."
7
- element-loading-spinner="el-icon-loading"
8
- element-loading-background="rgba(0, 0, 0, 0.3)"
9
- >
10
- <map-svg-sprite-color />
11
- <div style="height:100%;width:100%;position:relative;overflow-y:none">
12
- <div style="height:100%;width:100%;" ref="display"></div>
13
- <div class="beta-popovers">
14
- <div>
15
- <el-popover
16
- placement="right"
17
- :appendToBody="false"
18
- trigger="manual"
19
- popper-class="warning-popper flatmap-popper right-popper"
20
- v-model="hoverVisibilities[6].value"
21
- ref="warningPopover"
22
- >
23
- <p v-if="isLegacy" @mouseover="showToolitip(6)" @mouseout="hideToolitip(6)">
24
- This is a legacy map, you may view the latest map instead.
25
- </p>
26
- <p v-else-if="isFC" @mouseover="showToolitip(6)" @mouseout="hideToolitip(6)">
27
- This map displays the connectivity of individual neurons.
28
- Specifically, those which align with (parts of) the neuron
29
- populations from the
30
- <a href="https://sparc.science/resources/1ZUKXU2YmLcn2reCyXjlew" target="_blank" >
31
- ApiNATOMY
32
- </a>
33
- models available in
34
- <a href="https://sparc.science/resources/6eg3VpJbwQR4B84CjrvmyD" target="_blank" >
35
- SCKAN
36
- </a>.
37
- </p>
38
- <p v-else @mouseover="showToolitip(6)" @mouseout="hideToolitip(6)">
39
- This map displays the connectivity of neuron populations.
40
- Specifically, those from the primarily rat-based
41
- <a href="https://sparc.science/resources/1ZUKXU2YmLcn2reCyXjlew" target="_blank" >
42
- ApiNATOMY
43
- </a>
44
- models available in
45
- <a href="https://sparc.science/resources/6eg3VpJbwQR4B84CjrvmyD" target="_blank" >
46
- SCKAN
47
- </a>. New connectivity and species
48
- specificity will be added as the SPARC program progresses.
49
- </p>
50
- </el-popover>
51
- <i
52
- class="el-icon-warning warning-icon"
53
- v-if="displayWarning"
54
- @mouseover="showToolitip(6)"
55
- @mouseout="hideToolitip(6)"
56
- v-popover:warningPopover
57
- >
58
- <template v-if="isLegacy">
59
- <span class="warning-text">Legacy Map</span>
60
- <div class="latest-map-text" @click="viewLatestMap">Click here for the latest map</div>
61
- </template>
62
- <template v-else>
63
- <span class="warning-text">Beta</span>
64
- </template>
65
- </i>
66
- </div>
67
- <el-popover
68
- :content="latestChangesMessage"
69
- placement="right"
70
- v-if="displayLatestChanges"
71
- :appendToBody="false"
72
- trigger="manual"
73
- popper-class="warning-popper flatmap-popper right-popper"
74
- v-model="hoverVisibilities[7].value"
75
- ref="latestChangesPopover"
76
- ></el-popover>
77
- <i
78
- class="el-icon-warning latest-changesicon"
79
- v-if="displayLatestChanges && latestChangesMessage"
80
- @mouseover="showToolitip(7)"
81
- @mouseout="hideToolitip(7)"
82
- v-popover:latestChangesPopover
83
- >
84
- <span class="warning-text">What's new?</span>
85
- </i>
86
- </div>
87
-
88
- <!-- The element below is placed onto the flatmap when it is ready -->
89
- <i class="el-icon-arrow-down minimap-resize" :class="{ enlarge: minimapSmall, shrink: !minimapSmall}" ref="minimapResize" v-show="minimapResizeShow" @click="closeMinimap"></i>
90
-
91
- <div class="bottom-right-control">
92
- <el-popover
93
- content="Zoom in"
94
- placement="left"
95
- :appendToBody="false"
96
- trigger="manual"
97
- popper-class="flatmap-popper left-popper"
98
- v-model="hoverVisibilities[0].value"
99
- >
100
- <map-svg-icon
101
- icon="zoomIn"
102
- class="icon-button zoomIn"
103
- slot="reference"
104
- @click.native="zoomIn()"
105
- @mouseover.native="showToolitip(0)"
106
- @mouseout.native="hideToolitip(0)"
107
- />
108
- </el-popover>
109
- <el-popover
110
- content="Zoom out"
111
- placement="top-end"
112
- :appendToBody="false"
113
- trigger="manual"
114
- popper-class="flatmap-popper popper-zoomout"
115
- v-model="hoverVisibilities[1].value"
116
- >
117
- <map-svg-icon
118
- icon="zoomOut"
119
- class="icon-button zoomOut"
120
- slot="reference"
121
- @click.native="zoomOut()"
122
- @mouseover.native="showToolitip(1)"
123
- @mouseout.native="hideToolitip(1)"
124
- />
125
- </el-popover>
126
- <el-popover
127
- content="Reset"
128
- placement="top"
129
- :appendToBody="false"
130
- trigger="manual"
131
- popper-class="flatmap-popper"
132
- v-model="hoverVisibilities[2].value"
133
- >
134
- <div>
135
- Fit to
136
- <br>
137
- window
138
- </div>
139
- <map-svg-icon
140
- slot="reference"
141
- icon="fitWindow"
142
- class="icon-button fitWindow"
143
- @click.native="resetView()"
144
- @mouseover.native="showToolitip(2)"
145
- @mouseout.native="hideToolitip(2)"
146
- />
147
- </el-popover>
148
- </div>
149
- <el-popover
150
- content="Change pathway visibility"
151
- placement="right"
152
- :appendToBody="false"
153
- trigger="manual"
154
- popper-class="flatmap-popper right-popper"
155
- v-model="hoverVisibilities[4].value"
156
- ref="checkBoxPopover"
157
- />
158
- <div class="pathway-location" :class="{ open: drawerOpen, close: !drawerOpen }">
159
- <div
160
- class="pathway-container"
161
- :style="{'max-height': pathwaysMaxHeight + 'px'}"
162
- v-if="pathControls"
163
- v-popover:checkBoxPopover
164
- >
165
- <svg-legends v-if="!isFC" class="svg-legends-container"/>
166
- <el-popover
167
- content="Find these markers for data"
168
- placement="right"
169
- :appendToBody="false"
170
- trigger="manual"
171
- popper-class="flatmap-popper popper-bump-right right-popper"
172
- v-model="hoverVisibilities[5].value"
173
- ref="markerPopover"
174
- ></el-popover>
175
- <div
176
- v-show="hoverVisibilities[5].value"
177
- class="flatmap-marker-help"
178
- v-html="flatmapMarker"
179
- v-popover:markerPopover
180
- ></div>
181
- <tree-controls
182
- v-if="isFC && systems && systems.length > 0"
183
- :active="currentActive"
184
- :hover="currentHover"
185
- :tree-data="systems"
186
- ref="treeControls"
187
- @changed="systemSelected"
188
- @checkAll="checkAllSystems"
189
- @change-active="ftuSelected"
190
- />
191
- <selections-group
192
- v-if="!isFC && centreLines && centreLines.length > 0"
193
- title="Nerves"
194
- labelKey="label"
195
- identifierKey="key"
196
- :selections="centreLines"
197
- @changed="centreLinesSelected"
198
- ref="centrelinesSelection"
199
- key="centrelinesSelection"
200
- />
201
- <!--
202
- <selections-group
203
- v-if="isFC && sckanDisplay && sckanDisplay.length > 0"
204
- title="SCKAN"
205
- labelKey="label"
206
- identifierKey="key"
207
- :selections="sckanDisplay"
208
- @changed="sckanSelected"
209
- @checkAll="checkAllSCKAN"
210
- ref="skcanSelection"
211
- key="skcanSelection"
212
- />
213
- <selections-group
214
- v-if="layers && layers.length > 0"
215
- title="Layers"
216
- labelKey="description"
217
- identifierKey="id"
218
- :selections="layers"
219
- @changed="layersSelected"
220
- @checkAll="checkAllLayers"
221
- ref="layersSelection"
222
- key="layersSelection"
223
- />
224
- -->
225
- <selections-group
226
- v-if="!isFC && taxonConnectivity && taxonConnectivity.length > 0"
227
- title="Observed in"
228
- labelKey="label"
229
- identifierKey="taxon"
230
- :selections="taxonConnectivity"
231
- @changed="taxonsSelected"
232
- @checkAll="checkAllTaxons"
233
- ref="taxonSelection"
234
- key="taxonSelection"
235
- />
236
- <selections-group
237
- v-if="pathways && pathways.length > 0"
238
- title="Pathways"
239
- labelKey="label"
240
- identifierKey="type"
241
- colourStyle="line"
242
- :selections="pathways"
243
- @changed="pathwaysSelected"
244
- @checkAll="checkAllPathways"
245
- ref="pathwaysSelection"
246
- key="pathwaysSelection"
247
- />
248
- </div>
249
- <div
250
- @click="toggleDrawer"
251
- class="drawer-button"
252
- :class="{ open: drawerOpen, close: !drawerOpen }"
253
- >
254
- <i class="el-icon-arrow-left"></i>
255
- </div>
256
- </div>
257
- <el-popover
258
- ref="open-map-popover"
259
- placement="top-start"
260
- width="128"
261
- :append-to-body="false"
262
- trigger="click"
263
- popper-class="open-map-popper non-selectable"
264
- >
265
- <el-row v-for="item in openMapOptions" :key="item.key">
266
- <el-button
267
- type="primary"
268
- plain
269
- @click="$emit('open-map', item.key)"
270
- >
271
- {{item.display}}
272
- </el-button>
273
- </el-row>
274
- </el-popover>
275
- <el-popover
276
- ref="backgroundPopover"
277
- placement="top-start"
278
- width="175"
279
- :appendToBody="false"
280
- trigger="click"
281
- popper-class="background-popper"
282
- >
283
- <el-row class="backgroundText">Organs display</el-row>
284
- <el-row class="backgroundControl">
285
- <el-radio-group v-model="colourRadio" class="flatmap-radio" @change="setColour">
286
- <el-radio :label="true">Colour</el-radio>
287
- <el-radio :label="false">Greyscale</el-radio>
288
- </el-radio-group>
289
- </el-row>
290
- <el-row class="backgroundSpacer"></el-row>
291
- <el-row class="backgroundText">Outlines display</el-row>
292
- <el-row class="backgroundControl">
293
- <el-radio-group v-model="outlinesRadio" class="flatmap-radio" @change="setOutlines">
294
- <el-radio :label="true">Show</el-radio>
295
- <el-radio :label="false">Hide</el-radio>
296
- </el-radio-group>
297
- </el-row>
298
- <el-row class="backgroundSpacer"></el-row>
299
- <el-row class="backgroundText">Change background</el-row>
300
- <el-row class="backgroundControl">
301
- <div
302
- v-for="item in availableBackground"
303
- :key="item"
304
- :class="['backgroundChoice', item, item == currentBackground ? 'active' :'']"
305
- @click="backgroundChangeCallback(item)"
306
- />
307
- </el-row>
308
- </el-popover>
309
- <div
310
- class="settings-group"
311
- :class="{ open: drawerOpen, close: !drawerOpen }"
312
- >
313
- <el-row>
314
- <el-popover
315
- v-model="hoverVisibilities[8].value"
316
- content="Open new map"
317
- placement="right"
318
- :append-to-body="false"
319
- trigger="manual"
320
- popper-class="flatmap-popper right-popper"
321
- >
322
- <map-svg-icon
323
- v-if="enableOpenMapUI && openMapOptions.length > 0"
324
- slot="reference"
325
- v-popover:open-map-popover
326
- icon="openMap"
327
- class="icon-button"
328
- @mouseover.native="showToolitip(8)"
329
- @mouseout.native="hideToolitip(8)"
330
- />
331
- </el-popover>
332
- </el-row>
333
- <el-row>
334
- <el-popover
335
- content="Change settings"
336
- placement="right"
337
- v-model="hoverVisibilities[3].value"
338
- :appendToBody="false"
339
- trigger="manual"
340
- popper-class="flatmap-popper right-popper"
341
- >
342
- <map-svg-icon
343
- v-popover:backgroundPopover
344
- icon="changeBckgd"
345
- class="icon-button"
346
- slot="reference"
347
- @mouseover.native="showToolitip(3)"
348
- @mouseout.native="hideToolitip(3)"
349
- />
350
- </el-popover>
351
- </el-row>
352
- </div>
353
- <Tooltip
354
- ref="tooltip"
355
- class="tooltip"
356
- :entry="tooltipEntry"
357
- />
358
- </div>
359
- </div>
360
- </template>
361
-
362
- <script>
363
- /* eslint-disable no-alert, no-console */
364
- import Vue from "vue";
365
- import Tooltip from "./Tooltip";
366
- import SelectionsGroup from "./SelectionsGroup";
367
- import TreeControls from "./TreeControls";
368
- import { MapSvgIcon, MapSvgSpriteColor } from "@abi-software/svg-sprite";
369
- import SvgLegends from "./legends/SvgLegends";
370
- import {
371
- Button,
372
- Col,
373
- Loading,
374
- Radio,
375
- RadioGroup,
376
- Row
377
- } from "element-ui";
378
- import lang from "element-ui/lib/locale/lang/en";
379
- import locale from "element-ui/lib/locale";
380
- import flatmapMarker from "../icons/flatmap-marker";
381
- import {FlatmapQueries, findTaxonomyLabel} from "../services/flatmapQueries.js";
382
-
383
- locale.use(lang);
384
- Vue.use(Button);
385
- Vue.use(Col);
386
- Vue.use(Loading.directive);
387
- Vue.use(Radio);
388
- Vue.use(RadioGroup);
389
- Vue.use(Row);
390
- const ResizeSensor = require("css-element-queries/src/ResizeSensor");
391
-
392
- const processTaxon = (flatmapAPI, taxonIdentifiers) => {
393
- let processed = [];
394
- taxonIdentifiers.forEach(taxon => {
395
- findTaxonomyLabel(flatmapAPI, taxon).then(value => {
396
- const item = { taxon, label: value};
397
- processed.push(item);
398
- });
399
- });
400
-
401
- return processed;
402
- }
403
-
404
- const processFTUs = (parent, key) => {
405
- const ftus = [];
406
- let items = parent.organs ? parent.organs : parent.ftus;
407
- const children = items ? items.filter(
408
- (obj, index) =>
409
- items.findIndex((item) => item.label === obj.label) === index
410
- ) : undefined
411
- if (children) {
412
- children.forEach(child => {
413
- const data = {
414
- label: child.label,
415
- models: child.models,
416
- key: `${key}.${child.label}`,
417
- };
418
- const grandChildren = processFTUs(child, data.key);
419
- if (grandChildren.length > 0) {
420
- data.children = grandChildren;
421
- }
422
- ftus.push(data);
423
- })
424
- }
425
- return ftus;
426
- }
427
-
428
- const processSystems = systems => {
429
- const allSystems = [];
430
- if (systems && systems.length > 0) {
431
- const data = { label: "All", key: "All", children: [] };
432
- systems.forEach(system => {
433
- const child = {
434
- colour: system.colour,
435
- enabled: system.enabled,
436
- label: system.id,
437
- key: system.id,
438
- };
439
- const children = processFTUs(system, child.key);
440
- if (children.length > 0)
441
- child.children = children;
442
- data.children.push(child);
443
- });
444
-
445
- allSystems.push(data);
446
- }
447
-
448
- return allSystems;
449
- }
450
-
451
- const createUnfilledTooltipData = function (){
452
- return {
453
- destinations: [],
454
- origins: [],
455
- components: [],
456
- destinationsWithDatasets: [],
457
- originsWithDatasets: [],
458
- componentsWithDatasets: [],
459
- resource: undefined
460
- }
461
- }
462
-
463
- export default {
464
- name: "FlatmapVuer",
465
- components: {
466
- MapSvgIcon,
467
- MapSvgSpriteColor,
468
- Tooltip,
469
- TreeControls,
470
- SelectionsGroup,
471
- SvgLegends
472
- },
473
- beforeCreate: function() {
474
- this.mapManager = undefined;
475
- this.mapImp = undefined;
476
- //The state watcher may triggered before
477
- //created causing issue, This flag will
478
- //resolve this issue.
479
- this.setStateRequired = false;
480
- },
481
- methods: {
482
- viewLatestMap: function() {
483
- let biologicalSex = this.biologicalSex ? this.biologicalSex : undefined;
484
- //Human requires special handling
485
- if (this.entry === "NCBITaxon:9606") {
486
- biologicalSex = "PATO:0000384";
487
- }
488
- const state = {
489
- entry: this.entry,
490
- biologicalSex,
491
- viewport: this.mapImp.getState()
492
- };
493
- this.$emit("view-latest-map", state);
494
- },
495
- backgroundChangeCallback: function(colour) {
496
- this.currentBackground = colour;
497
- if (this.mapImp) {
498
- this.mapImp.setBackgroundColour(this.currentBackground, 1);
499
- }
500
- },
501
- toggleDrawer: function() {
502
- this.drawerOpen = !this.drawerOpen;
503
- },
504
- /**
505
- * Function to toggle colour/greyscale of organs.
506
- */
507
- setColour: function(flag) {
508
- this.colourRadio = flag;
509
- if (this.mapImp) {
510
- this.mapImp.setColour({ colour: flag, outline: this.outlinesRadio });
511
- }
512
- },
513
- /**
514
- * Function to toggle outlines f organs.
515
- */
516
- setOutlines: function(flag) {
517
- this.outlineRadio = flag;
518
- if (this.mapImp) {
519
- this.mapImp.setColour({ colour: this.colourRadio, outline: flag });
520
- }
521
- },
522
- /**
523
- * Function to toggle paths to default.
524
- * Also called when the associated button is pressed.
525
- */
526
- resetView: function() {
527
- if (this.mapImp) {
528
- this.mapImp.resetMap();
529
- if (this.$refs.centrelinesSelection) {
530
- this.$refs.centrelinesSelection.reset();
531
- }
532
- if (this.$refs.skcanSelection) {
533
- this.$refs.skcanSelection.reset();
534
- }
535
- if (this.$refs.layersSelection) {
536
- this.$refs.layersSelection.reset();
537
- }
538
- if (this.$refs.systemsSelection) {
539
- this.$refs.pathwaysSelection.reset();
540
- }
541
- if (this.$refs.pathwaysSelection) {
542
- this.$refs.pathwaysSelection.reset();
543
- }
544
- }
545
- },
546
- /**
547
- * Function to zoom in.
548
- * Also called when the associated button is pressed.
549
- */
550
- zoomIn: function() {
551
- if (this.mapImp) {
552
- this.mapImp.zoomIn();
553
- }
554
- },
555
- /**
556
- * Function to zoom out.
557
- * Also called when the associated button is pressed.
558
- */
559
- zoomOut: function() {
560
- if (this.mapImp) {
561
- this.mapImp.zoomOut();
562
- }
563
- },
564
- centreLinesSelected: function(payload) {
565
- if (this.mapImp) {
566
- this.mapImp.enableCentrelines(payload.value);
567
- }
568
- },
569
- sckanSelected: function(payload) {
570
- if (this.mapImp) {
571
- this.mapImp.enableSckanPath(payload.key, payload.value);
572
- }
573
- },
574
- checkAllSCKAN: function(payload) {
575
- if (this.mapImp) {
576
- payload.keys.forEach(key => this.mapImp.enableSckanPath(key, payload.value));
577
- }
578
- },
579
- systemSelected: function(payload) {
580
- if (this.mapImp) {
581
- this.mapImp.enableSystem(payload.key, payload.value);
582
- }
583
- },
584
- checkAllSystems: function(flag) {
585
- if (this.mapImp) {
586
- this.systems[0].children.forEach(key => this.mapImp.enableSystem(key.label, flag));
587
- }
588
- },
589
- ftuSelected: function(models) {
590
- this.searchAndShowResult(models, true);
591
- },
592
- layersSelected: function(payload) {
593
- if (this.mapImp) {
594
- this.mapImp.enableLayer(payload.key, payload.value);
595
- }
596
- },
597
- checkAllLayers: function(payload) {
598
- if (this.mapImp) {
599
- payload.keys.forEach(key => this.mapImp.enableLayer(key, payload.value));
600
- }
601
- },
602
- taxonsSelected: function(payload) {
603
- if (this.mapImp) {
604
- this.mapImp.enableConnectivityByTaxonIds(payload.key, payload.value);
605
- }
606
- },
607
- checkAllTaxons: function(payload) {
608
- if (this.mapImp) {
609
- payload.keys.forEach(key => this.mapImp.enableConnectivityByTaxonIds(key, payload.value));
610
- }
611
- },
612
- pathwaysSelected: function(payload) {
613
- if (this.mapImp) {
614
- this.mapImp.enablePath(payload.key, payload.value);
615
- }
616
- },
617
- checkAllPathways: function(payload) {
618
- if (this.mapImp) {
619
- payload.keys.forEach(key => this.mapImp.enablePath(key, payload.value));
620
- }
621
- },
622
- enablePanZoomEvents: function(flag) {
623
- this.mapImp.enablePanZoomEvents(flag);
624
- },
625
- eventCallback: function() {
626
- return (eventType, data, ...args) => {
627
- if (eventType !== "pan-zoom") {
628
- const label = data.label;
629
- const resource = [data.models];
630
- const taxonomy = this.entry;
631
- const biologicalSex = this.biologicalSex;
632
- const payload = {
633
- dataset: data.dataset,
634
- biologicalSex: biologicalSex,
635
- taxonomy: taxonomy,
636
- resource: resource,
637
- label: label,
638
- feature: data,
639
- userData: args,
640
- eventType: eventType,
641
- provenanceTaxonomy: data.taxons ? JSON.parse(data.taxons) : undefined
642
- };
643
- if (eventType === "click") {
644
- this.currentActive = data.models ? data.models : "";
645
- } else if (eventType === "mouseenter") {
646
- this.currentHover = data.models ? data.models : "";
647
- }
648
- if (data && data.type !== "marker" && eventType === "click"){
649
- this.checkAndCreatePopups(payload);
650
- }
651
- this.$emit("resource-selected", payload);
652
- } else {
653
- this.$emit("pan-zoom-callback", data);
654
- }
655
- };
656
- },
657
- // checkNeuronClicked shows a neuron path pop up if a path was recently clicked
658
- checkAndCreatePopups: async function(data) {
659
- // Call flatmap database to get the connection data
660
- let results = await this.flatmapQueries.retrieveFlatmapKnowledgeForEvent(data)
661
- // The line below only creates the tooltip if some data was found on the path
662
- // result 0 is the connection, result 1 is the pubmed results from flatmap
663
- if(results[0] || results[1] ||( data.feature.hyperlinks && data.feature.hyperlinks.length > 0)){
664
- this.resourceForTooltip = data.resource[0];
665
- this.createTooltipFromNeuronCuration(data);
666
- }
667
- },
668
- popUpCssHacks: function() {
669
- // Below is a hack to remove flatmap tooltips while popup is open
670
- let ftooltip = document.querySelector(".flatmap-tooltip-popup");
671
- if (ftooltip) ftooltip.style.display = "none";
672
- document.querySelector(".maplibregl-popup-close-button").style.display =
673
- "block";
674
- this.$refs.tooltip.$el.style.display = "flex";
675
- document.querySelector(".maplibregl-popup-close-button").onclick = () => {
676
- document.querySelector(".flatmap-tooltip-popup").style.display =
677
- "block";
678
- };
679
- },
680
- createTooltipFromNeuronCuration: async function(data) {
681
- this.tooltipEntry = await this.flatmapQueries.createTooltipData(data);
682
- this.displayTooltip();
683
- },
684
- // Keeping this as an API
685
- showPopup: function(featureId, node, options) {
686
- let myOptions = options;
687
- if (this.mapImp) {
688
- if (myOptions) {
689
- if (!myOptions.className) myOptions.className = "custom-popup";
690
- } else {
691
- myOptions = { className: "custom-popup", positionAtLastClick: true };
692
- }
693
- this.mapImp.showPopup(featureId, node, myOptions);
694
- }
695
- },
696
- showMarkerPopup: function(featureId, node, options) {
697
- if (this.mapImp) {
698
- this.mapImp.showMarkerPopup(featureId, node, options);
699
- }
700
- },
701
- closeMinimap: function(){
702
- let minimapEl = this.$refs.flatmapContainer.querySelector('.maplibregl-ctrl-minimap'); // find minimap
703
- if (this.minimapSmall) { //switch the classes on the minimap
704
- minimapEl.classList.add('enlarge');
705
- minimapEl.classList.remove('shrink');
706
- } else {
707
- minimapEl.classList.add('shrink');
708
- minimapEl.classList.remove('enlarge');
709
- }
710
- this.minimapSmall = !this.minimapSmall;
711
- },
712
- addResizeButtonToMinimap: function(){
713
- let minimapEl = this.$refs.flatmapContainer.querySelector('.maplibregl-ctrl-minimap');
714
- if (minimapEl){
715
- this.$refs.minimapResize.parentNode.removeChild(this.$refs.minimapResize);
716
- minimapEl.appendChild(this.$refs.minimapResize);
717
- this.minimapResizeShow = true;
718
- }
719
- },
720
- setHelpMode: function(helpMode) {
721
- if (helpMode) {
722
- this.inHelp = true;
723
- this.hoverVisibilities.forEach(item => {
724
- item.value = true;
725
- });
726
- this.openFlatmapHelpPopup();
727
- } else {
728
- this.inHelp = false;
729
- this.hoverVisibilities.forEach(item => {
730
- item.value = false;
731
- });
732
- this.closeFlatmapHelpPopup();
733
- }
734
- },
735
- showToolitip: function(tooltipNumber) {
736
- if (!this.inHelp) {
737
- clearTimeout(this.tooltipWait[tooltipNumber]);
738
- this.tooltipWait[tooltipNumber] = setTimeout(() => {
739
- this.hoverVisibilities[tooltipNumber].value = true;
740
- }, 500);
741
- }
742
- },
743
- hideToolitip: function(tooltipNumber) {
744
- if (!this.inHelp) {
745
- clearTimeout(this.tooltipWait[tooltipNumber]);
746
- this.tooltipWait[tooltipNumber] = setTimeout(() => {
747
- this.hoverVisibilities[tooltipNumber].value = false;
748
- }, 500);
749
- }
750
- },
751
- displayTooltip: function() {
752
- this.mapImp.showPopup(
753
- this.mapImp.modelFeatureIds(this.resourceForTooltip)[0],
754
- this.$refs.tooltip.$el,
755
- { className: "flatmapvuer-popover", positionAtLastClick: true }
756
- );
757
- this.popUpCssHacks();
758
- },
759
- openFlatmapHelpPopup: function() {
760
- if (this.mapImp) {
761
- let heartId = this.mapImp.featureIdsForModel("UBERON:0000948")[0];
762
- const elm = "Click for more information";
763
- this.mapImp.showPopup(heartId, elm, {
764
- anchor: "top",
765
- className: "flatmap-popup-popper"
766
- });
767
- }
768
- },
769
- closeFlatmapHelpPopup: function() {
770
- this.$el
771
- .querySelectorAll(".maplibregl-popup-close-button")
772
- .forEach(item => {
773
- item.click();
774
- });
775
- },
776
- getLabels: function() {
777
- let labels = [];
778
- if (this.mapImp) {
779
- let annotations = this.mapImp.annotations;
780
- for (let value of annotations.values()) {
781
- if (value.label) labels.push(value.label);
782
- }
783
- return Array.from(new Set(labels));
784
- }
785
- },
786
- getState: function() {
787
- if (this.mapImp) {
788
- let state = {
789
- entry: this.entry,
790
- viewport: this.mapImp.getState()
791
- };
792
- const identifier = this.mapImp.getIdentifier();
793
- if (this.biologicalSex)
794
- state['biologicalSex'] = this.biologicalSex;
795
- else if (identifier && identifier.biologicalSex)
796
- state['biologicalSex'] = identifier.biologicalSex;
797
- if (identifier && identifier.uuid)
798
- state['uuid'] = identifier.uuid;
799
- return state;
800
- }
801
- return undefined;
802
- },
803
- setState: function(state) {
804
- if (state) {
805
- if (this.mapImp &&
806
- (state.entry && (this.entry == state.entry)) &&
807
- (!state.biologicalSex || (state.biologicalSex === this.biologicalSex)))
808
- {
809
- if (state.viewport) {
810
- this.mapImp.setState(state.viewport);
811
- }
812
- } else {
813
- this.createFlatmap(state);
814
- }
815
- this.setStateRequired = false;
816
- }
817
- },
818
- restoreMapState: function(state) {
819
- if (state) {
820
- if (state.viewport)
821
- this.mapImp.setState(state.viewport);
822
- if (state.searchTerm)
823
- this.searchAndShowResult(state.searchTerm, true);
824
- }
825
- },
826
- createFlatmap: function(state) {
827
- if (!this.mapImp && !this.loading) {
828
- this.loading = true;
829
- let minimap = false;
830
- if (this.displayMinimap) {
831
- minimap = { position: "top-right" };
832
- }
833
-
834
- //As for flatmap-viewer@2.2.7, see below for the documentation
835
- //for the identifier:
836
-
837
- //@arg identifier {string|Object}
838
- // A string or object identifying the map to load. If a string its
839
- // value can be either the map's ``uuid``, assigned at generation time,
840
- // or taxon and biological sex identifiers of the species that the map
841
- // represents. The latest version of a map is loaded unless it has been
842
- // identified using a ``uuid`` (see below).
843
- // @arg identifier.taxon {string} The taxon identifier of the species
844
- // represented by the map. This is specified as metadata in the map's source file.
845
- // @arg identifier.biologicalSex {string} The biological sex of the species
846
- // represented by the map. This is specified as metadatain the map's source file.
847
- // @arg identifier.uuid {string} The unique uuid the flatmap. If given then this exact map will
848
- // be loaded, overriding ``taxon`` and ``biologicalSex``.
849
-
850
- let identifier = { taxon: this.entry };
851
- if (this.uuid) {
852
- identifier.uuid = this.uuid;
853
- }
854
- //This now handle the uses of uuid when resuming states
855
- if (state) {
856
- if (state.uuid) {
857
- identifier = { uuid: state.uuid };
858
- } else if (state.entry) {
859
- identifier.taxon = state.entry;
860
- if (state.biologicalSex) {
861
- identifier["biologicalSex"] = state.biologicalSex;
862
- } else if (identifier.taxon === "NCBITaxon:9606") {
863
- //For backward compatibility
864
- identifier["biologicalSex"] ="PATO:0000384";
865
- }
866
- }
867
- } else {
868
- // Set the bioloicalSex now if map is not resumed from
869
- // a saved state
870
- if (this.biologicalSex) {
871
- identifier["biologicalSex"] = this.biologicalSex;
872
- }
873
- }
874
-
875
- let promise1 = this.mapManager.loadMap(
876
- identifier,
877
- this.$refs.display,
878
- this.eventCallback(),
879
- {
880
- //fullscreenControl: false,
881
- //annotatable: false,
882
- //debug: true,
883
- featureInfo: this.featureInfo,
884
- "min-zoom": this.minZoom,
885
- layerControl: true,
886
- pathControls: true,
887
- searchable: this.searchable,
888
- tooltips: this.tooltips,
889
- minimap: minimap
890
- }
891
- );
892
- promise1.then(returnedObject => {
893
- this.mapImp = returnedObject;
894
- this.onFlatmapReady();
895
- if (this._stateToBeSet)
896
- this.restoreMapState(this._stateToBeSet);
897
- else {
898
- this.restoreMapState(state);
899
- }
900
- });
901
- } else if (state) {
902
- this._stateToBeSet = { viewport: state.viewport, searchTerm: state.searchTerm };
903
- if (this.mapImp && !this.loading)
904
- this.restoreMapState(this._stateToBeSet);
905
- }
906
- },
907
- computePathControlsMaximumHeight() {
908
- const elem = this.$refs.display;
909
- if (elem) {
910
- const computed = getComputedStyle(elem);
911
- const padding = parseInt(computed.paddingTop) + parseInt(computed.paddingBottom);
912
- const height = elem.clientHeight - padding;
913
- this.pathwaysMaxHeight = height - 170;
914
- }
915
- },
916
- mapResize: function() {
917
- try {
918
- this.computePathControlsMaximumHeight();
919
- if (this.mapImp) {
920
- this.mapImp.resize();
921
- this.showMinimap(this.displayMinimap);
922
- if (this.mapImp._minimap) {
923
- this.mapImp._minimap.resize();
924
- }
925
- }
926
- } catch {
927
- console.error("Map resize error");
928
- }
929
- },
930
- onFlatmapReady: function(){
931
- // onFlatmapReady is used for functions that need to run immediately after the flatmap is loaded
932
- this.sensor = new ResizeSensor(
933
- this.$refs.display,
934
- this.mapResize
935
- );
936
- if (this.mapImp.options && this.mapImp.options.style === "functional") {
937
- this.isFC = true;
938
- }
939
- this.mapImp.setBackgroundOpacity(1);
940
- this.backgroundChangeCallback(this.currentBackground);
941
- this.pathways = this.mapImp.pathTypes();
942
- this.mapImp.enableCentrelines(false);
943
- //Disable layers for now
944
- //this.layers = this.mapImp.getLayers();
945
- this.systems = processSystems(this.mapImp.getSystems());
946
- this.taxonConnectivity = processTaxon(this.flatmapAPI, this.mapImp.taxonIdentifiers);
947
- this.addResizeButtonToMinimap();
948
- this.loading = false;
949
- this.computePathControlsMaximumHeight();
950
- this.drawerOpen = true;
951
- this.mapResize();
952
- this.$emit("ready", this);
953
- },
954
- showMinimap: function(flag) {
955
- if (this.mapImp)
956
- this.mapImp.showMinimap(flag);
957
- },
958
- showPathwaysDrawer: function(flag) {
959
- this.drawerOpen = flag;
960
- },
961
- /**
962
- * Function to display features with annotation matching the provided term,
963
- * with the option to display the label using displayLabel flag.
964
- */
965
- searchAndShowResult: function(term, displayLabel) {
966
- if (this.mapImp) {
967
- if (term === undefined || term === "") {
968
- this.mapImp.clearSearchResults();
969
- return true;
970
- } else {
971
- const searchResults = this.mapImp.search(term);
972
- if (searchResults && searchResults.results &&
973
- searchResults.results.length > 0) {
974
- this.mapImp.showSearchResults(searchResults);
975
- if (displayLabel &&
976
- searchResults.results[0].featureId &&
977
- searchResults.results[0].text) {
978
- const annotation = this.mapImp.annotation(searchResults.results[0].featureId);
979
- this.mapImp.showPopup(
980
- searchResults.results[0].featureId,
981
- annotation.label,
982
- { className: "custom-popup", positionAtLastClick: false, preserveSelection: true }
983
- )
984
- }
985
- return true;
986
- }
987
- else
988
- this.mapImp.clearSearchResults();
989
- }
990
- }
991
- return false;
992
- },
993
- /**
994
- * Get the list of suggested terms
995
- */
996
- searchSuggestions: function(term) {
997
- if (this.mapImp)
998
- return this.mapImp.search(term);
999
- return [];
1000
- },
1001
- },
1002
- props: {
1003
- entry: String,
1004
- uuid: String,
1005
- biologicalSex: {
1006
- type: String,
1007
- default: ""
1008
- },
1009
- featureInfo: {
1010
- type: Boolean,
1011
- default: false
1012
- },
1013
- minZoom: {
1014
- type: Number,
1015
- default: 4
1016
- },
1017
- pathControls: {
1018
- type: Boolean,
1019
- default: false
1020
- },
1021
- searchable: {
1022
- type: Boolean,
1023
- default: false
1024
- },
1025
- layerControl: {
1026
- type: Boolean,
1027
- default: false
1028
- },
1029
- tooltips: {
1030
- type: Boolean,
1031
- default: true
1032
- },
1033
- helpMode: {
1034
- type: Boolean,
1035
- default: false
1036
- },
1037
- renderAtMounted: {
1038
- type: Boolean,
1039
- default: true
1040
- },
1041
- displayMinimap: {
1042
- type: Boolean,
1043
- default: false
1044
- },
1045
- displayWarning: {
1046
- type: Boolean,
1047
- default: false
1048
- },
1049
- /**
1050
- * Flag to determine rather open map UI should be
1051
- * presented or not.
1052
- */
1053
- enableOpenMapUI: {
1054
- type: Boolean,
1055
- default: false,
1056
- },
1057
- openMapOptions: {
1058
- type: Array,
1059
- default: function () {
1060
- return [
1061
- {
1062
- display: "Open AC Map",
1063
- key: "AC"
1064
- },
1065
- {
1066
- display: "Open FC Map",
1067
- key: "FC"
1068
- },
1069
- {
1070
- display: "Open 3D Human Map",
1071
- key: "3D"
1072
- },
1073
- ]
1074
- },
1075
- },
1076
- isLegacy: {
1077
- type: Boolean,
1078
- default: false
1079
- },
1080
- displayLatestChanges: {
1081
- type: Boolean,
1082
- default: false,
1083
- },
1084
- latestChangesMessage: {
1085
- type: String,
1086
- default: "'Observed In' controls and information are now included in AC maps. System control with FTU information has been added to the FC map.",
1087
- },
1088
- /**
1089
- * State containing state of the flatmap.
1090
- */
1091
- state: {
1092
- type: Object,
1093
- default: undefined
1094
- },
1095
- /**
1096
- * Specify the endpoint of the flatmap server.
1097
- */
1098
- flatmapAPI: {
1099
- type: String,
1100
- default: "https://mapcore-demo.org/current/flatmap/v3/"
1101
- },
1102
- sparcAPI: {
1103
- type: String,
1104
- default: "https://api.sparc.science/"
1105
- },
1106
- },
1107
- provide() {
1108
- return {
1109
- sparcAPI: this.sparcAPI,
1110
- flatmapAPI: this.flatmapAPI
1111
- }
1112
- },
1113
- data: function() {
1114
- return {
1115
- layers: [],
1116
- pathways: [],
1117
- sckanDisplay: [
1118
- {
1119
- label: "Display Path with SCKAN",
1120
- key: "VALID",
1121
- },
1122
- ],
1123
- centreLines: [
1124
- {
1125
- label: "Display Nerves",
1126
- key: "centrelines",
1127
- enabled: false
1128
- }
1129
- ],
1130
- systems: [],
1131
- taxonConnectivity: [],
1132
- pathwaysMaxHeight: 1000,
1133
- hoverVisibilities: [
1134
- { value: false },
1135
- { value: false },
1136
- { value: false },
1137
- { value: false },
1138
- { value: false },
1139
- { value: false },
1140
- { value: false },
1141
- { value: false },
1142
- { value: false },
1143
- ],
1144
- isFC: false,
1145
- inHelp: false,
1146
- currentBackground: "white",
1147
- availableBackground: ["white", "lightskyblue", "black"],
1148
- loading: false,
1149
- flatmapMarker: flatmapMarker,
1150
- tooltipEntry: createUnfilledTooltipData(),
1151
- connectivityTooltipVisible: false,
1152
- resourceForTooltip: undefined,
1153
- drawerOpen: false,
1154
- colourRadio: true,
1155
- outlinesRadio: true,
1156
- minimapResizeShow: false,
1157
- minimapSmall: false,
1158
- currentActive: "",
1159
- currentHover: "",
1160
- };
1161
- },
1162
- watch: {
1163
- entry: function() {
1164
- if (!this.state) this.createFlatmap();
1165
- },
1166
- helpMode: function(val) {
1167
- this.setHelpMode(val);
1168
- },
1169
- state: {
1170
- handler: function(state) {
1171
- if (this.mapManager) {
1172
- this.setState(state);
1173
- } else {
1174
- //this component has not been mounted yet
1175
- this.setStateRequired = true;
1176
- }
1177
- },
1178
- immediate: true,
1179
- deep: true
1180
- }
1181
- },
1182
- mounted: function() {
1183
- const flatmap = require("@abi-software/flatmap-viewer");
1184
- this.tooltipWait = [];
1185
- this.tooltipWait.length = this.hoverVisibilities.length;
1186
- this.mapManager = new flatmap.MapManager(this.flatmapAPI);
1187
- this.flatmapQueries = new FlatmapQueries();
1188
- this.flatmapQueries.initialise(this.flatmapAPI);
1189
- if (this.state) {
1190
- //State is set and require to set the state
1191
- if (this.setStateRequired) {
1192
- this.setState(this.state);
1193
- }
1194
- } else if(this.renderAtMounted) {
1195
- this.createFlatmap();
1196
- }
1197
- }
1198
- };
1199
- </script>
1200
-
1201
- <!-- Add "scoped" attribute to limit CSS to this component only -->
1202
- <style scoped lang="scss">
1203
- @import "~element-ui/packages/theme-chalk/src/button";
1204
- @import "~element-ui/packages/theme-chalk/src/loading";
1205
- @import "~element-ui/packages/theme-chalk/src/row";
1206
-
1207
- .beta-popovers {
1208
- position: absolute;
1209
- top: 90px;
1210
- left: 16px;
1211
- text-align: left;
1212
- font-size: 25px;
1213
- }
1214
-
1215
- .warning-icon {
1216
- color: $warning;
1217
-
1218
- &:hover {
1219
- cursor: pointer;
1220
- }
1221
- }
1222
-
1223
- .warning-text {
1224
- font-family: Asap, sans-serif;
1225
- font-size: 15px;
1226
- vertical-align: 5px;
1227
- }
1228
-
1229
- .latest-map-text {
1230
- color: $app-primary-color;;
1231
- font-family: Asap, sans-serif;
1232
- font-size: 12px;
1233
- margin-top: 5px;
1234
- vertical-align: 10px;
1235
- cursor: pointer;
1236
- }
1237
-
1238
- .latest-changesicon {
1239
- color: $success;
1240
-
1241
- &:hover {
1242
- cursor: pointer;
1243
- }
1244
- }
1245
-
1246
- .latest-changestext {
1247
- font-family: Asap, sans-serif;
1248
- font-size: 15px;
1249
- vertical-align: 5px;
1250
- }
1251
-
1252
- .flatmap-container {
1253
- height: 100%;
1254
- width: 100%;
1255
- }
1256
-
1257
- .pathway-location {
1258
- position: absolute;
1259
- bottom: 0px;
1260
- transition: all 1s ease;
1261
- &.open {
1262
- left: 0px;
1263
- }
1264
- &.close {
1265
- left: -298px;
1266
- }
1267
- }
1268
-
1269
- .svg-legends-container {
1270
- width:70%;
1271
- height:auto;
1272
- position:relative;
1273
- max-height:140px;
1274
- }
1275
-
1276
- .pathway-container {
1277
- float: left;
1278
- padding-left: 16px;
1279
- padding-right: 18px;
1280
- text-align: left;
1281
- overflow: auto;
1282
- border: 1px solid rgb(220, 223, 230);
1283
- padding-bottom: 16px;
1284
- background: #ffffff;
1285
- overflow-x: hidden;
1286
- scrollbar-width: thin;
1287
-
1288
- &::-webkit-scrollbar {
1289
- width: 4px;
1290
- }
1291
-
1292
- &::-webkit-scrollbar-thumb {
1293
- border-radius: 10px;
1294
- box-shadow: inset 0 0 6px #c0c4cc;
1295
- }
1296
- }
1297
-
1298
- .flatmap-marker-help {
1299
- display: inline-block;
1300
- }
1301
-
1302
- ::v-deep .popper-bump-right {
1303
- left: 30px;
1304
- }
1305
-
1306
- .el-dropdown-link {
1307
- cursor: pointer;
1308
- color: #409eff;
1309
- }
1310
- .el-icon-arrow-down {
1311
- font-size: 12px;
1312
- }
1313
- .demonstration {
1314
- display: block;
1315
- color: #8492a6;
1316
- font-size: 14px;
1317
- margin-bottom: 20px;
1318
- }
1319
-
1320
- .tooltip {
1321
- display: none;
1322
- }
1323
-
1324
- ::v-deep .maplibregl-popup {
1325
- max-width: 300px !important;
1326
- }
1327
-
1328
- ::v-deep .flatmap-tooltip-popup {
1329
- &.maplibregl-popup-anchor-bottom {
1330
- .maplibregl-popup-content {
1331
- margin-bottom: 12px;
1332
- &::after,
1333
- &::before {
1334
- top: 100%;
1335
- border-width: 12px;
1336
- }
1337
- /* this border color controlls the color of the triangle (what looks like the fill of the triangle) */
1338
- &::after {
1339
- margin-top: -1px;
1340
- border-color: rgb(255, 255, 255) transparent transparent transparent;
1341
- }
1342
- /* this border color controlls the outside, thin border */
1343
- &::before {
1344
- margin: 0 auto;
1345
- border-color: $app-primary-color transparent transparent transparent;
1346
- }
1347
- }
1348
- }
1349
- &.maplibregl-popup-anchor-top {
1350
- .maplibregl-popup-content {
1351
- margin-top: 18px;
1352
- &::after,
1353
- &::before {
1354
- top: calc(-100% + 6px);
1355
- border-width: 12px;
1356
- }
1357
- /* this border color controlls the color of the triangle (what looks like the fill of the triangle) */
1358
- &::after {
1359
- margin-top: 1px;
1360
- border-color: transparent transparent rgb(255, 255, 255) transparent;
1361
- }
1362
- &::before {
1363
- margin: 0 auto;
1364
- border-color: transparent transparent $app-primary-color transparent;
1365
- }
1366
- }
1367
- }
1368
- .maplibregl-popup-content {
1369
- border-radius: 4px;
1370
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
1371
- pointer-events: none;
1372
- display: none;
1373
- background: #fff;
1374
- border: 1px solid $app-primary-color;
1375
- padding-left: 6px;
1376
- padding-right: 6px;
1377
- display: flex;
1378
- justify-content: center;
1379
- align-items: center;
1380
- &::after,
1381
- &::before {
1382
- content: "";
1383
- display: block;
1384
- position: absolute;
1385
- width: 0;
1386
- height: 0;
1387
- border-style: solid;
1388
- flex-shrink: 0;
1389
- }
1390
- }
1391
- .maplibregl-popup-tip {
1392
- display: none;
1393
- }
1394
- }
1395
-
1396
- ::v-deep .maplibregl-popup {
1397
- &.flatmap-marker-popup {
1398
- box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.1);
1399
- pointer-events: auto;
1400
- background: #fff;
1401
- }
1402
- }
1403
-
1404
- /* Fix for chrome bug where under triangle pops up above one on top of it */
1405
- .selector:not(*:root),
1406
- ::v-deep.flatmap-tooltip-popup {
1407
- .maplibregl-popup-content::after {
1408
- top: 99.9%;
1409
- }
1410
- }
1411
-
1412
- ::v-deep .flatmap-tooltip-dialog {
1413
- .maplibregl-popup-tip {
1414
- display: none;
1415
- }
1416
- }
1417
-
1418
- ::v-deep .flatmap-marker-popup {
1419
- .maplibregl-popup-content {
1420
- padding: 0px;
1421
- }
1422
- }
1423
-
1424
- ::v-deep .flatmapvuer-popover {
1425
- .maplibregl-popup-close-button {
1426
- position: absolute;
1427
- right: 0.5em;
1428
- top: 0;
1429
- border: 0;
1430
- border-radius: 0 3px 0 0;
1431
- cursor: pointer;
1432
- background-color: transparent;
1433
- font-size: 2.5em;
1434
- color: grey;
1435
- top: 0.95em;
1436
- }
1437
- }
1438
-
1439
- .zoomOut {
1440
- padding-left: 8px;
1441
- }
1442
-
1443
- .fitWindow {
1444
- padding-left: 8px;
1445
- }
1446
-
1447
- .settings-group {
1448
- bottom: 16px;
1449
- position: absolute;
1450
- transition: all 1s ease;
1451
- &.open {
1452
- left: 322px;
1453
- }
1454
- &.close {
1455
- left: 24px;
1456
- }
1457
- }
1458
-
1459
- ::v-deep .background-popper {
1460
- padding: 5px 12px;
1461
- background-color: #ffffff;
1462
- border: 1px solid $app-primary-color;
1463
- box-shadow: 0px 2px 12px 0px rgba(0, 0, 0, 0.06);
1464
- height: 200px;
1465
- width: 175px;
1466
- min-width: 175px;
1467
- &.el-popper[x-placement^="top"] {
1468
- .popper__arrow {
1469
- border-top-color: $app-primary-color !important;
1470
- &::after {
1471
- border-top-color: #fff !important;
1472
- }
1473
- }
1474
- }
1475
- }
1476
-
1477
- ::v-deep .open-map-popper {
1478
- padding-top: 5px;
1479
- padding-bottom: 5px;
1480
- background-color: #ffffff;
1481
- border: 1px solid $app-primary-color;
1482
- box-shadow: 0px 2px 12px 0px rgba(0, 0, 0, 0.06);
1483
- width: 178px;
1484
- min-width: 178px;
1485
-
1486
- .el-row ~ .el-row {
1487
- margin-top: 8px;
1488
- }
1489
-
1490
- .el-button {
1491
- padding-top:5px;
1492
- padding-bottom:5px;
1493
- }
1494
-
1495
- &.el-popper[x-placement^="top"] {
1496
- .popper__arrow {
1497
- border-top-color: $app-primary-color !important;
1498
- &:after {
1499
- border-top-color: #fff !important;
1500
- }
1501
- }
1502
- }
1503
- }
1504
-
1505
- .backgroundText {
1506
- color: rgb(48, 49, 51);
1507
- font-size: 14px;
1508
- font-weight: normal;
1509
- line-height: 20px;
1510
- }
1511
-
1512
- .backgroundControl {
1513
- display: flex;
1514
- margin-top: 16px;
1515
- }
1516
-
1517
- .backgroundChoice {
1518
- width: 20px;
1519
- height: 20px;
1520
- border: 1px solid rgb(144, 147, 153);
1521
- margin-left: 20px;
1522
- &.active {
1523
- border: 2px solid $app-primary-color;
1524
- }
1525
- &:hover {
1526
- cursor: pointer;
1527
- }
1528
- &.white {
1529
- background-color: white;
1530
- margin-left: 10px;
1531
- }
1532
- &.black {
1533
- background-color: black;
1534
- }
1535
- &.lightskyblue {
1536
- background-color: lightskyblue;
1537
- }
1538
- }
1539
-
1540
- .icon-button {
1541
- height: 24px !important;
1542
- width: 24px !important;
1543
- color: $app-primary-color;
1544
- &:hover {
1545
- cursor: pointer;
1546
- }
1547
- }
1548
-
1549
- ::v-deep .maplibregl-ctrl-minimap {
1550
- transform-origin: top right;
1551
- @media (max-width: 1250px) {
1552
- height: 125px !important;// important is needed here as we are over-riding the style set by the flatmap
1553
- width: 180px !important;
1554
- >>> .maplibregl-canvas .maplibregl-canvas {
1555
- height: 125px !important;
1556
- width: 180px !important;
1557
- }
1558
- }
1559
- @media (min-width: 1251px) {
1560
- height: 190px !important;
1561
- width: 300px !important;
1562
- >>> .maplibregl-canvas .maplibregl-canvas {
1563
- height: 190px !important;
1564
- width: 300px !important;
1565
- }
1566
- }
1567
- transition: all 1s ease;
1568
- &.shrink {
1569
- transform: scale(0.5);
1570
- transform: scale(0.5);
1571
- }
1572
- }
1573
-
1574
- .minimap-resize {
1575
- position: absolute;
1576
- pointer-events: all;
1577
- cursor: pointer;
1578
- top: 0;
1579
- right: 0;
1580
- padding-top: 3px; // needed as icon is offset
1581
- width: 20px;
1582
- height: 14px;
1583
- z-index: 9;
1584
- transition: all 1s ease;
1585
- &.shrink {
1586
- transform: rotate(0deg);
1587
- }
1588
- &.enlarge {
1589
- transform: rotate(180deg) scale(2);
1590
- padding-bottom: 5px; // note padding is added to the opposite side since it is rotated
1591
- padding-left: 5px;
1592
- }
1593
- }
1594
-
1595
- ::v-deep .flatmap-popper {
1596
- padding: 6px 4px;
1597
- font-size: 12px;
1598
- color: rgb(48, 49, 51);
1599
- background-color: #f3ecf6;
1600
- border: 1px solid $app-primary-color;
1601
- white-space: nowrap;
1602
- min-width: unset;
1603
- &.warning-popper {
1604
- min-width: 150px;
1605
- max-width: 400px;
1606
- word-break: keep-all;
1607
- white-space: unset;
1608
- }
1609
- &.left-popper {
1610
- .popper__arrow {
1611
- border-left-color: $app-primary-color !important;
1612
- &::after {
1613
- border-left-color: #f3ecf6 !important;
1614
- }
1615
- }
1616
- }
1617
- &.right-popper {
1618
- .popper__arrow {
1619
- border-right-color: $app-primary-color !important;
1620
- &:after {
1621
- border-right-color: #f3ecf6 !important;
1622
- }
1623
- }
1624
- }
1625
- &.el-popper[x-placement^="top"] {
1626
- .popper__arrow {
1627
- border-top-color: $app-primary-color !important;
1628
- &:after {
1629
- border-top-color: #f3ecf6 !important;
1630
- }
1631
- }
1632
- }
1633
- }
1634
-
1635
- ::v-deep .el-loading-spinner {
1636
- i,
1637
- .el-loading-text {
1638
- color: $app-primary-color;
1639
- }
1640
- }
1641
-
1642
- ::v-deep .flatmap-popup-popper {
1643
- .maplibregl-popup-tip {
1644
- border-bottom-color: $app-primary-color;
1645
- }
1646
- .maplibregl-popup-content {
1647
- padding: 6px 4px;
1648
- font-size: 12px;
1649
- color: rgb(48, 49, 51);
1650
- background-color: #f3ecf6;
1651
- border: 1px solid $app-primary-color;
1652
- white-space: nowrap;
1653
- min-width: unset;
1654
- .maplibregl-popup-close-button {
1655
- display: none;
1656
- }
1657
- }
1658
- }
1659
-
1660
- ::v-deep .popper-zoomout {
1661
- padding-right: 13px !important;
1662
- left: -21px !important;
1663
- }
1664
-
1665
- ::v-deep .popper-zoomout {
1666
- .popper__arrow {
1667
- left: 53px !important;
1668
- }
1669
- }
1670
-
1671
- ::v-deep .maplibregl-popup-content {
1672
- padding: 0px;
1673
- }
1674
-
1675
- .bottom-right-control {
1676
- position: absolute;
1677
- right: 16px;
1678
- bottom: 16px;
1679
- }
1680
-
1681
- ::v-deep .my-drawer {
1682
- background: rgba(0, 0, 0, 0);
1683
- box-shadow: none;
1684
- }
1685
-
1686
- .drawer {
1687
- ::v-deep .el-drawer:focus {
1688
- outline: none;
1689
- }
1690
- }
1691
-
1692
- .open-drawer,
1693
- .drawer-button {
1694
- z-index: 8;
1695
- width: 20px;
1696
- height: 40px;
1697
- border: solid 1px $app-primary-color;
1698
- text-align: center;
1699
- vertical-align: middle;
1700
- cursor: pointer;
1701
- pointer-events: auto;
1702
- }
1703
-
1704
- .open-drawer {
1705
- position: absolute;
1706
- left: 0px;
1707
- background-color: #f7faff;
1708
- box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.06);
1709
- }
1710
-
1711
- .drawer-button {
1712
- float: left;
1713
- margin-top: calc(50% - 36px);
1714
- background-color: #F9F2FC;
1715
-
1716
- i {
1717
- font-weight: 600;
1718
- margin-top: 12px;
1719
- color: $app-primary-color;
1720
- transition-delay: 0.9s;
1721
- }
1722
- &.open {
1723
- i {
1724
- transform: rotate(0deg) scaleY(2);
1725
- }
1726
- }
1727
- &.close {
1728
- i {
1729
- transform: rotate(180deg) scaleY(2);
1730
- }
1731
- }
1732
- }
1733
-
1734
- ::v-deep .maplibregl-canvas-container {
1735
- canvas {
1736
- outline: none;
1737
- }
1738
- }
1739
-
1740
- .backgroundSpacer {
1741
- border-bottom: 1px solid #e4e7ed;
1742
- margin-bottom: 10px;
1743
- }
1744
-
1745
- .flatmap-radio {
1746
- ::v-deep label {
1747
- margin-right: 20px;
1748
- &:last-child {
1749
- margin-right: 0px;
1750
- }
1751
- }
1752
- .el-radio__input {
1753
- &.is-checked {
1754
- & + .el-radio__label {
1755
- color: $app-primary-color;
1756
- }
1757
- .el-radio__inner {
1758
- border-color: $app-primary-color;
1759
- background: $app-primary-color;
1760
- }
1761
- }
1762
- }
1763
- }
1764
-
1765
- ::v-deep .custom-popup {
1766
- .maplibregl-popup-tip {
1767
- display: none;
1768
- }
1769
- .maplibregl-popup-content {
1770
- border-radius: 4px;
1771
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
1772
- pointer-events: none;
1773
- display: none;
1774
- background: #fff;
1775
- font-family: "Asap",sans-serif;
1776
- font-size: 12pt;
1777
- color: $app-primary-color;
1778
- border: 1px solid $app-primary-color;
1779
- padding-left: 6px;
1780
- padding-right: 6px;
1781
- padding-top: 6px;
1782
- padding-bottom: 6px;
1783
- display: flex;
1784
- justify-content: center;
1785
- align-items: center;
1786
- &::after,
1787
- &::before {
1788
- content: "";
1789
- display: block;
1790
- position: absolute;
1791
- width: 0;
1792
- height: 0;
1793
- border-style: solid;
1794
- flex-shrink: 0;
1795
- }
1796
- .maplibregl-popup-close-button {
1797
- display: none;
1798
- }
1799
- }
1800
- &.maplibregl-popup-anchor-bottom {
1801
- .maplibregl-popup-content {
1802
- margin-bottom: 12px;
1803
- &::after,
1804
- &::before {
1805
- top: 100%;
1806
- border-width: 12px;
1807
- }
1808
- /* this border color controlls the color of the triangle (what looks like the fill of the triangle) */
1809
- &::after {
1810
- margin-top: -1px;
1811
- border-color: rgb(255, 255, 255) transparent transparent transparent;
1812
- }
1813
- /* this border color controlls the outside, thin border */
1814
- &::before {
1815
- margin: 0 auto;
1816
- border-color: $app-primary-color transparent transparent transparent;
1817
- }
1818
- }
1819
- }
1820
- &.maplibregl-popup-anchor-top {
1821
- .maplibregl-popup-content {
1822
- margin-top: 18px;
1823
- &::after,
1824
- &::before {
1825
- top: calc(-100% + 6px);
1826
- border-width: 12px;
1827
- }
1828
- /* this border color controlls the color of the triangle (what looks like the fill of the triangle) */
1829
- &::after {
1830
- margin-top: 1px;
1831
- border-color: transparent transparent rgb(255, 255, 255) transparent;
1832
- }
1833
- &::before {
1834
- margin: 0 auto;
1835
- border-color: transparent transparent $app-primary-color transparent;
1836
- }
1837
- }
1838
- }
1839
- }
1840
- </style>
1841
-
1
+ <template>
2
+ <div
3
+ class="flatmap-container"
4
+ ref="flatmapContainer"
5
+ v-loading="loading"
6
+ element-loading-text="Loading..."
7
+ element-loading-spinner="el-icon-loading"
8
+ element-loading-background="rgba(0, 0, 0, 0.3)"
9
+ >
10
+ <map-svg-sprite-color />
11
+ <div style="height:100%;width:100%;position:relative;overflow-y:none">
12
+ <div style="height:100%;width:100%;" ref="display"></div>
13
+ <div class="beta-popovers">
14
+ <div>
15
+ <el-popover
16
+ placement="right"
17
+ :appendToBody="false"
18
+ trigger="manual"
19
+ popper-class="warning-popper flatmap-popper right-popper"
20
+ v-model="hoverVisibilities[6].value"
21
+ ref="warningPopover"
22
+ >
23
+ <p v-if="isLegacy" @mouseover="showToolitip(6)" @mouseout="hideToolitip(6)">
24
+ This is a legacy map, you may view the latest map instead.
25
+ </p>
26
+ <p v-else-if="isFC" @mouseover="showToolitip(6)" @mouseout="hideToolitip(6)">
27
+ This map displays the connectivity of individual neurons.
28
+ Specifically, those which align with (parts of) the neuron
29
+ populations from the
30
+ <a href="https://sparc.science/resources/1ZUKXU2YmLcn2reCyXjlew" target="_blank" >
31
+ ApiNATOMY
32
+ </a>
33
+ models available in
34
+ <a href="https://sparc.science/resources/6eg3VpJbwQR4B84CjrvmyD" target="_blank" >
35
+ SCKAN
36
+ </a>.
37
+ </p>
38
+ <p v-else @mouseover="showToolitip(6)" @mouseout="hideToolitip(6)">
39
+ This map displays the connectivity of neuron populations.
40
+ Specifically, those from the primarily rat-based
41
+ <a href="https://sparc.science/resources/1ZUKXU2YmLcn2reCyXjlew" target="_blank" >
42
+ ApiNATOMY
43
+ </a>
44
+ models available in
45
+ <a href="https://sparc.science/resources/6eg3VpJbwQR4B84CjrvmyD" target="_blank" >
46
+ SCKAN
47
+ </a>. New connectivity and species
48
+ specificity will be added as the SPARC program progresses.
49
+ </p>
50
+ </el-popover>
51
+ <i
52
+ class="el-icon-warning warning-icon"
53
+ v-if="displayWarning"
54
+ @mouseover="showToolitip(6)"
55
+ @mouseout="hideToolitip(6)"
56
+ v-popover:warningPopover
57
+ >
58
+ <template v-if="isLegacy">
59
+ <span class="warning-text">Legacy Map</span>
60
+ <div class="latest-map-text" @click="viewLatestMap">Click here for the latest map</div>
61
+ </template>
62
+ <template v-else>
63
+ <span class="warning-text">Beta</span>
64
+ </template>
65
+ </i>
66
+ </div>
67
+ <el-popover
68
+ :content="latestChangesMessage"
69
+ placement="right"
70
+ v-if="displayLatestChanges"
71
+ :appendToBody="false"
72
+ trigger="manual"
73
+ popper-class="warning-popper flatmap-popper right-popper"
74
+ v-model="hoverVisibilities[7].value"
75
+ ref="latestChangesPopover"
76
+ ></el-popover>
77
+ <i
78
+ class="el-icon-warning latest-changesicon"
79
+ v-if="displayLatestChanges && latestChangesMessage"
80
+ @mouseover="showToolitip(7)"
81
+ @mouseout="hideToolitip(7)"
82
+ v-popover:latestChangesPopover
83
+ >
84
+ <span class="warning-text">What's new?</span>
85
+ </i>
86
+ </div>
87
+
88
+ <!-- The element below is placed onto the flatmap when it is ready -->
89
+ <i class="el-icon-arrow-down minimap-resize" :class="{ enlarge: minimapSmall, shrink: !minimapSmall}" ref="minimapResize" v-show="minimapResizeShow" @click="closeMinimap"></i>
90
+
91
+ <div class="bottom-right-control">
92
+ <el-popover
93
+ content="Zoom in"
94
+ placement="left"
95
+ :appendToBody="false"
96
+ trigger="manual"
97
+ popper-class="flatmap-popper left-popper"
98
+ v-model="hoverVisibilities[0].value"
99
+ >
100
+ <map-svg-icon
101
+ icon="zoomIn"
102
+ class="icon-button zoomIn"
103
+ slot="reference"
104
+ @click.native="zoomIn()"
105
+ @mouseover.native="showToolitip(0)"
106
+ @mouseout.native="hideToolitip(0)"
107
+ />
108
+ </el-popover>
109
+ <el-popover
110
+ content="Zoom out"
111
+ placement="top-end"
112
+ :appendToBody="false"
113
+ trigger="manual"
114
+ popper-class="flatmap-popper popper-zoomout"
115
+ v-model="hoverVisibilities[1].value"
116
+ >
117
+ <map-svg-icon
118
+ icon="zoomOut"
119
+ class="icon-button zoomOut"
120
+ slot="reference"
121
+ @click.native="zoomOut()"
122
+ @mouseover.native="showToolitip(1)"
123
+ @mouseout.native="hideToolitip(1)"
124
+ />
125
+ </el-popover>
126
+ <el-popover
127
+ content="Reset"
128
+ placement="top"
129
+ :appendToBody="false"
130
+ trigger="manual"
131
+ popper-class="flatmap-popper"
132
+ v-model="hoverVisibilities[2].value"
133
+ >
134
+ <div>
135
+ Fit to
136
+ <br>
137
+ window
138
+ </div>
139
+ <map-svg-icon
140
+ slot="reference"
141
+ icon="fitWindow"
142
+ class="icon-button fitWindow"
143
+ @click.native="resetView()"
144
+ @mouseover.native="showToolitip(2)"
145
+ @mouseout.native="hideToolitip(2)"
146
+ />
147
+ </el-popover>
148
+ </div>
149
+ <el-popover
150
+ content="Change pathway visibility"
151
+ placement="right"
152
+ :appendToBody="false"
153
+ trigger="manual"
154
+ popper-class="flatmap-popper right-popper"
155
+ v-model="hoverVisibilities[4].value"
156
+ ref="checkBoxPopover"
157
+ />
158
+ <div class="pathway-location" :class="{ open: drawerOpen, close: !drawerOpen }">
159
+ <div
160
+ class="pathway-container"
161
+ :style="{'max-height': pathwaysMaxHeight + 'px'}"
162
+ v-if="pathControls"
163
+ v-popover:checkBoxPopover
164
+ >
165
+ <svg-legends v-if="!isFC" class="svg-legends-container"/>
166
+ <el-popover
167
+ content="Find these markers for data"
168
+ placement="right"
169
+ :appendToBody="false"
170
+ trigger="manual"
171
+ popper-class="flatmap-popper popper-bump-right right-popper"
172
+ v-model="hoverVisibilities[5].value"
173
+ ref="markerPopover"
174
+ ></el-popover>
175
+ <div
176
+ v-show="hoverVisibilities[5].value"
177
+ class="flatmap-marker-help"
178
+ v-html="flatmapMarker"
179
+ v-popover:markerPopover
180
+ ></div>
181
+ <tree-controls
182
+ v-if="isFC && systems && systems.length > 0"
183
+ :active="currentActive"
184
+ :hover="currentHover"
185
+ :tree-data="systems"
186
+ ref="treeControls"
187
+ @changed="systemSelected"
188
+ @checkAll="checkAllSystems"
189
+ @change-active="ftuSelected"
190
+ />
191
+ <selections-group
192
+ v-if="!isFC && centreLines && centreLines.length > 0"
193
+ title="Nerves"
194
+ labelKey="label"
195
+ identifierKey="key"
196
+ :selections="centreLines"
197
+ @changed="centreLinesSelected"
198
+ ref="centrelinesSelection"
199
+ key="centrelinesSelection"
200
+ />
201
+ <!--
202
+ <selections-group
203
+ v-if="isFC && sckanDisplay && sckanDisplay.length > 0"
204
+ title="SCKAN"
205
+ labelKey="label"
206
+ identifierKey="key"
207
+ :selections="sckanDisplay"
208
+ @changed="sckanSelected"
209
+ @checkAll="checkAllSCKAN"
210
+ ref="skcanSelection"
211
+ key="skcanSelection"
212
+ />
213
+ <selections-group
214
+ v-if="layers && layers.length > 0"
215
+ title="Layers"
216
+ labelKey="description"
217
+ identifierKey="id"
218
+ :selections="layers"
219
+ @changed="layersSelected"
220
+ @checkAll="checkAllLayers"
221
+ ref="layersSelection"
222
+ key="layersSelection"
223
+ />
224
+ -->
225
+ <selections-group
226
+ v-if="!isFC && taxonConnectivity && taxonConnectivity.length > 0"
227
+ title="Observed in"
228
+ labelKey="label"
229
+ identifierKey="taxon"
230
+ :selections="taxonConnectivity"
231
+ @changed="taxonsSelected"
232
+ @checkAll="checkAllTaxons"
233
+ ref="taxonSelection"
234
+ key="taxonSelection"
235
+ />
236
+ <selections-group
237
+ v-if="pathways && pathways.length > 0"
238
+ title="Pathways"
239
+ labelKey="label"
240
+ identifierKey="type"
241
+ colourStyle="line"
242
+ :selections="pathways"
243
+ @changed="pathwaysSelected"
244
+ @checkAll="checkAllPathways"
245
+ ref="pathwaysSelection"
246
+ key="pathwaysSelection"
247
+ />
248
+ </div>
249
+ <div
250
+ @click="toggleDrawer"
251
+ class="drawer-button"
252
+ :class="{ open: drawerOpen, close: !drawerOpen }"
253
+ >
254
+ <i class="el-icon-arrow-left"></i>
255
+ </div>
256
+ </div>
257
+ <el-popover
258
+ ref="open-map-popover"
259
+ placement="top-start"
260
+ width="128"
261
+ :append-to-body="false"
262
+ trigger="click"
263
+ popper-class="open-map-popper non-selectable"
264
+ >
265
+ <el-row v-for="item in openMapOptions" :key="item.key">
266
+ <el-button
267
+ type="primary"
268
+ plain
269
+ @click="$emit('open-map', item.key)"
270
+ >
271
+ {{item.display}}
272
+ </el-button>
273
+ </el-row>
274
+ </el-popover>
275
+ <el-popover
276
+ ref="backgroundPopover"
277
+ placement="top-start"
278
+ width="175"
279
+ :appendToBody="false"
280
+ trigger="click"
281
+ popper-class="background-popper"
282
+ >
283
+ <el-row class="backgroundText">Organs display</el-row>
284
+ <el-row class="backgroundControl">
285
+ <el-radio-group v-model="colourRadio" class="flatmap-radio" @change="setColour">
286
+ <el-radio :label="true">Colour</el-radio>
287
+ <el-radio :label="false">Greyscale</el-radio>
288
+ </el-radio-group>
289
+ </el-row>
290
+ <el-row class="backgroundSpacer"></el-row>
291
+ <el-row class="backgroundText">Outlines display</el-row>
292
+ <el-row class="backgroundControl">
293
+ <el-radio-group v-model="outlinesRadio" class="flatmap-radio" @change="setOutlines">
294
+ <el-radio :label="true">Show</el-radio>
295
+ <el-radio :label="false">Hide</el-radio>
296
+ </el-radio-group>
297
+ </el-row>
298
+ <el-row class="backgroundSpacer"></el-row>
299
+ <el-row class="backgroundText">Change background</el-row>
300
+ <el-row class="backgroundControl">
301
+ <div
302
+ v-for="item in availableBackground"
303
+ :key="item"
304
+ :class="['backgroundChoice', item, item == currentBackground ? 'active' :'']"
305
+ @click="backgroundChangeCallback(item)"
306
+ />
307
+ </el-row>
308
+ </el-popover>
309
+ <div
310
+ class="settings-group"
311
+ :class="{ open: drawerOpen, close: !drawerOpen }"
312
+ >
313
+ <el-row>
314
+ <el-popover
315
+ v-model="hoverVisibilities[8].value"
316
+ content="Open new map"
317
+ placement="right"
318
+ :append-to-body="false"
319
+ trigger="manual"
320
+ popper-class="flatmap-popper right-popper"
321
+ >
322
+ <map-svg-icon
323
+ v-if="enableOpenMapUI && openMapOptions.length > 0"
324
+ slot="reference"
325
+ v-popover:open-map-popover
326
+ icon="openMap"
327
+ class="icon-button"
328
+ @mouseover.native="showToolitip(8)"
329
+ @mouseout.native="hideToolitip(8)"
330
+ />
331
+ </el-popover>
332
+ </el-row>
333
+ <el-row>
334
+ <el-popover
335
+ content="Change settings"
336
+ placement="right"
337
+ v-model="hoverVisibilities[3].value"
338
+ :appendToBody="false"
339
+ trigger="manual"
340
+ popper-class="flatmap-popper right-popper"
341
+ >
342
+ <map-svg-icon
343
+ v-popover:backgroundPopover
344
+ icon="changeBckgd"
345
+ class="icon-button"
346
+ slot="reference"
347
+ @mouseover.native="showToolitip(3)"
348
+ @mouseout.native="hideToolitip(3)"
349
+ />
350
+ </el-popover>
351
+ </el-row>
352
+ </div>
353
+ <Tooltip
354
+ ref="tooltip"
355
+ class="tooltip"
356
+ :entry="tooltipEntry"
357
+ />
358
+ </div>
359
+ </div>
360
+ </template>
361
+
362
+ <script>
363
+ /* eslint-disable no-alert, no-console */
364
+ import Vue from "vue";
365
+ import Tooltip from "./Tooltip";
366
+ import SelectionsGroup from "./SelectionsGroup";
367
+ import TreeControls from "./TreeControls";
368
+ import { MapSvgIcon, MapSvgSpriteColor } from "@abi-software/svg-sprite";
369
+ import SvgLegends from "./legends/SvgLegends";
370
+ import {
371
+ Button,
372
+ Col,
373
+ Loading,
374
+ Radio,
375
+ RadioGroup,
376
+ Row
377
+ } from "element-ui";
378
+ import lang from "element-ui/lib/locale/lang/en";
379
+ import locale from "element-ui/lib/locale";
380
+ import flatmapMarker from "../icons/flatmap-marker";
381
+ import {FlatmapQueries, findTaxonomyLabel} from "../services/flatmapQueries.js";
382
+
383
+ locale.use(lang);
384
+ Vue.use(Button);
385
+ Vue.use(Col);
386
+ Vue.use(Loading.directive);
387
+ Vue.use(Radio);
388
+ Vue.use(RadioGroup);
389
+ Vue.use(Row);
390
+ const ResizeSensor = require("css-element-queries/src/ResizeSensor");
391
+
392
+ const processTaxon = (flatmapAPI, taxonIdentifiers) => {
393
+ let processed = [];
394
+ taxonIdentifiers.forEach(taxon => {
395
+ findTaxonomyLabel(flatmapAPI, taxon).then(value => {
396
+ const item = { taxon, label: value};
397
+ processed.push(item);
398
+ });
399
+ });
400
+
401
+ return processed;
402
+ }
403
+
404
+ const processFTUs = (parent, key) => {
405
+ const ftus = [];
406
+ let items = parent.organs ? parent.organs : parent.ftus;
407
+ const children = items ? items.filter(
408
+ (obj, index) =>
409
+ items.findIndex((item) => item.label === obj.label) === index
410
+ ) : undefined
411
+ if (children) {
412
+ children.forEach(child => {
413
+ const data = {
414
+ label: child.label,
415
+ models: child.models,
416
+ key: `${key}.${child.label}`,
417
+ };
418
+ const grandChildren = processFTUs(child, data.key);
419
+ if (grandChildren.length > 0) {
420
+ data.children = grandChildren;
421
+ }
422
+ ftus.push(data);
423
+ })
424
+ }
425
+ return ftus;
426
+ }
427
+
428
+ const processSystems = systems => {
429
+ const allSystems = [];
430
+ if (systems && systems.length > 0) {
431
+ const data = { label: "All", key: "All", children: [] };
432
+ systems.forEach(system => {
433
+ const child = {
434
+ colour: system.colour,
435
+ enabled: system.enabled,
436
+ label: system.id,
437
+ key: system.id,
438
+ };
439
+ const children = processFTUs(system, child.key);
440
+ if (children.length > 0)
441
+ child.children = children;
442
+ data.children.push(child);
443
+ });
444
+
445
+ allSystems.push(data);
446
+ }
447
+
448
+ return allSystems;
449
+ }
450
+
451
+ const createUnfilledTooltipData = function (){
452
+ return {
453
+ destinations: [],
454
+ origins: [],
455
+ components: [],
456
+ destinationsWithDatasets: [],
457
+ originsWithDatasets: [],
458
+ componentsWithDatasets: [],
459
+ resource: undefined
460
+ }
461
+ }
462
+
463
+ export default {
464
+ name: "FlatmapVuer",
465
+ components: {
466
+ MapSvgIcon,
467
+ MapSvgSpriteColor,
468
+ Tooltip,
469
+ TreeControls,
470
+ SelectionsGroup,
471
+ SvgLegends
472
+ },
473
+ beforeCreate: function() {
474
+ this.mapManager = undefined;
475
+ this.mapImp = undefined;
476
+ //The state watcher may triggered before
477
+ //created causing issue, This flag will
478
+ //resolve this issue.
479
+ this.setStateRequired = false;
480
+ },
481
+ methods: {
482
+ viewLatestMap: function() {
483
+ let biologicalSex = this.biologicalSex ? this.biologicalSex : undefined;
484
+ //Human requires special handling
485
+ if (this.entry === "NCBITaxon:9606") {
486
+ biologicalSex = "PATO:0000384";
487
+ }
488
+ const state = {
489
+ entry: this.entry,
490
+ biologicalSex,
491
+ viewport: this.mapImp.getState()
492
+ };
493
+ this.$emit("view-latest-map", state);
494
+ },
495
+ backgroundChangeCallback: function(colour) {
496
+ this.currentBackground = colour;
497
+ if (this.mapImp) {
498
+ this.mapImp.setBackgroundColour(this.currentBackground, 1);
499
+ }
500
+ },
501
+ toggleDrawer: function() {
502
+ this.drawerOpen = !this.drawerOpen;
503
+ },
504
+ /**
505
+ * Function to toggle colour/greyscale of organs.
506
+ */
507
+ setColour: function(flag) {
508
+ this.colourRadio = flag;
509
+ if (this.mapImp) {
510
+ this.mapImp.setColour({ colour: flag, outline: this.outlinesRadio });
511
+ }
512
+ },
513
+ /**
514
+ * Function to toggle outlines f organs.
515
+ */
516
+ setOutlines: function(flag) {
517
+ this.outlineRadio = flag;
518
+ if (this.mapImp) {
519
+ this.mapImp.setColour({ colour: this.colourRadio, outline: flag });
520
+ }
521
+ },
522
+ /**
523
+ * Function to toggle paths to default.
524
+ * Also called when the associated button is pressed.
525
+ */
526
+ resetView: function() {
527
+ if (this.mapImp) {
528
+ this.mapImp.resetMap();
529
+ if (this.$refs.centrelinesSelection) {
530
+ this.$refs.centrelinesSelection.reset();
531
+ }
532
+ if (this.$refs.skcanSelection) {
533
+ this.$refs.skcanSelection.reset();
534
+ }
535
+ if (this.$refs.layersSelection) {
536
+ this.$refs.layersSelection.reset();
537
+ }
538
+ if (this.$refs.systemsSelection) {
539
+ this.$refs.pathwaysSelection.reset();
540
+ }
541
+ if (this.$refs.pathwaysSelection) {
542
+ this.$refs.pathwaysSelection.reset();
543
+ }
544
+ }
545
+ },
546
+ /**
547
+ * Function to zoom in.
548
+ * Also called when the associated button is pressed.
549
+ */
550
+ zoomIn: function() {
551
+ if (this.mapImp) {
552
+ this.mapImp.zoomIn();
553
+ }
554
+ },
555
+ /**
556
+ * Function to zoom out.
557
+ * Also called when the associated button is pressed.
558
+ */
559
+ zoomOut: function() {
560
+ if (this.mapImp) {
561
+ this.mapImp.zoomOut();
562
+ }
563
+ },
564
+ centreLinesSelected: function(payload) {
565
+ if (this.mapImp) {
566
+ this.mapImp.enableCentrelines(payload.value);
567
+ }
568
+ },
569
+ sckanSelected: function(payload) {
570
+ if (this.mapImp) {
571
+ this.mapImp.enableSckanPath(payload.key, payload.value);
572
+ }
573
+ },
574
+ checkAllSCKAN: function(payload) {
575
+ if (this.mapImp) {
576
+ payload.keys.forEach(key => this.mapImp.enableSckanPath(key, payload.value));
577
+ }
578
+ },
579
+ systemSelected: function(payload) {
580
+ if (this.mapImp) {
581
+ this.mapImp.enableSystem(payload.key, payload.value);
582
+ }
583
+ },
584
+ checkAllSystems: function(flag) {
585
+ if (this.mapImp) {
586
+ this.systems[0].children.forEach(key => this.mapImp.enableSystem(key.label, flag));
587
+ }
588
+ },
589
+ ftuSelected: function(models) {
590
+ this.searchAndShowResult(models, true);
591
+ },
592
+ layersSelected: function(payload) {
593
+ if (this.mapImp) {
594
+ this.mapImp.enableLayer(payload.key, payload.value);
595
+ }
596
+ },
597
+ checkAllLayers: function(payload) {
598
+ if (this.mapImp) {
599
+ payload.keys.forEach(key => this.mapImp.enableLayer(key, payload.value));
600
+ }
601
+ },
602
+ taxonsSelected: function(payload) {
603
+ if (this.mapImp) {
604
+ this.mapImp.enableConnectivityByTaxonIds(payload.key, payload.value);
605
+ }
606
+ },
607
+ checkAllTaxons: function(payload) {
608
+ if (this.mapImp) {
609
+ payload.keys.forEach(key => this.mapImp.enableConnectivityByTaxonIds(key, payload.value));
610
+ }
611
+ },
612
+ pathwaysSelected: function(payload) {
613
+ if (this.mapImp) {
614
+ this.mapImp.enablePath(payload.key, payload.value);
615
+ }
616
+ },
617
+ checkAllPathways: function(payload) {
618
+ if (this.mapImp) {
619
+ payload.keys.forEach(key => this.mapImp.enablePath(key, payload.value));
620
+ }
621
+ },
622
+ enablePanZoomEvents: function(flag) {
623
+ this.mapImp.enablePanZoomEvents(flag);
624
+ },
625
+ eventCallback: function() {
626
+ return (eventType, data, ...args) => {
627
+ if (eventType !== "pan-zoom") {
628
+ const label = data.label;
629
+ const resource = [data.models];
630
+ const taxonomy = this.entry;
631
+ const biologicalSex = this.biologicalSex;
632
+ const payload = {
633
+ dataset: data.dataset,
634
+ biologicalSex: biologicalSex,
635
+ taxonomy: taxonomy,
636
+ resource: resource,
637
+ label: label,
638
+ feature: data,
639
+ userData: args,
640
+ eventType: eventType,
641
+ provenanceTaxonomy: data.taxons ? JSON.parse(data.taxons) : undefined
642
+ };
643
+ if (eventType === "click") {
644
+ this.currentActive = data.models ? data.models : "";
645
+ } else if (eventType === "mouseenter") {
646
+ this.currentHover = data.models ? data.models : "";
647
+ }
648
+ if (data && data.type !== "marker" && eventType === "click"){
649
+ this.checkAndCreatePopups(payload);
650
+ }
651
+ this.$emit("resource-selected", payload);
652
+ } else {
653
+ this.$emit("pan-zoom-callback", data);
654
+ }
655
+ };
656
+ },
657
+ // checkNeuronClicked shows a neuron path pop up if a path was recently clicked
658
+ checkAndCreatePopups: async function(data) {
659
+ // Call flatmap database to get the connection data
660
+ let results = await this.flatmapQueries.retrieveFlatmapKnowledgeForEvent(data)
661
+ // The line below only creates the tooltip if some data was found on the path
662
+ // result 0 is the connection, result 1 is the pubmed results from flatmap
663
+ if(results[0] || results[1] ||( data.feature.hyperlinks && data.feature.hyperlinks.length > 0)){
664
+ this.resourceForTooltip = data.resource[0];
665
+ this.createTooltipFromNeuronCuration(data);
666
+ }
667
+ },
668
+ popUpCssHacks: function() {
669
+ // Below is a hack to remove flatmap tooltips while popup is open
670
+ let ftooltip = document.querySelector(".flatmap-tooltip-popup");
671
+ if (ftooltip) ftooltip.style.display = "none";
672
+ document.querySelector(".maplibregl-popup-close-button").style.display =
673
+ "block";
674
+ this.$refs.tooltip.$el.style.display = "flex";
675
+ document.querySelector(".maplibregl-popup-close-button").onclick = () => {
676
+ document.querySelector(".flatmap-tooltip-popup").style.display =
677
+ "block";
678
+ };
679
+ },
680
+ createTooltipFromNeuronCuration: async function(data) {
681
+ this.tooltipEntry = await this.flatmapQueries.createTooltipData(data);
682
+ this.displayTooltip();
683
+ },
684
+ // Keeping this as an API
685
+ showPopup: function(featureId, node, options) {
686
+ let myOptions = options;
687
+ if (this.mapImp) {
688
+ if (myOptions) {
689
+ if (!myOptions.className) myOptions.className = "custom-popup";
690
+ } else {
691
+ myOptions = { className: "custom-popup", positionAtLastClick: true };
692
+ }
693
+ this.mapImp.showPopup(featureId, node, myOptions);
694
+ }
695
+ },
696
+ showMarkerPopup: function(featureId, node, options) {
697
+ if (this.mapImp) {
698
+ this.mapImp.showMarkerPopup(featureId, node, options);
699
+ }
700
+ },
701
+ closeMinimap: function(){
702
+ let minimapEl = this.$refs.flatmapContainer.querySelector('.maplibregl-ctrl-minimap'); // find minimap
703
+ if (this.minimapSmall) { //switch the classes on the minimap
704
+ minimapEl.classList.add('enlarge');
705
+ minimapEl.classList.remove('shrink');
706
+ } else {
707
+ minimapEl.classList.add('shrink');
708
+ minimapEl.classList.remove('enlarge');
709
+ }
710
+ this.minimapSmall = !this.minimapSmall;
711
+ },
712
+ addResizeButtonToMinimap: function(){
713
+ let minimapEl = this.$refs.flatmapContainer.querySelector('.maplibregl-ctrl-minimap');
714
+ if (minimapEl){
715
+ this.$refs.minimapResize.parentNode.removeChild(this.$refs.minimapResize);
716
+ minimapEl.appendChild(this.$refs.minimapResize);
717
+ this.minimapResizeShow = true;
718
+ }
719
+ },
720
+ setHelpMode: function(helpMode) {
721
+ if (helpMode) {
722
+ this.inHelp = true;
723
+ this.hoverVisibilities.forEach(item => {
724
+ item.value = true;
725
+ });
726
+ this.openFlatmapHelpPopup();
727
+ } else {
728
+ this.inHelp = false;
729
+ this.hoverVisibilities.forEach(item => {
730
+ item.value = false;
731
+ });
732
+ this.closeFlatmapHelpPopup();
733
+ }
734
+ },
735
+ showToolitip: function(tooltipNumber) {
736
+ if (!this.inHelp) {
737
+ clearTimeout(this.tooltipWait[tooltipNumber]);
738
+ this.tooltipWait[tooltipNumber] = setTimeout(() => {
739
+ this.hoverVisibilities[tooltipNumber].value = true;
740
+ }, 500);
741
+ }
742
+ },
743
+ hideToolitip: function(tooltipNumber) {
744
+ if (!this.inHelp) {
745
+ clearTimeout(this.tooltipWait[tooltipNumber]);
746
+ this.tooltipWait[tooltipNumber] = setTimeout(() => {
747
+ this.hoverVisibilities[tooltipNumber].value = false;
748
+ }, 500);
749
+ }
750
+ },
751
+ displayTooltip: function() {
752
+ this.mapImp.showPopup(
753
+ this.mapImp.modelFeatureIds(this.resourceForTooltip)[0],
754
+ this.$refs.tooltip.$el,
755
+ { className: "flatmapvuer-popover", positionAtLastClick: true }
756
+ );
757
+ this.popUpCssHacks();
758
+ },
759
+ openFlatmapHelpPopup: function() {
760
+ if (this.mapImp) {
761
+ let heartId = this.mapImp.featureIdsForModel("UBERON:0000948")[0];
762
+ const elm = "Click for more information";
763
+ this.mapImp.showPopup(heartId, elm, {
764
+ anchor: "top",
765
+ className: "flatmap-popup-popper"
766
+ });
767
+ }
768
+ },
769
+ closeFlatmapHelpPopup: function() {
770
+ this.$el
771
+ .querySelectorAll(".maplibregl-popup-close-button")
772
+ .forEach(item => {
773
+ item.click();
774
+ });
775
+ },
776
+ getLabels: function() {
777
+ let labels = [];
778
+ if (this.mapImp) {
779
+ let annotations = this.mapImp.annotations;
780
+ for (let value of annotations.values()) {
781
+ if (value.label) labels.push(value.label);
782
+ }
783
+ return Array.from(new Set(labels));
784
+ }
785
+ },
786
+ getState: function() {
787
+ if (this.mapImp) {
788
+ let state = {
789
+ entry: this.entry,
790
+ viewport: this.mapImp.getState()
791
+ };
792
+ const identifier = this.mapImp.getIdentifier();
793
+ if (this.biologicalSex)
794
+ state['biologicalSex'] = this.biologicalSex;
795
+ else if (identifier && identifier.biologicalSex)
796
+ state['biologicalSex'] = identifier.biologicalSex;
797
+ if (identifier && identifier.uuid)
798
+ state['uuid'] = identifier.uuid;
799
+ return state;
800
+ }
801
+ return undefined;
802
+ },
803
+ setState: function(state) {
804
+ if (state) {
805
+ if (this.mapImp &&
806
+ (state.entry && (this.entry == state.entry)) &&
807
+ (!state.biologicalSex || (state.biologicalSex === this.biologicalSex)))
808
+ {
809
+ if (state.viewport) {
810
+ this.mapImp.setState(state.viewport);
811
+ }
812
+ } else {
813
+ this.createFlatmap(state);
814
+ }
815
+ this.setStateRequired = false;
816
+ }
817
+ },
818
+ restoreMapState: function(state) {
819
+ if (state) {
820
+ if (state.viewport)
821
+ this.mapImp.setState(state.viewport);
822
+ if (state.searchTerm)
823
+ this.searchAndShowResult(state.searchTerm, true);
824
+ }
825
+ },
826
+ createFlatmap: function(state) {
827
+ if (!this.mapImp && !this.loading) {
828
+ this.loading = true;
829
+ let minimap = false;
830
+ if (this.displayMinimap) {
831
+ minimap = { position: "top-right" };
832
+ }
833
+
834
+ //As for flatmap-viewer@2.2.7, see below for the documentation
835
+ //for the identifier:
836
+
837
+ //@arg identifier {string|Object}
838
+ // A string or object identifying the map to load. If a string its
839
+ // value can be either the map's ``uuid``, assigned at generation time,
840
+ // or taxon and biological sex identifiers of the species that the map
841
+ // represents. The latest version of a map is loaded unless it has been
842
+ // identified using a ``uuid`` (see below).
843
+ // @arg identifier.taxon {string} The taxon identifier of the species
844
+ // represented by the map. This is specified as metadata in the map's source file.
845
+ // @arg identifier.biologicalSex {string} The biological sex of the species
846
+ // represented by the map. This is specified as metadatain the map's source file.
847
+ // @arg identifier.uuid {string} The unique uuid the flatmap. If given then this exact map will
848
+ // be loaded, overriding ``taxon`` and ``biologicalSex``.
849
+
850
+ let identifier = { taxon: this.entry };
851
+ if (this.uuid) {
852
+ identifier.uuid = this.uuid;
853
+ }
854
+ //This now handle the uses of uuid when resuming states
855
+ if (state) {
856
+ if (state.uuid) {
857
+ identifier = { uuid: state.uuid };
858
+ } else if (state.entry) {
859
+ identifier.taxon = state.entry;
860
+ if (state.biologicalSex) {
861
+ identifier["biologicalSex"] = state.biologicalSex;
862
+ } else if (identifier.taxon === "NCBITaxon:9606") {
863
+ //For backward compatibility
864
+ identifier["biologicalSex"] ="PATO:0000384";
865
+ }
866
+ }
867
+ } else {
868
+ // Set the bioloicalSex now if map is not resumed from
869
+ // a saved state
870
+ if (this.biologicalSex) {
871
+ identifier["biologicalSex"] = this.biologicalSex;
872
+ }
873
+ }
874
+
875
+ let promise1 = this.mapManager.loadMap(
876
+ identifier,
877
+ this.$refs.display,
878
+ this.eventCallback(),
879
+ {
880
+ //fullscreenControl: false,
881
+ //annotatable: false,
882
+ //debug: true,
883
+ featureInfo: this.featureInfo,
884
+ "min-zoom": this.minZoom,
885
+ layerControl: true,
886
+ pathControls: true,
887
+ searchable: this.searchable,
888
+ tooltips: this.tooltips,
889
+ minimap: minimap
890
+ }
891
+ );
892
+ promise1.then(returnedObject => {
893
+ this.mapImp = returnedObject;
894
+ this.onFlatmapReady();
895
+ if (this._stateToBeSet)
896
+ this.restoreMapState(this._stateToBeSet);
897
+ else {
898
+ this.restoreMapState(state);
899
+ }
900
+ });
901
+ } else if (state) {
902
+ this._stateToBeSet = { viewport: state.viewport, searchTerm: state.searchTerm };
903
+ if (this.mapImp && !this.loading)
904
+ this.restoreMapState(this._stateToBeSet);
905
+ }
906
+ },
907
+ computePathControlsMaximumHeight() {
908
+ const elem = this.$refs.display;
909
+ if (elem) {
910
+ const computed = getComputedStyle(elem);
911
+ const padding = parseInt(computed.paddingTop) + parseInt(computed.paddingBottom);
912
+ const height = elem.clientHeight - padding;
913
+ this.pathwaysMaxHeight = height - 170;
914
+ }
915
+ },
916
+ mapResize: function() {
917
+ try {
918
+ this.computePathControlsMaximumHeight();
919
+ if (this.mapImp) {
920
+ this.mapImp.resize();
921
+ this.showMinimap(this.displayMinimap);
922
+ if (this.mapImp._minimap) {
923
+ this.mapImp._minimap.resize();
924
+ }
925
+ }
926
+ } catch {
927
+ console.error("Map resize error");
928
+ }
929
+ },
930
+ onFlatmapReady: function(){
931
+ // onFlatmapReady is used for functions that need to run immediately after the flatmap is loaded
932
+ this.sensor = new ResizeSensor(
933
+ this.$refs.display,
934
+ this.mapResize
935
+ );
936
+ if (this.mapImp.options && this.mapImp.options.style === "functional") {
937
+ this.isFC = true;
938
+ }
939
+ this.mapImp.setBackgroundOpacity(1);
940
+ this.backgroundChangeCallback(this.currentBackground);
941
+ this.pathways = this.mapImp.pathTypes();
942
+ this.mapImp.enableCentrelines(false);
943
+ //Disable layers for now
944
+ //this.layers = this.mapImp.getLayers();
945
+ this.systems = processSystems(this.mapImp.getSystems());
946
+ this.taxonConnectivity = processTaxon(this.flatmapAPI, this.mapImp.taxonIdentifiers);
947
+ this.addResizeButtonToMinimap();
948
+ this.loading = false;
949
+ this.computePathControlsMaximumHeight();
950
+ this.drawerOpen = true;
951
+ this.mapResize();
952
+ this.$emit("ready", this);
953
+ },
954
+ showMinimap: function(flag) {
955
+ if (this.mapImp)
956
+ this.mapImp.showMinimap(flag);
957
+ },
958
+ showPathwaysDrawer: function(flag) {
959
+ this.drawerOpen = flag;
960
+ },
961
+ /**
962
+ * Function to display features with annotation matching the provided term,
963
+ * with the option to display the label using displayLabel flag.
964
+ */
965
+ searchAndShowResult: function(term, displayLabel) {
966
+ if (this.mapImp) {
967
+ if (term === undefined || term === "") {
968
+ this.mapImp.clearSearchResults();
969
+ return true;
970
+ } else {
971
+ const searchResults = this.mapImp.search(term);
972
+ if (searchResults && searchResults.results &&
973
+ searchResults.results.length > 0) {
974
+ this.mapImp.showSearchResults(searchResults);
975
+ if (displayLabel &&
976
+ searchResults.results[0].featureId &&
977
+ searchResults.results[0].text) {
978
+ const annotation = this.mapImp.annotation(searchResults.results[0].featureId);
979
+ this.mapImp.showPopup(
980
+ searchResults.results[0].featureId,
981
+ annotation.label,
982
+ { className: "custom-popup", positionAtLastClick: false, preserveSelection: true }
983
+ )
984
+ }
985
+ return true;
986
+ }
987
+ else
988
+ this.mapImp.clearSearchResults();
989
+ }
990
+ }
991
+ return false;
992
+ },
993
+ /**
994
+ * Get the list of suggested terms
995
+ */
996
+ searchSuggestions: function(term) {
997
+ if (this.mapImp)
998
+ return this.mapImp.search(term);
999
+ return [];
1000
+ },
1001
+ },
1002
+ props: {
1003
+ entry: String,
1004
+ uuid: String,
1005
+ biologicalSex: {
1006
+ type: String,
1007
+ default: ""
1008
+ },
1009
+ featureInfo: {
1010
+ type: Boolean,
1011
+ default: false
1012
+ },
1013
+ minZoom: {
1014
+ type: Number,
1015
+ default: 4
1016
+ },
1017
+ pathControls: {
1018
+ type: Boolean,
1019
+ default: false
1020
+ },
1021
+ searchable: {
1022
+ type: Boolean,
1023
+ default: false
1024
+ },
1025
+ layerControl: {
1026
+ type: Boolean,
1027
+ default: false
1028
+ },
1029
+ tooltips: {
1030
+ type: Boolean,
1031
+ default: true
1032
+ },
1033
+ helpMode: {
1034
+ type: Boolean,
1035
+ default: false
1036
+ },
1037
+ renderAtMounted: {
1038
+ type: Boolean,
1039
+ default: true
1040
+ },
1041
+ displayMinimap: {
1042
+ type: Boolean,
1043
+ default: false
1044
+ },
1045
+ displayWarning: {
1046
+ type: Boolean,
1047
+ default: false
1048
+ },
1049
+ /**
1050
+ * Flag to determine rather open map UI should be
1051
+ * presented or not.
1052
+ */
1053
+ enableOpenMapUI: {
1054
+ type: Boolean,
1055
+ default: false,
1056
+ },
1057
+ openMapOptions: {
1058
+ type: Array,
1059
+ default: function () {
1060
+ return [
1061
+ {
1062
+ display: "Open AC Map",
1063
+ key: "AC"
1064
+ },
1065
+ {
1066
+ display: "Open FC Map",
1067
+ key: "FC"
1068
+ },
1069
+ {
1070
+ display: "Open 3D Human Map",
1071
+ key: "3D"
1072
+ },
1073
+ ]
1074
+ },
1075
+ },
1076
+ isLegacy: {
1077
+ type: Boolean,
1078
+ default: false
1079
+ },
1080
+ displayLatestChanges: {
1081
+ type: Boolean,
1082
+ default: false,
1083
+ },
1084
+ latestChangesMessage: {
1085
+ type: String,
1086
+ default: "'Observed In' controls and information are now included in AC maps. System control with FTU information has been added to the FC map.",
1087
+ },
1088
+ /**
1089
+ * State containing state of the flatmap.
1090
+ */
1091
+ state: {
1092
+ type: Object,
1093
+ default: undefined
1094
+ },
1095
+ /**
1096
+ * Specify the endpoint of the flatmap server.
1097
+ */
1098
+ flatmapAPI: {
1099
+ type: String,
1100
+ default: "https://mapcore-demo.org/current/flatmap/v3/"
1101
+ },
1102
+ sparcAPI: {
1103
+ type: String,
1104
+ default: "https://api.sparc.science/"
1105
+ },
1106
+ },
1107
+ provide() {
1108
+ return {
1109
+ sparcAPI: this.sparcAPI,
1110
+ flatmapAPI: this.flatmapAPI
1111
+ }
1112
+ },
1113
+ data: function() {
1114
+ return {
1115
+ layers: [],
1116
+ pathways: [],
1117
+ sckanDisplay: [
1118
+ {
1119
+ label: "Display Path with SCKAN",
1120
+ key: "VALID",
1121
+ },
1122
+ ],
1123
+ centreLines: [
1124
+ {
1125
+ label: "Display Nerves",
1126
+ key: "centrelines",
1127
+ enabled: false
1128
+ }
1129
+ ],
1130
+ systems: [],
1131
+ taxonConnectivity: [],
1132
+ pathwaysMaxHeight: 1000,
1133
+ hoverVisibilities: [
1134
+ { value: false },
1135
+ { value: false },
1136
+ { value: false },
1137
+ { value: false },
1138
+ { value: false },
1139
+ { value: false },
1140
+ { value: false },
1141
+ { value: false },
1142
+ { value: false },
1143
+ ],
1144
+ isFC: false,
1145
+ inHelp: false,
1146
+ currentBackground: "white",
1147
+ availableBackground: ["white", "lightskyblue", "black"],
1148
+ loading: false,
1149
+ flatmapMarker: flatmapMarker,
1150
+ tooltipEntry: createUnfilledTooltipData(),
1151
+ connectivityTooltipVisible: false,
1152
+ resourceForTooltip: undefined,
1153
+ drawerOpen: false,
1154
+ colourRadio: true,
1155
+ outlinesRadio: true,
1156
+ minimapResizeShow: false,
1157
+ minimapSmall: false,
1158
+ currentActive: "",
1159
+ currentHover: "",
1160
+ };
1161
+ },
1162
+ watch: {
1163
+ entry: function() {
1164
+ if (!this.state) this.createFlatmap();
1165
+ },
1166
+ helpMode: function(val) {
1167
+ this.setHelpMode(val);
1168
+ },
1169
+ state: {
1170
+ handler: function(state) {
1171
+ if (this.mapManager) {
1172
+ this.setState(state);
1173
+ } else {
1174
+ //this component has not been mounted yet
1175
+ this.setStateRequired = true;
1176
+ }
1177
+ },
1178
+ immediate: true,
1179
+ deep: true
1180
+ }
1181
+ },
1182
+ mounted: function() {
1183
+ const flatmap = require("@abi-software/flatmap-viewer");
1184
+ this.tooltipWait = [];
1185
+ this.tooltipWait.length = this.hoverVisibilities.length;
1186
+ this.mapManager = new flatmap.MapManager(this.flatmapAPI);
1187
+ this.flatmapQueries = new FlatmapQueries();
1188
+ this.flatmapQueries.initialise(this.flatmapAPI);
1189
+ if (this.state) {
1190
+ //State is set and require to set the state
1191
+ if (this.setStateRequired) {
1192
+ this.setState(this.state);
1193
+ }
1194
+ } else if(this.renderAtMounted) {
1195
+ this.createFlatmap();
1196
+ }
1197
+ }
1198
+ };
1199
+ </script>
1200
+
1201
+ <!-- Add "scoped" attribute to limit CSS to this component only -->
1202
+ <style scoped lang="scss">
1203
+ @import "~element-ui/packages/theme-chalk/src/button";
1204
+ @import "~element-ui/packages/theme-chalk/src/loading";
1205
+ @import "~element-ui/packages/theme-chalk/src/row";
1206
+
1207
+ .beta-popovers {
1208
+ position: absolute;
1209
+ top: 90px;
1210
+ left: 16px;
1211
+ text-align: left;
1212
+ font-size: 25px;
1213
+ }
1214
+
1215
+ .warning-icon {
1216
+ color: $warning;
1217
+
1218
+ &:hover {
1219
+ cursor: pointer;
1220
+ }
1221
+ }
1222
+
1223
+ .warning-text {
1224
+ font-family: Asap, sans-serif;
1225
+ font-size: 15px;
1226
+ vertical-align: 5px;
1227
+ }
1228
+
1229
+ .latest-map-text {
1230
+ color: $app-primary-color;;
1231
+ font-family: Asap, sans-serif;
1232
+ font-size: 12px;
1233
+ margin-top: 5px;
1234
+ vertical-align: 10px;
1235
+ cursor: pointer;
1236
+ }
1237
+
1238
+ .latest-changesicon {
1239
+ color: $success;
1240
+
1241
+ &:hover {
1242
+ cursor: pointer;
1243
+ }
1244
+ }
1245
+
1246
+ .latest-changestext {
1247
+ font-family: Asap, sans-serif;
1248
+ font-size: 15px;
1249
+ vertical-align: 5px;
1250
+ }
1251
+
1252
+ .flatmap-container {
1253
+ height: 100%;
1254
+ width: 100%;
1255
+ }
1256
+
1257
+ .pathway-location {
1258
+ position: absolute;
1259
+ bottom: 0px;
1260
+ transition: all 1s ease;
1261
+ &.open {
1262
+ left: 0px;
1263
+ }
1264
+ &.close {
1265
+ left: -298px;
1266
+ }
1267
+ }
1268
+
1269
+ .svg-legends-container {
1270
+ width:70%;
1271
+ height:auto;
1272
+ position:relative;
1273
+ max-height:140px;
1274
+ }
1275
+
1276
+ .pathway-container {
1277
+ float: left;
1278
+ padding-left: 16px;
1279
+ padding-right: 18px;
1280
+ text-align: left;
1281
+ overflow: auto;
1282
+ border: 1px solid rgb(220, 223, 230);
1283
+ padding-bottom: 16px;
1284
+ background: #ffffff;
1285
+ overflow-x: hidden;
1286
+ scrollbar-width: thin;
1287
+
1288
+ &::-webkit-scrollbar {
1289
+ width: 4px;
1290
+ }
1291
+
1292
+ &::-webkit-scrollbar-thumb {
1293
+ border-radius: 10px;
1294
+ box-shadow: inset 0 0 6px #c0c4cc;
1295
+ }
1296
+ }
1297
+
1298
+ .flatmap-marker-help {
1299
+ display: inline-block;
1300
+ }
1301
+
1302
+ ::v-deep .popper-bump-right {
1303
+ left: 30px;
1304
+ }
1305
+
1306
+ .el-dropdown-link {
1307
+ cursor: pointer;
1308
+ color: #409eff;
1309
+ }
1310
+ .el-icon-arrow-down {
1311
+ font-size: 12px;
1312
+ }
1313
+ .demonstration {
1314
+ display: block;
1315
+ color: #8492a6;
1316
+ font-size: 14px;
1317
+ margin-bottom: 20px;
1318
+ }
1319
+
1320
+ .tooltip {
1321
+ display: none;
1322
+ }
1323
+
1324
+ ::v-deep .maplibregl-popup {
1325
+ max-width: 300px !important;
1326
+ }
1327
+
1328
+ ::v-deep .flatmap-tooltip-popup {
1329
+ &.maplibregl-popup-anchor-bottom {
1330
+ .maplibregl-popup-content {
1331
+ margin-bottom: 12px;
1332
+ &::after,
1333
+ &::before {
1334
+ top: 100%;
1335
+ border-width: 12px;
1336
+ }
1337
+ /* this border color controlls the color of the triangle (what looks like the fill of the triangle) */
1338
+ &::after {
1339
+ margin-top: -1px;
1340
+ border-color: rgb(255, 255, 255) transparent transparent transparent;
1341
+ }
1342
+ /* this border color controlls the outside, thin border */
1343
+ &::before {
1344
+ margin: 0 auto;
1345
+ border-color: $app-primary-color transparent transparent transparent;
1346
+ }
1347
+ }
1348
+ }
1349
+ &.maplibregl-popup-anchor-top {
1350
+ .maplibregl-popup-content {
1351
+ margin-top: 18px;
1352
+ &::after,
1353
+ &::before {
1354
+ top: calc(-100% + 6px);
1355
+ border-width: 12px;
1356
+ }
1357
+ /* this border color controlls the color of the triangle (what looks like the fill of the triangle) */
1358
+ &::after {
1359
+ margin-top: 1px;
1360
+ border-color: transparent transparent rgb(255, 255, 255) transparent;
1361
+ }
1362
+ &::before {
1363
+ margin: 0 auto;
1364
+ border-color: transparent transparent $app-primary-color transparent;
1365
+ }
1366
+ }
1367
+ }
1368
+ .maplibregl-popup-content {
1369
+ border-radius: 4px;
1370
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
1371
+ pointer-events: none;
1372
+ display: none;
1373
+ background: #fff;
1374
+ border: 1px solid $app-primary-color;
1375
+ padding-left: 6px;
1376
+ padding-right: 6px;
1377
+ display: flex;
1378
+ justify-content: center;
1379
+ align-items: center;
1380
+ &::after,
1381
+ &::before {
1382
+ content: "";
1383
+ display: block;
1384
+ position: absolute;
1385
+ width: 0;
1386
+ height: 0;
1387
+ border-style: solid;
1388
+ flex-shrink: 0;
1389
+ }
1390
+ }
1391
+ .maplibregl-popup-tip {
1392
+ display: none;
1393
+ }
1394
+ }
1395
+
1396
+ ::v-deep .maplibregl-popup {
1397
+ &.flatmap-marker-popup {
1398
+ box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.1);
1399
+ pointer-events: auto;
1400
+ background: #fff;
1401
+ }
1402
+ }
1403
+
1404
+ /* Fix for chrome bug where under triangle pops up above one on top of it */
1405
+ .selector:not(*:root),
1406
+ ::v-deep.flatmap-tooltip-popup {
1407
+ .maplibregl-popup-content::after {
1408
+ top: 99.9%;
1409
+ }
1410
+ }
1411
+
1412
+ ::v-deep .flatmap-tooltip-dialog {
1413
+ .maplibregl-popup-tip {
1414
+ display: none;
1415
+ }
1416
+ }
1417
+
1418
+ ::v-deep .flatmap-marker-popup {
1419
+ .maplibregl-popup-content {
1420
+ padding: 0px;
1421
+ }
1422
+ }
1423
+
1424
+ ::v-deep .flatmapvuer-popover {
1425
+ .maplibregl-popup-close-button {
1426
+ position: absolute;
1427
+ right: 0.5em;
1428
+ top: 0;
1429
+ border: 0;
1430
+ border-radius: 0 3px 0 0;
1431
+ cursor: pointer;
1432
+ background-color: transparent;
1433
+ font-size: 2.5em;
1434
+ color: grey;
1435
+ top: 0.95em;
1436
+ }
1437
+ }
1438
+
1439
+ .zoomOut {
1440
+ padding-left: 8px;
1441
+ }
1442
+
1443
+ .fitWindow {
1444
+ padding-left: 8px;
1445
+ }
1446
+
1447
+ .settings-group {
1448
+ bottom: 16px;
1449
+ position: absolute;
1450
+ transition: all 1s ease;
1451
+ &.open {
1452
+ left: 322px;
1453
+ }
1454
+ &.close {
1455
+ left: 24px;
1456
+ }
1457
+ }
1458
+
1459
+ ::v-deep .background-popper {
1460
+ padding: 5px 12px;
1461
+ background-color: #ffffff;
1462
+ border: 1px solid $app-primary-color;
1463
+ box-shadow: 0px 2px 12px 0px rgba(0, 0, 0, 0.06);
1464
+ height: 200px;
1465
+ width: 175px;
1466
+ min-width: 175px;
1467
+ &.el-popper[x-placement^="top"] {
1468
+ .popper__arrow {
1469
+ border-top-color: $app-primary-color !important;
1470
+ &::after {
1471
+ border-top-color: #fff !important;
1472
+ }
1473
+ }
1474
+ }
1475
+ }
1476
+
1477
+ ::v-deep .open-map-popper {
1478
+ padding-top: 5px;
1479
+ padding-bottom: 5px;
1480
+ background-color: #ffffff;
1481
+ border: 1px solid $app-primary-color;
1482
+ box-shadow: 0px 2px 12px 0px rgba(0, 0, 0, 0.06);
1483
+ width: 178px;
1484
+ min-width: 178px;
1485
+
1486
+ .el-row ~ .el-row {
1487
+ margin-top: 8px;
1488
+ }
1489
+
1490
+ .el-button {
1491
+ padding-top:5px;
1492
+ padding-bottom:5px;
1493
+ }
1494
+
1495
+ &.el-popper[x-placement^="top"] {
1496
+ .popper__arrow {
1497
+ border-top-color: $app-primary-color !important;
1498
+ &:after {
1499
+ border-top-color: #fff !important;
1500
+ }
1501
+ }
1502
+ }
1503
+ }
1504
+
1505
+ .backgroundText {
1506
+ color: rgb(48, 49, 51);
1507
+ font-size: 14px;
1508
+ font-weight: normal;
1509
+ line-height: 20px;
1510
+ }
1511
+
1512
+ .backgroundControl {
1513
+ display: flex;
1514
+ margin-top: 16px;
1515
+ }
1516
+
1517
+ .backgroundChoice {
1518
+ width: 20px;
1519
+ height: 20px;
1520
+ border: 1px solid rgb(144, 147, 153);
1521
+ margin-left: 20px;
1522
+ &.active {
1523
+ border: 2px solid $app-primary-color;
1524
+ }
1525
+ &:hover {
1526
+ cursor: pointer;
1527
+ }
1528
+ &.white {
1529
+ background-color: white;
1530
+ margin-left: 10px;
1531
+ }
1532
+ &.black {
1533
+ background-color: black;
1534
+ }
1535
+ &.lightskyblue {
1536
+ background-color: lightskyblue;
1537
+ }
1538
+ }
1539
+
1540
+ .icon-button {
1541
+ height: 24px !important;
1542
+ width: 24px !important;
1543
+ color: $app-primary-color;
1544
+ &:hover {
1545
+ cursor: pointer;
1546
+ }
1547
+ }
1548
+
1549
+ ::v-deep .maplibregl-ctrl-minimap {
1550
+ transform-origin: top right;
1551
+ @media (max-width: 1250px) {
1552
+ height: 125px !important;// important is needed here as we are over-riding the style set by the flatmap
1553
+ width: 180px !important;
1554
+ >>> .maplibregl-canvas .maplibregl-canvas {
1555
+ height: 125px !important;
1556
+ width: 180px !important;
1557
+ }
1558
+ }
1559
+ @media (min-width: 1251px) {
1560
+ height: 190px !important;
1561
+ width: 300px !important;
1562
+ >>> .maplibregl-canvas .maplibregl-canvas {
1563
+ height: 190px !important;
1564
+ width: 300px !important;
1565
+ }
1566
+ }
1567
+ transition: all 1s ease;
1568
+ &.shrink {
1569
+ transform: scale(0.5);
1570
+ transform: scale(0.5);
1571
+ }
1572
+ }
1573
+
1574
+ .minimap-resize {
1575
+ position: absolute;
1576
+ pointer-events: all;
1577
+ cursor: pointer;
1578
+ top: 0;
1579
+ right: 0;
1580
+ padding-top: 3px; // needed as icon is offset
1581
+ width: 20px;
1582
+ height: 14px;
1583
+ z-index: 9;
1584
+ transition: all 1s ease;
1585
+ &.shrink {
1586
+ transform: rotate(0deg);
1587
+ }
1588
+ &.enlarge {
1589
+ transform: rotate(180deg) scale(2);
1590
+ padding-bottom: 5px; // note padding is added to the opposite side since it is rotated
1591
+ padding-left: 5px;
1592
+ }
1593
+ }
1594
+
1595
+ ::v-deep .flatmap-popper {
1596
+ padding: 6px 4px;
1597
+ font-size: 12px;
1598
+ color: rgb(48, 49, 51);
1599
+ background-color: #f3ecf6;
1600
+ border: 1px solid $app-primary-color;
1601
+ white-space: nowrap;
1602
+ min-width: unset;
1603
+ &.warning-popper {
1604
+ min-width: 150px;
1605
+ max-width: 400px;
1606
+ word-break: keep-all;
1607
+ white-space: unset;
1608
+ }
1609
+ &.left-popper {
1610
+ .popper__arrow {
1611
+ border-left-color: $app-primary-color !important;
1612
+ &::after {
1613
+ border-left-color: #f3ecf6 !important;
1614
+ }
1615
+ }
1616
+ }
1617
+ &.right-popper {
1618
+ .popper__arrow {
1619
+ border-right-color: $app-primary-color !important;
1620
+ &:after {
1621
+ border-right-color: #f3ecf6 !important;
1622
+ }
1623
+ }
1624
+ }
1625
+ &.el-popper[x-placement^="top"] {
1626
+ .popper__arrow {
1627
+ border-top-color: $app-primary-color !important;
1628
+ &:after {
1629
+ border-top-color: #f3ecf6 !important;
1630
+ }
1631
+ }
1632
+ }
1633
+ }
1634
+
1635
+ ::v-deep .el-loading-spinner {
1636
+ i,
1637
+ .el-loading-text {
1638
+ color: $app-primary-color;
1639
+ }
1640
+ }
1641
+
1642
+ ::v-deep .flatmap-popup-popper {
1643
+ .maplibregl-popup-tip {
1644
+ border-bottom-color: $app-primary-color;
1645
+ }
1646
+ .maplibregl-popup-content {
1647
+ padding: 6px 4px;
1648
+ font-size: 12px;
1649
+ color: rgb(48, 49, 51);
1650
+ background-color: #f3ecf6;
1651
+ border: 1px solid $app-primary-color;
1652
+ white-space: nowrap;
1653
+ min-width: unset;
1654
+ .maplibregl-popup-close-button {
1655
+ display: none;
1656
+ }
1657
+ }
1658
+ }
1659
+
1660
+ ::v-deep .popper-zoomout {
1661
+ padding-right: 13px !important;
1662
+ left: -21px !important;
1663
+ }
1664
+
1665
+ ::v-deep .popper-zoomout {
1666
+ .popper__arrow {
1667
+ left: 53px !important;
1668
+ }
1669
+ }
1670
+
1671
+ ::v-deep .maplibregl-popup-content {
1672
+ padding: 0px;
1673
+ }
1674
+
1675
+ .bottom-right-control {
1676
+ position: absolute;
1677
+ right: 16px;
1678
+ bottom: 16px;
1679
+ }
1680
+
1681
+ ::v-deep .my-drawer {
1682
+ background: rgba(0, 0, 0, 0);
1683
+ box-shadow: none;
1684
+ }
1685
+
1686
+ .drawer {
1687
+ ::v-deep .el-drawer:focus {
1688
+ outline: none;
1689
+ }
1690
+ }
1691
+
1692
+ .open-drawer,
1693
+ .drawer-button {
1694
+ z-index: 8;
1695
+ width: 20px;
1696
+ height: 40px;
1697
+ border: solid 1px $app-primary-color;
1698
+ text-align: center;
1699
+ vertical-align: middle;
1700
+ cursor: pointer;
1701
+ pointer-events: auto;
1702
+ }
1703
+
1704
+ .open-drawer {
1705
+ position: absolute;
1706
+ left: 0px;
1707
+ background-color: #f7faff;
1708
+ box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.06);
1709
+ }
1710
+
1711
+ .drawer-button {
1712
+ float: left;
1713
+ margin-top: calc(50% - 36px);
1714
+ background-color: #F9F2FC;
1715
+
1716
+ i {
1717
+ font-weight: 600;
1718
+ margin-top: 12px;
1719
+ color: $app-primary-color;
1720
+ transition-delay: 0.9s;
1721
+ }
1722
+ &.open {
1723
+ i {
1724
+ transform: rotate(0deg) scaleY(2);
1725
+ }
1726
+ }
1727
+ &.close {
1728
+ i {
1729
+ transform: rotate(180deg) scaleY(2);
1730
+ }
1731
+ }
1732
+ }
1733
+
1734
+ ::v-deep .maplibregl-canvas-container {
1735
+ canvas {
1736
+ outline: none;
1737
+ }
1738
+ }
1739
+
1740
+ .backgroundSpacer {
1741
+ border-bottom: 1px solid #e4e7ed;
1742
+ margin-bottom: 10px;
1743
+ }
1744
+
1745
+ .flatmap-radio {
1746
+ ::v-deep label {
1747
+ margin-right: 20px;
1748
+ &:last-child {
1749
+ margin-right: 0px;
1750
+ }
1751
+ }
1752
+ .el-radio__input {
1753
+ &.is-checked {
1754
+ & + .el-radio__label {
1755
+ color: $app-primary-color;
1756
+ }
1757
+ .el-radio__inner {
1758
+ border-color: $app-primary-color;
1759
+ background: $app-primary-color;
1760
+ }
1761
+ }
1762
+ }
1763
+ }
1764
+
1765
+ ::v-deep .custom-popup {
1766
+ .maplibregl-popup-tip {
1767
+ display: none;
1768
+ }
1769
+ .maplibregl-popup-content {
1770
+ border-radius: 4px;
1771
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
1772
+ pointer-events: none;
1773
+ display: none;
1774
+ background: #fff;
1775
+ font-family: "Asap",sans-serif;
1776
+ font-size: 12pt;
1777
+ color: $app-primary-color;
1778
+ border: 1px solid $app-primary-color;
1779
+ padding-left: 6px;
1780
+ padding-right: 6px;
1781
+ padding-top: 6px;
1782
+ padding-bottom: 6px;
1783
+ display: flex;
1784
+ justify-content: center;
1785
+ align-items: center;
1786
+ &::after,
1787
+ &::before {
1788
+ content: "";
1789
+ display: block;
1790
+ position: absolute;
1791
+ width: 0;
1792
+ height: 0;
1793
+ border-style: solid;
1794
+ flex-shrink: 0;
1795
+ }
1796
+ .maplibregl-popup-close-button {
1797
+ display: none;
1798
+ }
1799
+ }
1800
+ &.maplibregl-popup-anchor-bottom {
1801
+ .maplibregl-popup-content {
1802
+ margin-bottom: 12px;
1803
+ &::after,
1804
+ &::before {
1805
+ top: 100%;
1806
+ border-width: 12px;
1807
+ }
1808
+ /* this border color controlls the color of the triangle (what looks like the fill of the triangle) */
1809
+ &::after {
1810
+ margin-top: -1px;
1811
+ border-color: rgb(255, 255, 255) transparent transparent transparent;
1812
+ }
1813
+ /* this border color controlls the outside, thin border */
1814
+ &::before {
1815
+ margin: 0 auto;
1816
+ border-color: $app-primary-color transparent transparent transparent;
1817
+ }
1818
+ }
1819
+ }
1820
+ &.maplibregl-popup-anchor-top {
1821
+ .maplibregl-popup-content {
1822
+ margin-top: 18px;
1823
+ &::after,
1824
+ &::before {
1825
+ top: calc(-100% + 6px);
1826
+ border-width: 12px;
1827
+ }
1828
+ /* this border color controlls the color of the triangle (what looks like the fill of the triangle) */
1829
+ &::after {
1830
+ margin-top: 1px;
1831
+ border-color: transparent transparent rgb(255, 255, 255) transparent;
1832
+ }
1833
+ &::before {
1834
+ margin: 0 auto;
1835
+ border-color: transparent transparent $app-primary-color transparent;
1836
+ }
1837
+ }
1838
+ }
1839
+ }
1840
+ </style>
1841
+