@record-evolution/widget-mapbox 1.5.10 → 1.5.13

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.
@@ -1,12 +1,19 @@
1
- import { html, css, LitElement } from 'lit'
2
- import { property, state } from 'lit/decorators.js'
1
+ import { html, css, LitElement, unsafeCSS } from 'lit'
2
+ import { customElement, property, query, state } from 'lit/decorators.js'
3
3
  import { repeat } from 'lit/directives/repeat.js'
4
- // @ts-ignore
5
- // import mapboxgl from 'https://cdn.skypack.dev/-/mapbox-gl@v2.15.0-iKfohePv9lgutCMNih0d/dist=es2020,mode=imports,min/optimized/mapbox-gl.js'
6
- import mapboxgl from 'https://esm.run/mapbox-gl@3.0.1'
4
+ import mapboxgl from 'mapbox-gl'
5
+ // @ts-ignore - virtual import returns a string at build time
6
+ import mapboxCSS from 'mapbox-gl/dist/mapbox-gl.css'
7
7
  import * as GeoJSON from 'geojson'
8
8
  import tinycolor from 'tinycolor2'
9
- import { LayerBaseColor, InputData } from './definition-schema.js'
9
+ import {
10
+ CircleLayerConfiguration,
11
+ HeatmapLayerConfiguration,
12
+ InputData,
13
+ MapData,
14
+ SymbolLayerConfiguration,
15
+ TrackLayerConfiguration
16
+ } from './definition-schema.js'
10
17
 
11
18
  type Dataseries = Exclude<InputData['dataseries'], undefined>[number]
12
19
  type Point = Exclude<Dataseries['data'], undefined>[number]
@@ -14,29 +21,35 @@ type Theme = {
14
21
  theme_name: string
15
22
  theme_object: any
16
23
  }
17
- export class WidgetMapbox extends LitElement {
18
- @property({ type: Object })
19
- inputData?: InputData
20
-
21
- @property({ type: Object })
22
- theme?: Theme
23
-
24
- @state()
25
- private map: any | undefined = undefined
26
24
 
27
- @state()
28
- private dataSets: Dataseries[] = []
29
-
30
- @state()
31
- dataSources: any = new Map()
25
+ type DataSet = {
26
+ label?: string
27
+ type?: string
28
+ latestValues?: number
29
+ color?: string
30
+ config?:
31
+ | SymbolLayerConfiguration
32
+ | CircleLayerConfiguration
33
+ | HeatmapLayerConfiguration
34
+ | TrackLayerConfiguration
35
+ data?: MapData
36
+ }
32
37
 
33
- @state()
34
- colors: any = new Map()
38
+ @customElement('widget-mapbox-versionplaceholder')
39
+ export class WidgetMapbox extends LitElement {
40
+ @property({ type: Object }) inputData?: InputData
41
+ @property({ type: Object }) theme?: Theme
35
42
 
43
+ @state() private map: any | undefined = undefined
44
+ @state() private dataSets: DataSet[] = []
45
+ @state() dataSources: any = new Map()
46
+ @state() colors: any = new Map()
36
47
  @state() private themeBgColor?: string
37
48
  @state() private themeTitleColor?: string
38
49
  @state() private themeSubtitleColor?: string
39
50
 
51
+ @query('#map') private mapElement?: HTMLDivElement
52
+
40
53
  public version: string = 'versionplaceholder'
41
54
  private mapLoaded: boolean = false
42
55
  private resizeObserver: ResizeObserver
@@ -61,7 +74,12 @@ export class WidgetMapbox extends LitElement {
61
74
 
62
75
  update(changedProperties: Map<string, any>) {
63
76
  if (changedProperties.has('inputData')) {
77
+ if (this.map && this.inputData?.style !== this.mapStyle) {
78
+ this.createMap()
79
+ this.fitBounds()
80
+ }
64
81
  this.transformInputData()
82
+ this.syncDataLayers()
65
83
  }
66
84
 
67
85
  if (changedProperties.has('theme')) {
@@ -73,8 +91,9 @@ export class WidgetMapbox extends LitElement {
73
91
 
74
92
  firstUpdated() {
75
93
  this.registerTheme(this.theme)
76
- this.transformInputData()
77
94
  this.createMap()
95
+ this.transformInputData()
96
+ this.syncDataLayers()
78
97
  this.resizeObserver.observe(this.map._container)
79
98
  this.fitBounds()
80
99
  }
@@ -131,10 +150,6 @@ export class WidgetMapbox extends LitElement {
131
150
  transformInputData() {
132
151
  if (!this?.inputData || !this?.inputData?.dataseries?.length) return
133
152
 
134
- if (this.map && this.inputData?.style !== this.mapStyle) {
135
- this.createMap()
136
- }
137
-
138
153
  // choose random color if dataseries has none and store it for furure updates
139
154
  this.inputData.dataseries.forEach((ds) => {
140
155
  if (!this.colors.has(ds.label)) {
@@ -154,13 +169,12 @@ export class WidgetMapbox extends LitElement {
154
169
 
155
170
  distincts.forEach((piv, i) => {
156
171
  const prefix = piv ? `${piv} - ` : ''
157
- const pds: any = {
172
+ const pds: DataSet = {
158
173
  label: prefix + ds.label,
159
- order: ds.order,
160
174
  type: ds.type,
161
175
  latestValues: ds.latestValues,
162
176
  color: derivedColors[i],
163
- config: ds.config,
177
+ config: ds.symbolConfig ?? ds.circleConfig ?? ds.heatmapConfig ?? ds.lineConfig,
164
178
  data: distincts.length === 1 ? ds.data : ds.data?.filter((d) => d.pivot === piv)
165
179
  }
166
180
  this.dataSets.push(pds)
@@ -189,10 +203,9 @@ export class WidgetMapbox extends LitElement {
189
203
 
190
204
  // console.log('mapbox DataLayers', this.dataSources)
191
205
  })
192
- if (this.map) this.syncDataLayers()
193
206
  }
194
207
 
195
- createGEOJson(ds: Dataseries): GeoJSON.Feature[] | undefined {
208
+ createGEOJson(ds: DataSet): GeoJSON.Feature[] | undefined {
196
209
  ds.data?.forEach((p) => {
197
210
  p = {
198
211
  lon: Number(p.lon),
@@ -246,15 +259,15 @@ export class WidgetMapbox extends LitElement {
246
259
  return [feature]
247
260
  }
248
261
 
249
- addCircleLayer(dataSet: Dataseries) {
262
+ addCircleLayer(dataSet: DataSet) {
250
263
  if (!dataSet) return
251
264
  const layerConfig = {
252
265
  id: dataSet.label + ':circle',
253
266
  type: 'circle',
254
267
  source: 'input:' + dataSet.label,
255
268
  paint: {
256
- 'circle-blur': dataSet?.circle?.['circle-blur'] ?? 0,
257
- 'circle-opacity': dataSet?.circle?.['circle-opacity'] ?? 1,
269
+ 'circle-blur': dataSet?.config?.['circle-blur'] ?? 0,
270
+ 'circle-opacity': dataSet?.config?.['circle-opacity'] ?? 1,
258
271
  'circle-radius': ['get', 'value'],
259
272
  'circle-radius-transition': {
260
273
  duration: 1000,
@@ -268,18 +281,18 @@ export class WidgetMapbox extends LitElement {
268
281
 
269
282
  this.map?.addLayer(layerConfig)
270
283
 
271
- if (!dataSet?.symbol) return
284
+ if (!dataSet?.config?.['text-size']) return
272
285
  const layerConfig2 = {
273
286
  id: dataSet.label + ':symbol',
274
287
  type: 'symbol',
275
288
  source: 'input:' + dataSet.label,
276
289
  layout: {
277
290
  'text-field': ['get', 'value'],
278
- 'text-size': dataSet?.symbol?.['text-size'] ?? 14,
291
+ 'text-size': dataSet?.config?.['text-size'] ?? 14,
279
292
  'text-anchor': 'center'
280
293
  },
281
294
  paint: {
282
- 'text-color': dataSet?.symbol?.['text-color'] ?? '#000'
295
+ 'text-color': dataSet?.config?.['text-color'] ?? '#000'
283
296
  }
284
297
  // Place polygons under labels, roads and buildings.
285
298
  // 'aeroway-polygon'
@@ -287,7 +300,7 @@ export class WidgetMapbox extends LitElement {
287
300
  this.map?.addLayer(layerConfig2)
288
301
  }
289
302
 
290
- addSymbolLayer(dataSet: Dataseries) {
303
+ addSymbolLayer(dataSet: DataSet) {
291
304
  if (!dataSet) return
292
305
  const layerConfig = {
293
306
  id: dataSet.label + ':symbol',
@@ -295,13 +308,15 @@ export class WidgetMapbox extends LitElement {
295
308
  source: 'input:' + dataSet.label,
296
309
  layout: {
297
310
  'text-field': ['get', 'value'],
298
- 'text-size': dataSet?.symbol?.['text-size'] ?? 14,
311
+ 'text-size': dataSet?.config?.['text-size'] ?? 14,
299
312
  'text-anchor': 'center',
300
- 'icon-image': (dataSet?.symbol?.['icon-image'] ?? '') + (dataSet?.symbol?.['icon-size'] ?? 1),
313
+ 'icon-image':
314
+ ((dataSet?.config?.['icon-image'] as string) ?? '') +
315
+ ((dataSet?.config?.['icon-size'] as number) ?? 1),
301
316
  'icon-size': 1
302
317
  },
303
318
  paint: {
304
- 'text-color': dataSet?.symbol?.['text-color']
319
+ 'text-color': dataSet?.config?.['text-color']
305
320
  }
306
321
  // Place polygons under labels, roads and buildings.
307
322
  // 'aeroway-polygon'
@@ -309,7 +324,7 @@ export class WidgetMapbox extends LitElement {
309
324
  this.map?.addLayer(layerConfig)
310
325
  }
311
326
 
312
- addHeatmapLayer(dataSet: Dataseries) {
327
+ addHeatmapLayer(dataSet: DataSet) {
313
328
  if (!dataSet) return
314
329
  const values = (dataSet.data?.map((p) => p?.value).filter((v) => v !== undefined) as number[]) ?? []
315
330
  const min = Math.min(...values)
@@ -319,7 +334,7 @@ export class WidgetMapbox extends LitElement {
319
334
  type: 'heatmap',
320
335
  source: 'input:' + dataSet.label,
321
336
  paint: {
322
- ...dataSet?.heatmap,
337
+ ...dataSet?.config,
323
338
  'heatmap-color': [
324
339
  'interpolate',
325
340
  ['linear'],
@@ -356,7 +371,7 @@ export class WidgetMapbox extends LitElement {
356
371
  min,
357
372
  30,
358
373
  max,
359
- 30 + (dataSet?.heatmap?.['heatmap-radius'] ?? 0)
374
+ 30 + ((dataSet?.config?.['heatmap-radius'] as number) ?? 0)
360
375
  ],
361
376
  'heatmap-radius-transition': {
362
377
  duration: 1000,
@@ -377,7 +392,7 @@ export class WidgetMapbox extends LitElement {
377
392
  this.map?.addLayer(layerConfig)
378
393
  }
379
394
 
380
- addLineLayer(dataSet: Dataseries) {
395
+ addLineLayer(dataSet: DataSet) {
381
396
  if (!dataSet) return
382
397
  const layerConfig = {
383
398
  id: dataSet.label + ':line',
@@ -387,7 +402,7 @@ export class WidgetMapbox extends LitElement {
387
402
  'line-cap': 'round'
388
403
  },
389
404
  paint: {
390
- ...dataSet?.line,
405
+ 'line-width': dataSet?.config?.['line-width'] ?? 2,
391
406
  'line-color': dataSet.color
392
407
  }
393
408
  // Place polygons under labels, roads and buildings.
@@ -395,14 +410,16 @@ export class WidgetMapbox extends LitElement {
395
410
  }
396
411
  this.map?.addLayer(layerConfig)
397
412
 
398
- if (!dataSet?.symbol?.['icon-image']) return
413
+ if (!dataSet?.config?.['icon-image']) return
399
414
 
400
415
  const layerConfig2 = {
401
416
  id: dataSet.label + ':symbol',
402
417
  type: 'symbol',
403
418
  source: 'input:' + dataSet.label,
404
419
  layout: {
405
- 'icon-image': (dataSet?.symbol?.['icon-image'] ?? '') + (dataSet?.symbol?.['icon-size'] ?? 1),
420
+ 'icon-image':
421
+ ((dataSet?.config?.['icon-image'] as string) ?? '') +
422
+ ((dataSet?.config?.['icon-size'] as number) ?? 1),
406
423
  'icon-size': 1
407
424
  },
408
425
  paint: {
@@ -437,9 +454,11 @@ export class WidgetMapbox extends LitElement {
437
454
  })
438
455
 
439
456
  // add new layers or update the data of existing layers
440
- this.dataSets.forEach((ds: Dataseries) => {
441
- const sz = ds?.symbol?.['icon-size'] ?? 1
442
- const _imageName = ds?.symbol?.['icon-image'] ?? ''
457
+ this.dataSets.forEach((ds: DataSet) => {
458
+ const config = ds.config as SymbolLayerConfiguration
459
+ if (!config) return
460
+ const sz = config['icon-size'] ?? 1
461
+ const _imageName = config['icon-image'] ?? 'car'
443
462
  const imageName = _imageName + sz
444
463
  if (['line', 'symbol'].includes(ds.type ?? '') && !this.imageList.includes(imageName)) {
445
464
  const img = new Image(24 * sz, 24 * sz) as HTMLImageElement
@@ -528,16 +547,21 @@ export class WidgetMapbox extends LitElement {
528
547
  }
529
548
 
530
549
  createMap() {
531
- if (this.map) return
550
+ if (!this.mapElement) return
551
+ if (this.map) {
552
+ this.map.remove()
553
+ this.map = undefined
554
+ this.mapLoaded = false
555
+ this.imageList = []
556
+ }
532
557
  this.mapStyle = this.inputData?.style
533
558
  this.map = new mapboxgl.Map({
534
- container: this.shadowRoot?.getElementById('map') as HTMLCanvasElement,
559
+ container: this.mapElement,
535
560
  style: `mapbox://styles/mapbox/${this.mapStyle ?? 'light-v11'}`,
536
561
  center: [8.68417, 50.11552],
537
562
  zoom: 1.8,
538
563
  attributionControl: false
539
564
  })
540
-
541
565
  this.map.scrollZoom.disable()
542
566
 
543
567
  this.map.addControl(new mapboxgl.NavigationControl(), 'top-right')
@@ -549,7 +573,8 @@ export class WidgetMapbox extends LitElement {
549
573
 
550
574
  this.map.addControl(scale, 'bottom-left')
551
575
 
552
- console.log('MAPBOX VERSION', mapboxgl.version)
576
+ this.map.on('error', (event: ErrorEvent) => console.error('Mapbox error event', event.error))
577
+ this.map.once('styledata', () => console.log('MapBox Style data ready'))
553
578
 
554
579
  this.map.on('load', () => {
555
580
  this.mapLoaded = true
@@ -558,91 +583,90 @@ export class WidgetMapbox extends LitElement {
558
583
  })
559
584
  }
560
585
 
561
- static styles = css`
562
- :host {
563
- display: block;
564
- font-family: sans-serif;
565
- box-sizing: border-box;
566
- margin: auto;
567
- }
586
+ static styles = [
587
+ unsafeCSS(mapboxCSS),
588
+ css`
589
+ :host {
590
+ display: block;
591
+ font-family: sans-serif;
592
+ box-sizing: border-box;
593
+ margin: auto;
594
+ }
568
595
 
569
- .paging:not([active]) {
570
- display: none !important;
571
- }
596
+ .paging:not([active]) {
597
+ display: none !important;
598
+ }
572
599
 
573
- header {
574
- display: flex;
575
- margin: 0 0 16px 0;
576
- gap: 24px;
577
- justify-content: space-between;
578
- }
579
- h3 {
580
- margin: 0;
581
- max-width: 300px;
582
- overflow: hidden;
583
- text-overflow: ellipsis;
584
- white-space: nowrap;
585
- }
586
- p {
587
- margin: 10px 0 0 0;
588
- max-width: 300px;
589
- font-size: 14px;
590
- overflow: hidden;
591
- text-overflow: ellipsis;
592
- white-space: nowrap;
593
- line-height: 17px;
594
- }
600
+ header {
601
+ display: flex;
602
+ margin: 0 0 16px 0;
603
+ gap: 24px;
604
+ justify-content: space-between;
605
+ }
606
+ h3 {
607
+ margin: 0;
608
+ max-width: 300px;
609
+ overflow: hidden;
610
+ text-overflow: ellipsis;
611
+ white-space: nowrap;
612
+ }
613
+ p {
614
+ margin: 10px 0 0 0;
615
+ max-width: 300px;
616
+ font-size: 14px;
617
+ overflow: hidden;
618
+ text-overflow: ellipsis;
619
+ white-space: nowrap;
620
+ line-height: 17px;
621
+ }
595
622
 
596
- .wrapper {
597
- display: flex;
598
- flex-direction: column;
599
- box-sizing: border-box;
600
- padding: 16px;
601
- height: 100%;
602
- width: 100%;
603
- }
604
- #map {
605
- flex: 1;
606
- }
623
+ .wrapper {
624
+ display: flex;
625
+ flex-direction: column;
626
+ box-sizing: border-box;
627
+ padding: 16px;
628
+ height: 100%;
629
+ width: 100%;
630
+ }
631
+ #map {
632
+ flex: 1;
633
+ }
607
634
 
608
- .title {
609
- white-space: nowrap;
610
- }
635
+ .title {
636
+ white-space: nowrap;
637
+ }
611
638
 
612
- .legend {
613
- display: flex;
614
- flex-wrap: wrap;
615
- gap: 12px;
616
- }
639
+ .legend {
640
+ display: flex;
641
+ flex-wrap: wrap;
642
+ gap: 12px;
643
+ }
617
644
 
618
- .label {
619
- display: flex;
620
- align-items: center;
621
- font-size: 14px;
622
- gap: 8px;
623
- }
645
+ .label {
646
+ display: flex;
647
+ align-items: center;
648
+ font-size: 14px;
649
+ gap: 8px;
650
+ }
624
651
 
625
- a.mapboxgl-ctrl-logo {
626
- display: none;
627
- }
652
+ a.mapboxgl-ctrl-logo {
653
+ display: none;
654
+ }
628
655
 
629
- .no-data {
630
- font-size: 20px;
631
- display: flex;
632
- height: 100%;
633
- width: 100%;
634
- text-align: center;
635
- align-items: center;
636
- justify-content: center;
637
- }
638
- `
656
+ .no-data {
657
+ font-size: 20px;
658
+ display: flex;
659
+ height: 100%;
660
+ width: 100%;
661
+ text-align: center;
662
+ align-items: center;
663
+ justify-content: center;
664
+ }
665
+ `
666
+ ]
639
667
 
640
668
  render() {
641
669
  return html`
642
- <link
643
- href="https://api.mapbox.com/mapbox-gl-js/v${mapboxgl.version}/mapbox-gl.css"
644
- rel="stylesheet"
645
- />
646
670
  <div
647
671
  class="wrapper"
648
672
  style="background-color: ${this.themeBgColor}; color: ${this.themeTitleColor}"
@@ -681,5 +705,3 @@ export class WidgetMapbox extends LitElement {
681
705
  `
682
706
  }
683
707
  }
684
-
685
- window.customElements.define('widget-mapbox-versionplaceholder', WidgetMapbox)
package/thumbnail.png ADDED
Binary file