init
This commit is contained in:
commit
9cc8ed5d1b
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
node_modules
|
BIN
WechatIMG493.jpg
Normal file
BIN
WechatIMG493.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 221 KiB |
124
index.html
Normal file
124
index.html
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>D3 Flowchart</title>
|
||||||
|
<script src="https://d3js.org/d3.v7.min.js"></script>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: sans-serif;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<svg id="flowchart" width="1400" height="900"></svg>
|
||||||
|
<script>
|
||||||
|
const nodes = [
|
||||||
|
// Overall Goal
|
||||||
|
{ id: 'G1', x: 550, y: 20, width: 300, height: 50, color: '#2E8B57' }, // darkseagreen
|
||||||
|
|
||||||
|
// Main Pillars
|
||||||
|
{ id: 'P1', x: 50, y: 150, width: 300, height: 50, color: 'black' },
|
||||||
|
{ id: 'P2', x: 550, y: 150, width: 300, height: 50, color: 'black' },
|
||||||
|
{ id: 'P3', x: 1050, y: 150, width: 300, height: 50, color: 'black' },
|
||||||
|
|
||||||
|
// Column 1
|
||||||
|
{ id: 'W1', x: 250, y: 300, width: 150, height: 50, color: 'white', stroke: 'black' },
|
||||||
|
{ id: 'N1e', x: 50, y: 250, width: 150, height: 50, color: 'lightgrey' },
|
||||||
|
{ id: 'N1f', x: 50, y: 320, width: 150, height: 50, color: 'lightgrey' },
|
||||||
|
{ id: 'N1b_2d', x: 250, y: 400, width: 150, height: 70, color: 'lightgrey' },
|
||||||
|
{ id: 'W2', x: 50, y: 600, width: 150, height: 70, color: 'white', stroke: 'black' },
|
||||||
|
{ id: 'N1c', x: 50, y: 450, width: 150, height: 50, color: 'lightgrey' },
|
||||||
|
{ id: 'N1d', x: 50, y: 520, width: 150, height: 50, color: 'lightgrey' },
|
||||||
|
{ id: 'N1a', x: 250, y: 520, width: 150, height: 50, color: 'lightgrey' },
|
||||||
|
|
||||||
|
// Column 2
|
||||||
|
{ id: 'W3', x: 450, y: 300, width: 150, height: 50, color: 'white', stroke: 'black' },
|
||||||
|
{ id: 'N2a', x: 450, y: 400, width: 150, height: 70, color: 'lightgrey' },
|
||||||
|
{ id: 'W4', x: 650, y: 520, width: 150, height: 70, color: 'white', stroke: 'black' },
|
||||||
|
{ id: 'N2b', x: 450, y: 520, width: 150, height: 50, color: 'lightgrey' },
|
||||||
|
{ id: 'N2c', x: 650, y: 650, width: 150, height: 50, color: 'lightgrey' },
|
||||||
|
|
||||||
|
// Column 3
|
||||||
|
{ id: 'W5', x: 850, y: 300, width: 150, height: 70, color: 'white', stroke: 'black' },
|
||||||
|
{ id: 'W6', x: 1050, y: 300, width: 150, height: 50, color: 'white', stroke: 'black' },
|
||||||
|
{ id: 'W7', x: 1250, y: 300, width: 150, height: 50, color: 'white', stroke: 'black' },
|
||||||
|
{ id: 'N3a', x: 850, y: 450, width: 150, height: 70, color: 'lightgrey' },
|
||||||
|
{ id: 'N3b', x: 1050, y: 450, width: 150, height: 70, color: 'lightgrey' },
|
||||||
|
{ id: 'N3c', x: 1250, y: 450, width: 150, height: 70, color: 'lightgrey' }
|
||||||
|
];
|
||||||
|
|
||||||
|
const links = [
|
||||||
|
{ source: 'P1', target: 'G1', path: 'M200,150 V95 H700 V70' },
|
||||||
|
{ source: 'P2', target: 'G1', path: 'M700,150 V70' },
|
||||||
|
{ source: 'P3', target: 'G1', path: 'M1200,150 V95 H700 V70' },
|
||||||
|
{ source: 'W1', target: 'P1', path: 'M325,300 V225 H200 V200' },
|
||||||
|
{ source: 'N1e', target: 'W1', path: 'M125,275 H250' },
|
||||||
|
{ source: 'N1f', target: 'W1', path: 'M125,345 H250' },
|
||||||
|
{ source: 'N1b_2d', target: 'W1', path: 'M325,400 V350' },
|
||||||
|
{ source: 'W2', target: 'P1', path: 'M125,600 V225 H200 V200' },
|
||||||
|
{ source: 'N1c', target: 'W2', path: 'M125,475 V580 H125 V600' },
|
||||||
|
{ source: 'N1d', target: 'W2', path: 'M125,545 V600' },
|
||||||
|
{ source: 'N1a', target: 'W2', path: 'M325,545 H200 V600' },
|
||||||
|
{ source: 'W3', target: 'P2', path: 'M525,300 V225 H700 V200' },
|
||||||
|
{ source: 'N1b_2d', target: 'W3', path: 'M400,435 H450' },
|
||||||
|
{ source: 'N2a', target: 'W3', path: 'M525,400 V350' },
|
||||||
|
{ source: 'W4', target: 'P2', path: 'M725,520 V225 H700 V200' },
|
||||||
|
{ source: 'N2a', target: 'W4', path: 'M525,470 V555 H650' },
|
||||||
|
{ source: 'N2b', target: 'W4', path: 'M525,545 H650' },
|
||||||
|
{ source: 'N2c', target: 'W4', path: 'M725,650 V590' },
|
||||||
|
{ source: 'W5', target: 'P3', path: 'M925,300 V225 H1200 V200' },
|
||||||
|
{ source: 'N3a', target: 'W5', path: 'M925,450 V370' },
|
||||||
|
{ source: 'W6', target: 'P3', path: 'M1125,300 V225 H1200 V200' },
|
||||||
|
{ source: 'N3b', target: 'W6', path: 'M1125,450 V350' },
|
||||||
|
{ source: 'W7', target: 'P3', path: 'M1325,300 V225 H1200 V200' },
|
||||||
|
{ source: 'N3c', target: 'W7', path: 'M1325,450 V350' },
|
||||||
|
{ source: 'W1', target: 'W3', path: 'M400,325 H450' },
|
||||||
|
{ source: 'N2a', target: 'N1b_2d', path: 'M450,435 H400' },
|
||||||
|
{ source: 'W2', target: 'W4', path: 'M200,635 H650' }
|
||||||
|
];
|
||||||
|
|
||||||
|
const svg = d3.select("#flowchart");
|
||||||
|
|
||||||
|
// Define arrow marker
|
||||||
|
svg.append("defs").append("marker")
|
||||||
|
.attr("id", "arrow")
|
||||||
|
.attr("viewBox", "0 -5 10 10")
|
||||||
|
.attr("refX", 8)
|
||||||
|
.attr("refY", 0)
|
||||||
|
.attr("markerWidth", 6)
|
||||||
|
.attr("markerHeight", 6)
|
||||||
|
.attr("orient", "auto")
|
||||||
|
.append("path")
|
||||||
|
.attr("d", "M0,-5L10,0L0,5")
|
||||||
|
.attr("fill", "black");
|
||||||
|
|
||||||
|
// Draw links
|
||||||
|
svg.append("g")
|
||||||
|
.selectAll("path")
|
||||||
|
.data(links)
|
||||||
|
.enter().append("path")
|
||||||
|
.attr("d", d => d.path)
|
||||||
|
.attr("stroke", "black")
|
||||||
|
.attr("stroke-width", 1.5)
|
||||||
|
.attr("fill", "none")
|
||||||
|
.attr("marker-end", "url(#arrow)");
|
||||||
|
|
||||||
|
// Draw nodes
|
||||||
|
const nodeGroup = svg.append("g")
|
||||||
|
.selectAll("g")
|
||||||
|
.data(nodes)
|
||||||
|
.enter().append("g")
|
||||||
|
.attr("transform", d => `translate(${d.x}, ${d.y})`);
|
||||||
|
|
||||||
|
nodeGroup.append("rect")
|
||||||
|
.attr("width", d => d.width)
|
||||||
|
.attr("height", d => d.height)
|
||||||
|
.attr("fill", d => d.color)
|
||||||
|
.attr("stroke", d => d.stroke || "none")
|
||||||
|
.attr("stroke-width", 1.5);
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
389
test1-claude-4/index.html
Normal file
389
test1-claude-4/index.html
Normal file
@ -0,0 +1,389 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>US Critical Minerals Security Strategy</title>
|
||||||
|
<script src="https://d3js.org/d3.v7.min.js"></script>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 20px;
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
background: white;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.overall-goal {
|
||||||
|
background: #8bc34a;
|
||||||
|
color: white;
|
||||||
|
padding: 20px;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 18px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goal-title {
|
||||||
|
font-size: 14px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goal-text {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.node {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goal-node {
|
||||||
|
fill: #2c3e50;
|
||||||
|
stroke: #2c3e50;
|
||||||
|
stroke-width: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-node {
|
||||||
|
fill: #95a5a6;
|
||||||
|
stroke: #7f8c8d;
|
||||||
|
stroke-width: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kpi-node {
|
||||||
|
fill: white;
|
||||||
|
stroke: #2c3e50;
|
||||||
|
stroke-width: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.node-text {
|
||||||
|
fill: white;
|
||||||
|
font-size: 12px;
|
||||||
|
text-anchor: middle;
|
||||||
|
dominant-baseline: middle;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kpi-text {
|
||||||
|
fill: #2c3e50;
|
||||||
|
font-size: 10px;
|
||||||
|
text-anchor: middle;
|
||||||
|
dominant-baseline: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrow {
|
||||||
|
fill: none;
|
||||||
|
stroke: #666;
|
||||||
|
stroke-width: 2;
|
||||||
|
marker-end: url(#arrowhead);
|
||||||
|
}
|
||||||
|
|
||||||
|
.goal-number {
|
||||||
|
fill: white;
|
||||||
|
font-size: 48px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-anchor: middle;
|
||||||
|
dominant-baseline: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goal-title-text {
|
||||||
|
fill: white;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-anchor: start;
|
||||||
|
dominant-baseline: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goal-description {
|
||||||
|
fill: white;
|
||||||
|
font-size: 10px;
|
||||||
|
text-anchor: start;
|
||||||
|
dominant-baseline: middle;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="overall-goal">
|
||||||
|
<div class="goal-title">OVERALL GOAL</div>
|
||||||
|
<div class="goal-text">US critical minerals security</div>
|
||||||
|
</div>
|
||||||
|
<svg id="flowchart" width="1160" height="800"></svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const svg = d3.select("#flowchart");
|
||||||
|
const width = 1160;
|
||||||
|
const height = 800;
|
||||||
|
|
||||||
|
// 定义箭头标记
|
||||||
|
svg.append("defs").append("marker")
|
||||||
|
.attr("id", "arrowhead")
|
||||||
|
.attr("viewBox", "0 -5 10 10")
|
||||||
|
.attr("refX", 8)
|
||||||
|
.attr("refY", 0)
|
||||||
|
.attr("markerWidth", 6)
|
||||||
|
.attr("markerHeight", 6)
|
||||||
|
.attr("orient", "auto")
|
||||||
|
.append("path")
|
||||||
|
.attr("d", "M0,-5L10,0L0,5")
|
||||||
|
.attr("fill", "#666");
|
||||||
|
|
||||||
|
// 主要目标节点数据
|
||||||
|
const goalNodes = [
|
||||||
|
{
|
||||||
|
id: "goal1",
|
||||||
|
x: 160,
|
||||||
|
y: 180,
|
||||||
|
width: 300,
|
||||||
|
height: 80,
|
||||||
|
number: "1",
|
||||||
|
title: "Rebuild US critical minerals industry competitiveness",
|
||||||
|
kpi: "KPIs: Average time from discovery to construction; no. of workers in CM industry; no. of projects in pipeline"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "goal2",
|
||||||
|
x: 480,
|
||||||
|
y: 180,
|
||||||
|
width: 300,
|
||||||
|
height: 80,
|
||||||
|
number: "2",
|
||||||
|
title: "Reduce import reliance from non-allied countries",
|
||||||
|
kpi: "KPIs: Percentage of US critical minerals imported from or processed by non-allied countries"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "goal3",
|
||||||
|
x: 800,
|
||||||
|
y: 180,
|
||||||
|
width: 300,
|
||||||
|
height: 80,
|
||||||
|
number: "3",
|
||||||
|
title: "Reduce the risk of critical minerals market disruptions",
|
||||||
|
kpi: "KPIs: No. of months of critical mineral reserves; percentage of value chain data collected"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
// 行动节点数据
|
||||||
|
const actionNodes = [
|
||||||
|
// Goal 1 actions
|
||||||
|
{ id: "1a", x: 50, y: 320, width: 120, height: 60, text: "(1a) Develop critical minerals workforce" },
|
||||||
|
{ id: "1b", x: 50, y: 400, width: 120, height: 60, text: "(1b) Increase investment in R&D for new tech and AI usage" },
|
||||||
|
{ id: "1c", x: 50, y: 480, width: 120, height: 60, text: "(1c) Improve permitting and regulations" },
|
||||||
|
{ id: "1d", x: 50, y: 560, width: 120, height: 60, text: "(1d) Support domestic exploration with funding, data, and AI tools" },
|
||||||
|
|
||||||
|
{ id: "increase", x: 240, y: 320, width: 120, height: 60, text: "Increase domestic production" },
|
||||||
|
{ id: "support", x: 240, y: 440, width: 120, height: 60, text: "(1b)/(2d) Support secondary processing expansion" },
|
||||||
|
{ id: "financing", x: 240, y: 520, width: 120, height: 60, text: "(1a) Increase domestic financing and incentives" },
|
||||||
|
{ id: "expand", x: 180, y: 640, width: 120, height: 60, text: "Expand US critical minerals project pipeline" },
|
||||||
|
|
||||||
|
// Goal 2 actions
|
||||||
|
{ id: "2a", x: 380, y: 400, width: 120, height: 60, text: "(2a) Expand the role and use of existing multilateral arrangements" },
|
||||||
|
{ id: "2b", x: 530, y: 400, width: 120, height: 60, text: "(2b) Expand US government financing tools" },
|
||||||
|
{ id: "2c", x: 460, y: 520, width: 120, height: 60, text: "(2c) Support exploration and data initiatives" },
|
||||||
|
{ id: "increase2", x: 480, y: 320, width: 120, height: 60, text: "Increase allied production" },
|
||||||
|
{ id: "expand2", x: 460, y: 580, width: 120, height: 60, text: "Expand allied critical minerals project pipeline" },
|
||||||
|
|
||||||
|
// Goal 3 actions
|
||||||
|
{ id: "3a", x: 700, y: 400, width: 120, height: 60, text: "(3a) Develop a real-time value chain mapping and monitoring system" },
|
||||||
|
{ id: "3b", x: 850, y: 400, width: 120, height: 60, text: "(3b) Expand purpose and use of National Defense Stockpile" },
|
||||||
|
{ id: "3c", x: 1000, y: 400, width: 120, height: 60, text: "(3c) Identify and deploy targeted market incentives" },
|
||||||
|
|
||||||
|
{ id: "monitor", x: 720, y: 320, width: 120, height: 60, text: "Monitor critical minerals value chain vulnerabilities" },
|
||||||
|
{ id: "reduce1", x: 850, y: 320, width: 120, height: 60, text: "Reduce supply disruption risks" },
|
||||||
|
{ id: "reduce2", x: 1000, y: 320, width: 120, height: 60, text: "Reduce price disruption risks" }
|
||||||
|
];
|
||||||
|
|
||||||
|
// 绘制主要目标节点
|
||||||
|
goalNodes.forEach(goal => {
|
||||||
|
const g = svg.append("g").attr("class", "goal-group");
|
||||||
|
|
||||||
|
// 主要矩形
|
||||||
|
g.append("rect")
|
||||||
|
.attr("x", goal.x)
|
||||||
|
.attr("y", goal.y)
|
||||||
|
.attr("width", goal.width)
|
||||||
|
.attr("height", goal.height)
|
||||||
|
.attr("class", "goal-node")
|
||||||
|
.attr("rx", 5);
|
||||||
|
|
||||||
|
// 数字圆形
|
||||||
|
g.append("circle")
|
||||||
|
.attr("cx", goal.x + 30)
|
||||||
|
.attr("cy", goal.y + 40)
|
||||||
|
.attr("r", 25)
|
||||||
|
.attr("fill", "white");
|
||||||
|
|
||||||
|
g.append("text")
|
||||||
|
.attr("x", goal.x + 30)
|
||||||
|
.attr("y", goal.y + 40)
|
||||||
|
.attr("class", "goal-number")
|
||||||
|
.attr("fill", "#2c3e50")
|
||||||
|
.text(goal.number);
|
||||||
|
|
||||||
|
// 标题文本
|
||||||
|
const titleLines = goal.title.split(' ');
|
||||||
|
const midIndex = Math.ceil(titleLines.length / 2);
|
||||||
|
const firstLine = titleLines.slice(0, midIndex).join(' ');
|
||||||
|
const secondLine = titleLines.slice(midIndex).join(' ');
|
||||||
|
|
||||||
|
g.append("text")
|
||||||
|
.attr("x", goal.x + 70)
|
||||||
|
.attr("y", goal.y + 30)
|
||||||
|
.attr("class", "goal-title-text")
|
||||||
|
.text(firstLine);
|
||||||
|
|
||||||
|
g.append("text")
|
||||||
|
.attr("x", goal.x + 70)
|
||||||
|
.attr("y", goal.y + 50)
|
||||||
|
.attr("class", "goal-title-text")
|
||||||
|
.text(secondLine);
|
||||||
|
|
||||||
|
// KPI文本框
|
||||||
|
g.append("rect")
|
||||||
|
.attr("x", goal.x)
|
||||||
|
.attr("y", goal.y + 90)
|
||||||
|
.attr("width", goal.width)
|
||||||
|
.attr("height", 40)
|
||||||
|
.attr("class", "kpi-node")
|
||||||
|
.attr("rx", 3);
|
||||||
|
|
||||||
|
// 分行显示KPI文本
|
||||||
|
const kpiLines = wrapText(goal.kpi, 50);
|
||||||
|
kpiLines.forEach((line, i) => {
|
||||||
|
g.append("text")
|
||||||
|
.attr("x", goal.x + 10)
|
||||||
|
.attr("y", goal.y + 105 + i * 12)
|
||||||
|
.attr("class", "kpi-text")
|
||||||
|
.attr("text-anchor", "start")
|
||||||
|
.text(line);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 绘制行动节点
|
||||||
|
actionNodes.forEach(action => {
|
||||||
|
const g = svg.append("g").attr("class", "action-group");
|
||||||
|
|
||||||
|
g.append("rect")
|
||||||
|
.attr("x", action.x)
|
||||||
|
.attr("y", action.y)
|
||||||
|
.attr("width", action.width)
|
||||||
|
.attr("height", action.height)
|
||||||
|
.attr("class", "action-node")
|
||||||
|
.attr("rx", 3);
|
||||||
|
|
||||||
|
// 分行显示文本
|
||||||
|
const textLines = wrapText(action.text, 15);
|
||||||
|
textLines.forEach((line, i) => {
|
||||||
|
g.append("text")
|
||||||
|
.attr("x", action.x + action.width/2)
|
||||||
|
.attr("y", action.y + action.height/2 + (i - textLines.length/2 + 0.5) * 12)
|
||||||
|
.attr("class", "node-text")
|
||||||
|
.text(line);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 绘制连接线
|
||||||
|
const connections = [
|
||||||
|
// 从overall goal到三个主要目标的连接
|
||||||
|
{ from: { x: 580, y: 140 }, to: { x: 310, y: 180 } },
|
||||||
|
{ from: { x: 580, y: 140 }, to: { x: 630, y: 180 } },
|
||||||
|
{ from: { x: 580, y: 140 }, to: { x: 950, y: 180 } },
|
||||||
|
|
||||||
|
// Goal 1的连接
|
||||||
|
{ from: { x: 110, y: 320 }, to: { x: 240, y: 340 } },
|
||||||
|
{ from: { x: 110, y: 400 }, to: { x: 240, y: 380 } },
|
||||||
|
{ from: { x: 110, y: 480 }, to: { x: 240, y: 460 } },
|
||||||
|
{ from: { x: 110, y: 560 }, to: { x: 180, y: 640 } },
|
||||||
|
{ from: { x: 300, y: 640 }, to: { x: 460, y: 580 } },
|
||||||
|
{ from: { x: 360, y: 340 }, to: { x: 480, y: 320 } },
|
||||||
|
{ from: { x: 310, y: 260 }, to: { x: 300, y: 320 } },
|
||||||
|
|
||||||
|
// Goal 2的连接
|
||||||
|
{ from: { x: 440, y: 400 }, to: { x: 480, y: 350 } },
|
||||||
|
{ from: { x: 590, y: 400 }, to: { x: 540, y: 350 } },
|
||||||
|
{ from: { x: 520, y: 520 }, to: { x: 520, y: 580 } },
|
||||||
|
{ from: { x: 630, y: 260 }, to: { x: 540, y: 320 } },
|
||||||
|
|
||||||
|
// Goal 3的连接
|
||||||
|
{ from: { x: 760, y: 400 }, to: { x: 780, y: 350 } },
|
||||||
|
{ from: { x: 910, y: 400 }, to: { x: 910, y: 350 } },
|
||||||
|
{ from: { x: 1060, y: 400 }, to: { x: 1060, y: 350 } },
|
||||||
|
{ from: { x: 950, y: 260 }, to: { x: 780, y: 320 } },
|
||||||
|
{ from: { x: 950, y: 260 }, to: { x: 910, y: 320 } },
|
||||||
|
{ from: { x: 950, y: 260 }, to: { x: 1060, y: 320 } }
|
||||||
|
];
|
||||||
|
|
||||||
|
connections.forEach(conn => {
|
||||||
|
svg.append("line")
|
||||||
|
.attr("class", "arrow")
|
||||||
|
.attr("x1", conn.from.x)
|
||||||
|
.attr("y1", conn.from.y)
|
||||||
|
.attr("x2", conn.to.x)
|
||||||
|
.attr("y2", conn.to.y);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 从总目标到三个主要目标的向上箭头
|
||||||
|
svg.append("line")
|
||||||
|
.attr("class", "arrow")
|
||||||
|
.attr("x1", 580)
|
||||||
|
.attr("y1", 100)
|
||||||
|
.attr("x2", 310)
|
||||||
|
.attr("y2", 140)
|
||||||
|
.attr("transform", "rotate(-15 580 100)");
|
||||||
|
|
||||||
|
svg.append("line")
|
||||||
|
.attr("class", "arrow")
|
||||||
|
.attr("x1", 580)
|
||||||
|
.attr("y1", 100)
|
||||||
|
.attr("x2", 630)
|
||||||
|
.attr("y2", 140);
|
||||||
|
|
||||||
|
svg.append("line")
|
||||||
|
.attr("class", "arrow")
|
||||||
|
.attr("x1", 580)
|
||||||
|
.attr("y1", 100)
|
||||||
|
.attr("x2", 950)
|
||||||
|
.attr("y2", 140)
|
||||||
|
.attr("transform", "rotate(15 580 100)");
|
||||||
|
|
||||||
|
// 文本换行函数
|
||||||
|
function wrapText(text, maxLength) {
|
||||||
|
const words = text.split(' ');
|
||||||
|
const lines = [];
|
||||||
|
let currentLine = '';
|
||||||
|
|
||||||
|
words.forEach(word => {
|
||||||
|
if ((currentLine + word).length <= maxLength) {
|
||||||
|
currentLine += (currentLine ? ' ' : '') + word;
|
||||||
|
} else {
|
||||||
|
if (currentLine) lines.push(currentLine);
|
||||||
|
currentLine = word;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (currentLine) lines.push(currentLine);
|
||||||
|
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加交互性
|
||||||
|
svg.selectAll(".goal-group, .action-group")
|
||||||
|
.on("mouseover", function() {
|
||||||
|
d3.select(this).style("opacity", 0.8);
|
||||||
|
})
|
||||||
|
.on("mouseout", function() {
|
||||||
|
d3.select(this).style("opacity", 1);
|
||||||
|
})
|
||||||
|
.on("click", function() {
|
||||||
|
console.log("节点被点击");
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
16
test1/index.html
Normal file
16
test1/index.html
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>US Critical Minerals Security Strategy</title>
|
||||||
|
<script src="https://d3js.org/d3.v7.min.js"></script>
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="chart-container">
|
||||||
|
<!-- 图表将通过D3.js渲染到此处 -->
|
||||||
|
</div>
|
||||||
|
<script src="script.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
239
test1/script.js
Normal file
239
test1/script.js
Normal file
@ -0,0 +1,239 @@
|
|||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
const width = document.getElementById('chart-container').clientWidth;
|
||||||
|
const height = document.getElementById('chart-container').clientHeight;
|
||||||
|
|
||||||
|
const svg = d3.select("#chart-container")
|
||||||
|
.append("svg")
|
||||||
|
.attr("width", width)
|
||||||
|
.attr("height", height);
|
||||||
|
|
||||||
|
// 添加箭头标记定义
|
||||||
|
svg.append("defs").append("marker")
|
||||||
|
.attr("id", "arrowhead")
|
||||||
|
.attr("viewBox", "0 -5 10 10")
|
||||||
|
.attr("refX", 8)
|
||||||
|
.attr("refY", 0)
|
||||||
|
.attr("markerWidth", 6)
|
||||||
|
.attr("markerHeight", 6)
|
||||||
|
.attr("orient", "auto")
|
||||||
|
.append("path")
|
||||||
|
.attr("d", "M0,-5L10,0L0,5")
|
||||||
|
.attr("fill", "#666");
|
||||||
|
|
||||||
|
// 定义节点数据
|
||||||
|
const nodes = [
|
||||||
|
// 目标节点
|
||||||
|
{id: "goal", label: "OVERALL GOAL\nUS critical minerals security", x: width/2, y: 70, width: 300, height: 80, type: "goal"},
|
||||||
|
|
||||||
|
// 三大核心策略节点
|
||||||
|
{id: "strategy1", label: "1", x: 160, y: 180, width: 240, height: 100, type: "primary"},
|
||||||
|
{id: "strategy1_text", label: "Rebuild US critical minerals\nindustry competitiveness", x: 160, y: 175, width: 200, height: 30, type: "label"},
|
||||||
|
{id: "kpi1", label: "KPIs: Average time from discovery to construction; no.\nof workers in CM industry; no. of projects in pipeline", x: 160, y: 220, width: 240, height: 40, type: "kpi"},
|
||||||
|
|
||||||
|
{id: "strategy2", label: "2", x: 480, y: 180, width: 240, height: 100, type: "primary"},
|
||||||
|
{id: "strategy2_text", label: "Reduce import reliance from\nnon-allied countries", x: 480, y: 175, width: 200, height: 30, type: "label"},
|
||||||
|
{id: "kpi2", label: "KPIs: Percentage of US critical minerals imported from\nor processed by non-allied countries", x: 480, y: 220, width: 240, height: 40, type: "kpi"},
|
||||||
|
|
||||||
|
{id: "strategy3", label: "3", x: 800, y: 180, width: 240, height: 100, type: "primary"},
|
||||||
|
{id: "strategy3_text", label: "Reduce the risk of critical minerals\nmarket disruptions", x: 800, y: 175, width: 200, height: 30, type: "label"},
|
||||||
|
{id: "kpi3", label: "KPIs: No. of months of critical mineral reserves;\npercentage of value chain data collected", x: 800, y: 220, width: 240, height: 40, type: "kpi"},
|
||||||
|
|
||||||
|
// 二级节点
|
||||||
|
{id: "1e", label: "(1e) Develop\ncritical minerals\nworkforce", x: 60, y: 320, width: 100, height: 80, type: "grey"},
|
||||||
|
{id: "1d", label: "(1d) Increase\ninvestment in\nR&D for new\ntech and AI\nusage", x: 60, y: 440, width: 100, height: 80, type: "grey"},
|
||||||
|
{id: "1c", label: "(1c) Improve\npermitting and\nregulations", x: 60, y: 560, width: 100, height: 80, type: "grey"},
|
||||||
|
{id: "1b", label: "(1b)/(2d)\nSupport\nsecondary\nprocessing\nexpansion", x: 260, y: 440, width: 100, height: 80, type: "grey"},
|
||||||
|
|
||||||
|
{id: "domestic_prod", label: "Increase\ndomestic\nproduction", x: 260, y: 320, width: 100, height: 80, type: "secondary"},
|
||||||
|
{id: "allied_prod", label: "Increase allied\nproduction", x: 480, y: 320, width: 100, height: 80, type: "secondary"},
|
||||||
|
|
||||||
|
{id: "2a", label: "(2a) Expand the\nrole and use of\nexisting\nmultilateral\narrangements", x: 380, y: 440, width: 100, height: 80, type: "grey"},
|
||||||
|
|
||||||
|
{id: "2b", label: "(2b) Expand US\ngovernment\nfinancing tools", x: 580, y: 440, width: 100, height: 80, type: "grey"},
|
||||||
|
|
||||||
|
{id: "1a", label: "(1a) Increase\ndomestic\nfinancing and\nincentives", x: 260, y: 560, width: 100, height: 80, type: "grey"},
|
||||||
|
|
||||||
|
{id: "allied_pipeline", label: "Expand allied\ncritical minerals\nproject pipeline", x: 480, y: 560, width: 100, height: 80, type: "secondary"},
|
||||||
|
|
||||||
|
{id: "2c", label: "(2c) Support\nexploration and\ndata initiatives", x: 480, y: 680, width: 100, height: 80, type: "grey"},
|
||||||
|
|
||||||
|
{id: "3a", label: "(3a) Develop a\nreal-time value\nchain mapping\nand monitoring\nsystem", x: 690, y: 440, width: 100, height: 80, type: "grey"},
|
||||||
|
{id: "3b", label: "(3b) Expand\npurpose and\nuse of National\nDefense\nStockpile", x: 790, y: 440, width: 100, height: 80, type: "grey"},
|
||||||
|
{id: "3c", label: "(3c) Identify and\ndeploy targeted\nmarket\nincentives", x: 890, y: 440, width: 100, height: 80, type: "grey"},
|
||||||
|
|
||||||
|
{id: "value_chain", label: "Monitor critical\nminerals value\nchain\nvulnerabilities", x: 690, y: 320, width: 100, height: 80, type: "secondary"},
|
||||||
|
{id: "supply_risk", label: "Reduce supply\ndisruption risks", x: 790, y: 320, width: 100, height: 80, type: "secondary"},
|
||||||
|
{id: "price_risk", label: "Reduce price\ndisruption risks", x: 890, y: 320, width: 100, height: 80, type: "secondary"},
|
||||||
|
|
||||||
|
{id: "1d_pipeline", label: "Expand US\ncritical minerals\nproject pipeline", x: 160, y: 680, width: 100, height: 80, type: "secondary"}
|
||||||
|
];
|
||||||
|
|
||||||
|
// 定义连接数据
|
||||||
|
const links = [
|
||||||
|
{source: "goal", target: "strategy1"},
|
||||||
|
{source: "goal", target: "strategy2"},
|
||||||
|
{source: "goal", target: "strategy3"},
|
||||||
|
|
||||||
|
{source: "1e", target: "domestic_prod"},
|
||||||
|
{source: "domestic_prod", target: "strategy1"},
|
||||||
|
|
||||||
|
{source: "1d", target: "1e"},
|
||||||
|
|
||||||
|
{source: "1c", target: "1b"},
|
||||||
|
{source: "1b", target: "domestic_prod"},
|
||||||
|
|
||||||
|
{source: "domestic_prod", target: "allied_prod"},
|
||||||
|
{source: "allied_prod", target: "strategy2"},
|
||||||
|
|
||||||
|
{source: "2a", target: "allied_prod"},
|
||||||
|
{source: "2a", target: "2b"},
|
||||||
|
{source: "2b", target: "2a"},
|
||||||
|
|
||||||
|
{source: "1a", target: "1b"},
|
||||||
|
{source: "1a", target: "allied_pipeline"},
|
||||||
|
{source: "allied_pipeline", target: "allied_prod"},
|
||||||
|
|
||||||
|
{source: "2c", target: "allied_pipeline"},
|
||||||
|
|
||||||
|
{source: "1d", target: "1d_pipeline"},
|
||||||
|
{source: "1d_pipeline", target: "1c"},
|
||||||
|
|
||||||
|
{source: "3a", target: "value_chain"},
|
||||||
|
{source: "value_chain", target: "strategy3"},
|
||||||
|
|
||||||
|
{source: "3b", target: "supply_risk"},
|
||||||
|
{source: "supply_risk", target: "strategy3"},
|
||||||
|
|
||||||
|
{source: "3c", target: "price_risk"},
|
||||||
|
{source: "price_risk", target: "strategy3"}
|
||||||
|
];
|
||||||
|
|
||||||
|
// 绘制连接线
|
||||||
|
svg.selectAll("path.link")
|
||||||
|
.data(links)
|
||||||
|
.enter()
|
||||||
|
.append("path")
|
||||||
|
.attr("class", "link")
|
||||||
|
.attr("d", function(d) {
|
||||||
|
const source = nodes.find(n => n.id === d.source);
|
||||||
|
const target = nodes.find(n => n.id === d.target);
|
||||||
|
|
||||||
|
// 确定连接点位置
|
||||||
|
let sourceX, sourceY, targetX, targetY;
|
||||||
|
|
||||||
|
if (source.y < target.y) {
|
||||||
|
sourceX = source.x;
|
||||||
|
sourceY = source.y + source.height/2;
|
||||||
|
targetX = target.x;
|
||||||
|
targetY = target.y - target.height/2;
|
||||||
|
} else if (source.y > target.y) {
|
||||||
|
sourceX = source.x;
|
||||||
|
sourceY = source.y - source.height/2;
|
||||||
|
targetX = target.x;
|
||||||
|
targetY = target.y + target.height/2;
|
||||||
|
} else if (source.x < target.x) {
|
||||||
|
sourceX = source.x + source.width/2;
|
||||||
|
sourceY = source.y;
|
||||||
|
targetX = target.x - target.width/2;
|
||||||
|
targetY = target.y;
|
||||||
|
} else {
|
||||||
|
sourceX = source.x - source.width/2;
|
||||||
|
sourceY = source.y;
|
||||||
|
targetX = target.x + target.width/2;
|
||||||
|
targetY = target.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建路径
|
||||||
|
return `M${sourceX},${sourceY}L${targetX},${targetY}`;
|
||||||
|
})
|
||||||
|
.attr("marker-end", "url(#arrowhead)");
|
||||||
|
|
||||||
|
// 绘制节点
|
||||||
|
const nodeGroups = svg.selectAll("g.node")
|
||||||
|
.data(nodes.filter(n => !n.id.includes("_text") && !n.id.includes("kpi")))
|
||||||
|
.enter()
|
||||||
|
.append("g")
|
||||||
|
.attr("class", function(d) { return "node " + d.type; });
|
||||||
|
|
||||||
|
// 添加矩形
|
||||||
|
nodeGroups.append("rect")
|
||||||
|
.attr("x", d => d.x - d.width/2)
|
||||||
|
.attr("y", d => d.y - d.height/2)
|
||||||
|
.attr("width", d => d.width)
|
||||||
|
.attr("height", d => d.height)
|
||||||
|
.attr("rx", 4)
|
||||||
|
.attr("ry", 4);
|
||||||
|
|
||||||
|
// 添加标签文本
|
||||||
|
nodeGroups.append("text")
|
||||||
|
.attr("x", d => d.x)
|
||||||
|
.attr("y", d => d.y)
|
||||||
|
.attr("dy", ".35em")
|
||||||
|
.attr("text-anchor", "middle")
|
||||||
|
.text(d => d.label)
|
||||||
|
.each(function(d) {
|
||||||
|
// 处理多行文本
|
||||||
|
const text = d3.select(this);
|
||||||
|
const words = d.label.split('\n');
|
||||||
|
|
||||||
|
text.text(null);
|
||||||
|
|
||||||
|
words.forEach((word, i) => {
|
||||||
|
text.append("tspan")
|
||||||
|
.attr("x", d.x)
|
||||||
|
.attr("dy", i ? "1.2em" : "0")
|
||||||
|
.text(word);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 添加策略标题文本
|
||||||
|
svg.selectAll("text.strategy-text")
|
||||||
|
.data(nodes.filter(n => n.id.includes("_text")))
|
||||||
|
.enter()
|
||||||
|
.append("text")
|
||||||
|
.attr("class", "strategy-text")
|
||||||
|
.attr("x", d => d.x)
|
||||||
|
.attr("y", d => d.y)
|
||||||
|
.attr("text-anchor", "middle")
|
||||||
|
.attr("fill", "white")
|
||||||
|
.each(function(d) {
|
||||||
|
// 处理多行文本
|
||||||
|
const text = d3.select(this);
|
||||||
|
const words = d.label.split('\n');
|
||||||
|
|
||||||
|
text.text(null);
|
||||||
|
|
||||||
|
words.forEach((word, i) => {
|
||||||
|
text.append("tspan")
|
||||||
|
.attr("x", d.x)
|
||||||
|
.attr("dy", i ? "1.2em" : "0")
|
||||||
|
.text(word);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 添加KPI文本
|
||||||
|
svg.selectAll("text.kpi")
|
||||||
|
.data(nodes.filter(n => n.id.includes("kpi")))
|
||||||
|
.enter()
|
||||||
|
.append("text")
|
||||||
|
.attr("class", "kpi")
|
||||||
|
.attr("x", d => d.x)
|
||||||
|
.attr("y", d => d.y)
|
||||||
|
.attr("text-anchor", "middle")
|
||||||
|
.attr("fill", "white")
|
||||||
|
.attr("font-size", "10px")
|
||||||
|
.each(function(d) {
|
||||||
|
// 处理多行文本
|
||||||
|
const text = d3.select(this);
|
||||||
|
const words = d.label.split('\n');
|
||||||
|
|
||||||
|
text.text(null);
|
||||||
|
|
||||||
|
words.forEach((word, i) => {
|
||||||
|
text.append("tspan")
|
||||||
|
.attr("x", d.x)
|
||||||
|
.attr("dy", i ? "1.2em" : "0")
|
||||||
|
.text(word);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
59
test1/style.css
Normal file
59
test1/style.css
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
margin: 0;
|
||||||
|
padding: 20px;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
#chart-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 800px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.node {
|
||||||
|
fill: #ccc;
|
||||||
|
stroke: #fff;
|
||||||
|
stroke-width: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.node.goal {
|
||||||
|
fill: #8BC34A; /* 绿色背景,对应顶部框 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.node.primary {
|
||||||
|
fill: #000;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.node.secondary {
|
||||||
|
fill: #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.node.grey {
|
||||||
|
fill: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
text {
|
||||||
|
font-size: 12px;
|
||||||
|
text-anchor: middle;
|
||||||
|
dominant-baseline: middle;
|
||||||
|
fill: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.node.primary text {
|
||||||
|
fill: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link {
|
||||||
|
fill: none;
|
||||||
|
stroke: #666;
|
||||||
|
stroke-width: 1.5px;
|
||||||
|
marker-end: url(#arrowhead);
|
||||||
|
}
|
||||||
|
|
||||||
|
.number-circle {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: bold;
|
||||||
|
fill: white;
|
||||||
|
}
|
272
test2-gemini-2/index.html
Normal file
272
test2-gemini-2/index.html
Normal file
@ -0,0 +1,272 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>US Critical Minerals Security Flowchart</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
background-color: #f0f2f5;
|
||||||
|
margin: 0;
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Node Styles --- */
|
||||||
|
.node-group .node-rect {
|
||||||
|
stroke-width: 1.5;
|
||||||
|
stroke: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.node-goal .node-rect {
|
||||||
|
fill: #8BC34A; /* A green similar to the image */
|
||||||
|
stroke: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.node-pillar .node-rect {
|
||||||
|
fill: #212121;
|
||||||
|
stroke: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.node-action .node-rect {
|
||||||
|
fill: #E0E0E0; /* A light grey */
|
||||||
|
stroke: #9E9E9E;
|
||||||
|
}
|
||||||
|
|
||||||
|
.node-intermediate .node-rect {
|
||||||
|
fill: #FFFFFF;
|
||||||
|
stroke: #424242;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Text Styles --- */
|
||||||
|
.node-text {
|
||||||
|
font-size: 11px;
|
||||||
|
text-anchor: middle;
|
||||||
|
fill: #000;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.node-pillar .node-text, .node-goal .node-text {
|
||||||
|
fill: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.node-text .kpi-text {
|
||||||
|
font-size: 9px;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Link Styles --- */
|
||||||
|
.link {
|
||||||
|
fill: none;
|
||||||
|
stroke: #616161;
|
||||||
|
stroke-width: 1.5px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div id="chart"></div>
|
||||||
|
|
||||||
|
<script src="https://d3js.org/d3.v7.min.js"></script>
|
||||||
|
<script>
|
||||||
|
// 1. Data Definition
|
||||||
|
const nodes = [
|
||||||
|
// Top Goal
|
||||||
|
{ id: 'goal', type: 'goal', width: 280, height: 50, text: ['OVERALL GOAL', 'US critical minerals security'] },
|
||||||
|
|
||||||
|
// 3 Pillars
|
||||||
|
{ id: 'p1', type: 'pillar', width: 280, height: 75, text: ['1', 'Rebuild US critical minerals', 'industry competitiveness'], kpis: ['KPIs: Average time from discovery to construction; no.', 'of workers in CM industry; no. of projects in pipeline'] },
|
||||||
|
{ id: 'p2', type: 'pillar', width: 280, height: 75, text: ['2', 'Reduce import reliance from', 'non-allied countries'], kpis: ['KPIs: Percentage of US critical minerals imported from', 'or processed by non-allied countries'] },
|
||||||
|
{ id: 'p3', type: 'pillar', width: 280, height: 75, text: ['3', 'Reduce the risk of critical minerals', 'market disruptions'], kpis: ['KPIs: No. of months of critical mineral reserves;', 'percentage of value chain data collected'] },
|
||||||
|
|
||||||
|
// Intermediate Nodes
|
||||||
|
{ id: 'int1', type: 'intermediate', width: 130, height: 40, text: ['Increase domestic', 'production'] },
|
||||||
|
{ id: 'int2', type: 'intermediate', width: 130, height: 40, text: ['Increase allied', 'production'] },
|
||||||
|
{ id: 'int3', type: 'intermediate', width: 130, height: 40, text: ['Expand US', 'critical minerals', 'project pipeline'] },
|
||||||
|
{ id: 'int4', type: 'intermediate', width: 130, height: 40, text: ['Expand allied', 'critical minerals', 'project pipeline'] },
|
||||||
|
{ id: 'int5', type: 'intermediate', width: 130, height: 50, text: ['Monitor critical', 'minerals value', 'chain', 'vulnerabilities'] },
|
||||||
|
{ id: 'int6', type: 'intermediate', width: 130, height: 40, text: ['Reduce supply', 'disruption risks'] },
|
||||||
|
{ id: 'int7', type: 'intermediate', width: 130, height: 40, text: ['Reduce price', 'disruption risks'] },
|
||||||
|
|
||||||
|
// Action Nodes (Grey)
|
||||||
|
{ id: 'a1e', type: 'action', width: 130, height: 45, text: ['(1e) Develop', 'critical minerals', 'workforce'] },
|
||||||
|
{ id: 'a1f', type: 'action', width: 130, height: 55, text: ['(1f) Increase', 'investment in', 'R&D for new', 'tech and AI', 'usage'] },
|
||||||
|
{ id: 'a1c', type: 'action', width: 130, height: 45, text: ['(1c) Improve', 'permitting and', 'regulations'] },
|
||||||
|
{ id: 'a1d', type: 'action', width: 130, height: 55, text: ['(1d) Support', 'domestic', 'exploration with', 'funding, data,', 'and AI tools'] },
|
||||||
|
{ id: 'a1a', type: 'action', width: 130, height: 45, text: ['(1a) Increase', 'domestic', 'financing and', 'incentives'] },
|
||||||
|
{ id: 'a1b/2d', type: 'action', width: 130, height: 55, text: ['(1b)/(2d)', 'Support', 'secondary', 'processing', 'expansion'] },
|
||||||
|
{ id: 'a2a', type: 'action', width: 130, height: 55, text: ['(2a) Expand the', 'role and use of', 'existing', 'multilateral', 'arrangements'] },
|
||||||
|
{ id: 'a2b', type: 'action', width: 130, height: 45, text: ['(2b) Expand US', 'government', 'financing tools'] },
|
||||||
|
{ id: 'a2c', type: 'action', width: 130, height: 45, text: ['(2c) Support', 'exploration and', 'data initiatives'] },
|
||||||
|
{ id: 'a3a', type: 'action', width: 130, height: 55, text: ['(3a) Develop a', 'real-time value', 'chain mapping', 'and monitoring', 'system'] },
|
||||||
|
{ id: 'a3b', type: 'action', width: 130, height: 55, text: ['(3b) Expand', 'purpose and', 'use of National', 'Defense', 'Stockpile'] },
|
||||||
|
{ id: 'a3c', type: 'action', width: 130, height: 55, text: ['(3c) Identify and', 'deploy targeted', 'market', 'incentives'] },
|
||||||
|
];
|
||||||
|
|
||||||
|
const links = [
|
||||||
|
{ source: 'goal', target: 'p1' }, { source: 'goal', target: 'p2' }, { source: 'goal', target: 'p3' },
|
||||||
|
{ source: 'p1', target: 'int1' }, { source: 'p2', target: 'int2' },
|
||||||
|
{ source: 'p3', target: 'int5' }, { source: 'p3', target: 'int6' }, { source: 'p3', target: 'int7' },
|
||||||
|
{ source: 'int1', target: 'a1e' }, { source: 'int1', target: 'a1f' }, { source: 'int1', target: 'a1c' },
|
||||||
|
{ source: 'int1', target: 'a1b/2d' },
|
||||||
|
{ source: 'int3', target: 'a1d' },
|
||||||
|
{ source: 'a1a', target: 'int1' },
|
||||||
|
{ source: 'a1b/2d', target: 'int2' },
|
||||||
|
{ source: 'a1c', target: 'int3' }, { source: 'a1f', target: 'int3' },
|
||||||
|
{ source: 'a2a', target: 'int2' }, { source: 'a2b', target: 'a2a' },
|
||||||
|
{ source: 'a2a', target: 'int4' },
|
||||||
|
{ source: 'a2b', target: 'int4' },
|
||||||
|
{ source: 'int4', target: 'a2c' },
|
||||||
|
{ source: 'a3a', target: 'int5' }, { source: 'a3b', target: 'int6' }, { source: 'a3c', target: 'int7' },
|
||||||
|
{ source: 'int6', target: 'a3b' }
|
||||||
|
];
|
||||||
|
|
||||||
|
// 2. SVG Setup and Layout
|
||||||
|
const width = 960;
|
||||||
|
const height = 850;
|
||||||
|
const nodePositions = {
|
||||||
|
'goal': { x: width / 2, y: 50 },
|
||||||
|
'p1': { x: 150, y: 170 },
|
||||||
|
'p2': { x: width / 2, y: 170 },
|
||||||
|
'p3': { x: width - 150, y: 170 },
|
||||||
|
'int1': { x: 150, y: 320 },
|
||||||
|
'int2': { x: width / 2, y: 320 },
|
||||||
|
'int3': { x: 150, y: 510 },
|
||||||
|
'int4': { x: width / 2, y: 510 },
|
||||||
|
'int5': { x: 570, y: 320 },
|
||||||
|
'int6': { x: 710, y: 320 },
|
||||||
|
'int7': { x: 850, y: 320 },
|
||||||
|
'a1e': { x: 50, y: 420 },
|
||||||
|
'a1f': { x: 50, y: 510 },
|
||||||
|
'a1c': { x: 50, y: 600 },
|
||||||
|
'a1d': { x: 150, y: 600 },
|
||||||
|
'a1a': { x: 250, y: 420 },
|
||||||
|
'a1b/2d': { x: 250, y: 510 },
|
||||||
|
'a2a': { x: width / 2, y: 420 },
|
||||||
|
'a2b': { x: 350, y: 510 },
|
||||||
|
'a2c': { x: width / 2, y: 600 },
|
||||||
|
'a3a': { x: 570, y: 420 },
|
||||||
|
'a3b': { x: 710, y: 420 },
|
||||||
|
'a3c': { x: 850, y: 420 },
|
||||||
|
};
|
||||||
|
|
||||||
|
const svg = d3.select("#chart").append("svg")
|
||||||
|
.attr("width", width)
|
||||||
|
.attr("height", height);
|
||||||
|
|
||||||
|
// Define arrow markers
|
||||||
|
svg.append("defs").append("marker")
|
||||||
|
.attr("id", "arrow")
|
||||||
|
.attr("viewBox", "0 -5 10 10")
|
||||||
|
.attr("refX", 8)
|
||||||
|
.attr("refY", 0)
|
||||||
|
.attr("markerWidth", 5)
|
||||||
|
.attr("markerHeight", 5)
|
||||||
|
.attr("orient", "auto")
|
||||||
|
.append("path")
|
||||||
|
.attr("d", "M0,-5L10,0L0,5")
|
||||||
|
.attr("fill", "#616161");
|
||||||
|
|
||||||
|
// 3. Render Links
|
||||||
|
const linkGroup = svg.append("g").attr("class", "links");
|
||||||
|
links.forEach(link => {
|
||||||
|
const sourceNode = nodes.find(n => n.id === link.source);
|
||||||
|
const targetNode = nodes.find(n => n.id === link.target);
|
||||||
|
const sourcePos = nodePositions[link.source];
|
||||||
|
const targetPos = nodePositions[link.target];
|
||||||
|
|
||||||
|
let pathData;
|
||||||
|
const sx = sourcePos.x, sy = sourcePos.y;
|
||||||
|
const tx = targetPos.x, ty = targetPos.y;
|
||||||
|
const sourceHalfHeight = sourceNode.height / 2;
|
||||||
|
const targetHalfHeight = targetNode.height / 2;
|
||||||
|
const sourceHalfWidth = sourceNode.width / 2;
|
||||||
|
const targetHalfWidth = targetNode.width / 2;
|
||||||
|
|
||||||
|
// Simple routing for orthogonal lines
|
||||||
|
if (sx === tx) { // Vertical
|
||||||
|
pathData = `M ${sx},${sy < ty ? sy + sourceHalfHeight : sy - sourceHalfHeight} L ${tx},${sy < ty ? ty - targetHalfHeight : ty + targetHalfHeight}`;
|
||||||
|
} else if (sy === ty) { // Horizontal
|
||||||
|
pathData = `M ${sx < tx ? sx + sourceHalfWidth : sx - sourceHalfWidth},${sy} L ${tx < sx ? tx + targetHalfWidth : tx - targetHalfWidth},${ty}`;
|
||||||
|
} else { // L-shaped connectors
|
||||||
|
const midY = sy + (ty-sy)/2;
|
||||||
|
pathData = `M ${sx},${sy < ty ? sy + sourceHalfHeight : sy - sourceHalfHeight} L ${sx},${midY} L ${tx},${midY} L ${tx},${sy < ty ? ty - targetHalfHeight : ty + targetHalfHeight}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Custom paths based on the diagram
|
||||||
|
if( (link.source === 'a1c' && link.target === 'int3') || (link.source === 'a1f' && link.target === 'int3') ) {
|
||||||
|
pathData = `M ${sx + sourceHalfWidth},${sy} L ${tx - targetHalfWidth},${ty}`;
|
||||||
|
} else if (link.source === 'a1b/2d' && link.target === 'int2') {
|
||||||
|
pathData = `M ${sx + sourceHalfWidth},${sy} L ${tx - targetHalfWidth},${ty}`;
|
||||||
|
} else if (link.source === 'a2b' && link.target === 'a2a') {
|
||||||
|
pathData = `M ${sx},${sy - sourceHalfHeight} L ${tx},${ty + targetHalfHeight}`;
|
||||||
|
} else if (link.source === 'a2a' && link.target === 'int4') {
|
||||||
|
pathData = `M ${sx},${sy + sourceHalfHeight} L ${tx},${ty - targetHalfHeight}`;
|
||||||
|
} else if (link.source === 'a2b' && link.target === 'int4') {
|
||||||
|
const midX = sx + (tx-sx)/2;
|
||||||
|
pathData = `M ${sx-sourceHalfWidth},${sy} L ${midX},${sy} L ${midX},${ty+targetHalfHeight}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
linkGroup.append("path")
|
||||||
|
.attr("class", "link")
|
||||||
|
.attr("d", pathData)
|
||||||
|
.attr("marker-end", "url(#arrow)");
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// 4. Render Nodes
|
||||||
|
const nodeGroups = svg.selectAll("g.node-group")
|
||||||
|
.data(nodes)
|
||||||
|
.enter()
|
||||||
|
.append("g")
|
||||||
|
.attr("class", d => `node-group node-${d.type}`)
|
||||||
|
.attr("transform", d => `translate(${nodePositions[d.id].x}, ${nodePositions[d.id].y})`);
|
||||||
|
|
||||||
|
// Add rectangles
|
||||||
|
nodeGroups.append("rect")
|
||||||
|
.attr("class", "node-rect")
|
||||||
|
.attr("x", d => -d.width / 2)
|
||||||
|
.attr("y", d => -d.height / 2)
|
||||||
|
.attr("width", d => d.width)
|
||||||
|
.attr("height", d => d.height)
|
||||||
|
.attr("rx", 3) // Rounded corners
|
||||||
|
.attr("ry", 3);
|
||||||
|
|
||||||
|
// Add text labels
|
||||||
|
const text = nodeGroups.append("text")
|
||||||
|
.attr("class", "node-text")
|
||||||
|
.attr("dy", d => (d.kpis ? -5 : 0)); // Adjust vertical position for nodes with KPIs
|
||||||
|
|
||||||
|
text.each(function(d) {
|
||||||
|
const el = d3.select(this);
|
||||||
|
const lineHeight = 12; // Adjust this value for line spacing
|
||||||
|
const initialY = -( (d.text.length -1) * lineHeight) / 2; // Calculate starting Y to center the block
|
||||||
|
|
||||||
|
d.text.forEach((line, i) => {
|
||||||
|
el.append("tspan")
|
||||||
|
.attr("x", 0)
|
||||||
|
.attr("y", initialY + (i * lineHeight))
|
||||||
|
.text(line)
|
||||||
|
.style("font-weight", (i === 0 && (d.type === 'pillar' || d.text[0].startsWith('(')) ? "bold" : "normal"));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add KPI text for pillar nodes
|
||||||
|
if (d.kpis) {
|
||||||
|
const kpiYstart = d.height / 2 - (d.kpis.length * 10) - 4;
|
||||||
|
d.kpis.forEach((line, i) => {
|
||||||
|
el.append("tspan")
|
||||||
|
.attr("class", "kpi-text")
|
||||||
|
.attr("x", 0)
|
||||||
|
.attr("y", kpiYstart + (i * 10))
|
||||||
|
.text(line);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
222
test2-gemini/index.html
Normal file
222
test2-gemini/index.html
Normal file
@ -0,0 +1,222 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>美国关键矿产安全战略</title>
|
||||||
|
<script src="https://d3js.org/d3.v7.min.js"></script>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
margin: 0;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
svg {
|
||||||
|
background-color: #e9e9e9;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
}
|
||||||
|
.node rect {
|
||||||
|
stroke: #333;
|
||||||
|
stroke-width: 1px;
|
||||||
|
}
|
||||||
|
.node-goal rect {
|
||||||
|
fill: #84a94b;
|
||||||
|
stroke: none;
|
||||||
|
}
|
||||||
|
.node-pillar rect {
|
||||||
|
fill: #231f20;
|
||||||
|
stroke: none;
|
||||||
|
}
|
||||||
|
.node-action rect {
|
||||||
|
fill: #cccccc;
|
||||||
|
}
|
||||||
|
.node-intermediate rect {
|
||||||
|
fill: #ffffff;
|
||||||
|
}
|
||||||
|
.node text {
|
||||||
|
font-size: 12px;
|
||||||
|
fill: #000;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.node-goal text, .node-pillar text {
|
||||||
|
fill: #ffffff;
|
||||||
|
}
|
||||||
|
.kpi-text {
|
||||||
|
font-size: 10px;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
.link {
|
||||||
|
fill: none;
|
||||||
|
stroke: #555;
|
||||||
|
stroke-width: 1.5px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<svg id="chart" width="1200" height="850"></svg>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const data = {
|
||||||
|
nodes: [
|
||||||
|
// Main Goal
|
||||||
|
{ id: 'goal', type: 'goal', x: 450, y: 20, width: 300, height: 60, text: ['OVERALL GOAL', 'US critical minerals security'] },
|
||||||
|
|
||||||
|
// Pillars
|
||||||
|
{ id: 'pillar1', type: 'pillar', x: 50, y: 120, width: 320, height: 80, text: ['1 Rebuild US critical minerals', 'industry competitiveness'], kpi: 'KPIs: Average time from discovery to construction; no. of workers in CM industry; no. of projects in pipeline' },
|
||||||
|
{ id: 'pillar2', type: 'pillar', x: 440, y: 120, width: 320, height: 80, text: ['2 Reduce import reliance from', 'non-allied countries'], kpi: 'KPIs: Percentage of US critical minerals imported from or processed by non-allied countries' },
|
||||||
|
{ id: 'pillar3', type: 'pillar', x: 830, y: 120, width: 320, height: 80, text: ['3 Reduce the risk of critical minerals', 'market disruptions'], kpi: 'KPIs: No. of months of critical mineral reserves; percentage of value chain data collected' },
|
||||||
|
|
||||||
|
// Level 1 Intermediates
|
||||||
|
{ id: 'increase_domestic_prod', type: 'intermediate', x: 260, y: 300, width: 150, height: 50, text: ['Increase', 'domestic', 'production'] },
|
||||||
|
{ id: 'increase_allied_prod', type: 'intermediate', x: 480, y: 300, width: 150, height: 50, text: ['Increase allied', 'production'] },
|
||||||
|
{ id: 'monitor_vuln', type: 'intermediate', x: 750, y: 300, width: 150, height: 50, text: ['Monitor critical', 'minerals value', 'chain', 'vulnerabilities'] },
|
||||||
|
{ id: 'reduce_supply_risk', type: 'intermediate', x: 920, y: 300, width: 150, height: 50, text: ['Reduce supply', 'disruption risks'] },
|
||||||
|
{ id: 'reduce_price_risk', type: 'intermediate', x: 1090, y: 300, width: 150, height: 50, text: ['Reduce price', 'disruption risks'] },
|
||||||
|
|
||||||
|
// Level 2 Intermediates
|
||||||
|
{ id: 'expand_us_pipeline', type: 'intermediate', x: 150, y: 650, width: 150, height: 50, text: ['Expand US', 'critical minerals', 'project pipeline'] },
|
||||||
|
{ id: 'expand_allied_pipeline', type: 'intermediate', x: 550, y: 570, width: 150, height: 50, text: ['Expand allied', 'critical minerals', 'project pipeline'] },
|
||||||
|
|
||||||
|
// Actions (Grey Boxes)
|
||||||
|
{ id: '1e', type: 'action', x: 50, y: 300, width: 150, height: 50, text: ['(1e) Develop', 'critical minerals', 'workforce'] },
|
||||||
|
{ id: '1f', type: 'action', x: 50, y: 380, width: 150, height: 50, text: ['(1f) Increase', 'investment in', 'R&D for new', 'tech and AI usage'] },
|
||||||
|
{ id: '1c', type: 'action', x: 50, y: 540, width: 150, height: 50, text: ['(1c) Improve', 'permitting and', 'regulations'] },
|
||||||
|
{ id: '1d', type: 'action', x: 50, y: 650, width: 150, height: 50, text: ['(1d) Support', 'domestic', 'exploration with', 'funding, data,', 'and AI tools'] },
|
||||||
|
|
||||||
|
{ id: '1b_2d', type: 'action', x: 260, y: 460, width: 150, height: 50, text: ['(1b)/(2d)', 'Support', 'secondary', 'processing', 'expansion'] },
|
||||||
|
{ id: '1a', type: 'action', x: 260, y: 570, width: 150, height: 50, text: ['(1a) Increase', 'domestic', 'financing and', 'incentives'] },
|
||||||
|
|
||||||
|
{ id: '2a', type: 'action', x: 480, y: 460, width: 150, height: 50, text: ['(2a) Expand the', 'role and use of', 'existing', 'multilateral', 'arrangements'] },
|
||||||
|
{ id: '2b', type: 'action', x: 670, y: 460, width: 150, height: 50, text: ['(2b) Expand US', 'government', 'financing tools'] },
|
||||||
|
{ id: '2c', type: 'action', x: 550, y: 680, width: 150, height: 50, text: ['(2c) Support', 'exploration and', 'data initiatives'] },
|
||||||
|
|
||||||
|
{ id: '3a', type: 'action', x: 750, y: 460, width: 150, height: 50, text: ['(3a) Develop a', 'real-time value', 'chain mapping', 'and monitoring', 'system'] },
|
||||||
|
{ id: '3b', type: 'action', x: 920, y: 460, width: 150, height: 50, text: ['(3b) Expand', 'purpose and', 'use of National', 'Defense', 'Stockpile'] },
|
||||||
|
{ id: '3c', type: 'action', x: 1090, y: 460, width: 150, height: 50, text: ['(3c) Identify and', 'deploy targeted', 'market', 'incentives'] },
|
||||||
|
],
|
||||||
|
links: [
|
||||||
|
// Arrows
|
||||||
|
{ source: 'pillar1', target: 'increase_domestic_prod', path: 'M 210 200 V 300' },
|
||||||
|
{ source: 'pillar2', target: 'increase_allied_prod', path: 'M 600 200 V 300' },
|
||||||
|
{ source: 'pillar3', target: 'monitor_vuln', path: 'M 825 200 V 300' },
|
||||||
|
{ source: 'pillar3', target: 'reduce_supply_risk', path: 'M 995 200 V 300' },
|
||||||
|
{ source: 'pillar3', target: 'reduce_price_risk', path: 'M 1165 200 V 300' },
|
||||||
|
{ source: 'goal', target: 'pillar1', path: 'M 600 80 V 100 H 210 V 120' },
|
||||||
|
{ source: 'goal', target: 'pillar2', path: 'M 600 80 V 120' },
|
||||||
|
{ source: 'goal', target: 'pillar3', path: 'M 600 80 V 100 H 990 V 120' },
|
||||||
|
{ source: '1e', target: 'increase_domestic_prod', path: 'M 200 325 H 260' },
|
||||||
|
{ source: '1f', target: 'increase_domestic_prod', path: 'M 125 380 V 360 H 260' },
|
||||||
|
{ source: '1c', target: 'expand_us_pipeline', path: 'M 125 540 V 640 H 225' },
|
||||||
|
{ source: '1d', target: 'expand_us_pipeline', path: 'M 200 675 H 225' },
|
||||||
|
{ source: 'expand_us_pipeline', target: 'increase_domestic_prod', path: 'M 225 650 V 520 H 335 V 350' },
|
||||||
|
{ source: '1a', target: 'expand_us_pipeline', path: 'M 335 570 V 650' },
|
||||||
|
{ source: '1b_2d', target: 'increase_domestic_prod', path: 'M 335 460 V 350' },
|
||||||
|
{ source: '1b_2d', target: 'increase_allied_prod', path: 'M 410 485 H 450 V 350' },
|
||||||
|
{ source: '2a', target: 'increase_allied_prod', path: 'M 555 460 V 350' },
|
||||||
|
{ source: 'expand_allied_pipeline', target: 'increase_allied_prod', path: 'M 625 570 V 520 H 555 V 350' },
|
||||||
|
{ source: '2c', target: 'expand_allied_pipeline', path: 'M 625 680 V 620' },
|
||||||
|
{ source: '2b', target: 'expand_allied_pipeline', path: 'M 745 510 H 700 V 570' },
|
||||||
|
{ source: '2a', target: 'expand_allied_pipeline', path: 'M 555 510 H 580 V 570' },
|
||||||
|
{ source: '3a', target: 'monitor_vuln', path: 'M 825 460 V 350' },
|
||||||
|
{ source: '3b', target: 'reduce_supply_risk', path: 'M 995 460 V 350' },
|
||||||
|
{ source: '3c', target: 'reduce_price_risk', path: 'M 1165 460 V 350' },
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
const svg = d3.select("#chart");
|
||||||
|
|
||||||
|
// Define arrow marker
|
||||||
|
svg.append("defs").append("marker")
|
||||||
|
.attr("id", "arrow")
|
||||||
|
.attr("viewBox", "0 -5 10 10")
|
||||||
|
.attr("refX", 5)
|
||||||
|
.attr("refY", 0)
|
||||||
|
.attr("markerWidth", 6)
|
||||||
|
.attr("markerHeight", 6)
|
||||||
|
.attr("orient", "auto")
|
||||||
|
.append("path")
|
||||||
|
.attr("d", "M0,-5L10,0L0,5")
|
||||||
|
.attr("fill", "#555");
|
||||||
|
|
||||||
|
// Draw links
|
||||||
|
svg.append("g")
|
||||||
|
.selectAll("path")
|
||||||
|
.data(data.links)
|
||||||
|
.enter().append("path")
|
||||||
|
.attr("class", "link")
|
||||||
|
.attr("d", d => d.path)
|
||||||
|
.attr("marker-end", "url(#arrow)");
|
||||||
|
|
||||||
|
// Draw nodes
|
||||||
|
const node = svg.append("g")
|
||||||
|
.selectAll("g")
|
||||||
|
.data(data.nodes)
|
||||||
|
.enter().append("g")
|
||||||
|
.attr("class", d => `node node-${d.type}`)
|
||||||
|
.attr("transform", d => `translate(${d.x}, ${d.y})`);
|
||||||
|
|
||||||
|
node.append("rect")
|
||||||
|
.attr("width", d => d.width)
|
||||||
|
.attr("height", d => d.height);
|
||||||
|
|
||||||
|
// Add text to nodes
|
||||||
|
node.each(function(d) {
|
||||||
|
const group = d3.select(this);
|
||||||
|
const lineHeight = 14;
|
||||||
|
const y_start = (d.height - (d.text.length * lineHeight)) / 2 + 12;
|
||||||
|
|
||||||
|
d.text.forEach((line, i) => {
|
||||||
|
group.append("text")
|
||||||
|
.attr("x", d.width / 2)
|
||||||
|
.attr("y", y_start + (i * lineHeight))
|
||||||
|
.attr("text-anchor", "middle")
|
||||||
|
.text(line);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (d.kpi) {
|
||||||
|
const kpiLines = wrapText(d.kpi, d.width - 20);
|
||||||
|
const kpi_y_start = 45;
|
||||||
|
kpiLines.forEach((line, i) => {
|
||||||
|
group.append("text")
|
||||||
|
.attr("class", "kpi-text")
|
||||||
|
.attr("x", d.width / 2)
|
||||||
|
.attr("y", kpi_y_start + (i * 12))
|
||||||
|
.attr("text-anchor", "middle")
|
||||||
|
.text(line);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Text wrapping function for KPIs
|
||||||
|
function wrapText(text, width) {
|
||||||
|
const words = text.split(/\s+/).reverse();
|
||||||
|
let word;
|
||||||
|
const lines = [];
|
||||||
|
let line = [];
|
||||||
|
let tspan = d3.select(document.createElement("tspan"));
|
||||||
|
const testSvg = d3.select("body").append("svg").attr("width", 0).attr("height", 0);
|
||||||
|
const textElement = testSvg.append("text").attr("class", "kpi-text");
|
||||||
|
|
||||||
|
while (word = words.pop()) {
|
||||||
|
line.push(word);
|
||||||
|
textElement.text(line.join(" "));
|
||||||
|
if (textElement.node().getComputedTextLength() > width && line.length > 1) {
|
||||||
|
line.pop();
|
||||||
|
lines.push(line.join(" "));
|
||||||
|
line = [word];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lines.push(line.join(" "));
|
||||||
|
testSvg.remove();
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
252
test2-gpt-4.1/index.html
Normal file
252
test2-gpt-4.1/index.html
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>US Critical Minerals Security Flowchart</title>
|
||||||
|
<script src="https://d3js.org/d3.v7.min.js"></script>
|
||||||
|
<style>
|
||||||
|
body { background: #f8f8f8; }
|
||||||
|
.node rect {
|
||||||
|
rx: 8px;
|
||||||
|
ry: 8px;
|
||||||
|
stroke: #333;
|
||||||
|
stroke-width: 1.5px;
|
||||||
|
}
|
||||||
|
.node text {
|
||||||
|
font-family: 'Segoe UI', Arial, sans-serif;
|
||||||
|
font-size: 15px;
|
||||||
|
fill: #222;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.group-title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
fill: #fff;
|
||||||
|
}
|
||||||
|
.group-bg {
|
||||||
|
fill: #111;
|
||||||
|
rx: 8px;
|
||||||
|
ry: 8px;
|
||||||
|
}
|
||||||
|
.group-kpi {
|
||||||
|
font-size: 12px;
|
||||||
|
fill: #bdbdbd;
|
||||||
|
}
|
||||||
|
.arrow {
|
||||||
|
fill: none;
|
||||||
|
stroke: #888;
|
||||||
|
stroke-width: 2px;
|
||||||
|
marker-end: url(#arrowhead);
|
||||||
|
}
|
||||||
|
.main-goal {
|
||||||
|
fill: #4caf50;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.main-goal-bg {
|
||||||
|
fill: #d0e8d0;
|
||||||
|
rx: 8px;
|
||||||
|
ry: 8px;
|
||||||
|
stroke: #4caf50;
|
||||||
|
stroke-width: 2px;
|
||||||
|
}
|
||||||
|
.subnode {
|
||||||
|
fill: #fff;
|
||||||
|
stroke: #bbb;
|
||||||
|
stroke-width: 1.2px;
|
||||||
|
}
|
||||||
|
.highlight {
|
||||||
|
fill: #e0e0e0;
|
||||||
|
}
|
||||||
|
.gray {
|
||||||
|
fill: #f5f5f5;
|
||||||
|
}
|
||||||
|
.light {
|
||||||
|
fill: #e8f5e9;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<svg id="flowchart" width="1100" height="700"></svg>
|
||||||
|
<script>
|
||||||
|
const svg = d3.select("#flowchart");
|
||||||
|
const width = +svg.attr("width");
|
||||||
|
const height = +svg.attr("height");
|
||||||
|
|
||||||
|
// 箭头定义
|
||||||
|
svg.append("defs").append("marker")
|
||||||
|
.attr("id", "arrowhead")
|
||||||
|
.attr("viewBox", "0 -5 10 10")
|
||||||
|
.attr("refX", 18)
|
||||||
|
.attr("refY", 0)
|
||||||
|
.attr("markerWidth", 7)
|
||||||
|
.attr("markerHeight", 7)
|
||||||
|
.attr("orient", "auto")
|
||||||
|
.append("path")
|
||||||
|
.attr("d", "M0,-5L10,0L0,5")
|
||||||
|
.attr("fill", "#888");
|
||||||
|
|
||||||
|
// 主目标
|
||||||
|
svg.append("rect")
|
||||||
|
.attr("class", "main-goal-bg")
|
||||||
|
.attr("x", 370)
|
||||||
|
.attr("y", 30)
|
||||||
|
.attr("width", 360)
|
||||||
|
.attr("height", 60);
|
||||||
|
|
||||||
|
svg.append("text")
|
||||||
|
.attr("class", "main-goal")
|
||||||
|
.attr("x", 550)
|
||||||
|
.attr("y", 60)
|
||||||
|
.attr("text-anchor", "middle")
|
||||||
|
.text("US critical minerals security");
|
||||||
|
|
||||||
|
svg.append("text")
|
||||||
|
.attr("x", 390)
|
||||||
|
.attr("y", 50)
|
||||||
|
.attr("font-size", 13)
|
||||||
|
.attr("fill", "#333")
|
||||||
|
.text("OVERALL GOAL:");
|
||||||
|
|
||||||
|
// 分组数据
|
||||||
|
const groups = [
|
||||||
|
{
|
||||||
|
title: "1",
|
||||||
|
subtitle: "Rebuild US critical minerals industry competitiveness",
|
||||||
|
kpi: "KPIs: Average time from discovery to construction; no. of workers in CM industry; no. of projects in pipeline",
|
||||||
|
x: 60, y: 120, w: 320, h: 80,
|
||||||
|
color: "#111",
|
||||||
|
nodes: [
|
||||||
|
{ id: "1e", text: "Develop critical minerals workforce", x: 80, y: 230, w: 180, h: 40 },
|
||||||
|
{ id: "1f", text: "Increase investment in R&D for new tech and AI usage", x: 80, y: 280, w: 220, h: 40 },
|
||||||
|
{ id: "1c", text: "Improve permitting and regulations", x: 80, y: 330, w: 180, h: 40 },
|
||||||
|
{ id: "1d", text: "Support domestic exploration with funding, data, and AI tools", x: 80, y: 380, w: 240, h: 40 },
|
||||||
|
{ id: "1b2d", text: "Support secondary processing expansion", x: 250, y: 230, w: 200, h: 40 },
|
||||||
|
{ id: "1a", text: "Increase domestic financing and incentives", x: 250, y: 280, w: 200, h: 40 },
|
||||||
|
{ id: "expand", text: "Expand US critical minerals project pipeline", x: 250, y: 330, w: 220, h: 40, highlight: true },
|
||||||
|
{ id: "incprod", text: "Increase domestic production", x: 250, y: 380, w: 180, h: 40, highlight: true }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "2",
|
||||||
|
subtitle: "Reduce import reliance from non-allied countries",
|
||||||
|
kpi: "KPIs: Percentage of US critical minerals imported from or processed by non-allied countries",
|
||||||
|
x: 410, y: 120, w: 320, h: 80,
|
||||||
|
color: "#222",
|
||||||
|
nodes: [
|
||||||
|
{ id: "2a", text: "Expand the role and use of existing multilateral arrangements", x: 430, y: 230, w: 250, h: 40 },
|
||||||
|
{ id: "2b", text: "Expand US government financing tools", x: 430, y: 280, w: 220, h: 40 },
|
||||||
|
{ id: "2c", text: "Support exploration and processing data initiatives", x: 430, y: 330, w: 250, h: 40 },
|
||||||
|
{ id: "alliedprod", text: "Increase allied production", x: 700, y: 230, w: 180, h: 40, highlight: true },
|
||||||
|
{ id: "alliedpipe", text: "Expand allied critical minerals project pipeline", x: 700, y: 280, w: 220, h: 40, highlight: true }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "3",
|
||||||
|
subtitle: "Reduce the risk of critical minerals market disruptions",
|
||||||
|
kpi: "KPIs: No. of months of critical mineral reserves; percentage of value chain data collected",
|
||||||
|
x: 760, y: 120, w: 320, h: 80,
|
||||||
|
color: "#111",
|
||||||
|
nodes: [
|
||||||
|
{ id: "mon", text: "Monitor critical minerals value chain vulnerabilities", x: 780, y: 230, w: 260, h: 40 },
|
||||||
|
{ id: "3a", text: "Develop a real-time value chain mapping and monitoring system", x: 780, y: 280, w: 300, h: 40 },
|
||||||
|
{ id: "supply", text: "Reduce supply disruption risks", x: 780, y: 330, w: 200, h: 40 },
|
||||||
|
{ id: "3b", text: "Expand purpose and use of National Defense Stockpile", x: 780, y: 380, w: 300, h: 40 },
|
||||||
|
{ id: "price", text: "Reduce price disruption risks", x: 780, y: 430, w: 200, h: 40 },
|
||||||
|
{ id: "3c", text: "Identify and deploy targeted market incentives", x: 780, y: 480, w: 300, h: 40 }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
// 画分组背景和标题
|
||||||
|
groups.forEach(g => {
|
||||||
|
svg.append("rect")
|
||||||
|
.attr("class", "group-bg")
|
||||||
|
.attr("x", g.x)
|
||||||
|
.attr("y", g.y)
|
||||||
|
.attr("width", g.w)
|
||||||
|
.attr("height", g.h)
|
||||||
|
.attr("fill", g.color);
|
||||||
|
|
||||||
|
svg.append("text")
|
||||||
|
.attr("class", "group-title")
|
||||||
|
.attr("x", g.x + 18)
|
||||||
|
.attr("y", g.y + 28)
|
||||||
|
.text(g.title);
|
||||||
|
|
||||||
|
svg.append("text")
|
||||||
|
.attr("x", g.x + 45)
|
||||||
|
.attr("y", g.y + 28)
|
||||||
|
.attr("font-size", 16)
|
||||||
|
.attr("fill", "#fff")
|
||||||
|
.text(g.subtitle);
|
||||||
|
|
||||||
|
svg.append("text")
|
||||||
|
.attr("class", "group-kpi")
|
||||||
|
.attr("x", g.x + 10)
|
||||||
|
.attr("y", g.y + 55)
|
||||||
|
.text(g.kpi);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 画节点
|
||||||
|
groups.forEach(g => {
|
||||||
|
g.nodes.forEach(n => {
|
||||||
|
svg.append("rect")
|
||||||
|
.attr("class", n.highlight ? "subnode highlight" : "subnode")
|
||||||
|
.attr("x", n.x)
|
||||||
|
.attr("y", n.y)
|
||||||
|
.attr("width", n.w)
|
||||||
|
.attr("height", n.h);
|
||||||
|
|
||||||
|
svg.append("text")
|
||||||
|
.attr("x", n.x + n.w / 2)
|
||||||
|
.attr("y", n.y + n.h / 2 + 5)
|
||||||
|
.attr("text-anchor", "middle")
|
||||||
|
.attr("font-size", 14)
|
||||||
|
.attr("fill", "#222")
|
||||||
|
.text(n.text);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 连线数据(部分示例,实际可根据需要补充)
|
||||||
|
const links = [
|
||||||
|
// group 1
|
||||||
|
{ from: "1f", to: "1e" },
|
||||||
|
{ from: "1c", to: "expand" },
|
||||||
|
{ from: "1d", to: "expand" },
|
||||||
|
{ from: "1b2d", to: "incprod" },
|
||||||
|
{ from: "1a", to: "incprod" },
|
||||||
|
{ from: "expand", to: "incprod" },
|
||||||
|
// group 2
|
||||||
|
{ from: "2a", to: "alliedprod" },
|
||||||
|
{ from: "2b", to: "alliedpipe" },
|
||||||
|
{ from: "2c", to: "alliedpipe" },
|
||||||
|
{ from: "alliedpipe", to: "alliedprod" },
|
||||||
|
// group 3
|
||||||
|
{ from: "mon", to: "3a" },
|
||||||
|
{ from: "supply", to: "3b" },
|
||||||
|
{ from: "price", to: "3c" }
|
||||||
|
];
|
||||||
|
|
||||||
|
// 节点id到中心点映射
|
||||||
|
const nodeCenters = {};
|
||||||
|
groups.forEach(g => {
|
||||||
|
g.nodes.forEach(n => {
|
||||||
|
nodeCenters[n.id] = {
|
||||||
|
x: n.x + n.w / 2,
|
||||||
|
y: n.y + n.h / 2
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 画连线
|
||||||
|
links.forEach(l => {
|
||||||
|
const from = nodeCenters[l.from];
|
||||||
|
const to = nodeCenters[l.to];
|
||||||
|
if (from && to) {
|
||||||
|
svg.append("path")
|
||||||
|
.attr("class", "arrow")
|
||||||
|
.attr("d", `M${from.x},${from.y+20} C${from.x},${from.y+40} ${to.x},${to.y-40} ${to.x},${to.y-20}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
Loading…
x
Reference in New Issue
Block a user