@abi-software/flatmapvuer 1.0.1 → 1.1.0-beta.1

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.
@@ -20,6 +20,11 @@
20
20
  :visible="hoverVisibilities[6].value"
21
21
  ref="warningPopover"
22
22
  >
23
+ <!--
24
+ What magic meaning do the numbers 6, 7, etc have?
25
+
26
+ Please use `const` to assign meaningful names to them...
27
+ -->
23
28
  <p
24
29
  v-if="isLegacy"
25
30
  @mouseover="showToolitip(6)"
@@ -136,6 +141,128 @@
136
141
  <el-icon-arrow-down />
137
142
  </el-icon>
138
143
 
144
+ <div class="bottom-draw-control"
145
+ v-show="viewingMode === 'Annotation' && userInformation && !disableUI"
146
+ >
147
+ <el-popover
148
+ content="Draw Connection"
149
+ placement="top"
150
+ :teleported="false"
151
+ trigger="manual"
152
+ width="80"
153
+ popper-class="flatmap-popper"
154
+ :visible="hoverVisibilities[10].value"
155
+ >
156
+ <template #reference>
157
+ <map-svg-icon
158
+ icon="connection"
159
+ class="icon-button drawConnection inactive"
160
+ @click="connectionDialogPopup"
161
+ @mouseover="showToolitip(10)"
162
+ @mouseout="hideToolitip(10)"
163
+ />
164
+ </template>
165
+ </el-popover>
166
+ <el-popover
167
+ content="Draw Point"
168
+ placement="top"
169
+ :teleported="false"
170
+ trigger="manual"
171
+ width="80"
172
+ popper-class="flatmap-popper"
173
+ :visible="hoverVisibilities[11].value"
174
+ v-if="drawnType !== 'LineString' && drawnType !== 'Polygon'"
175
+ >
176
+ <template #reference>
177
+ <map-svg-icon
178
+ icon="drawPoint"
179
+ class="icon-button drawPoint"
180
+ @click="drawingEvent('Point')"
181
+ @mouseover="showToolitip(11)"
182
+ @mouseout="hideToolitip(11)"
183
+ />
184
+ </template>
185
+ </el-popover>
186
+ <el-popover
187
+ content="Draw Line"
188
+ placement="top"
189
+ :teleported="false"
190
+ trigger="manual"
191
+ width="80"
192
+ popper-class="flatmap-popper"
193
+ :visible="hoverVisibilities[12].value"
194
+ v-if="drawnType !== 'Point' && drawnType !== 'Polygon'"
195
+ >
196
+ <template #reference>
197
+ <map-svg-icon
198
+ icon="drawLine"
199
+ class="icon-button drawLineString"
200
+ @click="drawingEvent('LineString')"
201
+ @mouseover="showToolitip(12)"
202
+ @mouseout="hideToolitip(12)"
203
+ />
204
+ </template>
205
+ </el-popover>
206
+ <el-popover
207
+ content="Draw Polygon"
208
+ placement="top"
209
+ :teleported="false"
210
+ trigger="manual"
211
+ width="80"
212
+ popper-class="flatmap-popper"
213
+ :visible="hoverVisibilities[13].value"
214
+ v-if="drawnType !== 'Point' && drawnType !== 'LineString'"
215
+ >
216
+ <template #reference>
217
+ <map-svg-icon
218
+ icon="drawPolygon"
219
+ class="icon-button drawPolygon"
220
+ @click="drawingEvent('Polygon')"
221
+ @mouseover="showToolitip(13)"
222
+ @mouseout="hideToolitip(13)"
223
+ />
224
+ </template>
225
+ </el-popover>
226
+ <el-popover
227
+ content="Delete"
228
+ placement="top"
229
+ :teleported="false"
230
+ trigger="manual"
231
+ width="80"
232
+ popper-class="flatmap-popper"
233
+ :visible="hoverVisibilities[14].value"
234
+ >
235
+ <template #reference>
236
+ <map-svg-icon
237
+ icon="drawTrash"
238
+ class="icon-button drawDelete"
239
+ @click="drawingEvent('Delete')"
240
+ @mouseover="showToolitip(14)"
241
+ @mouseout="hideToolitip(14)"
242
+ />
243
+ </template>
244
+ </el-popover>
245
+ <el-popover
246
+ content="Edit"
247
+ placement="top"
248
+ :teleported="false"
249
+ trigger="manual"
250
+ width="80"
251
+ popper-class="flatmap-popper"
252
+ :visible="hoverVisibilities[15].value"
253
+ >
254
+ <template #reference>
255
+ <map-svg-icon
256
+ icon="comment"
257
+ class="icon-button drawEdit"
258
+ @click="drawingEvent('Edit')"
259
+ @mouseover="showToolitip(15)"
260
+ @mouseout="hideToolitip(15)"
261
+ />
262
+ </template>
263
+ </el-popover>
264
+ </div>
265
+
139
266
  <div class="bottom-right-control" v-show="!disableUI">
140
267
  <el-popover
141
268
  content="Zoom in"
@@ -162,7 +289,7 @@
162
289
  :teleported="false"
163
290
  trigger="manual"
164
291
  width="70"
165
- popper-class="flatmap-popper popper-zoomout"
292
+ popper-class="flatmap-popper"
166
293
  :visible="hoverVisibilities[1].value"
167
294
  >
168
295
  <template #reference>
@@ -270,14 +397,15 @@
270
397
  @change-active="ftuSelected"
271
398
  />
272
399
  <selections-group
273
- v-if="!isFC && centreLines && centreLines.length > 0"
274
- title="Nerves"
400
+ v-if="containsAlert && alertOptions"
401
+ title="Alert"
275
402
  labelKey="label"
276
403
  identifierKey="key"
277
- :selections="centreLines"
278
- @changed="centreLinesSelected"
279
- ref="centrelinesSelection"
280
- key="centrelinesSelection"
404
+ :selections="alertOptions"
405
+ @changed="alertSelected"
406
+ @checkboxMouseEnter="alertMouseEnterEmitted"
407
+ ref="alertSelection"
408
+ key="alertSelection"
281
409
  />
282
410
  <!--
283
411
  <selections-group
@@ -303,6 +431,18 @@
303
431
  key="layersSelection"
304
432
  />
305
433
  -->
434
+ <selections-group
435
+ v-if="pathways && pathways.length > 0"
436
+ title="Pathways"
437
+ labelKey="label"
438
+ identifierKey="type"
439
+ colourStyle="line"
440
+ :selections="pathways"
441
+ @changed="pathwaysSelected"
442
+ @checkAll="checkAllPathways"
443
+ ref="pathwaysSelection"
444
+ key="pathwaysSelection"
445
+ />
306
446
  <selections-group
307
447
  v-if="!isFC && taxonConnectivity && taxonConnectivity.length > 0"
308
448
  title="Observed in"
@@ -310,21 +450,20 @@
310
450
  identifierKey="taxon"
311
451
  :selections="taxonConnectivity"
312
452
  @changed="taxonsSelected"
453
+ @checkboxMouseEnter="taxonMouseEnterEmitted"
313
454
  @checkAll="checkAllTaxons"
314
455
  ref="taxonSelection"
315
456
  key="taxonSelection"
316
457
  />
317
458
  <selections-group
318
- v-if="pathways && pathways.length > 0"
319
- title="Pathways"
459
+ v-if="!isFC && centreLines && centreLines.length > 0"
460
+ title="Nerves"
320
461
  labelKey="label"
321
- identifierKey="type"
322
- colourStyle="line"
323
- :selections="pathways"
324
- @changed="pathwaysSelected"
325
- @checkAll="checkAllPathways"
326
- ref="pathwaysSelection"
327
- key="pathwaysSelection"
462
+ identifierKey="key"
463
+ :selections="centreLines"
464
+ @changed="centreLinesSelected"
465
+ ref="centrelinesSelection"
466
+ key="centrelinesSelection"
328
467
  />
329
468
  </div>
330
469
  <div
@@ -394,6 +533,52 @@
394
533
  </el-option>
395
534
  </el-select>
396
535
  </el-row>
536
+ <template v-if="viewingMode === 'Annotation' && userInformation">
537
+ <el-row class="backgroundText">Drawn By*</el-row>
538
+ <el-row class="backgroundControl">
539
+ <el-select
540
+ :teleported="false"
541
+ v-model="drawnType"
542
+ placeholder="Select"
543
+ class="select-box"
544
+ popper-class="flatmap_dropdown"
545
+ @change="setDrawnType"
546
+ >
547
+ <el-option
548
+ v-for="item in drawnTypes"
549
+ :key="item"
550
+ :label="item"
551
+ :value="item"
552
+ >
553
+ <el-row>
554
+ <el-col :span="12">{{ item }}</el-col>
555
+ </el-row>
556
+ </el-option>
557
+ </el-select>
558
+ </el-row>
559
+ <el-row class="backgroundText">Annotated By*</el-row>
560
+ <el-row class="backgroundControl">
561
+ <el-select
562
+ :teleported="false"
563
+ v-model="annotatedType"
564
+ placeholder="Select"
565
+ class="select-box"
566
+ popper-class="flatmap_dropdown"
567
+ @change="setAnnotatedType"
568
+ >
569
+ <el-option
570
+ v-for="item in annotatedTypes"
571
+ :key="item"
572
+ :label="item"
573
+ :value="item"
574
+ >
575
+ <el-row>
576
+ <el-col :span="12">{{ item }}</el-col>
577
+ </el-row>
578
+ </el-option>
579
+ </el-select>
580
+ </el-row>
581
+ </template>
397
582
  <el-row class="backgroundSpacer" v-if="displayFlightPathOption"></el-row>
398
583
  <el-row class="backgroundText" v-if="displayFlightPathOption">Flight path display</el-row>
399
584
  <el-row class="backgroundControl" v-if="displayFlightPathOption">
@@ -499,23 +684,37 @@
499
684
  :annotationEntry="annotationEntry"
500
685
  :entry="tooltipEntry"
501
686
  :annotationDisplay="viewingMode === 'Annotation'"
687
+ @annotation="commitAnnotationEvent"
688
+ />
689
+ <ConnectionDialog
690
+ class="connection-dialog"
691
+ v-show="connectionDisplay"
692
+ :entry="connectionEntry"
693
+ :drawing="inDrawing"
694
+ :connection="connection"
695
+ @display="connectionDialogPopup"
696
+ @confirm="confirmDrawnFeature"
697
+ @cancel="cancelDrawnFeature"
698
+ @popup="closePopup"
699
+ @tooltip="displayConnectedFeatureTooltip"
502
700
  />
503
701
  </div>
504
702
  </div>
505
703
  </template>
506
704
 
507
705
  <script>
508
- import { shallowRef } from 'vue'
706
+ /* eslint-disable no-alert, no-console */
707
+ import { shallowRef, markRaw } from 'vue'
509
708
  import {
510
709
  WarningFilled as ElIconWarningFilled,
511
710
  ArrowDown as ElIconArrowDown,
512
711
  ArrowLeft as ElIconArrowLeft,
513
712
  } from '@element-plus/icons-vue'
514
- /* eslint-disable no-alert, no-console */
515
713
  import Tooltip from './Tooltip.vue'
516
714
  import SelectionsGroup from './SelectionsGroup.vue'
517
715
  import TreeControls from './TreeControls.vue'
518
716
  import { MapSvgIcon, MapSvgSpriteColor } from '@abi-software/svg-sprite'
717
+ import '@abi-software/svg-sprite/dist/style.css'
519
718
  import SvgLegends from './legends/SvgLegends.vue'
520
719
  import {
521
720
  ElButton as Button,
@@ -525,6 +724,7 @@ import {
525
724
  ElRadioGroup as RadioGroup,
526
725
  ElRow as Row,
527
726
  ElSelect as Select,
727
+ ElDialog as Dialog,
528
728
  } from 'element-plus'
529
729
  import flatmapMarker from '../icons/flatmap-marker'
530
730
  import {
@@ -534,9 +734,82 @@ import {
534
734
  import yellowstar from '../icons/yellowstar'
535
735
  import ResizeSensor from 'css-element-queries/src/ResizeSensor'
536
736
  import * as flatmap from '@abi-software/flatmap-viewer'
737
+ import { AnnotationService } from '@abi-software/sparc-annotation'
738
+ import ConnectionDialog from './ConnectionDialog.vue'
537
739
  import { mapState } from 'pinia'
538
740
  import { useMainStore } from '@/store/index'
539
741
 
742
+ /**
743
+ * @param scopeElement Draggable scope area (Optional)
744
+ * @param dragElement Draggable element
745
+ */
746
+ const draggable = (scopeElement, dragElement) => {
747
+ let startX, startY, clickX, clickY, posX, posY
748
+ // reset position in case previous pupped up dialog is dragged
749
+ dragElement.style.left = ''
750
+ dragElement.style.top = ''
751
+ // const scopeRect = scopeElement.getBoundingClientRect()
752
+ // const dragRect = dragElement.getBoundingClientRect()
753
+
754
+ dragElement.addEventListener('mousedown', (e) => {
755
+ e.preventDefault();
756
+ startX = dragElement.offsetLeft
757
+ startY = dragElement.offsetTop
758
+ clickX = e.clientX
759
+ clickY = e.clientY
760
+
761
+ dragElement.addEventListener('mousemove', drag, false);
762
+ document.addEventListener('mouseup', () => {
763
+ dragElement.removeEventListener('mousemove', drag, false);
764
+ }, false);
765
+ }, false);
766
+
767
+ function drag(e) {
768
+ e.preventDefault();
769
+ posX = startX - (clickX - e.clientX)
770
+ posY = startY - (clickY - e.clientY)
771
+ // if (
772
+ // (posX > scopeRect.left && ((posX + dragRect.width) < scopeRect.right)) &&
773
+ // (posY > scopeRect.top && ((posY + dragRect.height) < scopeRect.bottom))
774
+ // ) {
775
+ dragElement.style.left = `${posX}px`;
776
+ dragElement.style.top = `${posY}px`;
777
+ // } else {
778
+ // if (posX <= scopeRect.left) {
779
+ // dragElement.style.left = '0px';
780
+ // } else if (posX + dragRect.width >= scopeRect.right) {
781
+ // dragElement.style.left = `${scopeRect.right - dragRect.width}px`;
782
+ // }
783
+ // if (posY <= scopeRect.top) {
784
+ // dragElement.style.top = '0px';
785
+ // } else if (posY + dragRect.height >= scopeRect.bottom) {
786
+ // dragElement.style.top = `${scopeRect.bottom - dragRect.height}px`;
787
+ // }
788
+ // }
789
+ }
790
+ }
791
+
792
+ const centroid = (geometry) => {
793
+ let featureGeometry = { lng: 0, lat: 0, }
794
+ let coordinates
795
+ if (geometry.type === "Polygon") {
796
+ coordinates = geometry.coordinates[0]
797
+ } else {
798
+ coordinates = geometry.coordinates
799
+ }
800
+ if (!(geometry.type === 'Point')) {
801
+ coordinates.map((coor) => {
802
+ featureGeometry.lng += parseFloat(coor[0])
803
+ featureGeometry.lat += parseFloat(coor[1])
804
+ })
805
+ featureGeometry.lng = featureGeometry.lng / coordinates.length
806
+ featureGeometry.lat = featureGeometry.lat / coordinates.length
807
+ } else {
808
+ featureGeometry.lng += parseFloat(coordinates[0])
809
+ featureGeometry.lat += parseFloat(coordinates[1])
810
+ }
811
+ return featureGeometry
812
+ }
540
813
 
541
814
  const processFTUs = (parent, key) => {
542
815
  const ftus = []
@@ -611,6 +884,7 @@ export default {
611
884
  RadioGroup,
612
885
  Row,
613
886
  Select,
887
+ Dialog,
614
888
  MapSvgIcon,
615
889
  MapSvgSpriteColor,
616
890
  Tooltip,
@@ -629,7 +903,310 @@ export default {
629
903
  //resolve this issue.
630
904
  this.setStateRequired = false
631
905
  },
906
+ setup(props) {
907
+ const annotator = markRaw(new AnnotationService(`${props.flatmapAPI}annotator`));
908
+ return { annotator }
909
+ },
632
910
  methods: {
911
+ /**
912
+ * @vuese
913
+ * Function to initialise drawing.
914
+ */
915
+ initialiseDrawing: function () {
916
+ this.inDrawing = false
917
+ this.initialiseDialog()
918
+ this.activeDrawTool = undefined
919
+ this.createdEvent = undefined
920
+ },
921
+ /**
922
+ * @vuese
923
+ * Function to cancel a newly drawn feature.
924
+ */
925
+ cancelDrawnFeature: function () {
926
+ if (this.createdEvent) {
927
+ this.closePopup()
928
+ this.annotationEntry = {
929
+ ...this.createdEvent.feature,
930
+ resourceId: this.serverURL,
931
+ }
932
+ this.rollbackAnnotationEvent()
933
+ this.initialiseDrawing()
934
+ }
935
+ },
936
+ /**
937
+ * @vuese
938
+ * Function to display connected features' tooltip for drawn connectivity.
939
+ * @arg id
940
+ */
941
+ displayConnectedFeatureTooltip: function (id) {
942
+ if (this.mapImp) {
943
+ const data = this.mapImp.featureProperties(id)
944
+ this.checkAndCreatePopups({ feature: data })
945
+ }
946
+ },
947
+ /**
948
+ * @vuese
949
+ * Function to confirm a newly drawn feature.
950
+ */
951
+ confirmDrawnFeature: function () {
952
+ if (this.createdEvent) {
953
+ this.checkAndCreatePopups(this.createdEvent)
954
+ // Add connection if exist to annotationEntry
955
+ // Connection will only be added in creating new drawn feature annotation
956
+ // And will not be updated if move drawn features
957
+ if (Object.keys(this.connectionEntry).length > 0) {
958
+ this.annotationEntry.feature.connection = this.connectionEntry
959
+ }
960
+ this.initialiseDrawing()
961
+ }
962
+ },
963
+ /**
964
+ * @vuese
965
+ * Function to initialise connection dialog.
966
+ */
967
+ initialiseDialog: function () {
968
+ this.connectionDisplay = false
969
+ this.connectionEntry = {}
970
+ },
971
+ /**
972
+ * @vuese
973
+ * Function to display the connection dialog after finalising a drawing.
974
+ */
975
+ connectionDialogPopup: function () {
976
+ const inactive = this.$el.querySelector('.drawConnection').classList.contains('inactive')
977
+ // disable click popup if icon inactive or in drawing
978
+ if (!inactive && !this.inDrawing) {
979
+ this.closePopup()
980
+ this.connectionDisplay = !this.connectionDisplay
981
+ }
982
+ },
983
+ /**
984
+ * @vuese
985
+ * Function to process the annotation toolbar click events.
986
+ * @arg type
987
+ */
988
+ drawingEvent: function (type) {
989
+ this.closePopup()
990
+ // disable mode icon click if any tool is active
991
+ if (this.drawnTypes.includes(type) && !this.activeDrawMode && !this.connectionDisplay) {
992
+ if (type === 'Point') {
993
+ const point = this.$el.querySelector('.mapbox-gl-draw_point')
994
+ this.$el.querySelector('.mapbox-gl-draw_point').click()
995
+ this.activeDrawTool = point.classList.contains('active') ? 'Point' : undefined
996
+ } else if (type === 'LineString') {
997
+ const line = this.$el.querySelector('.mapbox-gl-draw_line')
998
+ this.$el.querySelector('.mapbox-gl-draw_line').click()
999
+ this.activeDrawTool = line.classList.contains('active') ? 'LineString' : undefined
1000
+ } else if (type === 'Polygon') {
1001
+ const polygon = this.$el.querySelector('.mapbox-gl-draw_polygon')
1002
+ this.$el.querySelector('.mapbox-gl-draw_polygon').click()
1003
+ this.activeDrawTool = polygon.classList.contains('active') ? 'Polygon' : undefined
1004
+ }
1005
+ // disable tool icon click if any mode is on
1006
+ } else if (this.drawModes.includes(type) && !this.activeDrawTool) {
1007
+ if (type === 'Delete') {
1008
+ if (
1009
+ this.currentDrawnFeature &&
1010
+ // For either no mode is on or edit is on
1011
+ (!this.activeDrawMode || this.activeDrawMode === 'Edit')
1012
+ ) {
1013
+ // Force simple_select a feature for delete event
1014
+ this.doubleClickedFeature = false
1015
+ this.changeAnnotationDrawMode({
1016
+ mode: 'simple_select',
1017
+ options: { featureIds: [this.currentDrawnFeature.id] }
1018
+ })
1019
+ this.deleteOrEditAnnotationFeature()
1020
+ }
1021
+ this.activeDrawMode = this.activeDrawMode === 'Delete' ? undefined : 'Delete'
1022
+ // clear currentDrawnFeature when quit delete mode
1023
+ if (!this.activeDrawMode) {
1024
+ this.currentDrawnFeature = undefined
1025
+ }
1026
+ } else if (type === 'Edit') {
1027
+ this.activeDrawMode = this.activeDrawMode === 'Edit' ? undefined : 'Edit'
1028
+ }
1029
+ }
1030
+ },
1031
+ /**
1032
+ * @vuese
1033
+ * Function to update the annotation draw mode.
1034
+ * @arg mode
1035
+ */
1036
+ changeAnnotationDrawMode: function (mode) {
1037
+ if (this.mapImp) {
1038
+ this.mapImp.changeAnnotationDrawMode(mode)
1039
+ }
1040
+ },
1041
+ /**
1042
+ * @vuese
1043
+ * Function to remove all drawn annotations from flatmap annotation layer.
1044
+ */
1045
+ clearAnnotationFeature: function () {
1046
+ if (
1047
+ this.mapImp &&
1048
+ this.drawnAnnotationFeatures &&
1049
+ this.drawnAnnotationFeatures.length > 0
1050
+ ) {
1051
+ this.mapImp.clearAnnotationFeature()
1052
+ }
1053
+ },
1054
+ /**
1055
+ * @vuese
1056
+ * Function to fire the ``trash`` action.
1057
+ * See https://github.com/mapbox/mapbox-gl-draw/blob/main/docs/API.md#trash-draw for more details.
1058
+ */
1059
+ deleteOrEditAnnotationFeature: function () {
1060
+ if (this.mapImp) {
1061
+ // Fire the 'trash' button
1062
+ // Not only use to remove features
1063
+ // 'simple_select' for DELETE and 'direct_select' for EDIT
1064
+ this.mapImp.removeAnnotationFeature()
1065
+ }
1066
+ },
1067
+ /**
1068
+ * @vuese
1069
+ * Function to rollback the failure drawn from flatmap annotation layer.
1070
+ */
1071
+ rollbackAnnotationEvent: function () {
1072
+ // For 'updated' and 'deleted' callback
1073
+ if (
1074
+ this.mapImp &&
1075
+ this.drawnAnnotationEvent.includes(this.annotationEntry.type)
1076
+ ) {
1077
+ this.mapImp.rollbackAnnotationEvent(this.annotationEntry)
1078
+ }
1079
+ },
1080
+ /**
1081
+ * @vuese
1082
+ * Function to commit the emitted ``annotation`` data from successful new drawn to flatmap annotation layer.
1083
+ * @arg annotation
1084
+ */
1085
+ commitAnnotationEvent: function (annotation) {
1086
+ if (
1087
+ this.mapImp &&
1088
+ this.drawnAnnotationEvent.includes(this.annotationEntry.type) &&
1089
+ // Only when annotation comments stored successfully
1090
+ annotation
1091
+ ) {
1092
+ this.annotationSubmitted = true
1093
+ this.mapImp.commitAnnotationEvent(this.annotationEntry)
1094
+ if (this.annotationEntry.type === 'deleted') {
1095
+ this.closePopup()
1096
+ } else {
1097
+ // Use to update 'this.drawnAnnotationFeatures' when created or updated
1098
+ this.addAnnotationFeature()
1099
+ }
1100
+ }
1101
+ },
1102
+ /**
1103
+ * @vuese
1104
+ * Function to add existing drawn annotations to flatmap.
1105
+ */
1106
+ setFeatureAnnotated: function () {
1107
+ if (this.mapImp) {
1108
+ this.annotator.annotatedItemIds(this.userToken, this.serverURL)
1109
+ .then((annotatedItemIds) => {
1110
+ if ('resource' in annotatedItemIds) {
1111
+ // The annotator has `resource` and `items` fields
1112
+ annotatedItemIds = annotatedItemIds.itemIds
1113
+ }
1114
+ for (const id of annotatedItemIds) {
1115
+ this.mapImp.setFeatureAnnotated(id)
1116
+ }
1117
+ })
1118
+ .catch((reason) => {
1119
+ console.log(reason) // Error!
1120
+ })
1121
+ }
1122
+ },
1123
+ /**
1124
+ * @vuese
1125
+ * Function to draw existing drawn annotations based on selector.
1126
+ */
1127
+ addAnnotationFeature: function () {
1128
+ if (this.mapImp) {
1129
+ if (!this.annotationSubmitted) this.clearAnnotationFeature()
1130
+ if (this.drawnType !== 'None') {
1131
+ if (!this.annotationSubmitted) this.loading = true
1132
+ const userId = this.annotatedType === 'Anyone' ?
1133
+ undefined : this.userInformation.orcid ?
1134
+ this.userInformation.orcid : '0000-0000-0000-0000'
1135
+ const participated = this.annotatedType === 'Anyone' ?
1136
+ undefined : this.annotatedType === 'Me' ?
1137
+ true : false
1138
+ this.annotator.annotatedItemIds(this.userToken, this.serverURL, userId, participated)
1139
+ .then((annotatedItemIds) => {
1140
+ if ('resource' in annotatedItemIds) {
1141
+ // The annotator has `resource` and `items` fields
1142
+ annotatedItemIds = annotatedItemIds.itemIds
1143
+ }
1144
+ this.annotator.drawnFeatures(this.userToken, this.serverURL, annotatedItemIds)
1145
+ .then((drawnFeatures) => {
1146
+ if ('resource' in drawnFeatures) {
1147
+ // The annotator has `resource` and `features` fields
1148
+ drawnFeatures = drawnFeatures.features
1149
+ }
1150
+ // Use to switch the displayed feature type
1151
+ if (this.drawnType !== 'All tools') {
1152
+ drawnFeatures = drawnFeatures.filter((feature) => {
1153
+ return feature.geometry.type === this.drawnType
1154
+ })
1155
+ }
1156
+ this.drawnAnnotationFeatures = drawnFeatures
1157
+ this.loading = false
1158
+ // No need to call 'addAnnotationFeature' when a new feature created, as it is already on the map
1159
+ if (!this.annotationSubmitted) {
1160
+ for (const feature of drawnFeatures) {
1161
+ this.mapImp.addAnnotationFeature(feature)
1162
+ }
1163
+ }
1164
+ })
1165
+ .catch((reason) => {
1166
+ console.log(reason) // Error!
1167
+ })
1168
+ })
1169
+ .catch((reason) => {
1170
+ console.log(reason) // Error!
1171
+ })
1172
+ }
1173
+ }
1174
+ },
1175
+ /**
1176
+ * @vuese
1177
+ * Function to display annotator toolbar.
1178
+ * @arg flag
1179
+ */
1180
+ showAnnotator: function (flag) {
1181
+ if (this.mapImp) {
1182
+ // Control the show/hide of the drawn annotations
1183
+ this.mapImp.showAnnotator(flag)
1184
+ // Hide default toolbar, we will use customised SVG icons instead
1185
+ this.$el.querySelector('.maplibregl-ctrl-group').style.display = 'none'
1186
+ }
1187
+ },
1188
+ /**
1189
+ * @vuese
1190
+ * Function to switch the type of annotation.
1191
+ * @arg flag
1192
+ */
1193
+ setDrawnType: function (flag) {
1194
+ this.drawnType = flag
1195
+ if (this.mapImp) {
1196
+ this.addAnnotationFeature()
1197
+ }
1198
+ },
1199
+ /**
1200
+ * @vuese
1201
+ * Function to switch the type of person who annotated.
1202
+ * @arg flag
1203
+ */
1204
+ setAnnotatedType: function (flag) {
1205
+ this.annotatedType = flag
1206
+ if (this.mapImp) {
1207
+ this.addAnnotationFeature()
1208
+ }
1209
+ },
633
1210
  /**
634
1211
  * @vuese
635
1212
  * Function to switch from 2D to 3D
@@ -830,22 +1407,90 @@ export default {
830
1407
  * by providing path model identifier, ``pathId``.
831
1408
  * @arg pathId
832
1409
  */
833
- highlightConnectedPaths: function (payload) {
1410
+ highlightConnectedPaths: async function (payload) {
834
1411
  if (this.mapImp) {
835
1412
  let paths = [...this.mapImp.pathModelNodes(payload)]
836
- // The line below matches the paths to the annIdToFeatureId map to get the feature ids
837
1413
 
1414
+ // The line below is to get the path features from the geojson ids
838
1415
  let pathFeatures = paths.map((p) => this.mapImp.featureProperties(p))
1416
+
1417
+ // Query the flatmap knowledge graph for connectivity, we use this to grab the origins
1418
+ let connectivity = await this.flatmapQueries.queryForConnectivity(payload)
1419
+
1420
+ // Check and flatten the origins node graph
1421
+ let originsFlat = connectivity?.ids?.dendrites?.flat().flat()
1422
+
1423
+ // Remove the origin nodes from the path features so that we only see downstream nodes
1424
+ pathFeatures = pathFeatures.filter((p) => !originsFlat.includes(p.models))
1425
+
839
1426
  let toHighlight = []
1427
+ let highlight = false
1428
+
1429
+ // Loop through the path features and check if we have origin nodes
840
1430
  pathFeatures.forEach((p) => {
1431
+
1432
+ // Get the nodes from each path feature
841
1433
  this.mapImp.nodePathModels(p.featureId).forEach((f) => {
842
- toHighlight.push(f)
1434
+ highlight = true
1435
+ // s2 here is the second level paths
1436
+ let s2 = this.mapImp.pathModelNodes(f)
1437
+ s2.forEach((s) => {
1438
+ let s2Feature = this.mapImp.featureProperties([s]) // get the feature properties for s2
1439
+ if (originsFlat.includes(s2Feature.models)) {
1440
+ highlight = false // if we have an origin node, we don't want to highlight the path
1441
+ return
1442
+ }
1443
+ })
1444
+
1445
+ if (highlight) {
1446
+ toHighlight.push(f)
1447
+ }
843
1448
  })
844
1449
  })
1450
+
845
1451
  // display connected paths
846
1452
  this.mapImp.zoomToFeatures(toHighlight, { noZoomIn: true })
847
1453
  }
848
1454
  },
1455
+ /**
1456
+ * @vuese
1457
+ * Function to enable/disable mouse enter and leave event for
1458
+ * alert checkbox
1459
+ * @arg payload
1460
+ */
1461
+ alertMouseEnterEmitted: function (payload) {
1462
+ if (this.mapImp) {
1463
+ if (payload.value) {
1464
+ const ALERT_FILTER = {
1465
+ HAS: 'alert',
1466
+ }
1467
+ this.mapImp.setVisibilityFilter(ALERT_FILTER)
1468
+ } else {
1469
+ this.mapImp.clearVisibilityFilter()
1470
+ this.alertSelected({
1471
+ value: (payload.checked.length > 0),
1472
+ });
1473
+ }
1474
+ }
1475
+ },
1476
+ /**
1477
+ * @vuese
1478
+ * Function to enable/disable (show/hide) pathways with/without alert
1479
+ * by providing ``kay, value`` ``payload`` object ``{alertKey, true/false}``.
1480
+ * @arg payload
1481
+ */
1482
+ alertSelected: function (payload) {
1483
+ if (this.mapImp) {
1484
+ if (payload.value) {
1485
+ this.mapImp.clearVisibilityFilter()
1486
+ } else {
1487
+ const ALERT_FILTER = {
1488
+ NOT: {HAS: 'alert'}
1489
+ }
1490
+ this.mapImp.setVisibilityFilter(ALERT_FILTER)
1491
+ }
1492
+ }
1493
+ },
849
1494
  /**
850
1495
  * @vuese
851
1496
  * Function to enable/disable (show/hide) the system
@@ -913,6 +1558,22 @@ export default {
913
1558
  this.mapImp.enableConnectivityByTaxonIds(payload.key, payload.value)
914
1559
  }
915
1560
  },
1561
+ taxonMouseEnterEmitted: function (payload) {
1562
+ if (this.mapImp) {
1563
+ if (payload.value) {
1564
+ let gid = this.mapImp.taxonFeatureIds(payload.key)
1565
+ this.mapImp.enableConnectivityByTaxonIds(payload.key, payload.value) // make sure path is visible
1566
+ this.mapImp.zoomToGeoJSONFeatures(gid, {noZoomIn: true})
1567
+ } else {
1568
+ // reset visibility of paths
1569
+ this.mapImp.selectGeoJSONFeatures("-1")
1570
+ payload.selections.forEach((item) => {
1571
+ let show = payload.checked.includes(item.taxon)
1572
+ this.mapImp.enableConnectivityByTaxonIds(item.taxon, show)
1573
+ })
1574
+ }
1575
+ }
1576
+ },
916
1577
  /**
917
1578
  * @vuese
918
1579
  * Function to show or hide connectivity features observed in particular species
@@ -959,6 +1620,71 @@ export default {
959
1620
  enablePanZoomEvents: function (flag) {
960
1621
  this.mapImp.enablePanZoomEvents(flag)
961
1622
  },
1623
+ /**
1624
+ * @vuese
1625
+ * Function to process annotation callbacks, invoked when events occur with the map.
1626
+ * @arg payload,
1627
+ * @arg data
1628
+ */
1629
+ annotationEventCallback: function (payload, data) {
1630
+ // Popup closed will trigger aborted event
1631
+ if (data.type === 'aborted') {
1632
+ // Rollback drawing when no new annotation submitted
1633
+ if (!this.annotationSubmitted) this.rollbackAnnotationEvent()
1634
+ else this.annotationSubmitted = false
1635
+ } else if (data.type === 'modeChanged') {
1636
+ // 'modeChanged' event is before 'created' event
1637
+ if (data.feature.mode.startsWith('draw_')) {
1638
+ // Reset data entry for every draw
1639
+ this.initialiseDialog()
1640
+ this.inDrawing = true
1641
+ } else if (data.feature.mode === 'simple_select' && this.inDrawing) {
1642
+ if (this.createdEvent) {
1643
+ this.connectionDisplay = true
1644
+ } else {
1645
+ // Reset if a invalid draw
1646
+ this.initialiseDrawing()
1647
+ }
1648
+ } else if (data.feature.mode === 'direct_select') {
1649
+ this.doubleClickedFeature = true
1650
+ }
1651
+ } else if (data.type === 'selectionChanged') {
1652
+ this.currentDrawnFeature =
1653
+ data.feature.features.length === 0 ?
1654
+ undefined :
1655
+ data.feature.features[0]
1656
+ payload.feature.feature = this.currentDrawnFeature
1657
+ if (!this.inDrawing) {
1658
+ this.initialiseDialog()
1659
+ // For exist drawn annotation features
1660
+ if (this.currentDrawnFeature) {
1661
+ let feature = this.drawnAnnotationFeatures
1662
+ .filter((feature) => feature.id === this.currentDrawnFeature.id)[0]
1663
+ if (feature && feature.connection) {
1664
+ this.connectionEntry = feature.connection
1665
+ }
1666
+ this.drawModeEvent(payload)
1667
+ }
1668
+ }
1669
+ } else {
1670
+ if (data.type === 'created' || data.type === 'updated') {
1671
+ if (data.type === 'updated' && data.feature.action) {
1672
+ data.positionUpdated = data.feature.action === 'move'
1673
+ }
1674
+ const feature = this.mapImp.refreshAnnotationFeatureGeometry(data.feature)
1675
+ payload.feature.feature = feature
1676
+ // NB. this might now be `null` if user has deleted it (before OK/Submit)
1677
+ // so maybe then no `service.addAnnotation` ??
1678
+ }
1679
+ // Once double click mouse to confirm drawing, 'aborted' event will be triggered.
1680
+ // Hence disable direct popup when 'created' event, dialog will be used instead.
1681
+ if (data.type === 'created') {
1682
+ this.createdEvent = payload
1683
+ } else {
1684
+ this.checkAndCreatePopups(payload)
1685
+ }
1686
+ }
1687
+ },
962
1688
  /**
963
1689
  * @vuese
964
1690
  * A callback function, invoked when events occur with the map.
@@ -967,67 +1693,187 @@ export default {
967
1693
  */
968
1694
  eventCallback: function () {
969
1695
  return (eventType, data, ...args) => {
970
- if (eventType !== 'pan-zoom') {
971
- const label = data.label
972
- const resource = [data.models]
973
- const taxonomy = this.entry
974
- const biologicalSex = this.biologicalSex
975
- let taxons = undefined
976
- if (data.taxons) {
977
- // check if data.taxons is string or array
978
- if (typeof data.taxons !== 'object') {
979
- taxons = JSON.parse(data.taxons)
980
- } else {
981
- taxons = data.taxons
982
- }
983
- }
1696
+ if (eventType === 'annotation') {
984
1697
  const payload = {
985
- dataset: data.dataset,
986
- biologicalSex: biologicalSex,
987
- taxonomy: taxonomy,
988
- resource: resource,
989
- label: label,
990
1698
  feature: data,
991
1699
  userData: args,
992
1700
  eventType: eventType,
993
- provenanceTaxonomy: taxons,
994
1701
  }
995
- if (eventType === 'click') {
996
- if (this.viewingMode === 'Network Discovery') {
997
- this.highlightConnectedPaths([data.models])
998
- } else {
999
- this.currentActive = data.models ? data.models : ''
1702
+ this.annotationEventCallback(payload, data)
1703
+ } else {
1704
+ if (eventType !== 'pan-zoom') {
1705
+ this.featuresAlert = data.alert
1706
+ const label = data.label
1707
+ const resource = [data.models]
1708
+ const taxonomy = this.entry
1709
+ const biologicalSex = this.biologicalSex
1710
+ let taxons = undefined
1711
+ if (data.taxons) {
1712
+ // check if data.taxons is string or array
1713
+ if (typeof data.taxons !== 'object') {
1714
+ taxons = JSON.parse(data.taxons)
1715
+ } else {
1716
+ taxons = data.taxons
1717
+ }
1000
1718
  }
1001
- } else if (
1002
- eventType === 'mouseenter' &&
1003
- !(this.viewingMode === 'Network Discovery')
1004
- ) {
1005
- this.currentHover = data.models ? data.models : ''
1719
+ const payload = {
1720
+ dataset: data.dataset,
1721
+ biologicalSex: biologicalSex,
1722
+ taxonomy: taxonomy,
1723
+ resource: resource,
1724
+ label: label,
1725
+ feature: data,
1726
+ userData: args,
1727
+ eventType: eventType,
1728
+ provenanceTaxonomy: taxons,
1729
+ }
1730
+ if (eventType === 'click') {
1731
+ if (this.viewingMode === 'Network Discovery') {
1732
+ this.highlightConnectedPaths([data.models])
1733
+ } else {
1734
+ this.currentActive = data.models ? data.models : ''
1735
+ // Stop adding features if dialog displayed
1736
+ if (this.inDrawing && !this.connectionDisplay) {
1737
+ // Only clicked connection data will be added
1738
+ let nodeLabel = data.label ? data.label : `Feature ${data.id}`
1739
+ // only the linestring will have connection at the current stage
1740
+ if (this.activeDrawTool === 'LineString') {
1741
+ this.connectionEntry[data.featureId] = Object.assign({label: nodeLabel},
1742
+ Object.fromEntries(
1743
+ Object.entries(data)
1744
+ .filter(([key]) => ['featureId', 'models'].includes(key))
1745
+ .map(([key, value]) => [(key === 'featureId') ? 'id' : key, value])))
1746
+ }
1747
+ }
1748
+ }
1749
+ } else if (
1750
+ eventType === 'mouseenter' &&
1751
+ !(this.viewingMode === 'Network Discovery')
1752
+ ) {
1753
+ this.currentHover = data.models ? data.models : ''
1754
+ }
1755
+ if (
1756
+ data &&
1757
+ data.type !== 'marker' &&
1758
+ eventType === 'click' &&
1759
+ !(this.viewingMode === 'Network Discovery') &&
1760
+ // Disable popup when drawing
1761
+ !this.inDrawing
1762
+ ) {
1763
+ this.checkAndCreatePopups(payload)
1764
+ }
1765
+ this.$emit('resource-selected', payload)
1766
+ } else {
1767
+ this.$emit('pan-zoom-callback', data)
1006
1768
  }
1007
- if (
1008
- data &&
1009
- data.type !== 'marker' &&
1010
- eventType === 'click' &&
1011
- !(this.viewingMode === 'Network Discovery')
1012
- ) {
1013
- this.checkAndCreatePopups(payload)
1769
+ }
1770
+ }
1771
+ },
1772
+ /**
1773
+ * A hack to implement connection dialog drag action and scope.
1774
+ */
1775
+ dialogCssHacks: function () {
1776
+ this.$nextTick(() => {
1777
+ const dialog = this.$el.querySelector('.connection-dialog')
1778
+ draggable(this.$el, dialog)
1779
+ // dialog popup at the click position
1780
+ // slightly change x or y if close to boundary
1781
+ let posX, posY
1782
+ const containerRect = this.$el.getBoundingClientRect()
1783
+ const dialogRect = dialog.getBoundingClientRect()
1784
+ if (this.dialogPosition.x > containerRect.width / 2) {
1785
+ posX = this.dialogPosition.x - dialogRect.width
1786
+ } else {
1787
+ posX = this.dialogPosition.x
1788
+ }
1789
+ if (this.dialogPosition.y > containerRect.height / 2) {
1790
+ posY = this.dialogPosition.y - dialogRect.height
1791
+ } else {
1792
+ posY = this.dialogPosition.y
1793
+ }
1794
+ dialog.style.transform =
1795
+ `translate(${posX - this.dialogPosition.offsetX}px, ${posY - this.dialogPosition.offsetY}px)`
1796
+ })
1797
+ },
1798
+ /**
1799
+ * A hack to handle the status of annotation tools.
1800
+ */
1801
+ drawIconCssHacks: function () {
1802
+ // set tool/mode icon status
1803
+ if (this.$el.querySelector('.iconSelected') || !this.connectionDisplay) {
1804
+ this.drawnTypes.map((t) => {
1805
+ const dtype = this.$el.querySelector(`.draw${t}`)
1806
+ if (dtype) {
1807
+ dtype.classList.remove('iconSelected');
1808
+ dtype.classList.remove('inactive');
1014
1809
  }
1015
- /**
1016
- * The event emitted from the mouse event callbacks that come from flatmap-viewer. The payload
1017
- * argument provides an object with information on the feature where the mouse event takes place.
1018
- * @arg payload
1019
- */
1020
- this.$emit('resource-selected', payload)
1810
+ })
1811
+ this.drawModes.map((m) => {
1812
+ this.$el.querySelector(`.draw${m}`).classList.remove('iconSelected');
1813
+ this.$el.querySelector(`.draw${m}`).classList.remove('inactive');
1814
+ })
1815
+ }
1816
+ if (this.activeDrawTool) {
1817
+ this.$el.querySelector(`.draw${this.activeDrawTool}`).classList.add('iconSelected');
1818
+ this.drawModes.map((m) => {
1819
+ this.$el.querySelector(`.draw${m}`).classList.add('inactive');
1820
+ })
1821
+ } else if (this.activeDrawMode || this.connectionDisplay) {
1822
+ if (this.activeDrawMode) {
1823
+ this.$el.querySelector(`.draw${this.activeDrawMode}`).classList.add('iconSelected');
1824
+ }
1825
+ this.drawnTypes.map((t) => {
1826
+ const dtype = this.$el.querySelector(`.draw${t}`)
1827
+ if (dtype) dtype.classList.add('inactive');
1828
+ })
1829
+ }
1830
+ },
1831
+ /**
1832
+ * @vuese
1833
+ * Function to fire annotation event based on the provided ``data``.
1834
+ * Either edit or delete event.
1835
+ * @arg data
1836
+ */
1837
+ drawModeEvent: function (data) {
1838
+ if (this.activeDrawMode) {
1839
+ // double click fires 'updated' callback
1840
+ if (this.doubleClickedFeature) {
1841
+ if (data.feature.feature.geometry.type !== 'Point') {
1842
+ // show tooltip and enter edit mode
1843
+ this.changeAnnotationDrawMode({
1844
+ mode: 'direct_select',
1845
+ options: { featureId: data.feature.feature.id }
1846
+ })
1847
+ this.deleteOrEditAnnotationFeature()
1848
+ }
1849
+ this.doubleClickedFeature = false
1021
1850
  } else {
1022
- /**
1023
- * The event emitted in ``callback`` function from ``MapManager.loadMap()``
1024
- * if ``eventType`` is ``pan-zoom``.
1025
- * @arg data
1026
- */
1027
- this.$emit('pan-zoom-callback', data)
1851
+ // single click fires delete
1852
+ if (this.activeDrawMode === 'Delete') {
1853
+ this.changeAnnotationDrawMode({
1854
+ mode: 'simple_select',
1855
+ options: { featureIds: [data.feature.feature.id] }
1856
+ })
1857
+ this.deleteOrEditAnnotationFeature()
1858
+ }
1028
1859
  }
1029
1860
  }
1030
1861
  },
1862
+ /**
1863
+ * Function to create connectivity body from existing entries.
1864
+ */
1865
+ createConnectivityBody: function () {
1866
+ if (Object.keys(this.connectionEntry).length > 0) {
1867
+ const features = Object.values(this.connectionEntry)
1868
+ const body = {
1869
+ type: 'connectivity',
1870
+ source: features[0],
1871
+ target: features[features.length - 1],
1872
+ intermediates: features.slice(1, -1),
1873
+ }
1874
+ this.annotationEntry.body = body
1875
+ }
1876
+ },
1031
1877
  /**
1032
1878
  * @vuese
1033
1879
  * Function triggered by viewing mode change.
@@ -1047,13 +1893,33 @@ export default {
1047
1893
  checkAndCreatePopups: async function (data) {
1048
1894
  // Call flatmap database to get the connection data
1049
1895
  if (this.viewingMode === 'Annotation') {
1050
- if (data.feature && data.feature.featureId && data.feature.models) {
1896
+ if (data.feature) {
1051
1897
  this.annotationEntry = {
1052
1898
  ...data.feature,
1053
- resource: this.serverURL,
1054
- resourceId: this.serverUUID,
1899
+ resourceId: this.serverURL,
1900
+ }
1901
+ if (data.feature.featureId && data.feature.models) {
1902
+ this.displayTooltip(data.feature.models)
1903
+ } else if (data.feature.feature) {
1904
+ if (this.inDrawing || this.activeDrawMode) {
1905
+ this.annotationSubmitted = false
1906
+ this.annotationEntry.featureId = data.feature.feature.id
1907
+ this.createConnectivityBody()
1908
+ this.displayTooltip(
1909
+ data.feature.feature.id,
1910
+ centroid(data.feature.feature.geometry)
1911
+ )
1912
+ } else {
1913
+ // Not allowed to update feature if not on edit mode
1914
+ if (data.feature.type === 'updated') {
1915
+ this.rollbackAnnotationEvent()
1916
+ }
1917
+ }
1918
+ // Hide dialog when updated or deleted event is fired and tooltip is displayed
1919
+ if (data.feature.type === 'updated' || data.feature.type === 'deleted') {
1920
+ this.initialiseDialog()
1921
+ }
1055
1922
  }
1056
- this.displayTooltip(data.feature.models)
1057
1923
  } else {
1058
1924
  this.annotation = {}
1059
1925
  }
@@ -1087,6 +1953,14 @@ export default {
1087
1953
  if (ftooltip) ftooltip.style.display = 'block'
1088
1954
  }
1089
1955
  },
1956
+ /**
1957
+ * @vuese
1958
+ * Function to close popup.
1959
+ */
1960
+ closePopup: function () {
1961
+ let cbutton = document.querySelector('.maplibregl-popup-close-button')
1962
+ if (cbutton) cbutton.click()
1963
+ },
1090
1964
  /**
1091
1965
  * @vuese
1092
1966
  * Function to close tooltip.
@@ -1227,28 +2101,26 @@ export default {
1227
2101
  * by providing featureId (``feature``).
1228
2102
  * @arg feature
1229
2103
  */
1230
- displayTooltip: function (feature) {
2104
+ displayTooltip: function (feature, geometry = undefined) {
1231
2105
  this.tooltipDisplay = true
2106
+ let featureId = undefined
2107
+ let options = { className: 'flatmapvuer-popover' }
2108
+ if (geometry) {
2109
+ featureId = feature
2110
+ options.annotationFeatureGeometry = geometry
2111
+ } else {
2112
+ featureId = this.mapImp.modelFeatureIds(feature)[0]
2113
+ if (!this.inDrawing) {
2114
+ options.positionAtLastClick = true
2115
+ }
2116
+ }
1232
2117
  if (!this.disableUI) {
1233
2118
  this.$nextTick(() => {
1234
- this.displayPopup(feature)
1235
- });
2119
+ this.mapImp.showPopup(featureId, this.$refs.tooltip.$el, options)
2120
+ this.popUpCssHacks()
2121
+ })
1236
2122
  }
1237
2123
  },
1238
- /**
1239
- * @vuese
1240
- * Function to display popup
1241
- * by providing featureId (``feature``).
1242
- * @arg feature
1243
- */
1244
- displayPopup: function (feature) {
1245
- this.mapImp.showPopup(
1246
- this.mapImp.modelFeatureIds(feature)[0],
1247
- this.$refs.tooltip.$el,
1248
- { className: 'flatmapvuer-popover', positionAtLastClick: true }
1249
- )
1250
- this.popUpCssHacks()
1251
- },
1252
2124
  /**
1253
2125
  * @vuese
1254
2126
  * Function to open Flatmap Help Popup.
@@ -1429,7 +2301,6 @@ export default {
1429
2301
  )
1430
2302
  promise1.then((returnedObject) => {
1431
2303
  this.mapImp = returnedObject
1432
- this.serverUUID = this.mapImp.getIdentifier().uuid
1433
2304
  this.serverURL = this.mapImp.makeServerUrl('').slice(0, -1)
1434
2305
  let mapVersion = this.mapImp.details.version
1435
2306
  this.setFlightPathInfo(mapVersion)
@@ -1498,6 +2369,7 @@ export default {
1498
2369
  //this.layers = this.mapImp.getLayers();
1499
2370
  this.processSystems(this.mapImp.getSystems())
1500
2371
  this.processTaxon(this.flatmapAPI, this.mapImp.taxonIdentifiers)
2372
+ this.containsAlert = "alert" in this.mapImp.featureFilterRanges()
1501
2373
  this.addResizeButtonToMinimap()
1502
2374
  this.loading = false
1503
2375
  this.computePathControlsMaximumHeight()
@@ -1749,7 +2621,9 @@ export default {
1749
2621
  return {
1750
2622
  flatmapAPI: this.flatmapAPI,
1751
2623
  sparcAPI: this.sparcAPI,
1752
- userApiKey: this.userToken
2624
+ $annotator: this.annotator,
2625
+ userApiKey: this.userToken,
2626
+ getFeaturesAlert: () => this.featuresAlert,
1753
2627
  }
1754
2628
  },
1755
2629
  data: function () {
@@ -1759,7 +2633,6 @@ export default {
1759
2633
  //for the first time, otherwise it may display an arrow at a
1760
2634
  //undesired location.
1761
2635
  tooltipDisplay: false,
1762
- serverUUID: undefined,
1763
2636
  serverURL: undefined,
1764
2637
  layers: [],
1765
2638
  pathways: [],
@@ -1790,6 +2663,12 @@ export default {
1790
2663
  { value: false },
1791
2664
  { value: false },
1792
2665
  { value: false },
2666
+ { value: false },
2667
+ { value: false },
2668
+ { value: false },
2669
+ { value: false },
2670
+ { value: false },
2671
+ { value: false },
1793
2672
  ],
1794
2673
  yellowstar: yellowstar,
1795
2674
  isFC: false,
@@ -1801,7 +2680,7 @@ export default {
1801
2680
  tooltipEntry: createUnfilledTooltipData(),
1802
2681
  connectivityTooltipVisible: false,
1803
2682
  drawerOpen: false,
1804
- annotationRadio: false,
2683
+ featuresAlert: undefined,
1805
2684
  flightPath3DRadio: false,
1806
2685
  displayFlightPathOption: false,
1807
2686
  colourRadio: true,
@@ -1809,15 +2688,50 @@ export default {
1809
2688
  minimapResizeShow: false,
1810
2689
  minimapSmall: false,
1811
2690
  currentActive: '',
2691
+ currentDrawnFeature: undefined, // Clicked drawn annotation
1812
2692
  currentHover: '',
1813
2693
  viewingMode: 'Exploration',
1814
2694
  viewingModes: ['Annotation', 'Exploration', 'Network Discovery'],
2695
+ drawnType: 'All tools',
2696
+ drawnTypes: ['All tools', 'Point', 'LineString', 'Polygon', 'None'],
2697
+ annotatedType: 'Anyone',
2698
+ annotatedTypes: ['Anyone', 'Me', 'Others'],
1815
2699
  openMapRef: undefined,
1816
2700
  backgroundIconRef: undefined,
2701
+ annotator: undefined,
2702
+ userInformation: undefined,
2703
+ activeDrawTool: undefined,
2704
+ drawnAnnotationEvent: ['created', 'updated', 'deleted'],
2705
+ createdEvent: undefined,
2706
+ annotationSubmitted: false,
2707
+ inDrawing: false,
2708
+ connectionDisplay: false,
2709
+ connectionEntry: {},
2710
+ drawnAnnotationFeatures: undefined, // Store all exist drawn features
2711
+ doubleClickedFeature: false,
2712
+ activeDrawMode: undefined,
2713
+ drawModes: ['Delete', 'Edit'],
2714
+ dialogPosition: {
2715
+ offsetX: 0,
2716
+ offsetY: 0,
2717
+ x: undefined,
2718
+ y: undefined
2719
+ },
2720
+ containsAlert: false,
2721
+ alertOptions: [
2722
+ {
2723
+ label: 'Display Path With Alerts',
2724
+ key: 'alert',
2725
+ enabled: true,
2726
+ },
2727
+ ],
1817
2728
  }
1818
2729
  },
1819
2730
  computed: {
1820
2731
  ...mapState(useMainStore, ['userToken']),
2732
+ connection: function () {
2733
+ return Object.keys(this.connectionEntry).length > 0
2734
+ }
1821
2735
  },
1822
2736
  watch: {
1823
2737
  entry: function () {
@@ -1842,6 +2756,74 @@ export default {
1842
2756
  immediate: true,
1843
2757
  deep: true,
1844
2758
  },
2759
+ activeDrawTool: function () {
2760
+ this.drawIconCssHacks()
2761
+ },
2762
+ activeDrawMode: function () {
2763
+ this.drawIconCssHacks()
2764
+ },
2765
+ /**
2766
+ * hide dialog when connectionEntry is empty
2767
+ */
2768
+ connection: function (value) {
2769
+ const connectionIcon = this.$el.querySelector('.drawConnection')
2770
+ if (!value) {
2771
+ this.connectionDisplay = false
2772
+ connectionIcon.classList.add('inactive')
2773
+ } else {
2774
+ connectionIcon.classList.remove('inactive')
2775
+ }
2776
+ },
2777
+ /**
2778
+ * popup dialog via click icon
2779
+ */
2780
+ connectionDisplay: function (display) {
2781
+ const connectionIcon = this.$el.querySelector('.drawConnection')
2782
+ if (display) {
2783
+ connectionIcon.classList.add('iconSelected')
2784
+ this.dialogCssHacks()
2785
+ } else {
2786
+ connectionIcon.classList.remove('iconSelected')
2787
+ }
2788
+ this.drawIconCssHacks()
2789
+ },
2790
+ /**
2791
+ * Set dialog offset when flatmap annotator used
2792
+ */
2793
+ dialogPosition: {
2794
+ handler: function () {
2795
+ const containerRect = this.$el.getBoundingClientRect()
2796
+ this.dialogPosition.offsetX = containerRect.x
2797
+ this.dialogPosition.offsetY = containerRect.y
2798
+ },
2799
+ deep: true,
2800
+ once: true,
2801
+ },
2802
+ viewingMode: function (mode) {
2803
+ if (mode === 'Annotation') {
2804
+ this.$el.querySelector('.maplibregl-canvas').addEventListener('click', (e) => {
2805
+ e.preventDefault();
2806
+ this.dialogPosition.x = e.clientX
2807
+ this.dialogPosition.y = e.clientY
2808
+ // use to fix the draw point pop up position issue
2809
+ if (this.activeDrawTool === 'Point') {
2810
+ this.dialogCssHacks()
2811
+ }
2812
+ }, false)
2813
+ this.loading = true
2814
+ this.annotator.authenticate(this.userToken).then((userData) => {
2815
+ if (userData.name && userData.email) {
2816
+ this.showAnnotator(true)
2817
+ this.userInformation = userData
2818
+ this.setFeatureAnnotated()
2819
+ if (!this.drawnAnnotationFeatures) {
2820
+ this.addAnnotationFeature()
2821
+ }
2822
+ }
2823
+ this.loading = false
2824
+ })
2825
+ } else this.showAnnotator(false)
2826
+ },
1845
2827
  disableUI: function (isUIDisabled) {
1846
2828
  if (isUIDisabled) {
1847
2829
  this.closeTooltip()
@@ -1924,6 +2906,7 @@ export default {
1924
2906
  position: absolute;
1925
2907
  bottom: 0px;
1926
2908
  transition: all 1s ease;
2909
+ z-index: 8;
1927
2910
  &.open {
1928
2911
  left: 0px;
1929
2912
  }
@@ -1998,7 +2981,8 @@ export default {
1998
2981
  }
1999
2982
 
2000
2983
  :deep(.maplibregl-popup) {
2001
- max-width: 300px !important;
2984
+ z-index: 10;
2985
+ max-width: 330px !important;
2002
2986
  }
2003
2987
 
2004
2988
  :deep(.flatmap-tooltip-popup) {
@@ -2170,12 +3154,19 @@ export default {
2170
3154
  }
2171
3155
  }
2172
3156
 
2173
- .zoomOut {
2174
- padding-left: 8px;
3157
+ .drawPoint, .drawLineString, .drawPolygon,
3158
+ .drawDelete, .drawEdit, .drawConnection,
3159
+ .zoomIn, .zoomOut, .fitWindow {
3160
+ padding: 4px;
3161
+ }
3162
+
3163
+ .iconSelected {
3164
+ color: var(--el-color-primary-light-5) !important;
2175
3165
  }
2176
3166
 
2177
- .fitWindow {
2178
- padding-left: 8px;
3167
+ .inactive {
3168
+ color: #DDDDDD !important;
3169
+ cursor: not-allowed !important;
2179
3170
  }
2180
3171
 
2181
3172
  .yellow-star-legend {
@@ -2187,6 +3178,7 @@ export default {
2187
3178
  bottom: 16px;
2188
3179
  position: absolute;
2189
3180
  transition: all 1s ease;
3181
+ z-index: 10;
2190
3182
  &.open {
2191
3183
  left: 322px;
2192
3184
  }
@@ -2200,7 +3192,7 @@ export default {
2200
3192
  background-color: #ffffff;
2201
3193
  border: 1px solid $app-primary-color;
2202
3194
  box-shadow: 0px 2px 12px 0px rgba(0, 0, 0, 0.06);
2203
- height: 290px;
3195
+ height: fit-content;
2204
3196
  min-width: 200px;
2205
3197
  .el-popper__arrow {
2206
3198
  &:before {
@@ -2383,25 +3375,27 @@ export default {
2383
3375
  }
2384
3376
  }
2385
3377
 
2386
- :deep(.popper-zoomout) {
2387
- padding-right: 13px !important;
2388
- left: -21px !important;
2389
- }
2390
-
2391
- :deep(.popper-zoomout) {
2392
- .popper__arrow {
2393
- left: 53px !important;
2394
- }
2395
- }
2396
-
2397
3378
  :deep(.maplibregl-popup-content) {
2398
3379
  padding: 0px;
2399
3380
  }
2400
3381
 
3382
+ .bottom-draw-control {
3383
+ background-color: var(--el-color-primary-light-9);
3384
+ padding: 4px 4px 2px 4px;
3385
+ border-style: solid;
3386
+ border-color: var(--el-color-primary-light-5);
3387
+ border-radius: 1rem;
3388
+ position: absolute;
3389
+ right: calc(50vw - 100px);;
3390
+ bottom: 16px;
3391
+ z-index: 10;
3392
+ }
3393
+
2401
3394
  .bottom-right-control {
2402
3395
  position: absolute;
2403
3396
  right: 16px;
2404
3397
  bottom: 16px;
3398
+ z-index: 10;
2405
3399
  }
2406
3400
 
2407
3401
  :deep(.my-drawer) {
@@ -2587,6 +3581,12 @@ export default {
2587
3581
  }
2588
3582
  }
2589
3583
  }
3584
+
3585
+ .connection-dialog {
3586
+ position: absolute;
3587
+ z-index: 10;
3588
+ cursor: move;
3589
+ }
2590
3590
  </style>
2591
3591
 
2592
3592
  <style lang="scss">