@kalisio/kdk 2.2.0 → 2.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/core/api/application.js +2 -3
  2. package/core/client/components/KModal.vue +1 -1
  3. package/core/client/components/form/KForm.vue +12 -3
  4. package/core/client/components/media/KColorScale.vue +1 -1
  5. package/core/client/exporter.js +14 -8
  6. package/core/client/mixins/mixin.base-field.js +4 -1
  7. package/core/client/units.js +3 -3
  8. package/core/client/utils/utils.shapes.js +1 -1
  9. package/extras/icons/wind-speed-0.svg +8 -0
  10. package/extras/icons/wind-speed-10.svg +8 -0
  11. package/extras/icons/wind-speed-100.svg +12 -0
  12. package/extras/icons/wind-speed-105.svg +13 -0
  13. package/extras/icons/wind-speed-15.svg +9 -0
  14. package/extras/icons/wind-speed-20.svg +9 -0
  15. package/extras/icons/wind-speed-25.svg +10 -0
  16. package/extras/icons/wind-speed-30.svg +10 -0
  17. package/extras/icons/wind-speed-35.svg +11 -0
  18. package/extras/icons/wind-speed-40.svg +11 -0
  19. package/extras/icons/wind-speed-45.svg +12 -0
  20. package/extras/icons/wind-speed-5.svg +9 -0
  21. package/extras/icons/wind-speed-50.svg +9 -0
  22. package/extras/icons/wind-speed-55.svg +10 -0
  23. package/extras/icons/wind-speed-60.svg +10 -0
  24. package/extras/icons/wind-speed-65.svg +11 -0
  25. package/extras/icons/wind-speed-70.svg +11 -0
  26. package/extras/icons/wind-speed-75.svg +12 -0
  27. package/extras/icons/wind-speed-80.svg +12 -0
  28. package/extras/icons/wind-speed-85.svg +13 -0
  29. package/extras/icons/wind-speed-90.svg +13 -0
  30. package/extras/icons/wind-speed-95.svg +14 -0
  31. package/map/api/hooks/hooks.query.js +34 -11
  32. package/map/client/cesium/utils/index.js +2 -1
  33. package/map/client/cesium/utils/utils.features.js +8 -0
  34. package/map/client/cesium/utils/utils.style.js +4 -4
  35. package/map/client/components/KFeatureEditor.vue +2 -3
  36. package/map/client/components/KLayerStyleForm.vue +32 -24
  37. package/map/client/components/KTimezoneMap.vue +3 -4
  38. package/map/client/components/catalog/KConnectLayer.vue +14 -10
  39. package/map/client/components/legend/KColorScaleLegend.vue +6 -2
  40. package/map/client/components/legend/KLayerLegend.vue +13 -3
  41. package/map/client/components/legend/KLegend.vue +12 -10
  42. package/map/client/components/location/KLocationCardSection.vue +8 -4
  43. package/map/client/components/widget/KTimeSeries.vue +0 -6
  44. package/map/client/composables/catalog.js +17 -2
  45. package/map/client/composables/highlight.js +9 -30
  46. package/map/client/composables/probe.js +4 -1
  47. package/map/client/composables/weather.js +3 -3
  48. package/map/client/i18n/map_en.json +4 -0
  49. package/map/client/i18n/map_fr.json +4 -0
  50. package/map/client/leaflet/ShapeMarker.js +23 -12
  51. package/map/client/leaflet/TiledFeatureLayer.js +38 -8
  52. package/map/client/leaflet/TiledMeshLayer.js +3 -1
  53. package/map/client/leaflet/utils/utils.style.js +7 -3
  54. package/map/client/mixins/globe/mixin.base-globe.js +29 -16
  55. package/map/client/mixins/globe/mixin.geojson-layers.js +7 -0
  56. package/map/client/mixins/map/mixin.base-map.js +40 -17
  57. package/map/client/mixins/map/mixin.geojson-layers.js +7 -3
  58. package/map/client/mixins/mixin.activity.js +5 -1
  59. package/map/client/planets.js +8 -0
  60. package/map/client/utils/utils.catalog.js +17 -0
  61. package/map/client/utils/utils.layers.js +2 -1
  62. package/map/client/utils/utils.schema.js +0 -28
  63. package/map/client/utils/utils.style.js +12 -0
  64. package/map/common/wms-utils.js +8 -3
  65. package/package.json +1 -1
  66. package/test/client/core/layout.js +0 -1
  67. package/test/client/map/catalog.js +1 -1
  68. package/test/api/core/test-log-2023-12-19.log +0 -7
  69. package/test/api/core/test-log-2024-01-04.log +0 -14
  70. package/test/api/map/test-log-2023-11-24.log +0 -121
  71. package/test/api/map/test-log-2023-12-12.log +0 -29
  72. package/test/api/map/test-log-2023-12-13.log +0 -5
  73. package/test/api/map/test-log-2024-01-04.log +0 -2
  74. package/test/api/map/test-log-2024-01-11.log +0 -1
  75. package/test/api/map/test-log-2024-01-25.log +0 -19
@@ -555,18 +555,22 @@ export default {
555
555
  this.fillBaseStyle(values)
556
556
  // Clustering
557
557
  this.fillClusteringStyle(values)
558
- // Points
559
- await this.fillIconStyles(values)
560
- // Lines
561
- await this.fillLineStyles(values)
562
- // Polygons
563
- await this.fillPolygonStyles(values)
564
- // Popup
565
- await this.fillPopupStyles(values)
566
- // Tooltip
567
- await this.fillTooltipStyles(values)
568
- // Infobox
569
- await this.fillInfoBoxStyles(values)
558
+ if (this.isVectorLayer) {
559
+ // Points
560
+ await this.fillIconStyles(values)
561
+ // Lines
562
+ await this.fillLineStyles(values)
563
+ // Polygons
564
+ await this.fillPolygonStyles(values)
565
+ }
566
+ if (this.hasFeatureSchema) {
567
+ // Popup
568
+ await this.fillPopupStyles(values)
569
+ // Tooltip
570
+ await this.fillTooltipStyles(values)
571
+ // Infobox
572
+ await this.fillInfoBoxStyles(values)
573
+ }
570
574
  },
571
575
  validate () {
572
576
  const values = this.values()
@@ -640,18 +644,22 @@ export default {
640
644
  _.merge(values, this.baseValues())
641
645
  // Clustering
642
646
  _.merge(values, this.clusteringValues())
643
- // Point style
644
- _.mergeWith(values, this.iconStylesValues(), customizer)
645
- // Line style
646
- _.mergeWith(values, this.lineStylesValues(), customizer)
647
- // Polygon style
648
- _.mergeWith(values, this.polygonStylesValues(), customizer)
649
- // Popup style
650
- _.merge(values, this.popupStylesValues())
651
- // Tooltip style
652
- _.merge(values, this.tooltipStylesValues())
653
- // Infobox style
654
- _.merge(values, this.infoBoxStylesValues())
647
+ if (this.isVectorLayer) {
648
+ // Point style
649
+ _.mergeWith(values, this.iconStylesValues(), customizer)
650
+ // Line style
651
+ _.mergeWith(values, this.lineStylesValues(), customizer)
652
+ // Polygon style
653
+ _.mergeWith(values, this.polygonStylesValues(), customizer)
654
+ }
655
+ if (this.hasFeatureSchema) {
656
+ // Popup style
657
+ _.merge(values, this.popupStylesValues())
658
+ // Tooltip style
659
+ _.merge(values, this.tooltipStylesValues())
660
+ // Infobox style
661
+ _.merge(values, this.infoBoxStylesValues())
662
+ }
655
663
  return values
656
664
  },
657
665
  async onAddIconStyle (property) {
@@ -11,7 +11,6 @@ import { api, utils as kCoreUtils } from '../../../core/client'
11
11
  import * as mapMixins from '../mixins/map'
12
12
  import * as mixins from '../mixins'
13
13
  import { useCatalog, useCurrentActivity } from '../composables'
14
- import { createMarkerFromPointStyle } from '../leaflet/utils/index.js'
15
14
  import meta from 'moment-timezone/data/meta/latest.json'
16
15
 
17
16
  // Convert timezones to GeoJson
@@ -73,9 +72,9 @@ export default {
73
72
  this.addLayer(layers[0])
74
73
  }
75
74
  },
76
- getTimezoneMarker (feature, latlng) {
75
+ getTimezoneMarker (feature, options) {
77
76
  const isSelected = (this.timezone === feature.properties.name)
78
- return createMarkerFromPointStyle(latlng, {
77
+ return {
79
78
  shape: 'circle',
80
79
  color: 'primary',
81
80
  opacity: isSelected ? 1 : 0.5,
@@ -84,7 +83,7 @@ export default {
84
83
  color: 'dark',
85
84
  width: isSelected ? 3 : 1
86
85
  }
87
- })
86
+ }
88
87
  },
89
88
  getTimezoneTooltip (feature, layer) {
90
89
  const name = _.get(feature, 'properties.name')
@@ -239,11 +239,13 @@ export default {
239
239
  newLayer.cesium.parameters.styles = style
240
240
  // add legend url if available in the picked style
241
241
  const legendUrl = _.get(this.layer.styles, [style, 'legend'])
242
- if (legendUrl) newLayer.legend = {
243
- type: 'image',
244
- label: newLayer.name,
245
- content: {
246
- src: legendUrl
242
+ if (legendUrl) {
243
+ newLayer.legend = {
244
+ type: 'image',
245
+ label: newLayer.name,
246
+ content: {
247
+ src: legendUrl
248
+ }
247
249
  }
248
250
  }
249
251
  }
@@ -303,11 +305,13 @@ export default {
303
305
 
304
306
  // add legend url if available in the style
305
307
  const legendUrl = _.get(this.layer.styles, [style, 'legend'])
306
- if (legendUrl) newLayer.legend = {
307
- type: 'image',
308
- label: newLayer.name,
309
- content: {
310
- src: legendUrl
308
+ if (legendUrl) {
309
+ newLayer.legend = {
310
+ type: 'image',
311
+ label: newLayer.name,
312
+ content: {
313
+ src: legendUrl
314
+ }
311
315
  }
312
316
  }
313
317
  } else if (this.service.protocol === 'TMS') {
@@ -4,10 +4,10 @@
4
4
  :label="label"
5
5
  >
6
6
  <!-- content -->
7
- <div class="q-pa-sm">
7
+ <div class="q-pa-xs">
8
8
  <KColorScale
9
9
  v-bind="content"
10
- style="height: 44px;"
10
+ :style="`height: ${height}px;`"
11
11
  />
12
12
  </div>
13
13
  </KLegendRenderer>
@@ -26,6 +26,10 @@ defineProps({
26
26
  content: {
27
27
  type: Object,
28
28
  default: () => null
29
+ },
30
+ height: {
31
+ type: Number,
32
+ default: () => 40
29
33
  }
30
34
  })
31
35
  </script>
@@ -45,15 +45,25 @@ const legends = computed(() => {
45
45
  layerLegends.forEach(legend => {
46
46
  const minZoom = _.get(legend, 'minZoom', _.get(props.layer, `${props.engine}.minZoom`, 0))
47
47
  const maxZoom = _.get(legend, 'maxZoom', _.get(props.layer, `${props.engine}.maxZoom`, 99))
48
- if (props.zoom >= minZoom && props.zoom <= maxZoom) {
48
+ // Check for valid number on min/max zoom level as we might set it to false/null to indicate
49
+ // there is none and we should use defaults
50
+ const hasMinZoom = _.isNumber(minZoom)
51
+ const hasMaxZoom = _.isNumber(maxZoom)
52
+ const inMinZoomRange = (hasMinZoom ? props.zoom >= minZoom : true)
53
+ const inMaxZoomRange = (hasMaxZoom ? props.zoom <= maxZoom : true)
54
+ if (inMinZoomRange && inMaxZoomRange) {
49
55
  logger.debug(`[KDK] Register '${props.layer.name}'`)
50
56
  const renderer = props.renderers[legend.type]
51
57
  if (!renderer) {
52
58
  logger.warn(`[KDK] Cannot find any renderer for the layer's legend of type of ${legend.type}`)
53
59
  return
54
60
  }
55
- legend.renderer = coreUtils.loadComponent(renderer)
56
- result.push(legend)
61
+ result.push({
62
+ layer: props.layer,
63
+ label: legend.label,
64
+ content: legend.content,
65
+ renderer: coreUtils.loadComponent(renderer)
66
+ })
57
67
  }
58
68
  })
59
69
  return result
@@ -21,7 +21,7 @@
21
21
  </q-item-section>
22
22
  <!-- Helper -->
23
23
  <q-item-section v-if="sublegend.helper" side >
24
- <k-action
24
+ <KAction
25
25
  :id="sublegend.name + '-helper'"
26
26
  color="primary"
27
27
  :propagate="false"
@@ -51,16 +51,12 @@ import _ from 'lodash'
51
51
  import logger from 'loglevel'
52
52
  import sift from 'sift'
53
53
  import { ref, computed, watch } from 'vue'
54
- import { i18n, api } from '../../../../core/client'
55
- import { useCurrentActivity } from '../../composables'
54
+ import { i18n, Store } from '../../../../core/client'
55
+ import { useCurrentActivity, useCatalog } from '../../composables'
56
56
  import KLayerLegend from './KLayerLegend.vue'
57
57
 
58
58
  // Props
59
59
  const props = defineProps({
60
- contextOrId: {
61
- type: String,
62
- default: undefined
63
- },
64
60
  headerClass: {
65
61
  type: String,
66
62
  default: 'bg-grey-3 text-weight-regular'
@@ -90,6 +86,11 @@ const props = defineProps({
90
86
  }
91
87
  })
92
88
 
89
+ // Use global catalog
90
+ const { getSublegends } = useCatalog()
91
+ // Use local catalog if any
92
+ const { getSublegends: getContextSublegends } = useCatalog({ context: Store.get('context') })
93
+
93
94
  // Data
94
95
  const { CurrentActivity } = useCurrentActivity({ selection: false, probe: false })
95
96
  const sublegends = ref([])
@@ -150,9 +151,10 @@ function getHelperDialog (helper) {
150
151
  watch([() => props.sublegends, () => props.sublegendsFromCatalog], async () => {
151
152
  // Retrieve the legends from catalog if required
152
153
  if (props.sublegendsFromCatalog) {
153
- const catalogService = api.getService('catalog', props.contextOrId)
154
- const response = await catalogService.find({ query: { type: 'Sublegend' } })
155
- sublegends.value = response.data
154
+ sublegends.value = await getSublegends()
155
+ if (Store.get('context')) {
156
+ sublegends.value = sublegends.value.concat(await getContextSublegends())
157
+ }
156
158
  } else {
157
159
  sublegends.value = []
158
160
  }
@@ -32,10 +32,10 @@
32
32
  </template>
33
33
 
34
34
  <script setup>
35
- import { toRefs } from 'vue'
35
+ import { ref, watch } from 'vue'
36
36
  import KLocationMap from './KLocationMap.vue'
37
37
 
38
- // props
38
+ // Props
39
39
  const props = defineProps({
40
40
  location: {
41
41
  type: Object,
@@ -55,7 +55,11 @@ const props = defineProps({
55
55
  }
56
56
  })
57
57
 
58
- // data
59
- const { location: feature } = toRefs(props)
58
+ // Data
59
+ const feature = ref(null)
60
60
 
61
+ // Watch
62
+ watch(() => props.location, (value) => {
63
+ feature.value = value
64
+ }, { immediate: true })
61
65
  </script>
@@ -374,12 +374,6 @@ export default {
374
374
  const feature = (isWeatherProbe
375
375
  ? this.getProbedLocationForecastAtCurrentTime(this.probedLocation)
376
376
  : this.getProbedLocationMeasureAtCurrentTime(this.probedLocation))
377
- if (isWeatherProbe) {
378
- const tooltip = this.getForecastAsHtml(feature)
379
- if (tooltip) _.set(feature, 'properties.tooltip', { html: `<b>${tooltip}</b>` })
380
- const options = this.getWindBarbOptions(feature)
381
- if (options) _.set(feature, 'properties.icon', { type: 'WindBarb.icon', options })
382
- }
383
377
  this.highlight(feature, this.layer)
384
378
  },
385
379
  onZoomRestored () {
@@ -10,6 +10,7 @@ export function useCatalog (options = {}) {
10
10
  // Default filter queries
11
11
  layers: {},
12
12
  categories: {},
13
+ sublegends: {},
13
14
  views: {},
14
15
  // Default to global catalog
15
16
  context: '',
@@ -20,6 +21,7 @@ export function useCatalog (options = {}) {
20
21
  // Data
21
22
  const layers = ref([])
22
23
  const categories = ref([])
24
+ const sublegends = ref([])
23
25
  const views = ref([])
24
26
 
25
27
  // Computed
@@ -27,9 +29,12 @@ export function useCatalog (options = {}) {
27
29
  const orphanLayers = computed(() => catalog.getOrphanLayers(layers.value, layersByCategory.value))
28
30
 
29
31
  // Functions
30
- async function getLayers () {
32
+ async function getLayers (filterQuery = {}) {
33
+ const query = Object.assign({},
34
+ options.project ? Object.assign(getCatalogProjectQuery(options.project), options.layers) : options.layers,
35
+ filterQuery)
31
36
  layers.value = await catalog.getLayers({
32
- query: options.project ? Object.assign(getCatalogProjectQuery(options.project), options.layers) : options.layers,
37
+ query,
33
38
  context: options.context,
34
39
  planetApi: options.planetApi
35
40
  })
@@ -43,6 +48,14 @@ export function useCatalog (options = {}) {
43
48
  })
44
49
  return categories.value
45
50
  }
51
+ async function getSublegends () {
52
+ sublegends.value = await catalog.getSublegends({
53
+ query: options.sublegends,
54
+ context: options.context,
55
+ planetApi: options.planetApi
56
+ })
57
+ return sublegends.value
58
+ }
46
59
  async function getViews () {
47
60
  views.value = await catalog.getViews({
48
61
  query: options.project ? Object.assign(getCatalogProjectQuery(options.project), options.views) : options.views,
@@ -56,11 +69,13 @@ export function useCatalog (options = {}) {
56
69
  return {
57
70
  layers,
58
71
  categories,
72
+ sublegends,
59
73
  layersByCategory,
60
74
  orphanLayers,
61
75
  views,
62
76
  getLayers,
63
77
  getCategories,
78
+ getSublegends,
64
79
  getViews
65
80
  }
66
81
  }
@@ -2,7 +2,7 @@ import _ from 'lodash'
2
2
  import config from 'config'
3
3
  import bbox from '@turf/bbox'
4
4
  import bboxPolygon from '@turf/bbox-polygon'
5
- import { uid, getCssVar } from 'quasar'
5
+ import { uid } from 'quasar'
6
6
  import { unref, onUnmounted } from 'vue'
7
7
  import { getFeatureId, getFeatureStyleType } from '../utils.js'
8
8
  import * as composables from '../../../core/client/composables/index.js'
@@ -60,15 +60,6 @@ export function useHighlight (name, options = {}) {
60
60
  function getHighlight (feature, layer) {
61
61
  return get(getHighlightId(feature, layer))
62
62
  }
63
- function convertColors (highlight) {
64
- // Convert from quasar color palette to actual color
65
- _.forOwn(highlight.properties, (value, key) => {
66
- if (key.endsWith('-color')) {
67
- const color = getCssVar(value)
68
- if (color) _.set(highlight, `properties.${key}`, color)
69
- }
70
- })
71
- }
72
63
  function highlight (feature, layer, selected = true) {
73
64
  const highlightId = getHighlightId(feature, layer)
74
65
  // Define default highlight feature
@@ -90,9 +81,11 @@ export function useHighlight (name, options = {}) {
90
81
  Object.assign(highlight, bboxPolygon(bbox(highlight)))
91
82
  }
92
83
  // Assign style
93
- if (activity.is2D()) {
94
- if (selected) {
95
- let highlightStyle = _.get(config, `engines.${activity.engine}.style.selection`)[getFeatureStyleType(highlight)]
84
+ if (selected) {
85
+ // Do not alter config object
86
+ const selectionStylePath = `engines.${activity.engine}.style.selection.${getFeatureStyleType(highlight)}`
87
+ let highlightStyle = _.cloneDeep(_.get(config, selectionStylePath, {}))
88
+ if (activity.is2D()) {
96
89
  // adapt the size to the marker using feature style
97
90
  let size = _.get(feature, 'style.size')
98
91
  if (size) {
@@ -112,28 +105,14 @@ export function useHighlight (name, options = {}) {
112
105
  }
113
106
  }
114
107
  if (size) Object.assign(highlightStyle, { size: [size[0] + 8, size[1] + 8] })
115
- Object.assign(highlight, { style: highlightStyle })
116
- } else {
117
- // retrieve feature sytle
118
- Object.assign(highlight, { style: feature.style })
119
108
  }
109
+ Object.assign(highlight, { style: highlightStyle })
120
110
  } else {
121
- // 3D TODO: to be updated when swithing globe to new style
122
- Object.assign(highlight, {
123
- properties: {
124
- geodesic: true, // In 3D we use a circle on ground
125
- radius: 1000,
126
- 'stroke-color': getCssVar('secondary'),
127
- 'stroke-width': 3,
128
- 'fill-color': getCssVar('secondary'),
129
- 'fill-opacity': 0.5
130
- }
131
- }, options)
111
+ // retrieve feature sytle
112
+ Object.assign(highlight, { style: feature.style })
132
113
  }
133
114
  // Add additional information provided by feature, if any, for custom styling
134
115
  _.merge(highlight, _.omit(feature, ['geometry', 'style']))
135
- // TODO: to be removed when swithing globe to new style
136
- if (activity.is3D()) convertColors(highlight)
137
116
  set(highlightId, highlight)
138
117
  updateHighlightsLayer()
139
118
  return highlight
@@ -67,7 +67,10 @@ export function useProbe (name, options = {}) {
67
67
 
68
68
  let lastClickedPosition
69
69
  function onClicked (layer, event) {
70
- if (!isProbing()) return
70
+ if (!isProbing()) {
71
+ if (get('item')) clearProbe()
72
+ return
73
+ }
71
74
  // FIXME: For some layers, eg based on path, we get a first click with the layer as target
72
75
  // then a second click with the map as target, we need to filter the later for selection
73
76
  const containerPosition = _.get(event, 'containerPoint')
@@ -185,7 +185,7 @@ export function useWeather (options = {}) {
185
185
  forceDir: true
186
186
  }
187
187
  }
188
- function createWindBarbMarker (feature) {
188
+ function createWindBarbIcon (feature) {
189
189
  const options = getWindBarbOptions(feature)
190
190
  return (options ? new L.WindBarb.Icon(getWindBarbOptions(feature)) : null)
191
191
  }
@@ -195,7 +195,7 @@ export function useWeather (options = {}) {
195
195
  }
196
196
  function getProbedLocationForecastMarker (feature, latlng, options) {
197
197
  // Use wind barbs on probed features
198
- const icon = createWindBarbMarker(feature)
198
+ const icon = createWindBarbIcon(feature)
199
199
  return (icon ? L.marker(latlng, { icon }) : null)
200
200
  }
201
201
 
@@ -205,7 +205,7 @@ export function useWeather (options = {}) {
205
205
  isWeatherProbe,
206
206
  getForecastAsHtml,
207
207
  getWindBarbOptions,
208
- createWindBarbMarker,
208
+ createWindBarbIcon,
209
209
  getProbedLocationForecastAtCurrentTime,
210
210
  getProbedLocationForecastTooltip,
211
211
  getProbedLocationForecastMarker
@@ -35,6 +35,10 @@
35
35
  "REMOVING_LABEL": "Please wait while removing layer data",
36
36
  "REMOVE_DIALOG_TITLE": "Remove {layer} ?",
37
37
  "REMOVE_DIALOG_MESSAGE": "Are you sure you want to remove <b>{layer}</b> from your workspace ?"
38
+ },
39
+ "capture": {
40
+ "CAPTURE_PDF_FILE": "capture_{time}.pdf",
41
+ "CAPTURE_IMAGE_FILE": "capture_{time}.png"
38
42
  }
39
43
  },
40
44
  "mixins": {
@@ -35,6 +35,10 @@
35
35
  "REMOVING_LABEL": "Veuillez patienter pendant la suppression des données",
36
36
  "REMOVE_DIALOG_TITLE": "Supprimer {layer} ?",
37
37
  "REMOVE_DIALOG_MESSAGE": "Etes vous sûr de vouloir supprimer {layer} de votre espace de travail ?"
38
+ },
39
+ "capture": {
40
+ "CAPTURE_PDF_FILE": "impression_{time}.pdf",
41
+ "CAPTURE_IMAGE_FILE": "impression_{time}.png"
38
42
  }
39
43
  },
40
44
  "mixins": {
@@ -7,22 +7,33 @@ export const ShapeMarker = L.Marker.extend({
7
7
 
8
8
  // Constructor
9
9
  initialize (latlng, options) {
10
- const shape = coreUtils.createShape(options)
11
- if (shape) {
10
+ // Forward extra options for different purposes, i.e. clustering, panes, ...
11
+ const markerOptions = _.get(options, 'options', {})
12
+ // We allow to specify panes directly at root level
13
+ Object.assign(markerOptions, _.pick(options, ['pane', 'shadowPane']))
14
+ if (options.icon instanceof L.Icon) { // We allow to directly provide the icon
12
15
  L.Marker.prototype.initialize.call(this, latlng, {
13
- icon: L.divIcon({
14
- iconSize: [shape.width, shape.height],
15
- iconAnchor: this.getAnchor(shape.anchor, shape.size),
16
- popupAnchor: [0, -shape.height / 2],
17
- html: shape.html,
18
- className: ''
19
- }),
20
- // forward extra options for different purposes, i.e. clustering
21
- ..._.get(options, 'options', {})
16
+ icon: options.icon,
17
+ ...markerOptions
22
18
  })
23
19
  } else {
24
- logger.warn(`[KDK] unable to create the shape with the options: ${options}` )
20
+ const shape = coreUtils.createShape(options)
21
+ if (shape) {
22
+ L.Marker.prototype.initialize.call(this, latlng, {
23
+ icon: L.divIcon({
24
+ iconSize: [shape.size.width, shape.size.height],
25
+ iconAnchor: this.getAnchor(shape.anchor, shape.size),
26
+ popupAnchor: [0, -shape.size.height / 2],
27
+ html: shape.html,
28
+ className: ''
29
+ }),
30
+ ...markerOptions
31
+ })
32
+ } else {
33
+ logger.warn(`[KDK] unable to create the shape with the options: ${options}` )
34
+ }
25
35
  }
36
+
26
37
  },
27
38
 
28
39
  getAnchor (position, size) {
@@ -18,6 +18,7 @@ const TiledFeatureLayer = L.GridLayer.extend({
18
18
  this.userIsDragging = false
19
19
  this.userIsZooming = false
20
20
  this.pendingStationUpdates = []
21
+ this.pendingRequests = []
21
22
 
22
23
  this.getFeatureKey = (feature) => {
23
24
  return getFeatureId(feature, this.layer)
@@ -60,18 +61,18 @@ const TiledFeatureLayer = L.GridLayer.extend({
60
61
  getEvents () {
61
62
  const events = L.GridLayer.prototype.getEvents.call(this)
62
63
 
63
- // dragstart sets userIsDragging flag
64
- const onDragStart = events.dragstart
65
- events.dragstart = (event) => {
64
+ // movestart sets userIsDragging flag
65
+ const onMoveStart = events.movestart
66
+ events.movestart = (event) => {
66
67
  this.userIsDragging = true
67
- if (onDragStart) onDragStart.call(this, event)
68
+ if (onMoveStart) onMoveStart.call(this, event)
68
69
  }
69
70
 
70
- // dragstart clears userIsDragging flag
71
- const onDragEnd = events.dragend
72
- events.dragend = (event) => {
71
+ // moveend clears userIsDragging flag
72
+ const onMoveEnd = events.moveend
73
+ events.moveend = (event) => {
73
74
  this.userIsDragging = false
74
- if (onDragEnd) onDragEnd.call(this, event)
75
+ if (onMoveEnd) onMoveEnd.call(this, event)
75
76
  }
76
77
 
77
78
  // zoomstart records zoomStartLevel
@@ -269,6 +270,9 @@ const TiledFeatureLayer = L.GridLayer.extend({
269
270
  _update (center) {
270
271
  L.GridLayer.prototype._update.call(this)
271
272
 
273
+ // cleanup pending requests, ie only keep those still pending
274
+ this.pendingRequests = this.pendingRequests.filter((r) => r.status.pending)
275
+
272
276
  // No update while dragging
273
277
  if (this.userIsDragging) return
274
278
 
@@ -370,7 +374,13 @@ const TiledFeatureLayer = L.GridLayer.extend({
370
374
  if (this.enableDebug) tile.div.innerHTML += '</br>features request issued'
371
375
  })
372
376
 
377
+ // keep track of pending request
378
+ promise.status = { cancelled: false, pending: true }
379
+ this.pendingRequests.push(promise)
380
+
373
381
  promise.then((data) => {
382
+ if (promise.status.cancelled) return
383
+
374
384
  // Gather all associated tiles, taking children tiles into account
375
385
  const allTiles = [r.tiles]
376
386
  r.tiles.forEach((tile) => { if (tile.featuresChildren.length) allTiles.push(tile.featuresChildren) })
@@ -424,6 +434,8 @@ const TiledFeatureLayer = L.GridLayer.extend({
424
434
 
425
435
  if (this.enableDebug) logger.debug(`TiledFeatureLayer: allFeatures is ${this.allFeatures.size} long`)
426
436
  }).catch((err) => {
437
+ if (promise.status.cancelled) return
438
+
427
439
  // Gather all associated tiles, taking children tiles into account
428
440
  const allTiles = [r.tiles]
429
441
  r.tiles.forEach((tile) => { if (tile.featuresChildren.length) allTiles.push(tile.featuresChildren) })
@@ -440,6 +452,8 @@ const TiledFeatureLayer = L.GridLayer.extend({
440
452
  })
441
453
 
442
454
  if (this.enableDebug) logger.debug(`TiledFeatureLayer: allFeatures is ${this.allFeatures.size} long`)
455
+ }).finally(() => {
456
+ promise.status.pending = false
443
457
  })
444
458
  })
445
459
 
@@ -455,9 +469,15 @@ const TiledFeatureLayer = L.GridLayer.extend({
455
469
  if (this.enableDebug) tile.div.innerHTML += '</br>measures request issued'
456
470
  })
457
471
 
472
+ // keep track of pending request
473
+ promise.status = { cancelled: false, pending: true }
474
+ this.pendingRequests.push(promise)
475
+
458
476
  // When stations are fetched, we flag them with a 'measureRequestIssued' property that we
459
477
  // may use in dynamic styling
460
478
  Promise.all(stationPromises).then(() => {
479
+ if (promise.status.cancelled) return
480
+
461
481
  const flaggedStations = []
462
482
  r.tiles.forEach((tile) => {
463
483
  tile.features.forEach((id) => {
@@ -473,6 +493,8 @@ const TiledFeatureLayer = L.GridLayer.extend({
473
493
  })
474
494
 
475
495
  promise.then((data) => {
496
+ if (promise.status.cancelled) return
497
+
476
498
  // Gather all associated tiles, taking children tiles into account
477
499
  const allTiles = [r.tiles]
478
500
  r.tiles.forEach((tile) => { if (tile.measuresChildren.length) allTiles.push(tile.measuresChildren) })
@@ -498,6 +520,8 @@ const TiledFeatureLayer = L.GridLayer.extend({
498
520
  if (okMeasures.length) this.updateStations(featureCollection(okMeasures))
499
521
  })
500
522
  }).catch((err) => {
523
+ if (promise.status.cancelled) return
524
+
501
525
  const allTiles = [r.tiles]
502
526
  r.tiles.forEach((tile) => { if (tile.measuresChildren.length) allTiles.push(tile.measuresChildren) })
503
527
  const tiles = allTiles.flat()
@@ -508,6 +532,8 @@ const TiledFeatureLayer = L.GridLayer.extend({
508
532
  tile.div.innerHTML += `</br>measures request failed: ${err}`
509
533
  }
510
534
  })
535
+ }).finally(() => {
536
+ promise.status.pending = false
511
537
  })
512
538
  })
513
539
 
@@ -564,6 +590,10 @@ const TiledFeatureLayer = L.GridLayer.extend({
564
590
 
565
591
  this.pendingStationUpdates.length = 0
566
592
 
593
+ // 'cancel' all pending requests so we'll ignore their results
594
+ // when they'll finish (see _update)
595
+ this.pendingRequests.forEach((r) => { r.status.cancelled = true })
596
+
567
597
  // request grid layer redraw
568
598
  L.GridLayer.prototype.redraw.call(this)
569
599
  }