@orbcharts/core 4.0.0-pre-alpha.3 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/orbcharts-core.es.js +1662 -1589
- package/dist/orbcharts-core.umd.js +6 -6
- package/dist/src/test/simple-graph-test.d.ts +1 -2
- package/dist/src/types/Encoding.d.ts +4 -1
- package/dist/src/types/Plugin.d.ts +1 -1
- package/dist/src/utils/colorUtils.d.ts +1 -1
- package/dist/src/utils/encodingUtils.d.ts +11 -0
- package/dist/src/utils/index.d.ts +1 -0
- package/package.json +1 -1
- package/src/chart/createChart.ts +47 -4
- package/src/chart/createGraphData.ts +20 -19
- package/src/chart/createGridData.ts +12 -11
- package/src/chart/createMultivariateData.ts +10 -9
- package/src/chart/createSeriesData.ts +15 -14
- package/src/chart/createTreeData.ts +18 -17
- package/src/chart/defaults.ts +47 -24
- package/src/plugin/createPlugin.ts +33 -15
- package/src/test/createGraphData.test.ts +1 -1
- package/src/test/createTreeData.test.ts +1 -1
- package/src/test/simple-graph-test.js +1 -1
- package/src/types/Encoding.ts +4 -1
- package/src/types/Plugin.ts +4 -1
- package/src/utils/colorUtils.ts +1 -1
- package/src/utils/encodingUtils.ts +18 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/validator.ts +10 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { RawData, RawDataColumn, Encoding, ModelDataGraph, ModelDatumGraphNode, ModelDatumGraphEdge, Theme } from '../types'
|
|
2
2
|
import { aggregate } from '../utils/aggregateUtils'
|
|
3
3
|
import { getColorByFrom } from '../utils/colorUtils'
|
|
4
|
+
import { resolveGroupKey } from '../utils/encodingUtils'
|
|
4
5
|
|
|
5
6
|
export const createGraphData = (rawData: RawData, encoding: Encoding, theme: Theme): ModelDataGraph[] => {
|
|
6
7
|
// 判斷是一維陣列還是二維陣列
|
|
@@ -38,7 +39,7 @@ export const createGraphData = (rawData: RawData, encoding: Encoding, theme: The
|
|
|
38
39
|
(rawData as RawDataColumn[][]).forEach((datasetArray, datasetIndex) => {
|
|
39
40
|
datasetArray.forEach((d) => {
|
|
40
41
|
if (!d.source || !d.target) {
|
|
41
|
-
const datasetKey = (d
|
|
42
|
+
const datasetKey = resolveGroupKey(d, encoding.dataset, `dataset-${datasetIndex}`)
|
|
42
43
|
if (!datasetMap.has(datasetKey)) {
|
|
43
44
|
datasetMap.set(datasetKey, [])
|
|
44
45
|
}
|
|
@@ -48,7 +49,7 @@ export const createGraphData = (rawData: RawData, encoding: Encoding, theme: The
|
|
|
48
49
|
})
|
|
49
50
|
} else {
|
|
50
51
|
nodeData.forEach((d) => {
|
|
51
|
-
const datasetKey = (d
|
|
52
|
+
const datasetKey = resolveGroupKey(d, encoding.dataset, '')
|
|
52
53
|
if (!datasetMap.has(datasetKey)) {
|
|
53
54
|
datasetMap.set(datasetKey, [])
|
|
54
55
|
}
|
|
@@ -67,7 +68,7 @@ export const createGraphData = (rawData: RawData, encoding: Encoding, theme: The
|
|
|
67
68
|
(rawData as RawDataColumn[][]).forEach((datasetArray, datasetIndex) => {
|
|
68
69
|
datasetArray.forEach((d) => {
|
|
69
70
|
if (!d.source || !d.target) {
|
|
70
|
-
const datasetKey = (d
|
|
71
|
+
const datasetKey = resolveGroupKey(d, encoding.dataset, `dataset-${datasetIndex}`)
|
|
71
72
|
if (!datasetOrder.includes(datasetKey)) {
|
|
72
73
|
datasetOrder.push(datasetKey)
|
|
73
74
|
}
|
|
@@ -76,7 +77,7 @@ export const createGraphData = (rawData: RawData, encoding: Encoding, theme: The
|
|
|
76
77
|
})
|
|
77
78
|
} else {
|
|
78
79
|
nodeData.forEach((d) => {
|
|
79
|
-
const datasetKey = (d
|
|
80
|
+
const datasetKey = resolveGroupKey(d, encoding.dataset, '')
|
|
80
81
|
if (!datasetOrder.includes(datasetKey)) {
|
|
81
82
|
datasetOrder.push(datasetKey)
|
|
82
83
|
}
|
|
@@ -98,7 +99,7 @@ export const createGraphData = (rawData: RawData, encoding: Encoding, theme: The
|
|
|
98
99
|
// 依據 series 欄位將資料分組
|
|
99
100
|
const seriesMap = new Map<string, RawDataColumn[]>()
|
|
100
101
|
data.forEach((d) => {
|
|
101
|
-
const seriesKey = (d
|
|
102
|
+
const seriesKey = resolveGroupKey(d, encoding.series, '')
|
|
102
103
|
if (!seriesMap.has(seriesKey)) {
|
|
103
104
|
seriesMap.set(seriesKey, [])
|
|
104
105
|
}
|
|
@@ -113,7 +114,7 @@ export const createGraphData = (rawData: RawData, encoding: Encoding, theme: The
|
|
|
113
114
|
} else if (encoding.series.sort === 'original') {
|
|
114
115
|
const seriesOrder: string[] = []
|
|
115
116
|
data.forEach((d) => {
|
|
116
|
-
const seriesKey = (d
|
|
117
|
+
const seriesKey = resolveGroupKey(d, encoding.series, '')
|
|
117
118
|
if (!seriesOrder.includes(seriesKey)) {
|
|
118
119
|
seriesOrder.push(seriesKey)
|
|
119
120
|
}
|
|
@@ -130,7 +131,7 @@ export const createGraphData = (rawData: RawData, encoding: Encoding, theme: The
|
|
|
130
131
|
// 依據 category 欄位將 series 內的資料分組
|
|
131
132
|
const categoryMap = new Map<string, RawDataColumn[]>()
|
|
132
133
|
seriesItems.forEach((d) => {
|
|
133
|
-
const categoryKey = (d
|
|
134
|
+
const categoryKey = resolveGroupKey(d, encoding.category, '')
|
|
134
135
|
if (!categoryMap.has(categoryKey)) {
|
|
135
136
|
categoryMap.set(categoryKey, [])
|
|
136
137
|
}
|
|
@@ -145,7 +146,7 @@ export const createGraphData = (rawData: RawData, encoding: Encoding, theme: The
|
|
|
145
146
|
} else if (encoding.category.sort === 'original') {
|
|
146
147
|
const categoryOrder: string[] = []
|
|
147
148
|
seriesItems.forEach((d) => {
|
|
148
|
-
const categoryKey = (d
|
|
149
|
+
const categoryKey = resolveGroupKey(d, encoding.category, '')
|
|
149
150
|
if (!categoryOrder.includes(categoryKey)) {
|
|
150
151
|
categoryOrder.push(categoryKey)
|
|
151
152
|
}
|
|
@@ -170,7 +171,7 @@ export const createGraphData = (rawData: RawData, encoding: Encoding, theme: The
|
|
|
170
171
|
name: d.name || '',
|
|
171
172
|
data: d.data,
|
|
172
173
|
value: typeof value === 'number' ? value : null,
|
|
173
|
-
color: getColorByFrom(encoding.color.
|
|
174
|
+
color: getColorByFrom(encoding.color.by, {
|
|
174
175
|
index: categoryIndex,
|
|
175
176
|
seriesIndex,
|
|
176
177
|
categoryIndex,
|
|
@@ -222,7 +223,7 @@ export const createGraphData = (rawData: RawData, encoding: Encoding, theme: The
|
|
|
222
223
|
name: firstItem.name || categoryName,
|
|
223
224
|
data: firstItem.data,
|
|
224
225
|
value: aggregatedValue,
|
|
225
|
-
color: getColorByFrom(encoding.color.
|
|
226
|
+
color: getColorByFrom(encoding.color.by, {
|
|
226
227
|
index: categoryIndex,
|
|
227
228
|
seriesIndex,
|
|
228
229
|
categoryIndex,
|
|
@@ -280,7 +281,7 @@ export const createGraphData = (rawData: RawData, encoding: Encoding, theme: The
|
|
|
280
281
|
|
|
281
282
|
// 依據 dataset 分組處理 edges(這裡使用所有邊的資料,但只處理目前 dataset 的邊)
|
|
282
283
|
const datasetEdges = edgeData.filter(d => {
|
|
283
|
-
const datasetKey = (d
|
|
284
|
+
const datasetKey = resolveGroupKey(d, encoding.dataset, '')
|
|
284
285
|
return datasetKey === datasetName
|
|
285
286
|
})
|
|
286
287
|
|
|
@@ -289,8 +290,8 @@ export const createGraphData = (rawData: RawData, encoding: Encoding, theme: The
|
|
|
289
290
|
datasetEdges.forEach((d) => {
|
|
290
291
|
const source = d.source!
|
|
291
292
|
const target = d.target!
|
|
292
|
-
const series = (d
|
|
293
|
-
const category = (d
|
|
293
|
+
const series = resolveGroupKey(d, encoding.series, '')
|
|
294
|
+
const category = resolveGroupKey(d, encoding.category, '')
|
|
294
295
|
const groupKey = `${source}-${target}-${series}-${category}`
|
|
295
296
|
|
|
296
297
|
if (!edgeGroupMap.has(groupKey)) {
|
|
@@ -312,8 +313,8 @@ export const createGraphData = (rawData: RawData, encoding: Encoding, theme: The
|
|
|
312
313
|
// 不聚合,保持原始資料結構
|
|
313
314
|
groupEdges.forEach((d, index) => {
|
|
314
315
|
const value = (d as any)[encoding.value.from]
|
|
315
|
-
const edgeSeries = (d
|
|
316
|
-
const edgeCategory = (d
|
|
316
|
+
const edgeSeries = resolveGroupKey(d, encoding.series, '')
|
|
317
|
+
const edgeCategory = resolveGroupKey(d, encoding.category, '')
|
|
317
318
|
const edgeSeriesIndex = getEdgeSeriesIndex(edgeSeries)
|
|
318
319
|
const edgeCategoryIndex = getEdgeCategoryIndex(edgeCategory)
|
|
319
320
|
const edge: ModelDatumGraphEdge = {
|
|
@@ -323,7 +324,7 @@ export const createGraphData = (rawData: RawData, encoding: Encoding, theme: The
|
|
|
323
324
|
name: d.name || '',
|
|
324
325
|
data: d.data,
|
|
325
326
|
value: typeof value === 'number' ? value : null,
|
|
326
|
-
color: getColorByFrom(encoding.color.
|
|
327
|
+
color: getColorByFrom(encoding.color.by, {
|
|
327
328
|
index: edges.length,
|
|
328
329
|
seriesIndex: edgeSeriesIndex,
|
|
329
330
|
categoryIndex: edgeCategoryIndex,
|
|
@@ -352,8 +353,8 @@ export const createGraphData = (rawData: RawData, encoding: Encoding, theme: The
|
|
|
352
353
|
|
|
353
354
|
const aggregatedValue = aggregate(values, encoding.value.aggregate)
|
|
354
355
|
|
|
355
|
-
const edgeSeries = (firstEdge
|
|
356
|
-
const edgeCategory = (firstEdge
|
|
356
|
+
const edgeSeries = resolveGroupKey(firstEdge, encoding.series, '')
|
|
357
|
+
const edgeCategory = resolveGroupKey(firstEdge, encoding.category, '')
|
|
357
358
|
const edgeSeriesIndex = getEdgeSeriesIndex(edgeSeries)
|
|
358
359
|
const edgeCategoryIndex = getEdgeCategoryIndex(edgeCategory)
|
|
359
360
|
|
|
@@ -364,7 +365,7 @@ export const createGraphData = (rawData: RawData, encoding: Encoding, theme: The
|
|
|
364
365
|
name: firstEdge.name || '',
|
|
365
366
|
data: firstEdge.data,
|
|
366
367
|
value: aggregatedValue,
|
|
367
|
-
color: getColorByFrom(encoding.color.
|
|
368
|
+
color: getColorByFrom(encoding.color.by, {
|
|
368
369
|
index: edges.length,
|
|
369
370
|
seriesIndex: edgeSeriesIndex,
|
|
370
371
|
categoryIndex: edgeCategoryIndex,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { RawData, RawDataColumn, Encoding, ModelDataGrid, ModelDatumGrid, Theme } from '../types'
|
|
2
2
|
import { aggregate } from '../utils/aggregateUtils'
|
|
3
3
|
import { getColorByFrom } from '../utils/colorUtils'
|
|
4
|
+
import { resolveGroupKey } from '../utils/encodingUtils'
|
|
4
5
|
|
|
5
6
|
export const createGridData = (rawData: RawData, encoding: Encoding, theme: Theme): ModelDataGrid[] => {
|
|
6
7
|
// 依據 dataset 欄位將資料分組
|
|
@@ -13,7 +14,7 @@ export const createGridData = (rawData: RawData, encoding: Encoding, theme: Them
|
|
|
13
14
|
// 二維陣列:每個子陣列代表一個 dataset
|
|
14
15
|
(rawData as RawDataColumn[][]).forEach((datasetArray, datasetIndex) => {
|
|
15
16
|
datasetArray.forEach((d) => {
|
|
16
|
-
const datasetKey = (d
|
|
17
|
+
const datasetKey = resolveGroupKey(d, encoding.dataset, `dataset-${datasetIndex}`)
|
|
17
18
|
if (!datasetMap.has(datasetKey)) {
|
|
18
19
|
datasetMap.set(datasetKey, [])
|
|
19
20
|
}
|
|
@@ -23,7 +24,7 @@ export const createGridData = (rawData: RawData, encoding: Encoding, theme: Them
|
|
|
23
24
|
} else {
|
|
24
25
|
// 一維陣列:依據 dataset 欄位分組
|
|
25
26
|
(rawData as RawDataColumn[]).forEach((d) => {
|
|
26
|
-
const datasetKey = (d
|
|
27
|
+
const datasetKey = resolveGroupKey(d, encoding.dataset, '')
|
|
27
28
|
if (!datasetMap.has(datasetKey)) {
|
|
28
29
|
datasetMap.set(datasetKey, [])
|
|
29
30
|
}
|
|
@@ -42,7 +43,7 @@ export const createGridData = (rawData: RawData, encoding: Encoding, theme: Them
|
|
|
42
43
|
if (is2DArray) {
|
|
43
44
|
(rawData as RawDataColumn[][]).forEach((datasetArray, datasetIndex) => {
|
|
44
45
|
datasetArray.forEach((d) => {
|
|
45
|
-
const datasetKey = (d
|
|
46
|
+
const datasetKey = resolveGroupKey(d, encoding.dataset, `dataset-${datasetIndex}`)
|
|
46
47
|
if (!datasetOrder.includes(datasetKey)) {
|
|
47
48
|
datasetOrder.push(datasetKey)
|
|
48
49
|
}
|
|
@@ -50,7 +51,7 @@ export const createGridData = (rawData: RawData, encoding: Encoding, theme: Them
|
|
|
50
51
|
})
|
|
51
52
|
} else {
|
|
52
53
|
(rawData as RawDataColumn[]).forEach((d) => {
|
|
53
|
-
const datasetKey = (d
|
|
54
|
+
const datasetKey = resolveGroupKey(d, encoding.dataset, '')
|
|
54
55
|
if (!datasetOrder.includes(datasetKey)) {
|
|
55
56
|
datasetOrder.push(datasetKey)
|
|
56
57
|
}
|
|
@@ -70,7 +71,7 @@ export const createGridData = (rawData: RawData, encoding: Encoding, theme: Them
|
|
|
70
71
|
// 依據 series 欄位將資料分組
|
|
71
72
|
const seriesMap = new Map<string, RawDataColumn[]>()
|
|
72
73
|
data.forEach((d) => {
|
|
73
|
-
const seriesKey = (d
|
|
74
|
+
const seriesKey = resolveGroupKey(d, encoding.series, '')
|
|
74
75
|
if (!seriesMap.has(seriesKey)) {
|
|
75
76
|
seriesMap.set(seriesKey, [])
|
|
76
77
|
}
|
|
@@ -86,7 +87,7 @@ export const createGridData = (rawData: RawData, encoding: Encoding, theme: Them
|
|
|
86
87
|
// original 排序:依照原始資料中 series 名稱出現的順序
|
|
87
88
|
const seriesOrder: string[] = []
|
|
88
89
|
data.forEach((d) => {
|
|
89
|
-
const seriesKey = (d
|
|
90
|
+
const seriesKey = resolveGroupKey(d, encoding.series, '')
|
|
90
91
|
if (!seriesOrder.includes(seriesKey)) {
|
|
91
92
|
seriesOrder.push(seriesKey)
|
|
92
93
|
}
|
|
@@ -100,7 +101,7 @@ export const createGridData = (rawData: RawData, encoding: Encoding, theme: Them
|
|
|
100
101
|
// 建立全局的 category 排序(跨該 dataset 下所有 series)
|
|
101
102
|
const globalCategoryOrder: string[] = []
|
|
102
103
|
data.forEach((d) => {
|
|
103
|
-
const categoryKey = (d
|
|
104
|
+
const categoryKey = resolveGroupKey(d, encoding.category, '')
|
|
104
105
|
if (!globalCategoryOrder.includes(categoryKey)) {
|
|
105
106
|
globalCategoryOrder.push(categoryKey)
|
|
106
107
|
}
|
|
@@ -126,7 +127,7 @@ export const createGridData = (rawData: RawData, encoding: Encoding, theme: Them
|
|
|
126
127
|
// 依據 category 欄位將 series 內的資料分組
|
|
127
128
|
const categoryMap = new Map<string, RawDataColumn[]>()
|
|
128
129
|
seriesItems.forEach((d) => {
|
|
129
|
-
const categoryKey = (d
|
|
130
|
+
const categoryKey = resolveGroupKey(d, encoding.category, '')
|
|
130
131
|
if (!categoryMap.has(categoryKey)) {
|
|
131
132
|
categoryMap.set(categoryKey, [])
|
|
132
133
|
}
|
|
@@ -147,7 +148,7 @@ export const createGridData = (rawData: RawData, encoding: Encoding, theme: Them
|
|
|
147
148
|
name: categoryName,
|
|
148
149
|
data: undefined,
|
|
149
150
|
value: null,
|
|
150
|
-
color: getColorByFrom(encoding.color.
|
|
151
|
+
color: getColorByFrom(encoding.color.by, {
|
|
151
152
|
index: categoryIndex,
|
|
152
153
|
seriesIndex,
|
|
153
154
|
categoryIndex,
|
|
@@ -169,7 +170,7 @@ export const createGridData = (rawData: RawData, encoding: Encoding, theme: Them
|
|
|
169
170
|
name: d.name || '',
|
|
170
171
|
data: d.data,
|
|
171
172
|
value: typeof value === 'number' ? value : null,
|
|
172
|
-
color: getColorByFrom(encoding.color.
|
|
173
|
+
color: getColorByFrom(encoding.color.by, {
|
|
173
174
|
index: categoryIndex,
|
|
174
175
|
seriesIndex,
|
|
175
176
|
categoryIndex,
|
|
@@ -222,7 +223,7 @@ export const createGridData = (rawData: RawData, encoding: Encoding, theme: Them
|
|
|
222
223
|
name: firstItem.name || categoryName,
|
|
223
224
|
data: firstItem.data,
|
|
224
225
|
value: aggregatedValue,
|
|
225
|
-
color: getColorByFrom(encoding.color.
|
|
226
|
+
color: getColorByFrom(encoding.color.by, {
|
|
226
227
|
index: categoryIndex,
|
|
227
228
|
seriesIndex,
|
|
228
229
|
categoryIndex,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { RawData, RawDataColumn, Encoding, ModelDataMultivariate, ModelDatumMultivariate, Theme } from '../types'
|
|
2
2
|
import { getColorByFrom } from '../utils/colorUtils'
|
|
3
|
+
import { resolveGroupKey } from '../utils/encodingUtils'
|
|
3
4
|
|
|
4
5
|
export const createMultivariateData = (rawData: RawData, encoding: Encoding, theme: Theme): ModelDataMultivariate[] => {
|
|
5
6
|
// 依據 dataset 欄位將資料分組
|
|
@@ -12,7 +13,7 @@ export const createMultivariateData = (rawData: RawData, encoding: Encoding, the
|
|
|
12
13
|
// 二維陣列:每個子陣列代表一個 dataset
|
|
13
14
|
(rawData as RawDataColumn[][]).forEach((datasetArray, datasetIndex) => {
|
|
14
15
|
datasetArray.forEach((d) => {
|
|
15
|
-
const datasetKey = (d
|
|
16
|
+
const datasetKey = resolveGroupKey(d, encoding.dataset, `dataset-${datasetIndex}`)
|
|
16
17
|
if (!datasetMap.has(datasetKey)) {
|
|
17
18
|
datasetMap.set(datasetKey, [])
|
|
18
19
|
}
|
|
@@ -22,7 +23,7 @@ export const createMultivariateData = (rawData: RawData, encoding: Encoding, the
|
|
|
22
23
|
} else {
|
|
23
24
|
// 一維陣列:依據 dataset 欄位分組
|
|
24
25
|
(rawData as RawDataColumn[]).forEach((d) => {
|
|
25
|
-
const datasetKey = (d
|
|
26
|
+
const datasetKey = resolveGroupKey(d, encoding.dataset, '')
|
|
26
27
|
if (!datasetMap.has(datasetKey)) {
|
|
27
28
|
datasetMap.set(datasetKey, [])
|
|
28
29
|
}
|
|
@@ -41,7 +42,7 @@ export const createMultivariateData = (rawData: RawData, encoding: Encoding, the
|
|
|
41
42
|
if (is2DArray) {
|
|
42
43
|
(rawData as RawDataColumn[][]).forEach((datasetArray, datasetIndex) => {
|
|
43
44
|
datasetArray.forEach((d) => {
|
|
44
|
-
const datasetKey = (d
|
|
45
|
+
const datasetKey = resolveGroupKey(d, encoding.dataset, `dataset-${datasetIndex}`)
|
|
45
46
|
if (!datasetOrder.includes(datasetKey)) {
|
|
46
47
|
datasetOrder.push(datasetKey)
|
|
47
48
|
}
|
|
@@ -49,7 +50,7 @@ export const createMultivariateData = (rawData: RawData, encoding: Encoding, the
|
|
|
49
50
|
})
|
|
50
51
|
} else {
|
|
51
52
|
(rawData as RawDataColumn[]).forEach((d) => {
|
|
52
|
-
const datasetKey = (d
|
|
53
|
+
const datasetKey = resolveGroupKey(d, encoding.dataset, '')
|
|
53
54
|
if (!datasetOrder.includes(datasetKey)) {
|
|
54
55
|
datasetOrder.push(datasetKey)
|
|
55
56
|
}
|
|
@@ -69,7 +70,7 @@ export const createMultivariateData = (rawData: RawData, encoding: Encoding, the
|
|
|
69
70
|
// 依據 series 欄位將資料分組
|
|
70
71
|
const seriesMap = new Map<string, RawDataColumn[]>()
|
|
71
72
|
data.forEach((d) => {
|
|
72
|
-
const seriesKey = (d
|
|
73
|
+
const seriesKey = resolveGroupKey(d, encoding.series, '')
|
|
73
74
|
if (!seriesMap.has(seriesKey)) {
|
|
74
75
|
seriesMap.set(seriesKey, [])
|
|
75
76
|
}
|
|
@@ -85,7 +86,7 @@ export const createMultivariateData = (rawData: RawData, encoding: Encoding, the
|
|
|
85
86
|
// original 排序:依照原始資料中 series 名稱出現的順序
|
|
86
87
|
const seriesOrder: string[] = []
|
|
87
88
|
data.forEach((d) => {
|
|
88
|
-
const seriesKey = (d
|
|
89
|
+
const seriesKey = resolveGroupKey(d, encoding.series, '')
|
|
89
90
|
if (!seriesOrder.includes(seriesKey)) {
|
|
90
91
|
seriesOrder.push(seriesKey)
|
|
91
92
|
}
|
|
@@ -104,7 +105,7 @@ export const createMultivariateData = (rawData: RawData, encoding: Encoding, the
|
|
|
104
105
|
// 依據 category 欄位將 series 內的資料分組
|
|
105
106
|
const categoryMap = new Map<string, RawDataColumn[]>()
|
|
106
107
|
seriesItems.forEach((d) => {
|
|
107
|
-
const categoryKey = (d
|
|
108
|
+
const categoryKey = resolveGroupKey(d, encoding.category, '')
|
|
108
109
|
if (!categoryMap.has(categoryKey)) {
|
|
109
110
|
categoryMap.set(categoryKey, [])
|
|
110
111
|
}
|
|
@@ -120,7 +121,7 @@ export const createMultivariateData = (rawData: RawData, encoding: Encoding, the
|
|
|
120
121
|
// original 排序:依照原始資料中 category 名稱出現的順序
|
|
121
122
|
const categoryOrder: string[] = []
|
|
122
123
|
seriesItems.forEach((d) => {
|
|
123
|
-
const categoryKey = (d
|
|
124
|
+
const categoryKey = resolveGroupKey(d, encoding.category, '')
|
|
124
125
|
if (!categoryOrder.includes(categoryKey)) {
|
|
125
126
|
categoryOrder.push(categoryKey)
|
|
126
127
|
}
|
|
@@ -155,7 +156,7 @@ export const createMultivariateData = (rawData: RawData, encoding: Encoding, the
|
|
|
155
156
|
name: d.name || '',
|
|
156
157
|
data: d.data,
|
|
157
158
|
value: null, // MultivariateData 的主要值在 values 陣列中,這裡設為 null
|
|
158
|
-
color: getColorByFrom(encoding.color.
|
|
159
|
+
color: getColorByFrom(encoding.color.by, {
|
|
159
160
|
index,
|
|
160
161
|
seriesIndex,
|
|
161
162
|
categoryIndex,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { RawData, RawDataColumn, Encoding, ModelDataSeries, ModelDatumSeries, Theme } from '../types'
|
|
2
2
|
import { aggregate } from '../utils/aggregateUtils'
|
|
3
3
|
import { getColorByFrom } from '../utils/colorUtils'
|
|
4
|
+
import { resolveGroupKey } from '../utils/encodingUtils'
|
|
4
5
|
|
|
5
6
|
export const createSeriesData = (rawData: RawData, encoding: Encoding, theme: Theme): ModelDataSeries[] => {
|
|
6
7
|
// 依據 dataset 欄位將資料分組
|
|
@@ -13,7 +14,7 @@ export const createSeriesData = (rawData: RawData, encoding: Encoding, theme: Th
|
|
|
13
14
|
// 二維陣列:每個子陣列代表一個 dataset
|
|
14
15
|
(rawData as RawDataColumn[][]).forEach((datasetArray, datasetIndex) => {
|
|
15
16
|
datasetArray.forEach((d) => {
|
|
16
|
-
const datasetKey = (d
|
|
17
|
+
const datasetKey = resolveGroupKey(d, encoding.dataset, `dataset-${datasetIndex}`)
|
|
17
18
|
if (!datasetMap.has(datasetKey)) {
|
|
18
19
|
datasetMap.set(datasetKey, [])
|
|
19
20
|
}
|
|
@@ -23,7 +24,7 @@ export const createSeriesData = (rawData: RawData, encoding: Encoding, theme: Th
|
|
|
23
24
|
} else {
|
|
24
25
|
// 一維陣列:依據 dataset 欄位分組
|
|
25
26
|
(rawData as RawDataColumn[]).forEach((d) => {
|
|
26
|
-
const datasetKey = (d
|
|
27
|
+
const datasetKey = resolveGroupKey(d, encoding.dataset, '')
|
|
27
28
|
if (!datasetMap.has(datasetKey)) {
|
|
28
29
|
datasetMap.set(datasetKey, [])
|
|
29
30
|
}
|
|
@@ -42,7 +43,7 @@ export const createSeriesData = (rawData: RawData, encoding: Encoding, theme: Th
|
|
|
42
43
|
if (is2DArray) {
|
|
43
44
|
(rawData as RawDataColumn[][]).forEach((datasetArray, datasetIndex) => {
|
|
44
45
|
datasetArray.forEach((d) => {
|
|
45
|
-
const datasetKey = (d
|
|
46
|
+
const datasetKey = resolveGroupKey(d, encoding.dataset, `dataset-${datasetIndex}`)
|
|
46
47
|
if (!datasetOrder.includes(datasetKey)) {
|
|
47
48
|
datasetOrder.push(datasetKey)
|
|
48
49
|
}
|
|
@@ -50,7 +51,7 @@ export const createSeriesData = (rawData: RawData, encoding: Encoding, theme: Th
|
|
|
50
51
|
})
|
|
51
52
|
} else {
|
|
52
53
|
(rawData as RawDataColumn[]).forEach((d) => {
|
|
53
|
-
const datasetKey = (d
|
|
54
|
+
const datasetKey = resolveGroupKey(d, encoding.dataset, '')
|
|
54
55
|
if (!datasetOrder.includes(datasetKey)) {
|
|
55
56
|
datasetOrder.push(datasetKey)
|
|
56
57
|
}
|
|
@@ -70,7 +71,7 @@ export const createSeriesData = (rawData: RawData, encoding: Encoding, theme: Th
|
|
|
70
71
|
// 依據 series 欄位將資料分組
|
|
71
72
|
const seriesMap = new Map<string, RawDataColumn[]>()
|
|
72
73
|
data.forEach((d) => {
|
|
73
|
-
const seriesKey = (d
|
|
74
|
+
const seriesKey = resolveGroupKey(d, encoding.series, '')
|
|
74
75
|
if (!seriesMap.has(seriesKey)) {
|
|
75
76
|
seriesMap.set(seriesKey, [])
|
|
76
77
|
}
|
|
@@ -86,7 +87,7 @@ export const createSeriesData = (rawData: RawData, encoding: Encoding, theme: Th
|
|
|
86
87
|
// original 排序:依照原始資料中 series 名稱出現的順序
|
|
87
88
|
const seriesOrder: string[] = []
|
|
88
89
|
data.forEach((d) => {
|
|
89
|
-
const seriesKey = (d
|
|
90
|
+
const seriesKey = resolveGroupKey(d, encoding.series, '')
|
|
90
91
|
if (!seriesOrder.includes(seriesKey)) {
|
|
91
92
|
seriesOrder.push(seriesKey)
|
|
92
93
|
}
|
|
@@ -103,7 +104,7 @@ export const createSeriesData = (rawData: RawData, encoding: Encoding, theme: Th
|
|
|
103
104
|
if (encoding.category.sort === 'original') {
|
|
104
105
|
// original:依照原始資料中 category 名稱出現的全域順序,不進行排序
|
|
105
106
|
data.forEach((d) => {
|
|
106
|
-
const categoryKey = (d
|
|
107
|
+
const categoryKey = resolveGroupKey(d, encoding.category, '')
|
|
107
108
|
if (!globalCategoryOrder.includes(categoryKey)) {
|
|
108
109
|
globalCategoryOrder.push(categoryKey)
|
|
109
110
|
}
|
|
@@ -112,14 +113,14 @@ export const createSeriesData = (rawData: RawData, encoding: Encoding, theme: Th
|
|
|
112
113
|
// alphabetical:依照字母順序排序所有 category
|
|
113
114
|
const allCategoryNames = new Set<string>()
|
|
114
115
|
data.forEach((d) => {
|
|
115
|
-
allCategoryNames.add((d
|
|
116
|
+
allCategoryNames.add(resolveGroupKey(d, encoding.category, ''))
|
|
116
117
|
})
|
|
117
118
|
globalCategoryOrder.push(...Array.from(allCategoryNames).sort((a, b) => a.localeCompare(b, undefined, { numeric: true })))
|
|
118
119
|
} else if (Array.isArray(encoding.category.sort)) {
|
|
119
120
|
// 自訂順序:指定的 category 優先,其餘依原始順序補上
|
|
120
121
|
const allCategoryNames: string[] = []
|
|
121
122
|
data.forEach((d) => {
|
|
122
|
-
const categoryKey = (d
|
|
123
|
+
const categoryKey = resolveGroupKey(d, encoding.category, '')
|
|
123
124
|
if (!allCategoryNames.includes(categoryKey)) {
|
|
124
125
|
allCategoryNames.push(categoryKey)
|
|
125
126
|
}
|
|
@@ -145,7 +146,7 @@ export const createSeriesData = (rawData: RawData, encoding: Encoding, theme: Th
|
|
|
145
146
|
// 依據 category 欄位將 series 內的資料分組
|
|
146
147
|
const categoryMap = new Map<string, RawDataColumn[]>()
|
|
147
148
|
seriesItems.forEach((d) => {
|
|
148
|
-
const categoryKey = (d
|
|
149
|
+
const categoryKey = resolveGroupKey(d, encoding.category, '')
|
|
149
150
|
if (!categoryMap.has(categoryKey)) {
|
|
150
151
|
categoryMap.set(categoryKey, [])
|
|
151
152
|
}
|
|
@@ -161,7 +162,7 @@ export const createSeriesData = (rawData: RawData, encoding: Encoding, theme: Th
|
|
|
161
162
|
if (encoding.category.sort === 'original' && encoding.value.aggregate === 'none') {
|
|
162
163
|
// sort=original + 不聚合:直接依照 seriesItems 的原始排列順序,不進行分組重排
|
|
163
164
|
seriesItems.forEach((d, index) => {
|
|
164
|
-
const categoryName = (d
|
|
165
|
+
const categoryName = resolveGroupKey(d, encoding.category, '')
|
|
165
166
|
const categoryIndex = globalCategoryIndexMap.get(categoryName)!
|
|
166
167
|
const value = (d as any)[encoding.value.from]
|
|
167
168
|
seriesData.push({
|
|
@@ -171,7 +172,7 @@ export const createSeriesData = (rawData: RawData, encoding: Encoding, theme: Th
|
|
|
171
172
|
name: d.name || '',
|
|
172
173
|
data: d.data,
|
|
173
174
|
value: typeof value === 'number' ? value : null,
|
|
174
|
-
color: getColorByFrom(encoding.color.
|
|
175
|
+
color: getColorByFrom(encoding.color.by, {
|
|
175
176
|
index: categoryIndex,
|
|
176
177
|
seriesIndex,
|
|
177
178
|
categoryIndex,
|
|
@@ -218,7 +219,7 @@ export const createSeriesData = (rawData: RawData, encoding: Encoding, theme: Th
|
|
|
218
219
|
name: d.name || '',
|
|
219
220
|
data: d.data,
|
|
220
221
|
value: typeof value === 'number' ? value : null,
|
|
221
|
-
color: getColorByFrom(encoding.color.
|
|
222
|
+
color: getColorByFrom(encoding.color.by, {
|
|
222
223
|
index: categoryIndex,
|
|
223
224
|
seriesIndex,
|
|
224
225
|
categoryIndex,
|
|
@@ -271,7 +272,7 @@ export const createSeriesData = (rawData: RawData, encoding: Encoding, theme: Th
|
|
|
271
272
|
name: firstItem.name || categoryName,
|
|
272
273
|
data: firstItem.data,
|
|
273
274
|
value: aggregatedValue,
|
|
274
|
-
color: getColorByFrom(encoding.color.
|
|
275
|
+
color: getColorByFrom(encoding.color.by, {
|
|
275
276
|
index: categoryIndex,
|
|
276
277
|
seriesIndex,
|
|
277
278
|
categoryIndex,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { RawData, RawDataColumn, Encoding, ModelDataTree, ModelDatumTree, Theme } from '../types'
|
|
2
2
|
import { aggregate } from '../utils/aggregateUtils'
|
|
3
3
|
import { getColorByFrom } from '../utils/colorUtils'
|
|
4
|
+
import { resolveGroupKey } from '../utils/encodingUtils'
|
|
4
5
|
|
|
5
6
|
export const createTreeData = (rawData: RawData, encoding: Encoding, theme: Theme): ModelDataTree[] => {
|
|
6
7
|
// 依據 dataset 欄位將資料分組
|
|
@@ -13,7 +14,7 @@ export const createTreeData = (rawData: RawData, encoding: Encoding, theme: Them
|
|
|
13
14
|
// 二維陣列:每個子陣列代表一個 dataset
|
|
14
15
|
(rawData as RawDataColumn[][]).forEach((datasetArray, datasetIndex) => {
|
|
15
16
|
datasetArray.forEach((d) => {
|
|
16
|
-
const datasetKey = (d
|
|
17
|
+
const datasetKey = resolveGroupKey(d, encoding.dataset, `dataset-${datasetIndex}`)
|
|
17
18
|
if (!datasetMap.has(datasetKey)) {
|
|
18
19
|
datasetMap.set(datasetKey, [])
|
|
19
20
|
}
|
|
@@ -23,7 +24,7 @@ export const createTreeData = (rawData: RawData, encoding: Encoding, theme: Them
|
|
|
23
24
|
} else {
|
|
24
25
|
// 一維陣列:依據 dataset 欄位分組
|
|
25
26
|
(rawData as RawDataColumn[]).forEach((d) => {
|
|
26
|
-
const datasetKey = (d
|
|
27
|
+
const datasetKey = resolveGroupKey(d, encoding.dataset, '')
|
|
27
28
|
if (!datasetMap.has(datasetKey)) {
|
|
28
29
|
datasetMap.set(datasetKey, [])
|
|
29
30
|
}
|
|
@@ -41,7 +42,7 @@ export const createTreeData = (rawData: RawData, encoding: Encoding, theme: Them
|
|
|
41
42
|
if (is2DArray) {
|
|
42
43
|
(rawData as RawDataColumn[][]).forEach((datasetArray, datasetIndex) => {
|
|
43
44
|
datasetArray.forEach((d) => {
|
|
44
|
-
const datasetKey = (d
|
|
45
|
+
const datasetKey = resolveGroupKey(d, encoding.dataset, `dataset-${datasetIndex}`)
|
|
45
46
|
if (!datasetOrder.includes(datasetKey)) {
|
|
46
47
|
datasetOrder.push(datasetKey)
|
|
47
48
|
}
|
|
@@ -49,7 +50,7 @@ export const createTreeData = (rawData: RawData, encoding: Encoding, theme: Them
|
|
|
49
50
|
})
|
|
50
51
|
} else {
|
|
51
52
|
(rawData as RawDataColumn[]).forEach((d) => {
|
|
52
|
-
const datasetKey = (d
|
|
53
|
+
const datasetKey = resolveGroupKey(d, encoding.dataset, '')
|
|
53
54
|
if (!datasetOrder.includes(datasetKey)) {
|
|
54
55
|
datasetOrder.push(datasetKey)
|
|
55
56
|
}
|
|
@@ -68,7 +69,7 @@ export const createTreeData = (rawData: RawData, encoding: Encoding, theme: Them
|
|
|
68
69
|
// 建立 dataset 內排序後的 series 名稱陣列(僅作為屬性與色彩來源,不做拆樹)
|
|
69
70
|
const seriesMap = new Map<string, RawDataColumn[]>()
|
|
70
71
|
data.forEach((d) => {
|
|
71
|
-
const seriesKey = (d
|
|
72
|
+
const seriesKey = resolveGroupKey(d, encoding.series, '')
|
|
72
73
|
if (!seriesMap.has(seriesKey)) {
|
|
73
74
|
seriesMap.set(seriesKey, [])
|
|
74
75
|
}
|
|
@@ -81,7 +82,7 @@ export const createTreeData = (rawData: RawData, encoding: Encoding, theme: Them
|
|
|
81
82
|
} else if (encoding.series.sort === 'original') {
|
|
82
83
|
const seriesOrder: string[] = []
|
|
83
84
|
data.forEach((d) => {
|
|
84
|
-
const seriesKey = (d
|
|
85
|
+
const seriesKey = resolveGroupKey(d, encoding.series, '')
|
|
85
86
|
if (!seriesOrder.includes(seriesKey)) {
|
|
86
87
|
seriesOrder.push(seriesKey)
|
|
87
88
|
}
|
|
@@ -94,7 +95,7 @@ export const createTreeData = (rawData: RawData, encoding: Encoding, theme: Them
|
|
|
94
95
|
// 建立 dataset 內排序後的 category 名稱陣列(僅作為屬性與色彩來源,不做拆樹)
|
|
95
96
|
const categoryMap = new Map<string, RawDataColumn[]>()
|
|
96
97
|
data.forEach((d) => {
|
|
97
|
-
const categoryKey = (d
|
|
98
|
+
const categoryKey = resolveGroupKey(d, encoding.category, '')
|
|
98
99
|
if (!categoryMap.has(categoryKey)) {
|
|
99
100
|
categoryMap.set(categoryKey, [])
|
|
100
101
|
}
|
|
@@ -107,7 +108,7 @@ export const createTreeData = (rawData: RawData, encoding: Encoding, theme: Them
|
|
|
107
108
|
} else if (encoding.category.sort === 'original') {
|
|
108
109
|
const categoryOrder: string[] = []
|
|
109
110
|
data.forEach((d) => {
|
|
110
|
-
const categoryKey = (d
|
|
111
|
+
const categoryKey = resolveGroupKey(d, encoding.category, '')
|
|
111
112
|
if (!categoryOrder.includes(categoryKey)) {
|
|
112
113
|
categoryOrder.push(categoryKey)
|
|
113
114
|
}
|
|
@@ -128,7 +129,7 @@ export const createTreeData = (rawData: RawData, encoding: Encoding, theme: Them
|
|
|
128
129
|
})
|
|
129
130
|
|
|
130
131
|
let globalNodeIndex = 0
|
|
131
|
-
const defaultSeriesName = sortedSeriesNames[0] || '
|
|
132
|
+
const defaultSeriesName = sortedSeriesNames[0] || ''
|
|
132
133
|
const defaultSeriesIndex = sortedSeriesNames.indexOf(defaultSeriesName)
|
|
133
134
|
|
|
134
135
|
// 為每個 id 建立 ModelDatumTree,並進行聚合
|
|
@@ -136,9 +137,9 @@ export const createTreeData = (rawData: RawData, encoding: Encoding, theme: Them
|
|
|
136
137
|
|
|
137
138
|
Array.from(nodeMap.entries()).forEach(([nodeId, nodeItems]) => {
|
|
138
139
|
const firstItem = nodeItems[0]
|
|
139
|
-
const seriesName = (firstItem
|
|
140
|
+
const seriesName = resolveGroupKey(firstItem, encoding.series, '')
|
|
140
141
|
const seriesIndex = sortedSeriesNames.indexOf(seriesName)
|
|
141
|
-
const categoryName = (firstItem
|
|
142
|
+
const categoryName = resolveGroupKey(firstItem, encoding.category, '')
|
|
142
143
|
const categoryIndex = sortedCategoryNames.indexOf(categoryName)
|
|
143
144
|
|
|
144
145
|
if (encoding.value.aggregate === 'none') {
|
|
@@ -151,7 +152,7 @@ export const createTreeData = (rawData: RawData, encoding: Encoding, theme: Them
|
|
|
151
152
|
name: firstItem.name || nodeId,
|
|
152
153
|
data: firstItem.data,
|
|
153
154
|
value: typeof value === 'number' ? value : null,
|
|
154
|
-
color: getColorByFrom(encoding.color.
|
|
155
|
+
color: getColorByFrom(encoding.color.by, {
|
|
155
156
|
index: globalNodeIndex - 1,
|
|
156
157
|
seriesIndex,
|
|
157
158
|
categoryIndex,
|
|
@@ -187,7 +188,7 @@ export const createTreeData = (rawData: RawData, encoding: Encoding, theme: Them
|
|
|
187
188
|
name: firstItem.name || nodeId,
|
|
188
189
|
data: firstItem.data,
|
|
189
190
|
value: aggregatedValue,
|
|
190
|
-
color: getColorByFrom(encoding.color.
|
|
191
|
+
color: getColorByFrom(encoding.color.by, {
|
|
191
192
|
index: globalNodeIndex - 1,
|
|
192
193
|
seriesIndex,
|
|
193
194
|
categoryIndex,
|
|
@@ -283,7 +284,7 @@ export const createTreeData = (rawData: RawData, encoding: Encoding, theme: Them
|
|
|
283
284
|
name: 'Empty Tree',
|
|
284
285
|
data: {},
|
|
285
286
|
value: null,
|
|
286
|
-
color: getColorByFrom(encoding.color.
|
|
287
|
+
color: getColorByFrom(encoding.color.by, {
|
|
287
288
|
index: globalNodeIndex - 1,
|
|
288
289
|
seriesIndex: defaultSeriesIndex < 0 ? 0 : defaultSeriesIndex,
|
|
289
290
|
categoryIndex: 0,
|
|
@@ -296,7 +297,7 @@ export const createTreeData = (rawData: RawData, encoding: Encoding, theme: Them
|
|
|
296
297
|
children: [],
|
|
297
298
|
series: defaultSeriesName,
|
|
298
299
|
seriesIndex: defaultSeriesIndex < 0 ? 0 : defaultSeriesIndex,
|
|
299
|
-
category: '
|
|
300
|
+
category: '',
|
|
300
301
|
categoryIndex: 0,
|
|
301
302
|
})
|
|
302
303
|
} else if (trees.length === 1) {
|
|
@@ -310,7 +311,7 @@ export const createTreeData = (rawData: RawData, encoding: Encoding, theme: Them
|
|
|
310
311
|
name: `Virtual Root (${datasetName})`,
|
|
311
312
|
data: {},
|
|
312
313
|
value: null,
|
|
313
|
-
color: getColorByFrom(encoding.color.
|
|
314
|
+
color: getColorByFrom(encoding.color.by, {
|
|
314
315
|
index: globalNodeIndex - 1,
|
|
315
316
|
seriesIndex: defaultSeriesIndex < 0 ? 0 : defaultSeriesIndex,
|
|
316
317
|
categoryIndex: 0,
|
|
@@ -333,7 +334,7 @@ export const createTreeData = (rawData: RawData, encoding: Encoding, theme: Them
|
|
|
333
334
|
}),
|
|
334
335
|
series: defaultSeriesName,
|
|
335
336
|
seriesIndex: defaultSeriesIndex < 0 ? 0 : defaultSeriesIndex,
|
|
336
|
-
category: '
|
|
337
|
+
category: '',
|
|
337
338
|
categoryIndex: 0,
|
|
338
339
|
}
|
|
339
340
|
result.push(virtualRoot)
|