@kalisio/kdk 2.6.4 → 2.7.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 (398) hide show
  1. package/core/api/application.js +2 -4
  2. package/core/api/authentication.js +2 -3
  3. package/core/api/db.js +10 -2
  4. package/core/api/hooks/hooks.authorisations.js +4 -2
  5. package/core/api/hooks/hooks.push.js +6 -2
  6. package/core/api/hooks/hooks.query.js +29 -12
  7. package/core/api/hooks/hooks.users.js +30 -17
  8. package/core/api/models/configurations.model.mongodb.js +4 -0
  9. package/core/api/services/authorisations/authorisations.service.js +1 -1
  10. package/core/api/services/configurations/configurations.hooks.js +33 -0
  11. package/core/api/services/index.js +41 -7
  12. package/core/api/services/messages/messages.hooks.js +9 -3
  13. package/core/client/api.js +14 -1
  14. package/core/client/capabilities.js +1 -6
  15. package/core/client/components/KAvatar.vue +24 -20
  16. package/core/client/components/account/KProfile.vue +10 -71
  17. package/core/client/components/account/index.js +0 -2
  18. package/core/client/components/app/KSettings.vue +1 -0
  19. package/core/client/components/collection/KBoard.vue +4 -3
  20. package/core/client/components/collection/KCardSection.vue +1 -0
  21. package/core/client/components/collection/KGrid.vue +2 -0
  22. package/core/client/components/collection/KTable.vue +5 -1
  23. package/core/client/components/collection/KTimeLine.vue +9 -1
  24. package/core/client/components/collection/index.js +0 -2
  25. package/core/client/components/form/KChipsField.vue +2 -1
  26. package/core/client/components/form/KEmailField.vue +1 -0
  27. package/core/client/components/form/KFileField.vue +22 -1
  28. package/core/client/components/form/KForm.vue +2 -0
  29. package/core/client/components/form/KItemField.vue +1 -0
  30. package/core/client/components/form/KNumberField.vue +1 -0
  31. package/core/client/components/form/KPasswordField.vue +1 -0
  32. package/core/client/components/form/KPhoneField.vue +1 -0
  33. package/core/client/components/form/KPropertyItemField.vue +1 -0
  34. package/core/client/components/form/KResolutionField.vue +1 -0
  35. package/core/client/components/form/KSelectField.vue +31 -0
  36. package/core/client/components/form/KTagField.vue +1 -0
  37. package/core/client/components/form/KTextField.vue +1 -0
  38. package/core/client/components/form/KTokenField.vue +1 -0
  39. package/core/client/components/form/KUnitField.vue +1 -0
  40. package/core/client/components/form/KUrlField.vue +1 -0
  41. package/core/client/components/graphics/KIcon.vue +3 -4
  42. package/core/client/components/layout/KPage.vue +1 -0
  43. package/core/client/components/layout/KWindow.vue +6 -3
  44. package/core/client/components/messages/KMessageComposer.vue +2 -1
  45. package/core/client/components/messages/KMessagesTimeLine.vue +1 -1
  46. package/core/client/components/time/KDate.vue +1 -2
  47. package/core/client/components/time/KDateTime.vue +11 -11
  48. package/core/client/components/time/KTime.vue +1 -1
  49. package/core/client/composables/collection.js +33 -8
  50. package/core/client/composables/errors.js +1 -1
  51. package/core/client/composables/layout.js +9 -9
  52. package/core/client/configurations.js +50 -0
  53. package/core/client/exporter.js +1 -1
  54. package/core/client/i18n/core_en.json +6 -39
  55. package/core/client/i18n/core_fr.json +6 -39
  56. package/core/client/index.js +2 -0
  57. package/core/client/layout.js +8 -8
  58. package/core/client/mixins/mixin.base-activity.js +5 -2
  59. package/core/client/mixins/mixin.base-field.js +3 -3
  60. package/core/client/search.js +2 -1
  61. package/core/client/utils/utils.collection.js +8 -8
  62. package/core/client/utils/utils.items.js +4 -0
  63. package/core/client/utils/utils.push.js +3 -3
  64. package/core/client/utils/utils.session.js +7 -5
  65. package/core/client/utils/utils.shapes.js +38 -7
  66. package/core/client/utils/utils.time.js +21 -22
  67. package/core/common/schemas/users.update-profile.json +3 -2
  68. package/coverage/core/api/application.js.html +392 -398
  69. package/coverage/core/api/authentication.js.html +352 -187
  70. package/coverage/core/api/db.js.html +165 -126
  71. package/coverage/core/api/hooks/hooks.authentication.js.html +22 -196
  72. package/coverage/core/api/hooks/hooks.authorisations.js.html +383 -662
  73. package/coverage/core/api/hooks/hooks.logger.js.html +41 -41
  74. package/coverage/core/api/hooks/hooks.model.js.html +113 -101
  75. package/coverage/core/api/hooks/hooks.push.js.html +124 -97
  76. package/coverage/core/api/hooks/hooks.query.js.html +292 -217
  77. package/coverage/core/api/hooks/hooks.schemas.js.html +123 -123
  78. package/coverage/core/api/hooks/hooks.service.js.html +1 -1
  79. package/coverage/core/api/hooks/hooks.storage.js.html +1 -1
  80. package/coverage/core/api/hooks/{hooks.groups.js.html → hooks.tags.js.html} +100 -76
  81. package/coverage/core/api/hooks/hooks.users.js.html +255 -447
  82. package/coverage/core/api/hooks/index.html +107 -122
  83. package/coverage/core/api/hooks/index.js.html +4 -10
  84. package/coverage/core/api/index.html +46 -61
  85. package/coverage/core/api/index.js.html +9 -9
  86. package/coverage/core/api/marshall.js.html +9 -9
  87. package/coverage/core/api/models/{organisations.model.mongodb.js.html → configurations.model.mongodb.js.html} +10 -7
  88. package/coverage/core/api/models/index.html +35 -50
  89. package/coverage/core/api/models/messages.model.mongodb.js.html +39 -27
  90. package/coverage/core/api/models/tags.model.mongodb.js.html +26 -32
  91. package/coverage/core/api/models/users.model.mongodb.js.html +10 -10
  92. package/coverage/core/api/services/account/account.hooks.js.html +5 -5
  93. package/coverage/core/api/services/account/account.service.js.html +127 -127
  94. package/coverage/core/api/services/account/index.html +22 -22
  95. package/coverage/core/api/services/authorisations/authorisations.hooks.js.html +1 -1
  96. package/coverage/core/api/services/authorisations/authorisations.service.js.html +213 -222
  97. package/coverage/core/api/services/authorisations/index.html +21 -21
  98. package/coverage/core/api/services/{organisations/organisations.hooks.js.html → configurations/configurations.hooks.js.html} +16 -10
  99. package/coverage/core/api/services/{groups → configurations}/index.html +8 -8
  100. package/coverage/core/api/services/databases/databases.hooks.js.html +1 -1
  101. package/coverage/core/api/services/databases/databases.service.js.html +1 -1
  102. package/coverage/core/api/services/databases/index.html +1 -1
  103. package/coverage/core/api/services/import-export/import-export.hooks.js.html +76 -76
  104. package/coverage/core/api/services/import-export/import-export.service.js.html +32 -32
  105. package/coverage/core/api/services/import-export/index.html +32 -32
  106. package/coverage/core/api/services/index.html +21 -21
  107. package/coverage/core/api/services/index.js.html +313 -142
  108. package/coverage/core/api/services/mailer/index.html +32 -32
  109. package/coverage/core/api/services/mailer/mailer.hooks.js.html +80 -80
  110. package/coverage/core/api/services/mailer/mailer.service.js.html +32 -32
  111. package/coverage/core/api/services/messages/index.html +21 -21
  112. package/coverage/core/api/services/messages/messages.hooks.js.html +112 -76
  113. package/coverage/core/api/services/push/index.html +32 -32
  114. package/coverage/core/api/services/push/push.hooks.js.html +80 -80
  115. package/coverage/core/api/services/push/push.service.js.html +34 -34
  116. package/coverage/core/api/services/storage/index.html +29 -29
  117. package/coverage/core/api/services/storage/storage.hooks.js.html +80 -80
  118. package/coverage/core/api/services/storage/storage.service.js.html +29 -29
  119. package/coverage/core/api/services/tags/index.html +21 -21
  120. package/coverage/core/api/services/tags/tags.hooks.js.html +119 -71
  121. package/coverage/core/api/services/users/index.html +27 -12
  122. package/coverage/core/api/services/users/users.hooks.js.html +14 -11
  123. package/coverage/core/api/services/users/users.service.js.html +100 -0
  124. package/coverage/core/common/errors.js.html +1 -1
  125. package/coverage/core/common/index.html +42 -27
  126. package/coverage/core/common/index.js.html +1 -1
  127. package/coverage/core/common/permissions.js.html +166 -472
  128. package/coverage/core/common/schema.js.html +4 -4
  129. package/coverage/core/common/utils.js.html +31 -25
  130. package/coverage/core/common/utils.offline.js.html +199 -0
  131. package/coverage/index.html +192 -192
  132. package/coverage/lcov-report/core/api/application.js.html +392 -398
  133. package/coverage/lcov-report/core/api/authentication.js.html +352 -187
  134. package/coverage/lcov-report/core/api/db.js.html +165 -126
  135. package/coverage/lcov-report/core/api/hooks/hooks.authentication.js.html +22 -196
  136. package/coverage/lcov-report/core/api/hooks/hooks.authorisations.js.html +383 -662
  137. package/coverage/lcov-report/core/api/hooks/hooks.logger.js.html +41 -41
  138. package/coverage/lcov-report/core/api/hooks/hooks.model.js.html +113 -101
  139. package/coverage/lcov-report/core/api/hooks/hooks.push.js.html +124 -97
  140. package/coverage/lcov-report/core/api/hooks/hooks.query.js.html +292 -217
  141. package/coverage/lcov-report/core/api/hooks/hooks.schemas.js.html +123 -123
  142. package/coverage/lcov-report/core/api/hooks/hooks.service.js.html +1 -1
  143. package/coverage/lcov-report/core/api/hooks/hooks.storage.js.html +1 -1
  144. package/coverage/lcov-report/core/api/hooks/{hooks.groups.js.html → hooks.tags.js.html} +100 -76
  145. package/coverage/lcov-report/core/api/hooks/hooks.users.js.html +255 -447
  146. package/coverage/lcov-report/core/api/hooks/index.html +107 -122
  147. package/coverage/lcov-report/core/api/hooks/index.js.html +4 -10
  148. package/coverage/lcov-report/core/api/index.html +46 -61
  149. package/coverage/lcov-report/core/api/index.js.html +9 -9
  150. package/coverage/lcov-report/core/api/marshall.js.html +9 -9
  151. package/coverage/lcov-report/core/api/models/{organisations.model.mongodb.js.html → configurations.model.mongodb.js.html} +10 -7
  152. package/coverage/lcov-report/core/api/models/index.html +35 -50
  153. package/coverage/lcov-report/core/api/models/messages.model.mongodb.js.html +39 -27
  154. package/coverage/lcov-report/core/api/models/tags.model.mongodb.js.html +26 -32
  155. package/coverage/lcov-report/core/api/models/users.model.mongodb.js.html +10 -10
  156. package/coverage/lcov-report/core/api/services/account/account.hooks.js.html +5 -5
  157. package/coverage/lcov-report/core/api/services/account/account.service.js.html +127 -127
  158. package/coverage/lcov-report/core/api/services/account/index.html +22 -22
  159. package/coverage/lcov-report/core/api/services/authorisations/authorisations.hooks.js.html +1 -1
  160. package/coverage/lcov-report/core/api/services/authorisations/authorisations.service.js.html +213 -222
  161. package/coverage/lcov-report/core/api/services/authorisations/index.html +21 -21
  162. package/coverage/lcov-report/core/api/services/{groups/groups.hooks.js.html → configurations/configurations.hooks.js.html} +16 -10
  163. package/coverage/lcov-report/core/api/services/{groups → configurations}/index.html +8 -8
  164. package/coverage/lcov-report/core/api/services/databases/databases.hooks.js.html +1 -1
  165. package/coverage/lcov-report/core/api/services/databases/databases.service.js.html +1 -1
  166. package/coverage/lcov-report/core/api/services/databases/index.html +1 -1
  167. package/coverage/lcov-report/core/api/services/import-export/import-export.hooks.js.html +76 -76
  168. package/coverage/lcov-report/core/api/services/import-export/import-export.service.js.html +32 -32
  169. package/coverage/lcov-report/core/api/services/import-export/index.html +32 -32
  170. package/coverage/lcov-report/core/api/services/index.html +21 -21
  171. package/coverage/lcov-report/core/api/services/index.js.html +313 -142
  172. package/coverage/lcov-report/core/api/services/mailer/index.html +32 -32
  173. package/coverage/lcov-report/core/api/services/mailer/mailer.hooks.js.html +80 -80
  174. package/coverage/lcov-report/core/api/services/mailer/mailer.service.js.html +32 -32
  175. package/coverage/lcov-report/core/api/services/messages/index.html +21 -21
  176. package/coverage/lcov-report/core/api/services/messages/messages.hooks.js.html +112 -76
  177. package/coverage/lcov-report/core/api/services/push/index.html +32 -32
  178. package/coverage/lcov-report/core/api/services/push/push.hooks.js.html +80 -80
  179. package/coverage/lcov-report/core/api/services/push/push.service.js.html +34 -34
  180. package/coverage/lcov-report/core/api/services/storage/index.html +29 -29
  181. package/coverage/lcov-report/core/api/services/storage/storage.hooks.js.html +80 -80
  182. package/coverage/lcov-report/core/api/services/storage/storage.service.js.html +29 -29
  183. package/coverage/lcov-report/core/api/services/tags/index.html +21 -21
  184. package/coverage/lcov-report/core/api/services/tags/tags.hooks.js.html +119 -71
  185. package/coverage/lcov-report/core/api/services/users/index.html +27 -12
  186. package/coverage/lcov-report/core/api/services/users/users.hooks.js.html +14 -11
  187. package/coverage/lcov-report/core/api/services/users/users.service.js.html +100 -0
  188. package/coverage/lcov-report/core/common/errors.js.html +1 -1
  189. package/coverage/lcov-report/core/common/index.html +42 -27
  190. package/coverage/lcov-report/core/common/index.js.html +1 -1
  191. package/coverage/lcov-report/core/common/permissions.js.html +166 -472
  192. package/coverage/lcov-report/core/common/schema.js.html +4 -4
  193. package/coverage/lcov-report/core/common/utils.js.html +31 -25
  194. package/coverage/lcov-report/core/common/utils.offline.js.html +199 -0
  195. package/coverage/lcov-report/index.html +192 -192
  196. package/coverage/lcov-report/map/api/hooks/hooks.catalog.js.html +169 -31
  197. package/coverage/lcov-report/map/api/hooks/hooks.features.js.html +1 -1
  198. package/coverage/lcov-report/map/api/hooks/hooks.query.js.html +215 -35
  199. package/coverage/lcov-report/map/api/hooks/index.html +7 -7
  200. package/coverage/lcov-report/map/api/hooks/index.js.html +1 -1
  201. package/coverage/lcov-report/map/api/index.html +1 -1
  202. package/coverage/lcov-report/map/api/index.js.html +1 -1
  203. package/coverage/lcov-report/map/api/marshall.js.html +1 -1
  204. package/coverage/lcov-report/map/api/models/alerts.model.mongodb.js.html +1 -1
  205. package/coverage/lcov-report/map/api/models/catalog.model.mongodb.js.html +82 -7
  206. package/coverage/lcov-report/map/api/models/features.model.mongodb.js.html +1 -1
  207. package/coverage/lcov-report/map/api/models/index.html +22 -7
  208. package/coverage/lcov-report/map/api/models/projects.model.mongodb.js.html +1 -1
  209. package/coverage/lcov-report/{core/api/models/groups.model.mongodb.js.html → map/api/models/styles.model.mongodb.js.html} +10 -7
  210. package/coverage/lcov-report/map/api/services/alerts/alerts.hooks.js.html +1 -1
  211. package/coverage/lcov-report/map/api/services/alerts/alerts.service.js.html +1 -1
  212. package/coverage/lcov-report/map/api/services/alerts/index.html +1 -1
  213. package/coverage/lcov-report/map/api/services/catalog/catalog.hooks.js.html +39 -12
  214. package/coverage/lcov-report/map/api/services/catalog/index.html +5 -5
  215. package/coverage/lcov-report/map/api/services/daptiles/daptiles.service.js.html +1 -1
  216. package/coverage/lcov-report/map/api/services/daptiles/index.html +1 -1
  217. package/coverage/lcov-report/map/api/services/features/features.hooks.js.html +86 -11
  218. package/coverage/lcov-report/map/api/services/features/features.service.js.html +307 -4
  219. package/coverage/lcov-report/map/api/services/features/index.html +7 -7
  220. package/coverage/lcov-report/map/api/services/index.html +5 -5
  221. package/coverage/lcov-report/map/api/services/index.js.html +326 -50
  222. package/coverage/lcov-report/map/api/services/projects/index.html +1 -1
  223. package/coverage/lcov-report/map/api/services/projects/projects.hooks.js.html +1 -1
  224. package/coverage/{core/api/services/organisations → lcov-report/map/api/services/styles}/index.html +10 -25
  225. package/coverage/lcov-report/{core/api/services/organisations/organisations.hooks.js.html → map/api/services/styles/styles.hooks.js.html} +45 -12
  226. package/coverage/lcov-report/map/common/dynamic-grid-source.js.html +1 -1
  227. package/coverage/lcov-report/map/common/errors.js.html +1 -1
  228. package/coverage/lcov-report/map/common/geotiff-grid-source.js.html +7 -10
  229. package/coverage/lcov-report/map/common/grid.js.html +1 -1
  230. package/coverage/lcov-report/map/common/index.html +19 -19
  231. package/coverage/lcov-report/map/common/index.js.html +1 -1
  232. package/coverage/lcov-report/map/common/meteo-model-grid-source.js.html +1 -1
  233. package/coverage/lcov-report/map/common/moment-utils.js.html +1 -1
  234. package/coverage/lcov-report/map/common/opendap-grid-source.js.html +1 -1
  235. package/coverage/lcov-report/map/common/opendap-utils.js.html +4 -7
  236. package/coverage/lcov-report/map/common/permissions.js.html +10 -4
  237. package/coverage/lcov-report/map/common/time-based-grid-source.js.html +1 -1
  238. package/coverage/lcov-report/map/common/tms-utils.js.html +9 -12
  239. package/coverage/lcov-report/map/common/wcs-grid-source.js.html +3 -3
  240. package/coverage/lcov-report/map/common/wcs-utils.js.html +12 -15
  241. package/coverage/lcov-report/map/common/weacast-grid-source.js.html +2 -2
  242. package/coverage/lcov-report/map/common/wfs-utils.js.html +14 -17
  243. package/coverage/lcov-report/map/common/wms-utils.js.html +30 -12
  244. package/coverage/lcov-report/map/common/wmts-utils.js.html +10 -13
  245. package/coverage/lcov.info +4157 -3816
  246. package/coverage/map/api/hooks/hooks.catalog.js.html +169 -31
  247. package/coverage/map/api/hooks/hooks.features.js.html +1 -1
  248. package/coverage/map/api/hooks/hooks.query.js.html +215 -35
  249. package/coverage/map/api/hooks/index.html +7 -7
  250. package/coverage/map/api/hooks/index.js.html +1 -1
  251. package/coverage/map/api/index.html +1 -1
  252. package/coverage/map/api/index.js.html +1 -1
  253. package/coverage/map/api/marshall.js.html +1 -1
  254. package/coverage/map/api/models/alerts.model.mongodb.js.html +1 -1
  255. package/coverage/map/api/models/catalog.model.mongodb.js.html +82 -7
  256. package/coverage/map/api/models/features.model.mongodb.js.html +1 -1
  257. package/coverage/map/api/models/index.html +22 -7
  258. package/coverage/map/api/models/projects.model.mongodb.js.html +1 -1
  259. package/coverage/{core/api/models/groups.model.mongodb.js.html → map/api/models/styles.model.mongodb.js.html} +10 -7
  260. package/coverage/map/api/services/alerts/alerts.hooks.js.html +1 -1
  261. package/coverage/map/api/services/alerts/alerts.service.js.html +1 -1
  262. package/coverage/map/api/services/alerts/index.html +1 -1
  263. package/coverage/map/api/services/catalog/catalog.hooks.js.html +39 -12
  264. package/coverage/map/api/services/catalog/index.html +5 -5
  265. package/coverage/map/api/services/daptiles/daptiles.service.js.html +1 -1
  266. package/coverage/map/api/services/daptiles/index.html +1 -1
  267. package/coverage/map/api/services/features/features.hooks.js.html +86 -11
  268. package/coverage/map/api/services/features/features.service.js.html +307 -4
  269. package/coverage/map/api/services/features/index.html +7 -7
  270. package/coverage/map/api/services/index.html +5 -5
  271. package/coverage/map/api/services/index.js.html +326 -50
  272. package/coverage/map/api/services/projects/index.html +1 -1
  273. package/coverage/map/api/services/projects/projects.hooks.js.html +1 -1
  274. package/coverage/{lcov-report/core/api/services/organisations → map/api/services/styles}/index.html +10 -25
  275. package/coverage/{core/api/services/groups/groups.hooks.js.html → map/api/services/styles/styles.hooks.js.html} +45 -12
  276. package/coverage/map/common/dynamic-grid-source.js.html +1 -1
  277. package/coverage/map/common/errors.js.html +1 -1
  278. package/coverage/map/common/geotiff-grid-source.js.html +7 -10
  279. package/coverage/map/common/grid.js.html +1 -1
  280. package/coverage/map/common/index.html +19 -19
  281. package/coverage/map/common/index.js.html +1 -1
  282. package/coverage/map/common/meteo-model-grid-source.js.html +1 -1
  283. package/coverage/map/common/moment-utils.js.html +1 -1
  284. package/coverage/map/common/opendap-grid-source.js.html +1 -1
  285. package/coverage/map/common/opendap-utils.js.html +4 -7
  286. package/coverage/map/common/permissions.js.html +10 -4
  287. package/coverage/map/common/time-based-grid-source.js.html +1 -1
  288. package/coverage/map/common/tms-utils.js.html +9 -12
  289. package/coverage/map/common/wcs-grid-source.js.html +3 -3
  290. package/coverage/map/common/wcs-utils.js.html +12 -15
  291. package/coverage/map/common/weacast-grid-source.js.html +2 -2
  292. package/coverage/map/common/wfs-utils.js.html +14 -17
  293. package/coverage/map/common/wms-utils.js.html +30 -12
  294. package/coverage/map/common/wmts-utils.js.html +10 -13
  295. package/coverage/tmp/coverage-1028514-1773134124472-0.json +1 -0
  296. package/coverage/tmp/coverage-1028526-1773134124448-0.json +1 -0
  297. package/coverage/tmp/coverage-1028537-1773134124431-0.json +1 -0
  298. package/coverage/tmp/coverage-1028549-1773134124401-0.json +1 -0
  299. package/coverage/tmp/coverage-1028556-1773134124353-0.json +1 -0
  300. package/extras/configs/widgets.top.js +3 -3
  301. package/extras/tests/core/collection.mjs +2 -9
  302. package/extras/tours/pane.top.js +0 -9
  303. package/map/api/hooks/hooks.catalog.js +18 -4
  304. package/map/api/services/catalog/catalog.hooks.js +3 -0
  305. package/map/api/services/features/features.hooks.js +3 -1
  306. package/map/api/services/index.js +2 -6
  307. package/map/api/services/styles/styles.hooks.js +6 -1
  308. package/map/client/components/KFeatureActionButton.vue +9 -3
  309. package/map/client/components/KFeaturesFilterManager.vue +5 -5
  310. package/map/client/components/KFilterCondition.vue +17 -10
  311. package/map/client/components/KLayerEditor.vue +49 -39
  312. package/map/client/components/KMeasureTool.vue +7 -1
  313. package/map/client/components/KTimezoneMap.vue +29 -9
  314. package/map/client/components/catalog/KLayersPanel.vue +26 -16
  315. package/map/client/components/catalog/KLayersSelector.vue +13 -2
  316. package/map/client/components/catalog/KViewsPanel.vue +5 -4
  317. package/map/client/components/form/KSelectLayersField.vue +28 -17
  318. package/map/client/components/form/KSelectViewsField.vue +18 -9
  319. package/map/client/components/form/KTimezoneField.vue +1 -2
  320. package/map/client/components/legend/KVariablesLegend.vue +10 -1
  321. package/map/client/components/location/KLocationCardSection.vue +7 -2
  322. package/map/client/components/location/KLocationMap.vue +31 -7
  323. package/map/client/components/selection/KSelectedLayerFeatures.vue +2 -2
  324. package/map/client/components/stickies/KZoomControl.vue +1 -1
  325. package/map/client/components/styles/KStyleManager.vue +4 -1
  326. package/map/client/components/widget/KTimeSeries.vue +174 -497
  327. package/map/client/components/widget/KTimeSeriesSelector.vue +72 -0
  328. package/map/client/components/widget/KTimeSeriesToolbar.vue +83 -0
  329. package/map/client/composables/catalog.js +6 -10
  330. package/map/client/composables/highlight.js +12 -9
  331. package/map/client/composables/project.js +1 -1
  332. package/map/client/composables/selection.js +8 -7
  333. package/map/client/composables/weather.js +9 -2
  334. package/map/client/geolocation.js +8 -5
  335. package/map/client/i18n/map_en.json +11 -10
  336. package/map/client/i18n/map_fr.json +10 -9
  337. package/map/client/leaflet/TiledFeatureLayer.js +85 -82
  338. package/map/client/leaflet/utils/utils.geojson.js +3 -3
  339. package/map/client/mixins/globe/mixin.base-globe.js +15 -6
  340. package/map/client/mixins/globe/mixin.geojson-layers.js +27 -18
  341. package/map/client/mixins/map/mixin.edit-layers.js +9 -1
  342. package/map/client/mixins/map/mixin.pmtiles-layers.js +118 -29
  343. package/map/client/mixins/map/mixin.tiled-mesh-layers.js +12 -5
  344. package/map/client/mixins/map/mixin.tiled-wind-layers.js +19 -10
  345. package/map/client/mixins/mixin.activity.js +23 -30
  346. package/map/client/mixins/mixin.feature-selection.js +41 -5
  347. package/map/client/planets.js +1 -1
  348. package/map/client/readers/reader.kml.js +2 -3
  349. package/map/client/utils/utils.catalog.js +36 -10
  350. package/map/client/utils/utils.layers.js +39 -8
  351. package/map/client/utils/utils.project.js +4 -0
  352. package/map/client/utils/utils.style.js +37 -7
  353. package/map/client/utils/utils.time-series.js +215 -6
  354. package/map/common/schemas/catalog.update.json +1 -1
  355. package/map/common/weacast-grid-source.js +1 -1
  356. package/package.json +3 -3
  357. package/scripts/kash/CHANGELOG.md +0 -4
  358. package/scripts/kash/README.md +0 -9
  359. package/scripts/kash/kash.sh +45 -40
  360. package/scripts/kash/scripts/run_tests.sh +1 -4
  361. package/test/api/core/authentication.test.js +9 -4
  362. package/test/api/core/config/default.cjs +1 -0
  363. package/test/api/core/hooks.test.js +6 -0
  364. package/test/api/core/index.test.js +43 -18
  365. package/test/api/core/push.test.js +8 -8
  366. package/test/api/core/test-log-2026-03-10.log +60 -0
  367. package/test/api/core/users.test.js +384 -0
  368. package/test/api/map/grid-sources.test.js +1 -1
  369. package/test/api/map/test-log-2026-03-10.log +56 -0
  370. package/vite/package.json +11 -2
  371. package/vite/test/core/composables.test.js +77 -0
  372. package/vite/vitest.config.js +13 -0
  373. package/vite/yarn.lock +1096 -18
  374. package/.vscode/settings.json +0 -5
  375. package/core/client/components/account/KAccount.vue +0 -68
  376. package/core/client/components/account/KDeleteAccountManager.vue +0 -62
  377. package/core/client/components/account/KEmailManager.vue +0 -128
  378. package/core/client/components/account/KPasswordManager.vue +0 -90
  379. package/core/client/components/account/KVerifyEmailManager.vue +0 -105
  380. package/core/client/components/collection/KColumn.vue +0 -227
  381. package/core/client/components/collection/KHistory.vue +0 -113
  382. package/core/client/components/collection/KHistoryEntry.vue +0 -109
  383. package/coverage/core/api/hooks/hooks.organisations.js.html +0 -541
  384. package/coverage/core/api/services/organisations/organisations.service.js.html +0 -343
  385. package/coverage/core/api/utils.js.html +0 -118
  386. package/coverage/lcov-report/core/api/hooks/hooks.organisations.js.html +0 -541
  387. package/coverage/lcov-report/core/api/services/organisations/organisations.service.js.html +0 -343
  388. package/coverage/lcov-report/core/api/utils.js.html +0 -118
  389. package/coverage/tmp/coverage-151166-1723543324307-0.json +0 -1
  390. package/coverage/tmp/coverage-151178-1723543324283-0.json +0 -1
  391. package/coverage/tmp/coverage-151189-1723543324271-0.json +0 -1
  392. package/coverage/tmp/coverage-151201-1723543324248-0.json +0 -1
  393. package/coverage/tmp/coverage-151208-1723543324227-0.json +0 -1
  394. package/scripts/kash/LICENSE +0 -21
  395. package/test/api/core/test-log-2024-04-22.log +0 -84
  396. package/test/api/core/test-log-2024-04-23.log +0 -23
  397. package/test/api/core/test-log-2024-08-13.log +0 -3
  398. package/test/api/map/test-log-2025-03-08.log +0 -0
@@ -1,508 +1,185 @@
1
1
  <template>
2
- <div id="time-series" class="column">
3
- <k-chart id="timeseries-chart" :ref="onChartCreated" class="col q-pl-sm q-pr-sm" />
2
+ <div class="column justify-start no-wrap">
3
+ <q-resize-observer @resize="onResized" />
4
+ <q-splitter v-if="isSelectorVisible" v-model="splitterModel" :style="`height: ${height}px`">
5
+ <template v-slot:before>
6
+ <KTimeSeriesSelector
7
+ :menu="false"
8
+ />
9
+ </template>
10
+ <template v-slot:after>
11
+ <div class="fit column justify-start no-wrap">
12
+ <KStackableTimeSeries
13
+ ref="chartRef"
14
+ class="col"
15
+ :time-series="timeSeries"
16
+ :chart-options="chartOptions"
17
+ :actions="actions"
18
+ @zoom-end="onZoomEnd"
19
+ />
20
+ </div>
21
+ </template>
22
+ </q-splitter>
23
+ <div v-else class="col column justify-start no-wrap">
24
+ <KStackableTimeSeries
25
+ ref="chartRef"
26
+ class="col"
27
+ :time-series="timeSeries"
28
+ :chart-options="chartOptions"
29
+ :actions="actions"
30
+ @zoom-end="onZoomEnd"
31
+ />
32
+ </div>
4
33
  </div>
5
34
  </template>
6
35
 
7
- <script>
36
+ <script setup>
8
37
  import _ from 'lodash'
9
- import moment from 'moment'
10
- import logger from 'loglevel'
11
- import centroid from '@turf/centroid'
12
- import Papa from 'papaparse'
13
- import { downloadAsBlob } from '../../../../core/client/utils'
14
- import { Store } from '../../../../core/client/store'
15
- import { Units } from '../../../../core/client/units'
16
- import { Time } from '../../../../core/client/time'
17
- import { Events } from '../../../../core/client/events'
18
- import { KChart } from '../../../../core/client/components'
19
- import { useCurrentActivity, useWeather, useMeasure, useHighlight } from '../../composables'
20
- import 'chartjs-adapter-moment'
21
- import { getCssVar } from 'quasar'
38
+ import { ref, computed, watch } from 'vue'
39
+ import { Store, composables as kCoreComposables } from '../../../../core/client/index.js'
40
+ import KStackableTimeSeries from './KStackableTimeSeries.vue'
41
+ import KTimeSeriesSelector from './KTimeSeriesSelector.vue'
42
+ import { getChartOptions } from '../../utils/utils.time-series.js'
22
43
 
23
- export default {
24
- name: 'k-time-series',
25
- components: {
26
- KChart
27
- },
28
- props: {
29
- highlight: {
30
- type: Object,
31
- default: () => ({ 'stroke-color': 'primary', 'fill-opacity': 0, zOrder: 1 })
32
- }
33
- },
34
- computed: {
35
- title () {
36
- // Compute the layer name if any
37
- const layerName = this.hasSelectedFeature() && this.getSelectedLayer() ? this.$t(this.getSelectedLayer().name) : undefined
38
- // Compute the probe location name
39
- let probeName
40
- if (this.probedLocation) {
41
- // Check if we have a property as tooltip or popup and use it
42
- if (this.hasSelectedLayer()) {
43
- const probeNameProperty = _.get(this.getSelectedLayer(), `${this.kActivity.engine}.tooltip.property`,
44
- _.get(this.getSelectedLayer(), `${this.kActivity.engine}.popup.pick[0]`))
45
- if (probeNameProperty) probeName = _.get(this.probedLocation, `properties.${probeNameProperty}`)
46
- }
47
- // Otherwise test for conventional names otherwise
48
- if (!probeName) probeName = _.get(this.probedLocation, 'properties.name', _.get(this.probedLocation, 'properties.NAME'))
49
- }
50
- if (layerName && probeName) return `${layerName} - ${probeName}`
51
- if (probeName) return probeName
52
- return ''
53
- },
54
- location () {
55
- if (this.hasSelectedLocation()) return this.getSelectedLocation()
56
- else if (this.hasProbedLocation()) return this.getProbedLocation()
57
- },
58
- feature () {
59
- return this.hasSelectedFeature() && this.getSelectedFeature()
60
- },
61
- layer () {
62
- if (this.hasSelectedLayer()) return this.getSelectedLayer()
63
- else if (this.hasProbedLayer()) return this.getProbedLayer()
64
- },
65
- probedVariables () {
66
- // If the feature is linked to a layer with variables use it
67
- // Otherwise use all available variables to search for those applicable to it
68
- return (this.layer && this.layer.variables ? this.layer.variables : this.kActivity.variables)
69
- },
70
- runOptions () {
71
- // Build options from runtimes for UI
72
- let runOptions = []
73
- if (this.hasRunTimes()) {
74
- runOptions = this.runTimes.map(runTime => ({
75
- label: Time.format(runTime, 'date.short') + ' - ' + Time.format(runTime, 'time.short'),
76
- value: runTime
77
- }))
78
- // Select latest runTime as default option
79
- _.last(runOptions).default = true
80
- }
81
- return runOptions
82
- }
83
- },
84
- watch: {
85
- variables: function () {
86
- this.refresh()
87
- },
88
- // This also cover the case where the feature changes
89
- location: function () {
90
- this.refresh()
91
- }
92
- },
93
- data () {
94
- return {
95
- probedLocation: null,
96
- zoomHistory: [],
97
- runTime: null,
98
- runTimes: []
99
- }
100
- },
101
- methods: {
102
- hasVariable (name, properties, match) {
103
- // The feature target the variable if it has at least the target variable value name
104
- let hasVariable = (properties[name] && Array.isArray(properties[name]))
105
- // Test if the variable needs to match additional properties
106
- // in case multiple variables target the same property value name
107
- if (match) {
108
- _.forOwn(match, (value, key) => { hasVariable = hasVariable && (properties[key] === value) })
109
- }
110
- return hasVariable
111
- },
112
- hasZoomHistory () {
113
- return this.zoomHistory.length > 0
114
- },
115
- getBaseUnit (variable, properties) {
116
- const unit = variable.unit
117
- // Could be either directly the unit or the property of the measure storing the unit
118
- return _.get(properties, unit, unit)
119
- },
120
- hasRunTimes () {
121
- return this.runTimes && (this.runTimes.length > 1)
122
- },
123
- getSelectedRunTime () {
124
- // Set default run as latest
125
- return this.runTime || _.last(this.runTimes)
126
- },
127
- setupAvailableRunTimes () {
128
- this.runTimes = []
129
- const runTime = this.probedLocation.runTime
130
- this.probedVariables.forEach(variable => {
131
- if (!variable.runTimes) return
132
- // Check if we are targetting a specific level
133
- const name = (this.kActivity.forecastLevel ? `${variable.name}-${this.kActivity.forecastLevel}` : variable.name)
44
+ // Data
45
+ const chartRef = ref(null)
46
+ const chartOptions = ref(getChartOptions())
47
+ const splitterModel = ref(25)
48
+ const isZoomed = ref(false)
49
+ const height = ref(0)
50
+ const { CurrentActivityContext } = kCoreComposables.useCurrentActivity()
51
+ const { state } = CurrentActivityContext
134
52
 
135
- if (runTime && runTime[name]) this.runTimes.push(runTime[name])
136
- })
137
- // Make union of all available run times
138
- this.runTimes = _.union(...this.runTimes).map(time => moment.utc(time)).sort((a, b) => a - b)
139
- },
140
- setupAvailableDatasets () {
141
- this.datasets = []
142
- const time = this.probedLocation.time || this.probedLocation.forecastTime
143
- const runTime = this.probedLocation.runTime
144
- const properties = this.probedLocation.properties
145
- let axisId = 0
146
- this.probedVariables.forEach((variable, index) => {
147
- // Check if we are targetting a specific level
148
- const name = (this.kActivity.forecastLevel ? `${variable.name}-${this.kActivity.forecastLevel}` : variable.name)
149
- // Falback to base unit
150
- const unit = this.getBaseUnit(variable, properties)
151
- const label = this.$t(variable.label) || variable.label
152
- // Aggregated variable available for feature ?
153
- if (this.hasVariable(name, properties, variable.baseQuery)) {
154
- // Build data structure as expected by visualisation
155
- let values = properties[name].map((value, index) => ({ x: time[name][index], y: value }))
156
- // Keep only selected value if multiple are provided for the same time (eg different forecasts)
157
- if (variable.runTimes && !_.isEmpty(_.get(runTime, name)) && this.getSelectedRunTime()) {
158
- values = values.filter((value, index) => (runTime[name][index] === this.getSelectedRunTime().toISOString()))
159
- } else values = _.uniqBy(values, 'x')
160
- // Then transform to date object as expected by visualisation
161
- // To enable decimation the x, e.g. time, values should be defined in millisecond (parsing is disable)
162
- values = values.map((value) => Object.assign(value, { x: new Date(value.x).getTime() }))
163
- this.datasets.push(_.merge({
164
- label: `${label} (${Units.getTargetUnitSymbol(unit)})`,
165
- unit,
166
- data: values,
167
- cubicInterpolationMode: 'monotone',
168
- tension: 0.4,
169
- yAxisID: `y${axisId++}`
170
- }, _.omit(variable.chartjs, 'yAxis')))
171
- }
172
- })
173
- },
174
- setupAvailableYAxes () {
175
- this.yAxes = {}
176
- const properties = this.probedLocation.properties
177
- let axisId = 0
178
- this.probedVariables.forEach(variable => {
179
- // Check if we are targetting a specific level
180
- const name = (this.kActivity.forecastLevel ? `${variable.name}-${this.kActivity.forecastLevel}` : variable.name)
181
- // Falback to base unit
182
- const unit = this.getBaseUnit(variable, properties)
183
- // Variable available for feature ?
184
- if (this.hasVariable(name, properties, variable.baseQuery)) {
185
- this.yAxes[`y${axisId}`] = _.merge({
186
- unit,
187
- display: 'auto',
188
- position: (axisId + 1) % 2 ? 'left' : 'right',
189
- ticks: {
190
- color: this.datasets[axisId].backgroundColor,
191
- callback: function (value, index, values) {
192
- if (values[index] !== undefined) {
193
- return Units.format(values[index].value, unit, null, { symbol: false })
194
- }
195
- }
196
- }
197
- }, _.get(variable.chartjs, 'yAxis', {}))
198
- axisId++
199
- }
200
- })
201
- },
202
- hasAvailableDatasets () {
203
- const keys = Object.keys(this.probedLocation.properties)
204
- for (let i = 0; i < this.probedVariables.length; i++) {
205
- let name = this.probedVariables[i].name
206
- // Check if we are targetting a specific level
207
- if (this.kActivity.forecastLevel) name = `${name}-${this.kActivity.forecastLevel}`
208
- if (_.indexOf(keys, name) !== -1) {
209
- const values = this.probedLocation.properties[name]
210
- if (values && Array.isArray(values)) return true
211
- }
212
- }
213
- return false
214
- },
215
- onChartCreated (ref) {
216
- if (ref && !this.chart) {
217
- this.chart = ref
218
- }
219
- },
220
- async setupGraph () {
221
- if (!this.probedLocation) return
222
- // As we have async operations during the whole chart creation process avoid reentrance
223
- // otherwise we might have interleaved calls leading to multiple charts being created
224
- if (this.buildingChart) return
225
- // Try/Catch required to ensure we reset the build flag
226
- try {
227
- this.buildingChart = true
228
- // TODO this.setupAvailableTimes()
229
- // Compute appropriate time span gaps
230
- const scrapTimeSpan = _.get(this.layer, 'queryFrom')
231
- const timeSpanGaps = scrapTimeSpan ? Math.abs(moment.duration(scrapTimeSpan)) : undefined
232
- this.setupAvailableRunTimes()
233
- this.setupAvailableDatasets()
234
- this.setupAvailableYAxes()
235
- // Is current time visible in data time range ?
236
- const currentTime = Time.getCurrentTime()
237
- const timeRange = Time.getRange()
238
- const annotations = []
239
- if (currentTime.isBetween(timeRange.start, timeRange.end)) {
240
- annotations.push({
241
- type: 'line',
242
- mode: 'vertical',
243
- scaleID: 'x',
244
- value: currentTime.toDate(),
245
- borderColor: 'grey',
246
- borderWidth: 1,
247
- label: {
248
- backgroundColor: 'rgba(0,0,0,0.65)',
249
- content: _.get(Time.getCurrentFormattedTime(), 'time.long'),
250
- position: 'start',
251
- enabled: true
252
- }
253
- })
254
- }
255
- // Display also time of probed feature, only if single variable
256
- if ((this.probedVariables.length === 1) && this.feature && this.feature.time) {
257
- const time = moment.utc(this.feature.time[this.probedVariables[0].name])
258
- if (time.isBetween(timeRange.start, timeRange.end)) {
259
- annotations.push({
260
- type: 'line',
261
- mode: 'vertical',
262
- scaleID: 'x',
263
- value: time.toDate(),
264
- borderColor: 'green',
265
- borderWidth: 1,
266
- label: {
267
- backgroundColor: 'rgba(0,0,0,0.65)',
268
- content: Time.format(time, 'time.long'),
269
- position: 'start',
270
- enabled: true
271
- }
272
- })
273
- }
274
- }
275
- this.chart.update({
276
- type: 'line',
277
- data: {
278
- labels: this.times,
279
- datasets: this.datasets
280
- },
281
- options: _.merge({
282
- maintainAspectRatio: false,
283
- animation: false,
284
- parsing: false,
285
- spanGaps: timeSpanGaps,
286
- scales: {
287
- x: {
288
- type: 'time',
289
- time: {
290
- unit: 'hour'
291
- },
292
- ticks: {
293
- autoskip: true,
294
- maxRotation: 20,
295
- major: {
296
- enabled: true
297
- },
298
- callback: function (value, index, values) {
299
- if (values[index] !== undefined) {
300
- if (values[index].major === true) {
301
- return Time.format(moment(values[index].value), 'date.short')
302
- } else {
303
- return Time.format(moment(values[index].value), 'time.short')
304
- }
305
- }
306
- },
307
- font: function (context) {
308
- if (context.tick && context.tick.major) {
309
- return {
310
- weight: 'bold'
311
- }
312
- }
313
- }
314
- }
315
- }
316
- },
317
- plugins: {
318
- title: {
319
- display: true,
320
- text: this.title,
321
- align: 'start'
322
- },
323
- datalabels: {
324
- display: false
325
- },
326
- tooltip: {
327
- mode: 'x',
328
- callbacks: {
329
- title: (context) => {
330
- // As we are selecting tooltip items based on x coordinate all should have the same one, which is actually the time
331
- const x = _.get(context, '[0].parsed.x')
332
- return (x ? `${Time.format(x, 'date.short')} - ${Time.format(x, 'time.short')}` : '')
333
- },
334
- label: (context) => {
335
- const { unit, label } = context.dataset
336
- const y = _.get(context, 'parsed.y')
337
- // We have unit in label name for legend but we want it after the value for tooltip
338
- return label.replace(`(${Units.getTargetUnitSymbol(unit)})`, '') + ': ' + Units.format(y, unit)
339
- }
340
- }
341
- },
342
- annotation: {
343
- annotations
344
- },
345
- zoom: {
346
- zoom: {
347
- drag: {
348
- enabled: true,
349
- backgroundColor: getCssVar('secondary')
350
- },
351
- mode: 'x',
352
- onZoomStart: this.onZoomStarted,
353
- onZoom: this.onZoomed
354
- }
355
- },
356
- decimation: {
357
- enabled: true,
358
- algorithm: 'lttb'
359
- }
360
- }
361
- }, { scales: this.yAxes })
362
- })
363
- } catch (error) {
364
- logger.error(error)
365
- }
366
- this.buildingChart = false
367
- },
368
- onUpdateRun (runTime) {
369
- this.runTime = runTime
370
- this.setupGraph()
371
- },
372
- updateProbedLocationHighlight () {
373
- this.clearHighlights()
374
- if (!this.probedLocation) return
375
- const isWeatherProbe = this.isWeatherProbe(this.probedLocation)
376
- const feature = (isWeatherProbe
377
- ? this.getProbedLocationForecastAtCurrentTime(this.probedLocation)
378
- : this.getProbedLocationMeasureAtCurrentTime(this.probedLocation))
379
- this.highlight(feature, this.layer)
380
- },
381
- onZoomRestored () {
382
- if (!_.isEmpty(this.zoomHistory)) {
383
- Time.patchRange(_.last(this.zoomHistory))
384
- this.zoomHistory = _.slice(this.zoomHistory, 0, this.zoomHistory.length - 1)
385
- }
386
- },
387
- onZoomStarted ({ chart }) {
388
- this.zoomHistory.push({
389
- start: moment(Time.getRange().start),
390
- end: moment(Time.getRange().end)
391
- })
392
- },
393
- onZoomed ({ chart }) {
394
- const start = moment(_.get(chart, 'scales.x.min'))
395
- const end = moment(_.get(chart, 'scales.x.max'))
396
- Time.patchRange({ start, end })
397
- },
398
- onCenterOn () {
399
- this.kActivity.centerOnSelection()
400
- },
401
- onExportSeries () {
402
- let times = []
403
- const time = _.get(this.probedLocation, 'time', _.get(this.probedLocation, 'forecastTime'))
404
- this.probedVariables.forEach(variable => {
405
- // Check if we are targetting a specific level
406
- const name = (this.kActivity.forecastLevel ? `${variable.name}-${this.kActivity.forecastLevel}` : variable.name)
407
- if (time && time[name]) times.push(time[name])
408
- })
409
- // Make union of all available times for x-axis
410
- times = _.union(...times).map(time => moment.utc(time)).sort((a, b) => a - b)
411
- // Convert to json
412
- const json = times.map(time => {
413
- const row = {
414
- [this.$t('KTimeSeries.TIME_LABEL')]: time.toISOString()
415
- }
416
- this.datasets.forEach(dataset => {
417
- const value = _.find(dataset.data, item => moment.utc(item.x).toISOString() === time.toISOString())
418
- row[dataset.label] = value ? value.y : null
419
- })
420
- return row
421
- })
422
- // Convert to csv
423
- const csv = Papa.unparse(json)
424
- let filename = (this.title ? this.title.replace(/\s/g, '') : this.$t('KTimeSeries.SERIES_EXPORT_FILE'))
425
- filename += '_' + new Date().toLocaleString()
426
- downloadAsBlob(csv, _.snakeCase(filename) + '.csv', 'text/csv;charset=utf-8;')
427
- },
428
- async refresh () {
429
- // Clear previous run time setup if any
430
- this.runTime = null
431
- this.probedLocation = null
432
- this.clearHighlights()
433
- if (!this.location) {
434
- // Clear current graph if any
435
- this.chart.clear()
436
- return
437
- }
438
- // Then manage selection
439
- this.highlight(this.location, this.layer)
440
- if (this.hasProbedLocation()) this.centerOnProbe()
441
- else this.centerOnSelection()
442
- // Update timeseries data if required
443
- const { start, end } = Time.getRange()
444
- // No feature clicked => dynamic weacast probe at position
445
- if (!this.feature) {
446
- if (this.kActivity.probeLocation) { // Maybe there's a specific probeLocation function
447
- this.probedLocation = await this.kActivity.probeLocation(this.location.lng, this.location.lat, start, end)
448
- }
449
- if (!this.probedLocation) {
450
- this.probedLocation = await this.kActivity.getForecastForLocation(this.location.lng, this.location.lat, start, end)
451
- _.set(this.probedLocation, 'properties.name', this.$t('mixins.timeseries.FORECAST_PROBE') +
452
- ` (${this.location.lng.toFixed(2)}°, ${this.location.lat.toFixed(2)}°)`)
453
- }
454
- } else if (this.layer.probe) { // Static weacast probe
455
- const probe = await this.kActivity.getForecastProbe(this.layer.probe)
456
- if (probe) {
457
- this.probedLocation = await this.kActivity.getForecastForFeature(_.get(this.feature, probe.featureId), start, end)
458
- }
459
- } else if (this.layer.variables && this.layer.service) { // Static measure probe
460
- this.probedLocation = await this.kActivity.getMeasureForFeature(this.layer, this.feature, start, end)
461
- } else { // dynamic weacast probe at feature position
462
- const name = _.get(this.feature, 'properties.name', _.get(this.feature, 'properties.NAME'))
463
- const location = centroid(this.feature)
464
- const longitude = _.get(location, 'geometry.coordinates[0]')
465
- const latitude = _.get(location, 'geometry.coordinates[1]')
466
- this.probedLocation = await this.kActivity.getForecastForLocation(longitude, latitude, start, end)
467
- _.set(this.probedLocation, 'properties.name', this.$t('mixins.timeseries.FORECAST_PROBE') +
468
- (name ? ` (${name})` : ` (${longitude.toFixed(2)}°, ${latitude.toFixed(2)}°)`))
469
- }
470
- await this.setupGraph()
471
- this.updateProbedLocationHighlight()
472
- }
473
- },
474
- async mounted () {
475
- // Initialize the time range
476
- const span = Store.get('timeseries.span')
477
- const start = moment(Time.getCurrentTime()).subtract(span, 'm')
478
- const end = moment(Time.getCurrentTime()).add(span, 'm')
479
- Time.patchRange({ start, end })
480
- // Force a first refresh
481
- await this.refresh()
482
- // Then setup listeners
483
- Events.on('time-current-time-changed', this.refresh)
484
- Events.on('time-range-changed', this.refresh)
485
- Events.on('time-format-changed', this.refresh)
486
- Events.on('timeseries-span-changed', this.refresh)
487
- this.kActivity.$engineEvents.on('forecast-model-changed', this.refresh)
488
- this.kActivity.$engineEvents.on('selected-level-changed', this.refresh)
489
- },
490
- beforeUnmount () {
491
- // Release listeners
492
- Events.off('time-current-time-changed', this.refresh)
493
- Events.off('time-range-changed', this.refresh)
494
- Events.off('time-format-changed', this.refresh)
495
- Events.off('timeseries-span-changed', this.refresh)
496
- this.kActivity.$engineEvents.off('forecast-model-changed', this.refresh)
497
- this.kActivity.$engineEvents.off('selected-level-changed', this.refresh)
498
- },
499
- setup (props) {
500
- return {
501
- ...useCurrentActivity(),
502
- ...useWeather(),
503
- ...useMeasure(),
504
- ...useHighlight('time-series', props.highlight)
53
+ // Computed
54
+ const hasSingleSerie = computed(() => {
55
+ return state.timeSeries.length === 1
56
+ })
57
+ const hasPinnedSerie = computed(() => {
58
+ return _.find(state.timeSeries, timeSerie => timeSerie.pinned)
59
+ })
60
+ const actions = computed(() => {
61
+ return [{
62
+ id: 'time-serie-logarithmic',
63
+ icon: 'legend_toggle',
64
+ tooltip: 'KTimeSeries.LOGARITHMIC_SERIE_TOOLTIP',
65
+ toggle: { icon: 'legend_toggle', tooltip: 'KTimeSeries.LINEAR_SERIE_TOOLTIP' },
66
+ toggled: ':timeSerie.logarithmic',
67
+ size: 'sm',
68
+ handler: onLogarithmicSerie
69
+ }, {
70
+ id: 'export-time-serie',
71
+ icon: 'las la-file-download',
72
+ tooltip: 'KTimeSeries.EXPORT_SERIE_TOOLTIP',
73
+ size: 'sm',
74
+ handler: onExportData
75
+ }, {
76
+ id: 'pin-time-serie',
77
+ icon: 'las la-thumbtack',
78
+ tooltip: 'KTimeSeries.PIN_SERIE_TOOLTIP',
79
+ size: 'sm',
80
+ visible: (timeSerie) => !hasSingleSerie.value && !hasPinnedSerie.value,
81
+ handler: onPinSerie
82
+ }, {
83
+ id: 'unpin-time-serie',
84
+ icon: 'las la-thumbtack',
85
+ color: 'accent',
86
+ tooltip: 'KTimeSeries.UNPIN_SERIE_TOOLTIP',
87
+ size: 'sm',
88
+ visible: (timeSerie) => isPinned(timeSerie),
89
+ handler: onPinSerie
90
+ }]
91
+ })
92
+ const timeSeries = computed(() => state.timeSeries)
93
+ const isSelectorVisible = computed(() => {
94
+ const visible = Store.get('layout.windows.top.gt.sm')
95
+ return visible && !hasSingleSerie.value
96
+ })
97
+
98
+ // Watch change in time series drivers like time range, format, ...
99
+ watch(Store.get('time.range'), async () => {
100
+ if (chartRef.value) {
101
+ // Reset any zoom
102
+ chartRef.value.resetZoom()
103
+ isZoomed.value = true
104
+ }
105
+ // Update underlying data
106
+ await fetchData()
107
+ // Then graphics
108
+ updateChart()
109
+ })
110
+ watch([Store.get('time.format'), Store.get('units.default')], () => {
111
+ // Update graphics
112
+ updateChart()
113
+ })
114
+
115
+ // Functions
116
+ async function fetchData () {
117
+ if (!chartRef.value) return
118
+ const promises = []
119
+ _.forEach(timeSeries.value, timeSerie => {
120
+ _.forEach(timeSerie.series, serie => {
121
+ promises.push(serie.fetch())
122
+ })
123
+ })
124
+ // Need to wait for data before updating
125
+ await Promise.all(promises)
126
+ }
127
+ function updateChart () {
128
+ if (!chartRef.value) return
129
+ chartRef.value.requestUpdate()
130
+ }
131
+ function isPinned (timeSerie) {
132
+ timeSerie = _.find(state.timeSeries, { id: timeSerie.id })
133
+ return timeSerie && timeSerie.pinned
134
+ }
135
+ function pinTimeSerie (timeSerie) {
136
+ // Pinning means showing an additional graph
137
+ const invisibleTimeSerie = state.timeSeries.find(timeSerie => !timeSerie.visible)
138
+
139
+ // Only one graph visible at a time if no pinning
140
+ // Otherwise only one in addition to the pinned one
141
+ // This is managed by pinning action that will hide once pinned
142
+ _.forEach(state.timeSeries, timeSerie => {
143
+ timeSerie.visible = (timeSerie.id === invisibleTimeSerie.id) || timeSerie.pinned
144
+ })
145
+ }
146
+ function unpinTimeSerie (timeSerie) {
147
+ // Unpinning means hiding the graph
148
+ timeSerie.visible = false
149
+ }
150
+ function onPinSerie (timeSerie) {
151
+ timeSerie = _.find(state.timeSeries, { id: timeSerie.id })
152
+ if (timeSerie) {
153
+ timeSerie.pinned = !timeSerie.pinned
154
+ if (timeSerie.pinned) {
155
+ pinTimeSerie(timeSerie)
156
+ chartRef.value.zoomToData()
157
+ } else {
158
+ unpinTimeSerie(timeSerie)
159
+ chartRef.value.resetZoom()
505
160
  }
506
161
  }
507
162
  }
163
+ function onLogarithmicSerie (timeSerie) {
164
+ timeSerie = _.find(state.timeSeries, { id: timeSerie.id })
165
+ if (timeSerie) timeSerie.logarithmic = !timeSerie.logarithmic
166
+ }
167
+ function onExportData (timeSerie) {
168
+ if (chartRef.value) chartRef.value.exportSeries(timeSerie)
169
+ }
170
+ function onZoomEnd ({ chart, start, end }) {
171
+ isZoomed.value = true
172
+ }
173
+ function onRestoreZoom () {
174
+ isZoomed.value = (chartRef.value ? chartRef.value.restorePreviousZoom() : false)
175
+ }
176
+ function onResized (size) {
177
+ height.value = size.height
178
+ }
179
+
180
+ // Expose
181
+ defineExpose({
182
+ isZoomed,
183
+ onRestoreZoom
184
+ })
508
185
  </script>