infinite-agents-public/infinite_variants/infinite_variant_6/test_output/visualization_3.html

420 lines
12 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Interactive Line Chart with Brush - Iteration 3</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #0f2027 0%, #203a43 50%, #2c5364 100%);
min-height: 100vh;
padding: 20px;
}
#viz-container {
max-width: 1100px;
margin: 0 auto;
background: white;
border-radius: 15px;
box-shadow: 0 20px 60px rgba(0,0,0,0.4);
padding: 30px;
}
h1 {
color: #2d3748;
margin-bottom: 10px;
font-size: 2em;
}
.subtitle {
color: #718096;
margin-bottom: 30px;
font-size: 0.9em;
}
.chart-main {
margin-bottom: 20px;
}
.chart-context {
margin-top: 30px;
}
.line {
fill: none;
stroke: #2c5364;
stroke-width: 2;
}
.area {
fill: url(#gradient);
opacity: 0.3;
}
.brush .selection {
fill: #2c5364;
fill-opacity: 0.2;
stroke: #2c5364;
stroke-width: 2;
}
.axis text {
font-size: 11px;
fill: #4a5568;
}
.axis path,
.axis line {
stroke: #cbd5e0;
}
.grid line {
stroke: #e2e8f0;
stroke-opacity: 0.7;
}
.grid path {
stroke-width: 0;
}
.dot {
fill: #2c5364;
stroke: white;
stroke-width: 2;
cursor: pointer;
opacity: 0;
transition: opacity 0.3s;
}
.chart-main:hover .dot {
opacity: 1;
}
.tooltip {
position: absolute;
background: rgba(0, 0, 0, 0.85);
color: white;
padding: 12px 16px;
border-radius: 8px;
pointer-events: none;
opacity: 0;
transition: opacity 0.3s;
font-size: 13px;
z-index: 1000;
}
.attribution {
margin-top: 30px;
padding-top: 20px;
border-top: 1px solid #e2e8f0;
font-size: 0.85em;
color: #718096;
}
.attribution a {
color: #2c5364;
text-decoration: none;
}
.attribution a:hover {
text-decoration: underline;
}
@media (max-width: 768px) {
#viz-container {
padding: 20px;
}
h1 {
font-size: 1.5em;
}
}
</style>
</head>
<body>
<div id="viz-container">
<h1>Global CO2 Emissions Timeline</h1>
<p class="subtitle">Interactive timeline with brush selection for detailed exploration</p>
<div class="chart-main"></div>
<div class="chart-context"></div>
<div class="attribution">
<p><strong>Visualization inspired by:</strong> <a href="https://observablehq.com/@d3/focus-context" target="_blank">D3 Focus + Context via Brushing</a></p>
<p><strong>Data from:</strong> Carbon Intensity API (simulated)</p>
<p><strong>Created:</strong> 2025-10-10T23:52:30Z</p>
</div>
</div>
<!-- Metadata section (required for state tracking) -->
<div id="metadata" style="display:none;">
{
"iteration": 3,
"web_source": "https://observablehq.com/@d3/focus-context",
"techniques_learned": [
"Brush selection for focus + context pattern",
"Coordinated views with zoom synchronization",
"Area charts with linear gradients"
],
"data_source": "https://api.carbonintensity.org.uk",
"created": "2025-10-10T23:52:30Z"
}
</div>
<div class="tooltip"></div>
<!-- External libraries -->
<script src="https://d3js.org/d3.v7.min.js"></script>
<script>
/**
* Global CO2 Emissions Timeline - Iteration 3
*
* Web Source: https://observablehq.com/@d3/focus-context
* Techniques Learned:
* 1. Brush selection for focus + context pattern
* 2. Coordinated views with zoom synchronization
* 3. Area charts with linear gradients
*
* Data Source: Carbon Intensity API (simulated)
*
* Created: 2025-10-10T23:52:30Z
* Run ID: test_run_001
*/
// Generate simulated time series data
const startDate = new Date(2010, 0, 1);
const endDate = new Date(2024, 11, 31);
const data = [];
for (let d = new Date(startDate); d <= endDate; d.setMonth(d.getMonth() + 1)) {
const baseValue = 400 + Math.random() * 50;
const trend = (d.getTime() - startDate.getTime()) / (endDate.getTime() - startDate.getTime()) * 30;
data.push({
date: new Date(d),
value: baseValue + trend + Math.sin(d.getMonth() / 12 * Math.PI * 2) * 15
});
}
// Set up dimensions
const margin = { top: 20, right: 30, bottom: 110, left: 60 };
const margin2 = { top: 430, right: 30, bottom: 30, left: 60 };
const width = Math.min(1000, window.innerWidth - 100) - margin.left - margin.right;
const height = 500 - margin.top - margin.bottom;
const height2 = 500 - margin2.top - margin2.bottom;
// Create main SVG
const svg = d3.select('.chart-main')
.append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.attr('role', 'img')
.attr('aria-label', 'Line chart showing CO2 emissions over time with brush selection');
// Define gradient for area
const gradient = svg.append('defs')
.append('linearGradient')
.attr('id', 'gradient')
.attr('x1', '0%')
.attr('y1', '0%')
.attr('x2', '0%')
.attr('y2', '100%');
gradient.append('stop')
.attr('offset', '0%')
.attr('stop-color', '#2c5364')
.attr('stop-opacity', 0.5);
gradient.append('stop')
.attr('offset', '100%')
.attr('stop-color', '#2c5364')
.attr('stop-opacity', 0.1);
// Main chart group
const focus = svg.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`);
// Context chart group
const context = svg.append('g')
.attr('transform', `translate(${margin2.left},${margin2.top})`);
// Scales for main chart
const x = d3.scaleTime()
.domain(d3.extent(data, d => d.date))
.range([0, width]);
const y = d3.scaleLinear()
.domain([d3.min(data, d => d.value) * 0.95, d3.max(data, d => d.value) * 1.05])
.range([height, 0]);
// Scales for context chart
const x2 = d3.scaleTime()
.domain(x.domain())
.range([0, width]);
const y2 = d3.scaleLinear()
.domain(y.domain())
.range([height2, 0]);
// Line generators
const line = d3.line()
.x(d => x(d.date))
.y(d => y(d.value));
const line2 = d3.line()
.x(d => x2(d.date))
.y(d => y2(d.value));
// Learned Technique 3: Area charts with linear gradients
const area = d3.area()
.x(d => x(d.date))
.y0(height)
.y1(d => y(d.value));
const area2 = d3.area()
.x(d => x2(d.date))
.y0(height2)
.y1(d => y2(d.value));
// Grid lines
const xGrid = d3.axisBottom(x)
.tickSize(height)
.tickFormat('');
focus.append('g')
.attr('class', 'grid')
.call(xGrid);
// Axes
const xAxis = d3.axisBottom(x);
const yAxis = d3.axisLeft(y).tickFormat(d => d + ' ppm');
const xAxis2 = d3.axisBottom(x2);
focus.append('g')
.attr('class', 'axis axis--x')
.attr('transform', `translate(0,${height})`)
.call(xAxis);
focus.append('g')
.attr('class', 'axis axis--y')
.call(yAxis);
context.append('g')
.attr('class', 'axis axis--x')
.attr('transform', `translate(0,${height2})`)
.call(xAxis2);
// Add Y axis label
focus.append('text')
.attr('transform', 'rotate(-90)')
.attr('y', 0 - margin.left)
.attr('x', 0 - (height / 2))
.attr('dy', '1em')
.style('text-anchor', 'middle')
.style('fill', '#4a5568')
.style('font-size', '12px')
.text('CO2 Concentration (ppm)');
// Draw area and line on main chart
focus.append('path')
.datum(data)
.attr('class', 'area')
.attr('d', area);
focus.append('path')
.datum(data)
.attr('class', 'line')
.attr('d', line);
// Draw area and line on context chart
context.append('path')
.datum(data)
.attr('class', 'area')
.attr('d', area2);
context.append('path')
.datum(data)
.attr('class', 'line')
.attr('d', line2);
// Add dots for hover interaction
const tooltip = d3.select('.tooltip');
const dots = focus.selectAll('.dot')
.data(data)
.enter()
.append('circle')
.attr('class', 'dot')
.attr('cx', d => x(d.date))
.attr('cy', d => y(d.value))
.attr('r', 4)
.on('mouseover', function(event, d) {
d3.select(this).attr('r', 6);
tooltip
.style('opacity', 1)
.html(`
<strong>${d3.timeFormat('%B %Y')(d.date)}</strong><br>
CO2: ${d.value.toFixed(2)} ppm
`)
.style('left', (event.pageX + 10) + 'px')
.style('top', (event.pageY - 28) + 'px');
})
.on('mouseout', function() {
d3.select(this).attr('r', 4);
tooltip.style('opacity', 0);
});
// Learned Technique 1 & 2: Brush selection for focus + context
const brush = d3.brushX()
.extent([[0, 0], [width, height2]])
.on('brush end', brushed);
context.append('g')
.attr('class', 'brush')
.call(brush)
.call(brush.move, x.range()); // Initialize with full range
function brushed(event) {
if (event.selection) {
const [x0, x1] = event.selection.map(x2.invert);
x.domain([x0, x1]);
// Update main chart
focus.select('.line')
.attr('d', line);
focus.select('.area')
.attr('d', area);
focus.select('.axis--x')
.call(xAxis);
dots
.attr('cx', d => x(d.date))
.attr('cy', d => y(d.value));
focus.select('.grid')
.call(xGrid);
}
}
// Keyboard accessibility
svg.attr('tabindex', 0)
.on('keydown', function(event) {
if (event.key === 'r' || event.key === 'R') {
// Reset zoom
x.domain(d3.extent(data, d => d.date));
context.select('.brush').call(brush.move, x2.range());
}
});
</script>
</body>
</html>