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