@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.
- package/core/api/application.js +2 -4
- package/core/api/authentication.js +2 -3
- package/core/api/db.js +10 -2
- package/core/api/hooks/hooks.authorisations.js +4 -2
- package/core/api/hooks/hooks.push.js +6 -2
- package/core/api/hooks/hooks.query.js +29 -12
- package/core/api/hooks/hooks.users.js +30 -17
- package/core/api/models/configurations.model.mongodb.js +4 -0
- package/core/api/services/authorisations/authorisations.service.js +1 -1
- package/core/api/services/configurations/configurations.hooks.js +33 -0
- package/core/api/services/index.js +41 -7
- package/core/api/services/messages/messages.hooks.js +9 -3
- package/core/client/api.js +14 -1
- package/core/client/capabilities.js +1 -6
- package/core/client/components/KAvatar.vue +24 -20
- package/core/client/components/account/KProfile.vue +10 -71
- package/core/client/components/account/index.js +0 -2
- package/core/client/components/app/KSettings.vue +1 -0
- package/core/client/components/collection/KBoard.vue +4 -3
- package/core/client/components/collection/KCardSection.vue +1 -0
- package/core/client/components/collection/KGrid.vue +2 -0
- package/core/client/components/collection/KTable.vue +5 -1
- package/core/client/components/collection/KTimeLine.vue +9 -1
- package/core/client/components/collection/index.js +0 -2
- package/core/client/components/form/KChipsField.vue +2 -1
- package/core/client/components/form/KEmailField.vue +1 -0
- package/core/client/components/form/KFileField.vue +22 -1
- package/core/client/components/form/KForm.vue +2 -0
- package/core/client/components/form/KItemField.vue +1 -0
- package/core/client/components/form/KNumberField.vue +1 -0
- package/core/client/components/form/KPasswordField.vue +1 -0
- package/core/client/components/form/KPhoneField.vue +1 -0
- package/core/client/components/form/KPropertyItemField.vue +1 -0
- package/core/client/components/form/KResolutionField.vue +1 -0
- package/core/client/components/form/KSelectField.vue +31 -0
- package/core/client/components/form/KTagField.vue +1 -0
- package/core/client/components/form/KTextField.vue +1 -0
- package/core/client/components/form/KTokenField.vue +1 -0
- package/core/client/components/form/KUnitField.vue +1 -0
- package/core/client/components/form/KUrlField.vue +1 -0
- package/core/client/components/graphics/KIcon.vue +3 -4
- package/core/client/components/layout/KPage.vue +1 -0
- package/core/client/components/layout/KWindow.vue +6 -3
- package/core/client/components/messages/KMessageComposer.vue +2 -1
- package/core/client/components/messages/KMessagesTimeLine.vue +1 -1
- package/core/client/components/time/KDate.vue +1 -2
- package/core/client/components/time/KDateTime.vue +11 -11
- package/core/client/components/time/KTime.vue +1 -1
- package/core/client/composables/collection.js +33 -8
- package/core/client/composables/errors.js +1 -1
- package/core/client/composables/layout.js +9 -9
- package/core/client/configurations.js +50 -0
- package/core/client/exporter.js +1 -1
- package/core/client/i18n/core_en.json +6 -39
- package/core/client/i18n/core_fr.json +6 -39
- package/core/client/index.js +2 -0
- package/core/client/layout.js +8 -8
- package/core/client/mixins/mixin.base-activity.js +5 -2
- package/core/client/mixins/mixin.base-field.js +3 -3
- package/core/client/search.js +2 -1
- package/core/client/utils/utils.collection.js +8 -8
- package/core/client/utils/utils.items.js +4 -0
- package/core/client/utils/utils.push.js +3 -3
- package/core/client/utils/utils.session.js +7 -5
- package/core/client/utils/utils.shapes.js +38 -7
- package/core/client/utils/utils.time.js +21 -22
- package/core/common/schemas/users.update-profile.json +3 -2
- package/coverage/core/api/application.js.html +1 -1
- package/coverage/core/api/authentication.js.html +1 -1
- package/coverage/core/api/db.js.html +1 -1
- package/coverage/core/api/hooks/hooks.authentication.js.html +1 -1
- package/coverage/core/api/hooks/hooks.authorisations.js.html +1 -1
- package/coverage/core/api/hooks/hooks.logger.js.html +1 -1
- package/coverage/core/api/hooks/hooks.model.js.html +1 -1
- package/coverage/core/api/hooks/hooks.push.js.html +22 -10
- package/coverage/core/api/hooks/hooks.query.js.html +33 -6
- package/coverage/core/api/hooks/hooks.schemas.js.html +1 -1
- package/coverage/core/api/hooks/hooks.service.js.html +1 -1
- package/coverage/core/api/hooks/hooks.storage.js.html +1 -1
- package/coverage/core/api/hooks/hooks.tags.js.html +1 -1
- package/coverage/core/api/hooks/hooks.users.js.html +4 -4
- package/coverage/core/api/hooks/index.html +23 -23
- package/coverage/core/api/hooks/index.js.html +1 -1
- package/coverage/core/api/index.html +1 -1
- package/coverage/core/api/index.js.html +1 -1
- package/coverage/core/api/marshall.js.html +1 -1
- package/coverage/core/api/models/configurations.model.mongodb.js.html +1 -1
- package/coverage/core/api/models/index.html +1 -1
- package/coverage/core/api/models/messages.model.mongodb.js.html +1 -1
- package/coverage/core/api/models/tags.model.mongodb.js.html +1 -1
- package/coverage/core/api/models/users.model.mongodb.js.html +1 -1
- package/coverage/core/api/services/account/account.hooks.js.html +1 -1
- package/coverage/core/api/services/account/account.service.js.html +1 -1
- package/coverage/core/api/services/account/index.html +1 -1
- package/coverage/core/api/services/authorisations/authorisations.hooks.js.html +1 -1
- package/coverage/core/api/services/authorisations/authorisations.service.js.html +1 -1
- package/coverage/core/api/services/authorisations/index.html +1 -1
- package/coverage/core/api/services/configurations/configurations.hooks.js.html +1 -1
- package/coverage/core/api/services/configurations/index.html +1 -1
- package/coverage/core/api/services/databases/databases.hooks.js.html +1 -1
- package/coverage/core/api/services/databases/databases.service.js.html +1 -1
- package/coverage/core/api/services/databases/index.html +1 -1
- package/coverage/core/api/services/import-export/import-export.hooks.js.html +1 -1
- package/coverage/core/api/services/import-export/import-export.service.js.html +1 -1
- package/coverage/core/api/services/import-export/index.html +1 -1
- package/coverage/core/api/services/index.html +1 -1
- package/coverage/core/api/services/index.js.html +1 -1
- package/coverage/core/api/services/mailer/index.html +1 -1
- package/coverage/core/api/services/mailer/mailer.hooks.js.html +1 -1
- package/coverage/core/api/services/mailer/mailer.service.js.html +1 -1
- package/coverage/core/api/services/messages/index.html +1 -1
- package/coverage/core/api/services/messages/messages.hooks.js.html +1 -1
- package/coverage/core/api/services/push/index.html +1 -1
- package/coverage/core/api/services/push/push.hooks.js.html +1 -1
- package/coverage/core/api/services/push/push.service.js.html +1 -1
- package/coverage/core/api/services/storage/index.html +1 -1
- package/coverage/core/api/services/storage/storage.hooks.js.html +1 -1
- package/coverage/core/api/services/storage/storage.service.js.html +1 -1
- package/coverage/core/api/services/tags/index.html +1 -1
- package/coverage/core/api/services/tags/tags.hooks.js.html +1 -1
- package/coverage/core/api/services/users/index.html +1 -1
- package/coverage/core/api/services/users/users.hooks.js.html +1 -1
- package/coverage/core/api/services/users/users.service.js.html +1 -1
- package/coverage/core/common/errors.js.html +1 -1
- package/coverage/core/common/index.html +1 -1
- package/coverage/core/common/index.js.html +1 -1
- package/coverage/core/common/permissions.js.html +1 -1
- package/coverage/core/common/schema.js.html +1 -1
- package/coverage/core/common/utils.js.html +1 -1
- package/coverage/core/common/utils.offline.js.html +1 -1
- package/coverage/index.html +17 -17
- package/coverage/lcov-report/core/api/application.js.html +1 -1
- package/coverage/lcov-report/core/api/authentication.js.html +1 -1
- package/coverage/lcov-report/core/api/db.js.html +1 -1
- package/coverage/lcov-report/core/api/hooks/hooks.authentication.js.html +1 -1
- package/coverage/lcov-report/core/api/hooks/hooks.authorisations.js.html +1 -1
- package/coverage/lcov-report/core/api/hooks/hooks.logger.js.html +1 -1
- package/coverage/lcov-report/core/api/hooks/hooks.model.js.html +1 -1
- package/coverage/lcov-report/core/api/hooks/hooks.push.js.html +22 -10
- package/coverage/lcov-report/core/api/hooks/hooks.query.js.html +33 -6
- package/coverage/lcov-report/core/api/hooks/hooks.schemas.js.html +1 -1
- package/coverage/lcov-report/core/api/hooks/hooks.service.js.html +1 -1
- package/coverage/lcov-report/core/api/hooks/hooks.storage.js.html +1 -1
- package/coverage/lcov-report/core/api/hooks/hooks.tags.js.html +1 -1
- package/coverage/lcov-report/core/api/hooks/hooks.users.js.html +4 -4
- package/coverage/lcov-report/core/api/hooks/index.html +23 -23
- package/coverage/lcov-report/core/api/hooks/index.js.html +1 -1
- package/coverage/lcov-report/core/api/index.html +1 -1
- package/coverage/lcov-report/core/api/index.js.html +1 -1
- package/coverage/lcov-report/core/api/marshall.js.html +1 -1
- package/coverage/lcov-report/core/api/models/configurations.model.mongodb.js.html +1 -1
- package/coverage/lcov-report/core/api/models/index.html +1 -1
- package/coverage/lcov-report/core/api/models/messages.model.mongodb.js.html +1 -1
- package/coverage/lcov-report/core/api/models/tags.model.mongodb.js.html +1 -1
- package/coverage/lcov-report/core/api/models/users.model.mongodb.js.html +1 -1
- package/coverage/lcov-report/core/api/services/account/account.hooks.js.html +1 -1
- package/coverage/lcov-report/core/api/services/account/account.service.js.html +1 -1
- package/coverage/lcov-report/core/api/services/account/index.html +1 -1
- package/coverage/lcov-report/core/api/services/authorisations/authorisations.hooks.js.html +1 -1
- package/coverage/lcov-report/core/api/services/authorisations/authorisations.service.js.html +1 -1
- package/coverage/lcov-report/core/api/services/authorisations/index.html +1 -1
- package/coverage/lcov-report/core/api/services/configurations/configurations.hooks.js.html +1 -1
- package/coverage/lcov-report/core/api/services/configurations/index.html +1 -1
- package/coverage/lcov-report/core/api/services/databases/databases.hooks.js.html +1 -1
- package/coverage/lcov-report/core/api/services/databases/databases.service.js.html +1 -1
- package/coverage/lcov-report/core/api/services/databases/index.html +1 -1
- package/coverage/lcov-report/core/api/services/import-export/import-export.hooks.js.html +1 -1
- package/coverage/lcov-report/core/api/services/import-export/import-export.service.js.html +1 -1
- package/coverage/lcov-report/core/api/services/import-export/index.html +1 -1
- package/coverage/lcov-report/core/api/services/index.html +1 -1
- package/coverage/lcov-report/core/api/services/index.js.html +1 -1
- package/coverage/lcov-report/core/api/services/mailer/index.html +1 -1
- package/coverage/lcov-report/core/api/services/mailer/mailer.hooks.js.html +1 -1
- package/coverage/lcov-report/core/api/services/mailer/mailer.service.js.html +1 -1
- package/coverage/lcov-report/core/api/services/messages/index.html +1 -1
- package/coverage/lcov-report/core/api/services/messages/messages.hooks.js.html +1 -1
- package/coverage/lcov-report/core/api/services/push/index.html +1 -1
- package/coverage/lcov-report/core/api/services/push/push.hooks.js.html +1 -1
- package/coverage/lcov-report/core/api/services/push/push.service.js.html +1 -1
- package/coverage/lcov-report/core/api/services/storage/index.html +1 -1
- package/coverage/lcov-report/core/api/services/storage/storage.hooks.js.html +1 -1
- package/coverage/lcov-report/core/api/services/storage/storage.service.js.html +1 -1
- package/coverage/lcov-report/core/api/services/tags/index.html +1 -1
- package/coverage/lcov-report/core/api/services/tags/tags.hooks.js.html +1 -1
- package/coverage/lcov-report/core/api/services/users/index.html +1 -1
- package/coverage/lcov-report/core/api/services/users/users.hooks.js.html +1 -1
- package/coverage/lcov-report/core/api/services/users/users.service.js.html +1 -1
- package/coverage/lcov-report/core/common/errors.js.html +1 -1
- package/coverage/lcov-report/core/common/index.html +1 -1
- package/coverage/lcov-report/core/common/index.js.html +1 -1
- package/coverage/lcov-report/core/common/permissions.js.html +1 -1
- package/coverage/lcov-report/core/common/schema.js.html +1 -1
- package/coverage/lcov-report/core/common/utils.js.html +1 -1
- package/coverage/lcov-report/core/common/utils.offline.js.html +1 -1
- package/coverage/lcov-report/index.html +17 -17
- package/coverage/lcov-report/map/api/hooks/hooks.catalog.js.html +1 -1
- package/coverage/lcov-report/map/api/hooks/hooks.features.js.html +1 -1
- package/coverage/lcov-report/map/api/hooks/hooks.query.js.html +184 -4
- package/coverage/lcov-report/map/api/hooks/index.html +5 -5
- package/coverage/lcov-report/map/api/hooks/index.js.html +1 -1
- package/coverage/lcov-report/map/api/index.html +1 -1
- package/coverage/lcov-report/map/api/index.js.html +1 -1
- package/coverage/lcov-report/map/api/marshall.js.html +1 -1
- package/coverage/lcov-report/map/api/models/alerts.model.mongodb.js.html +1 -1
- package/coverage/lcov-report/map/api/models/catalog.model.mongodb.js.html +1 -1
- package/coverage/lcov-report/map/api/models/features.model.mongodb.js.html +1 -1
- package/coverage/lcov-report/map/api/models/index.html +1 -1
- package/coverage/lcov-report/map/api/models/projects.model.mongodb.js.html +1 -1
- package/coverage/lcov-report/map/api/models/styles.model.mongodb.js.html +1 -1
- package/coverage/lcov-report/map/api/services/alerts/alerts.hooks.js.html +1 -1
- package/coverage/lcov-report/map/api/services/alerts/alerts.service.js.html +1 -1
- package/coverage/lcov-report/map/api/services/alerts/index.html +1 -1
- package/coverage/lcov-report/map/api/services/catalog/catalog.hooks.js.html +1 -1
- package/coverage/lcov-report/map/api/services/catalog/index.html +1 -1
- package/coverage/lcov-report/map/api/services/daptiles/daptiles.service.js.html +1 -1
- package/coverage/lcov-report/map/api/services/daptiles/index.html +1 -1
- package/coverage/lcov-report/map/api/services/features/features.hooks.js.html +1 -1
- package/coverage/lcov-report/map/api/services/features/features.service.js.html +1 -1
- package/coverage/lcov-report/map/api/services/features/index.html +1 -1
- package/coverage/lcov-report/map/api/services/index.html +1 -1
- package/coverage/lcov-report/map/api/services/index.js.html +1 -1
- package/coverage/lcov-report/map/api/services/projects/index.html +1 -1
- package/coverage/lcov-report/map/api/services/projects/projects.hooks.js.html +1 -1
- package/coverage/lcov-report/map/api/services/styles/index.html +1 -1
- package/coverage/lcov-report/map/api/services/styles/styles.hooks.js.html +1 -1
- package/coverage/lcov-report/map/common/dynamic-grid-source.js.html +1 -1
- package/coverage/lcov-report/map/common/errors.js.html +1 -1
- package/coverage/lcov-report/map/common/geotiff-grid-source.js.html +1 -1
- package/coverage/lcov-report/map/common/grid.js.html +1 -1
- package/coverage/lcov-report/map/common/index.html +1 -1
- package/coverage/lcov-report/map/common/index.js.html +1 -1
- package/coverage/lcov-report/map/common/meteo-model-grid-source.js.html +1 -1
- package/coverage/lcov-report/map/common/moment-utils.js.html +1 -1
- package/coverage/lcov-report/map/common/opendap-grid-source.js.html +1 -1
- package/coverage/lcov-report/map/common/opendap-utils.js.html +1 -1
- package/coverage/lcov-report/map/common/permissions.js.html +1 -1
- package/coverage/lcov-report/map/common/time-based-grid-source.js.html +1 -1
- package/coverage/lcov-report/map/common/tms-utils.js.html +1 -1
- package/coverage/lcov-report/map/common/wcs-grid-source.js.html +1 -1
- package/coverage/lcov-report/map/common/wcs-utils.js.html +1 -1
- package/coverage/lcov-report/map/common/weacast-grid-source.js.html +1 -1
- package/coverage/lcov-report/map/common/wfs-utils.js.html +1 -1
- package/coverage/lcov-report/map/common/wms-utils.js.html +1 -1
- package/coverage/lcov-report/map/common/wmts-utils.js.html +1 -1
- package/coverage/lcov.info +358 -280
- package/coverage/map/api/hooks/hooks.catalog.js.html +1 -1
- package/coverage/map/api/hooks/hooks.features.js.html +1 -1
- package/coverage/map/api/hooks/hooks.query.js.html +184 -4
- package/coverage/map/api/hooks/index.html +5 -5
- package/coverage/map/api/hooks/index.js.html +1 -1
- package/coverage/map/api/index.html +1 -1
- package/coverage/map/api/index.js.html +1 -1
- package/coverage/map/api/marshall.js.html +1 -1
- package/coverage/map/api/models/alerts.model.mongodb.js.html +1 -1
- package/coverage/map/api/models/catalog.model.mongodb.js.html +1 -1
- package/coverage/map/api/models/features.model.mongodb.js.html +1 -1
- package/coverage/map/api/models/index.html +1 -1
- package/coverage/map/api/models/projects.model.mongodb.js.html +1 -1
- package/coverage/map/api/models/styles.model.mongodb.js.html +1 -1
- package/coverage/map/api/services/alerts/alerts.hooks.js.html +1 -1
- package/coverage/map/api/services/alerts/alerts.service.js.html +1 -1
- package/coverage/map/api/services/alerts/index.html +1 -1
- package/coverage/map/api/services/catalog/catalog.hooks.js.html +1 -1
- package/coverage/map/api/services/catalog/index.html +1 -1
- package/coverage/map/api/services/daptiles/daptiles.service.js.html +1 -1
- package/coverage/map/api/services/daptiles/index.html +1 -1
- package/coverage/map/api/services/features/features.hooks.js.html +1 -1
- package/coverage/map/api/services/features/features.service.js.html +1 -1
- package/coverage/map/api/services/features/index.html +1 -1
- package/coverage/map/api/services/index.html +1 -1
- package/coverage/map/api/services/index.js.html +1 -1
- package/coverage/map/api/services/projects/index.html +1 -1
- package/coverage/map/api/services/projects/projects.hooks.js.html +1 -1
- package/coverage/map/api/services/styles/index.html +1 -1
- package/coverage/map/api/services/styles/styles.hooks.js.html +1 -1
- package/coverage/map/common/dynamic-grid-source.js.html +1 -1
- package/coverage/map/common/errors.js.html +1 -1
- package/coverage/map/common/geotiff-grid-source.js.html +1 -1
- package/coverage/map/common/grid.js.html +1 -1
- package/coverage/map/common/index.html +1 -1
- package/coverage/map/common/index.js.html +1 -1
- package/coverage/map/common/meteo-model-grid-source.js.html +1 -1
- package/coverage/map/common/moment-utils.js.html +1 -1
- package/coverage/map/common/opendap-grid-source.js.html +1 -1
- package/coverage/map/common/opendap-utils.js.html +1 -1
- package/coverage/map/common/permissions.js.html +1 -1
- package/coverage/map/common/time-based-grid-source.js.html +1 -1
- package/coverage/map/common/tms-utils.js.html +1 -1
- package/coverage/map/common/wcs-grid-source.js.html +1 -1
- package/coverage/map/common/wcs-utils.js.html +1 -1
- package/coverage/map/common/weacast-grid-source.js.html +1 -1
- package/coverage/map/common/wfs-utils.js.html +1 -1
- package/coverage/map/common/wms-utils.js.html +1 -1
- package/coverage/map/common/wmts-utils.js.html +1 -1
- package/coverage/tmp/coverage-1028514-1773134124472-0.json +1 -0
- package/coverage/tmp/coverage-1028526-1773134124448-0.json +1 -0
- package/coverage/tmp/coverage-1028537-1773134124431-0.json +1 -0
- package/coverage/tmp/coverage-1028549-1773134124401-0.json +1 -0
- package/coverage/tmp/{coverage-222566-1765963609278-0.json → coverage-1028556-1773134124353-0.json} +1 -1
- package/extras/configs/widgets.top.js +3 -3
- package/extras/tests/core/collection.mjs +2 -9
- package/map/api/hooks/hooks.catalog.js +18 -4
- package/map/api/services/catalog/catalog.hooks.js +3 -0
- package/map/api/services/features/features.hooks.js +3 -1
- package/map/api/services/index.js +2 -6
- package/map/api/services/styles/styles.hooks.js +6 -1
- package/map/client/components/KFeatureActionButton.vue +9 -3
- package/map/client/components/KFeaturesFilterManager.vue +5 -5
- package/map/client/components/KFilterCondition.vue +17 -10
- package/map/client/components/KLayerEditor.vue +49 -39
- package/map/client/components/KMeasureTool.vue +7 -1
- package/map/client/components/KTimezoneMap.vue +29 -9
- package/map/client/components/catalog/KLayersPanel.vue +26 -16
- package/map/client/components/catalog/KLayersSelector.vue +13 -2
- package/map/client/components/catalog/KViewsPanel.vue +5 -4
- package/map/client/components/form/KSelectLayersField.vue +28 -17
- package/map/client/components/form/KSelectViewsField.vue +18 -9
- package/map/client/components/form/KTimezoneField.vue +1 -2
- package/map/client/components/legend/KVariablesLegend.vue +10 -1
- package/map/client/components/location/KLocationCardSection.vue +7 -2
- package/map/client/components/location/KLocationMap.vue +31 -7
- package/map/client/components/selection/KSelectedLayerFeatures.vue +2 -2
- package/map/client/components/stickies/KZoomControl.vue +1 -1
- package/map/client/components/styles/KStyleManager.vue +4 -1
- package/map/client/components/widget/KTimeSeries.vue +174 -497
- package/map/client/components/widget/KTimeSeriesSelector.vue +72 -0
- package/map/client/components/widget/KTimeSeriesToolbar.vue +83 -0
- package/map/client/composables/catalog.js +6 -10
- package/map/client/composables/highlight.js +12 -9
- package/map/client/composables/project.js +1 -1
- package/map/client/composables/selection.js +8 -7
- package/map/client/composables/weather.js +9 -2
- package/map/client/geolocation.js +8 -5
- package/map/client/i18n/map_en.json +10 -8
- package/map/client/i18n/map_fr.json +9 -7
- package/map/client/leaflet/TiledFeatureLayer.js +85 -82
- package/map/client/leaflet/utils/utils.geojson.js +3 -3
- package/map/client/mixins/globe/mixin.base-globe.js +15 -6
- package/map/client/mixins/globe/mixin.geojson-layers.js +27 -18
- package/map/client/mixins/map/mixin.edit-layers.js +9 -1
- package/map/client/mixins/map/mixin.pmtiles-layers.js +118 -29
- package/map/client/mixins/map/mixin.tiled-mesh-layers.js +12 -5
- package/map/client/mixins/map/mixin.tiled-wind-layers.js +19 -10
- package/map/client/mixins/mixin.activity.js +23 -30
- package/map/client/mixins/mixin.feature-selection.js +41 -5
- package/map/client/planets.js +1 -1
- package/map/client/readers/reader.kml.js +2 -3
- package/map/client/utils/utils.catalog.js +36 -10
- package/map/client/utils/utils.layers.js +39 -8
- package/map/client/utils/utils.project.js +4 -0
- package/map/client/utils/utils.style.js +37 -7
- package/map/client/utils/utils.time-series.js +215 -6
- package/map/common/schemas/catalog.update.json +1 -1
- package/map/common/weacast-grid-source.js +1 -1
- package/package.json +3 -3
- package/scripts/kash/CHANGELOG.md +0 -4
- package/scripts/kash/README.md +0 -9
- package/scripts/kash/kash.sh +45 -40
- package/scripts/kash/scripts/run_tests.sh +1 -4
- package/test/api/core/authentication.test.js +9 -4
- package/test/api/core/config/default.cjs +1 -0
- package/test/api/core/hooks.test.js +6 -0
- package/test/api/core/index.test.js +43 -18
- package/test/api/core/push.test.js +8 -8
- package/test/api/core/test-log-2026-03-10.log +60 -0
- package/test/api/core/users.test.js +384 -0
- package/test/api/map/grid-sources.test.js +1 -1
- package/test/api/map/test-log-2026-03-10.log +56 -0
- package/vite/package.json +11 -2
- package/vite/test/core/composables.test.js +77 -0
- package/vite/vitest.config.js +13 -0
- package/vite/yarn.lock +1096 -18
- package/client/css/core.variables.scss +0 -72
- package/client/i18n/core_en.json +0 -744
- package/client/i18n/core_fr.json +0 -744
- package/client/i18n/map_en.json +0 -800
- package/client/i18n/map_fr.json +0 -800
- package/client/kdk.client.css +0 -47
- package/client/kdk.client.js +0 -41097
- package/client/kdk.client.map.css +0 -47
- package/client/kdk.client.map.js +0 -38182
- package/client/kdk.client.map.min.css +0 -1
- package/client/kdk.client.map.min.js +0 -27032
- package/client/kdk.client.min.css +0 -1
- package/client/kdk.client.min.js +0 -29074
- package/client/schemas/capture.create.json +0 -132
- package/client/schemas/catalog.update.json +0 -44
- package/client/schemas/messages.update.json +0 -16
- package/client/schemas/projects.create.json +0 -52
- package/client/schemas/projects.update.json +0 -52
- package/client/schemas/settings.update.json +0 -286
- package/client/schemas/tags.update.json +0 -35
- package/client/schemas/users.update-profile.json +0 -34
- package/core/client/components/account/KAccount.vue +0 -68
- package/core/client/components/account/KDeleteAccountManager.vue +0 -62
- package/core/client/components/account/KEmailManager.vue +0 -128
- package/core/client/components/account/KPasswordManager.vue +0 -90
- package/core/client/components/account/KVerifyEmailManager.vue +0 -105
- package/core/client/components/collection/KColumn.vue +0 -227
- package/core/client/components/collection/KHistory.vue +0 -113
- package/core/client/components/collection/KHistoryEntry.vue +0 -109
- package/coverage/tmp/coverage-222524-1765963609350-0.json +0 -1
- package/coverage/tmp/coverage-222536-1765963609335-0.json +0 -1
- package/coverage/tmp/coverage-222547-1765963609324-0.json +0 -1
- package/coverage/tmp/coverage-222559-1765963609309-0.json +0 -1
- package/scripts/kash/LICENSE +0 -21
- package/test/api/core/test-log-2025-07-31.log +0 -15
- package/test/api/core/test-log-2025-10-03.log +0 -18
- package/test/api/core/test-log-2025-11-10.log +0 -0
- package/test/api/core/test-log-2025-11-12.log +0 -117
- package/test/api/core/test-log-2025-11-27.log +0 -0
- package/test/api/core/test-log-2025-11-28.log +0 -17
- package/test/api/core/test-log-2025-12-09.log +0 -148
- package/test/api/core/test-log-2025-12-17.log +0 -58
- package/test/api/core/test-log-2026-01-29.log +0 -17
- package/test/api/map/test-log-2025-07-23.log +0 -1
- package/test/api/map/test-log-2025-11-28.log +0 -33
- package/test/api/map/test-log-2025-12-10.log +0 -2
- 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.
|
|
6
|
-
this.handleWidget(this.getWidgetForSelection())
|
|
9
|
+
this.updateSelection()
|
|
7
10
|
}
|
|
8
11
|
},
|
|
9
12
|
'probe.item': {
|
|
10
13
|
handler () {
|
|
11
|
-
this.
|
|
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
|
-
|
|
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
|
}
|
package/map/client/planets.js
CHANGED
|
@@ -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('
|
|
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(
|
|
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 =
|
|
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: () =>
|
|
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,
|
|
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 =
|
|
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 =
|
|
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 =
|
|
187
|
+
const catalogService = planetApi.getService('catalog', options.context)
|
|
179
188
|
if (catalogService) {
|
|
180
|
-
const response = await catalogService.find({
|
|
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(
|
|
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 (
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
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 {
|
|
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: `${
|
|
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: `${
|
|
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
|
+
}
|