@kalisio/kdk 2.1.9 → 2.2.1
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/.travis.test.sh +42 -10
- package/README.md +2 -2
- package/core/api/application.js +7 -2
- package/core/api/authentication.js +17 -1
- package/core/api/db.js +7 -2
- package/core/api/hooks/hooks.authentication.js +4 -2
- package/core/api/hooks/hooks.authorisations.js +12 -2
- package/core/api/hooks/hooks.model.js +5 -5
- package/core/api/hooks/hooks.organisations.js +0 -4
- package/core/api/services/account/account.hooks.js +10 -6
- package/core/api/services/account/account.service.js +1 -1
- package/{map/api/services/geocoder/geocoder.hooks.js → core/api/services/import-export/import-export.hooks.js} +7 -5
- package/core/api/services/import-export/import-export.service.js +11 -0
- package/core/api/services/index.js +13 -1
- package/core/api/services/users/users.hooks.js +2 -3
- package/core/client/api.js +16 -14
- package/core/client/capabilities.js +6 -2
- package/core/client/components/KContent.vue +11 -1
- package/core/client/components/KDialog.vue +17 -15
- package/core/client/components/KModal.vue +1 -1
- package/core/client/components/KSponsor.vue +1 -1
- package/core/client/components/KTextArea.vue +5 -1
- package/core/client/components/app/KAbout.vue +1 -2
- package/core/client/components/app/KWelcome.vue +3 -5
- package/core/client/components/chart/KTimeSeriesChart.vue +24 -37
- package/core/client/components/collection/KColumn.vue +20 -17
- package/core/client/components/editor/KModalEditor.vue +0 -2
- package/core/client/components/form/KChipsField.vue +12 -2
- package/core/client/components/form/KColorField.vue +12 -2
- package/core/client/components/form/KColorScaleField.vue +12 -2
- package/core/client/components/form/KDateTimeRangeField.vue +12 -2
- package/core/client/components/form/KDatetimeField.vue +12 -2
- package/core/client/components/form/KEmailField.vue +12 -2
- package/core/client/components/form/KFileField.vue +12 -2
- package/core/client/components/form/KForm.vue +47 -9
- package/core/client/components/form/KIconField.vue +12 -2
- package/core/client/components/form/KItemField.vue +25 -4
- package/core/client/components/form/KNumberField.vue +12 -2
- package/core/client/components/form/KOptionsField.vue +12 -2
- package/core/client/components/form/KPasswordField.vue +12 -2
- package/core/client/components/form/KPhoneField.vue +13 -3
- package/core/client/components/form/KPropertyItemField.vue +12 -2
- package/core/client/components/form/KResolutionField.vue +126 -0
- package/core/client/components/form/KRoleField.vue +12 -2
- package/core/client/components/form/KSelectField.vue +14 -4
- package/core/client/components/form/KTextField.vue +12 -2
- package/core/client/components/form/KTextareaField.vue +13 -3
- package/core/client/components/form/KToggleField.vue +12 -2
- package/core/client/components/form/KTokenField.vue +12 -2
- package/core/client/components/form/KUnitField.vue +12 -2
- package/core/client/components/form/KUrlField.vue +12 -2
- package/core/client/components/input/KIconChooser.vue +10 -12
- package/core/client/components/input/KPalette.vue +2 -1
- package/core/client/components/layout/KPage.vue +5 -4
- package/core/client/components/layout/KWindow.vue +10 -10
- package/core/client/components/media/KColorScale.vue +25 -19
- package/core/client/components/media/KImageViewer.vue +57 -33
- package/core/client/components/media/KShape.vue +14 -103
- package/core/client/components/screen/KRegisterScreen.vue +0 -1
- package/core/client/components/screen/KScreenFooter.vue +0 -18
- package/core/client/components/team/KAddMember.vue +16 -22
- package/core/client/components/team/KGroupsActivity.vue +14 -0
- package/core/client/components/team/KMembersActivity.vue +12 -0
- package/core/client/components/team/KTagsActivity.vue +14 -0
- package/core/client/components/time/KDateTime.vue +23 -7
- package/core/client/components/time/KTimeControl.vue +142 -0
- package/core/client/components/tool/KExportTool.vue +57 -0
- package/core/client/composables/collection.js +0 -1
- package/core/client/composables/pwa.js +0 -1
- package/core/client/composables/schema.js +1 -1
- package/core/client/composables/session.js +30 -6
- package/core/client/exporter.js +147 -0
- package/core/client/i18n/core_en.json +91 -23
- package/core/client/i18n/core_fr.json +92 -23
- package/core/client/index.js +3 -0
- package/core/client/layout.js +34 -14
- package/core/client/local-storage.js +8 -6
- package/core/client/mixins/index.js +0 -1
- package/core/client/mixins/mixin.base-field.js +24 -2
- package/core/client/mixins/mixin.object-proxy.js +0 -1
- package/core/client/search.js +2 -1
- package/core/client/services/index.js +2 -1
- package/core/client/services/local-settings.service.js +4 -4
- package/core/client/theme.js +3 -3
- package/core/client/time.js +4 -0
- package/core/client/units.js +150 -5
- package/core/client/utils/index.js +13 -6
- package/core/client/utils/utils.account.js +1 -1
- package/core/client/utils/utils.colors.js +43 -0
- package/core/client/utils/utils.platform.js +0 -1
- package/core/client/utils/utils.pwa.js +14 -14
- package/core/client/utils/utils.session.js +1 -1
- package/core/client/utils/utils.shapes.js +270 -0
- package/core/client/utils/utils.time.js +37 -0
- package/core/common/permissions.js +3 -0
- package/core/common/schemas/settings.update.json +50 -29
- package/extras/css/core.variables.scss +3 -1
- package/extras/icons/wind-speed-0.svg +8 -0
- package/extras/icons/wind-speed-10.svg +8 -0
- package/extras/icons/wind-speed-100.svg +12 -0
- package/extras/icons/wind-speed-105.svg +13 -0
- package/extras/icons/wind-speed-15.svg +9 -0
- package/extras/icons/wind-speed-20.svg +9 -0
- package/extras/icons/wind-speed-25.svg +10 -0
- package/extras/icons/wind-speed-30.svg +10 -0
- package/extras/icons/wind-speed-35.svg +11 -0
- package/extras/icons/wind-speed-40.svg +11 -0
- package/extras/icons/wind-speed-45.svg +12 -0
- package/extras/icons/wind-speed-5.svg +9 -0
- package/extras/icons/wind-speed-50.svg +9 -0
- package/extras/icons/wind-speed-55.svg +10 -0
- package/extras/icons/wind-speed-60.svg +10 -0
- package/extras/icons/wind-speed-65.svg +11 -0
- package/extras/icons/wind-speed-70.svg +11 -0
- package/extras/icons/wind-speed-75.svg +12 -0
- package/extras/icons/wind-speed-80.svg +12 -0
- package/extras/icons/wind-speed-85.svg +13 -0
- package/extras/icons/wind-speed-90.svg +13 -0
- package/extras/icons/wind-speed-95.svg +14 -0
- package/extras/tours/map/navigation-bar.js +17 -15
- package/extras/tours/map/timeline.js +33 -33
- package/map/api/config/categories.cjs +4 -1
- package/map/api/hooks/hooks.catalog.js +39 -0
- package/map/api/hooks/hooks.features.js +23 -3
- package/map/api/hooks/hooks.query.js +65 -21
- package/map/api/models/projects.model.mongodb.js +8 -0
- package/map/api/services/catalog/catalog.hooks.js +5 -3
- package/map/api/services/features/features.hooks.js +18 -6
- package/map/api/services/index.js +22 -6
- package/map/api/services/projects/projects.hooks.js +118 -0
- package/map/client/capture.js +16 -0
- package/map/client/cesium/utils/index.js +4 -0
- package/map/client/cesium/utils/utils.events.js +30 -0
- package/map/client/cesium/utils/utils.features.js +8 -0
- package/map/client/cesium/utils/utils.popup.js +17 -0
- package/map/client/cesium/utils/utils.style.js +137 -0
- package/map/client/components/KCapture.vue +50 -0
- package/map/client/components/KCaptureTextArea.vue +53 -0
- package/map/client/components/KCompass.vue +2 -2
- package/map/client/components/KFeaturesChart.vue +1 -1
- package/map/client/components/KFeaturesFilter.vue +2 -2
- package/map/client/components/KLayerStyleForm.vue +288 -454
- package/map/client/components/KLevelSlider.vue +1 -1
- package/map/client/components/KNorth.vue +31 -0
- package/map/client/components/KProjectMenu.vue +88 -0
- package/map/client/components/KTimezoneMap.vue +36 -24
- package/map/client/components/catalog/KAddLayer.vue +3 -4
- package/map/client/components/catalog/KConnectLayer.vue +20 -4
- package/map/client/components/catalog/KCreateLayer.vue +1 -2
- package/map/client/components/catalog/KCreateProject.vue +100 -0
- package/map/client/components/catalog/KCreateView.vue +25 -2
- package/map/client/components/catalog/KLayersPanel.vue +24 -27
- package/map/client/components/catalog/KLayersSelector.vue +1 -1
- package/map/client/components/catalog/KProjectEditor.vue +91 -0
- package/map/client/components/catalog/KProjectManager.vue +60 -0
- package/map/client/components/catalog/KProjectSelector.vue +38 -0
- package/map/client/components/catalog/KProjectsPanel.vue +153 -0
- package/map/client/components/catalog/KSelectLayers.vue +96 -0
- package/map/client/components/catalog/KSelectViews.vue +96 -0
- package/map/client/components/catalog/KViewsPanel.vue +66 -30
- package/map/client/components/form/KDirectionField.vue +24 -5
- package/map/client/components/form/KLayerCategoryField.vue +12 -2
- package/map/client/components/form/KLocationField.vue +20 -5
- package/map/client/components/form/KOwsLayerField.vue +12 -2
- package/map/client/components/form/KOwsServiceField.vue +12 -2
- package/map/client/components/form/KSelectLayersField.vue +159 -0
- package/map/client/components/form/KSelectViewsField.vue +121 -0
- package/map/client/components/form/KTimezoneField.vue +24 -17
- package/map/client/components/legend/KColorScaleLegend.vue +6 -2
- package/map/client/components/legend/KLayerLegend.vue +71 -0
- package/map/client/components/legend/KLegend.vue +54 -51
- package/map/client/components/legend/KLegendRenderer.vue +5 -3
- package/map/client/components/legend/KSymbolsLegend.vue +12 -10
- package/map/client/components/legend/KVariablesLegend.vue +78 -0
- package/map/client/components/location/KGeocodersFilter.vue +2 -4
- package/map/client/components/location/KLocationCardSection.vue +8 -4
- package/map/client/components/location/KLocationMap.vue +48 -17
- package/map/client/components/location/KLocationSearch.vue +13 -3
- package/map/client/components/tools/KSearchTool.vue +17 -12
- package/map/client/components/widget/KElevationProfile.vue +16 -19
- package/map/client/components/widget/KMapillaryViewer.vue +21 -22
- package/map/client/components/widget/KTimeSeries.vue +35 -29
- package/map/client/composables/activity.js +15 -2
- package/map/client/composables/catalog.js +81 -0
- package/map/client/composables/highlight.js +45 -30
- package/map/client/composables/index.js +2 -0
- package/map/client/composables/location.js +25 -18
- package/map/client/composables/probe.js +4 -1
- package/map/client/composables/project.js +122 -0
- package/map/client/composables/weather.js +3 -3
- package/map/client/geolocation.js +1 -1
- package/map/client/globe.js +2 -0
- package/map/client/i18n/map_en.json +127 -76
- package/map/client/i18n/map_fr.json +128 -72
- package/map/client/index.js +3 -0
- package/map/client/init.js +17 -0
- package/map/client/leaflet/GSMaPLayer.js +16 -17
- package/map/client/leaflet/ShapeMarker.js +51 -0
- package/map/client/leaflet/TiledFeatureLayer.js +39 -9
- package/map/client/leaflet/TiledMeshLayer.js +13 -15
- package/map/client/leaflet/TiledWindLayer.js +6 -10
- package/map/client/leaflet/utils/index.js +4 -0
- package/map/client/leaflet/utils/utils.events.js +41 -0
- package/map/client/leaflet/utils/utils.popup.js +21 -0
- package/map/client/leaflet/utils/utils.style.js +195 -0
- package/map/client/leaflet/utils/utils.tiles.js +87 -0
- package/map/client/map.js +2 -0
- package/map/client/mixins/globe/mixin.base-globe.js +39 -18
- package/map/client/mixins/globe/mixin.geojson-layers.js +139 -69
- package/map/client/mixins/globe/mixin.popup.js +2 -1
- package/map/client/mixins/globe/mixin.style.js +6 -4
- package/map/client/mixins/globe/mixin.tooltip.js +8 -3
- package/map/client/mixins/map/mixin.base-map.js +53 -28
- package/map/client/mixins/map/mixin.edit-layers.js +15 -15
- package/map/client/mixins/map/mixin.forecast-layers.js +3 -1
- package/map/client/mixins/map/mixin.geojson-layers.js +60 -20
- package/map/client/mixins/map/mixin.georaster-layers.js +4 -11
- package/map/client/mixins/map/mixin.heatmap-layers.js +1 -1
- package/map/client/mixins/map/mixin.popup.js +2 -1
- package/map/client/mixins/map/mixin.style.js +4 -67
- package/map/client/mixins/map/mixin.tiled-mesh-layers.js +2 -1
- package/map/client/mixins/map/mixin.tiled-wind-layers.js +4 -2
- package/map/client/mixins/map/mixin.tooltip.js +2 -1
- package/map/client/mixins/mixin.activity.js +71 -192
- package/map/client/mixins/mixin.catalog-panel.js +6 -6
- package/map/client/mixins/mixin.context.js +12 -9
- package/map/client/mixins/mixin.feature-service.js +29 -300
- package/map/client/mixins/mixin.weacast.js +11 -17
- package/map/client/pixi-utils.js +1 -1
- package/map/client/planets.js +66 -0
- package/map/client/utils/index.js +6 -0
- package/map/client/utils/utils.capture.js +176 -0
- package/map/client/utils/utils.catalog.js +166 -0
- package/map/client/utils/utils.features.js +364 -0
- package/map/client/utils/utils.js +0 -151
- package/map/client/utils/utils.layers.js +175 -0
- package/map/client/utils/utils.location.js +91 -23
- package/map/client/utils/utils.project.js +8 -0
- package/map/client/utils/utils.schema.js +0 -1
- package/map/client/utils/utils.style.js +309 -0
- package/map/client/utils.all.js +2 -2
- package/map/client/utils.globe.js +1 -1
- package/map/client/utils.map.js +1 -1
- package/map/common/permissions.js +2 -0
- package/map/common/schemas/capture.create.json +132 -0
- package/map/common/schemas/projects.create.json +52 -0
- package/map/common/schemas/projects.update.json +52 -0
- package/map/common/wms-utils.js +8 -3
- package/package.json +6 -5
- package/test/api/core/account.test.js +20 -0
- package/test/api/core/config/default.cjs +16 -3
- package/test/api/core/import-export.test.js +86 -0
- package/test/api/core/test-log-2024-01-04.log +14 -0
- package/test/api/map/catalog.test.js +164 -0
- package/test/api/map/index.test.js +25 -61
- 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/client/core/layout.js +24 -5
- package/test/client/core/utils.js +7 -0
- package/test/client/map/catalog.js +78 -1
- package/test/client/map/time.js +2 -1
- package/core/client/components/screen/KEndpointScreen.vue +0 -80
- package/core/client/mixins/mixin.account.js +0 -61
- package/extras/icons/kdk.png +0 -0
- package/map/api/services/geocoder/geocoder.service.js +0 -79
- package/map/client/cesium/utils.js +0 -133
- package/map/client/components/KCaptureToolbar.vue +0 -155
- package/map/client/components/KColorLegend.vue +0 -349
- package/map/client/components/KTimeline.vue +0 -293
- package/map/client/components/KUrlLegend.vue +0 -122
- package/map/client/leaflet/utils.js +0 -246
|
@@ -1,126 +1,20 @@
|
|
|
1
1
|
import _ from 'lodash'
|
|
2
|
-
import explode from '@turf/explode'
|
|
3
|
-
import kinks from '@turf/kinks'
|
|
4
|
-
import clean from '@turf/clean-coords'
|
|
5
2
|
import { getType, getGeom } from '@turf/invariant'
|
|
6
3
|
import logger from 'loglevel'
|
|
7
|
-
import
|
|
8
|
-
import { Time } from '../../../core/client/time.js'
|
|
9
|
-
import { transformFeatures } from '../utils.js'
|
|
4
|
+
import * as features from '../utils/utils.features.js'
|
|
10
5
|
|
|
11
6
|
export const featureService = {
|
|
12
7
|
methods: {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
// A null query indicate to skip update
|
|
20
|
-
if (!result) return
|
|
21
|
-
else Object.assign(baseQuery, result)
|
|
22
|
-
} else {
|
|
23
|
-
Object.assign(baseQuery, options.baseQuery)
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
return baseQuery
|
|
27
|
-
},
|
|
28
|
-
async getFilterQueryForFeatures (options) {
|
|
29
|
-
// Any filter query to process ?
|
|
30
|
-
const filterQuery = {}
|
|
31
|
-
if (options.filterQuery) {
|
|
32
|
-
if (typeof options.filterQuery === 'function') {
|
|
33
|
-
const result = await options.filterQuery()
|
|
34
|
-
Object.assign(filterQuery, result)
|
|
35
|
-
} else {
|
|
36
|
-
Object.assign(filterQuery, options.filterQuery)
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
// Any filters to process ?
|
|
40
|
-
const filters = _.get(options, 'filters', [])
|
|
41
|
-
if (!_.isEmpty(filters)) {
|
|
42
|
-
// To be flexible enough filters can provide a query for their active and inactive state
|
|
43
|
-
// Similarly, filters can be combined with a different operator for each state
|
|
44
|
-
const filterOperators = _.get(options, 'filterOperators', { active: '$or', inactive: '$and' })
|
|
45
|
-
// Aggregate filter queries according to filter states
|
|
46
|
-
const activeFilters = filters
|
|
47
|
-
.filter(filter => filter.isActive)
|
|
48
|
-
.map(filter => filter.active)
|
|
49
|
-
.filter(query => !_.isEmpty(query))
|
|
50
|
-
if (!_.isEmpty(activeFilters)) filterQuery[filterOperators.active] = activeFilters
|
|
51
|
-
const inactiveFilters = filters
|
|
52
|
-
.filter(filter => !filter.isActive)
|
|
53
|
-
.map(filter => filter.inactive)
|
|
54
|
-
.filter(query => !_.isEmpty(query))
|
|
55
|
-
if (!_.isEmpty(inactiveFilters)) filterQuery[filterOperators.inactive] = inactiveFilters
|
|
56
|
-
}
|
|
57
|
-
return filterQuery
|
|
58
|
-
},
|
|
59
|
-
async getSortQueryForFeatures (options) {
|
|
60
|
-
// Any sort query to process ?
|
|
61
|
-
const sortQuery = {}
|
|
62
|
-
if (options.sortQuery) {
|
|
63
|
-
if (typeof options.sortQuery === 'function') {
|
|
64
|
-
const result = await options.sortQuery()
|
|
65
|
-
Object.assign(sortQuery, result)
|
|
66
|
-
} else {
|
|
67
|
-
Object.assign(sortQuery, options.sortQuery)
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
return { $sort: sortQuery }
|
|
71
|
-
},
|
|
72
|
-
getFeaturesUpdateInterval (options) {
|
|
73
|
-
const interval = (_.has(options, 'every')
|
|
74
|
-
? _.get(options, 'every')
|
|
75
|
-
// Backward compatibility with old configuration style
|
|
76
|
-
: _.get(options, this.engine, options).interval)
|
|
77
|
-
return (interval ? moment.duration(interval) : null)
|
|
78
|
-
},
|
|
79
|
-
getFeaturesQueryInterval (options) {
|
|
80
|
-
const interval = this.getFeaturesUpdateInterval(options)
|
|
81
|
-
let queryInterval = (_.has(options, 'queryFrom')
|
|
82
|
-
? _.get(options, 'queryFrom')
|
|
83
|
-
// Backward compatibility with old configuration style
|
|
84
|
-
: _.get(options, this.engine, options).queryInterval)
|
|
85
|
-
// If query interval not given use 2 x refresh interval as default value
|
|
86
|
-
// this ensures we cover last interval if server/client update processes are not in sync
|
|
87
|
-
if (!queryInterval && interval) queryInterval = moment.duration(-2 * interval.asMilliseconds())
|
|
88
|
-
return (queryInterval ? moment.duration(queryInterval) : null)
|
|
89
|
-
},
|
|
90
|
-
shouldSkipFeaturesUpdate (lastUpdateTime, options, interval) {
|
|
91
|
-
// If not given try to compute query interval from options
|
|
92
|
-
if (!interval) {
|
|
93
|
-
interval = this.getFeaturesUpdateInterval(options)
|
|
94
|
-
}
|
|
95
|
-
// We assume this is not a time-varying layer
|
|
96
|
-
if (!interval) return true
|
|
97
|
-
const now = Time.getCurrentTime()
|
|
98
|
-
const elapsed = moment.duration(now.diff(lastUpdateTime))
|
|
99
|
-
// If query interval has elapsed since last update we need to update again
|
|
100
|
-
return (Math.abs(elapsed.asMilliseconds()) < interval.asMilliseconds())
|
|
101
|
-
},
|
|
8
|
+
getBaseQueryForFeatures: features.getBaseQueryForFeatures,
|
|
9
|
+
getFilterQueryForFeatures: features.getFilterQueryForFeatures,
|
|
10
|
+
getSortQueryForFeatures: features.getSortQueryForFeatures,
|
|
11
|
+
getFeaturesUpdateInterval: features.getFeaturesUpdateInterval,
|
|
12
|
+
getFeaturesQueryInterval: features.getFeaturesQueryInterval,
|
|
13
|
+
shouldSkipFeaturesUpdate: features.shouldSkipFeaturesUpdate,
|
|
102
14
|
getFeaturesLevel (options) {
|
|
103
15
|
return (this.selectableLevelsLayer && (this.selectableLevelsLayer.name === options.name) ? this.selectedLevel : null)
|
|
104
16
|
},
|
|
105
|
-
|
|
106
|
-
// Any base/filter/sort query to process ?
|
|
107
|
-
const query = await this.getBaseQueryForFeatures(options)
|
|
108
|
-
const filterQuery = await this.getFilterQueryForFeatures(options)
|
|
109
|
-
const sortQuery = await this.getSortQueryForFeatures(options)
|
|
110
|
-
Object.assign(query, filterQuery, sortQuery)
|
|
111
|
-
const response = await this.$api.getService(options.probeService).find({ query })
|
|
112
|
-
const features = (response.type === 'FeatureCollection' ? response.features : [response])
|
|
113
|
-
if (typeof options.processor === 'function') {
|
|
114
|
-
features.forEach(feature => options.processor(feature))
|
|
115
|
-
} else if (typeof options.processor === 'string') {
|
|
116
|
-
const compiler = _.template(options.processor)
|
|
117
|
-
features.forEach(feature => compiler({ feature, properties: feature.properties }))
|
|
118
|
-
}
|
|
119
|
-
if (options.transform) {
|
|
120
|
-
transformFeatures(features, options.transform)
|
|
121
|
-
}
|
|
122
|
-
return response
|
|
123
|
-
},
|
|
17
|
+
getProbeFeatures: features.getProbeFeatures,
|
|
124
18
|
async getProbeFeaturesFromLayer (name) {
|
|
125
19
|
// Retrieve the layer
|
|
126
20
|
const layer = this.getLayerByName(name)
|
|
@@ -128,84 +22,13 @@ export const featureService = {
|
|
|
128
22
|
return this.getProbeFeatures(layer)
|
|
129
23
|
},
|
|
130
24
|
async getFeaturesQuery (options, queryInterval, queryLevel) {
|
|
131
|
-
// If not given try to compute query interval from options
|
|
132
|
-
if (!queryInterval) {
|
|
133
|
-
queryInterval = this.getFeaturesQueryInterval(options)
|
|
134
|
-
}
|
|
135
25
|
// If not given try to compute query level from options
|
|
136
26
|
if (!queryLevel) {
|
|
137
27
|
queryLevel = this.getFeaturesLevel(options)
|
|
138
28
|
}
|
|
139
|
-
|
|
140
|
-
let query = await this.getBaseQueryForFeatures(options)
|
|
141
|
-
// Request features with at least one data available during last query interval
|
|
142
|
-
if (queryInterval) {
|
|
143
|
-
// Check if we have variables to be aggregated in time or not
|
|
144
|
-
if (options.variables) {
|
|
145
|
-
query = Object.assign({
|
|
146
|
-
$groupBy: options.featureId,
|
|
147
|
-
// Take care we might have multiple variables targetting the same value name
|
|
148
|
-
// but that differentiate using others properties (compound feature ID)
|
|
149
|
-
$aggregate: _.uniq(options.variables.map(variable => variable.name))
|
|
150
|
-
}, query)
|
|
151
|
-
} else if (options.featureId) {
|
|
152
|
-
query = Object.assign({
|
|
153
|
-
$groupBy: options.featureId,
|
|
154
|
-
$aggregate: ['geometry']
|
|
155
|
-
}, query)
|
|
156
|
-
}
|
|
157
|
-
const now = Time.getCurrentTime()
|
|
158
|
-
if (moment.isDuration(queryInterval)) {
|
|
159
|
-
// Depending on the duration format we might have negative or positive values
|
|
160
|
-
const gte = (queryInterval.asMilliseconds() > 0
|
|
161
|
-
? now.clone().subtract(queryInterval)
|
|
162
|
-
: now.clone().add(queryInterval))
|
|
163
|
-
const lte = now
|
|
164
|
-
Object.assign(query, {
|
|
165
|
-
$sort: { time: -1, runTime: -1 },
|
|
166
|
-
time: {
|
|
167
|
-
$gte: gte.toISOString(),
|
|
168
|
-
$lte: lte.toISOString()
|
|
169
|
-
}
|
|
170
|
-
})
|
|
171
|
-
// If we can aggregate then keep track of last element of each aggregation
|
|
172
|
-
if (options.featureId) query.$limit = 1
|
|
173
|
-
} else if (typeof queryInterval === 'object') {
|
|
174
|
-
query.time = queryInterval
|
|
175
|
-
} else {
|
|
176
|
-
Object.assign(query, {
|
|
177
|
-
$sort: { time: -1, runTime: -1 },
|
|
178
|
-
time: { $lte: now.toISOString() }
|
|
179
|
-
})
|
|
180
|
-
// If we can aggregate then keep track of last element of each aggregation
|
|
181
|
-
if (options.featureId) query.$limit = 1
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
if (!_.isNil(queryLevel)) {
|
|
185
|
-
query.level = queryLevel
|
|
186
|
-
}
|
|
187
|
-
// Any filter/sort query to process ?
|
|
188
|
-
const filterQuery = await this.getFilterQueryForFeatures(options)
|
|
189
|
-
const sortQuery = await this.getSortQueryForFeatures(options)
|
|
190
|
-
// Take care to not erase possible existing sort options
|
|
191
|
-
_.merge(query, filterQuery, sortQuery)
|
|
192
|
-
|
|
193
|
-
return query
|
|
194
|
-
},
|
|
195
|
-
async getFeaturesFromQuery (options, query) {
|
|
196
|
-
const response = await this.$api.getService(options.service).find({ query })
|
|
197
|
-
const features = (response.type === 'FeatureCollection' ? response.features : [response])
|
|
198
|
-
if (typeof options.processor === 'function') {
|
|
199
|
-
features.forEach(feature => options.processor(feature))
|
|
200
|
-
} else if (typeof options.processor === 'string') {
|
|
201
|
-
const compiler = _.template(options.processor)
|
|
202
|
-
features.forEach(feature => compiler({ feature, properties: feature.properties }))
|
|
203
|
-
}
|
|
204
|
-
if (options.transform) {
|
|
205
|
-
transformFeatures(features, options.transform)
|
|
206
|
-
}
|
|
207
|
-
return response
|
|
29
|
+
return features.getFeaturesQuery(options, queryInterval, queryLevel)
|
|
208
30
|
},
|
|
31
|
+
getFeaturesFromQuery: features.getFeaturesFromQuery,
|
|
209
32
|
async getFeatures (options, queryInterval, queryLevel) {
|
|
210
33
|
const query = await this.getFeaturesQuery(options, queryInterval, queryLevel)
|
|
211
34
|
const response = await this.getFeaturesFromQuery(options, query)
|
|
@@ -218,11 +41,17 @@ export const featureService = {
|
|
|
218
41
|
return this.getFeatures(layer, queryInterval)
|
|
219
42
|
},
|
|
220
43
|
getMeasureForFeatureBaseQuery (layer, feature) {
|
|
44
|
+
// We might have a different ID to identify measures related to a timeseries (what is called a chronicle)
|
|
45
|
+
// than measures displayed on a map. For instance mobile measures might appear at different locations,
|
|
46
|
+
// but when selecting one we would like to display the timeseries related to all locations.
|
|
47
|
+
let featureId = layer.chronicleId || layer.featureId
|
|
221
48
|
// Support compound ID
|
|
222
|
-
|
|
223
|
-
|
|
49
|
+
featureId = (Array.isArray(featureId) ? featureId : [featureId])
|
|
50
|
+
const query = featureId.reduce((result, id) =>
|
|
224
51
|
Object.assign(result, { ['properties.' + id]: _.get(feature, 'properties.' + id) }),
|
|
225
52
|
{})
|
|
53
|
+
query.$groupBy = featureId
|
|
54
|
+
return query
|
|
226
55
|
},
|
|
227
56
|
async getMeasureForFeatureQuery (layer, feature, startTime, endTime) {
|
|
228
57
|
const query = await this.getFeaturesQuery(_.merge({
|
|
@@ -253,121 +82,19 @@ export const featureService = {
|
|
|
253
82
|
this.unsetCursor('processing-cursor')
|
|
254
83
|
return probedLocation
|
|
255
84
|
},
|
|
256
|
-
checkFeatures
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
// Removes redundant coordinates
|
|
262
|
-
if (options.redundantCoordinates) {
|
|
263
|
-
features.forEach(feature => clean(feature, { mutate: true }))
|
|
264
|
-
}
|
|
265
|
-
// Filter invalid features
|
|
266
|
-
let kinksFeatures
|
|
267
|
-
if (options.kinks) {
|
|
268
|
-
kinksFeatures = _.remove(features, feature => {
|
|
269
|
-
const type = getType(feature)
|
|
270
|
-
if ((type === 'MultiPolygon') || (type === 'Polygon')) {
|
|
271
|
-
const invalidFeatures = kinks(feature)
|
|
272
|
-
return _.get(invalidFeatures, 'features', []).length > 0
|
|
273
|
-
} else { // No possible self-intersection on points, self-intersections allowed on lines
|
|
274
|
-
return false
|
|
275
|
-
}
|
|
276
|
-
})
|
|
277
|
-
}
|
|
278
|
-
return { kinks: kinksFeatures }
|
|
279
|
-
},
|
|
280
|
-
async createFeatures (geoJson, layerId, chunkSize = 5000, processCallback) {
|
|
281
|
-
if (!layerId) return
|
|
282
|
-
const features = (geoJson.type === 'FeatureCollection' ? geoJson.features : [geoJson])
|
|
283
|
-
features.forEach(feature => {
|
|
284
|
-
// Remove any temporary ID as we will use the one from MongoDB
|
|
285
|
-
delete feature._id
|
|
286
|
-
feature.layer = layerId
|
|
287
|
-
})
|
|
288
|
-
// Single edition mode
|
|
289
|
-
if (features.length === 1) {
|
|
290
|
-
const feature = await this.$api.getService('features').create(features[0])
|
|
291
|
-
return feature
|
|
292
|
-
} else {
|
|
293
|
-
// Create chunks to avoid reaching some limits (upload size, timeout, etc.)
|
|
294
|
-
// We create chunks according to the number of points and not features.
|
|
295
|
-
// Indeed otherwise it would create very different chunks depending on the geometry type
|
|
296
|
-
// (eg a bucket of 1000 polygons can actually contains a lot of points).
|
|
297
|
-
// const chunks = _.chunk(features, chunkSize)
|
|
298
|
-
const chunks = []
|
|
299
|
-
let chunkPoints = 0
|
|
300
|
-
let chunk = []
|
|
301
|
-
features.forEach(feature => {
|
|
302
|
-
const explodedFeature = explode(feature)
|
|
303
|
-
const nbPoints = explodedFeature.features.length
|
|
304
|
-
// Check if current chunk is not full
|
|
305
|
-
if ((chunkPoints + nbPoints) <= chunkSize) {
|
|
306
|
-
chunkPoints += nbPoints
|
|
307
|
-
chunk.push(feature)
|
|
308
|
-
} else {
|
|
309
|
-
// Otherwise push current chunk if not empty
|
|
310
|
-
if (chunk.length > 0) {
|
|
311
|
-
chunks.push(chunk)
|
|
312
|
-
}
|
|
313
|
-
// Then start new chunk
|
|
314
|
-
chunk = [feature]
|
|
315
|
-
chunkPoints = nbPoints
|
|
316
|
-
}
|
|
317
|
-
})
|
|
318
|
-
// Push last chunk
|
|
319
|
-
if (chunk.length > 0) {
|
|
320
|
-
chunks.push(chunk)
|
|
321
|
-
}
|
|
322
|
-
// Write the chunks
|
|
323
|
-
for (let i = 0; i < chunks.length; i++) {
|
|
324
|
-
await this.$api.getService('features').create(chunks[i])
|
|
325
|
-
if (typeof processCallback === 'function') await processCallback(i, chunks[i])
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
},
|
|
329
|
-
async editFeaturesGeometry (geoJson) {
|
|
330
|
-
const features = (geoJson.type === 'FeatureCollection' ? geoJson.features : [geoJson])
|
|
331
|
-
const updatedFeatures = []
|
|
332
|
-
for (let i = 0; i < features.length; i++) {
|
|
333
|
-
const feature = features[i]
|
|
334
|
-
if (feature._id) {
|
|
335
|
-
const updatedFeature = await this.$api.getService('features').patch(feature._id, _.pick(feature, ['geometry']))
|
|
336
|
-
updatedFeatures.push(updatedFeature)
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
return (geoJson.type === 'FeatureCollection' ? Object.assign(geoJson, { features: updatedFeatures }) : updatedFeatures)
|
|
340
|
-
},
|
|
341
|
-
async editFeaturesProperties (geoJson) {
|
|
342
|
-
const features = (geoJson.type === 'FeatureCollection' ? geoJson.features : [geoJson])
|
|
343
|
-
const updatedFeatures = []
|
|
344
|
-
for (let i = 0; i < features.length; i++) {
|
|
345
|
-
const feature = features[i]
|
|
346
|
-
if (feature._id) {
|
|
347
|
-
const updatedFeature = await this.$api.getService('features').patch(feature._id, _.pick(feature, ['properties']))
|
|
348
|
-
updatedFeatures.push(updatedFeature)
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
return (geoJson.type === 'FeatureCollection' ? Object.assign(geoJson, { features: updatedFeatures }) : updatedFeatures)
|
|
352
|
-
},
|
|
353
|
-
async removeFeatures (geoJsonOrLayerId) {
|
|
354
|
-
// Remove all features of a given layer
|
|
355
|
-
if (typeof geoJsonOrLayerId === 'string') {
|
|
356
|
-
await this.$api.getService('features').remove(null, { query: { layer: geoJsonOrLayerId } })
|
|
357
|
-
} else {
|
|
358
|
-
const features = (geoJsonOrLayerId.type === 'FeatureCollection' ? geoJsonOrLayerId.features : [geoJsonOrLayerId])
|
|
359
|
-
for (let i = 0; i < features.length; i++) {
|
|
360
|
-
const feature = features[i]
|
|
361
|
-
if (feature._id) await this.$api.getService('features').remove(feature._id)
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
},
|
|
85
|
+
checkFeatures: features.checkFeatures,
|
|
86
|
+
createFeatures: features.createFeatures,
|
|
87
|
+
editFeaturesGeometry: features.editFeaturesGeometry,
|
|
88
|
+
editFeaturesProperties: features.editFeaturesProperties,
|
|
89
|
+
removeFeatures: features.removeFeatures,
|
|
365
90
|
onFeaturesUpdated (feature) {
|
|
366
91
|
// We only support single feature edition
|
|
367
92
|
if (!getType(feature) || !getGeom(feature)) return
|
|
368
93
|
// Find related layer
|
|
369
94
|
const layer = this.getLayerById(feature.layer)
|
|
370
|
-
if (!layer || !this.isLayerVisible(layer.name)
|
|
95
|
+
if (!layer || !this.isLayerVisible(layer.name)) return
|
|
96
|
+
// Only possible when not edited by default
|
|
97
|
+
if ((typeof this.isLayerEdited === 'function') && this.isLayerEdited(layer)) return
|
|
371
98
|
// As by default we update the whole layer in fetch and replace mode force add/update only mode
|
|
372
99
|
// Can only apply to realtime layers as we need to force a data refresh
|
|
373
100
|
if (typeof this.updateLayer === 'function') this.updateLayer(layer.name, feature, { removeMissing: false })
|
|
@@ -377,7 +104,9 @@ export const featureService = {
|
|
|
377
104
|
if (!getType(feature) || !getGeom(feature)) return
|
|
378
105
|
// Find related layer
|
|
379
106
|
const layer = this.getLayerById(feature.layer)
|
|
380
|
-
if (!layer || !this.isLayerVisible(layer.name)
|
|
107
|
+
if (!layer || !this.isLayerVisible(layer.name)) return
|
|
108
|
+
// Only possible when not edited by default
|
|
109
|
+
if ((typeof this.isLayerEdited === 'function') && this.isLayerEdited(layer)) return
|
|
381
110
|
// Can only apply to realtime layers as we need to force a data refresh
|
|
382
111
|
if (typeof this.updateLayer === 'function') this.updateLayer(layer.name, feature, { remove: true })
|
|
383
112
|
}
|
|
@@ -13,17 +13,11 @@ export const weacast = {
|
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
15
|
methods: {
|
|
16
|
+
getWeacastApi () {
|
|
17
|
+
// As we usually proxy weacast service we use our API unless another client has been specified by override
|
|
18
|
+
return this.$api
|
|
19
|
+
},
|
|
16
20
|
async setupWeacast () {
|
|
17
|
-
// As we usually proxy weacast service we use our API unless another client has been specified
|
|
18
|
-
if (!this.weacastApi) this.weacastApi = this.$api
|
|
19
|
-
// We need to implement time management
|
|
20
|
-
this.weacastApi.setForecastTime = (time) => {
|
|
21
|
-
this.$api.forecastTime = time
|
|
22
|
-
this.$api.emit('forecast-time-changed', time)
|
|
23
|
-
}
|
|
24
|
-
this.weacastApi.getForecastTime = () => {
|
|
25
|
-
return this.$api.forecastTime
|
|
26
|
-
}
|
|
27
21
|
try {
|
|
28
22
|
await this.setupForecastModels()
|
|
29
23
|
} catch (error) {
|
|
@@ -31,12 +25,12 @@ export const weacast = {
|
|
|
31
25
|
}
|
|
32
26
|
},
|
|
33
27
|
async setupForecastModels () {
|
|
34
|
-
if (!this.
|
|
35
|
-
const response = await this.
|
|
28
|
+
if (!this.getWeacastApi()) return
|
|
29
|
+
const response = await this.getWeacastApi().getService('forecasts').find()
|
|
36
30
|
// Required to use splice when modifying objects inside an array to make it reactive
|
|
37
31
|
this.forecastModels.splice(0, this.forecastModels.length, ...response.data)
|
|
38
32
|
// store forecast models on the weacast api object too (useful in the weacast grid source)
|
|
39
|
-
this.
|
|
33
|
+
this.getWeacastApi().models = this.forecastModels
|
|
40
34
|
// Add 'virtual' actions used to trigger the layer/filters
|
|
41
35
|
this.forecastModels.forEach(forecastModel => {
|
|
42
36
|
forecastModel.actions = [{ id: 'toggle', handler: () => this.setForecastModel(forecastModel) }]
|
|
@@ -98,7 +92,7 @@ export const weacast = {
|
|
|
98
92
|
return (tokens.length === 0) || !_.isFinite(_.toNumber(tokens[tokens.length - 1]))
|
|
99
93
|
})
|
|
100
94
|
}
|
|
101
|
-
const response = await this.
|
|
95
|
+
const response = await this.getWeacastApi().getService('probes')
|
|
102
96
|
.create({
|
|
103
97
|
forecast: this.forecastModel.name,
|
|
104
98
|
elements
|
|
@@ -119,7 +113,7 @@ export const weacast = {
|
|
|
119
113
|
if (this.probe && (this.probe.name === name) && (this.probe.forecast === this.forecastModel.name)) {
|
|
120
114
|
return this.probe
|
|
121
115
|
}
|
|
122
|
-
const results = await this.
|
|
116
|
+
const results = await this.getWeacastApi().getService('probes').find({
|
|
123
117
|
query: {
|
|
124
118
|
name,
|
|
125
119
|
forecast: this.forecastModel.name,
|
|
@@ -158,7 +152,7 @@ export const weacast = {
|
|
|
158
152
|
const windSpeed = (this.forecastLevel ? `windSpeed-${this.forecastLevel}` : 'windSpeed')
|
|
159
153
|
elements = elements.concat([windDirection, windSpeed])
|
|
160
154
|
|
|
161
|
-
const results = await this.
|
|
155
|
+
const results = await this.getWeacastApi().getService('probe-results').find({
|
|
162
156
|
query: {
|
|
163
157
|
probeId: this.probe._id,
|
|
164
158
|
forecastTime: {
|
|
@@ -180,7 +174,7 @@ export const weacast = {
|
|
|
180
174
|
return probedLocation
|
|
181
175
|
},
|
|
182
176
|
onCurrentForecastTimeChanged (time) {
|
|
183
|
-
if (this.
|
|
177
|
+
if (this.getWeacastApi()) this.getWeacastApi().setForecastTime(time)
|
|
184
178
|
},
|
|
185
179
|
onWeacastSelectedLevelChanged (level) {
|
|
186
180
|
// Used when selectable levels are cleared
|
package/map/client/pixi-utils.js
CHANGED
|
@@ -198,7 +198,7 @@ function buildColorMapShaderCode (thresholds, colors, interpolate) {
|
|
|
198
198
|
return code
|
|
199
199
|
}
|
|
200
200
|
|
|
201
|
-
export function buildColorMapShaderCodeFromDomain (domain, colors, invertScale) {
|
|
201
|
+
export function buildColorMapShaderCodeFromDomain (domain, colors, invertScale = false) {
|
|
202
202
|
// expects N values in domain an N colors
|
|
203
203
|
// color0 .. color1 .. color2 ..... colorn-1 .. colorn
|
|
204
204
|
// | | | | |
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import _ from 'lodash'
|
|
2
|
+
import logger from 'loglevel'
|
|
3
|
+
import config from 'config'
|
|
4
|
+
import { createClient } from '../../core/client/api.js'
|
|
5
|
+
import { setupApi } from './init.js'
|
|
6
|
+
|
|
7
|
+
export const Planets = {
|
|
8
|
+
|
|
9
|
+
initialize () {
|
|
10
|
+
this.planets = {}
|
|
11
|
+
},
|
|
12
|
+
|
|
13
|
+
async connect (name, options = {}) {
|
|
14
|
+
_.defaults(options, {
|
|
15
|
+
origin: window.location.origin,
|
|
16
|
+
apiPath: config.apiPath,
|
|
17
|
+
apiJwt: `${name}-jwt`,
|
|
18
|
+
gatewayJwt: `${name}-gateway-jwt`,
|
|
19
|
+
apiTimeout: config.apiTimeout,
|
|
20
|
+
transport: config.transport,
|
|
21
|
+
appName: `${name}`,
|
|
22
|
+
renewJwt: false
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
const client = createClient(options)
|
|
26
|
+
setupApi.bind(client)(options)
|
|
27
|
+
client.on('authenticated', (data) => {
|
|
28
|
+
// Store API gateway token if any
|
|
29
|
+
if (data.gatewayToken) client.get('storage').setItem(options.gatewayJwt, data.gatewayToken)
|
|
30
|
+
})
|
|
31
|
+
client.on('logout', (data) => {
|
|
32
|
+
// Remove API gateway token if any
|
|
33
|
+
client.get('storage').removeItem(options.gatewayJwt)
|
|
34
|
+
})
|
|
35
|
+
const accessToken = await client.get('storage').getItem(options.apiJwt)
|
|
36
|
+
if (!accessToken) {
|
|
37
|
+
logger.error(new Error(`You must set planet ${name} token first`))
|
|
38
|
+
return
|
|
39
|
+
}
|
|
40
|
+
await client.authenticate({
|
|
41
|
+
strategy: 'jwt',
|
|
42
|
+
accessToken
|
|
43
|
+
})
|
|
44
|
+
this.planets[name] = client
|
|
45
|
+
return client
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
async disconnect (name) {
|
|
49
|
+
await this.planets[name].logout()
|
|
50
|
+
delete this.planets[name]
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
isConnected (name) {
|
|
54
|
+
return !_.isNil(this.planets[name])
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
get (name) {
|
|
58
|
+
if (!this.planets[name]) logger.error(new Error(`You must connect to planet ${name} first`))
|
|
59
|
+
else return this.planets[name]
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
// Register an existing planet
|
|
63
|
+
set (name, client) {
|
|
64
|
+
this.planets[name] = client
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -1,3 +1,9 @@
|
|
|
1
1
|
export * from './utils.js'
|
|
2
|
+
export * from './utils.capture.js'
|
|
3
|
+
export * from './utils.catalog.js'
|
|
4
|
+
export * from './utils.features.js'
|
|
5
|
+
export * from './utils.layers.js'
|
|
2
6
|
export * from './utils.location.js'
|
|
7
|
+
export * from './utils.project.js'
|
|
3
8
|
export * from './utils.schema.js'
|
|
9
|
+
export * from './utils.style.js'
|