@kalisio/kdk 2.3.2 → 2.4.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 (441) hide show
  1. package/.eslintignore +2 -1
  2. package/.github/workflows/main.yaml +3 -3
  3. package/README.md +1 -0
  4. package/core/api/db.js +6 -1
  5. package/core/api/hooks/hooks.model.js +1 -1
  6. package/core/api/hooks/hooks.schemas.js +0 -2
  7. package/core/api/models/messages.model.mongodb.js +13 -0
  8. package/core/api/services/authorisations/authorisations.service.js +13 -4
  9. package/core/api/services/index.js +19 -0
  10. package/core/api/services/messages/messages.hooks.js +38 -0
  11. package/core/client/api.js +7 -32
  12. package/core/client/capabilities.js +2 -2
  13. package/core/client/components/KActivity.vue +29 -6
  14. package/core/client/components/KContent.vue +2 -2
  15. package/core/client/components/KDialog.vue +4 -7
  16. package/core/client/components/KStamp.vue +3 -9
  17. package/core/client/components/KStore.vue +2 -4
  18. package/core/client/components/KTab.vue +95 -0
  19. package/core/client/components/action/KAction.vue +15 -2
  20. package/core/client/components/action/KBugReportAction.vue +4 -2
  21. package/core/client/components/action/KToggleFullscreenAction.vue +25 -0
  22. package/core/client/components/app/KSettings.vue +17 -13
  23. package/core/client/components/chart/KDataTable.vue +6 -9
  24. package/core/client/components/chart/KTimeSeriesChart.vue +62 -49
  25. package/core/client/components/collection/KBoard.vue +22 -33
  26. package/core/client/components/collection/KCard.vue +71 -56
  27. package/core/client/components/collection/KCardSection.vue +20 -10
  28. package/core/client/components/collection/KDescriptionCardSection.vue +47 -0
  29. package/core/client/components/collection/KGrid.vue +234 -54
  30. package/core/client/components/collection/KScrollDown.vue +97 -0
  31. package/core/client/components/collection/KScrollToTop.vue +93 -0
  32. package/core/client/components/collection/KTable.vue +87 -33
  33. package/core/client/components/collection/KTimeLine.vue +406 -0
  34. package/core/client/components/collection/index.js +1 -5
  35. package/core/client/components/document/KDocument.vue +20 -55
  36. package/core/client/components/document/KHtml.vue +17 -7
  37. package/core/client/components/document/KImage.vue +78 -0
  38. package/core/client/components/document/KMarkdown.vue +12 -16
  39. package/core/client/components/document/KPdf.vue +69 -0
  40. package/core/client/components/form/KFileField.vue +2 -2
  41. package/core/client/components/form/KSelectField.vue +2 -1
  42. package/core/client/components/form/KUnitField.vue +3 -1
  43. package/core/client/components/layout/KFab.vue +9 -10
  44. package/core/client/components/layout/KLayout.vue +104 -6
  45. package/core/client/components/layout/KOpener.vue +14 -19
  46. package/core/client/components/layout/KPage.vue +195 -105
  47. package/core/client/components/layout/KWindow.vue +54 -32
  48. package/core/client/components/layout/index.js +0 -2
  49. package/core/client/components/media/KRibbon.vue +95 -0
  50. package/core/client/components/menu/KMenu.vue +4 -4
  51. package/core/client/components/team/KGroupsActivity.vue +25 -27
  52. package/core/client/components/team/KMembersActivity.vue +21 -23
  53. package/core/client/components/team/KOrganisationsActivity.vue +20 -22
  54. package/core/client/components/team/KTagsActivity.vue +21 -23
  55. package/core/client/components/time/KAbsoluteTimeRange.vue +70 -170
  56. package/core/client/composables/activity.js +14 -12
  57. package/core/client/composables/collection.js +3 -1
  58. package/core/client/composables/counter.js +51 -0
  59. package/core/client/composables/index.js +3 -0
  60. package/core/client/composables/layout.js +13 -2
  61. package/core/client/composables/messages.js +15 -0
  62. package/core/client/composables/pwa.js +1 -1
  63. package/core/client/composables/schema.js +6 -6
  64. package/core/client/composables/screen.js +23 -0
  65. package/core/client/directives/index.js +1 -0
  66. package/core/client/directives/v-hover.js +23 -0
  67. package/core/client/document.js +61 -0
  68. package/core/client/exporter.js +1 -1
  69. package/core/client/filter.js +0 -1
  70. package/core/client/guards.js +1 -1
  71. package/core/client/i18n/core_en.json +14 -8
  72. package/core/client/i18n/core_fr.json +15 -9
  73. package/core/client/index.js +9 -3
  74. package/core/client/layout.js +129 -29
  75. package/core/client/local-storage.js +1 -1
  76. package/core/client/mixins/index.js +0 -1
  77. package/core/client/mixins/mixin.base-activity.js +23 -13
  78. package/core/client/mixins/mixin.base-item.js +6 -3
  79. package/core/client/services/index.js +4 -1
  80. package/core/client/services/local-settings.service.js +4 -0
  81. package/core/client/storage.js +1 -1
  82. package/core/client/store.js +1 -1
  83. package/core/client/template-context.js +17 -0
  84. package/core/client/units.js +49 -27
  85. package/core/client/utils/index.js +3 -2
  86. package/core/client/utils/utils.actions.js +4 -0
  87. package/core/client/utils/utils.colors.js +155 -2
  88. package/core/client/utils/utils.items.js +26 -0
  89. package/core/client/utils/utils.math.js +3 -0
  90. package/core/client/utils/utils.platform.js +3 -1
  91. package/core/client/utils/utils.screen.js +82 -0
  92. package/core/client/utils/utils.time.js +0 -1
  93. package/core/common/schemas/settings.update.json +12 -0
  94. package/coverage/base.css +224 -0
  95. package/coverage/block-navigation.js +87 -0
  96. package/coverage/core/api/application.js.html +1870 -0
  97. package/coverage/core/api/authentication.js.html +742 -0
  98. package/coverage/core/api/db.js.html +793 -0
  99. package/coverage/core/api/hooks/hooks.authentication.js.html +313 -0
  100. package/coverage/core/api/hooks/hooks.authorisations.js.html +1243 -0
  101. package/coverage/core/api/hooks/hooks.groups.js.html +229 -0
  102. package/coverage/core/api/hooks/hooks.logger.js.html +163 -0
  103. package/coverage/core/api/hooks/hooks.model.js.html +955 -0
  104. package/coverage/core/api/hooks/hooks.organisations.js.html +541 -0
  105. package/coverage/core/api/hooks/hooks.push.js.html +253 -0
  106. package/coverage/core/api/hooks/hooks.query.js.html +862 -0
  107. package/coverage/core/api/hooks/hooks.schemas.js.html +298 -0
  108. package/coverage/core/api/hooks/hooks.service.js.html +319 -0
  109. package/coverage/core/api/hooks/hooks.storage.js.html +193 -0
  110. package/coverage/core/api/hooks/hooks.users.js.html +868 -0
  111. package/coverage/core/api/hooks/index.html +296 -0
  112. package/coverage/core/api/hooks/index.js.html +121 -0
  113. package/coverage/core/api/index.html +191 -0
  114. package/coverage/core/api/index.js.html +148 -0
  115. package/coverage/core/api/marshall.js.html +448 -0
  116. package/coverage/core/api/models/groups.model.mongodb.js.html +109 -0
  117. package/coverage/core/api/models/index.html +176 -0
  118. package/coverage/core/api/models/messages.model.mongodb.js.html +121 -0
  119. package/coverage/core/api/models/organisations.model.mongodb.js.html +94 -0
  120. package/coverage/core/api/models/tags.model.mongodb.js.html +115 -0
  121. package/coverage/core/api/models/users.model.mongodb.js.html +115 -0
  122. package/coverage/core/api/services/account/account.hooks.js.html +208 -0
  123. package/coverage/core/api/services/account/account.service.js.html +436 -0
  124. package/coverage/core/api/services/account/index.html +131 -0
  125. package/coverage/core/api/services/authorisations/authorisations.hooks.js.html +184 -0
  126. package/coverage/core/api/services/authorisations/authorisations.service.js.html +529 -0
  127. package/coverage/core/api/services/authorisations/index.html +131 -0
  128. package/coverage/core/api/services/databases/databases.hooks.js.html +193 -0
  129. package/coverage/core/api/services/databases/databases.service.js.html +100 -0
  130. package/coverage/core/api/services/databases/index.html +131 -0
  131. package/coverage/core/api/services/groups/groups.hooks.js.html +178 -0
  132. package/coverage/core/api/services/groups/index.html +116 -0
  133. package/coverage/core/api/services/import-export/import-export.hooks.js.html +184 -0
  134. package/coverage/core/api/services/import-export/import-export.service.js.html +118 -0
  135. package/coverage/core/api/services/import-export/index.html +131 -0
  136. package/coverage/core/api/services/index.html +116 -0
  137. package/coverage/core/api/services/index.js.html +556 -0
  138. package/coverage/core/api/services/mailer/index.html +131 -0
  139. package/coverage/core/api/services/mailer/mailer.hooks.js.html +190 -0
  140. package/coverage/core/api/services/mailer/mailer.service.js.html +118 -0
  141. package/coverage/core/api/services/messages/index.html +116 -0
  142. package/coverage/core/api/services/messages/messages.hooks.js.html +199 -0
  143. package/coverage/core/api/services/organisations/index.html +131 -0
  144. package/coverage/core/api/services/organisations/organisations.hooks.js.html +178 -0
  145. package/coverage/core/api/services/organisations/organisations.service.js.html +343 -0
  146. package/coverage/core/api/services/push/index.html +131 -0
  147. package/coverage/core/api/services/push/push.hooks.js.html +190 -0
  148. package/coverage/core/api/services/push/push.service.js.html +121 -0
  149. package/coverage/core/api/services/storage/index.html +131 -0
  150. package/coverage/core/api/services/storage/storage.hooks.js.html +190 -0
  151. package/coverage/core/api/services/storage/storage.service.js.html +172 -0
  152. package/coverage/core/api/services/tags/index.html +116 -0
  153. package/coverage/core/api/services/tags/tags.hooks.js.html +178 -0
  154. package/coverage/core/api/services/users/index.html +116 -0
  155. package/coverage/core/api/services/users/users.hooks.js.html +307 -0
  156. package/coverage/core/api/utils.js.html +118 -0
  157. package/coverage/core/common/errors.js.html +88 -0
  158. package/coverage/core/common/index.html +176 -0
  159. package/coverage/core/common/index.js.html +115 -0
  160. package/coverage/core/common/permissions.js.html +1048 -0
  161. package/coverage/core/common/schema.js.html +190 -0
  162. package/coverage/core/common/utils.js.html +220 -0
  163. package/coverage/favicon.png +0 -0
  164. package/coverage/index.html +506 -0
  165. package/coverage/lcov-report/base.css +224 -0
  166. package/coverage/lcov-report/block-navigation.js +87 -0
  167. package/coverage/lcov-report/core/api/application.js.html +1870 -0
  168. package/coverage/lcov-report/core/api/authentication.js.html +742 -0
  169. package/coverage/lcov-report/core/api/db.js.html +793 -0
  170. package/coverage/lcov-report/core/api/hooks/hooks.authentication.js.html +313 -0
  171. package/coverage/lcov-report/core/api/hooks/hooks.authorisations.js.html +1243 -0
  172. package/coverage/lcov-report/core/api/hooks/hooks.groups.js.html +229 -0
  173. package/coverage/lcov-report/core/api/hooks/hooks.logger.js.html +163 -0
  174. package/coverage/lcov-report/core/api/hooks/hooks.model.js.html +955 -0
  175. package/coverage/lcov-report/core/api/hooks/hooks.organisations.js.html +541 -0
  176. package/coverage/lcov-report/core/api/hooks/hooks.push.js.html +253 -0
  177. package/coverage/lcov-report/core/api/hooks/hooks.query.js.html +862 -0
  178. package/coverage/lcov-report/core/api/hooks/hooks.schemas.js.html +298 -0
  179. package/coverage/lcov-report/core/api/hooks/hooks.service.js.html +319 -0
  180. package/coverage/lcov-report/core/api/hooks/hooks.storage.js.html +193 -0
  181. package/coverage/lcov-report/core/api/hooks/hooks.users.js.html +868 -0
  182. package/coverage/lcov-report/core/api/hooks/index.html +296 -0
  183. package/coverage/lcov-report/core/api/hooks/index.js.html +121 -0
  184. package/coverage/lcov-report/core/api/index.html +191 -0
  185. package/coverage/lcov-report/core/api/index.js.html +148 -0
  186. package/coverage/lcov-report/core/api/marshall.js.html +448 -0
  187. package/coverage/lcov-report/core/api/models/groups.model.mongodb.js.html +109 -0
  188. package/coverage/lcov-report/core/api/models/index.html +176 -0
  189. package/coverage/lcov-report/core/api/models/messages.model.mongodb.js.html +121 -0
  190. package/coverage/lcov-report/core/api/models/organisations.model.mongodb.js.html +94 -0
  191. package/coverage/lcov-report/core/api/models/tags.model.mongodb.js.html +115 -0
  192. package/coverage/lcov-report/core/api/models/users.model.mongodb.js.html +115 -0
  193. package/coverage/lcov-report/core/api/services/account/account.hooks.js.html +208 -0
  194. package/coverage/lcov-report/core/api/services/account/account.service.js.html +436 -0
  195. package/coverage/lcov-report/core/api/services/account/index.html +131 -0
  196. package/coverage/lcov-report/core/api/services/authorisations/authorisations.hooks.js.html +184 -0
  197. package/coverage/lcov-report/core/api/services/authorisations/authorisations.service.js.html +529 -0
  198. package/coverage/lcov-report/core/api/services/authorisations/index.html +131 -0
  199. package/coverage/lcov-report/core/api/services/databases/databases.hooks.js.html +193 -0
  200. package/coverage/lcov-report/core/api/services/databases/databases.service.js.html +100 -0
  201. package/coverage/lcov-report/core/api/services/databases/index.html +131 -0
  202. package/coverage/lcov-report/core/api/services/groups/groups.hooks.js.html +178 -0
  203. package/coverage/lcov-report/core/api/services/groups/index.html +116 -0
  204. package/coverage/lcov-report/core/api/services/import-export/import-export.hooks.js.html +184 -0
  205. package/coverage/lcov-report/core/api/services/import-export/import-export.service.js.html +118 -0
  206. package/coverage/lcov-report/core/api/services/import-export/index.html +131 -0
  207. package/coverage/lcov-report/core/api/services/index.html +116 -0
  208. package/coverage/lcov-report/core/api/services/index.js.html +556 -0
  209. package/coverage/lcov-report/core/api/services/mailer/index.html +131 -0
  210. package/coverage/lcov-report/core/api/services/mailer/mailer.hooks.js.html +190 -0
  211. package/coverage/lcov-report/core/api/services/mailer/mailer.service.js.html +118 -0
  212. package/coverage/lcov-report/core/api/services/messages/index.html +116 -0
  213. package/coverage/lcov-report/core/api/services/messages/messages.hooks.js.html +199 -0
  214. package/coverage/lcov-report/core/api/services/organisations/index.html +131 -0
  215. package/coverage/lcov-report/core/api/services/organisations/organisations.hooks.js.html +178 -0
  216. package/coverage/lcov-report/core/api/services/organisations/organisations.service.js.html +343 -0
  217. package/coverage/lcov-report/core/api/services/push/index.html +131 -0
  218. package/coverage/lcov-report/core/api/services/push/push.hooks.js.html +190 -0
  219. package/coverage/lcov-report/core/api/services/push/push.service.js.html +121 -0
  220. package/coverage/lcov-report/core/api/services/storage/index.html +131 -0
  221. package/coverage/lcov-report/core/api/services/storage/storage.hooks.js.html +190 -0
  222. package/coverage/lcov-report/core/api/services/storage/storage.service.js.html +172 -0
  223. package/coverage/lcov-report/core/api/services/tags/index.html +116 -0
  224. package/coverage/lcov-report/core/api/services/tags/tags.hooks.js.html +178 -0
  225. package/coverage/lcov-report/core/api/services/users/index.html +116 -0
  226. package/coverage/lcov-report/core/api/services/users/users.hooks.js.html +307 -0
  227. package/coverage/lcov-report/core/api/utils.js.html +118 -0
  228. package/coverage/lcov-report/core/common/errors.js.html +88 -0
  229. package/coverage/lcov-report/core/common/index.html +176 -0
  230. package/coverage/lcov-report/core/common/index.js.html +115 -0
  231. package/coverage/lcov-report/core/common/permissions.js.html +1048 -0
  232. package/coverage/lcov-report/core/common/schema.js.html +190 -0
  233. package/coverage/lcov-report/core/common/utils.js.html +220 -0
  234. package/coverage/lcov-report/favicon.png +0 -0
  235. package/coverage/lcov-report/index.html +506 -0
  236. package/coverage/lcov-report/map/api/hooks/hooks.catalog.js.html +457 -0
  237. package/coverage/lcov-report/map/api/hooks/hooks.features.js.html +397 -0
  238. package/coverage/lcov-report/map/api/hooks/hooks.query.js.html +1309 -0
  239. package/coverage/lcov-report/map/api/hooks/index.html +161 -0
  240. package/coverage/lcov-report/map/api/hooks/index.js.html +94 -0
  241. package/coverage/lcov-report/map/api/index.html +131 -0
  242. package/coverage/lcov-report/map/api/index.js.html +139 -0
  243. package/coverage/lcov-report/map/api/marshall.js.html +178 -0
  244. package/coverage/lcov-report/map/api/models/alerts.model.mongodb.js.html +106 -0
  245. package/coverage/lcov-report/map/api/models/catalog.model.mongodb.js.html +127 -0
  246. package/coverage/lcov-report/map/api/models/features.model.mongodb.js.html +196 -0
  247. package/coverage/lcov-report/map/api/models/index.html +161 -0
  248. package/coverage/lcov-report/map/api/models/projects.model.mongodb.js.html +109 -0
  249. package/coverage/lcov-report/map/api/services/alerts/alerts.hooks.js.html +274 -0
  250. package/coverage/lcov-report/map/api/services/alerts/alerts.service.js.html +610 -0
  251. package/coverage/lcov-report/map/api/services/alerts/index.html +131 -0
  252. package/coverage/lcov-report/map/api/services/catalog/catalog.hooks.js.html +310 -0
  253. package/coverage/lcov-report/map/api/services/catalog/index.html +116 -0
  254. package/coverage/lcov-report/map/api/services/daptiles/daptiles.service.js.html +1510 -0
  255. package/coverage/lcov-report/map/api/services/daptiles/index.html +116 -0
  256. package/coverage/lcov-report/map/api/services/features/features.hooks.js.html +241 -0
  257. package/coverage/lcov-report/map/api/services/features/features.service.js.html +241 -0
  258. package/coverage/lcov-report/map/api/services/features/index.html +131 -0
  259. package/coverage/lcov-report/map/api/services/index.html +116 -0
  260. package/coverage/lcov-report/map/api/services/index.js.html +817 -0
  261. package/coverage/lcov-report/map/api/services/projects/index.html +116 -0
  262. package/coverage/lcov-report/map/api/services/projects/projects.hooks.js.html +439 -0
  263. package/coverage/lcov-report/map/common/dynamic-grid-source.js.html +466 -0
  264. package/coverage/lcov-report/map/common/errors.js.html +94 -0
  265. package/coverage/lcov-report/map/common/geotiff-grid-source.js.html +541 -0
  266. package/coverage/lcov-report/map/common/grid.js.html +1612 -0
  267. package/coverage/lcov-report/map/common/index.html +371 -0
  268. package/coverage/lcov-report/map/common/index.js.html +172 -0
  269. package/coverage/lcov-report/map/common/meteo-model-grid-source.js.html +556 -0
  270. package/coverage/lcov-report/map/common/moment-utils.js.html +157 -0
  271. package/coverage/lcov-report/map/common/opendap-grid-source.js.html +868 -0
  272. package/coverage/lcov-report/map/common/opendap-utils.js.html +826 -0
  273. package/coverage/lcov-report/map/common/permissions.js.html +124 -0
  274. package/coverage/lcov-report/map/common/time-based-grid-source.js.html +418 -0
  275. package/coverage/lcov-report/map/common/tms-utils.js.html +274 -0
  276. package/coverage/lcov-report/map/common/wcs-grid-source.js.html +364 -0
  277. package/coverage/lcov-report/map/common/wcs-utils.js.html +586 -0
  278. package/coverage/lcov-report/map/common/weacast-grid-source.js.html +1033 -0
  279. package/coverage/lcov-report/map/common/wfs-utils.js.html +574 -0
  280. package/coverage/lcov-report/map/common/wms-utils.js.html +451 -0
  281. package/coverage/lcov-report/map/common/wmts-utils.js.html +547 -0
  282. package/coverage/lcov-report/prettify.css +1 -0
  283. package/coverage/lcov-report/prettify.js +2 -0
  284. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  285. package/coverage/lcov-report/sorter.js +196 -0
  286. package/coverage/lcov.info +11245 -0
  287. package/coverage/map/api/hooks/hooks.catalog.js.html +457 -0
  288. package/coverage/map/api/hooks/hooks.features.js.html +397 -0
  289. package/coverage/map/api/hooks/hooks.query.js.html +1309 -0
  290. package/coverage/map/api/hooks/index.html +161 -0
  291. package/coverage/map/api/hooks/index.js.html +94 -0
  292. package/coverage/map/api/index.html +131 -0
  293. package/coverage/map/api/index.js.html +139 -0
  294. package/coverage/map/api/marshall.js.html +178 -0
  295. package/coverage/map/api/models/alerts.model.mongodb.js.html +106 -0
  296. package/coverage/map/api/models/catalog.model.mongodb.js.html +127 -0
  297. package/coverage/map/api/models/features.model.mongodb.js.html +196 -0
  298. package/coverage/map/api/models/index.html +161 -0
  299. package/coverage/map/api/models/projects.model.mongodb.js.html +109 -0
  300. package/coverage/map/api/services/alerts/alerts.hooks.js.html +274 -0
  301. package/coverage/map/api/services/alerts/alerts.service.js.html +610 -0
  302. package/coverage/map/api/services/alerts/index.html +131 -0
  303. package/coverage/map/api/services/catalog/catalog.hooks.js.html +310 -0
  304. package/coverage/map/api/services/catalog/index.html +116 -0
  305. package/coverage/map/api/services/daptiles/daptiles.service.js.html +1510 -0
  306. package/coverage/map/api/services/daptiles/index.html +116 -0
  307. package/coverage/map/api/services/features/features.hooks.js.html +241 -0
  308. package/coverage/map/api/services/features/features.service.js.html +241 -0
  309. package/coverage/map/api/services/features/index.html +131 -0
  310. package/coverage/map/api/services/index.html +116 -0
  311. package/coverage/map/api/services/index.js.html +817 -0
  312. package/coverage/map/api/services/projects/index.html +116 -0
  313. package/coverage/map/api/services/projects/projects.hooks.js.html +439 -0
  314. package/coverage/map/common/dynamic-grid-source.js.html +466 -0
  315. package/coverage/map/common/errors.js.html +94 -0
  316. package/coverage/map/common/geotiff-grid-source.js.html +541 -0
  317. package/coverage/map/common/grid.js.html +1612 -0
  318. package/coverage/map/common/index.html +371 -0
  319. package/coverage/map/common/index.js.html +172 -0
  320. package/coverage/map/common/meteo-model-grid-source.js.html +556 -0
  321. package/coverage/map/common/moment-utils.js.html +157 -0
  322. package/coverage/map/common/opendap-grid-source.js.html +868 -0
  323. package/coverage/map/common/opendap-utils.js.html +826 -0
  324. package/coverage/map/common/permissions.js.html +124 -0
  325. package/coverage/map/common/time-based-grid-source.js.html +418 -0
  326. package/coverage/map/common/tms-utils.js.html +274 -0
  327. package/coverage/map/common/wcs-grid-source.js.html +364 -0
  328. package/coverage/map/common/wcs-utils.js.html +586 -0
  329. package/coverage/map/common/weacast-grid-source.js.html +1033 -0
  330. package/coverage/map/common/wfs-utils.js.html +574 -0
  331. package/coverage/map/common/wms-utils.js.html +451 -0
  332. package/coverage/map/common/wmts-utils.js.html +547 -0
  333. package/coverage/prettify.css +1 -0
  334. package/coverage/prettify.js +2 -0
  335. package/coverage/sort-arrow-sprite.png +0 -0
  336. package/coverage/sorter.js +196 -0
  337. package/coverage/tmp/coverage-280506-1731704745613-0.json +1 -0
  338. package/coverage/tmp/coverage-280518-1731704745599-0.json +1 -0
  339. package/coverage/tmp/coverage-280529-1731704745588-0.json +1 -0
  340. package/coverage/tmp/coverage-280541-1731704745574-0.json +1 -0
  341. package/coverage/tmp/coverage-280548-1731704745545-0.json +1 -0
  342. package/extras/css/core.variables.scss +32 -8
  343. package/extras/icons/attribution.png +0 -0
  344. package/map/api/services/catalog/catalog.hooks.js +5 -7
  345. package/map/api/services/features/features.hooks.js +1 -1
  346. package/map/client/cesium/utils/utils.style.js +11 -2
  347. package/map/client/components/KAttribution.vue +108 -0
  348. package/map/client/components/KPositionIndicator.vue +11 -18
  349. package/map/client/components/KProjectMenu.vue +4 -4
  350. package/map/client/components/catalog/KCategoryItem.vue +74 -0
  351. package/map/client/components/catalog/KLayerCategories.vue +24 -12
  352. package/map/client/components/catalog/KLayersPanel.vue +139 -116
  353. package/map/client/components/catalog/KProjectSelector.vue +29 -17
  354. package/map/client/components/catalog/KProjectsPanel.vue +19 -35
  355. package/map/client/components/catalog/KViewSelector.vue +37 -25
  356. package/map/client/components/catalog/KViewsPanel.vue +19 -35
  357. package/map/client/components/form/KLocationField.vue +1 -2
  358. package/map/client/components/legend/KLegend.vue +34 -34
  359. package/map/client/components/location/KLocationCardSection.vue +18 -22
  360. package/map/client/components/location/KLocationMap.vue +36 -38
  361. package/map/client/components/location/KLocationTimeLineCard.vue +147 -0
  362. package/map/client/components/location/KLocationTip.vue +12 -2
  363. package/map/client/components/widget/KInformationBox.vue +0 -4
  364. package/map/client/components/widget/KStackableTimeSeries.vue +8 -1
  365. package/map/client/components/widget/KTimeSeries.vue +1 -1
  366. package/map/client/composables/highlight.js +29 -31
  367. package/map/client/composables/probe.js +7 -3
  368. package/map/client/composables/weather.js +71 -31
  369. package/map/client/i18n/map_en.json +3 -0
  370. package/map/client/i18n/map_fr.json +3 -0
  371. package/map/client/init.js +4 -3
  372. package/map/client/leaflet/ShapeMarker.js +1 -1
  373. package/map/client/leaflet/utils/utils.events.js +1 -1
  374. package/map/client/leaflet/utils/utils.style.js +20 -8
  375. package/map/client/mixins/globe/mixin.base-globe.js +111 -13
  376. package/map/client/mixins/globe/mixin.file-layers.js +10 -10
  377. package/map/client/mixins/globe/mixin.geojson-layers.js +90 -15
  378. package/map/client/mixins/globe/mixin.style.js +2 -0
  379. package/map/client/mixins/index.js +0 -1
  380. package/map/client/mixins/map/index.js +1 -0
  381. package/map/client/mixins/map/mixin.base-map.js +21 -2
  382. package/map/client/mixins/map/mixin.canvas-layers.js +7 -2
  383. package/map/client/mixins/map/mixin.edit-layers.js +12 -4
  384. package/map/client/mixins/map/mixin.file-layers.js +3 -0
  385. package/map/client/mixins/map/mixin.geojson-layers.js +90 -5
  386. package/map/client/mixins/map/mixin.pmtiles-layers.js +106 -0
  387. package/map/client/mixins/mixin.activity.js +8 -3
  388. package/map/client/mixins/mixin.feature-service.js +73 -32
  389. package/map/client/mixins/mixin.levels.js +1 -0
  390. package/map/client/mixins/mixin.weacast.js +10 -87
  391. package/map/client/utils/index.js +1 -0
  392. package/map/client/utils/utils.capture.js +1 -1
  393. package/map/client/utils/utils.catalog.js +7 -7
  394. package/map/client/utils/utils.features.js +59 -1
  395. package/map/client/utils/utils.layers.js +8 -0
  396. package/map/client/utils/utils.time-series.js +121 -0
  397. package/map/client/utils/utils.weacast.js +102 -0
  398. package/package.json +6 -6
  399. package/scripts/init_runner.sh +2 -2
  400. package/scripts/kash/CHANGELOG.md +12 -0
  401. package/scripts/kash/README.md +2 -0
  402. package/scripts/kash/kash.sh +34 -32
  403. package/scripts/run_tests.sh +2 -2
  404. package/scripts/setup_workspace.sh +24 -6
  405. package/test/api/core/hooks.test.js +6 -3
  406. package/test/api/core/test-log-2023-12-19.log +7 -0
  407. package/test/api/core/test-log-2024-01-04.log +14 -0
  408. package/test/api/core/test-log-2024-05-14.log +6 -0
  409. package/test/api/core/{test-log-2024-04-23.log → test-log-2024-06-06.log} +3 -3
  410. package/test/api/core/test-log-2024-06-26.log +25 -0
  411. package/test/api/core/test-log-2024-06-28.log +2 -0
  412. package/test/api/core/test-log-2024-07-09.log +0 -0
  413. package/test/api/core/test-log-2024-08-13.log +69 -0
  414. package/test/api/core/test-log-2024-10-28.log +53 -0
  415. package/test/api/core/test-log-2024-11-05.log +30 -0
  416. package/test/api/core/test-log-2024-11-15.log +23 -0
  417. package/test/api/map/alerts.test.js +3 -1
  418. package/test/api/map/config/layers.json +3 -1
  419. package/test/api/map/index.test.js +18 -1
  420. package/test/api/map/test-log-2023-11-24.log +121 -0
  421. package/test/api/map/test-log-2023-12-12.log +29 -0
  422. package/test/api/map/test-log-2023-12-13.log +5 -0
  423. package/test/api/map/test-log-2024-01-04.log +2 -0
  424. package/test/api/map/test-log-2024-01-11.log +1 -0
  425. package/test/api/map/test-log-2024-01-25.log +19 -0
  426. package/test/api/map/test-log-2024-06-06.log +39 -0
  427. package/test/api/map/test-log-2024-08-13.log +13 -0
  428. package/test/api/map/test-log-2024-08-20.log +55 -0
  429. package/test/api/map/test-log-2024-09-09.log +92 -0
  430. package/test/api/map/test-log-2024-10-28.log +11 -0
  431. package/test/client/core/utils.js +13 -0
  432. package/test/client/map/api.js +34 -0
  433. package/test/client/map/catalog.js +6 -2
  434. package/test/client/map/index.js +1 -0
  435. package/test/client/map/utils.js +4 -2
  436. package/core/client/components/collection/KList.vue +0 -135
  437. package/core/client/components/layout/KPageSticky.vue +0 -53
  438. package/core/client/mixins/mixin.base-collection.js +0 -162
  439. package/core/client/utils/utils.data.js +0 -22
  440. package/map/client/mixins/mixin.catalog-panel.js +0 -26
  441. package/test/api/core/test-log-2024-04-22.log +0 -84
@@ -1,7 +1,9 @@
1
1
  import _ from 'lodash'
2
+ import sift from 'sift'
2
3
  import { getType, getGeom } from '@turf/invariant'
3
4
  import logger from 'loglevel'
4
5
  import * as features from '../utils/utils.features.js'
6
+ import * as layers from '../utils/utils.layers.js'
5
7
 
6
8
  export const featureService = {
7
9
  methods: {
@@ -31,7 +33,7 @@ export const featureService = {
31
33
  getFeaturesFromQuery: features.getFeaturesFromQuery,
32
34
  async getFeatures (options, queryInterval, queryLevel) {
33
35
  const query = await this.getFeaturesQuery(options, queryInterval, queryLevel)
34
- const response = await this.getFeaturesFromQuery(options, query)
36
+ const response = await features.getFeaturesFromQuery(options, query)
35
37
  return response
36
38
  },
37
39
  async getFeaturesFromLayer (name, queryInterval) {
@@ -40,42 +42,23 @@ export const featureService = {
40
42
  if (!layer) return
41
43
  return this.getFeatures(layer, queryInterval)
42
44
  },
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
48
- // Support compound ID
49
- featureId = (Array.isArray(featureId) ? featureId : [featureId])
50
- const query = featureId.reduce((result, id) =>
51
- Object.assign(result, { ['properties.' + id]: _.get(feature, 'properties.' + id) }),
52
- {})
53
- query.$groupBy = featureId
54
- return query
55
- },
45
+ getMeasureForFeatureBaseQuery: features.getMeasureForFeatureBaseQuery,
56
46
  async getMeasureForFeatureQuery (layer, feature, startTime, endTime) {
57
47
  const query = await this.getFeaturesQuery(_.merge({
58
- baseQuery: this.getMeasureForFeatureBaseQuery(layer, feature)
48
+ baseQuery: features.getMeasureForFeatureBaseQuery(layer, feature)
59
49
  }, layer), {
60
50
  $gte: startTime.toISOString(),
61
51
  $lte: endTime.toISOString()
62
52
  })
63
53
  return query
64
54
  },
65
- async getMeasureForFeatureFromQuery (layer, feature, query) {
66
- const result = await this.getFeaturesFromQuery(layer, query)
67
- if (result.features.length > 0) {
68
- return result.features[0]
69
- } else {
70
- return _.cloneDeep(feature)
71
- }
72
- },
55
+ getMeasureForFeatureFromQuery: features.getMeasureForFeatureFromQuery,
73
56
  async getMeasureForFeature (layer, feature, startTime, endTime) {
74
57
  let probedLocation
75
58
  this.setCursor('processing-cursor')
76
59
  try {
77
60
  const query = await this.getMeasureForFeatureQuery(layer, feature, startTime, endTime)
78
- probedLocation = await this.getMeasureForFeatureFromQuery(layer, feature, query)
61
+ probedLocation = await features.getMeasureForFeatureFromQuery(layer, feature, query)
79
62
  } catch (error) {
80
63
  logger.error(error)
81
64
  }
@@ -87,28 +70,69 @@ export const featureService = {
87
70
  editFeaturesGeometry: features.editFeaturesGeometry,
88
71
  editFeaturesProperties: features.editFeaturesProperties,
89
72
  removeFeatures: features.removeFeatures,
90
- onFeaturesUpdated (feature) {
73
+ onFeaturesUpdated (feature, layerId) {
91
74
  // We only support single feature edition
92
75
  if (!getType(feature) || !getGeom(feature)) return
93
76
  // Find related layer
94
- const layer = this.getLayerById(feature.layer)
77
+ const layer = this.getLayerById(layerId || feature.layer)
95
78
  if (!layer || !this.isLayerVisible(layer.name)) return
96
79
  // Only possible when not edited by default
97
80
  if ((typeof this.isLayerEdited === 'function') && this.isLayerEdited(layer)) return
81
+ // Check for time-based layers if update is in the currently visualized time range
82
+ // so that we don't add too much old features
83
+ if (!features.isFeatureInQueryInterval(feature, layer)) return
98
84
  // As by default we update the whole layer in fetch and replace mode force add/update only mode
99
85
  // Can only apply to realtime layers as we need to force a data refresh
100
- if (typeof this.updateLayer === 'function') this.updateLayer(layer.name, feature, { removeMissing: false })
86
+ if (typeof this.updateLayer === 'function') {
87
+ // Check if feature should be filtered or not according to layer base query
88
+ const filteredFeature = [feature].filter(sift(_.omit(layer.baseQuery || {}, ['$skip', '$sort', '$limit', '$select'])))
89
+ if (filteredFeature.length > 0) this.updateLayer(layer.name, feature, { removeMissing: false })
90
+ }
101
91
  },
102
- onFeaturesRemoved (feature) {
92
+ onFeaturesRemoved (feature, layerId) {
103
93
  // We only support single feature edition
104
94
  if (!getType(feature) || !getGeom(feature)) return
105
95
  // Find related layer
106
- const layer = this.getLayerById(feature.layer)
96
+ const layer = this.getLayerById(layerId || feature.layer)
107
97
  if (!layer || !this.isLayerVisible(layer.name)) return
108
98
  // Only possible when not edited by default
109
99
  if ((typeof this.isLayerEdited === 'function') && this.isLayerEdited(layer)) return
100
+ // Check for time-based layers if update is in the currently visualized time range ? Should not be relevent in this case.
101
+ // Indeed, as time has passed we might have old features that need to be cleaned,
102
+ // ie features now outside the request time range but inside the initial time range when they were requested
103
+ //if (!features.isFeatureInQueryInterval(feature, layer)) return
110
104
  // Can only apply to realtime layers as we need to force a data refresh
111
- if (typeof this.updateLayer === 'function') this.updateLayer(layer.name, feature, { remove: true })
105
+ if (typeof this.updateLayer === 'function') {
106
+ // Check if feature should be filtered or not according to layer base query
107
+ const filteredFeature = [feature].filter(sift(_.omit(layer.baseQuery || {}, ['$skip', '$sort', '$limit', '$select'])))
108
+ if (filteredFeature.length > 0) this.updateLayer(layer.name, feature, { remove: true })
109
+ }
110
+ },
111
+ listenToServiceEvents (layer) {
112
+ // User-defined layers are already managed
113
+ if (!layer.service || !layer.serviceEvents || layers.isInMemoryLayer(layer) || layers.isFeatureLayer(layer)) return
114
+ const service = this.$api.getService(layer.service)
115
+ // Check if service available and not already registered
116
+ if (!service || this.layerServiceEventListeners[layer._id]) return
117
+ // Generate listeners targetting the right layer as in this case the features won't hold it contrary to user-defined layers
118
+ const onFeaturesUpdated = (feature) => this.onFeaturesUpdated(feature, layer._id)
119
+ const onFeaturesRemoved = (feature) => this.onFeaturesRemoved(feature, layer._id)
120
+ this.layerServiceEventListeners[layer._id] = { layerService: layer.service, onFeaturesUpdated, onFeaturesRemoved }
121
+ service.on('created', onFeaturesUpdated)
122
+ service.on('patched', onFeaturesUpdated)
123
+ service.on('removed', onFeaturesRemoved)
124
+ },
125
+ unlistenToServiceEvents (layer) {
126
+ // Check if listeners are registered for layer
127
+ if (!this.layerServiceEventListeners[layer._id]) return
128
+ const { layerService, onFeaturesUpdated, onFeaturesRemoved } = this.layerServiceEventListeners[layer._id]
129
+ const service = this.$api.getService(layerService)
130
+ // Check if service still available
131
+ if (!service) return
132
+ service.off('created', onFeaturesUpdated)
133
+ service.off('patched', onFeaturesUpdated)
134
+ service.off('removed', onFeaturesRemoved)
135
+ delete this.layerServiceEventListeners[layer._id]
112
136
  }
113
137
  },
114
138
  created () {
@@ -116,17 +140,34 @@ export const featureService = {
116
140
  this.$api.getService('features').timeout = 60 * 60 * 1000 // 1h should be sufficient since we also have size limits
117
141
  },
118
142
  mounted () {
119
- // Listen to user layer changes
143
+ // Here we need to listen to service events for all realtime layers triggered by it
144
+ // 1) user-defined layers targetting the features service
120
145
  const featuresService = this.$api.getService('features')
121
146
  featuresService.on('created', this.onFeaturesUpdated)
122
147
  featuresService.on('patched', this.onFeaturesUpdated)
123
148
  featuresService.on('removed', this.onFeaturesRemoved)
149
+ // 2) built-in layers targetting specific services
150
+ // As we don't know target services upfront we register listeners when layer are added, we track it in a map
151
+ this.layerServiceEventListeners = {}
152
+ this.$engineEvents.on('layer-added', this.listenToServiceEvents)
153
+ this.$engineEvents.on('layer-removed', this.unlistenToServiceEvents)
124
154
  },
125
155
  beforeUnmount () {
126
- // Remove event connections
156
+ // Remove all listeners
127
157
  const featuresService = this.$api.getService('features')
128
158
  featuresService.off('created', this.onFeaturesUpdated)
129
159
  featuresService.off('patched', this.onFeaturesUpdated)
130
160
  featuresService.off('removed', this.onFeaturesRemoved)
161
+ _.forOwn(this.layerServiceEventListeners, listeners => {
162
+ const { layerService, onFeaturesUpdated, onFeaturesRemoved } = listeners
163
+ const service = this.$api.getService(layerService)
164
+ // Check if service still available
165
+ if (!service) return
166
+ service.off('created', onFeaturesUpdated)
167
+ service.off('patched', onFeaturesUpdated)
168
+ service.off('removed', onFeaturesRemoved)
169
+ })
170
+ this.$engineEvents.off('layer-added', this.listenToServiceEvents)
171
+ this.$engineEvents.off('layer-removed', this.unlistenToServiceEvents)
131
172
  }
132
173
  }
@@ -33,6 +33,7 @@ export const levels = {
33
33
  }
34
34
  },
35
35
  setSelectedLevel (level) {
36
+ if (this.selectedLevel === level) return
36
37
  this.selectedLevel = level
37
38
  this.onSelectedLevelChanged(level)
38
39
  },
@@ -1,5 +1,6 @@
1
1
  import _ from 'lodash'
2
2
  import logger from 'loglevel'
3
+ import { getForecastForLocation, getForecastProbe, getForecastForFeature } from '../utils/utils.weacast.js'
3
4
 
4
5
  export const weacast = {
5
6
  emits: [
@@ -46,6 +47,7 @@ export const weacast = {
46
47
  this.setForecastModel(forecastModel)
47
48
  },
48
49
  setForecastModel (model) {
50
+ if (this.forecastModel === model) return
49
51
  this.forecastModel = model
50
52
  this.onForecastModelChanged(model)
51
53
  },
@@ -54,6 +56,7 @@ export const weacast = {
54
56
  this.$engineEvents.emit('forecast-model-changed', model)
55
57
  },
56
58
  setForecastLevel (level) {
59
+ if (this.forecastLevel === level) return
57
60
  this.forecastLevel = level
58
61
  this.onForecastLevelChanged(level)
59
62
  },
@@ -64,45 +67,11 @@ export const weacast = {
64
67
  async getForecastForLocation (long, lat, startTime, endTime) {
65
68
  // Not yet ready
66
69
  if (!this.forecastModel) return
67
- // From now to last available time
68
- const geometry = {
69
- type: 'Point',
70
- coordinates: [long, lat]
71
- }
72
- const query = {
73
- forecastTime: {
74
- $gte: startTime.format(),
75
- $lte: endTime.format()
76
- },
77
- geometry: {
78
- $geoIntersects: {
79
- $geometry: geometry
80
- }
81
- }
82
- }
83
- let probedLocation
84
70
  this.setCursor('processing-cursor')
85
- try {
86
- let elements = this.forecastModel.elements.map(element => element.name)
87
- // Filter available elements according to current level if any
88
- if (this.forecastLevel) elements = elements.filter(element => element.endsWith(this.forecastLevel.toString()))
89
- else {
90
- elements = elements.filter(element => {
91
- const tokens = element.split('-')
92
- return (tokens.length === 0) || !_.isFinite(_.toNumber(tokens[tokens.length - 1]))
93
- })
94
- }
95
- const response = await this.getWeacastApi().getService('probes')
96
- .create({
97
- forecast: this.forecastModel.name,
98
- elements
99
- }, { query })
100
- if (response.features.length > 0) {
101
- probedLocation = response.features[0]
102
- } else throw new Error('Cannot find valid forecast at location')
103
- } catch (error) {
104
- logger.error(error)
105
- }
71
+ const probedLocation = await getForecastForLocation({
72
+ long, lat, startTime, endTime, forecastModel: this.forecastModel,
73
+ forecastLevel: this.forecastLevel, weacastApi: this.getWeacastApi()
74
+ })
106
75
  this.unsetCursor('processing-cursor')
107
76
  return probedLocation
108
77
  },
@@ -113,20 +82,8 @@ export const weacast = {
113
82
  if (this.probe && (this.probe.name === name) && (this.probe.forecast === this.forecastModel.name)) {
114
83
  return this.probe
115
84
  }
116
- const results = await this.getWeacastApi().getService('probes').find({
117
- query: {
118
- name,
119
- forecast: this.forecastModel.name,
120
- $paginate: false,
121
- $select: ['elements', 'forecast', 'featureId']
122
- }
123
- })
124
- if (results.length > 0) {
125
- this.probe = results[0]
126
- return this.probe
127
- } else {
128
- return null
129
- }
85
+ this.probe = await getForecastProbe({ name, forecastModel: this.forecastModel, weacastApi: this.getWeacastApi() })
86
+ return this.probe
130
87
  },
131
88
  async getForecastForFeature (featureId, startTime, endTime) {
132
89
  // Not yet ready
@@ -134,42 +91,8 @@ export const weacast = {
134
91
  // Check if probe is available
135
92
  if (!this.probe) return
136
93
 
137
- let probedLocation
138
94
  this.setCursor('processing-cursor')
139
- try {
140
- let elements = this.forecastModel.elements.map(element => element.name)
141
- // Filter available elements according to current level if any
142
- if (this.forecastLevel) {
143
- elements = elements.filter(element => element.endsWith(this.forecastLevel.toString()))
144
- } else {
145
- elements = elements.filter(element => {
146
- const tokens = element.split('-')
147
- return (tokens.length === 0) || !_.isFinite(_.toNumber(tokens[tokens.length - 1]))
148
- })
149
- }
150
- // Need to add derived values for static probes as they are not computed on the fly
151
- const windDirection = (this.forecastLevel ? `windDirection-${this.forecastLevel}` : 'windDirection')
152
- const windSpeed = (this.forecastLevel ? `windSpeed-${this.forecastLevel}` : 'windSpeed')
153
- elements = elements.concat([windDirection, windSpeed])
154
-
155
- const results = await this.getWeacastApi().getService('probe-results').find({
156
- query: {
157
- probeId: this.probe._id,
158
- forecastTime: {
159
- $gte: startTime.format(),
160
- $lte: endTime.format()
161
- },
162
- [this.probe.featureId]: featureId,
163
- $groupBy: this.probe.featureId,
164
- $aggregate: elements
165
- }
166
- })
167
- if (results.length > 0) {
168
- probedLocation = results[0]
169
- } else throw new Error('Cannot find valid forecast for feature')
170
- } catch (error) {
171
- logger.error(error)
172
- }
95
+ const probedLocation = await getForecastForFeature({ probe: this.probe, featureId, startTime, endTime, forecastModel: this.forecastModel, forecastLevel: this.forecastLevel, weacastApi: this.getWeacastApi() })
173
96
  this.unsetCursor('processing-cursor')
174
97
  return probedLocation
175
98
  },
@@ -7,3 +7,4 @@ export * from './utils.location.js'
7
7
  export * from './utils.project.js'
8
8
  export * from './utils.schema.js'
9
9
  export * from './utils.style.js'
10
+ export * from './utils.time-series.js'
@@ -103,7 +103,7 @@ function headerFooterComponent (text, position) {
103
103
  return { content: [{ component: _.get(config, `capture.${position}.component`, 'KCaptureTextArea'), text, position }], visible: true }
104
104
  }
105
105
  function compassComponent (position) {
106
- return { content: _.union(Layout.getPage().content, [{ component: 'layout/KPageSticky', position, offset: [0, 5], content: [{ component: 'KNorth' }] }]) }
106
+ return { content: _.union(Layout.getStickies().content, [{ position, offset: [0, 5], content: [{ component: 'KNorth' }] }]) }
107
107
  }
108
108
  function legendComponent () {
109
109
  return {
@@ -24,17 +24,17 @@ export function setUrlJwt (item, path, baseUrl, jwtField, jwt) {
24
24
  export async function setEngineJwt (layers, planetApi) {
25
25
  // Backward compatibility when we previously used a single API
26
26
  if (!planetApi) planetApi = api
27
- const planetConfig = planetApi.getConfig()
28
27
  // If we need to use API gateway forward token as query parameter
29
28
  // (Leaflet does not support anything else by default as it mainly uses raw <img> tags)
30
- let jwt = (planetConfig.gatewayJwt ? await planetApi.get('storage').getItem(planetConfig.gatewayJwt) : null)
31
- let jwtField = planetConfig.gatewayJwtField
32
- // Check both the default built-in config or the server provided one if any (eg mobile apps)
33
- const gatewayUrl = Store.get('capabilities.api.gateway') || planetConfig.gateway
29
+ let jwt = (planetApi.hasConfig('gatewayJwt') ? await planetApi.get('storage').getItem(planetApi.getConfig('gatewayJwt')) : null)
30
+ let jwtField = planetApi.getConfig('gatewayJwtField')
31
+ // Check both the default built-in config or the server provided one if any
32
+ const gatewayUrl = (planetApi.hasConfig('gateway') ? planetApi.getConfig('gateway') : Store.get('capabilities.api.gateway'))
34
33
  if (jwt) {
35
34
  layers.forEach(layer => {
36
35
  setUrlJwt(layer, 'iconUrl', gatewayUrl, jwtField, jwt)
37
36
  setUrlJwt(layer, 'leaflet.source', gatewayUrl, jwtField, jwt)
37
+ setUrlJwt(layer, 'leaflet.url', gatewayUrl, jwtField, jwt)
38
38
  setUrlJwt(layer, 'opendap.url', gatewayUrl, jwtField, jwt)
39
39
  setUrlJwt(layer, 'geotiff.url', gatewayUrl, jwtField, jwt)
40
40
  setUrlJwt(layer, 'wfs.url', gatewayUrl, jwtField, jwt)
@@ -45,9 +45,9 @@ export async function setEngineJwt (layers, planetApi) {
45
45
  }
46
46
  // We might also proxy some data directly from the app when using object storage
47
47
  // This is only for raw raster data not OGC protocols
48
- jwt = (planetConfig.apiJwt ? await planetApi.get('storage').getItem(planetConfig.apiJwt) : null)
48
+ jwt = (planetApi.hasConfig('apiJwt') ? await planetApi.get('storage').getItem(planetApi.getConfig('apiJwt')) : null)
49
49
  jwtField = 'jwt'
50
- const apiUrl = planetApi.getBaseUrl()
50
+ const apiUrl = planetApi.getConfig('domain')
51
51
  if (jwt) {
52
52
  layers.forEach(layer => {
53
53
  setUrlJwt(layer, 'geotiff.url', apiUrl, jwtField, jwt)
@@ -216,6 +216,24 @@ export async function getFeaturesQuery (options, queryInterval, queryLevel) {
216
216
  return query
217
217
  }
218
218
 
219
+ export function isFeatureInQueryInterval (feature, options) {
220
+ // We assume this is not a time-varying layer
221
+ if (!feature.time) return true
222
+ const queryInterval = getFeaturesQueryInterval(options)
223
+ if (!moment.isDuration(queryInterval)) return true
224
+ const now = Time.getCurrentTime()
225
+ const time = moment.utc(feature.time)
226
+ // Depending on the duration format we might have negative or positive values
227
+ const gte = (queryInterval.asMilliseconds() > 0
228
+ ? now.clone().subtract(queryInterval)
229
+ : now.clone().add(queryInterval))
230
+ const lte = now
231
+ // In realtime mode take into account that we don't update time continuously but according to a frequency
232
+ // so that we might receive features "in the future" according to the current time
233
+ if (Time.isRealtime()) lte.add(Time.get().interval, 's')
234
+ return time.isSameOrAfter(gte) && time.isSameOrBefore(lte)
235
+ }
236
+
219
237
  export async function getFeaturesFromQuery (options, query) {
220
238
  // Check API to be used in case the layer is coming from a remote "planet"
221
239
  const planetApi = (typeof options.getPlanetApi === 'function' ? options.getPlanetApi() : api)
@@ -225,6 +243,46 @@ export async function getFeaturesFromQuery (options, query) {
225
243
  return response
226
244
  }
227
245
 
246
+ export function getMeasureForFeatureBaseQuery (layer, feature) {
247
+ // We might have a different ID to identify measures related to a timeseries (what is called a chronicle)
248
+ // than measures displayed on a map. For instance mobile measures might appear at different locations,
249
+ // but when selecting one we would like to display the timeseries related to all locations.
250
+ let featureId = layer.chronicleId || layer.featureId
251
+ // Support compound ID
252
+ featureId = (Array.isArray(featureId) ? featureId : [featureId])
253
+ const query = featureId.reduce((result, id) =>
254
+ Object.assign(result, { ['properties.' + id]: _.get(feature, 'properties.' + id) }),
255
+ {})
256
+ query.$groupBy = featureId
257
+ return query
258
+ }
259
+
260
+ export async function getMeasureForFeatureQuery (layer, feature, startTime, endTime, level) {
261
+ const query = await getFeaturesQuery(_.merge({
262
+ baseQuery: getMeasureForFeatureBaseQuery(layer, feature)
263
+ }, layer), {
264
+ $gte: startTime.toISOString(),
265
+ $lte: endTime.toISOString()
266
+ }, level)
267
+ return query
268
+ }
269
+
270
+ export async function getMeasureForFeatureFromQuery (layer, feature, query) {
271
+ const result = await getFeaturesFromQuery(layer, query)
272
+ return _.get(result, 'features[0]')
273
+ }
274
+
275
+ export async function getMeasureForFeature (layer, feature, startTime, endTime, level) {
276
+ let probedLocation
277
+ try {
278
+ const query = await getMeasureForFeatureQuery(layer, feature, startTime, endTime, level)
279
+ probedLocation = await getMeasureForFeatureFromQuery(layer, feature, query)
280
+ } catch (error) {
281
+ logger.error(error)
282
+ }
283
+ return probedLocation
284
+ }
285
+
228
286
  export function checkFeatures (geoJson, options = {
229
287
  kinks: true,
230
288
  redundantCoordinates: true
@@ -361,4 +419,4 @@ export function getFeatureStyleType (feature) {
361
419
  if (['Polygon', 'MultiPolygon'].includes(geometryType)) return 'polygon'
362
420
  logger.warn(`[KDK] unsupported geometry of type of ${geometryType}`)
363
421
  return
364
- }
422
+ }
@@ -27,6 +27,10 @@ export function isLayerSelectable (layer) {
27
27
  return _.get(layer, 'isSelectable', true)
28
28
  }
29
29
 
30
+ export function isLayerHighlightable (layer) {
31
+ return _.get(layer, 'isHighlightable', true)
32
+ }
33
+
30
34
  export function isLayerProbable (layer) {
31
35
  return _.get(layer, 'isProbable', false)
32
36
  }
@@ -61,6 +65,10 @@ export function isTerrainLayer (layer) {
61
65
  return (cesiumOptions.type === 'Cesium') || (cesiumOptions.type === 'Ellipsoid')
62
66
  }
63
67
 
68
+ export function isMeasureLayer (layer) {
69
+ return layer.variables && layer.service
70
+ }
71
+
64
72
  export async function saveGeoJsonLayer (layer, geoJson, chunkSize = 5000) {
65
73
  // Check for invalid features first
66
74
  const check = checkFeatures(geoJson)
@@ -0,0 +1,121 @@
1
+ import _ from 'lodash'
2
+ import moment from 'moment'
3
+ import { Time, Units, i18n } from '../../../core/client/index.js'
4
+ import { isMeasureLayer } from './utils.layers.js'
5
+ import { getMeasureForFeature } from './utils.features.js'
6
+ import { getForecastForLocation, getForecastProbe, getForecastForFeature } from './utils.weacast.js'
7
+
8
+ async function getDataForVariable(data, variable, forecastLevel, runTime) {
9
+ data = await data
10
+ const times = _.get(data, 'time', _.get(data, 'forecastTime', {}))
11
+ const runTimes = _.get(data, 'runTime', {})
12
+ const properties = _.get(data, 'properties', {})
13
+ // Check if we are targetting a specific variable at level (forecast model case)
14
+ const name = (forecastLevel ? `${variable.name}-${forecastLevel}` : variable.name)
15
+ let values = []
16
+ // Aggregated variable available for feature ?
17
+ if (properties[name] && Array.isArray(properties[name])) {
18
+ // Build data structure as expected by visualisation
19
+ values = properties[name].map((value, index) => {
20
+ value = Units.convert(value, variable.unit, variable.targetUnit)
21
+ return { time: moment.utc(times[name][index]).valueOf(), [name]: value }
22
+ })
23
+ // Keep only selected value if multiple are provided for the same time (eg different forecasts)
24
+ if (variable.runTimes && runTime && !_.isEmpty(_.get(runTimes, name))) {
25
+ values = values.filter((value, index) => (runTimes[name][index] === runTime.toISOString()))
26
+ } else values = _.uniqBy(values, 'time')
27
+ }
28
+ return values
29
+ }
30
+
31
+ async function fetchDataForSeries({
32
+ feature, location, layer, startTime, endTime,
33
+ level, forecastModel, forecastLevel, probeFunction, weacastApi
34
+ }) {
35
+ // Use current time range if not provided
36
+ const { start, end } = Time.getRange()
37
+ if (!startTime) startTime = start
38
+ if (!endTime) endTime = end
39
+ // Depending on input use the right function to retrieve data
40
+ let data
41
+ // No feature clicked => custom probe function or dynamic weacast probe at position
42
+ if (!feature) {
43
+ if (probeFunction) data = await probeFunction({ longitude: location.lng, latitude: location.lat, startTime, endTime })
44
+ else data = await getForecastForLocation({ longitude: location.lng, latitude: location.lat, startTime, endTime, forecastModel, forecastLevel, weacastApi })
45
+ } else if (layer.probe) { // Static weacast probe
46
+ const probe = await getForecastProbe({ name: layer.probe, forecastModel, weacastApi })
47
+ if (probe) {
48
+ data = await getForecastForFeature({ probe, featureId: _.get(feature, probe.featureId), startTime, endTime, forecastModel, forecastLevel, weacastApi })
49
+ }
50
+ } else if (isMeasureLayer(layer)) { // Static measure probe
51
+ data = await getMeasureForFeature(layer, feature, startTime, endTime, level)
52
+ } else { // dynamic weacast probe at feature position
53
+ const location = centroid(feature)
54
+ const longitude = _.get(location, 'geometry.coordinates[0]')
55
+ const latitude = _.get(location, 'geometry.coordinates[1]')
56
+ data = await getForecastForLocation({ longitude, latitude, startTime, endTime, forecastModel, forecastLevel, weacastApi })
57
+ }
58
+ return data
59
+ }
60
+
61
+ // Build timeseries to be used in charts for target feature and associated layer definition or probe location
62
+ export function getTimeSeries({
63
+ feature, location, layer, layers, startTime, endTime, runTime,
64
+ level, forecastModel, forecastLevel, probeFunction, weacastApi, fetchDelay
65
+ }) {
66
+ // A feature comes from a single layer so target variables from it
67
+ let variables = _.get(layer, 'variables', [])
68
+ // However, a probe can target variables coming from multiple layers
69
+ if (layers && layers.length > 0) layers.forEach(layer => { variables = variables.concat(_.get(layer, 'variables', [])) })
70
+ variables = _.uniqBy(variables, 'name')
71
+ if (variables.length === 0) return []
72
+ const properties = _.get(feature, 'properties', {})
73
+ // Create promise to fetch data as it will be shared by all series,
74
+ // indeed a measure stores all aggregated variables
75
+ const data = fetchDataForSeries({
76
+ feature, location, layer, startTime, endTime,
77
+ level, forecastModel, forecastLevel, probeFunction, weacastApi
78
+ })
79
+ // Fetch data function to request data update,
80
+ // we use debounce as a measure stores all aggregated variables
81
+ // so that when all series are updated at once a single query will be send.
82
+ const fetch = _.debounce(() => fetchDataForSeries({
83
+ feature, location, layer, startTime, endTime,
84
+ level, forecastModel, forecastLevel, probeFunction, weacastApi
85
+ }), fetchDelay || 250, { leading: true, trailing: false })
86
+
87
+ const series = variables.map(variable => {
88
+ // Base unit could be either directly the unit or the property of the measure storing the unit
89
+ const baseUnit = _.get(properties, 'unit', variable.unit)
90
+ // Known by the unit system ?
91
+ const unit = Units.getUnit(baseUnit)
92
+ const targetUnit = Units.getTargetUnit(baseUnit)
93
+ const serie = {
94
+ probedLocation: data,
95
+ data: getDataForVariable(data, variable, forecastLevel),
96
+ variable: {
97
+ name: variable.name,
98
+ label: `${i18n.tie(variable.label)} (${Units.getTargetUnitSymbol(baseUnit)})`,
99
+ unit,
100
+ targetUnit,
101
+ chartjs: Object.assign({
102
+ parsing: {
103
+ xAxisKey: 'time',
104
+ yAxisKey: (forecastLevel ? `${variable.name}-${forecastLevel}` : variable.name)
105
+ },
106
+ cubicInterpolationMode: 'monotone',
107
+ tension: 0.4
108
+ }, _.cloneDeep(variable.chartjs))
109
+ }
110
+ }
111
+ // FIXME: how to share promise between series ?
112
+ serie.fetch = () => {
113
+ serie.probedLocation = fetch()
114
+ serie.data = getDataForVariable(serie.probedLocation, variable, forecastLevel, runTime)
115
+ return serie.data
116
+ }
117
+ return serie
118
+ })
119
+
120
+ return series
121
+ }