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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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>
@@ -269,6 +396,16 @@
269
396
  @checkAll="checkAllSystems"
270
397
  @change-active="ftuSelected"
271
398
  />
399
+ <selections-group
400
+ v-if="containsAlert && alertOptions"
401
+ title="Alert"
402
+ labelKey="label"
403
+ identifierKey="key"
404
+ :selections="alertOptions"
405
+ @changed="alertSelected"
406
+ ref="alertSelection"
407
+ key="alertSelection"
408
+ />
272
409
  <selections-group
273
410
  v-if="!isFC && centreLines && centreLines.length > 0"
274
411
  title="Nerves"
@@ -310,6 +447,7 @@
310
447
  identifierKey="taxon"
311
448
  :selections="taxonConnectivity"
312
449
  @changed="taxonsSelected"
450
+ @checkboxMouseEnter="checkboxMouseEnterEmitted"
313
451
  @checkAll="checkAllTaxons"
314
452
  ref="taxonSelection"
315
453
  key="taxonSelection"
@@ -394,6 +532,52 @@
394
532
  </el-option>
395
533
  </el-select>
396
534
  </el-row>
535
+ <template v-if="viewingMode === 'Annotation' && userInformation">
536
+ <el-row class="backgroundText">Drawn By*</el-row>
537
+ <el-row class="backgroundControl">
538
+ <el-select
539
+ :teleported="false"
540
+ v-model="drawnType"
541
+ placeholder="Select"
542
+ class="select-box"
543
+ popper-class="flatmap_dropdown"
544
+ @change="setDrawnType"
545
+ >
546
+ <el-option
547
+ v-for="item in drawnTypes"
548
+ :key="item"
549
+ :label="item"
550
+ :value="item"
551
+ >
552
+ <el-row>
553
+ <el-col :span="12">{{ item }}</el-col>
554
+ </el-row>
555
+ </el-option>
556
+ </el-select>
557
+ </el-row>
558
+ <el-row class="backgroundText">Annotated By*</el-row>
559
+ <el-row class="backgroundControl">
560
+ <el-select
561
+ :teleported="false"
562
+ v-model="annotatedType"
563
+ placeholder="Select"
564
+ class="select-box"
565
+ popper-class="flatmap_dropdown"
566
+ @change="setAnnotatedType"
567
+ >
568
+ <el-option
569
+ v-for="item in annotatedTypes"
570
+ :key="item"
571
+ :label="item"
572
+ :value="item"
573
+ >
574
+ <el-row>
575
+ <el-col :span="12">{{ item }}</el-col>
576
+ </el-row>
577
+ </el-option>
578
+ </el-select>
579
+ </el-row>
580
+ </template>
397
581
  <el-row class="backgroundSpacer" v-if="displayFlightPathOption"></el-row>
398
582
  <el-row class="backgroundText" v-if="displayFlightPathOption">Flight path display</el-row>
399
583
  <el-row class="backgroundControl" v-if="displayFlightPathOption">
@@ -499,23 +683,37 @@
499
683
  :annotationEntry="annotationEntry"
500
684
  :entry="tooltipEntry"
501
685
  :annotationDisplay="viewingMode === 'Annotation'"
686
+ @annotation="commitAnnotationEvent"
687
+ />
688
+ <ConnectionDialog
689
+ class="connection-dialog"
690
+ v-show="connectionDisplay"
691
+ :entry="connectionEntry"
692
+ :drawing="inDrawing"
693
+ :connection="connection"
694
+ @display="connectionDialogPopup"
695
+ @confirm="confirmDrawnFeature"
696
+ @cancel="cancelDrawnFeature"
697
+ @popup="closePopup"
698
+ @tooltip="displayConnectedFeatureTooltip"
502
699
  />
503
700
  </div>
504
701
  </div>
505
702
  </template>
506
703
 
507
704
  <script>
508
- import { shallowRef } from 'vue'
705
+ /* eslint-disable no-alert, no-console */
706
+ import { shallowRef, markRaw } from 'vue'
509
707
  import {
510
708
  WarningFilled as ElIconWarningFilled,
511
709
  ArrowDown as ElIconArrowDown,
512
710
  ArrowLeft as ElIconArrowLeft,
513
711
  } from '@element-plus/icons-vue'
514
- /* eslint-disable no-alert, no-console */
515
712
  import Tooltip from './Tooltip.vue'
516
713
  import SelectionsGroup from './SelectionsGroup.vue'
517
714
  import TreeControls from './TreeControls.vue'
518
715
  import { MapSvgIcon, MapSvgSpriteColor } from '@abi-software/svg-sprite'
716
+ import '@abi-software/svg-sprite/dist/style.css'
519
717
  import SvgLegends from './legends/SvgLegends.vue'
520
718
  import {
521
719
  ElButton as Button,
@@ -525,6 +723,7 @@ import {
525
723
  ElRadioGroup as RadioGroup,
526
724
  ElRow as Row,
527
725
  ElSelect as Select,
726
+ ElDialog as Dialog,
528
727
  } from 'element-plus'
529
728
  import flatmapMarker from '../icons/flatmap-marker'
530
729
  import {
@@ -534,9 +733,82 @@ import {
534
733
  import yellowstar from '../icons/yellowstar'
535
734
  import ResizeSensor from 'css-element-queries/src/ResizeSensor'
536
735
  import * as flatmap from '@abi-software/flatmap-viewer'
736
+ import { AnnotationService } from '@abi-software/sparc-annotation'
737
+ import ConnectionDialog from './ConnectionDialog.vue'
537
738
  import { mapState } from 'pinia'
538
739
  import { useMainStore } from '@/store/index'
539
740
 
741
+ /**
742
+ * @param scopeElement Draggable scope area (Optional)
743
+ * @param dragElement Draggable element
744
+ */
745
+ const draggable = (scopeElement, dragElement) => {
746
+ let startX, startY, clickX, clickY, posX, posY
747
+ // reset position in case previous pupped up dialog is dragged
748
+ dragElement.style.left = ''
749
+ dragElement.style.top = ''
750
+ // const scopeRect = scopeElement.getBoundingClientRect()
751
+ // const dragRect = dragElement.getBoundingClientRect()
752
+
753
+ dragElement.addEventListener('mousedown', (e) => {
754
+ e.preventDefault();
755
+ startX = dragElement.offsetLeft
756
+ startY = dragElement.offsetTop
757
+ clickX = e.clientX
758
+ clickY = e.clientY
759
+
760
+ dragElement.addEventListener('mousemove', drag, false);
761
+ document.addEventListener('mouseup', () => {
762
+ dragElement.removeEventListener('mousemove', drag, false);
763
+ }, false);
764
+ }, false);
765
+
766
+ function drag(e) {
767
+ e.preventDefault();
768
+ posX = startX - (clickX - e.clientX)
769
+ posY = startY - (clickY - e.clientY)
770
+ // if (
771
+ // (posX > scopeRect.left && ((posX + dragRect.width) < scopeRect.right)) &&
772
+ // (posY > scopeRect.top && ((posY + dragRect.height) < scopeRect.bottom))
773
+ // ) {
774
+ dragElement.style.left = `${posX}px`;
775
+ dragElement.style.top = `${posY}px`;
776
+ // } else {
777
+ // if (posX <= scopeRect.left) {
778
+ // dragElement.style.left = '0px';
779
+ // } else if (posX + dragRect.width >= scopeRect.right) {
780
+ // dragElement.style.left = `${scopeRect.right - dragRect.width}px`;
781
+ // }
782
+ // if (posY <= scopeRect.top) {
783
+ // dragElement.style.top = '0px';
784
+ // } else if (posY + dragRect.height >= scopeRect.bottom) {
785
+ // dragElement.style.top = `${scopeRect.bottom - dragRect.height}px`;
786
+ // }
787
+ // }
788
+ }
789
+ }
790
+
791
+ const centroid = (geometry) => {
792
+ let featureGeometry = { lng: 0, lat: 0, }
793
+ let coordinates
794
+ if (geometry.type === "Polygon") {
795
+ coordinates = geometry.coordinates[0]
796
+ } else {
797
+ coordinates = geometry.coordinates
798
+ }
799
+ if (!(geometry.type === 'Point')) {
800
+ coordinates.map((coor) => {
801
+ featureGeometry.lng += parseFloat(coor[0])
802
+ featureGeometry.lat += parseFloat(coor[1])
803
+ })
804
+ featureGeometry.lng = featureGeometry.lng / coordinates.length
805
+ featureGeometry.lat = featureGeometry.lat / coordinates.length
806
+ } else {
807
+ featureGeometry.lng += parseFloat(coordinates[0])
808
+ featureGeometry.lat += parseFloat(coordinates[1])
809
+ }
810
+ return featureGeometry
811
+ }
540
812
 
541
813
  const processFTUs = (parent, key) => {
542
814
  const ftus = []
@@ -611,6 +883,7 @@ export default {
611
883
  RadioGroup,
612
884
  Row,
613
885
  Select,
886
+ Dialog,
614
887
  MapSvgIcon,
615
888
  MapSvgSpriteColor,
616
889
  Tooltip,
@@ -629,7 +902,310 @@ export default {
629
902
  //resolve this issue.
630
903
  this.setStateRequired = false
631
904
  },
905
+ setup(props) {
906
+ const annotator = markRaw(new AnnotationService(`${props.flatmapAPI}annotator`));
907
+ return { annotator }
908
+ },
632
909
  methods: {
910
+ /**
911
+ * @vuese
912
+ * Function to initialise drawing.
913
+ */
914
+ initialiseDrawing: function () {
915
+ this.inDrawing = false
916
+ this.initialiseDialog()
917
+ this.activeDrawTool = undefined
918
+ this.createdEvent = undefined
919
+ },
920
+ /**
921
+ * @vuese
922
+ * Function to cancel a newly drawn feature.
923
+ */
924
+ cancelDrawnFeature: function () {
925
+ if (this.createdEvent) {
926
+ this.closePopup()
927
+ this.annotationEntry = {
928
+ ...this.createdEvent.feature,
929
+ resourceId: this.serverURL,
930
+ }
931
+ this.rollbackAnnotationEvent()
932
+ this.initialiseDrawing()
933
+ }
934
+ },
935
+ /**
936
+ * @vuese
937
+ * Function to display connected features' tooltip for drawn connectivity.
938
+ * @arg id
939
+ */
940
+ displayConnectedFeatureTooltip: function (id) {
941
+ if (this.mapImp) {
942
+ const data = this.mapImp.featureProperties(id)
943
+ this.checkAndCreatePopups({ feature: data })
944
+ }
945
+ },
946
+ /**
947
+ * @vuese
948
+ * Function to confirm a newly drawn feature.
949
+ */
950
+ confirmDrawnFeature: function () {
951
+ if (this.createdEvent) {
952
+ this.checkAndCreatePopups(this.createdEvent)
953
+ // Add connection if exist to annotationEntry
954
+ // Connection will only be added in creating new drawn feature annotation
955
+ // And will not be updated if move drawn features
956
+ if (Object.keys(this.connectionEntry).length > 0) {
957
+ this.annotationEntry.feature.connection = this.connectionEntry
958
+ }
959
+ this.initialiseDrawing()
960
+ }
961
+ },
962
+ /**
963
+ * @vuese
964
+ * Function to initialise connection dialog.
965
+ */
966
+ initialiseDialog: function () {
967
+ this.connectionDisplay = false
968
+ this.connectionEntry = {}
969
+ },
970
+ /**
971
+ * @vuese
972
+ * Function to display the connection dialog after finalising a drawing.
973
+ */
974
+ connectionDialogPopup: function () {
975
+ const inactive = this.$el.querySelector('.drawConnection').classList.contains('inactive')
976
+ // disable click popup if icon inactive or in drawing
977
+ if (!inactive && !this.inDrawing) {
978
+ this.closePopup()
979
+ this.connectionDisplay = !this.connectionDisplay
980
+ }
981
+ },
982
+ /**
983
+ * @vuese
984
+ * Function to process the annotation toolbar click events.
985
+ * @arg type
986
+ */
987
+ drawingEvent: function (type) {
988
+ this.closePopup()
989
+ // disable mode icon click if any tool is active
990
+ if (this.drawnTypes.includes(type) && !this.activeDrawMode && !this.connectionDisplay) {
991
+ if (type === 'Point') {
992
+ const point = this.$el.querySelector('.mapbox-gl-draw_point')
993
+ this.$el.querySelector('.mapbox-gl-draw_point').click()
994
+ this.activeDrawTool = point.classList.contains('active') ? 'Point' : undefined
995
+ } else if (type === 'LineString') {
996
+ const line = this.$el.querySelector('.mapbox-gl-draw_line')
997
+ this.$el.querySelector('.mapbox-gl-draw_line').click()
998
+ this.activeDrawTool = line.classList.contains('active') ? 'LineString' : undefined
999
+ } else if (type === 'Polygon') {
1000
+ const polygon = this.$el.querySelector('.mapbox-gl-draw_polygon')
1001
+ this.$el.querySelector('.mapbox-gl-draw_polygon').click()
1002
+ this.activeDrawTool = polygon.classList.contains('active') ? 'Polygon' : undefined
1003
+ }
1004
+ // disable tool icon click if any mode is on
1005
+ } else if (this.drawModes.includes(type) && !this.activeDrawTool) {
1006
+ if (type === 'Delete') {
1007
+ if (
1008
+ this.currentDrawnFeature &&
1009
+ // For either no mode is on or edit is on
1010
+ (!this.activeDrawMode || this.activeDrawMode === 'Edit')
1011
+ ) {
1012
+ // Force simple_select a feature for delete event
1013
+ this.doubleClickedFeature = false
1014
+ this.changeAnnotationDrawMode({
1015
+ mode: 'simple_select',
1016
+ options: { featureIds: [this.currentDrawnFeature.id] }
1017
+ })
1018
+ this.deleteOrEditAnnotationFeature()
1019
+ }
1020
+ this.activeDrawMode = this.activeDrawMode === 'Delete' ? undefined : 'Delete'
1021
+ // clear currentDrawnFeature when quit delete mode
1022
+ if (!this.activeDrawMode) {
1023
+ this.currentDrawnFeature = undefined
1024
+ }
1025
+ } else if (type === 'Edit') {
1026
+ this.activeDrawMode = this.activeDrawMode === 'Edit' ? undefined : 'Edit'
1027
+ }
1028
+ }
1029
+ },
1030
+ /**
1031
+ * @vuese
1032
+ * Function to update the annotation draw mode.
1033
+ * @arg mode
1034
+ */
1035
+ changeAnnotationDrawMode: function (mode) {
1036
+ if (this.mapImp) {
1037
+ this.mapImp.changeAnnotationDrawMode(mode)
1038
+ }
1039
+ },
1040
+ /**
1041
+ * @vuese
1042
+ * Function to remove all drawn annotations from flatmap annotation layer.
1043
+ */
1044
+ clearAnnotationFeature: function () {
1045
+ if (
1046
+ this.mapImp &&
1047
+ this.drawnAnnotationFeatures &&
1048
+ this.drawnAnnotationFeatures.length > 0
1049
+ ) {
1050
+ this.mapImp.clearAnnotationFeature()
1051
+ }
1052
+ },
1053
+ /**
1054
+ * @vuese
1055
+ * Function to fire the ``trash`` action.
1056
+ * See https://github.com/mapbox/mapbox-gl-draw/blob/main/docs/API.md#trash-draw for more details.
1057
+ */
1058
+ deleteOrEditAnnotationFeature: function () {
1059
+ if (this.mapImp) {
1060
+ // Fire the 'trash' button
1061
+ // Not only use to remove features
1062
+ // 'simple_select' for DELETE and 'direct_select' for EDIT
1063
+ this.mapImp.removeAnnotationFeature()
1064
+ }
1065
+ },
1066
+ /**
1067
+ * @vuese
1068
+ * Function to rollback the failure drawn from flatmap annotation layer.
1069
+ */
1070
+ rollbackAnnotationEvent: function () {
1071
+ // For 'updated' and 'deleted' callback
1072
+ if (
1073
+ this.mapImp &&
1074
+ this.drawnAnnotationEvent.includes(this.annotationEntry.type)
1075
+ ) {
1076
+ this.mapImp.rollbackAnnotationEvent(this.annotationEntry)
1077
+ }
1078
+ },
1079
+ /**
1080
+ * @vuese
1081
+ * Function to commit the emitted ``annotation`` data from successful new drawn to flatmap annotation layer.
1082
+ * @arg annotation
1083
+ */
1084
+ commitAnnotationEvent: function (annotation) {
1085
+ if (
1086
+ this.mapImp &&
1087
+ this.drawnAnnotationEvent.includes(this.annotationEntry.type) &&
1088
+ // Only when annotation comments stored successfully
1089
+ annotation
1090
+ ) {
1091
+ this.annotationSubmitted = true
1092
+ this.mapImp.commitAnnotationEvent(this.annotationEntry)
1093
+ if (this.annotationEntry.type === 'deleted') {
1094
+ this.closePopup()
1095
+ } else {
1096
+ // Use to update 'this.drawnAnnotationFeatures' when created or updated
1097
+ this.addAnnotationFeature()
1098
+ }
1099
+ }
1100
+ },
1101
+ /**
1102
+ * @vuese
1103
+ * Function to add existing drawn annotations to flatmap.
1104
+ */
1105
+ setFeatureAnnotated: function () {
1106
+ if (this.mapImp) {
1107
+ this.annotator.annotatedItemIds(this.userToken, this.serverURL)
1108
+ .then((annotatedItemIds) => {
1109
+ if ('resource' in annotatedItemIds) {
1110
+ // The annotator has `resource` and `items` fields
1111
+ annotatedItemIds = annotatedItemIds.itemIds
1112
+ }
1113
+ for (const id of annotatedItemIds) {
1114
+ this.mapImp.setFeatureAnnotated(id)
1115
+ }
1116
+ })
1117
+ .catch((reason) => {
1118
+ console.log(reason) // Error!
1119
+ })
1120
+ }
1121
+ },
1122
+ /**
1123
+ * @vuese
1124
+ * Function to draw existing drawn annotations based on selector.
1125
+ */
1126
+ addAnnotationFeature: function () {
1127
+ if (this.mapImp) {
1128
+ if (!this.annotationSubmitted) this.clearAnnotationFeature()
1129
+ if (this.drawnType !== 'None') {
1130
+ if (!this.annotationSubmitted) this.loading = true
1131
+ const userId = this.annotatedType === 'Anyone' ?
1132
+ undefined : this.userInformation.orcid ?
1133
+ this.userInformation.orcid : '0000-0000-0000-0000'
1134
+ const participated = this.annotatedType === 'Anyone' ?
1135
+ undefined : this.annotatedType === 'Me' ?
1136
+ true : false
1137
+ this.annotator.annotatedItemIds(this.userToken, this.serverURL, userId, participated)
1138
+ .then((annotatedItemIds) => {
1139
+ if ('resource' in annotatedItemIds) {
1140
+ // The annotator has `resource` and `items` fields
1141
+ annotatedItemIds = annotatedItemIds.itemIds
1142
+ }
1143
+ this.annotator.drawnFeatures(this.userToken, this.serverURL, annotatedItemIds)
1144
+ .then((drawnFeatures) => {
1145
+ if ('resource' in drawnFeatures) {
1146
+ // The annotator has `resource` and `features` fields
1147
+ drawnFeatures = drawnFeatures.features
1148
+ }
1149
+ // Use to switch the displayed feature type
1150
+ if (this.drawnType !== 'All tools') {
1151
+ drawnFeatures = drawnFeatures.filter((feature) => {
1152
+ return feature.geometry.type === this.drawnType
1153
+ })
1154
+ }
1155
+ this.drawnAnnotationFeatures = drawnFeatures
1156
+ this.loading = false
1157
+ // No need to call 'addAnnotationFeature' when a new feature created, as it is already on the map
1158
+ if (!this.annotationSubmitted) {
1159
+ for (const feature of drawnFeatures) {
1160
+ this.mapImp.addAnnotationFeature(feature)
1161
+ }
1162
+ }
1163
+ })
1164
+ .catch((reason) => {
1165
+ console.log(reason) // Error!
1166
+ })
1167
+ })
1168
+ .catch((reason) => {
1169
+ console.log(reason) // Error!
1170
+ })
1171
+ }
1172
+ }
1173
+ },
1174
+ /**
1175
+ * @vuese
1176
+ * Function to display annotator toolbar.
1177
+ * @arg flag
1178
+ */
1179
+ showAnnotator: function (flag) {
1180
+ if (this.mapImp) {
1181
+ // Control the show/hide of the drawn annotations
1182
+ this.mapImp.showAnnotator(flag)
1183
+ // Hide default toolbar, we will use customised SVG icons instead
1184
+ this.$el.querySelector('.maplibregl-ctrl-group').style.display = 'none'
1185
+ }
1186
+ },
1187
+ /**
1188
+ * @vuese
1189
+ * Function to switch the type of annotation.
1190
+ * @arg flag
1191
+ */
1192
+ setDrawnType: function (flag) {
1193
+ this.drawnType = flag
1194
+ if (this.mapImp) {
1195
+ this.addAnnotationFeature()
1196
+ }
1197
+ },
1198
+ /**
1199
+ * @vuese
1200
+ * Function to switch the type of person who annotated.
1201
+ * @arg flag
1202
+ */
1203
+ setAnnotatedType: function (flag) {
1204
+ this.annotatedType = flag
1205
+ if (this.mapImp) {
1206
+ this.addAnnotationFeature()
1207
+ }
1208
+ },
633
1209
  /**
634
1210
  * @vuese
635
1211
  * Function to switch from 2D to 3D
@@ -830,22 +1406,69 @@ export default {
830
1406
  * by providing path model identifier, ``pathId``.
831
1407
  * @arg pathId
832
1408
  */
833
- highlightConnectedPaths: function (payload) {
1409
+ highlightConnectedPaths: async function (payload) {
834
1410
  if (this.mapImp) {
835
1411
  let paths = [...this.mapImp.pathModelNodes(payload)]
836
- // The line below matches the paths to the annIdToFeatureId map to get the feature ids
837
1412
 
1413
+ // The line below is to get the path features from the geojson ids
838
1414
  let pathFeatures = paths.map((p) => this.mapImp.featureProperties(p))
1415
+
1416
+ // Query the flatmap knowledge graph for connectivity, we use this to grab the origins
1417
+ let connectivity = await this.flatmapQueries.queryForConnectivity(payload)
1418
+
1419
+ // Check and flatten the origins node graph
1420
+ let originsFlat = connectivity?.ids?.dendrites?.flat().flat()
1421
+
1422
+ // Remove the origin nodes from the path features so that we only see downstream nodes
1423
+ pathFeatures = pathFeatures.filter((p) => !originsFlat.includes(p.models))
1424
+
839
1425
  let toHighlight = []
1426
+ let highlight = false
1427
+
1428
+ // Loop through the path features and check if we have origin nodes
840
1429
  pathFeatures.forEach((p) => {
1430
+
1431
+ // Get the nodes from each path feature
841
1432
  this.mapImp.nodePathModels(p.featureId).forEach((f) => {
842
- toHighlight.push(f)
1433
+ highlight = true
1434
+ // s2 here is the second level paths
1435
+ let s2 = this.mapImp.pathModelNodes(f)
1436
+ s2.forEach((s) => {
1437
+ let s2Feature = this.mapImp.featureProperties([s]) // get the feature properties for s2
1438
+ if (originsFlat.includes(s2Feature.models)) {
1439
+ highlight = false // if we have an origin node, we don't want to highlight the path
1440
+ return
1441
+ }
1442
+ })
1443
+
1444
+ if (highlight) {
1445
+ toHighlight.push(f)
1446
+ }
843
1447
  })
844
1448
  })
1449
+
845
1450
  // display connected paths
846
1451
  this.mapImp.zoomToFeatures(toHighlight, { noZoomIn: true })
847
1452
  }
848
1453
  },
1454
+ /**
1455
+ * @vuese
1456
+ * Function to enable/disable (show/hide) pathways with/without alert
1457
+ * by providing ``kay, value`` ``payload`` object ``{alertKey, true/false}``.
1458
+ * @arg payload
1459
+ */
1460
+ alertSelected: function (payload) {
1461
+ if (this.mapImp) {
1462
+ if (payload.value) {
1463
+ this.mapImp.clearVisibilityFilter()
1464
+ } else {
1465
+ const ALERT_FILTER = {
1466
+ NOT: {HAS: 'alert'}
1467
+ }
1468
+ this.mapImp.setVisibilityFilter(ALERT_FILTER)
1469
+ }
1470
+ }
1471
+ },
849
1472
  /**
850
1473
  * @vuese
851
1474
  * Function to enable/disable (show/hide) the system
@@ -913,6 +1536,22 @@ export default {
913
1536
  this.mapImp.enableConnectivityByTaxonIds(payload.key, payload.value)
914
1537
  }
915
1538
  },
1539
+ checkboxMouseEnterEmitted: function (payload) {
1540
+ if (this.mapImp) {
1541
+ if (payload.value) {
1542
+ let gid = this.mapImp.taxonFeatureIds(payload.key)
1543
+ this.mapImp.enableConnectivityByTaxonIds(payload.key, payload.value) // make sure path is visible
1544
+ this.mapImp.zoomToGeoJSONFeatures(gid, {noZoomIn: true})
1545
+ } else {
1546
+ // reset visibility of paths
1547
+ this.mapImp.selectGeoJSONFeatures("-1")
1548
+ payload.selections.forEach((item) => {
1549
+ let show = payload.checked.includes(item.taxon)
1550
+ this.mapImp.enableConnectivityByTaxonIds(item.taxon, show)
1551
+ })
1552
+ }
1553
+ }
1554
+ },
916
1555
  /**
917
1556
  * @vuese
918
1557
  * Function to show or hide connectivity features observed in particular species
@@ -959,6 +1598,71 @@ export default {
959
1598
  enablePanZoomEvents: function (flag) {
960
1599
  this.mapImp.enablePanZoomEvents(flag)
961
1600
  },
1601
+ /**
1602
+ * @vuese
1603
+ * Function to process annotation callbacks, invoked when events occur with the map.
1604
+ * @arg payload,
1605
+ * @arg data
1606
+ */
1607
+ annotationEventCallback: function (payload, data) {
1608
+ // Popup closed will trigger aborted event
1609
+ if (data.type === 'aborted') {
1610
+ // Rollback drawing when no new annotation submitted
1611
+ if (!this.annotationSubmitted) this.rollbackAnnotationEvent()
1612
+ else this.annotationSubmitted = false
1613
+ } else if (data.type === 'modeChanged') {
1614
+ // 'modeChanged' event is before 'created' event
1615
+ if (data.feature.mode.startsWith('draw_')) {
1616
+ // Reset data entry for every draw
1617
+ this.initialiseDialog()
1618
+ this.inDrawing = true
1619
+ } else if (data.feature.mode === 'simple_select' && this.inDrawing) {
1620
+ if (this.createdEvent) {
1621
+ this.connectionDisplay = true
1622
+ } else {
1623
+ // Reset if a invalid draw
1624
+ this.initialiseDrawing()
1625
+ }
1626
+ } else if (data.feature.mode === 'direct_select') {
1627
+ this.doubleClickedFeature = true
1628
+ }
1629
+ } else if (data.type === 'selectionChanged') {
1630
+ this.currentDrawnFeature =
1631
+ data.feature.features.length === 0 ?
1632
+ undefined :
1633
+ data.feature.features[0]
1634
+ payload.feature.feature = this.currentDrawnFeature
1635
+ if (!this.inDrawing) {
1636
+ this.initialiseDialog()
1637
+ // For exist drawn annotation features
1638
+ if (this.currentDrawnFeature) {
1639
+ let feature = this.drawnAnnotationFeatures
1640
+ .filter((feature) => feature.id === this.currentDrawnFeature.id)[0]
1641
+ if (feature && feature.connection) {
1642
+ this.connectionEntry = feature.connection
1643
+ }
1644
+ this.drawModeEvent(payload)
1645
+ }
1646
+ }
1647
+ } else {
1648
+ if (data.type === 'created' || data.type === 'updated') {
1649
+ if (data.type === 'updated' && data.feature.action) {
1650
+ data.positionUpdated = data.feature.action === 'move'
1651
+ }
1652
+ const feature = this.mapImp.refreshAnnotationFeatureGeometry(data.feature)
1653
+ payload.feature.feature = feature
1654
+ // NB. this might now be `null` if user has deleted it (before OK/Submit)
1655
+ // so maybe then no `service.addAnnotation` ??
1656
+ }
1657
+ // Once double click mouse to confirm drawing, 'aborted' event will be triggered.
1658
+ // Hence disable direct popup when 'created' event, dialog will be used instead.
1659
+ if (data.type === 'created') {
1660
+ this.createdEvent = payload
1661
+ } else {
1662
+ this.checkAndCreatePopups(payload)
1663
+ }
1664
+ }
1665
+ },
962
1666
  /**
963
1667
  * @vuese
964
1668
  * A callback function, invoked when events occur with the map.
@@ -967,67 +1671,187 @@ export default {
967
1671
  */
968
1672
  eventCallback: function () {
969
1673
  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
- }
1674
+ if (eventType === 'annotation') {
984
1675
  const payload = {
985
- dataset: data.dataset,
986
- biologicalSex: biologicalSex,
987
- taxonomy: taxonomy,
988
- resource: resource,
989
- label: label,
990
1676
  feature: data,
991
1677
  userData: args,
992
1678
  eventType: eventType,
993
- provenanceTaxonomy: taxons,
994
1679
  }
995
- if (eventType === 'click') {
996
- if (this.viewingMode === 'Network Discovery') {
997
- this.highlightConnectedPaths([data.models])
998
- } else {
999
- this.currentActive = data.models ? data.models : ''
1680
+ this.annotationEventCallback(payload, data)
1681
+ } else {
1682
+ if (eventType !== 'pan-zoom') {
1683
+ this.featuresAlert = data.alert
1684
+ const label = data.label
1685
+ const resource = [data.models]
1686
+ const taxonomy = this.entry
1687
+ const biologicalSex = this.biologicalSex
1688
+ let taxons = undefined
1689
+ if (data.taxons) {
1690
+ // check if data.taxons is string or array
1691
+ if (typeof data.taxons !== 'object') {
1692
+ taxons = JSON.parse(data.taxons)
1693
+ } else {
1694
+ taxons = data.taxons
1695
+ }
1000
1696
  }
1001
- } else if (
1002
- eventType === 'mouseenter' &&
1003
- !(this.viewingMode === 'Network Discovery')
1004
- ) {
1005
- this.currentHover = data.models ? data.models : ''
1697
+ const payload = {
1698
+ dataset: data.dataset,
1699
+ biologicalSex: biologicalSex,
1700
+ taxonomy: taxonomy,
1701
+ resource: resource,
1702
+ label: label,
1703
+ feature: data,
1704
+ userData: args,
1705
+ eventType: eventType,
1706
+ provenanceTaxonomy: taxons,
1707
+ }
1708
+ if (eventType === 'click') {
1709
+ if (this.viewingMode === 'Network Discovery') {
1710
+ this.highlightConnectedPaths([data.models])
1711
+ } else {
1712
+ this.currentActive = data.models ? data.models : ''
1713
+ // Stop adding features if dialog displayed
1714
+ if (this.inDrawing && !this.connectionDisplay) {
1715
+ // Only clicked connection data will be added
1716
+ let nodeLabel = data.label ? data.label : `Feature ${data.id}`
1717
+ // only the linestring will have connection at the current stage
1718
+ if (this.activeDrawTool === 'LineString') {
1719
+ this.connectionEntry[data.featureId] = Object.assign({label: nodeLabel},
1720
+ Object.fromEntries(
1721
+ Object.entries(data)
1722
+ .filter(([key]) => ['featureId', 'models'].includes(key))
1723
+ .map(([key, value]) => [(key === 'featureId') ? 'id' : key, value])))
1724
+ }
1725
+ }
1726
+ }
1727
+ } else if (
1728
+ eventType === 'mouseenter' &&
1729
+ !(this.viewingMode === 'Network Discovery')
1730
+ ) {
1731
+ this.currentHover = data.models ? data.models : ''
1732
+ }
1733
+ if (
1734
+ data &&
1735
+ data.type !== 'marker' &&
1736
+ eventType === 'click' &&
1737
+ !(this.viewingMode === 'Network Discovery') &&
1738
+ // Disable popup when drawing
1739
+ !this.inDrawing
1740
+ ) {
1741
+ this.checkAndCreatePopups(payload)
1742
+ }
1743
+ this.$emit('resource-selected', payload)
1744
+ } else {
1745
+ this.$emit('pan-zoom-callback', data)
1006
1746
  }
1007
- if (
1008
- data &&
1009
- data.type !== 'marker' &&
1010
- eventType === 'click' &&
1011
- !(this.viewingMode === 'Network Discovery')
1012
- ) {
1013
- this.checkAndCreatePopups(payload)
1747
+ }
1748
+ }
1749
+ },
1750
+ /**
1751
+ * A hack to implement connection dialog drag action and scope.
1752
+ */
1753
+ dialogCssHacks: function () {
1754
+ this.$nextTick(() => {
1755
+ const dialog = this.$el.querySelector('.connection-dialog')
1756
+ draggable(this.$el, dialog)
1757
+ // dialog popup at the click position
1758
+ // slightly change x or y if close to boundary
1759
+ let posX, posY
1760
+ const containerRect = this.$el.getBoundingClientRect()
1761
+ const dialogRect = dialog.getBoundingClientRect()
1762
+ if (this.dialogPosition.x > containerRect.width / 2) {
1763
+ posX = this.dialogPosition.x - dialogRect.width
1764
+ } else {
1765
+ posX = this.dialogPosition.x
1766
+ }
1767
+ if (this.dialogPosition.y > containerRect.height / 2) {
1768
+ posY = this.dialogPosition.y - dialogRect.height
1769
+ } else {
1770
+ posY = this.dialogPosition.y
1771
+ }
1772
+ dialog.style.transform =
1773
+ `translate(${posX - this.dialogPosition.offsetX}px, ${posY - this.dialogPosition.offsetY}px)`
1774
+ })
1775
+ },
1776
+ /**
1777
+ * A hack to handle the status of annotation tools.
1778
+ */
1779
+ drawIconCssHacks: function () {
1780
+ // set tool/mode icon status
1781
+ if (this.$el.querySelector('.iconSelected') || !this.connectionDisplay) {
1782
+ this.drawnTypes.map((t) => {
1783
+ const dtype = this.$el.querySelector(`.draw${t}`)
1784
+ if (dtype) {
1785
+ dtype.classList.remove('iconSelected');
1786
+ dtype.classList.remove('inactive');
1787
+ }
1788
+ })
1789
+ this.drawModes.map((m) => {
1790
+ this.$el.querySelector(`.draw${m}`).classList.remove('iconSelected');
1791
+ this.$el.querySelector(`.draw${m}`).classList.remove('inactive');
1792
+ })
1793
+ }
1794
+ if (this.activeDrawTool) {
1795
+ this.$el.querySelector(`.draw${this.activeDrawTool}`).classList.add('iconSelected');
1796
+ this.drawModes.map((m) => {
1797
+ this.$el.querySelector(`.draw${m}`).classList.add('inactive');
1798
+ })
1799
+ } else if (this.activeDrawMode || this.connectionDisplay) {
1800
+ if (this.activeDrawMode) {
1801
+ this.$el.querySelector(`.draw${this.activeDrawMode}`).classList.add('iconSelected');
1802
+ }
1803
+ this.drawnTypes.map((t) => {
1804
+ const dtype = this.$el.querySelector(`.draw${t}`)
1805
+ if (dtype) dtype.classList.add('inactive');
1806
+ })
1807
+ }
1808
+ },
1809
+ /**
1810
+ * @vuese
1811
+ * Function to fire annotation event based on the provided ``data``.
1812
+ * Either edit or delete event.
1813
+ * @arg data
1814
+ */
1815
+ drawModeEvent: function (data) {
1816
+ if (this.activeDrawMode) {
1817
+ // double click fires 'updated' callback
1818
+ if (this.doubleClickedFeature) {
1819
+ if (data.feature.feature.geometry.type !== 'Point') {
1820
+ // show tooltip and enter edit mode
1821
+ this.changeAnnotationDrawMode({
1822
+ mode: 'direct_select',
1823
+ options: { featureId: data.feature.feature.id }
1824
+ })
1825
+ this.deleteOrEditAnnotationFeature()
1014
1826
  }
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)
1827
+ this.doubleClickedFeature = false
1021
1828
  } 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)
1829
+ // single click fires delete
1830
+ if (this.activeDrawMode === 'Delete') {
1831
+ this.changeAnnotationDrawMode({
1832
+ mode: 'simple_select',
1833
+ options: { featureIds: [data.feature.feature.id] }
1834
+ })
1835
+ this.deleteOrEditAnnotationFeature()
1836
+ }
1028
1837
  }
1029
1838
  }
1030
1839
  },
1840
+ /**
1841
+ * Function to create connectivity body from existing entries.
1842
+ */
1843
+ createConnectivityBody: function () {
1844
+ if (Object.keys(this.connectionEntry).length > 0) {
1845
+ const features = Object.values(this.connectionEntry)
1846
+ const body = {
1847
+ type: 'connectivity',
1848
+ source: features[0],
1849
+ target: features[features.length - 1],
1850
+ intermediates: features.slice(1, -1),
1851
+ }
1852
+ this.annotationEntry.body = body
1853
+ }
1854
+ },
1031
1855
  /**
1032
1856
  * @vuese
1033
1857
  * Function triggered by viewing mode change.
@@ -1047,13 +1871,33 @@ export default {
1047
1871
  checkAndCreatePopups: async function (data) {
1048
1872
  // Call flatmap database to get the connection data
1049
1873
  if (this.viewingMode === 'Annotation') {
1050
- if (data.feature && data.feature.featureId && data.feature.models) {
1874
+ if (data.feature) {
1051
1875
  this.annotationEntry = {
1052
1876
  ...data.feature,
1053
- resource: this.serverURL,
1054
- resourceId: this.serverUUID,
1877
+ resourceId: this.serverURL,
1878
+ }
1879
+ if (data.feature.featureId && data.feature.models) {
1880
+ this.displayTooltip(data.feature.models)
1881
+ } else if (data.feature.feature) {
1882
+ if (this.inDrawing || this.activeDrawMode) {
1883
+ this.annotationSubmitted = false
1884
+ this.annotationEntry.featureId = data.feature.feature.id
1885
+ this.createConnectivityBody()
1886
+ this.displayTooltip(
1887
+ data.feature.feature.id,
1888
+ centroid(data.feature.feature.geometry)
1889
+ )
1890
+ } else {
1891
+ // Not allowed to update feature if not on edit mode
1892
+ if (data.feature.type === 'updated') {
1893
+ this.rollbackAnnotationEvent()
1894
+ }
1895
+ }
1896
+ // Hide dialog when updated or deleted event is fired and tooltip is displayed
1897
+ if (data.feature.type === 'updated' || data.feature.type === 'deleted') {
1898
+ this.initialiseDialog()
1899
+ }
1055
1900
  }
1056
- this.displayTooltip(data.feature.models)
1057
1901
  } else {
1058
1902
  this.annotation = {}
1059
1903
  }
@@ -1087,6 +1931,14 @@ export default {
1087
1931
  if (ftooltip) ftooltip.style.display = 'block'
1088
1932
  }
1089
1933
  },
1934
+ /**
1935
+ * @vuese
1936
+ * Function to close popup.
1937
+ */
1938
+ closePopup: function () {
1939
+ let cbutton = document.querySelector('.maplibregl-popup-close-button')
1940
+ if (cbutton) cbutton.click()
1941
+ },
1090
1942
  /**
1091
1943
  * @vuese
1092
1944
  * Function to close tooltip.
@@ -1227,28 +2079,26 @@ export default {
1227
2079
  * by providing featureId (``feature``).
1228
2080
  * @arg feature
1229
2081
  */
1230
- displayTooltip: function (feature) {
2082
+ displayTooltip: function (feature, geometry = undefined) {
1231
2083
  this.tooltipDisplay = true
2084
+ let featureId = undefined
2085
+ let options = { className: 'flatmapvuer-popover' }
2086
+ if (geometry) {
2087
+ featureId = feature
2088
+ options.annotationFeatureGeometry = geometry
2089
+ } else {
2090
+ featureId = this.mapImp.modelFeatureIds(feature)[0]
2091
+ if (!this.inDrawing) {
2092
+ options.positionAtLastClick = true
2093
+ }
2094
+ }
1232
2095
  if (!this.disableUI) {
1233
2096
  this.$nextTick(() => {
1234
- this.displayPopup(feature)
1235
- });
2097
+ this.mapImp.showPopup(featureId, this.$refs.tooltip.$el, options)
2098
+ this.popUpCssHacks()
2099
+ })
1236
2100
  }
1237
2101
  },
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
2102
  /**
1253
2103
  * @vuese
1254
2104
  * Function to open Flatmap Help Popup.
@@ -1429,7 +2279,6 @@ export default {
1429
2279
  )
1430
2280
  promise1.then((returnedObject) => {
1431
2281
  this.mapImp = returnedObject
1432
- this.serverUUID = this.mapImp.getIdentifier().uuid
1433
2282
  this.serverURL = this.mapImp.makeServerUrl('').slice(0, -1)
1434
2283
  let mapVersion = this.mapImp.details.version
1435
2284
  this.setFlightPathInfo(mapVersion)
@@ -1498,6 +2347,7 @@ export default {
1498
2347
  //this.layers = this.mapImp.getLayers();
1499
2348
  this.processSystems(this.mapImp.getSystems())
1500
2349
  this.processTaxon(this.flatmapAPI, this.mapImp.taxonIdentifiers)
2350
+ this.containsAlert = "alert" in this.mapImp.featureFilterRanges()
1501
2351
  this.addResizeButtonToMinimap()
1502
2352
  this.loading = false
1503
2353
  this.computePathControlsMaximumHeight()
@@ -1749,7 +2599,9 @@ export default {
1749
2599
  return {
1750
2600
  flatmapAPI: this.flatmapAPI,
1751
2601
  sparcAPI: this.sparcAPI,
1752
- userApiKey: this.userToken
2602
+ $annotator: this.annotator,
2603
+ userApiKey: this.userToken,
2604
+ getFeaturesAlert: () => this.featuresAlert,
1753
2605
  }
1754
2606
  },
1755
2607
  data: function () {
@@ -1759,7 +2611,6 @@ export default {
1759
2611
  //for the first time, otherwise it may display an arrow at a
1760
2612
  //undesired location.
1761
2613
  tooltipDisplay: false,
1762
- serverUUID: undefined,
1763
2614
  serverURL: undefined,
1764
2615
  layers: [],
1765
2616
  pathways: [],
@@ -1790,6 +2641,12 @@ export default {
1790
2641
  { value: false },
1791
2642
  { value: false },
1792
2643
  { value: false },
2644
+ { value: false },
2645
+ { value: false },
2646
+ { value: false },
2647
+ { value: false },
2648
+ { value: false },
2649
+ { value: false },
1793
2650
  ],
1794
2651
  yellowstar: yellowstar,
1795
2652
  isFC: false,
@@ -1801,7 +2658,7 @@ export default {
1801
2658
  tooltipEntry: createUnfilledTooltipData(),
1802
2659
  connectivityTooltipVisible: false,
1803
2660
  drawerOpen: false,
1804
- annotationRadio: false,
2661
+ featuresAlert: undefined,
1805
2662
  flightPath3DRadio: false,
1806
2663
  displayFlightPathOption: false,
1807
2664
  colourRadio: true,
@@ -1809,15 +2666,50 @@ export default {
1809
2666
  minimapResizeShow: false,
1810
2667
  minimapSmall: false,
1811
2668
  currentActive: '',
2669
+ currentDrawnFeature: undefined, // Clicked drawn annotation
1812
2670
  currentHover: '',
1813
2671
  viewingMode: 'Exploration',
1814
2672
  viewingModes: ['Annotation', 'Exploration', 'Network Discovery'],
2673
+ drawnType: 'All tools',
2674
+ drawnTypes: ['All tools', 'Point', 'LineString', 'Polygon', 'None'],
2675
+ annotatedType: 'Anyone',
2676
+ annotatedTypes: ['Anyone', 'Me', 'Others'],
1815
2677
  openMapRef: undefined,
1816
2678
  backgroundIconRef: undefined,
2679
+ annotator: undefined,
2680
+ userInformation: undefined,
2681
+ activeDrawTool: undefined,
2682
+ drawnAnnotationEvent: ['created', 'updated', 'deleted'],
2683
+ createdEvent: undefined,
2684
+ annotationSubmitted: false,
2685
+ inDrawing: false,
2686
+ connectionDisplay: false,
2687
+ connectionEntry: {},
2688
+ drawnAnnotationFeatures: undefined, // Store all exist drawn features
2689
+ doubleClickedFeature: false,
2690
+ activeDrawMode: undefined,
2691
+ drawModes: ['Delete', 'Edit'],
2692
+ dialogPosition: {
2693
+ offsetX: 0,
2694
+ offsetY: 0,
2695
+ x: undefined,
2696
+ y: undefined
2697
+ },
2698
+ containsAlert: false,
2699
+ alertOptions: [
2700
+ {
2701
+ label: 'Display Path With Alerts',
2702
+ key: 'alert',
2703
+ enabled: true,
2704
+ },
2705
+ ],
1817
2706
  }
1818
2707
  },
1819
2708
  computed: {
1820
2709
  ...mapState(useMainStore, ['userToken']),
2710
+ connection: function () {
2711
+ return Object.keys(this.connectionEntry).length > 0
2712
+ }
1821
2713
  },
1822
2714
  watch: {
1823
2715
  entry: function () {
@@ -1842,6 +2734,74 @@ export default {
1842
2734
  immediate: true,
1843
2735
  deep: true,
1844
2736
  },
2737
+ activeDrawTool: function () {
2738
+ this.drawIconCssHacks()
2739
+ },
2740
+ activeDrawMode: function () {
2741
+ this.drawIconCssHacks()
2742
+ },
2743
+ /**
2744
+ * hide dialog when connectionEntry is empty
2745
+ */
2746
+ connection: function (value) {
2747
+ const connectionIcon = this.$el.querySelector('.drawConnection')
2748
+ if (!value) {
2749
+ this.connectionDisplay = false
2750
+ connectionIcon.classList.add('inactive')
2751
+ } else {
2752
+ connectionIcon.classList.remove('inactive')
2753
+ }
2754
+ },
2755
+ /**
2756
+ * popup dialog via click icon
2757
+ */
2758
+ connectionDisplay: function (display) {
2759
+ const connectionIcon = this.$el.querySelector('.drawConnection')
2760
+ if (display) {
2761
+ connectionIcon.classList.add('iconSelected')
2762
+ this.dialogCssHacks()
2763
+ } else {
2764
+ connectionIcon.classList.remove('iconSelected')
2765
+ }
2766
+ this.drawIconCssHacks()
2767
+ },
2768
+ /**
2769
+ * Set dialog offset when flatmap annotator used
2770
+ */
2771
+ dialogPosition: {
2772
+ handler: function () {
2773
+ const containerRect = this.$el.getBoundingClientRect()
2774
+ this.dialogPosition.offsetX = containerRect.x
2775
+ this.dialogPosition.offsetY = containerRect.y
2776
+ },
2777
+ deep: true,
2778
+ once: true,
2779
+ },
2780
+ viewingMode: function (mode) {
2781
+ if (mode === 'Annotation') {
2782
+ this.$el.querySelector('.maplibregl-canvas').addEventListener('click', (e) => {
2783
+ e.preventDefault();
2784
+ this.dialogPosition.x = e.clientX
2785
+ this.dialogPosition.y = e.clientY
2786
+ // use to fix the draw point pop up position issue
2787
+ if (this.activeDrawTool === 'Point') {
2788
+ this.dialogCssHacks()
2789
+ }
2790
+ }, false)
2791
+ this.loading = true
2792
+ this.annotator.authenticate(this.userToken).then((userData) => {
2793
+ if (userData.name && userData.email) {
2794
+ this.showAnnotator(true)
2795
+ this.userInformation = userData
2796
+ this.setFeatureAnnotated()
2797
+ if (!this.drawnAnnotationFeatures) {
2798
+ this.addAnnotationFeature()
2799
+ }
2800
+ }
2801
+ this.loading = false
2802
+ })
2803
+ } else this.showAnnotator(false)
2804
+ },
1845
2805
  disableUI: function (isUIDisabled) {
1846
2806
  if (isUIDisabled) {
1847
2807
  this.closeTooltip()
@@ -1924,6 +2884,7 @@ export default {
1924
2884
  position: absolute;
1925
2885
  bottom: 0px;
1926
2886
  transition: all 1s ease;
2887
+ z-index: 8;
1927
2888
  &.open {
1928
2889
  left: 0px;
1929
2890
  }
@@ -1998,7 +2959,8 @@ export default {
1998
2959
  }
1999
2960
 
2000
2961
  :deep(.maplibregl-popup) {
2001
- max-width: 300px !important;
2962
+ z-index: 10;
2963
+ max-width: 330px !important;
2002
2964
  }
2003
2965
 
2004
2966
  :deep(.flatmap-tooltip-popup) {
@@ -2170,12 +3132,19 @@ export default {
2170
3132
  }
2171
3133
  }
2172
3134
 
2173
- .zoomOut {
2174
- padding-left: 8px;
3135
+ .drawPoint, .drawLineString, .drawPolygon,
3136
+ .drawDelete, .drawEdit, .drawConnection,
3137
+ .zoomIn, .zoomOut, .fitWindow {
3138
+ padding: 4px;
2175
3139
  }
2176
3140
 
2177
- .fitWindow {
2178
- padding-left: 8px;
3141
+ .iconSelected {
3142
+ color: var(--el-color-primary-light-5) !important;
3143
+ }
3144
+
3145
+ .inactive {
3146
+ color: #DDDDDD !important;
3147
+ cursor: not-allowed !important;
2179
3148
  }
2180
3149
 
2181
3150
  .yellow-star-legend {
@@ -2187,6 +3156,7 @@ export default {
2187
3156
  bottom: 16px;
2188
3157
  position: absolute;
2189
3158
  transition: all 1s ease;
3159
+ z-index: 10;
2190
3160
  &.open {
2191
3161
  left: 322px;
2192
3162
  }
@@ -2200,7 +3170,7 @@ export default {
2200
3170
  background-color: #ffffff;
2201
3171
  border: 1px solid $app-primary-color;
2202
3172
  box-shadow: 0px 2px 12px 0px rgba(0, 0, 0, 0.06);
2203
- height: 290px;
3173
+ height: fit-content;
2204
3174
  min-width: 200px;
2205
3175
  .el-popper__arrow {
2206
3176
  &:before {
@@ -2383,25 +3353,27 @@ export default {
2383
3353
  }
2384
3354
  }
2385
3355
 
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
3356
  :deep(.maplibregl-popup-content) {
2398
3357
  padding: 0px;
2399
3358
  }
2400
3359
 
3360
+ .bottom-draw-control {
3361
+ background-color: var(--el-color-primary-light-9);
3362
+ padding: 4px 4px 2px 4px;
3363
+ border-style: solid;
3364
+ border-color: var(--el-color-primary-light-5);
3365
+ border-radius: 1rem;
3366
+ position: absolute;
3367
+ right: calc(50vw - 100px);;
3368
+ bottom: 16px;
3369
+ z-index: 10;
3370
+ }
3371
+
2401
3372
  .bottom-right-control {
2402
3373
  position: absolute;
2403
3374
  right: 16px;
2404
3375
  bottom: 16px;
3376
+ z-index: 10;
2405
3377
  }
2406
3378
 
2407
3379
  :deep(.my-drawer) {
@@ -2587,6 +3559,12 @@ export default {
2587
3559
  }
2588
3560
  }
2589
3561
  }
3562
+
3563
+ .connection-dialog {
3564
+ position: absolute;
3565
+ z-index: 10;
3566
+ cursor: move;
3567
+ }
2590
3568
  </style>
2591
3569
 
2592
3570
  <style lang="scss">