@kalisio/kdk 2.1.9 → 2.2.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.
Files changed (272) hide show
  1. package/.travis.test.sh +42 -10
  2. package/README.md +2 -2
  3. package/core/api/application.js +7 -2
  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/KModal.vue +1 -1
  21. package/core/client/components/KSponsor.vue +1 -1
  22. package/core/client/components/KTextArea.vue +5 -1
  23. package/core/client/components/app/KAbout.vue +1 -2
  24. package/core/client/components/app/KWelcome.vue +3 -5
  25. package/core/client/components/chart/KTimeSeriesChart.vue +24 -37
  26. package/core/client/components/collection/KColumn.vue +20 -17
  27. package/core/client/components/editor/KModalEditor.vue +0 -2
  28. package/core/client/components/form/KChipsField.vue +12 -2
  29. package/core/client/components/form/KColorField.vue +12 -2
  30. package/core/client/components/form/KColorScaleField.vue +12 -2
  31. package/core/client/components/form/KDateTimeRangeField.vue +12 -2
  32. package/core/client/components/form/KDatetimeField.vue +12 -2
  33. package/core/client/components/form/KEmailField.vue +12 -2
  34. package/core/client/components/form/KFileField.vue +12 -2
  35. package/core/client/components/form/KForm.vue +47 -9
  36. package/core/client/components/form/KIconField.vue +12 -2
  37. package/core/client/components/form/KItemField.vue +25 -4
  38. package/core/client/components/form/KNumberField.vue +12 -2
  39. package/core/client/components/form/KOptionsField.vue +12 -2
  40. package/core/client/components/form/KPasswordField.vue +12 -2
  41. package/core/client/components/form/KPhoneField.vue +13 -3
  42. package/core/client/components/form/KPropertyItemField.vue +12 -2
  43. package/core/client/components/form/KResolutionField.vue +126 -0
  44. package/core/client/components/form/KRoleField.vue +12 -2
  45. package/core/client/components/form/KSelectField.vue +14 -4
  46. package/core/client/components/form/KTextField.vue +12 -2
  47. package/core/client/components/form/KTextareaField.vue +13 -3
  48. package/core/client/components/form/KToggleField.vue +12 -2
  49. package/core/client/components/form/KTokenField.vue +12 -2
  50. package/core/client/components/form/KUnitField.vue +12 -2
  51. package/core/client/components/form/KUrlField.vue +12 -2
  52. package/core/client/components/input/KIconChooser.vue +10 -12
  53. package/core/client/components/input/KPalette.vue +2 -1
  54. package/core/client/components/layout/KPage.vue +5 -4
  55. package/core/client/components/layout/KWindow.vue +10 -10
  56. package/core/client/components/media/KColorScale.vue +25 -19
  57. package/core/client/components/media/KImageViewer.vue +57 -33
  58. package/core/client/components/media/KShape.vue +14 -103
  59. package/core/client/components/screen/KRegisterScreen.vue +0 -1
  60. package/core/client/components/screen/KScreenFooter.vue +0 -18
  61. package/core/client/components/team/KAddMember.vue +16 -22
  62. package/core/client/components/team/KGroupsActivity.vue +14 -0
  63. package/core/client/components/team/KMembersActivity.vue +12 -0
  64. package/core/client/components/team/KTagsActivity.vue +14 -0
  65. package/core/client/components/time/KDateTime.vue +23 -7
  66. package/core/client/components/time/KTimeControl.vue +142 -0
  67. package/core/client/components/tool/KExportTool.vue +57 -0
  68. package/core/client/composables/collection.js +0 -1
  69. package/core/client/composables/pwa.js +0 -1
  70. package/core/client/composables/schema.js +1 -1
  71. package/core/client/composables/session.js +30 -6
  72. package/core/client/exporter.js +147 -0
  73. package/core/client/i18n/core_en.json +91 -23
  74. package/core/client/i18n/core_fr.json +92 -23
  75. package/core/client/index.js +3 -0
  76. package/core/client/layout.js +34 -14
  77. package/core/client/local-storage.js +8 -6
  78. package/core/client/mixins/index.js +0 -1
  79. package/core/client/mixins/mixin.base-field.js +24 -2
  80. package/core/client/mixins/mixin.object-proxy.js +0 -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 +150 -5
  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/icons/wind-speed-0.svg +8 -0
  99. package/extras/icons/wind-speed-10.svg +8 -0
  100. package/extras/icons/wind-speed-100.svg +12 -0
  101. package/extras/icons/wind-speed-105.svg +13 -0
  102. package/extras/icons/wind-speed-15.svg +9 -0
  103. package/extras/icons/wind-speed-20.svg +9 -0
  104. package/extras/icons/wind-speed-25.svg +10 -0
  105. package/extras/icons/wind-speed-30.svg +10 -0
  106. package/extras/icons/wind-speed-35.svg +11 -0
  107. package/extras/icons/wind-speed-40.svg +11 -0
  108. package/extras/icons/wind-speed-45.svg +12 -0
  109. package/extras/icons/wind-speed-5.svg +9 -0
  110. package/extras/icons/wind-speed-50.svg +9 -0
  111. package/extras/icons/wind-speed-55.svg +10 -0
  112. package/extras/icons/wind-speed-60.svg +10 -0
  113. package/extras/icons/wind-speed-65.svg +11 -0
  114. package/extras/icons/wind-speed-70.svg +11 -0
  115. package/extras/icons/wind-speed-75.svg +12 -0
  116. package/extras/icons/wind-speed-80.svg +12 -0
  117. package/extras/icons/wind-speed-85.svg +13 -0
  118. package/extras/icons/wind-speed-90.svg +13 -0
  119. package/extras/icons/wind-speed-95.svg +14 -0
  120. package/extras/tours/map/navigation-bar.js +17 -15
  121. package/extras/tours/map/timeline.js +33 -33
  122. package/map/api/config/categories.cjs +4 -1
  123. package/map/api/hooks/hooks.catalog.js +39 -0
  124. package/map/api/hooks/hooks.features.js +23 -3
  125. package/map/api/hooks/hooks.query.js +65 -21
  126. package/map/api/models/projects.model.mongodb.js +8 -0
  127. package/map/api/services/catalog/catalog.hooks.js +5 -3
  128. package/map/api/services/features/features.hooks.js +18 -6
  129. package/map/api/services/index.js +22 -6
  130. package/map/api/services/projects/projects.hooks.js +118 -0
  131. package/map/client/capture.js +16 -0
  132. package/map/client/cesium/utils/index.js +4 -0
  133. package/map/client/cesium/utils/utils.events.js +30 -0
  134. package/map/client/cesium/utils/utils.features.js +8 -0
  135. package/map/client/cesium/utils/utils.popup.js +17 -0
  136. package/map/client/cesium/utils/utils.style.js +137 -0
  137. package/map/client/components/KCapture.vue +50 -0
  138. package/map/client/components/KCaptureTextArea.vue +53 -0
  139. package/map/client/components/KCompass.vue +2 -2
  140. package/map/client/components/KFeaturesChart.vue +1 -1
  141. package/map/client/components/KFeaturesFilter.vue +2 -2
  142. package/map/client/components/KLayerStyleForm.vue +288 -454
  143. package/map/client/components/KLevelSlider.vue +1 -1
  144. package/map/client/components/KNorth.vue +31 -0
  145. package/map/client/components/KProjectMenu.vue +88 -0
  146. package/map/client/components/KTimezoneMap.vue +36 -24
  147. package/map/client/components/catalog/KAddLayer.vue +3 -4
  148. package/map/client/components/catalog/KConnectLayer.vue +20 -4
  149. package/map/client/components/catalog/KCreateLayer.vue +1 -2
  150. package/map/client/components/catalog/KCreateProject.vue +100 -0
  151. package/map/client/components/catalog/KCreateView.vue +25 -2
  152. package/map/client/components/catalog/KLayersPanel.vue +24 -27
  153. package/map/client/components/catalog/KLayersSelector.vue +1 -1
  154. package/map/client/components/catalog/KProjectEditor.vue +91 -0
  155. package/map/client/components/catalog/KProjectManager.vue +60 -0
  156. package/map/client/components/catalog/KProjectSelector.vue +38 -0
  157. package/map/client/components/catalog/KProjectsPanel.vue +153 -0
  158. package/map/client/components/catalog/KSelectLayers.vue +96 -0
  159. package/map/client/components/catalog/KSelectViews.vue +96 -0
  160. package/map/client/components/catalog/KViewsPanel.vue +66 -30
  161. package/map/client/components/form/KDirectionField.vue +24 -5
  162. package/map/client/components/form/KLayerCategoryField.vue +12 -2
  163. package/map/client/components/form/KLocationField.vue +20 -5
  164. package/map/client/components/form/KOwsLayerField.vue +12 -2
  165. package/map/client/components/form/KOwsServiceField.vue +12 -2
  166. package/map/client/components/form/KSelectLayersField.vue +159 -0
  167. package/map/client/components/form/KSelectViewsField.vue +121 -0
  168. package/map/client/components/form/KTimezoneField.vue +24 -17
  169. package/map/client/components/legend/KColorScaleLegend.vue +6 -2
  170. package/map/client/components/legend/KLayerLegend.vue +71 -0
  171. package/map/client/components/legend/KLegend.vue +54 -51
  172. package/map/client/components/legend/KLegendRenderer.vue +5 -3
  173. package/map/client/components/legend/KSymbolsLegend.vue +12 -10
  174. package/map/client/components/legend/KVariablesLegend.vue +78 -0
  175. package/map/client/components/location/KGeocodersFilter.vue +2 -4
  176. package/map/client/components/location/KLocationCardSection.vue +8 -4
  177. package/map/client/components/location/KLocationMap.vue +48 -17
  178. package/map/client/components/location/KLocationSearch.vue +13 -3
  179. package/map/client/components/tools/KSearchTool.vue +17 -12
  180. package/map/client/components/widget/KElevationProfile.vue +16 -19
  181. package/map/client/components/widget/KMapillaryViewer.vue +21 -22
  182. package/map/client/components/widget/KTimeSeries.vue +35 -29
  183. package/map/client/composables/activity.js +15 -2
  184. package/map/client/composables/catalog.js +81 -0
  185. package/map/client/composables/highlight.js +45 -30
  186. package/map/client/composables/index.js +2 -0
  187. package/map/client/composables/location.js +25 -18
  188. package/map/client/composables/probe.js +4 -1
  189. package/map/client/composables/project.js +122 -0
  190. package/map/client/composables/weather.js +3 -3
  191. package/map/client/geolocation.js +1 -1
  192. package/map/client/globe.js +2 -0
  193. package/map/client/i18n/map_en.json +127 -76
  194. package/map/client/i18n/map_fr.json +128 -72
  195. package/map/client/index.js +3 -0
  196. package/map/client/init.js +17 -0
  197. package/map/client/leaflet/GSMaPLayer.js +16 -17
  198. package/map/client/leaflet/ShapeMarker.js +51 -0
  199. package/map/client/leaflet/TiledFeatureLayer.js +39 -9
  200. package/map/client/leaflet/TiledMeshLayer.js +13 -15
  201. package/map/client/leaflet/TiledWindLayer.js +6 -10
  202. package/map/client/leaflet/utils/index.js +4 -0
  203. package/map/client/leaflet/utils/utils.events.js +41 -0
  204. package/map/client/leaflet/utils/utils.popup.js +21 -0
  205. package/map/client/leaflet/utils/utils.style.js +195 -0
  206. package/map/client/leaflet/utils/utils.tiles.js +87 -0
  207. package/map/client/map.js +2 -0
  208. package/map/client/mixins/globe/mixin.base-globe.js +39 -18
  209. package/map/client/mixins/globe/mixin.geojson-layers.js +139 -69
  210. package/map/client/mixins/globe/mixin.popup.js +2 -1
  211. package/map/client/mixins/globe/mixin.style.js +6 -4
  212. package/map/client/mixins/globe/mixin.tooltip.js +8 -3
  213. package/map/client/mixins/map/mixin.base-map.js +53 -28
  214. package/map/client/mixins/map/mixin.edit-layers.js +15 -15
  215. package/map/client/mixins/map/mixin.forecast-layers.js +3 -1
  216. package/map/client/mixins/map/mixin.geojson-layers.js +60 -20
  217. package/map/client/mixins/map/mixin.georaster-layers.js +4 -11
  218. package/map/client/mixins/map/mixin.heatmap-layers.js +1 -1
  219. package/map/client/mixins/map/mixin.popup.js +2 -1
  220. package/map/client/mixins/map/mixin.style.js +4 -67
  221. package/map/client/mixins/map/mixin.tiled-mesh-layers.js +2 -1
  222. package/map/client/mixins/map/mixin.tiled-wind-layers.js +4 -2
  223. package/map/client/mixins/map/mixin.tooltip.js +2 -1
  224. package/map/client/mixins/mixin.activity.js +71 -192
  225. package/map/client/mixins/mixin.catalog-panel.js +6 -6
  226. package/map/client/mixins/mixin.context.js +12 -9
  227. package/map/client/mixins/mixin.feature-service.js +29 -300
  228. package/map/client/mixins/mixin.weacast.js +11 -17
  229. package/map/client/pixi-utils.js +1 -1
  230. package/map/client/planets.js +66 -0
  231. package/map/client/utils/index.js +6 -0
  232. package/map/client/utils/utils.capture.js +176 -0
  233. package/map/client/utils/utils.catalog.js +166 -0
  234. package/map/client/utils/utils.features.js +364 -0
  235. package/map/client/utils/utils.js +0 -151
  236. package/map/client/utils/utils.layers.js +175 -0
  237. package/map/client/utils/utils.location.js +91 -23
  238. package/map/client/utils/utils.project.js +8 -0
  239. package/map/client/utils/utils.schema.js +0 -1
  240. package/map/client/utils/utils.style.js +309 -0
  241. package/map/client/utils.all.js +2 -2
  242. package/map/client/utils.globe.js +1 -1
  243. package/map/client/utils.map.js +1 -1
  244. package/map/common/permissions.js +2 -0
  245. package/map/common/schemas/capture.create.json +132 -0
  246. package/map/common/schemas/projects.create.json +52 -0
  247. package/map/common/schemas/projects.update.json +52 -0
  248. package/map/common/wms-utils.js +8 -3
  249. package/package.json +6 -5
  250. package/test/api/core/account.test.js +20 -0
  251. package/test/api/core/config/default.cjs +16 -3
  252. package/test/api/core/import-export.test.js +86 -0
  253. package/test/api/core/test-log-2024-01-04.log +14 -0
  254. package/test/api/map/catalog.test.js +164 -0
  255. package/test/api/map/index.test.js +25 -61
  256. package/test/api/map/test-log-2024-01-04.log +2 -0
  257. package/test/api/map/test-log-2024-01-11.log +1 -0
  258. package/test/api/map/test-log-2024-01-25.log +19 -0
  259. package/test/client/core/layout.js +24 -5
  260. package/test/client/core/utils.js +7 -0
  261. package/test/client/map/catalog.js +78 -1
  262. package/test/client/map/time.js +2 -1
  263. package/core/client/components/screen/KEndpointScreen.vue +0 -80
  264. package/core/client/mixins/mixin.account.js +0 -61
  265. package/extras/icons/kdk.png +0 -0
  266. package/map/api/services/geocoder/geocoder.service.js +0 -79
  267. package/map/client/cesium/utils.js +0 -133
  268. package/map/client/components/KCaptureToolbar.vue +0 -155
  269. package/map/client/components/KColorLegend.vue +0 -349
  270. package/map/client/components/KTimeline.vue +0 -293
  271. package/map/client/components/KUrlLegend.vue +0 -122
  272. package/map/client/leaflet/utils.js +0 -246
@@ -1,126 +1,20 @@
1
1
  import _ from 'lodash'
2
- import explode from '@turf/explode'
3
- import kinks from '@turf/kinks'
4
- import clean from '@turf/clean-coords'
5
2
  import { getType, getGeom } from '@turf/invariant'
6
3
  import logger from 'loglevel'
7
- import moment from 'moment'
8
- import { Time } from '../../../core/client/time.js'
9
- import { transformFeatures } from '../utils.js'
4
+ import * as features from '../utils/utils.features.js'
10
5
 
11
6
  export const featureService = {
12
7
  methods: {
13
- async getBaseQueryForFeatures (options) {
14
- // Any base query to process ?
15
- const baseQuery = {}
16
- if (options.baseQuery) {
17
- if (typeof options.baseQuery === 'function') {
18
- const result = await options.baseQuery()
19
- // A null query indicate to skip update
20
- if (!result) return
21
- else Object.assign(baseQuery, result)
22
- } else {
23
- Object.assign(baseQuery, options.baseQuery)
24
- }
25
- }
26
- return baseQuery
27
- },
28
- async getFilterQueryForFeatures (options) {
29
- // Any filter query to process ?
30
- const filterQuery = {}
31
- if (options.filterQuery) {
32
- if (typeof options.filterQuery === 'function') {
33
- const result = await options.filterQuery()
34
- Object.assign(filterQuery, result)
35
- } else {
36
- Object.assign(filterQuery, options.filterQuery)
37
- }
38
- }
39
- // Any filters to process ?
40
- const filters = _.get(options, 'filters', [])
41
- if (!_.isEmpty(filters)) {
42
- // To be flexible enough filters can provide a query for their active and inactive state
43
- // Similarly, filters can be combined with a different operator for each state
44
- const filterOperators = _.get(options, 'filterOperators', { active: '$or', inactive: '$and' })
45
- // Aggregate filter queries according to filter states
46
- const activeFilters = filters
47
- .filter(filter => filter.isActive)
48
- .map(filter => filter.active)
49
- .filter(query => !_.isEmpty(query))
50
- if (!_.isEmpty(activeFilters)) filterQuery[filterOperators.active] = activeFilters
51
- const inactiveFilters = filters
52
- .filter(filter => !filter.isActive)
53
- .map(filter => filter.inactive)
54
- .filter(query => !_.isEmpty(query))
55
- if (!_.isEmpty(inactiveFilters)) filterQuery[filterOperators.inactive] = inactiveFilters
56
- }
57
- return filterQuery
58
- },
59
- async getSortQueryForFeatures (options) {
60
- // Any sort query to process ?
61
- const sortQuery = {}
62
- if (options.sortQuery) {
63
- if (typeof options.sortQuery === 'function') {
64
- const result = await options.sortQuery()
65
- Object.assign(sortQuery, result)
66
- } else {
67
- Object.assign(sortQuery, options.sortQuery)
68
- }
69
- }
70
- return { $sort: sortQuery }
71
- },
72
- getFeaturesUpdateInterval (options) {
73
- const interval = (_.has(options, 'every')
74
- ? _.get(options, 'every')
75
- // Backward compatibility with old configuration style
76
- : _.get(options, this.engine, options).interval)
77
- return (interval ? moment.duration(interval) : null)
78
- },
79
- getFeaturesQueryInterval (options) {
80
- const interval = this.getFeaturesUpdateInterval(options)
81
- let queryInterval = (_.has(options, 'queryFrom')
82
- ? _.get(options, 'queryFrom')
83
- // Backward compatibility with old configuration style
84
- : _.get(options, this.engine, options).queryInterval)
85
- // If query interval not given use 2 x refresh interval as default value
86
- // this ensures we cover last interval if server/client update processes are not in sync
87
- if (!queryInterval && interval) queryInterval = moment.duration(-2 * interval.asMilliseconds())
88
- return (queryInterval ? moment.duration(queryInterval) : null)
89
- },
90
- shouldSkipFeaturesUpdate (lastUpdateTime, options, interval) {
91
- // If not given try to compute query interval from options
92
- if (!interval) {
93
- interval = this.getFeaturesUpdateInterval(options)
94
- }
95
- // We assume this is not a time-varying layer
96
- if (!interval) return true
97
- const now = Time.getCurrentTime()
98
- const elapsed = moment.duration(now.diff(lastUpdateTime))
99
- // If query interval has elapsed since last update we need to update again
100
- return (Math.abs(elapsed.asMilliseconds()) < interval.asMilliseconds())
101
- },
8
+ getBaseQueryForFeatures: features.getBaseQueryForFeatures,
9
+ getFilterQueryForFeatures: features.getFilterQueryForFeatures,
10
+ getSortQueryForFeatures: features.getSortQueryForFeatures,
11
+ getFeaturesUpdateInterval: features.getFeaturesUpdateInterval,
12
+ getFeaturesQueryInterval: features.getFeaturesQueryInterval,
13
+ shouldSkipFeaturesUpdate: features.shouldSkipFeaturesUpdate,
102
14
  getFeaturesLevel (options) {
103
15
  return (this.selectableLevelsLayer && (this.selectableLevelsLayer.name === options.name) ? this.selectedLevel : null)
104
16
  },
105
- async getProbeFeatures (options) {
106
- // Any base/filter/sort query to process ?
107
- const query = await this.getBaseQueryForFeatures(options)
108
- const filterQuery = await this.getFilterQueryForFeatures(options)
109
- const sortQuery = await this.getSortQueryForFeatures(options)
110
- Object.assign(query, filterQuery, sortQuery)
111
- const response = await this.$api.getService(options.probeService).find({ query })
112
- const features = (response.type === 'FeatureCollection' ? response.features : [response])
113
- if (typeof options.processor === 'function') {
114
- features.forEach(feature => options.processor(feature))
115
- } else if (typeof options.processor === 'string') {
116
- const compiler = _.template(options.processor)
117
- features.forEach(feature => compiler({ feature, properties: feature.properties }))
118
- }
119
- if (options.transform) {
120
- transformFeatures(features, options.transform)
121
- }
122
- return response
123
- },
17
+ getProbeFeatures: features.getProbeFeatures,
124
18
  async getProbeFeaturesFromLayer (name) {
125
19
  // Retrieve the layer
126
20
  const layer = this.getLayerByName(name)
@@ -128,84 +22,13 @@ export const featureService = {
128
22
  return this.getProbeFeatures(layer)
129
23
  },
130
24
  async getFeaturesQuery (options, queryInterval, queryLevel) {
131
- // If not given try to compute query interval from options
132
- if (!queryInterval) {
133
- queryInterval = this.getFeaturesQueryInterval(options)
134
- }
135
25
  // If not given try to compute query level from options
136
26
  if (!queryLevel) {
137
27
  queryLevel = this.getFeaturesLevel(options)
138
28
  }
139
- // Any base query to process ?
140
- let query = await this.getBaseQueryForFeatures(options)
141
- // Request features with at least one data available during last query interval
142
- if (queryInterval) {
143
- // Check if we have variables to be aggregated in time or not
144
- if (options.variables) {
145
- query = Object.assign({
146
- $groupBy: options.featureId,
147
- // Take care we might have multiple variables targetting the same value name
148
- // but that differentiate using others properties (compound feature ID)
149
- $aggregate: _.uniq(options.variables.map(variable => variable.name))
150
- }, query)
151
- } else if (options.featureId) {
152
- query = Object.assign({
153
- $groupBy: options.featureId,
154
- $aggregate: ['geometry']
155
- }, query)
156
- }
157
- const now = Time.getCurrentTime()
158
- if (moment.isDuration(queryInterval)) {
159
- // Depending on the duration format we might have negative or positive values
160
- const gte = (queryInterval.asMilliseconds() > 0
161
- ? now.clone().subtract(queryInterval)
162
- : now.clone().add(queryInterval))
163
- const lte = now
164
- Object.assign(query, {
165
- $sort: { time: -1, runTime: -1 },
166
- time: {
167
- $gte: gte.toISOString(),
168
- $lte: lte.toISOString()
169
- }
170
- })
171
- // If we can aggregate then keep track of last element of each aggregation
172
- if (options.featureId) query.$limit = 1
173
- } else if (typeof queryInterval === 'object') {
174
- query.time = queryInterval
175
- } else {
176
- Object.assign(query, {
177
- $sort: { time: -1, runTime: -1 },
178
- time: { $lte: now.toISOString() }
179
- })
180
- // If we can aggregate then keep track of last element of each aggregation
181
- if (options.featureId) query.$limit = 1
182
- }
183
- }
184
- if (!_.isNil(queryLevel)) {
185
- query.level = queryLevel
186
- }
187
- // Any filter/sort query to process ?
188
- const filterQuery = await this.getFilterQueryForFeatures(options)
189
- const sortQuery = await this.getSortQueryForFeatures(options)
190
- // Take care to not erase possible existing sort options
191
- _.merge(query, filterQuery, sortQuery)
192
-
193
- return query
194
- },
195
- async getFeaturesFromQuery (options, query) {
196
- const response = await this.$api.getService(options.service).find({ query })
197
- const features = (response.type === 'FeatureCollection' ? response.features : [response])
198
- if (typeof options.processor === 'function') {
199
- features.forEach(feature => options.processor(feature))
200
- } else if (typeof options.processor === 'string') {
201
- const compiler = _.template(options.processor)
202
- features.forEach(feature => compiler({ feature, properties: feature.properties }))
203
- }
204
- if (options.transform) {
205
- transformFeatures(features, options.transform)
206
- }
207
- return response
29
+ return features.getFeaturesQuery(options, queryInterval, queryLevel)
208
30
  },
31
+ getFeaturesFromQuery: features.getFeaturesFromQuery,
209
32
  async getFeatures (options, queryInterval, queryLevel) {
210
33
  const query = await this.getFeaturesQuery(options, queryInterval, queryLevel)
211
34
  const response = await this.getFeaturesFromQuery(options, query)
@@ -218,11 +41,17 @@ export const featureService = {
218
41
  return this.getFeatures(layer, queryInterval)
219
42
  },
220
43
  getMeasureForFeatureBaseQuery (layer, feature) {
44
+ // We might have a different ID to identify measures related to a timeseries (what is called a chronicle)
45
+ // than measures displayed on a map. For instance mobile measures might appear at different locations,
46
+ // but when selecting one we would like to display the timeseries related to all locations.
47
+ let featureId = layer.chronicleId || layer.featureId
221
48
  // Support compound ID
222
- const featureId = (Array.isArray(layer.featureId) ? layer.featureId : [layer.featureId])
223
- return featureId.reduce((result, id) =>
49
+ featureId = (Array.isArray(featureId) ? featureId : [featureId])
50
+ const query = featureId.reduce((result, id) =>
224
51
  Object.assign(result, { ['properties.' + id]: _.get(feature, 'properties.' + id) }),
225
52
  {})
53
+ query.$groupBy = featureId
54
+ return query
226
55
  },
227
56
  async getMeasureForFeatureQuery (layer, feature, startTime, endTime) {
228
57
  const query = await this.getFeaturesQuery(_.merge({
@@ -253,121 +82,19 @@ export const featureService = {
253
82
  this.unsetCursor('processing-cursor')
254
83
  return probedLocation
255
84
  },
256
- checkFeatures (geoJson, options = {
257
- kinks: true,
258
- redundantCoordinates: true
259
- }) {
260
- const features = (geoJson.type === 'FeatureCollection' ? geoJson.features : [geoJson])
261
- // Removes redundant coordinates
262
- if (options.redundantCoordinates) {
263
- features.forEach(feature => clean(feature, { mutate: true }))
264
- }
265
- // Filter invalid features
266
- let kinksFeatures
267
- if (options.kinks) {
268
- kinksFeatures = _.remove(features, feature => {
269
- const type = getType(feature)
270
- if ((type === 'MultiPolygon') || (type === 'Polygon')) {
271
- const invalidFeatures = kinks(feature)
272
- return _.get(invalidFeatures, 'features', []).length > 0
273
- } else { // No possible self-intersection on points, self-intersections allowed on lines
274
- return false
275
- }
276
- })
277
- }
278
- return { kinks: kinksFeatures }
279
- },
280
- async createFeatures (geoJson, layerId, chunkSize = 5000, processCallback) {
281
- if (!layerId) return
282
- const features = (geoJson.type === 'FeatureCollection' ? geoJson.features : [geoJson])
283
- features.forEach(feature => {
284
- // Remove any temporary ID as we will use the one from MongoDB
285
- delete feature._id
286
- feature.layer = layerId
287
- })
288
- // Single edition mode
289
- if (features.length === 1) {
290
- const feature = await this.$api.getService('features').create(features[0])
291
- return feature
292
- } else {
293
- // Create chunks to avoid reaching some limits (upload size, timeout, etc.)
294
- // We create chunks according to the number of points and not features.
295
- // Indeed otherwise it would create very different chunks depending on the geometry type
296
- // (eg a bucket of 1000 polygons can actually contains a lot of points).
297
- // const chunks = _.chunk(features, chunkSize)
298
- const chunks = []
299
- let chunkPoints = 0
300
- let chunk = []
301
- features.forEach(feature => {
302
- const explodedFeature = explode(feature)
303
- const nbPoints = explodedFeature.features.length
304
- // Check if current chunk is not full
305
- if ((chunkPoints + nbPoints) <= chunkSize) {
306
- chunkPoints += nbPoints
307
- chunk.push(feature)
308
- } else {
309
- // Otherwise push current chunk if not empty
310
- if (chunk.length > 0) {
311
- chunks.push(chunk)
312
- }
313
- // Then start new chunk
314
- chunk = [feature]
315
- chunkPoints = nbPoints
316
- }
317
- })
318
- // Push last chunk
319
- if (chunk.length > 0) {
320
- chunks.push(chunk)
321
- }
322
- // Write the chunks
323
- for (let i = 0; i < chunks.length; i++) {
324
- await this.$api.getService('features').create(chunks[i])
325
- if (typeof processCallback === 'function') await processCallback(i, chunks[i])
326
- }
327
- }
328
- },
329
- async editFeaturesGeometry (geoJson) {
330
- const features = (geoJson.type === 'FeatureCollection' ? geoJson.features : [geoJson])
331
- const updatedFeatures = []
332
- for (let i = 0; i < features.length; i++) {
333
- const feature = features[i]
334
- if (feature._id) {
335
- const updatedFeature = await this.$api.getService('features').patch(feature._id, _.pick(feature, ['geometry']))
336
- updatedFeatures.push(updatedFeature)
337
- }
338
- }
339
- return (geoJson.type === 'FeatureCollection' ? Object.assign(geoJson, { features: updatedFeatures }) : updatedFeatures)
340
- },
341
- async editFeaturesProperties (geoJson) {
342
- const features = (geoJson.type === 'FeatureCollection' ? geoJson.features : [geoJson])
343
- const updatedFeatures = []
344
- for (let i = 0; i < features.length; i++) {
345
- const feature = features[i]
346
- if (feature._id) {
347
- const updatedFeature = await this.$api.getService('features').patch(feature._id, _.pick(feature, ['properties']))
348
- updatedFeatures.push(updatedFeature)
349
- }
350
- }
351
- return (geoJson.type === 'FeatureCollection' ? Object.assign(geoJson, { features: updatedFeatures }) : updatedFeatures)
352
- },
353
- async removeFeatures (geoJsonOrLayerId) {
354
- // Remove all features of a given layer
355
- if (typeof geoJsonOrLayerId === 'string') {
356
- await this.$api.getService('features').remove(null, { query: { layer: geoJsonOrLayerId } })
357
- } else {
358
- const features = (geoJsonOrLayerId.type === 'FeatureCollection' ? geoJsonOrLayerId.features : [geoJsonOrLayerId])
359
- for (let i = 0; i < features.length; i++) {
360
- const feature = features[i]
361
- if (feature._id) await this.$api.getService('features').remove(feature._id)
362
- }
363
- }
364
- },
85
+ checkFeatures: features.checkFeatures,
86
+ createFeatures: features.createFeatures,
87
+ editFeaturesGeometry: features.editFeaturesGeometry,
88
+ editFeaturesProperties: features.editFeaturesProperties,
89
+ removeFeatures: features.removeFeatures,
365
90
  onFeaturesUpdated (feature) {
366
91
  // We only support single feature edition
367
92
  if (!getType(feature) || !getGeom(feature)) return
368
93
  // Find related layer
369
94
  const layer = this.getLayerById(feature.layer)
370
- if (!layer || !this.isLayerVisible(layer.name) || this.isLayerEdited(layer)) return
95
+ if (!layer || !this.isLayerVisible(layer.name)) return
96
+ // Only possible when not edited by default
97
+ if ((typeof this.isLayerEdited === 'function') && this.isLayerEdited(layer)) return
371
98
  // As by default we update the whole layer in fetch and replace mode force add/update only mode
372
99
  // Can only apply to realtime layers as we need to force a data refresh
373
100
  if (typeof this.updateLayer === 'function') this.updateLayer(layer.name, feature, { removeMissing: false })
@@ -377,7 +104,9 @@ export const featureService = {
377
104
  if (!getType(feature) || !getGeom(feature)) return
378
105
  // Find related layer
379
106
  const layer = this.getLayerById(feature.layer)
380
- if (!layer || !this.isLayerVisible(layer.name) || this.isLayerEdited(layer)) return
107
+ if (!layer || !this.isLayerVisible(layer.name)) return
108
+ // Only possible when not edited by default
109
+ if ((typeof this.isLayerEdited === 'function') && this.isLayerEdited(layer)) return
381
110
  // Can only apply to realtime layers as we need to force a data refresh
382
111
  if (typeof this.updateLayer === 'function') this.updateLayer(layer.name, feature, { remove: true })
383
112
  }
@@ -13,17 +13,11 @@ export const weacast = {
13
13
  }
14
14
  },
15
15
  methods: {
16
+ getWeacastApi () {
17
+ // As we usually proxy weacast service we use our API unless another client has been specified by override
18
+ return this.$api
19
+ },
16
20
  async setupWeacast () {
17
- // As we usually proxy weacast service we use our API unless another client has been specified
18
- if (!this.weacastApi) this.weacastApi = this.$api
19
- // We need to implement time management
20
- this.weacastApi.setForecastTime = (time) => {
21
- this.$api.forecastTime = time
22
- this.$api.emit('forecast-time-changed', time)
23
- }
24
- this.weacastApi.getForecastTime = () => {
25
- return this.$api.forecastTime
26
- }
27
21
  try {
28
22
  await this.setupForecastModels()
29
23
  } catch (error) {
@@ -31,12 +25,12 @@ export const weacast = {
31
25
  }
32
26
  },
33
27
  async setupForecastModels () {
34
- if (!this.weacastApi) return
35
- const response = await this.weacastApi.getService('forecasts').find()
28
+ if (!this.getWeacastApi()) return
29
+ const response = await this.getWeacastApi().getService('forecasts').find()
36
30
  // Required to use splice when modifying objects inside an array to make it reactive
37
31
  this.forecastModels.splice(0, this.forecastModels.length, ...response.data)
38
32
  // store forecast models on the weacast api object too (useful in the weacast grid source)
39
- this.weacastApi.models = this.forecastModels
33
+ this.getWeacastApi().models = this.forecastModels
40
34
  // Add 'virtual' actions used to trigger the layer/filters
41
35
  this.forecastModels.forEach(forecastModel => {
42
36
  forecastModel.actions = [{ id: 'toggle', handler: () => this.setForecastModel(forecastModel) }]
@@ -98,7 +92,7 @@ export const weacast = {
98
92
  return (tokens.length === 0) || !_.isFinite(_.toNumber(tokens[tokens.length - 1]))
99
93
  })
100
94
  }
101
- const response = await this.weacastApi.getService('probes')
95
+ const response = await this.getWeacastApi().getService('probes')
102
96
  .create({
103
97
  forecast: this.forecastModel.name,
104
98
  elements
@@ -119,7 +113,7 @@ export const weacast = {
119
113
  if (this.probe && (this.probe.name === name) && (this.probe.forecast === this.forecastModel.name)) {
120
114
  return this.probe
121
115
  }
122
- const results = await this.weacastApi.getService('probes').find({
116
+ const results = await this.getWeacastApi().getService('probes').find({
123
117
  query: {
124
118
  name,
125
119
  forecast: this.forecastModel.name,
@@ -158,7 +152,7 @@ export const weacast = {
158
152
  const windSpeed = (this.forecastLevel ? `windSpeed-${this.forecastLevel}` : 'windSpeed')
159
153
  elements = elements.concat([windDirection, windSpeed])
160
154
 
161
- const results = await this.weacastApi.getService('probe-results').find({
155
+ const results = await this.getWeacastApi().getService('probe-results').find({
162
156
  query: {
163
157
  probeId: this.probe._id,
164
158
  forecastTime: {
@@ -180,7 +174,7 @@ export const weacast = {
180
174
  return probedLocation
181
175
  },
182
176
  onCurrentForecastTimeChanged (time) {
183
- if (this.weacastApi) this.weacastApi.setForecastTime(time)
177
+ if (this.getWeacastApi()) this.getWeacastApi().setForecastTime(time)
184
178
  },
185
179
  onWeacastSelectedLevelChanged (level) {
186
180
  // Used when selectable levels are cleared
@@ -198,7 +198,7 @@ function buildColorMapShaderCode (thresholds, colors, interpolate) {
198
198
  return code
199
199
  }
200
200
 
201
- export function buildColorMapShaderCodeFromDomain (domain, colors, invertScale) {
201
+ export function buildColorMapShaderCodeFromDomain (domain, colors, invertScale = false) {
202
202
  // expects N values in domain an N colors
203
203
  // color0 .. color1 .. color2 ..... colorn-1 .. colorn
204
204
  // | | | | |
@@ -0,0 +1,66 @@
1
+ import _ from 'lodash'
2
+ import logger from 'loglevel'
3
+ import config from 'config'
4
+ import { createClient } from '../../core/client/api.js'
5
+ import { setupApi } from './init.js'
6
+
7
+ export const Planets = {
8
+
9
+ initialize () {
10
+ this.planets = {}
11
+ },
12
+
13
+ async connect (name, options = {}) {
14
+ _.defaults(options, {
15
+ origin: window.location.origin,
16
+ apiPath: config.apiPath,
17
+ apiJwt: `${name}-jwt`,
18
+ gatewayJwt: `${name}-gateway-jwt`,
19
+ apiTimeout: config.apiTimeout,
20
+ transport: config.transport,
21
+ appName: `${name}`,
22
+ renewJwt: false
23
+ })
24
+
25
+ const client = createClient(options)
26
+ setupApi.bind(client)(options)
27
+ client.on('authenticated', (data) => {
28
+ // Store API gateway token if any
29
+ if (data.gatewayToken) client.get('storage').setItem(options.gatewayJwt, data.gatewayToken)
30
+ })
31
+ client.on('logout', (data) => {
32
+ // Remove API gateway token if any
33
+ client.get('storage').removeItem(options.gatewayJwt)
34
+ })
35
+ const accessToken = await client.get('storage').getItem(options.apiJwt)
36
+ if (!accessToken) {
37
+ logger.error(new Error(`You must set planet ${name} token first`))
38
+ return
39
+ }
40
+ await client.authenticate({
41
+ strategy: 'jwt',
42
+ accessToken
43
+ })
44
+ this.planets[name] = client
45
+ return client
46
+ },
47
+
48
+ async disconnect (name) {
49
+ await this.planets[name].logout()
50
+ delete this.planets[name]
51
+ },
52
+
53
+ isConnected (name) {
54
+ return !_.isNil(this.planets[name])
55
+ },
56
+
57
+ get (name) {
58
+ if (!this.planets[name]) logger.error(new Error(`You must connect to planet ${name} first`))
59
+ else return this.planets[name]
60
+ },
61
+
62
+ // Register an existing planet
63
+ set (name, client) {
64
+ this.planets[name] = client
65
+ }
66
+ }
@@ -1,3 +1,9 @@
1
1
  export * from './utils.js'
2
+ export * from './utils.capture.js'
3
+ export * from './utils.catalog.js'
4
+ export * from './utils.features.js'
5
+ export * from './utils.layers.js'
2
6
  export * from './utils.location.js'
7
+ export * from './utils.project.js'
3
8
  export * from './utils.schema.js'
9
+ export * from './utils.style.js'