@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.
- package/dist/src/widget-mapbox.d.ts +16 -8
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/widget-mapbox.js +209 -134
- package/dist/widget-mapbox.js.map +1 -1
- package/package.json +6 -4
- package/src/default-data.json +13 -23
- package/src/definition-schema.d.ts +27 -11
- package/src/definition-schema.json +30 -4
- package/src/widget-mapbox.ts +154 -132
- package/thumbnail.png +0 -0
package/src/widget-mapbox.ts
CHANGED
|
@@ -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
|
-
|
|
5
|
-
// import
|
|
6
|
-
import
|
|
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 {
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
|
|
34
|
-
|
|
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:
|
|
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.
|
|
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:
|
|
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:
|
|
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?.
|
|
257
|
-
'circle-opacity': dataSet?.
|
|
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?.
|
|
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?.
|
|
291
|
+
'text-size': dataSet?.config?.['text-size'] ?? 14,
|
|
279
292
|
'text-anchor': 'center'
|
|
280
293
|
},
|
|
281
294
|
paint: {
|
|
282
|
-
'text-color': dataSet?.
|
|
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:
|
|
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?.
|
|
311
|
+
'text-size': dataSet?.config?.['text-size'] ?? 14,
|
|
299
312
|
'text-anchor': 'center',
|
|
300
|
-
'icon-image':
|
|
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?.
|
|
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:
|
|
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?.
|
|
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?.
|
|
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:
|
|
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
|
-
|
|
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?.
|
|
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':
|
|
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:
|
|
441
|
-
const
|
|
442
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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 =
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
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
|
-
|
|
570
|
-
|
|
571
|
-
|
|
596
|
+
.paging:not([active]) {
|
|
597
|
+
display: none !important;
|
|
598
|
+
}
|
|
572
599
|
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
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
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
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
|
-
|
|
609
|
-
|
|
610
|
-
|
|
635
|
+
.title {
|
|
636
|
+
white-space: nowrap;
|
|
637
|
+
}
|
|
611
638
|
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
639
|
+
.legend {
|
|
640
|
+
display: flex;
|
|
641
|
+
flex-wrap: wrap;
|
|
642
|
+
gap: 12px;
|
|
643
|
+
}
|
|
617
644
|
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
645
|
+
.label {
|
|
646
|
+
display: flex;
|
|
647
|
+
align-items: center;
|
|
648
|
+
font-size: 14px;
|
|
649
|
+
gap: 8px;
|
|
650
|
+
}
|
|
624
651
|
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
652
|
+
a.mapboxgl-ctrl-logo {
|
|
653
|
+
display: none;
|
|
654
|
+
}
|
|
628
655
|
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
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
|