@kalisio/kdk 2.1.8 → 2.2.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.
Files changed (248) hide show
  1. package/.travis.test.sh +42 -10
  2. package/README.md +2 -2
  3. package/core/api/application.js +6 -1
  4. package/core/api/authentication.js +17 -1
  5. package/core/api/db.js +7 -2
  6. package/core/api/hooks/hooks.authentication.js +4 -2
  7. package/core/api/hooks/hooks.authorisations.js +12 -2
  8. package/core/api/hooks/hooks.model.js +5 -5
  9. package/core/api/hooks/hooks.organisations.js +0 -4
  10. package/core/api/services/account/account.hooks.js +10 -6
  11. package/core/api/services/account/account.service.js +1 -1
  12. package/{map/api/services/geocoder/geocoder.hooks.js → core/api/services/import-export/import-export.hooks.js} +7 -5
  13. package/core/api/services/import-export/import-export.service.js +11 -0
  14. package/core/api/services/index.js +13 -1
  15. package/core/api/services/users/users.hooks.js +2 -3
  16. package/core/client/api.js +16 -14
  17. package/core/client/capabilities.js +6 -2
  18. package/core/client/components/KContent.vue +11 -1
  19. package/core/client/components/KDialog.vue +17 -15
  20. package/core/client/components/KSponsor.vue +1 -1
  21. package/core/client/components/KTextArea.vue +5 -1
  22. package/core/client/components/app/KAbout.vue +1 -2
  23. package/core/client/components/app/KWelcome.vue +3 -5
  24. package/core/client/components/chart/KTimeSeriesChart.vue +24 -37
  25. package/core/client/components/collection/KColumn.vue +20 -17
  26. package/core/client/components/editor/KModalEditor.vue +0 -2
  27. package/core/client/components/form/KChipsField.vue +12 -2
  28. package/core/client/components/form/KColorField.vue +12 -2
  29. package/core/client/components/form/KColorScaleField.vue +12 -2
  30. package/core/client/components/form/KDateTimeRangeField.vue +12 -2
  31. package/core/client/components/form/KDatetimeField.vue +12 -2
  32. package/core/client/components/form/KEmailField.vue +12 -2
  33. package/core/client/components/form/KFileField.vue +12 -2
  34. package/core/client/components/form/KForm.vue +43 -9
  35. package/core/client/components/form/KIconField.vue +12 -2
  36. package/core/client/components/form/KItemField.vue +25 -4
  37. package/core/client/components/form/KNumberField.vue +12 -2
  38. package/core/client/components/form/KOptionsField.vue +12 -2
  39. package/core/client/components/form/KPasswordField.vue +12 -2
  40. package/core/client/components/form/KPhoneField.vue +13 -3
  41. package/core/client/components/form/KPropertyItemField.vue +12 -2
  42. package/core/client/components/form/KResolutionField.vue +126 -0
  43. package/core/client/components/form/KRoleField.vue +12 -2
  44. package/core/client/components/form/KSelectField.vue +14 -4
  45. package/core/client/components/form/KTextField.vue +12 -2
  46. package/core/client/components/form/KTextareaField.vue +13 -3
  47. package/core/client/components/form/KToggleField.vue +12 -2
  48. package/core/client/components/form/KTokenField.vue +12 -2
  49. package/core/client/components/form/KUnitField.vue +12 -2
  50. package/core/client/components/form/KUrlField.vue +12 -2
  51. package/core/client/components/input/KIconChooser.vue +10 -12
  52. package/core/client/components/input/KPalette.vue +2 -1
  53. package/core/client/components/layout/KPage.vue +5 -4
  54. package/core/client/components/layout/KWindow.vue +10 -10
  55. package/core/client/components/media/KColorScale.vue +26 -20
  56. package/core/client/components/media/KImageViewer.vue +57 -33
  57. package/core/client/components/media/KShape.vue +14 -103
  58. package/core/client/components/screen/KRegisterScreen.vue +0 -1
  59. package/core/client/components/screen/KScreenFooter.vue +0 -18
  60. package/core/client/components/team/KAddMember.vue +14 -11
  61. package/core/client/components/team/KGroupsActivity.vue +14 -0
  62. package/core/client/components/team/KMembersActivity.vue +12 -0
  63. package/core/client/components/team/KTagsActivity.vue +14 -0
  64. package/core/client/components/time/KDateTime.vue +23 -7
  65. package/core/client/components/time/KTimeControl.vue +142 -0
  66. package/core/client/components/tool/KExportTool.vue +57 -0
  67. package/core/client/composables/collection.js +0 -1
  68. package/core/client/composables/pwa.js +0 -1
  69. package/core/client/composables/schema.js +1 -1
  70. package/core/client/composables/session.js +30 -6
  71. package/core/client/exporter.js +141 -0
  72. package/core/client/i18n/core_en.json +93 -24
  73. package/core/client/i18n/core_fr.json +94 -24
  74. package/core/client/index.js +3 -0
  75. package/core/client/layout.js +34 -14
  76. package/core/client/local-storage.js +8 -6
  77. package/core/client/mixins/index.js +0 -1
  78. package/core/client/mixins/mixin.base-field.js +24 -2
  79. package/core/client/mixins/mixin.object-proxy.js +0 -1
  80. package/core/client/readers/reader.csv.js +1 -1
  81. package/core/client/search.js +2 -1
  82. package/core/client/services/index.js +2 -1
  83. package/core/client/services/local-settings.service.js +4 -4
  84. package/core/client/theme.js +3 -3
  85. package/core/client/time.js +4 -0
  86. package/core/client/units.js +149 -4
  87. package/core/client/utils/index.js +13 -6
  88. package/core/client/utils/utils.account.js +1 -1
  89. package/core/client/utils/utils.colors.js +43 -0
  90. package/core/client/utils/utils.platform.js +0 -1
  91. package/core/client/utils/utils.pwa.js +14 -14
  92. package/core/client/utils/utils.session.js +1 -1
  93. package/core/client/utils/utils.shapes.js +270 -0
  94. package/core/client/utils/utils.time.js +37 -0
  95. package/core/common/permissions.js +3 -0
  96. package/core/common/schemas/settings.update.json +50 -29
  97. package/extras/css/core.variables.scss +3 -1
  98. package/extras/tours/map/navigation-bar.js +17 -15
  99. package/extras/tours/map/timeline.js +33 -33
  100. package/map/api/config/categories.cjs +4 -1
  101. package/map/api/hooks/hooks.catalog.js +39 -0
  102. package/map/api/hooks/hooks.features.js +23 -3
  103. package/map/api/hooks/hooks.query.js +31 -10
  104. package/map/api/models/projects.model.mongodb.js +8 -0
  105. package/map/api/services/catalog/catalog.hooks.js +5 -3
  106. package/map/api/services/features/features.hooks.js +18 -6
  107. package/map/api/services/index.js +22 -6
  108. package/map/api/services/projects/projects.hooks.js +118 -0
  109. package/map/client/capture.js +16 -0
  110. package/map/client/cesium/utils/index.js +3 -0
  111. package/map/client/cesium/utils/utils.events.js +30 -0
  112. package/map/client/cesium/utils/utils.popup.js +17 -0
  113. package/map/client/cesium/{utils.js → utils/utils.style.js} +53 -49
  114. package/map/client/components/KCapture.vue +50 -0
  115. package/map/client/components/KCaptureTextArea.vue +53 -0
  116. package/map/client/components/KCompass.vue +2 -2
  117. package/map/client/components/KFeaturesChart.vue +1 -1
  118. package/map/client/components/KFeaturesFilter.vue +2 -2
  119. package/map/client/components/KLayerStyleForm.vue +256 -430
  120. package/map/client/components/KLevelSlider.vue +1 -1
  121. package/map/client/components/KNorth.vue +31 -0
  122. package/map/client/components/KProjectMenu.vue +88 -0
  123. package/map/client/components/KTimezoneMap.vue +36 -23
  124. package/map/client/components/catalog/KAddLayer.vue +3 -4
  125. package/map/client/components/catalog/KConnectLayer.vue +16 -4
  126. package/map/client/components/catalog/KCreateLayer.vue +1 -2
  127. package/map/client/components/catalog/KCreateProject.vue +100 -0
  128. package/map/client/components/catalog/KCreateView.vue +25 -2
  129. package/map/client/components/catalog/KLayersPanel.vue +24 -27
  130. package/map/client/components/catalog/KLayersSelector.vue +1 -1
  131. package/map/client/components/catalog/KProjectEditor.vue +91 -0
  132. package/map/client/components/catalog/KProjectManager.vue +60 -0
  133. package/map/client/components/catalog/KProjectSelector.vue +38 -0
  134. package/map/client/components/catalog/KProjectsPanel.vue +153 -0
  135. package/map/client/components/catalog/KSelectLayers.vue +96 -0
  136. package/map/client/components/catalog/KSelectViews.vue +96 -0
  137. package/map/client/components/catalog/KViewsPanel.vue +66 -30
  138. package/map/client/components/form/KDirectionField.vue +24 -5
  139. package/map/client/components/form/KLayerCategoryField.vue +12 -2
  140. package/map/client/components/form/KLocationField.vue +20 -5
  141. package/map/client/components/form/KOwsLayerField.vue +12 -2
  142. package/map/client/components/form/KOwsServiceField.vue +12 -2
  143. package/map/client/components/form/KSelectLayersField.vue +159 -0
  144. package/map/client/components/form/KSelectViewsField.vue +121 -0
  145. package/map/client/components/form/KTimezoneField.vue +24 -17
  146. package/map/client/components/legend/KColorScaleLegend.vue +1 -1
  147. package/map/client/components/legend/KLayerLegend.vue +61 -0
  148. package/map/client/components/legend/KLegend.vue +45 -44
  149. package/map/client/components/legend/KLegendRenderer.vue +5 -3
  150. package/map/client/components/legend/KSymbolsLegend.vue +12 -10
  151. package/map/client/components/legend/KVariablesLegend.vue +78 -0
  152. package/map/client/components/location/KGeocodersFilter.vue +2 -4
  153. package/map/client/components/location/KLocationMap.vue +48 -17
  154. package/map/client/components/location/KLocationSearch.vue +13 -3
  155. package/map/client/components/tools/KSearchTool.vue +17 -12
  156. package/map/client/components/widget/KElevationProfile.vue +16 -19
  157. package/map/client/components/widget/KMapillaryViewer.vue +21 -22
  158. package/map/client/components/widget/KTimeSeries.vue +35 -23
  159. package/map/client/composables/activity.js +15 -2
  160. package/map/client/composables/catalog.js +66 -0
  161. package/map/client/composables/highlight.js +56 -20
  162. package/map/client/composables/index.js +2 -0
  163. package/map/client/composables/location.js +25 -18
  164. package/map/client/composables/project.js +122 -0
  165. package/map/client/geolocation.js +1 -1
  166. package/map/client/globe.js +2 -0
  167. package/map/client/i18n/map_en.json +123 -76
  168. package/map/client/i18n/map_fr.json +124 -72
  169. package/map/client/index.js +3 -0
  170. package/map/client/init.js +17 -0
  171. package/map/client/leaflet/GSMaPLayer.js +16 -17
  172. package/map/client/leaflet/ShapeMarker.js +40 -0
  173. package/map/client/leaflet/TiledFeatureLayer.js +1 -1
  174. package/map/client/leaflet/TiledMeshLayer.js +11 -15
  175. package/map/client/leaflet/TiledWindLayer.js +6 -10
  176. package/map/client/leaflet/utils/index.js +4 -0
  177. package/map/client/leaflet/utils/utils.events.js +41 -0
  178. package/map/client/leaflet/utils/utils.popup.js +21 -0
  179. package/map/client/leaflet/utils/utils.style.js +191 -0
  180. package/map/client/leaflet/utils/utils.tiles.js +87 -0
  181. package/map/client/map.js +2 -0
  182. package/map/client/mixins/globe/mixin.base-globe.js +29 -21
  183. package/map/client/mixins/globe/mixin.geojson-layers.js +132 -69
  184. package/map/client/mixins/globe/mixin.popup.js +2 -1
  185. package/map/client/mixins/globe/mixin.style.js +6 -4
  186. package/map/client/mixins/globe/mixin.tooltip.js +8 -3
  187. package/map/client/mixins/map/mixin.base-map.js +13 -11
  188. package/map/client/mixins/map/mixin.edit-layers.js +15 -15
  189. package/map/client/mixins/map/mixin.forecast-layers.js +3 -1
  190. package/map/client/mixins/map/mixin.geojson-layers.js +56 -20
  191. package/map/client/mixins/map/mixin.georaster-layers.js +4 -11
  192. package/map/client/mixins/map/mixin.heatmap-layers.js +1 -1
  193. package/map/client/mixins/map/mixin.popup.js +2 -1
  194. package/map/client/mixins/map/mixin.style.js +4 -67
  195. package/map/client/mixins/map/mixin.tiled-mesh-layers.js +2 -1
  196. package/map/client/mixins/map/mixin.tiled-wind-layers.js +4 -2
  197. package/map/client/mixins/map/mixin.tooltip.js +2 -1
  198. package/map/client/mixins/mixin.activity.js +66 -191
  199. package/map/client/mixins/mixin.catalog-panel.js +6 -6
  200. package/map/client/mixins/mixin.context.js +13 -10
  201. package/map/client/mixins/mixin.feature-service.js +29 -300
  202. package/map/client/mixins/mixin.weacast.js +11 -17
  203. package/map/client/pixi-utils.js +1 -1
  204. package/map/client/planets.js +58 -0
  205. package/map/client/utils/index.js +6 -0
  206. package/map/client/utils/utils.capture.js +176 -0
  207. package/map/client/utils/utils.catalog.js +149 -0
  208. package/map/client/utils/utils.features.js +364 -0
  209. package/map/client/utils/utils.js +0 -151
  210. package/map/client/utils/utils.layers.js +174 -0
  211. package/map/client/utils/utils.location.js +91 -23
  212. package/map/client/utils/utils.project.js +8 -0
  213. package/map/client/utils/utils.schema.js +0 -1
  214. package/map/client/utils/utils.style.js +297 -0
  215. package/map/client/utils.all.js +2 -2
  216. package/map/client/utils.globe.js +1 -1
  217. package/map/client/utils.map.js +1 -1
  218. package/map/common/permissions.js +2 -0
  219. package/map/common/schemas/capture.create.json +132 -0
  220. package/map/common/schemas/projects.create.json +52 -0
  221. package/map/common/schemas/projects.update.json +52 -0
  222. package/package.json +6 -5
  223. package/test/api/core/account.test.js +20 -0
  224. package/test/api/core/config/default.cjs +16 -3
  225. package/test/api/core/import-export.test.js +86 -0
  226. package/test/api/core/test-log-2023-12-19.log +7 -0
  227. package/test/api/core/test-log-2024-01-04.log +14 -0
  228. package/test/api/map/catalog.test.js +164 -0
  229. package/test/api/map/index.test.js +25 -61
  230. package/test/api/map/test-log-2023-11-24.log +121 -0
  231. package/test/api/map/test-log-2023-12-12.log +29 -0
  232. package/test/api/map/test-log-2023-12-13.log +5 -0
  233. package/test/api/map/test-log-2024-01-04.log +2 -0
  234. package/test/api/map/test-log-2024-01-11.log +1 -0
  235. package/test/api/map/test-log-2024-01-25.log +19 -0
  236. package/test/client/core/layout.js +25 -5
  237. package/test/client/core/utils.js +7 -0
  238. package/test/client/map/catalog.js +78 -1
  239. package/test/client/map/time.js +2 -1
  240. package/core/client/components/screen/KEndpointScreen.vue +0 -80
  241. package/core/client/mixins/mixin.account.js +0 -61
  242. package/extras/icons/kdk.png +0 -0
  243. package/map/api/services/geocoder/geocoder.service.js +0 -79
  244. package/map/client/components/KCaptureToolbar.vue +0 -155
  245. package/map/client/components/KColorLegend.vue +0 -349
  246. package/map/client/components/KTimeline.vue +0 -293
  247. package/map/client/components/KUrlLegend.vue +0 -122
  248. package/map/client/leaflet/utils.js +0 -246
@@ -1,11 +1,11 @@
1
1
  import _ from 'lodash'
2
2
  import logger from 'loglevel'
3
3
  import config from 'config'
4
- import explode from '@turf/explode'
5
- import { Loading, Dialog } from 'quasar'
4
+ import { Store, utils as kCoreUtils } from '../../../core/client/index.js'
6
5
  import { Geolocation } from '../geolocation.js'
7
- import { setEngineJwt } from '../utils.js'
8
- import { i18n, utils as kCoreUtils } from '../../../core/client/index.js'
6
+ import { setEngineJwt, getLayers, getCategories } from '../utils/utils.catalog.js'
7
+ import * as layers from '../utils/utils.layers.js'
8
+ import { getCatalogProjectQuery } from '../utils/utils.project.js'
9
9
 
10
10
  export const activity = {
11
11
  data () {
@@ -41,33 +41,27 @@ export const activity = {
41
41
  return []
42
42
  },
43
43
  async getCatalogLayers () {
44
- let layers = []
45
- // We get layers coming from global catalog first if any
46
- const globalCatalogService = this.$api.getService('catalog', '')
47
- if (globalCatalogService) {
48
- const response = await globalCatalogService.find()
49
- layers = layers.concat(response.data)
44
+ const query = {}
45
+ // Do we get layers coming from project ?
46
+ if (this.project) {
47
+ Object.assign(query, this.catalogProjectQuery ? this.catalogProjectQuery : getCatalogProjectQuery(this.project))
48
+ } else {
49
+ this.project = null
50
50
  }
51
+
52
+ // We get layers coming from global catalog first if any
53
+ let layers = await getLayers({ query })
51
54
  // Then we get layers coming from contextual catalog if any
52
- const catalogService = this.$api.getService('catalog')
53
- if (catalogService && (catalogService !== globalCatalogService)) {
54
- const response = await catalogService.find()
55
- layers = layers.concat(response.data)
56
- }
57
- // Do we need to inject a token ?
58
- await setEngineJwt(layers)
55
+ const context = Store.get('context')
56
+ if (context) layers = layers.concat(await getLayers({ query, context }))
59
57
  return layers
60
58
  },
61
59
  async addCatalogLayer (layer) {
62
60
  // Check if available for current engine
63
61
  if (layer[this.engine]) {
64
- // Process i18n
65
- if (layer.i18n) i18n.registerTranslation(layer.i18n)
66
- layer.label = this.$tie(layer.name)
67
- layer.description = this.$tie(layer.description)
68
62
  // Check for Weacast API availability
69
63
  const isWeacastLayer = _.get(layer, `${this.engine}.type`, '').startsWith('weacast.')
70
- if (isWeacastLayer && (!this.weacastApi || !this.forecastModel)) return
64
+ if (isWeacastLayer && (!this.getWeacastApi() || !this.forecastModel)) return
71
65
  await this.addLayer(layer)
72
66
  }
73
67
  // Filter layers with variables, not just visible ones because we might want to
@@ -82,26 +76,14 @@ export const activity = {
82
76
  }
83
77
  },
84
78
  async getCatalogCategories () {
85
- let categories = []
86
79
  // We get categories coming from global catalog first if any
87
- const globalCatalogService = this.$api.getService('catalog', '')
88
- if (globalCatalogService) {
89
- const response = await globalCatalogService.find({ query: { type: 'Category' } })
90
- categories = categories.concat(response.data)
91
- }
80
+ let categories = await getCategories()
92
81
  // Then we get categories coming from contextual catalog if any
93
- const catalogService = this.$api.getService('catalog')
94
- if (catalogService && (catalogService !== globalCatalogService)) {
95
- const response = await catalogService.find({ query: { type: 'Category' } })
96
- categories = categories.concat(response.data)
97
- }
82
+ const context = Store.get('context')
83
+ if (context) categories = categories.concat(await getCategories({ context }))
98
84
  return categories
99
85
  },
100
86
  async addCatalogCategory (category) {
101
- // Process i18n
102
- if (category.i18n) i18n.registerTranslation(category.i18n)
103
- category.label = this.$tie(category.name)
104
- category.description = this.$tie(category.description)
105
87
  this.layerCategories.push(category)
106
88
  },
107
89
  async refreshLayerCategories () {
@@ -128,45 +110,21 @@ export const activity = {
128
110
  if (baseLayer) await this.showLayer(baseLayer.name)
129
111
  }
130
112
  },
131
- isInMemoryLayer (layer) {
132
- return layer._id === undefined
133
- },
134
- isUserLayer (layer) {
135
- return (_.get(layer, 'scope') === 'user')
136
- },
137
- isFeatureLayer (layer) {
138
- return (_.get(layer, 'service') === 'features')
139
- },
140
- hasFeatureSchema (layer) {
141
- return _.has(layer, 'schema')
142
- },
113
+ isInMemoryLayer: layers.isInMemoryLayer,
114
+ isUserLayer: layers.isUserLayer,
115
+ isFeatureLayer: layers.isFeatureLayer,
116
+ hasFeatureSchema: layers.hasFeatureSchema,
143
117
  isLayerSelectable (layer) {
144
118
  // Only possible when not edited by default
145
119
  if ((typeof this.isLayerEdited === 'function') && this.isLayerEdited(layer)) return false
146
- return _.get(layer, 'isSelectable', true)
147
- },
148
- isLayerProbable (layer) {
149
- return _.get(layer, 'isProbable', false)
150
- },
151
- isLayerStorable (layer) {
152
- // Only possible when not saved by default
153
- if (layer._id) return false
154
- return _.get(layer, 'isStorable', this.isUserLayer(layer))
155
- },
156
- isLayerEditable (layer) {
157
- // Only possible when saved by default
158
- if (!layer._id) return false
159
- return _.get(layer, 'isEditable', this.isUserLayer(layer))
160
- },
161
- isLayerRemovable (layer) {
162
- return _.get(layer, 'isRemovable', this.isUserLayer(layer))
163
- },
164
- isLayerStyleEditable (layer) {
165
- return _.get(layer, 'isStyleEditable', this.isUserLayer(layer))
166
- },
167
- isLayerDataEditable (layer) {
168
- return _.get(layer, 'isDataEditable', this.isUserLayer(layer) && this.isFeatureLayer(layer))
169
- },
120
+ return layers.isLayerSelectable(layer)
121
+ },
122
+ isLayerProbable: layers.isLayerProbable,
123
+ isLayerStorable: layers.isLayerStorable,
124
+ isLayerEditable: layers.isLayerEditable,
125
+ isLayerRemovable: layers.isLayerRemovable,
126
+ isLayerStyleEditable: layers.isLayerStyleEditable,
127
+ isLayerDataEditable: layers.isLayerDataEditable,
170
128
  async resetLayer (layer) {
171
129
  // Reset layer with new setup but keep track of current visibility state
172
130
  // as adding the layer back will restore default visibility state
@@ -217,90 +175,26 @@ export const activity = {
217
175
  async onSaveLayer (layer) {
218
176
  // Stop any running edition
219
177
  if ((typeof this.isLayerEdited === 'function') && this.isLayerEdited(layer)) await this.stopEditLayer('accept')
178
+ let createdLayer
220
179
  // Take care that WFS layers rely on the same type as our own feature layers
221
180
  if (!_.has(layer, 'wfs') && _.get(layer, `${this.engine}.type`) === 'geoJson') {
222
181
  const geoJson = this.toGeoJson(layer.name)
223
- // Check for invalid features first
224
- const check = this.checkFeatures(geoJson)
225
- if (check.kinks.length > 0) {
226
- const result = await kCoreUtils.dialog({
227
- title: this.$t('mixins.activity.INVALID_FEATURES_DIALOG_TITLE', { features: check.kinks }),
228
- message: this.$t('mixins.activity.INVALID_FEATURES_DIALOG_MESSAGE', { features: check.kinks }),
229
- options: {
230
- type: 'toggle',
231
- model: [],
232
- items: [
233
- { label: this.$t('mixins.activity.DOWNLOAD_INVALID_FEATURES_LABEL'), value: 'download' }
234
- ]
235
- },
236
- html: true,
237
- ok: {
238
- label: this.$t('OK'),
239
- flat: true
240
- },
241
- cancel: {
242
- label: this.$t('CANCEL'),
243
- flat: true
244
- }
245
- })
246
- if (!result.ok) return
247
- // Export invalid features if required
248
- if (_.get(result, 'data', []).includes('download')) {
249
- kCoreUtils.downloadAsBlob(JSON.stringify({ type: 'FeatureCollection', features: check.kinks }),
250
- this.$t('mixins.activity.INVALID_FEATURES_FILE'), 'application/json;charset=utf-8;')
251
- }
252
- }
253
- // Change data source from in-memory to features service
254
- _.set(layer, 'service', 'features')
255
- if (_.has(layer, 'leaflet')) _.set(layer, 'leaflet.source', '/api/features')
256
- if (_.has(layer, 'cesium')) _.set(layer, 'cesium.source', '/api/features')
257
- const features = (geoJson.type === 'FeatureCollection' ? geoJson.features : [geoJson])
258
- // If too much data use tiling
259
- // The threshold is based on the number of points and not features.
260
- // Indeed otherwise the complexity will be different depending on the geometry type
261
- // (eg a bucket of 1000 polygons can actually contains a lot of points).
262
- let nbPoints = 0
263
- features.forEach(feature => {
264
- nbPoints += explode(feature).features.length
265
- })
266
- if (this.is2D() && (nbPoints > 5000)) {
267
- _.set(layer, 'leaflet.tiled', true)
268
- _.set(layer, 'leaflet.minZoom', 15)
269
- }
270
- Loading.show({ message: this.$t('mixins.activity.SAVING_LABEL', { processed: 0, total: features.length }), html: true })
271
- try {
272
- let createdLayer = await this.$api.getService('catalog')
273
- .create(_.omit(layer, ['actions', 'label', 'isVisible', 'isDisabled']))
274
- const chunkSize = _.get(this, 'activityOptions.featuresChunkSize', 5000)
275
- let nbFeatures = 0
276
- // We use the generated DB ID as layer ID on features
277
- await this.createFeatures(geoJson, createdLayer._id, chunkSize, (i, chunk) => {
278
- // Update saving message according to new chunk data
279
- nbFeatures += chunk.length
280
- Loading.show({
281
- message: this.$t('mixins.activity.SAVING_LABEL', { processed: nbFeatures, total: features.length }),
282
- html: true
283
- })
284
- })
285
- // Because we save all features in a single service use filtering to separate layers
286
- createdLayer = await this.$api.getService('catalog').patch(createdLayer._id, { baseQuery: { layer: createdLayer._id } })
287
- // Reset layer with new setup
288
- await this.resetLayer(createdLayer)
289
- if (_.get(layer, 'leaflet.tiled')) {
290
- this.$notify({ type: 'positive', message: this.$t('mixins.activity.SAVE_DIALOG_MESSAGE'), timeout: 10000, html: true })
291
- }
292
- } catch (error) {
293
- // User error message on operation should be raised by error hook, otherwise this is more a coding error
294
- logger.error(`[KDK] ${error}`)
295
- }
296
- Loading.hide()
182
+ createdLayer = await layers.saveGeoJsonLayer(layer, geoJson, _.get(this, 'activityOptions.featuresChunkSize', 5000))
297
183
  } else {
298
184
  // Otherwise simply save in catalog
299
- const createdLayer = await this.$api.getService('catalog')
300
- .create(_.omit(layer, ['actions', 'label', 'isVisible', 'isDisabled']))
301
- // Reset layer with new setup
302
- await this.resetLayer(createdLayer)
185
+ createdLayer = await layers.saveLayer(layer)
186
+ }
187
+ // Add layer to current project ?
188
+ if (this.project) {
189
+ this.project.layers.push({ _id: createdLayer._id })
190
+ await this.$api.getService('projects').patch(this.project._id, {
191
+ layers: this.project.layers
192
+ })
303
193
  }
194
+ // Reset layer with new setup
195
+ // It should be triggerred by real-time event
196
+ // but as we might not always use sockets perform it anyway
197
+ if (createdLayer) await this.resetLayer(createdLayer)
304
198
  },
305
199
  editLayerByName (name, editOptions = {}) {
306
200
  // this one is used through postRobot to trigger edition
@@ -324,39 +218,14 @@ export const activity = {
324
218
  await this.stopEditLayer(status)
325
219
  },
326
220
  async onRemoveLayer (layer) {
327
- Dialog.create({
328
- title: this.$t('mixins.activity.REMOVE_DIALOG_TITLE', { layer: layer.label || layer.name }),
329
- message: this.$t('mixins.activity.REMOVE_DIALOG_MESSAGE', { layer: layer.label || layer.name }),
330
- html: true,
331
- ok: {
332
- label: this.$t('OK'),
333
- flat: true
334
- },
335
- cancel: {
336
- label: this.$t('CANCEL'),
337
- flat: true
338
- }
339
- }).onOk(async () => {
340
- Loading.show({ message: this.$t('mixins.activity.REMOVING_LABEL'), html: true })
341
- try {
342
- // Stop any running edition
343
- if ((typeof this.isLayerEdited === 'function') && this.isLayerEdited(layer)) await this.stopEditLayer('reject')
344
- if (layer._id) {
345
- // If persistent feature layer remove features as well
346
- if (this.isFeatureLayer(layer)) {
347
- await this.removeFeatures(layer._id)
348
- }
349
- await this.$api.getService('catalog').remove(layer._id)
350
- }
351
- // Actual layer removal should be triggerred by real-time event
352
- // but as we might not always use sockets perform it anyway
353
- this.removeLayer(layer.name)
354
- } catch (error) {
355
- // User error message on operation should be raised by error hook, otherwise this is more a coding error
356
- logger.error(`[KDK] ${error}`)
357
- }
358
- Loading.hide()
359
- })
221
+ // Stop any running edition
222
+ if ((typeof this.isLayerEdited === 'function') && this.isLayerEdited(layer)) await this.stopEditLayer('reject')
223
+ if (await layers.removeLayer(layer)) {
224
+ // Actual layer removal should be triggerred by real-time event
225
+ // but as we might not always use sockets perform it anyway
226
+ this.removeLayer(layer.name)
227
+ }
228
+ // Removing the layer should automatically update all projects
360
229
  },
361
230
  onEngineReady (engine) {
362
231
  this.engine = engine
@@ -401,10 +270,13 @@ export const activity = {
401
270
  this.catalogListeners = {}
402
271
  if (globalCatalogService.events !== undefined) {
403
272
  globalCatalogService.events.forEach(event => {
404
- this.catalogListeners[event] = (object) => this.onCatalogUpdated(event, object)
405
- globalCatalogService.on(event, this.catalogListeners[event])
406
- if (catalogService && (catalogService !== globalCatalogService)) {
407
- catalogService.on(event, this.catalogListeners[event])
273
+ // Avoid reentrance
274
+ if (!this.catalogListeners[event]) {
275
+ this.catalogListeners[event] = (object) => this.onCatalogUpdated(event, object)
276
+ globalCatalogService.on(event, this.catalogListeners[event])
277
+ if (catalogService && (catalogService !== globalCatalogService)) {
278
+ catalogService.on(event, this.catalogListeners[event])
279
+ }
408
280
  }
409
281
  })
410
282
  }
@@ -415,9 +287,12 @@ export const activity = {
415
287
  const catalogService = this.$api.getService('catalog')
416
288
  if (globalCatalogService.events !== undefined) {
417
289
  globalCatalogService.events.forEach(event => {
418
- globalCatalogService.removeListener(event, this.catalogListeners[event])
419
- if (catalogService && (catalogService !== globalCatalogService)) {
420
- catalogService.removeListener(event, this.catalogListeners[event])
290
+ // Avoid reentrance
291
+ if (this.catalogListeners[event]) {
292
+ globalCatalogService.removeListener(event, this.catalogListeners[event])
293
+ if (catalogService && (catalogService !== globalCatalogService)) {
294
+ catalogService.removeListener(event, this.catalogListeners[event])
295
+ }
421
296
  }
422
297
  })
423
298
  }
@@ -3,24 +3,24 @@ export const catalogPanel = {
3
3
  panelStyle () {
4
4
  const screenHeight = this.$q.screen.height
5
5
  this.scrollAreaMaxHeight = screenHeight * 0.75 // 75vh
6
- this.scrollAreaMaxWidth = 390
6
+ this.scrollAreaMinWidth = 390
7
7
  if (this.$q.screen.lt.sm) {
8
8
  this.scrollAreaMaxHeight = screenHeight * 0.6
9
- this.scrollAreaMaxWidth = 300
9
+ this.scrollAreaMinWidth = 300
10
10
  } else if (this.$q.screen.lt.md) {
11
11
  this.scrollAreaMaxHeight = screenHeight * 0.65
12
- this.scrollAreaMaxWidth = 330
12
+ this.scrollAreaMinWidth = 330
13
13
  } else if (this.$q.screen.lt.lg) {
14
14
  this.scrollAreaMaxHeight = screenHeight * 0.7
15
- this.scrollAreaMaxWidth = 360
15
+ this.scrollAreaMinWidth = 360
16
16
  }
17
- return `height: ${this.scrollAreaMaxHeight}px; width: ${this.scrollAreaMaxWidth}px`
17
+ return `height: ${this.scrollAreaMaxHeight}px; min-width: ${this.scrollAreaMinWidth}px;'`
18
18
  }
19
19
  },
20
20
  data () {
21
21
  return {
22
22
  scrollAreaMaxHeight: 0,
23
- scrollAreaMaxWidth: 390
23
+ scrollAreaMinWidth: 390
24
24
  }
25
25
  }
26
26
  }
@@ -1,13 +1,14 @@
1
1
  import _ from 'lodash'
2
2
  import moment from 'moment'
3
3
  import sift from 'sift'
4
- import { utils as kCoreUtils, Time, Store } from '../../../core/client/index.js'
4
+ import { utils as kCoreUtils, Time, Store, LocalStorage } from '../../../core/client/index.js'
5
+ import { isTerrainLayer } from '../utils/utils.layers.js'
5
6
 
6
7
  export const context = {
7
8
  methods: {
8
9
  getContextKey (context) {
9
10
  // Generate a unique key to store context based on app name, map activity name and context type
10
- return this.getAppName().toLowerCase() + `-${this.activityName}-${context}`
11
+ return `${this.activityName}-${context}`
11
12
  },
12
13
  shouldRestoreContext (context) {
13
14
  // Use user settings except if the view has explicitly revoked restoration
@@ -61,7 +62,7 @@ export const context = {
61
62
  break
62
63
  case 'time': {
63
64
  targetParameters = {
64
- time: Time.getCurrentTime().restoreContext()
65
+ time: Time.getCurrentTime()
65
66
  }
66
67
  break
67
68
  }
@@ -130,8 +131,8 @@ export const context = {
130
131
  // Take care that 3D requires at least a terrain layer.
131
132
  // So if we try to remove a terrain layer without activating a new one, keep it active
132
133
  // (this can be the case when the view has been saved from the 2D activity and restored in the 3D activity).
133
- const inactivatedTerrainLayer = _.find(inactivatedLayers, (name) => this.hasLayer(name) && (this.getLayerByName(name).type === 'TerrainLayer'))
134
- const activatedTerrainLayer = _.find(activedLayers, (name) => this.hasLayer(name) && (this.getLayerByName(name).type === 'TerrainLayer'))
134
+ const inactivatedTerrainLayer = _.find(inactivatedLayers, (name) => this.hasLayer(name) && isTerrainLayer(this.getLayerByName(name)))
135
+ const activatedTerrainLayer = _.find(activedLayers, (name) => this.hasLayer(name) && isTerrainLayer(this.getLayerByName(name)))
135
136
  if (inactivatedTerrainLayer && !activatedTerrainLayer) _.pull(inactivatedLayers, inactivatedTerrainLayer)
136
137
  await Promise.all(activedLayers.map(layer => this.showLayer(layer)))
137
138
  await Promise.all(inactivatedLayers.map(layer => this.hideLayer(layer)))
@@ -160,16 +161,16 @@ export const context = {
160
161
  if (!_.isEqual(this.getRouteContext(context), targetParameters)) {
161
162
  this.updateRouteContext(context, targetParameters)
162
163
  }
163
- window.localStorage.setItem(this.getContextKey(context), JSON.stringify(targetParameters))
164
+ LocalStorage.set(this.getContextKey(context), targetParameters)
164
165
  }
165
166
  },
166
167
  async restoreContext (context) {
167
168
  let targetParameters = this.getRouteContext(context)
168
169
  // Restore from local storage/catalog if no route parameters
169
170
  if (_.isEmpty(targetParameters)) {
170
- const savedParameters = window.localStorage.getItem(this.getContextKey(context))
171
- if (this.shouldRestoreContext(context) && savedParameters) {
172
- targetParameters = JSON.parse(savedParameters)
171
+ const savedParameters = LocalStorage.get(this.getContextKey(context))
172
+ if (this.shouldRestoreContext(context) && !_.isEmpty(savedParameters)) {
173
+ targetParameters = savedParameters
173
174
  // Backward compatibility: we previously stored the bounds as an array
174
175
  if (Array.isArray(targetParameters)) {
175
176
  targetParameters = {
@@ -227,7 +228,8 @@ export const context = {
227
228
  if (hasLayers) {
228
229
  Object.assign(context, this.getContextParameters('layers'))
229
230
  }
230
- await this.$api.getService('catalog').create(context)
231
+ context = await this.$api.getService('catalog').create(context)
232
+ return context
231
233
  },
232
234
  async loadContext (context) {
233
235
  // If not context object retrieve it from catalog first
@@ -242,6 +244,7 @@ export const context = {
242
244
  if (!context) throw new Error('Cannot find or invalid context')
243
245
  this.setContextParameters('view', context)
244
246
  this.setContextParameters('layers', context)
247
+ return context
245
248
  },
246
249
  updateViewSettings (enabled) {
247
250
  if (!enabled) this.clearContext('view')