@kalisio/kdk 2.6.3 → 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 (419) 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 +1 -1
  69. package/coverage/core/api/authentication.js.html +1 -1
  70. package/coverage/core/api/db.js.html +1 -1
  71. package/coverage/core/api/hooks/hooks.authentication.js.html +1 -1
  72. package/coverage/core/api/hooks/hooks.authorisations.js.html +1 -1
  73. package/coverage/core/api/hooks/hooks.logger.js.html +1 -1
  74. package/coverage/core/api/hooks/hooks.model.js.html +1 -1
  75. package/coverage/core/api/hooks/hooks.push.js.html +22 -10
  76. package/coverage/core/api/hooks/hooks.query.js.html +33 -6
  77. package/coverage/core/api/hooks/hooks.schemas.js.html +1 -1
  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.tags.js.html +1 -1
  81. package/coverage/core/api/hooks/hooks.users.js.html +4 -4
  82. package/coverage/core/api/hooks/index.html +23 -23
  83. package/coverage/core/api/hooks/index.js.html +1 -1
  84. package/coverage/core/api/index.html +1 -1
  85. package/coverage/core/api/index.js.html +1 -1
  86. package/coverage/core/api/marshall.js.html +1 -1
  87. package/coverage/core/api/models/configurations.model.mongodb.js.html +1 -1
  88. package/coverage/core/api/models/index.html +1 -1
  89. package/coverage/core/api/models/messages.model.mongodb.js.html +1 -1
  90. package/coverage/core/api/models/tags.model.mongodb.js.html +1 -1
  91. package/coverage/core/api/models/users.model.mongodb.js.html +1 -1
  92. package/coverage/core/api/services/account/account.hooks.js.html +1 -1
  93. package/coverage/core/api/services/account/account.service.js.html +1 -1
  94. package/coverage/core/api/services/account/index.html +1 -1
  95. package/coverage/core/api/services/authorisations/authorisations.hooks.js.html +1 -1
  96. package/coverage/core/api/services/authorisations/authorisations.service.js.html +1 -1
  97. package/coverage/core/api/services/authorisations/index.html +1 -1
  98. package/coverage/core/api/services/configurations/configurations.hooks.js.html +1 -1
  99. package/coverage/core/api/services/configurations/index.html +1 -1
  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 +1 -1
  104. package/coverage/core/api/services/import-export/import-export.service.js.html +1 -1
  105. package/coverage/core/api/services/import-export/index.html +1 -1
  106. package/coverage/core/api/services/index.html +1 -1
  107. package/coverage/core/api/services/index.js.html +1 -1
  108. package/coverage/core/api/services/mailer/index.html +1 -1
  109. package/coverage/core/api/services/mailer/mailer.hooks.js.html +1 -1
  110. package/coverage/core/api/services/mailer/mailer.service.js.html +1 -1
  111. package/coverage/core/api/services/messages/index.html +1 -1
  112. package/coverage/core/api/services/messages/messages.hooks.js.html +1 -1
  113. package/coverage/core/api/services/push/index.html +1 -1
  114. package/coverage/core/api/services/push/push.hooks.js.html +1 -1
  115. package/coverage/core/api/services/push/push.service.js.html +1 -1
  116. package/coverage/core/api/services/storage/index.html +1 -1
  117. package/coverage/core/api/services/storage/storage.hooks.js.html +1 -1
  118. package/coverage/core/api/services/storage/storage.service.js.html +1 -1
  119. package/coverage/core/api/services/tags/index.html +1 -1
  120. package/coverage/core/api/services/tags/tags.hooks.js.html +1 -1
  121. package/coverage/core/api/services/users/index.html +1 -1
  122. package/coverage/core/api/services/users/users.hooks.js.html +1 -1
  123. package/coverage/core/api/services/users/users.service.js.html +1 -1
  124. package/coverage/core/common/errors.js.html +1 -1
  125. package/coverage/core/common/index.html +1 -1
  126. package/coverage/core/common/index.js.html +1 -1
  127. package/coverage/core/common/permissions.js.html +1 -1
  128. package/coverage/core/common/schema.js.html +1 -1
  129. package/coverage/core/common/utils.js.html +1 -1
  130. package/coverage/core/common/utils.offline.js.html +1 -1
  131. package/coverage/index.html +17 -17
  132. package/coverage/lcov-report/core/api/application.js.html +1 -1
  133. package/coverage/lcov-report/core/api/authentication.js.html +1 -1
  134. package/coverage/lcov-report/core/api/db.js.html +1 -1
  135. package/coverage/lcov-report/core/api/hooks/hooks.authentication.js.html +1 -1
  136. package/coverage/lcov-report/core/api/hooks/hooks.authorisations.js.html +1 -1
  137. package/coverage/lcov-report/core/api/hooks/hooks.logger.js.html +1 -1
  138. package/coverage/lcov-report/core/api/hooks/hooks.model.js.html +1 -1
  139. package/coverage/lcov-report/core/api/hooks/hooks.push.js.html +22 -10
  140. package/coverage/lcov-report/core/api/hooks/hooks.query.js.html +33 -6
  141. package/coverage/lcov-report/core/api/hooks/hooks.schemas.js.html +1 -1
  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.tags.js.html +1 -1
  145. package/coverage/lcov-report/core/api/hooks/hooks.users.js.html +4 -4
  146. package/coverage/lcov-report/core/api/hooks/index.html +23 -23
  147. package/coverage/lcov-report/core/api/hooks/index.js.html +1 -1
  148. package/coverage/lcov-report/core/api/index.html +1 -1
  149. package/coverage/lcov-report/core/api/index.js.html +1 -1
  150. package/coverage/lcov-report/core/api/marshall.js.html +1 -1
  151. package/coverage/lcov-report/core/api/models/configurations.model.mongodb.js.html +1 -1
  152. package/coverage/lcov-report/core/api/models/index.html +1 -1
  153. package/coverage/lcov-report/core/api/models/messages.model.mongodb.js.html +1 -1
  154. package/coverage/lcov-report/core/api/models/tags.model.mongodb.js.html +1 -1
  155. package/coverage/lcov-report/core/api/models/users.model.mongodb.js.html +1 -1
  156. package/coverage/lcov-report/core/api/services/account/account.hooks.js.html +1 -1
  157. package/coverage/lcov-report/core/api/services/account/account.service.js.html +1 -1
  158. package/coverage/lcov-report/core/api/services/account/index.html +1 -1
  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 +1 -1
  161. package/coverage/lcov-report/core/api/services/authorisations/index.html +1 -1
  162. package/coverage/lcov-report/core/api/services/configurations/configurations.hooks.js.html +1 -1
  163. package/coverage/lcov-report/core/api/services/configurations/index.html +1 -1
  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 +1 -1
  168. package/coverage/lcov-report/core/api/services/import-export/import-export.service.js.html +1 -1
  169. package/coverage/lcov-report/core/api/services/import-export/index.html +1 -1
  170. package/coverage/lcov-report/core/api/services/index.html +1 -1
  171. package/coverage/lcov-report/core/api/services/index.js.html +1 -1
  172. package/coverage/lcov-report/core/api/services/mailer/index.html +1 -1
  173. package/coverage/lcov-report/core/api/services/mailer/mailer.hooks.js.html +1 -1
  174. package/coverage/lcov-report/core/api/services/mailer/mailer.service.js.html +1 -1
  175. package/coverage/lcov-report/core/api/services/messages/index.html +1 -1
  176. package/coverage/lcov-report/core/api/services/messages/messages.hooks.js.html +1 -1
  177. package/coverage/lcov-report/core/api/services/push/index.html +1 -1
  178. package/coverage/lcov-report/core/api/services/push/push.hooks.js.html +1 -1
  179. package/coverage/lcov-report/core/api/services/push/push.service.js.html +1 -1
  180. package/coverage/lcov-report/core/api/services/storage/index.html +1 -1
  181. package/coverage/lcov-report/core/api/services/storage/storage.hooks.js.html +1 -1
  182. package/coverage/lcov-report/core/api/services/storage/storage.service.js.html +1 -1
  183. package/coverage/lcov-report/core/api/services/tags/index.html +1 -1
  184. package/coverage/lcov-report/core/api/services/tags/tags.hooks.js.html +1 -1
  185. package/coverage/lcov-report/core/api/services/users/index.html +1 -1
  186. package/coverage/lcov-report/core/api/services/users/users.hooks.js.html +1 -1
  187. package/coverage/lcov-report/core/api/services/users/users.service.js.html +1 -1
  188. package/coverage/lcov-report/core/common/errors.js.html +1 -1
  189. package/coverage/lcov-report/core/common/index.html +1 -1
  190. package/coverage/lcov-report/core/common/index.js.html +1 -1
  191. package/coverage/lcov-report/core/common/permissions.js.html +1 -1
  192. package/coverage/lcov-report/core/common/schema.js.html +1 -1
  193. package/coverage/lcov-report/core/common/utils.js.html +1 -1
  194. package/coverage/lcov-report/core/common/utils.offline.js.html +1 -1
  195. package/coverage/lcov-report/index.html +17 -17
  196. package/coverage/lcov-report/map/api/hooks/hooks.catalog.js.html +1 -1
  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 +184 -4
  199. package/coverage/lcov-report/map/api/hooks/index.html +5 -5
  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 +1 -1
  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 +1 -1
  208. package/coverage/lcov-report/map/api/models/projects.model.mongodb.js.html +1 -1
  209. package/coverage/lcov-report/map/api/models/styles.model.mongodb.js.html +1 -1
  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 +1 -1
  214. package/coverage/lcov-report/map/api/services/catalog/index.html +1 -1
  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 +1 -1
  218. package/coverage/lcov-report/map/api/services/features/features.service.js.html +1 -1
  219. package/coverage/lcov-report/map/api/services/features/index.html +1 -1
  220. package/coverage/lcov-report/map/api/services/index.html +1 -1
  221. package/coverage/lcov-report/map/api/services/index.js.html +1 -1
  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/lcov-report/map/api/services/styles/index.html +1 -1
  225. package/coverage/lcov-report/map/api/services/styles/styles.hooks.js.html +1 -1
  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 +1 -1
  229. package/coverage/lcov-report/map/common/grid.js.html +1 -1
  230. package/coverage/lcov-report/map/common/index.html +1 -1
  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 +1 -1
  236. package/coverage/lcov-report/map/common/permissions.js.html +1 -1
  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 +1 -1
  239. package/coverage/lcov-report/map/common/wcs-grid-source.js.html +1 -1
  240. package/coverage/lcov-report/map/common/wcs-utils.js.html +1 -1
  241. package/coverage/lcov-report/map/common/weacast-grid-source.js.html +1 -1
  242. package/coverage/lcov-report/map/common/wfs-utils.js.html +1 -1
  243. package/coverage/lcov-report/map/common/wms-utils.js.html +1 -1
  244. package/coverage/lcov-report/map/common/wmts-utils.js.html +1 -1
  245. package/coverage/lcov.info +358 -280
  246. package/coverage/map/api/hooks/hooks.catalog.js.html +1 -1
  247. package/coverage/map/api/hooks/hooks.features.js.html +1 -1
  248. package/coverage/map/api/hooks/hooks.query.js.html +184 -4
  249. package/coverage/map/api/hooks/index.html +5 -5
  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 +1 -1
  256. package/coverage/map/api/models/features.model.mongodb.js.html +1 -1
  257. package/coverage/map/api/models/index.html +1 -1
  258. package/coverage/map/api/models/projects.model.mongodb.js.html +1 -1
  259. package/coverage/map/api/models/styles.model.mongodb.js.html +1 -1
  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 +1 -1
  264. package/coverage/map/api/services/catalog/index.html +1 -1
  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 +1 -1
  268. package/coverage/map/api/services/features/features.service.js.html +1 -1
  269. package/coverage/map/api/services/features/index.html +1 -1
  270. package/coverage/map/api/services/index.html +1 -1
  271. package/coverage/map/api/services/index.js.html +1 -1
  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/map/api/services/styles/index.html +1 -1
  275. package/coverage/map/api/services/styles/styles.hooks.js.html +1 -1
  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 +1 -1
  279. package/coverage/map/common/grid.js.html +1 -1
  280. package/coverage/map/common/index.html +1 -1
  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 +1 -1
  286. package/coverage/map/common/permissions.js.html +1 -1
  287. package/coverage/map/common/time-based-grid-source.js.html +1 -1
  288. package/coverage/map/common/tms-utils.js.html +1 -1
  289. package/coverage/map/common/wcs-grid-source.js.html +1 -1
  290. package/coverage/map/common/wcs-utils.js.html +1 -1
  291. package/coverage/map/common/weacast-grid-source.js.html +1 -1
  292. package/coverage/map/common/wfs-utils.js.html +1 -1
  293. package/coverage/map/common/wms-utils.js.html +1 -1
  294. package/coverage/map/common/wmts-utils.js.html +1 -1
  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-222566-1765963609278-0.json → coverage-1028556-1773134124353-0.json} +1 -1
  300. package/extras/configs/widgets.top.js +3 -3
  301. package/extras/tests/core/collection.mjs +2 -9
  302. package/map/api/hooks/hooks.catalog.js +18 -4
  303. package/map/api/services/catalog/catalog.hooks.js +3 -0
  304. package/map/api/services/features/features.hooks.js +3 -1
  305. package/map/api/services/index.js +2 -6
  306. package/map/api/services/styles/styles.hooks.js +6 -1
  307. package/map/client/components/KFeatureActionButton.vue +9 -3
  308. package/map/client/components/KFeaturesFilterManager.vue +5 -5
  309. package/map/client/components/KFilterCondition.vue +17 -10
  310. package/map/client/components/KLayerEditor.vue +49 -39
  311. package/map/client/components/KMeasureTool.vue +7 -1
  312. package/map/client/components/KTimezoneMap.vue +29 -9
  313. package/map/client/components/catalog/KLayersPanel.vue +26 -16
  314. package/map/client/components/catalog/KLayersSelector.vue +13 -2
  315. package/map/client/components/catalog/KViewsPanel.vue +5 -4
  316. package/map/client/components/form/KSelectLayersField.vue +28 -17
  317. package/map/client/components/form/KSelectViewsField.vue +18 -9
  318. package/map/client/components/form/KTimezoneField.vue +1 -2
  319. package/map/client/components/legend/KVariablesLegend.vue +10 -1
  320. package/map/client/components/location/KLocationCardSection.vue +7 -2
  321. package/map/client/components/location/KLocationMap.vue +31 -7
  322. package/map/client/components/selection/KSelectedLayerFeatures.vue +2 -2
  323. package/map/client/components/stickies/KZoomControl.vue +1 -1
  324. package/map/client/components/styles/KStyleManager.vue +4 -1
  325. package/map/client/components/widget/KTimeSeries.vue +174 -497
  326. package/map/client/components/widget/KTimeSeriesSelector.vue +72 -0
  327. package/map/client/components/widget/KTimeSeriesToolbar.vue +83 -0
  328. package/map/client/composables/catalog.js +6 -10
  329. package/map/client/composables/highlight.js +12 -9
  330. package/map/client/composables/project.js +1 -1
  331. package/map/client/composables/selection.js +8 -7
  332. package/map/client/composables/weather.js +9 -2
  333. package/map/client/geolocation.js +8 -5
  334. package/map/client/i18n/map_en.json +10 -8
  335. package/map/client/i18n/map_fr.json +9 -7
  336. package/map/client/leaflet/TiledFeatureLayer.js +85 -82
  337. package/map/client/leaflet/utils/utils.geojson.js +3 -3
  338. package/map/client/mixins/globe/mixin.base-globe.js +15 -6
  339. package/map/client/mixins/globe/mixin.geojson-layers.js +27 -18
  340. package/map/client/mixins/map/mixin.edit-layers.js +9 -1
  341. package/map/client/mixins/map/mixin.pmtiles-layers.js +118 -29
  342. package/map/client/mixins/map/mixin.tiled-mesh-layers.js +12 -5
  343. package/map/client/mixins/map/mixin.tiled-wind-layers.js +19 -10
  344. package/map/client/mixins/mixin.activity.js +23 -30
  345. package/map/client/mixins/mixin.feature-selection.js +41 -5
  346. package/map/client/planets.js +1 -1
  347. package/map/client/readers/reader.kml.js +2 -3
  348. package/map/client/utils/utils.catalog.js +36 -10
  349. package/map/client/utils/utils.layers.js +39 -8
  350. package/map/client/utils/utils.project.js +4 -0
  351. package/map/client/utils/utils.style.js +37 -7
  352. package/map/client/utils/utils.time-series.js +215 -6
  353. package/map/common/schemas/catalog.update.json +1 -1
  354. package/map/common/weacast-grid-source.js +1 -1
  355. package/package.json +3 -3
  356. package/scripts/kash/CHANGELOG.md +0 -4
  357. package/scripts/kash/README.md +0 -9
  358. package/scripts/kash/kash.sh +45 -40
  359. package/scripts/kash/scripts/run_tests.sh +1 -4
  360. package/test/api/core/authentication.test.js +9 -4
  361. package/test/api/core/config/default.cjs +1 -0
  362. package/test/api/core/hooks.test.js +6 -0
  363. package/test/api/core/index.test.js +43 -18
  364. package/test/api/core/push.test.js +8 -8
  365. package/test/api/core/test-log-2026-03-10.log +60 -0
  366. package/test/api/core/users.test.js +384 -0
  367. package/test/api/map/grid-sources.test.js +1 -1
  368. package/test/api/map/test-log-2026-03-10.log +56 -0
  369. package/vite/package.json +11 -2
  370. package/vite/test/core/composables.test.js +77 -0
  371. package/vite/vitest.config.js +13 -0
  372. package/vite/yarn.lock +1096 -18
  373. package/client/css/core.variables.scss +0 -72
  374. package/client/i18n/core_en.json +0 -744
  375. package/client/i18n/core_fr.json +0 -744
  376. package/client/i18n/map_en.json +0 -800
  377. package/client/i18n/map_fr.json +0 -800
  378. package/client/kdk.client.css +0 -47
  379. package/client/kdk.client.js +0 -41097
  380. package/client/kdk.client.map.css +0 -47
  381. package/client/kdk.client.map.js +0 -38182
  382. package/client/kdk.client.map.min.css +0 -1
  383. package/client/kdk.client.map.min.js +0 -27032
  384. package/client/kdk.client.min.css +0 -1
  385. package/client/kdk.client.min.js +0 -29074
  386. package/client/schemas/capture.create.json +0 -132
  387. package/client/schemas/catalog.update.json +0 -44
  388. package/client/schemas/messages.update.json +0 -16
  389. package/client/schemas/projects.create.json +0 -52
  390. package/client/schemas/projects.update.json +0 -52
  391. package/client/schemas/settings.update.json +0 -286
  392. package/client/schemas/tags.update.json +0 -35
  393. package/client/schemas/users.update-profile.json +0 -34
  394. package/core/client/components/account/KAccount.vue +0 -68
  395. package/core/client/components/account/KDeleteAccountManager.vue +0 -62
  396. package/core/client/components/account/KEmailManager.vue +0 -128
  397. package/core/client/components/account/KPasswordManager.vue +0 -90
  398. package/core/client/components/account/KVerifyEmailManager.vue +0 -105
  399. package/core/client/components/collection/KColumn.vue +0 -227
  400. package/core/client/components/collection/KHistory.vue +0 -113
  401. package/core/client/components/collection/KHistoryEntry.vue +0 -109
  402. package/coverage/tmp/coverage-222524-1765963609350-0.json +0 -1
  403. package/coverage/tmp/coverage-222536-1765963609335-0.json +0 -1
  404. package/coverage/tmp/coverage-222547-1765963609324-0.json +0 -1
  405. package/coverage/tmp/coverage-222559-1765963609309-0.json +0 -1
  406. package/scripts/kash/LICENSE +0 -21
  407. package/test/api/core/test-log-2025-07-31.log +0 -15
  408. package/test/api/core/test-log-2025-10-03.log +0 -18
  409. package/test/api/core/test-log-2025-11-10.log +0 -0
  410. package/test/api/core/test-log-2025-11-12.log +0 -117
  411. package/test/api/core/test-log-2025-11-27.log +0 -0
  412. package/test/api/core/test-log-2025-11-28.log +0 -17
  413. package/test/api/core/test-log-2025-12-09.log +0 -148
  414. package/test/api/core/test-log-2025-12-17.log +0 -58
  415. package/test/api/core/test-log-2026-01-29.log +0 -17
  416. package/test/api/map/test-log-2025-07-23.log +0 -1
  417. package/test/api/map/test-log-2025-11-28.log +0 -33
  418. package/test/api/map/test-log-2025-12-10.log +0 -2
  419. package/test/api/map/test-log-2026-01-06.log +0 -26
@@ -1,15 +1,17 @@
1
+ import _ from 'lodash'
2
+ import { ForecastProbeId, updateTimeSeries } from '../utils/utils.time-series.js'
3
+ import { Layout } from '../../../core/client/layout.js'
4
+
1
5
  export const featureSelection = {
2
6
  watch: {
3
7
  'selection.items': {
4
8
  handler () {
5
- this.updateHighlights()
6
- this.handleWidget(this.getWidgetForSelection())
9
+ this.updateSelection()
7
10
  }
8
11
  },
9
12
  'probe.item': {
10
13
  handler () {
11
- this.updateHighlights()
12
- this.handleWidget(this.getWidgetForProbe())
14
+ this.updateSelection()
13
15
  }
14
16
  }
15
17
  },
@@ -19,11 +21,45 @@ export const featureSelection = {
19
21
  this.getSelectedItems().forEach(item => {
20
22
  this.highlight(item.feature || item.location, item.layer)
21
23
  })
22
- if (this.hasProbedLocation()) this.highlight(this.getProbedLocation(), this.getProbedLayer())
24
+ },
25
+ async updateProbedLocationHighlight () {
26
+ if (this.hasProbedLocation()) {
27
+ this.highlight(this.getProbedLocation(), this.getProbedLayer() || { name: ForecastProbeId })
28
+ }
29
+ },
30
+ async updateSelection () {
31
+ this.updateHighlights()
32
+ await this.updateTimeSeries()
33
+ // As probed location depends on time series do this after
34
+ await this.updateProbedLocationHighlight()
35
+ if (this.hasProbedLocation() || this.hasSelectedItems()) {
36
+ this.handleWidget(this.getWidgetForProbe() || this.getWidgetForSelection())
37
+ }
38
+ },
39
+ async updateTimeSeries () {
40
+ if (!this.state) return
41
+ const timeSeries = await updateTimeSeries(this.state.timeSeries || [])
42
+ this.state.timeSeries = timeSeries
23
43
  },
24
44
  handleWidget (widget) {
25
45
  // If window already open on another widget keep it
26
46
  if (widget && (widget !== 'none') && !this.isWidgetWindowVisible(widget)) this.openWidget(widget)
27
47
  }
48
+ },
49
+ mounted () {
50
+ this.$engineEvents.on('forecast-model-changed', this.updateSelection)
51
+ this.$engineEvents.on('selected-level-changed', this.updateSelection)
52
+ // We use debounce here to avoid multiple refresh when editing settings for instance
53
+ this.requestTimeSeriesUpdate = _.debounce(() => this.updateTimeSeries(), 250)
54
+ this.$events.on('timeseries-group-by-changed', this.requestTimeSeriesUpdate)
55
+ this.$events.on('units-changed', this.requestTimeSeriesUpdate)
56
+ this.$events.on('time-current-time-changed', this.updateProbedLocationHighlight)
57
+ },
58
+ beforeUnmount () {
59
+ this.$engineEvents.off('forecast-model-changed', this.updateSelection)
60
+ this.$engineEvents.off('selected-level-changed', this.updateSelection)
61
+ this.$events.off('timeseries-group-by-changed', this.updateTimeSeries)
62
+ this.$events.off('units-changed', this.requestTimeSeriesUpdate)
63
+ this.$events.off('time-current-time-changed', this.requestTimeSeriesUpdate)
28
64
  }
29
65
  }
@@ -24,7 +24,7 @@ export const Planets = {
24
24
 
25
25
  const client = await createClient(options)
26
26
  setupApi.bind(client)(options)
27
- client.on('authenticated', (data) => {
27
+ client.on('login', (data) => {
28
28
  // Store API gateway token if any
29
29
  if (data.gatewayToken) client.get('storage').setItem(options.gatewayJwt, data.gatewayToken)
30
30
  })
@@ -2,7 +2,7 @@ import logger from 'loglevel'
2
2
  import _ from 'lodash'
3
3
  import { kml } from '@tmcw/togeojson'
4
4
  import { i18n } from '../../../core/client/i18n.js'
5
- import { convertSimpleStyleToPointStyle, convertSimpleStyleToLineStyle, convertSimpleStyleToPolygonStyle } from '../utils/utils.style.js'
5
+ import { convertSimpleStyleToPointStyle, convertSimpleStyleToLineStyle, convertSimpleStyleToPolygonStyle, kmlStyleSpecialProperties } from '../utils/utils.style.js'
6
6
 
7
7
  export const KMLReader = {
8
8
  read (files, options) {
@@ -39,13 +39,12 @@ export const KMLReader = {
39
39
 
40
40
  function getExtraPropertiesFromKMLByName (document) {
41
41
  const properties = {}
42
- const propertiesToAdd = ['extrude', 'altitudeMode']
43
42
  const placemarks = document.getElementsByTagName('Placemark')
44
43
  _.forEach(placemarks, placemark => {
45
44
  const nameElements = placemark.getElementsByTagName('name')
46
45
  if (!nameElements.length) return
47
46
  const name = nameElements[0].textContent
48
- _.forEach(propertiesToAdd, property => {
47
+ _.forEach(kmlStyleSpecialProperties, property => {
49
48
  const propertyElements = placemark.getElementsByTagName(property)
50
49
  if (!propertyElements.length) return
51
50
 
@@ -2,6 +2,7 @@ import _ from 'lodash'
2
2
  import sift from 'sift'
3
3
  import { api, i18n, Store } from '../../../core/client/index.js'
4
4
  import { buildUrl } from '../../../core/common/index.js'
5
+ import { getCatalogProjectQuery, getViewsProjectQuery } from './utils.project.js'
5
6
 
6
7
  // Helper to set a JWT as query param in a target URL
7
8
  export function setUrlJwt (item, path, baseUrl, jwtField, jwt) {
@@ -104,17 +105,19 @@ export async function getLayers (options = {}) {
104
105
  context: '',
105
106
  planetApi: api
106
107
  })
107
-
108
+ // We expect the project object to expose the underlying API
109
+ const planetApi = (options.project && typeof options.project.getPlanetApi === 'function' ? options.project.getPlanetApi() : options.planetApi)
110
+
108
111
  let layers = []
109
- const catalogService = options.planetApi.getService('catalog', options.context)
112
+ const catalogService = planetApi.getService('catalog', options.context)
110
113
  if (catalogService) {
111
- const response = await catalogService.find({ query: options.query })
114
+ const response = await catalogService.find({ query: (options.project ? Object.assign(getCatalogProjectQuery(options.project), options.query) : options.query) })
112
115
  _.forEach(response.data, processTranslations)
113
116
  // Set which API to use to retrieve layer data
114
- layers = layers.concat(response.data.map(layer => Object.assign(layer, { getPlanetApi: () => options.planetApi })))
117
+ layers = layers.concat(response.data.map(layer => Object.assign(layer, { getPlanetApi: () => planetApi })))
115
118
  }
116
119
  // Do we need to inject a token ?
117
- await setEngineJwt(layers, options.planetApi)
120
+ await setEngineJwt(layers, planetApi)
118
121
  return layers
119
122
  }
120
123
 
@@ -124,9 +127,11 @@ export async function getCategories (options = {}) {
124
127
  context: '',
125
128
  planetApi: api
126
129
  })
130
+ // We expect the project object to expose the underlying API
131
+ const planetApi = (options.project && typeof options.project.getPlanetApi === 'function' ? options.project.getPlanetApi() : options.planetApi)
127
132
 
128
133
  let categories = []
129
- const catalogService = options.planetApi.getService('catalog', options.context)
134
+ const catalogService = planetApi.getService('catalog', options.context)
130
135
  if (catalogService) {
131
136
  const response = await catalogService.find({ query: Object.assign({ type: 'Category' }, options.query) })
132
137
  _.forEach(response.data, processTranslations)
@@ -141,9 +146,11 @@ export async function getSublegends (options = {}) {
141
146
  context: '',
142
147
  planetApi: api
143
148
  })
149
+ // We expect the project object to expose the underlying API
150
+ const planetApi = (options.project && typeof options.project.getPlanetApi === 'function' ? options.project.getPlanetApi() : options.planetApi)
144
151
 
145
152
  let sublegends = []
146
- const catalogService = options.planetApi.getService('catalog', options.context)
153
+ const catalogService = planetApi.getService('catalog', options.context)
147
154
  if (catalogService) {
148
155
  const response = await catalogService.find({ query: Object.assign({ type: 'Sublegend' }, options.query) })
149
156
  _.forEach(response.data, processTranslations)
@@ -173,13 +180,32 @@ export async function getViews (options = {}) {
173
180
  context: '',
174
181
  planetApi: api
175
182
  })
176
-
183
+ // We expect the project object to expose the underlying API
184
+ const planetApi = (options.project && typeof options.project.getPlanetApi === 'function' ? options.project.getPlanetApi() : options.planetApi)
185
+
177
186
  let views = []
178
- const catalogService = options.planetApi.getService('catalog', options.context)
187
+ const catalogService = planetApi.getService('catalog', options.context)
179
188
  if (catalogService) {
180
- const response = await catalogService.find({ query: Object.assign({ type: 'Context' }, options.query) })
189
+ const response = await catalogService.find({
190
+ query: Object.assign({ type: 'Context' }, (options.project ? Object.assign(getViewsProjectQuery(options.project), options.query) : options.query))
191
+ })
181
192
  _.forEach(response.data, processTranslations)
182
193
  views = views.concat(response.data)
183
194
  }
184
195
  return views
185
196
  }
197
+
198
+ // Order is given as an array of catalog item IDs or names
199
+ export function orderCatalogItemsBy(items, itemsOrder) {
200
+ if (!_.isEmpty(itemsOrder)) {
201
+ for (let i = itemsOrder.length; i >= 0; i--) {
202
+ const itemIdOrName = itemsOrder[i]
203
+ // Move item to beginning of array
204
+ const itemIndex = items.findIndex(item => (item?._id === itemIdOrName) || (item?.name === itemIdOrName))
205
+ if (itemIndex >= 0) {
206
+ const removedItems = items.splice(itemIndex, 1)
207
+ if (removedItems.length > 0) items.unshift(removedItems[0])
208
+ }
209
+ }
210
+ }
211
+ }
@@ -1,5 +1,6 @@
1
1
  import _ from 'lodash'
2
2
  import logger from 'loglevel'
3
+ import sift from 'sift'
3
4
  import { Notify, Loading, uid } from 'quasar'
4
5
  import explode from '@turf/explode'
5
6
  import SphericalMercator from '@mapbox/sphericalmercator'
@@ -366,8 +367,8 @@ async function generateStyleFromFilters (layer, defaultStyle) {
366
367
  const templates = generateStyleTemplates(defaultStyle, filters)
367
368
  const result = Object.assign(
368
369
  {},
369
- _.mapKeys(templates, (value, key) => `leaflet.${key}`),
370
- _.mapKeys(templates, (value, key) => `cesium.${key}`)
370
+ _.has(layer, 'leaflet') ? _.mapKeys(templates, (value, key) => `leaflet.${key}`) : {},
371
+ _.has(layer, 'cesium') ? _.mapKeys(templates, (value, key) => `cesium.${key}`) : {}
371
372
  )
372
373
  return result
373
374
  }
@@ -384,13 +385,18 @@ export async function editLayerStyle (layer, style, ignoreFeatureStyle = false)
384
385
  await api.getService('catalog').patch(layer._id, result)
385
386
  } else {
386
387
  const legend = await getLegendForLayer(Object.assign({}, layer, { 'cesium.style': style, 'leaflet.style': style }))
387
- const patch = Object.assign({}, { 'cesium.style': style, 'leaflet.style': style }, legend)
388
+ const patch = Object.assign(
389
+ {},
390
+ _.has(layer, 'cesium') ? { 'cesium.style': style } : {},
391
+ _.has(layer, 'leaflet') ? { 'leaflet.style': style } : {},
392
+ legend
393
+ )
388
394
  if (ignoreFeatureStyle) patch.ignoreFeatureStyle = true
389
395
  await api.getService('catalog').patch(layer._id, patch)
390
396
  }
391
397
  } else {
392
- _.set(layer, 'cesium.style', style)
393
- _.set(layer, 'leaflet.style', style)
398
+ if (_.has(layer, 'cesium')) _.set(layer, 'cesium.style', style)
399
+ if (_.has(layer, 'leaflet')) _.set(layer, 'leaflet.style', style)
394
400
  Object.assign(layer, await getLegendForLayer(layer))
395
401
  if (ignoreFeatureStyle) layer.ignoreFeatureStyle = true
396
402
  }
@@ -399,7 +405,7 @@ export async function editLayerStyle (layer, style, ignoreFeatureStyle = false)
399
405
 
400
406
  export async function updateLayerWithFiltersStyle (layer) {
401
407
  if (!layer._id) return
402
- const defaultStyle = getDefaultStyleFromTemplates(_.get(layer, 'leaflet.style', {}))
408
+ const defaultStyle = getDefaultStyleFromTemplates(_.get(layer, 'leaflet.style', _.get(layer, 'cesium.style', {})))
403
409
  const style = await generateStyleFromFilters(layer, defaultStyle)
404
410
  if (!style) return
405
411
 
@@ -408,7 +414,7 @@ export async function updateLayerWithFiltersStyle (layer) {
408
414
 
409
415
  export async function editFilterStyle (layer, filter, engineStyle, style, ignoreFeatureStyle = false) {
410
416
  if (!layer._id) return
411
- const layerDefaultStyle = getDefaultStyleFromTemplates(_.get(layer, 'leaflet.style', {}))
417
+ const layerDefaultStyle = getDefaultStyleFromTemplates(_.get(layer, 'leaflet.style', _.get(layer, 'cesium.style', {})))
412
418
 
413
419
  const filters = await parseFiltersFromLayer(layer)
414
420
  const targetFilterCondition = filterQueryToConditions(filter.active)
@@ -499,7 +505,7 @@ export async function getLegendForLayer (layer) {
499
505
  if (!hasFilterWithStyle) return { $unset: { legend: '' } }
500
506
  return legend
501
507
  } else {
502
- const layerStyle = getDefaultStyleFromTemplates(_.get(layer, 'leaflet.style', {}))
508
+ const layerStyle = getDefaultStyleFromTemplates(_.get(layer, 'leaflet.style', _.get(layer, 'cesium.style', {})))
503
509
  const legend = generateLegendFromStyle(layer, layerStyle, _.get(layer, 'geometryTypes', []))
504
510
  legend.label = _.get(layer, 'label', _.get(layer, 'name'))
505
511
  return { legend }
@@ -680,3 +686,28 @@ export async function removeLayer (layer) {
680
686
  Loading.hide()
681
687
  return true
682
688
  }
689
+
690
+ export function getFilterFunctionFromLayerFilters (layer) {
691
+ // To be flexible enough filters can provide a query for their active and inactive state
692
+ // Similarly, filters can be combined with a different operator for each state
693
+ const filterOperators = _.get(layer, 'filterOperators', { active: '$or', inactive: '$and' })
694
+ const activeFilters = layer.filters
695
+ .filter(filter => filter.isActive)
696
+ .map(filter => filter.active)
697
+ .filter(filter => !_.isEmpty(filter))
698
+ const activeCondition = activeFilters.length > 1
699
+ ? { [filterOperators.active]: activeFilters }
700
+ : activeFilters.length === 1 ? activeFilters[0] : {}
701
+ const inactiveFilters = layer.filters
702
+ .filter(filter => !filter.isActive)
703
+ .map(filter => filter.inactive)
704
+ .filter(filter => !_.isEmpty(filter))
705
+ const inactiveCondition = inactiveFilters.length > 1
706
+ ? { [filterOperators.inactive]: inactiveFilters }
707
+ : inactiveFilters.length === 1 ? inactiveFilters[0] : {}
708
+
709
+ const finalCondition = activeFilters.length && inactiveFilters.length
710
+ ? { '$and': [ activeCondition, inactiveCondition ] } : activeFilters.length ? activeCondition : inactiveCondition
711
+
712
+ return sift(finalCondition)
713
+ }
@@ -6,3 +6,7 @@ export function getCatalogProjectQuery (project) {
6
6
  const nameQuery = { name: { $in: _.map(_.filter(project.layers, 'name'), 'name') } }
7
7
  return { $or: [idQuery, nameQuery] }
8
8
  }
9
+
10
+ export function getViewsProjectQuery (project) {
11
+ return { _id: { $in: _.map(project.views, '_id') } }
12
+ }
@@ -182,6 +182,8 @@ export const DefaultStyle = {
182
182
  }
183
183
  }
184
184
 
185
+ export const kmlStyleSpecialProperties = ['extrude', 'altitudeMode']
186
+
185
187
  // Map properties of a given style according to given mapping, performing number conversion if required
186
188
  export function convertStyle (style, mapping, asNumber = []) {
187
189
  let convertedStyle = {}
@@ -328,12 +330,30 @@ export function generateStyleTemplates (defaultStyle, styles, dotify = true) {
328
330
 
329
331
  // Process all properties, for each property
330
332
  properties.forEach((property, index) => {
331
- if (!_.has(style.values, property)) return
332
- // Conversion from palette to RGB color is required
333
- const value = (property.includes('color')
334
- ? kdkCoreUtils.getHtmlColor(_.get(style.values, property))
335
- : _.get(style.values, property))
333
+ // Don't need to create template if the section (point, line, polygon) is not defined in the style
334
+ if (!_.get(style.values, property.split('.')[0])) return
335
+
336
+ let value = null
337
+ // If no value is defined for the given property, that means this property is in a optional sub-object (e.g. stroke, icon)
338
+ // So we need to hide the element corresponding to this sub-object by setting all of its properties to null or default values
339
+ // We must create the condition anyway to prevent default style from being applied on this case
340
+ if (!_.get(style.values, property)) {
341
+ if (_.isNumber(_.get(DefaultStyle, property))) {
342
+ value = 0
343
+ } else if (property.includes('shape')) {
344
+ value = _.get(DefaultStyle, property)
345
+ } else if (property.includes('color')) {
346
+ value = kdkCoreUtils.getHtmlColor(_.get(defaultStyle, property))
347
+ }
348
+ } else {
349
+ // Conversion from palette to RGB color is required
350
+ value = (property.includes('color')
351
+ ? kdkCoreUtils.getHtmlColor(_.get(style.values, property))
352
+ : _.get(style.values, property))
353
+ }
354
+
336
355
  // Generate style value for given property value
356
+ if (_.isNil(value)) return
337
357
  templates[index] += `if (${predicate}) { %>${value}<% } else `
338
358
  })
339
359
  })
@@ -346,18 +366,28 @@ export function generateStyleTemplates (defaultStyle, styles, dotify = true) {
346
366
  const value = (property.includes('color')
347
367
  ? kdkCoreUtils.getHtmlColor(_.get(defaultStyle, property))
348
368
  : _.get(defaultStyle, property))
369
+ if (_.isNil(value)) return
349
370
  // Avoid converting numbers to string on default values
350
371
  if (hasStyles) templates[index] += `{ %>${value}<% }`
351
372
  else templates[index] = value
352
373
  })
353
374
  // Set all templates
375
+ options.template = []
354
376
  properties.forEach((property, index) => {
355
- if (!_.has(defaultStyle, property)) return
377
+ if (!_.has(defaultStyle, property) || _.isEmpty(templates[index])) return
356
378
  // We voluntary use dot notation here by default as this object should be used to update style values using a patch operation
357
379
  if (dotify) options[`style.${property}`] = (hasStyles ? `<% ${templates[index]} %>` : templates[index])
358
380
  else _.set(options, `style.${property}`, (hasStyles ? `<% ${templates[index]} %>` : templates[index]))
381
+
382
+ // Check if templating is needed
383
+ if (options[`style.${property}`].startsWith('<% if')) {
384
+ options.template.push(`style.${property}`)
385
+ // Remove duplicates "<% { %>" that are added to default style (else statement) when filters are reapplied
386
+ options[`style.${property}`] = options[`style.${property}`].replace(/(<% { %>)+/g, '<% { %>').replace(/(<% } %>)+/g, '<% } %>')
387
+ } else {
388
+ options[`style.${property}`] = options[`style.${property}`].replace(/(<% { %>)+/g, '').replace(/(<% } %>)+/g, '')
389
+ }
359
390
  })
360
- options.template = (hasStyles ? properties : []).map(property => `style.${property}`)
361
391
  return options
362
392
  }
363
393
 
@@ -1,10 +1,68 @@
1
1
  import _ from 'lodash'
2
2
  import moment from 'moment'
3
+ import { unref } from 'vue'
4
+ import sift from 'sift'
5
+ import chroma from 'chroma-js'
3
6
  import centroid from '@turf/centroid'
4
- import { Time, Units, i18n } from '../../../core/client/index.js'
5
- import { isMeasureLayer } from './utils.layers.js'
7
+ import { Time, Store, Units, i18n } from '../../../core/client/index.js'
8
+ import { getFeatureId, getFeatureLabel } from './utils.js'
6
9
  import { getMeasureForFeature } from './utils.features.js'
10
+ import { formatUserCoordinates } from './utils.location.js'
11
+ import { isMeasureLayer } from './utils.layers.js'
7
12
  import { getForecastForLocation, getForecastProbe, getForecastForFeature } from './utils.weacast.js'
13
+ import { useCurrentActivity } from '../composables/activity.js'
14
+
15
+ // When organizing time series by feature the dataset color is the variable color as given in the layer
16
+ // eg 'temperature' data in red and 'humidity' data in blue
17
+ // When organizing time series by variable the dataset color should be different for each feature
18
+ // eg 'Toulouse' data in red, 'Paris' data in blue, etc.
19
+ // We pregenerate a fixed set of colors for this to ensure they are always assigned in the same order
20
+ const nbColors = 10
21
+ const Colors = chroma.scale('Set1').colors(nbColors)
22
+
23
+ // Add a small delta (minutes) to data time range so that some ticks are always visible
24
+ // and points on on the left/right side are not cut
25
+ const TimeRangeDelta = 2
26
+
27
+ // ID of weather forecast probe timeseries
28
+ export const ForecastProbeId = 'forecast-probe'
29
+
30
+ export function getChartOptions (title) {
31
+ return {
32
+ title: {
33
+ display: true,
34
+ text: title,
35
+ align: 'start'
36
+ },
37
+ scales: {
38
+ x: {
39
+ min: (startTime, endTime) => startTime.clone().subtract(TimeRangeDelta, 'minutes').valueOf(),
40
+ max: (startTime, endTime) => endTime.clone().add(TimeRangeDelta, 'minutes').valueOf()
41
+ }
42
+ },
43
+ plugins: {
44
+ legend: {
45
+ labels: {
46
+ usePointStyle: true,
47
+ generateLabels: (chart) => {
48
+ return chart.data.datasets.map((dataset, index) => {
49
+ // If we have a single value but like to draw a line it does not make sense so that we "force" point display
50
+ const hasSingleValue = (dataset.data.length === 1)
51
+ return {
52
+ text: dataset.label,
53
+ datasetIndex: index,
54
+ fillStyle: dataset.backgroundColor,
55
+ strokeStyle: dataset.borderColor,
56
+ pointStyle: (hasSingleValue ? 'rectRot' : 'line'),
57
+ hidden: !chart.isDatasetVisible(index)
58
+ }
59
+ })
60
+ }
61
+ }
62
+ }
63
+ }
64
+ }
65
+ }
8
66
 
9
67
  // Extract target variable data for timeseries from timeseries request result
10
68
  async function getDataForVariable(data, variable, forecastLevel, runTime) {
@@ -81,7 +139,7 @@ async function fetchDataForMeasureSeries({
81
139
  // Build timeseries to be used in charts for target feature and associated layer definition or probe location
82
140
  export function getForecastTimeSeries({
83
141
  feature, location, layer, startTime, endTime, runTime,
84
- forecastLayers, forecastModel, forecastLevel, weacastApi, fetchDelay
142
+ forecastLayers, forecastModel, forecastLevel, forecastLevelUnit, weacastApi, fetchDelay
85
143
  }) {
86
144
  let forecastVariables = []
87
145
  if (forecastLayers && forecastLayers.length > 0) forecastLayers.forEach(layer => { forecastVariables = forecastVariables.concat(_.get(layer, 'variables', [])) })
@@ -106,12 +164,16 @@ export function getForecastTimeSeries({
106
164
  // Known by the unit system ?
107
165
  const unit = Units.getUnit(baseUnit)
108
166
  const targetUnit = Units.getTargetUnit(baseUnit)
167
+ // We allow variable name to be customized based on level information
168
+ const label = _.template(i18n.tie(variable.label))({
169
+ level: forecastLevel, levelUnit: forecastLevelUnit
170
+ })
109
171
  const serie = {
110
172
  probedLocationData: forecastData,
111
173
  data: getDataForVariable(forecastData, variable, forecastLevel, runTime),
112
174
  variable: {
113
175
  name: variable.name,
114
- label: `${i18n.tie(variable.label)} (${Units.getTargetUnitSymbol(baseUnit)})`,
176
+ label: `${label} (${Units.getTargetUnitSymbol(baseUnit)})`,
115
177
  unit,
116
178
  targetUnit,
117
179
  chartjs: Object.assign({
@@ -138,7 +200,7 @@ export function getForecastTimeSeries({
138
200
  // Build timeseries to be used in charts for target feature and associated layer definition or probe location
139
201
  export function getMeasureTimeSeries({
140
202
  feature, location, layer, layers, startTime, endTime, runTime,
141
- level, probeFunction, fetchDelay
203
+ level, levelUnit, probeFunction, fetchDelay
142
204
  }) {
143
205
  // A feature comes from a single layer so target variables from it by default
144
206
  let variables = _.get(layer, 'variables', [])
@@ -165,12 +227,16 @@ export function getMeasureTimeSeries({
165
227
  // Known by the unit system ?
166
228
  const unit = Units.getUnit(baseUnit)
167
229
  const targetUnit = Units.getTargetUnit(baseUnit)
230
+ // We allow variable name to be customized based on level information
231
+ const label = _.template(i18n.tie(variable.label))({
232
+ level, levelUnit
233
+ })
168
234
  const serie = {
169
235
  probedLocationData: data,
170
236
  data: getDataForVariable(data, variable),
171
237
  variable: {
172
238
  name: variable.name,
173
- label: `${i18n.tie(variable.label)} (${Units.getTargetUnitSymbol(baseUnit)})`,
239
+ label: `${label} (${Units.getTargetUnitSymbol(baseUnit)})`,
174
240
  unit,
175
241
  targetUnit,
176
242
  chartjs: Object.assign({
@@ -193,3 +259,146 @@ export function getMeasureTimeSeries({
193
259
 
194
260
  return series
195
261
  }
262
+
263
+ // Helper function to update time series whenever something related changes, eg time span
264
+ export async function updateTimeSeries (previousTimeSeries) {
265
+ const { CurrentActivity, hasSelectedItems, getSelectedItems, hasProbedLocation, getProbedLocation } = useCurrentActivity()
266
+ const activity = unref(CurrentActivity)
267
+ if (!activity) return
268
+ // Initialize the time range
269
+ const span = Store.get('timeseries.span')
270
+ const start = moment(Time.getCurrentTime()).subtract(span, 'm')
271
+ const end = moment(Time.getCurrentTime()).add(span, 'm')
272
+ Time.patchRange({ start, end })
273
+ // Weather probe targets variables coming from multiple layers
274
+ const forecastLayers = _.values(activity.layers).filter(sift({ tags: ['weather', 'forecast'] }))
275
+ const featureLevel = activity.selectableLevelsLayer ? ` - ${activity.selectedLevel} ${activity.selectableLevels.unit}` : ''
276
+ const forecastLevel = activity.forecastLevel ? ` - ${activity.forecastLevel} ${activity.selectableLevels.unit}` : ''
277
+
278
+ let timeSeries = []
279
+ if (hasProbedLocation()) {
280
+ const coordinates = formatUserCoordinates(getProbedLocation().lat, getProbedLocation().lng, Store.get('locationFormat', 'FFf'))
281
+ // When custom probe function we provide visible layers as input
282
+ if (activity.probeLocation) {
283
+ let variableLayers = _.difference(_.values(activity.layers).filter(sift({ variables: { $exists: true }, isVisible: true })), forecastLayers)
284
+ variableLayers = variableLayers.filter(layer => activity.canProbeLocation({ location: getProbedLocation(), layer, level: activity.selectedLevel }))
285
+ variableLayers.forEach(layer => {
286
+ const series = getMeasureTimeSeries({
287
+ location: getProbedLocation(),
288
+ layer,
289
+ level: activity.selectedLevel,
290
+ levelUnit: (activity.selectableLevels ? activity.selectableLevels.unit : ''),
291
+ probeFunction: activity.probeLocation
292
+ })
293
+ if (!_.isEmpty(series)) {
294
+ timeSeries.push({
295
+ id: `${layer.name}-measure-probe`,
296
+ label: `${layer.label} (${coordinates})` + featureLevel,
297
+ series
298
+ })
299
+ }
300
+ })
301
+ }
302
+ // Or weather forecast probe
303
+ if (_.isEmpty(timeSeries) && activity.forecastModel) {
304
+ const series = getForecastTimeSeries({
305
+ location: getProbedLocation(),
306
+ forecastLayers,
307
+ forecastModel: activity.forecastModel,
308
+ forecastLevel: activity.forecastLevel,
309
+ forecastLevelUnit: (activity.selectableLevels ? activity.selectableLevels.unit : ''),
310
+ weacastApi: activity.getWeacastApi()
311
+ })
312
+ if (!_.isEmpty(series)) {
313
+ timeSeries.push({
314
+ id: ForecastProbeId,
315
+ label: `${activity.forecastModel.label} (${coordinates})` + forecastLevel,
316
+ series
317
+ })
318
+ }
319
+ }
320
+ }
321
+ if (hasSelectedItems()) {
322
+ getSelectedItems().forEach(item => {
323
+ const featureId = getFeatureId(item.feature, item.layer)
324
+ const featureLabel = getFeatureLabel(item.feature, item.layer)
325
+ // Measure
326
+ if (isMeasureLayer(item.layer)) {
327
+ const series = getMeasureTimeSeries({
328
+ feature: item.feature,
329
+ layer: item.layer,
330
+ level: activity.selectedLevel
331
+ })
332
+ if (!_.isEmpty(series)) {
333
+ timeSeries.push({
334
+ id: `${item.layer.name}-${featureId}-measure`,
335
+ label: `${item.layer.label} - ${featureLabel || featureId}` + featureLevel,
336
+ series
337
+ })
338
+ }
339
+ }
340
+ // Or weather forecast probe
341
+ if (_.isEmpty(timeSeries) && activity.forecastModel) {
342
+ const series = getForecastTimeSeries({
343
+ feature: item.feature,
344
+ layer: item.layer,
345
+ forecastLayers,
346
+ forecastModel: activity.forecastModel,
347
+ forecastLevel: activity.forecastLevel,
348
+ weacastApi: activity.getWeacastApi()
349
+ })
350
+ if (!_.isEmpty(series)) {
351
+ timeSeries.push({
352
+ id: `${item.layer.name}-${featureId}-probe`,
353
+ label: `${activity.forecastModel.label} (${item.layer.label} - ${featureLabel || featureId})` + forecastLevel,
354
+ series
355
+ })
356
+ }
357
+ }
358
+ })
359
+ }
360
+
361
+ const groupBy = Store.get('timeseries.groupBy')
362
+ // Default is to group by feature
363
+ if (groupBy === 'variable') {
364
+ const timeSeriesByVariable = {}
365
+ timeSeries.forEach((timeSerie, index) => {
366
+ timeSerie.series.forEach(serie => {
367
+ // We do not mix variables with different units
368
+ const variable = `${_.get(serie, 'variable.name')}-${_.get(serie, 'variable.unit.name')}`
369
+ const variableLabel = _.get(serie, 'variable.label')
370
+ // When organizing time series by feature chart name is the feature name while the dataset label is the variable name,
371
+ // eg a 'Toulouse' station collecting 'temperature' and 'humidity' data
372
+ // When organizing time series by variable the chart name is the variable name while the dataset label is the feature name
373
+ // eg the 'temperature' data for different stations 'Toulouse', 'Paris', etc.
374
+ _.set(serie, 'variable.label', timeSerie.label)
375
+ _.set(serie, 'variable.chartjs.backgroundColor', Colors[index % nbColors])
376
+ _.set(serie, 'variable.chartjs.borderColor', Colors[index % nbColors])
377
+ if (timeSeriesByVariable[variable]) {
378
+ timeSeriesByVariable[variable].series.push(serie)
379
+ } else {
380
+ timeSeriesByVariable[variable] = {
381
+ id: variable,
382
+ label: variableLabel,
383
+ series: [serie]
384
+ }
385
+ }
386
+ })
387
+ })
388
+ timeSeries = _.values(timeSeriesByVariable)
389
+ }
390
+ // Restore previous state if any
391
+ if (previousTimeSeries) {
392
+ timeSeries.forEach(timeSerie => {
393
+ const previousTimeSerie = _.find(previousTimeSeries, { id: timeSerie.id })
394
+ // Keep track of some states only
395
+ if (previousTimeSerie) Object.assign(timeSerie, _.pick(previousTimeSerie, ['visible', 'pinned', 'logarithmic']))
396
+ })
397
+ }
398
+ // Make first serie visible if required, always measure first if any
399
+ if (!_.isEmpty(timeSeries) && !_.find(timeSeries, { visible: true })) {
400
+ const timeSerie = _.find(timeSeries, timeSerie => timeSerie.label.includes('measure')) || timeSeries[0]
401
+ timeSerie.visible = true
402
+ }
403
+ return timeSeries
404
+ }
@@ -16,7 +16,7 @@
16
16
  },
17
17
  "description": {
18
18
  "type": ["string", "null"],
19
- "maxLength": 256,
19
+ "maxLength": 4096,
20
20
  "field": {
21
21
  "component": "form/KTextField",
22
22
  "label": "schemas.CATALOG_DESCRIPTION_FIELD_LABEL"
@@ -58,7 +58,7 @@ export class WeacastGridSource extends GridSource {
58
58
  constructor (options) {
59
59
  super(options)
60
60
 
61
- this.api = options.weacastApi
61
+ this.api = options.planetApi
62
62
  this.usable = false
63
63
  }
64
64