init
This commit is contained in:
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>
|
||||
Reference in New Issue
Block a user