Ray1ee01 commited on
Commit
c8a0ba2
·
verified ·
1 Parent(s): 3c00a10

Upload folder using huggingface_hub

Browse files
modules/chart_engine/template/d3-js/type73_pyramid_chart/pyramid_chart_01.js ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ REQUIREMENTS_BEGIN
3
+ {
4
+ "chart_type": "Pyramid Chart",
5
+ "chart_name": "pyramid_chart_01",
6
+ "required_fields": ["x", "y"],
7
+ "required_fields_type": [["categorical"], ["numerical"]],
8
+ "required_fields_range": [[3, 10], [0, 100]],
9
+ "required_fields_icons": [],
10
+ "required_other_icons": [],
11
+ "required_fields_colors": ["x"],
12
+ "required_other_colors": [],
13
+ "supported_effects": [],
14
+ "min_height": 400,
15
+ "min_width": 400,
16
+ "background": "light",
17
+ "icon_mark": "none",
18
+ "icon_label": "none",
19
+ "has_x_axis": "no",
20
+ "has_y_axis": "no"
21
+ }
22
+ REQUIREMENTS_END
23
+ */
24
+
25
+ function makeChart(containerSelector, data) {
26
+ // 提取数据
27
+ const jsonData = data;
28
+ const chartData = jsonData.data.data;
29
+ const variables = jsonData.variables;
30
+ const typography = jsonData.typography;
31
+ const colors = jsonData.colors || {};
32
+ const colorResolver = chartUtils.color.resolver(jsonData);
33
+ const dataColumns = chartUtils.schema.columns(jsonData);
34
+ const images = jsonData.images || {};
35
+
36
+ // 清空容器
37
+ d3.select(containerSelector).html("");
38
+
39
+ // 获取字段名
40
+ const categoryField = chartUtils.schema.columnField(dataColumns, 0);
41
+ const valueField = chartUtils.schema.columnField(dataColumns, 1);
42
+
43
+ // 按值从小到大排序数据(小的在顶部)
44
+ const sortedData = [...chartData].sort((a, b) => +a[valueField] - +b[valueField]);
45
+
46
+ // 计算总和以获取百分比
47
+ const total = d3.sum(sortedData, d => +d[valueField]);
48
+
49
+ // 为每个数据点添加百分比和累积百分比
50
+ let cumulativePercent = 0;
51
+ sortedData.forEach(d => {
52
+ d.percent = (+d[valueField] / total) * 100;
53
+ d.cumulativePercentStart = cumulativePercent;
54
+ cumulativePercent += d.percent;
55
+ d.cumulativePercentEnd = cumulativePercent;
56
+ });
57
+
58
+ // 设置尺寸和边距
59
+ const width = variables.width;
60
+ const height = variables.height;
61
+ const margin = { top: 40, right: 120, bottom: 40, left: 60 };
62
+
63
+ // 创建SVG
64
+ const svg = d3.select(containerSelector)
65
+ .append("svg")
66
+ .attr("width", "100%")
67
+ .attr("height", height)
68
+ .attr("viewBox", `0 0 ${width} ${height}`)
69
+ .attr("style", "max-width: 100%; height: auto;")
70
+ .attr("xmlns", "http://www.w3.org/2000/svg")
71
+ .attr("xmlns:xlink", "http://www.w3.org/1999/xlink");
72
+
73
+ // 创建图表区域
74
+ const chartWidth = width - margin.left - margin.right;
75
+ const chartHeight = height - margin.top - margin.bottom;
76
+
77
+ const g = svg.append("g")
78
+ .attr("transform", `translate(${margin.left}, ${margin.top})`);
79
+
80
+ // 计算金字塔的最大宽度(底部)和高度
81
+ const maxPyramidWidth = chartWidth * 0.6;
82
+ const pyramidHeight = chartHeight * 0.6; // 使用90%的高度,留出上下空间
83
+
84
+ // 计算面积比例
85
+ // 金字塔总面积
86
+ const totalArea = maxPyramidWidth * pyramidHeight / 2;
87
+
88
+ // 计算每个部分的高度(基于面积比例)
89
+ let currentHeight = 0;
90
+ const sections = [];
91
+
92
+ sortedData.forEach((d, i) => {
93
+ // 该部分应占的面积比例
94
+ const areaRatio = d.percent / 100;
95
+ const sectionArea = totalArea * areaRatio;
96
+
97
+ // 计算该部分的高度
98
+ // 对于梯形,面积 = (上底+下底) * 高 / 2
99
+ // 我们需要求解高度,已知面积和下底(上一部分的上底)
100
+
101
+ // 首先计算该部分在整个三角形中的相对位置
102
+ const bottomPosition = currentHeight / pyramidHeight;
103
+ // 正三角形:底部宽,顶部窄
104
+ const bottomWidth = maxPyramidWidth * bottomPosition;
105
+
106
+ // 求解该部分的高度
107
+ // 设高度为h,则上底 = maxPyramidWidth * (currentHeight + h) / pyramidHeight
108
+ // 面积方程:sectionArea = (bottomWidth + topWidth) * h / 2
109
+
110
+ // 简化后的二次方程:
111
+ // h^2 * (maxPyramidWidth / (2 * pyramidHeight)) + h * bottomWidth - 2 * sectionArea = 0
112
+
113
+ const a = maxPyramidWidth / (2 * pyramidHeight);
114
+ const b = bottomWidth;
115
+ const c = -2 * sectionArea;
116
+
117
+ // 使用求根公式
118
+ const h = (-b + Math.sqrt(b*b - 4*a*c)) / (2*a);
119
+
120
+ // 计算该部分的上底宽度
121
+ const topPosition = (currentHeight + h) / pyramidHeight;
122
+ const topWidth = maxPyramidWidth * topPosition;
123
+
124
+ sections.push({
125
+ data: d,
126
+ bottomY: currentHeight,
127
+ topY: currentHeight + h,
128
+ bottomWidth: bottomWidth,
129
+ topWidth: topWidth
130
+ });
131
+
132
+ currentHeight += h;
133
+ });
134
+
135
+ // 计算垂直居中的偏移量
136
+ const verticalOffset = (chartHeight - pyramidHeight) / 2;
137
+
138
+ // 绘制金字塔的每一层
139
+ sections.forEach((section, i) => {
140
+ const d = section.data;
141
+
142
+ // 获取颜色
143
+ const color = colorResolver.field(d[categoryField], i, { palette: "category10" }).value;
144
+
145
+ // 绘制梯形 - 添加垂直偏移
146
+ const points = [
147
+ [chartWidth / 2 - section.topWidth / 2, section.topY + verticalOffset],
148
+ [chartWidth / 2 + section.topWidth / 2, section.topY + verticalOffset],
149
+ [chartWidth / 2 + section.bottomWidth / 2, section.bottomY + verticalOffset],
150
+ [chartWidth / 2 - section.bottomWidth / 2, section.bottomY + verticalOffset]
151
+ ];
152
+
153
+ g.append("polygon")
154
+ .attr("points", points.map(p => p.join(",")).join(" "))
155
+ .attr("fill", color);
156
+
157
+ // 计算标签位置,避免重叠 - 添加垂直偏移
158
+ const labelY = (section.topY + section.bottomY) / 2 + verticalOffset;
159
+ const labelX = chartWidth / 2 + Math.max(section.topWidth, section.bottomWidth) / 2 + 10;
160
+
161
+ // 添加标签
162
+ g.append("text")
163
+ .attr("x", labelX)
164
+ .attr("y", labelY)
165
+ .attr("dominant-baseline", "middle")
166
+ .attr("font-size", "14px")
167
+ .attr("font-weight", "bold")
168
+ .text(`${d[categoryField]} ${chartUtils.format.percent(d.percent, { digits: 0 }).text}`);
169
+ });
170
+
171
+ return svg.node();
172
+ }
modules/chart_engine/template/d3-js/type73_pyramid_chart/pyramid_chart_02.js ADDED
@@ -0,0 +1,174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ REQUIREMENTS_BEGIN
3
+ {
4
+ "chart_type": "Pyramid Chart",
5
+ "chart_name": "pyramid_chart_02",
6
+ "required_fields": ["x", "y"],
7
+ "required_fields_type": [["categorical"], ["numerical"]],
8
+ "required_fields_range": [[3, 10], [0, 100]],
9
+ "required_fields_icons": [],
10
+ "required_other_icons": [],
11
+ "required_fields_colors": ["x"],
12
+ "required_other_colors": [],
13
+ "supported_effects": [],
14
+ "min_height": 400,
15
+ "min_width": 400,
16
+ "background": "light",
17
+ "icon_mark": "none",
18
+ "icon_label": "none",
19
+ "has_x_axis": "no",
20
+ "has_y_axis": "no"
21
+ }
22
+ REQUIREMENTS_END
23
+ */
24
+
25
+ function makeChart(containerSelector, data) {
26
+ // 提取数据
27
+ const jsonData = data;
28
+ const chartData = jsonData.data.data;
29
+ const variables = jsonData.variables;
30
+ const typography = jsonData.typography;
31
+ const colors = jsonData.colors || {};
32
+ const colorResolver = chartUtils.color.resolver(jsonData);
33
+ const dataColumns = chartUtils.schema.columns(jsonData);
34
+ const images = jsonData.images || {};
35
+
36
+ // 清空容器
37
+ d3.select(containerSelector).html("");
38
+
39
+ // 获取字段名
40
+ const categoryField = chartUtils.schema.columnField(dataColumns, 0);
41
+ const valueField = chartUtils.schema.columnField(dataColumns, 1);
42
+
43
+ // 按值从小到大排序数据(小的在顶部)
44
+ const sortedData = [...chartData].sort((a, b) => +a[valueField] - +b[valueField]);
45
+
46
+ // 计算总和以获取百分比
47
+ const total = d3.sum(sortedData, d => +d[valueField]);
48
+
49
+ // 为每个数据点添加百分比和累积百分比
50
+ let cumulativePercent = 0;
51
+ sortedData.forEach(d => {
52
+ d.percent = (+d[valueField] / total) * 100;
53
+ d.cumulativePercentStart = cumulativePercent;
54
+ cumulativePercent += d.percent;
55
+ d.cumulativePercentEnd = cumulativePercent;
56
+ });
57
+
58
+ // 设置尺寸和边距
59
+ const width = variables.width;
60
+ const height = variables.height;
61
+ const margin = { top: 40, right: 120, bottom: 40, left: 60 };
62
+
63
+ // 创建SVG
64
+ const svg = d3.select(containerSelector)
65
+ .append("svg")
66
+ .attr("width", "100%")
67
+ .attr("height", height)
68
+ .attr("viewBox", `0 0 ${width} ${height}`)
69
+ .attr("style", "max-width: 100%; height: auto;")
70
+ .attr("xmlns", "http://www.w3.org/2000/svg")
71
+ .attr("xmlns:xlink", "http://www.w3.org/1999/xlink");
72
+
73
+ // 创建图表区域
74
+ const chartWidth = width - margin.left - margin.right;
75
+ const chartHeight = height - margin.top - margin.bottom;
76
+
77
+ const g = svg.append("g")
78
+ .attr("transform", `translate(${margin.left}, ${margin.top})`);
79
+
80
+ // 计算金字塔的最大宽度(底部)和高度
81
+ const maxPyramidWidth = chartWidth * 0.6;
82
+ const pyramidHeight = chartHeight * 0.6; // 使用90%的高度,留出上下空间
83
+
84
+ // 计算面积比例
85
+ // 金字塔总面积
86
+ const totalArea = maxPyramidWidth * pyramidHeight / 2;
87
+
88
+ // 计算每个部分的高度(基于面积比例)
89
+ let currentHeight = 0;
90
+ const sections = [];
91
+
92
+ sortedData.forEach((d, i) => {
93
+ // 该部分应占的面积比例
94
+ const areaRatio = d.percent / 100;
95
+ const sectionArea = totalArea * areaRatio;
96
+
97
+ // 计算该部分的高度
98
+ // 对于梯形,面积 = (上底+下底) * 高 / 2
99
+ // 我们需要求解高度,已知面积和下底(上一部分的上底)
100
+
101
+ // 首先计算该部分在整个三角形中的相对位置
102
+ const bottomPosition = currentHeight / pyramidHeight;
103
+ // 正三角形:底部宽,顶部窄
104
+ const bottomWidth = maxPyramidWidth * bottomPosition;
105
+
106
+ // 求解该部分的高度
107
+ // 设高度为h,则上底 = maxPyramidWidth * (currentHeight + h) / pyramidHeight
108
+ // 面积方程:sectionArea = (bottomWidth + topWidth) * h / 2
109
+
110
+ // 简化后的二次方程:
111
+ // h^2 * (maxPyramidWidth / (2 * pyramidHeight)) + h * bottomWidth - 2 * sectionArea = 0
112
+
113
+ const a = maxPyramidWidth / (2 * pyramidHeight);
114
+ const b = bottomWidth;
115
+ const c = -2 * sectionArea;
116
+
117
+ // 使用求根公式
118
+ const h = (-b + Math.sqrt(b*b - 4*a*c)) / (2*a);
119
+
120
+ // 计算该部分的上底宽度
121
+ const topPosition = (currentHeight + h) / pyramidHeight;
122
+ const topWidth = maxPyramidWidth * topPosition;
123
+
124
+ sections.push({
125
+ data: d,
126
+ bottomY: currentHeight,
127
+ topY: currentHeight + h,
128
+ bottomWidth: bottomWidth,
129
+ topWidth: topWidth
130
+ });
131
+
132
+ currentHeight += h;
133
+ });
134
+
135
+ // 计算垂直居中的偏移量
136
+ const verticalOffset = (chartHeight - pyramidHeight) / 2;
137
+
138
+ const y_padding = 5;
139
+
140
+ // 绘制金字塔的每一层
141
+ sections.forEach((section, i) => {
142
+ const d = section.data;
143
+
144
+ // 获取颜色
145
+ const color = colorResolver.field(d[categoryField], i, { palette: "category10" }).value;
146
+
147
+ // 绘制梯形 - 添加垂直偏移
148
+ const points = [
149
+ [chartWidth / 2 - section.topWidth / 2, section.topY + verticalOffset + y_padding * i],
150
+ [chartWidth / 2 + section.topWidth / 2, section.topY + verticalOffset + y_padding * i],
151
+ [chartWidth / 2 + section.bottomWidth / 2, section.bottomY + verticalOffset + y_padding * i],
152
+ [chartWidth / 2 - section.bottomWidth / 2, section.bottomY + verticalOffset + y_padding * i]
153
+ ];
154
+
155
+ g.append("polygon")
156
+ .attr("points", points.map(p => p.join(",")).join(" "))
157
+ .attr("fill", color);
158
+
159
+ // 计算标签位置,避免重叠 - 添加垂直偏移
160
+ const labelY = (section.topY + section.bottomY) / 2 + verticalOffset + y_padding * i;
161
+ const labelX = chartWidth / 2 + Math.max(section.topWidth, section.bottomWidth) / 2 + 10;
162
+
163
+ // 添加标签
164
+ g.append("text")
165
+ .attr("x", labelX)
166
+ .attr("y", labelY)
167
+ .attr("dominant-baseline", "middle")
168
+ .attr("font-size", "14px")
169
+ .attr("font-weight", "bold")
170
+ .text(`${d[categoryField]} ${chartUtils.format.percent(d.percent, { digits: 0 }).text}`);
171
+ });
172
+
173
+ return svg.node();
174
+ }