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

404 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>Animated Scatter Plot - Iteration 5</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
#viz-container {
max-width: 1000px;
margin: 0 auto;
background: white;
border-radius: 15px;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
padding: 30px;
}
h1 {
color: #2d3748;
margin-bottom: 10px;
font-size: 2em;
}
.subtitle {
color: #718096;
margin-bottom: 30px;
font-size: 0.9em;
}
.controls {
margin-bottom: 20px;
display: flex;
gap: 15px;
align-items: center;
}
.controls button {
padding: 10px 20px;
background: #667eea;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
transition: background 0.3s;
}
.controls button:hover {
background: #764ba2;
}
.controls button.active {
background: #764ba2;
}
.chart-container {
border: 1px solid #e2e8f0;
border-radius: 8px;
}
.dot {
stroke: #fff;
stroke-width: 1.5px;
cursor: pointer;
}
.axis text {
font-size: 11px;
fill: #4a5568;
}
.axis path,
.axis line {
stroke: #cbd5e0;
}
.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;
}
.legend {
margin-top: 20px;
display: flex;
justify-content: center;
gap: 20px;
flex-wrap: wrap;
}
.legend-item {
display: flex;
align-items: center;
gap: 8px;
}
.legend-color {
width: 15px;
height: 15px;
border-radius: 50%;
}
.attribution {
margin-top: 30px;
padding-top: 20px;
border-top: 1px solid #e2e8f0;
font-size: 0.85em;
color: #718096;
}
.attribution a {
color: #667eea;
text-decoration: none;
}
.attribution a:hover {
text-decoration: underline;
}
</style>
</head>
<body>
<div id="viz-container">
<h1>Economic Indicators by Country</h1>
<p class="subtitle">GDP vs Life Expectancy with animated transitions</p>
<div class="controls">
<button id="play-btn">Play Animation</button>
<button id="reset-btn">Reset</button>
<span id="year-label" style="font-weight: bold; color: #667eea;">Year: 2020</span>
</div>
<div class="chart-container"></div>
<div class="legend">
<div class="legend-item">
<div class="legend-color" style="background: #e74c3c;"></div>
<span>Africa</span>
</div>
<div class="legend-item">
<div class="legend-color" style="background: #3498db;"></div>
<span>Asia</span>
</div>
<div class="legend-item">
<div class="legend-color" style="background: #2ecc71;"></div>
<span>Europe</span>
</div>
<div class="legend-item">
<div class="legend-color" style="background: #f39c12;"></div>
<span>Americas</span>
</div>
</div>
<div class="attribution">
<p><strong>Visualization inspired by:</strong> <a href="https://observablehq.com/@d3/scatterplot" target="_blank">D3 Scatterplot</a></p>
<p><strong>Data from:</strong> World Bank API (simulated)</p>
<p><strong>Created:</strong> 2025-10-10T23:55:15Z</p>
</div>
</div>
<!-- Metadata section (required for state tracking) -->
<div id="metadata" style="display:none;">
{
"iteration": 5,
"web_source": "https://observablehq.com/@d3/scatterplot",
"techniques_learned": [
"Scatter plot with size encoding",
"Animated data transitions between states",
"Color scales for categorical data"
],
"data_source": "https://api.worldbank.org",
"created": "2025-10-10T23:55:15Z"
}
</div>
<div class="tooltip"></div>
<script src="https://d3js.org/d3.v7.min.js"></script>
<script>
/**
* Economic Indicators by Country - Iteration 5
*
* Web Source: https://observablehq.com/@d3/scatterplot
* Techniques Learned:
* 1. Scatter plot with size encoding
* 2. Animated data transitions between states
* 3. Color scales for categorical data
*
* Data Source: World Bank API (simulated)
*
* Created: 2025-10-10T23:55:15Z
* Run ID: test_run_001
*/
// Generate simulated data for multiple years
const regions = ['Africa', 'Asia', 'Europe', 'Americas'];
const countries = [
{ name: 'USA', region: 'Americas' },
{ name: 'China', region: 'Asia' },
{ name: 'India', region: 'Asia' },
{ name: 'Germany', region: 'Europe' },
{ name: 'Brazil', region: 'Americas' },
{ name: 'Nigeria', region: 'Africa' },
{ name: 'Japan', region: 'Asia' },
{ name: 'France', region: 'Europe' },
{ name: 'UK', region: 'Europe' },
{ name: 'South Africa', region: 'Africa' }
];
const years = [2020, 2021, 2022, 2023, 2024];
const dataByYear = {};
years.forEach(year => {
dataByYear[year] = countries.map(country => ({
country: country.name,
region: country.region,
gdp: Math.random() * 50000 + 10000 + (year - 2020) * 2000,
lifeExpectancy: Math.random() * 15 + 65 + (year - 2020) * 0.3,
population: Math.random() * 300000000 + 50000000
}));
});
let currentYear = 2020;
let isPlaying = false;
let animationInterval;
const margin = { top: 20, right: 30, bottom: 50, left: 60 };
const width = Math.min(900, window.innerWidth - 100) - margin.left - margin.right;
const height = 500 - margin.top - margin.bottom;
const svg = d3.select('.chart-container')
.append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`);
const tooltip = d3.select('.tooltip');
// Learned Technique 3: Color scales for categorical data
const colorScale = d3.scaleOrdinal()
.domain(regions)
.range(['#e74c3c', '#3498db', '#2ecc71', '#f39c12']);
// Scales
const x = d3.scaleLinear()
.domain([0, 70000])
.range([0, width]);
const y = d3.scaleLinear()
.domain([60, 85])
.range([height, 0]);
// Learned Technique 1: Size encoding for population
const size = d3.scaleSqrt()
.domain([0, 400000000])
.range([5, 40]);
// Axes
const xAxis = d3.axisBottom(x).tickFormat(d => '$' + d/1000 + 'K');
const yAxis = d3.axisLeft(y).tickFormat(d => d + ' yrs');
svg.append('g')
.attr('class', 'axis')
.attr('transform', `translate(0,${height})`)
.call(xAxis);
svg.append('g')
.attr('class', 'axis')
.call(yAxis);
// Axis labels
svg.append('text')
.attr('x', width / 2)
.attr('y', height + 40)
.style('text-anchor', 'middle')
.style('fill', '#4a5568')
.text('GDP per capita (USD)');
svg.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')
.text('Life Expectancy (years)');
// Draw initial data
updateChart(currentYear);
function updateChart(year) {
const data = dataByYear[year];
// Learned Technique 2: Animated transitions between states
const dots = svg.selectAll('.dot')
.data(data, d => d.country);
dots.enter()
.append('circle')
.attr('class', 'dot')
.attr('cx', d => x(d.gdp))
.attr('cy', d => y(d.lifeExpectancy))
.attr('r', 0)
.attr('fill', d => colorScale(d.region))
.merge(dots)
.transition()
.duration(800)
.attr('cx', d => x(d.gdp))
.attr('cy', d => y(d.lifeExpectancy))
.attr('r', d => size(d.population));
dots.exit()
.transition()
.duration(500)
.attr('r', 0)
.remove();
// Re-attach event listeners
svg.selectAll('.dot')
.on('mouseover', function(event, d) {
d3.select(this)
.transition()
.duration(200)
.attr('r', size(d.population) * 1.2);
tooltip
.style('opacity', 1)
.html(`
<strong>${d.country}</strong><br>
Region: ${d.region}<br>
GDP: $${d.gdp.toFixed(0)}<br>
Life Exp: ${d.lifeExpectancy.toFixed(1)} yrs<br>
Pop: ${(d.population / 1000000).toFixed(1)}M
`)
.style('left', (event.pageX + 10) + 'px')
.style('top', (event.pageY - 28) + 'px');
})
.on('mouseout', function(event, d) {
d3.select(this)
.transition()
.duration(200)
.attr('r', size(d.population));
tooltip.style('opacity', 0);
});
document.getElementById('year-label').textContent = `Year: ${year}`;
}
// Controls
document.getElementById('play-btn').addEventListener('click', function() {
if (isPlaying) {
clearInterval(animationInterval);
this.textContent = 'Play Animation';
this.classList.remove('active');
isPlaying = false;
} else {
this.textContent = 'Pause';
this.classList.add('active');
isPlaying = true;
animationInterval = setInterval(() => {
currentYear++;
if (currentYear > 2024) {
currentYear = 2020;
}
updateChart(currentYear);
}, 1500);
}
});
document.getElementById('reset-btn').addEventListener('click', function() {
if (isPlaying) {
clearInterval(animationInterval);
document.getElementById('play-btn').textContent = 'Play Animation';
document.getElementById('play-btn').classList.remove('active');
isPlaying = false;
}
currentYear = 2020;
updateChart(currentYear);
});
</script>
</body>
</html>