{
const countryNeighbors = {
"Germany": ["France", "Netherlands", "Belgium", "Luxembourg", "Denmark", "Poland", "Czechia", "Austria", "Switzerland"],
"France": ["Spain", "Italy", "Switzerland", "Germany", "Belgium", "Luxembourg", "Andorra", "Monaco"],
"USA": ["Canada", "Mexico"],
"Canada": ["USA"],
"Mexico": ["USA", "Guatemala", "Belize"],
"China": ["Russian Federation", "Mongolia", "North Korea", "Vietnam", "Laos", "Myanmar", "India", "Bhutan", "Nepal", "Pakistan", "Kazakhstan", "Kyrgyzstan", "Tajikistan"],
"United Kingdom": ["Ireland"],
"Brazil": ["Argentina", "Paraguay", "Uruguay", "Bolivia (Plurinational State of)", "Peru", "Colombia", "Venezuela", "Guyana", "Suriname"],
"Russian Federation": ["Norway", "Finland", "Estonia", "Latvia", "Lithuania", "Poland", "Belarus", "Ukraine", "Georgia", "Azerbaijan", "Kazakhstan", "Mongolia", "China", "North Korea"],
"Japan": ["Rep. of Korea", "China", "Russian Federation"],
"Australia": ["Indonesia", "Papua New Guinea", "New Zealand"],
"India": ["Pakistan", "China", "Nepal", "Bhutan", "Bangladesh", "Myanmar"],
"South Africa": ["Namibia", "Botswana", "Zimbabwe", "Mozambique", "Eswatini", "Lesotho"],
"Poland": ["Germany", "Czechia", "Slovakia", "Ukraine", "Belarus", "Lithuania", "Russian Federation"],
"Netherlands": ["Germany", "Belgium"],
"Belgium": ["France", "Netherlands", "Germany", "Luxembourg"],
"Switzerland": ["France", "Germany", "Austria", "Liechtenstein", "Italy"],
"Italy": ["France", "Switzerland", "Austria", "Slovenia", "San Marino", "Vatican"],
"Spain": ["Portugal", "France", "Andorra", "Morocco"],
"Philippines": ["Malaysia", "Indonesia", "Vietnam"],
"Rep. of Korea": ["North Korea", "Japan", "China"],
"Singapore": ["Malaysia", "Indonesia", "Thailand"],
"Other Asia, nes": ["China", "India", "Japan"],
"Israel": ["Egypt", "Jordan", "Lebanon", "Syria", "State of Palestine"],
"Oman": ["United Arab Emirates", "Yemen", "Saudi Arabia"],
"Slovakia": ["Poland", "Czechia", "Austria", "Hungary", "Ukraine"],
"Indonesia": ["Malaysia", "Papua New Guinea", "Timor-Leste", "Philippines", "Singapore"],
"China, Hong Kong SAR": ["China"],
"Denmark": ["Germany", "Sweden"],
"Norway": ["Sweden", "Finland", "Russian Federation"],
"Portugal": ["Spain"],
"Bangladesh": ["India", "Myanmar"],
"Sweden": ["Norway", "Finland", "Denmark"],
"Qatar": ["Saudi Arabia", "United Arab Emirates", "Bahrain"],
"Hungary": ["Slovakia", "Ukraine", "Romania", "Serbia", "Croatia", "Slovenia", "Austria"],
"Finland": ["Sweden", "Norway", "Russian Federation"],
"Malaysia": ["Thailand", "Indonesia", "Brunei Darussalam", "Singapore"],
"Botswana": ["South Africa", "Namibia", "Zimbabwe", "Zambia"],
"Ireland": ["United Kingdom"],
"Thailand": ["Myanmar", "Laos", "Cambodia", "Malaysia"],
"Romania": ["Hungary", "Ukraine", "Moldova", "Bulgaria", "Serbia"],
"Ukraine": ["Belarus", "Russian Federation", "Poland", "Slovakia", "Hungary", "Romania", "Moldova"],
"Austria": ["Germany", "Czechia", "Slovakia", "Hungary", "Slovenia", "Italy", "Switzerland", "Liechtenstein"],
"China, Macao SAR": ["China"],
"Saudi Arabia": ["Jordan", "Iraq", "Kuwait", "Qatar", "United Arab Emirates", "Oman", "Yemen"],
"Argentina": ["Chile", "Bolivia (Plurinational State of)", "Paraguay", "Brazil", "Uruguay"],
"El Salvador": ["Guatemala", "Honduras"],
"Türkiye": ["Greece", "Bulgaria", "Georgia", "Armenia", "Azerbaijan", "Iran", "Iraq", "Syria"],
"Nigeria": ["Niger", "Chad", "Cameroon", "Benin"],
"Colombia": ["Panama", "Venezuela", "Brazil", "Peru", "Ecuador"],
"Algeria": ["Morocco", "Western Sahara", "Mauritania", "Mali", "Niger", "Libya", "Tunisia"],
"Viet Nam": ["China", "Laos", "Cambodia"],
"Venezuela": ["Colombia", "Brazil", "Guyana"],
"Gabon": ["Equatorial Guinea", "Cameroon", "Congo", "Dem. Rep. of the Congo"],
"Sri Lanka": ["India", "Maldives", "Indonesia"],
"Chile": ["Peru", "Bolivia (Plurinational State of)", "Argentina"],
"Belarus": ["Russian Federation", "Ukraine", "Poland", "Lithuania", "Latvia"],
"Costa Rica": ["Nicaragua", "Panama"],
"Trinidad and Tobago": ["Venezuela", "Guyana", "Grenada"],
"Bulgaria": ["Romania", "Serbia", "North Macedonia", "Greece", "Türkiye"],
"Syria": ["Türkiye", "Iraq", "Jordan", "Lebanon", "Israel"],
"New Zealand": ["Australia", "Fiji", "Tonga"],
"Greece": ["Albania", "North Macedonia", "Bulgaria", "Türkiye"],
"Brunei Darussalam": ["Malaysia", "Indonesia", "Philippines"],
"United Arab Emirates": ["Saudi Arabia", "Oman", "Qatar"],
"Azerbaijan": ["Russian Federation", "Georgia", "Armenia", "Türkiye", "Iran"],
"Lithuania": ["Latvia", "Belarus", "Poland", "Russian Federation"],
"Kazakhstan": ["Russian Federation", "China", "Kyrgyzstan", "Uzbekistan", "Turkmenistan"],
"Tunisia": ["Algeria", "Libya"],
"Dominican Rep.": ["Haiti", "Cuba", "Puerto Rico"],
"Pakistan": ["Iran", "Afghanistan", "China", "India"],
"Ecuador": ["Colombia", "Peru"],
"Sudan (...2011)": ["Egypt", "Libya", "Chad", "Central African Republic", "South Sudan", "Ethiopia", "Eritrea"],
"Morocco": ["Algeria", "Western Sahara", "Spain"],
"Jordan": ["Syria", "Iraq", "Saudi Arabia", "Israel", "State of Palestine"],
"Slovenia": ["Italy", "Austria", "Hungary", "Croatia"],
"Angola": ["Namibia", "Zambia", "Dem. Rep. of the Congo", "Congo"],
"Peru": ["Ecuador", "Colombia", "Brazil", "Bolivia (Plurinational State of)", "Chile"],
"Cambodia": ["Thailand", "Laos", "Viet Nam"],
"Luxembourg": ["Belgium", "France", "Germany"],
"Paraguay": ["Bolivia (Plurinational State of)", "Brazil", "Argentina"],
"Guatemala": ["Mexico", "Belize", "El Salvador", "Honduras"],
"Croatia": ["Slovenia", "Hungary", "Serbia", "Bosnia and Herzegovina", "Montenegro"],
"Côte d'Ivoire": ["Liberia", "Guinea", "Mali", "Burkina Faso", "Ghana"],
"Cuba": ["United States", "Mexico", "Haiti"],
"Yemen": ["Saudi Arabia", "Oman"],
"Bolivia (Plurinational State of)": ["Brazil", "Paraguay", "Argentina", "Chile", "Peru"],
"Zambia": ["Angola", "Dem. Rep. of the Congo", "Tanzania", "Malawi", "Mozambique", "Zimbabwe", "Botswana", "Namibia"],
"Estonia": ["Latvia", "Russian Federation", "Finland"],
"Serbia": ["Hungary", "Romania", "Bulgaria", "North Macedonia", "Kosovo", "Montenegro", "Bosnia and Herzegovina", "Croatia"],
"Egypt": ["Libya", "Sudan", "Israel", "State of Palestine"],
"Zimbabwe": ["Zambia", "Mozambique", "South Africa", "Botswana"],
"Panama": ["Costa Rica", "Colombia"],
"Libya": ["Tunisia", "Algeria", "Niger", "Chad", "Sudan", "Egypt"],
"Ghana": ["Côte d'Ivoire", "Burkina Faso", "Togo"],
"Congo": ["Gabon", "Cameroon", "Central African Republic", "Dem. Rep. of the Congo", "Angola"],
"State of Palestine": ["Israel", "Jordan", "Egypt"],
"Jamaica": ["Cuba", "Haiti", "Cayman Islands"],
"Iceland": ["Norway", "United Kingdom", "Denmark"],
"Iran": ["Türkiye", "Iraq", "Pakistan", "Afghanistan", "Turkmenistan", "Azerbaijan", "Armenia"],
"Czechia": ["Germany", "Poland", "Slovakia", "Austria"],
"Mali": ["Algeria", "Niger", "Burkina Faso", "Côte d'Ivoire", "Guinea", "Senegal", "Mauritania"],
"Kenya": ["Ethiopia", "Somalia", "South Sudan", "Uganda", "Tanzania"],
"Mozambique": ["Tanzania", "Malawi", "Zambia", "Zimbabwe", "South Africa", "Eswatini"],
"Kuwait": ["Iraq", "Saudi Arabia"],
"Bahrain": ["Saudi Arabia", "Qatar", "United Arab Emirates"],
"Seychelles": ["Madagascar", "Mauritius", "Tanzania"],
"Myanmar": ["China", "India", "Bangladesh", "Thailand", "Laos"],
"Nepal": ["India", "China"],
"Burkina Faso": ["Mali", "Niger", "Benin", "Togo", "Ghana", "Côte d'Ivoire"],
"Honduras": ["Guatemala", "El Salvador", "Nicaragua"],
"Papua New Guinea": ["Indonesia", "Australia", "Solomon Islands"],
"Lebanon": ["Syria", "Israel"],
"Malta": ["Italy", "Tunisia", "Libya"],
"Sudan": ["Egypt", "Libya", "Chad", "Central African Republic", "South Sudan", "Ethiopia", "Eritrea"],
"Ethiopia": ["Eritrea", "Djibouti", "Somalia", "Kenya", "South Sudan", "Sudan"],
"United Rep. of Tanzania": ["Kenya", "Uganda", "Rwanda", "Burundi", "Dem. Rep. of the Congo", "Zambia", "Malawi", "Mozambique"],
"Latvia": ["Estonia", "Lithuania", "Belarus", "Russian Federation"],
"Nicaragua": ["Honduras", "Costa Rica"],
"Mongolia": ["Russian Federation", "China"],
"Iraq": ["Türkiye", "Iran", "Kuwait", "Saudi Arabia", "Jordan", "Syria"],
"Cameroon": ["Nigeria", "Chad", "Central African Republic", "Congo", "Gabon", "Equatorial Guinea"],
"Dem. Rep. of the Congo": ["Congo", "Central African Republic", "South Sudan", "Uganda", "Rwanda", "Burundi", "Tanzania", "Zambia", "Angola"],
"Lao People's Dem. Rep.": ["China", "Myanmar", "Thailand", "Cambodia", "Viet Nam"],
"Uzbekistan": ["Kazakhstan", "Kyrgyzstan", "Tajikistan", "Afghanistan", "Turkmenistan"],
"North Macedonia": ["Kosovo", "Serbia", "Bulgaria", "Greece", "Albania"],
"Uruguay": ["Brazil", "Argentina"],
"Guyana": ["Venezuela", "Brazil", "Suriname"],
"Uganda": ["South Sudan", "Kenya", "Tanzania", "Rwanda", "Dem. Rep. of the Congo"],
"Mauritania": ["Western Sahara", "Algeria", "Mali", "Senegal", "Gambia"],
"Kyrgyzstan": ["Kazakhstan", "China", "Tajikistan", "Uzbekistan"],
"Suriname": ["Guyana", "Brazil", "French Guiana"],
"Armenia": ["Georgia", "Azerbaijan", "Iran", "Türkiye"],
"Cyprus": ["Turkey", "Syria", "Lebanon"],
"Gambia": ["Senegal", "Mauritania", "Guinea-Bissau"],
"Georgia": ["Russian Federation", "Azerbaijan", "Armenia", "Türkiye"]
};
function getNeighbors(country) {
return countryNeighbors[country] || [];
}
function analyzeTradeData() {
const countryData = [];
uniqueReporters.forEach(reporter => {
const neighbors = getNeighbors(reporter);
const imports = tradedata.filter(d => d.Reporter === reporter && d.ImportOrExport === "Import");
const exports = tradedata.filter(d => d.Reporter === reporter && d.ImportOrExport === "Export");
const totalImportValue = d3.sum(imports, d => +d.Value_USD);
const totalExportValue = d3.sum(exports, d => +d.Value_USD);
const importPartners = [...new Set(imports.map(d => d.Partner))];
const exportPartners = [...new Set(exports.map(d => d.Partner))];
let neighborImportValue = 0;
let neighborExportValue = 0;
const importNeighbors = [];
const exportNeighbors = [];
importPartners.forEach(partner => {
if (neighbors.includes(partner)) {
importNeighbors.push(partner);
neighborImportValue += d3.sum(imports.filter(d => d.Partner === partner), d => +d.Value_USD);
}
});
exportPartners.forEach(partner => {
if (neighbors.includes(partner)) {
exportNeighbors.push(partner);
neighborExportValue += d3.sum(exports.filter(d => d.Partner === partner), d => +d.Value_USD);
}
});
countryData.push({
country: reporter,
totalNeighbors: neighbors.length,
imports: {
total: totalImportValue,
neighbor: neighborImportValue,
neighborCount: importNeighbors.length,
neighborPercent: totalImportValue > 0 ? (neighborImportValue / totalImportValue) * 100 : 0,
neighbors: importNeighbors
},
exports: {
total: totalExportValue,
neighbor: neighborExportValue,
neighborCount: exportNeighbors.length,
neighborPercent: totalExportValue > 0 ? (neighborExportValue / totalExportValue) * 100 : 0,
neighbors: exportNeighbors
}
});
});
return countryData.sort((a, b) => {
if (b.totalNeighbors !== a.totalNeighbors) {
return b.totalNeighbors - a.totalNeighbors;
}
return a.country.localeCompare(b.country);
});
}
const countryData = analyzeTradeData();
const margin = {top: 60, right: 200, bottom: 80, left: 150};
const width = 960;
const displayCountries = countryData;
const height = Math.max(600, 60 + displayCountries.length * 20 + 80);
const innerWidth = width - margin.left - margin.right;
const innerHeight = height - margin.top - margin.bottom;
const svg = d3.create("svg")
.attr("width", width)
.attr("height", height);
svg.append("text")
.attr("x", width / 2)
.attr("y", 30)
.attr("text-anchor", "middle")
.attr("font-size", "24px")
.text("Trade with Neighboring Countries vs. Total Trade");
const g = svg.append("g")
.attr("transform", `translate(${margin.left}, ${margin.top})`);
const y = d3.scaleBand()
.domain(displayCountries.map(d => d.country))
.range([0, innerHeight])
.padding(0.3);
const maxValue = d3.max(displayCountries, d =>
Math.max(d.imports.total, d.exports.total)
);
const x = d3.scaleLinear()
.domain([0, maxValue * 1.1])
.range([0, innerWidth]);
const colors = {
totalImport: "#ff9966",
totalExport: "#66b3ff",
neighborImport: "#cc5200",
neighborExport: "#0066cc"
};
g.append("g")
.call(d3.axisLeft(y))
.selectAll("text")
.attr("font-size", "8px");
g.append("g")
.attr("transform", `translate(0, ${innerHeight})`)
.call(d3.axisBottom(x).ticks(10)
.tickFormat(d => d3.format(".1f")(d / 1e12) + "T"))
.append("text")
.attr("x", innerWidth / 2)
.attr("y", 40)
.attr("fill", "#000")
.attr("text-anchor", "middle")
.attr("font-size", "14px")
.text("Trade Value (Trillion USD)");
g.selectAll(".row-background")
.data(displayCountries)
.join("rect")
.attr("class", "row-background")
.attr("y", d => y(d.country))
.attr("x", 0)
.attr("width", innerWidth)
.attr("height", y.bandwidth())
.attr("fill", (d, i) => i % 2 === 0 ? "#f8f8f8" : "#ffffff")
.attr("opacity", 0.5);
g.selectAll(".bar-total-import")
.data(displayCountries)
.join("rect")
.attr("class", "bar-total-import")
.attr("y", d => y(d.country))
.attr("x", 0)
.attr("height", y.bandwidth() / 2)
.attr("width", d => x(d.imports.total))
.attr("fill", colors.totalImport)
.attr("stroke", "#fff")
.attr("stroke-width", 0.5);
g.selectAll(".bar-neighbor-import")
.data(displayCountries)
.join("rect")
.attr("class", "bar-neighbor-import")
.attr("y", d => y(d.country))
.attr("x", 0)
.attr("height", y.bandwidth() / 2)
.attr("width", d => x(d.imports.neighbor))
.attr("fill", colors.neighborImport)
.attr("stroke", "#fff")
.attr("stroke-width", 0.5);
g.selectAll(".bar-total-export")
.data(displayCountries)
.join("rect")
.attr("class", "bar-total-export")
.attr("y", d => y(d.country) + y.bandwidth() / 2)
.attr("x", 0)
.attr("height", y.bandwidth() / 2)
.attr("width", d => x(d.exports.total))
.attr("fill", colors.totalExport)
.attr("stroke", "#fff")
.attr("stroke-width", 0.5);
g.selectAll(".bar-neighbor-export")
.data(displayCountries)
.join("rect")
.attr("class", "bar-neighbor-export")
.attr("y", d => y(d.country) + y.bandwidth() / 2)
.attr("x", 0)
.attr("height", y.bandwidth() / 2)
.attr("width", d => x(d.exports.neighbor))
.attr("fill", colors.neighborExport)
.attr("stroke", "#fff")
.attr("stroke-width", 0.5);
g.selectAll(".label-neighbor-import")
.data(displayCountries.filter(d => d.imports.neighborPercent > 20))
.join("text")
.attr("class", "label-neighbor-import")
.attr("y", d => y(d.country) + y.bandwidth() / 4)
.attr("x", d => x(d.imports.neighbor) + 2)
.attr("dy", "0.35em")
.attr("font-size", "7px")
.attr("fill", "#000")
.text(d => `${Math.round(d.imports.neighborPercent)}%`);
g.selectAll(".label-neighbor-export")
.data(displayCountries.filter(d => d.exports.neighborPercent > 20))
.join("text")
.attr("class", "label-neighbor-export")
.attr("y", d => y(d.country) + y.bandwidth() * 3/4)
.attr("x", d => x(d.exports.neighbor) + 2)
.attr("dy", "0.35em")
.attr("font-size", "7px")
.attr("fill", "#000")
.text(d => `${Math.round(d.exports.neighborPercent)}%`);
const legend = svg.append("g")
.attr("transform", `translate(${width - margin.right + 20}, ${margin.top})`);
const legendItems = [
{ label: "Total Imports", color: colors.totalImport },
{ label: "Neighbor Imports", color: colors.neighborImport },
{ label: "Total Exports", color: colors.totalExport },
{ label: "Neighbor Exports", color: colors.neighborExport }
];
legend.selectAll("rect")
.data(legendItems)
.join("rect")
.attr("x", 0)
.attr("y", (d, i) => i * 25)
.attr("width", 15)
.attr("height", 15)
.attr("fill", d => d.color);
legend.selectAll("text")
.data(legendItems)
.join("text")
.attr("x", 25)
.attr("y", (d, i) => i * 25 + 12)
.text(d => d.label);
const explanation = svg.append("g")
.attr("transform", `translate(${width - margin.right + 10}, ${margin.top + 120})`);
explanation.append("text")
.attr("font-size", "14px")
.attr("font-weight", "bold")
.text("Key Insights:");
const insights = [
"• For most countries, trade with neighbors",
" represents only a small fraction of their",
" total trade volume",
"",
"• Even countries with many neighbors tend",
" to trade more with distant partners than",
" with their immediate neighbors",
"",
"• Import and export patterns are generally",
" similar for each country, with some",
" exceptions in specific regions",
"",
"• The darker portions of the bars show the",
" percentage of trade with neighboring",
" countries - note how small this is for",
" many major economies"
];
explanation.selectAll(".insight")
.data(insights)
.join("text")
.attr("class", "insight")
.attr("x", 0)
.attr("y", (d, i) => 25 + i * 20)
.attr("font-size", "12px")
.text(d => d);
svg.append("g")
.attr("transform", `translate(${margin.left}, ${height - 45})`)
.append("text")
.attr("font-size", "14px")
.attr("font-weight", "bold")
.text("Q1: Most countries trade much more with distant partners than with their immediate neighbors.");
svg.append("g")
.attr("transform", `translate(${margin.left}, ${height - 25})`)
.append("text")
.attr("font-size", "14px")
.attr("font-weight", "bold")
.text("Q2: Import and export patterns with neighbors are similar, with only minor differences for most countries.");
const tooltip = d3.select("body").append("div")
.style("position", "absolute")
.style("visibility", "hidden")
.style("background-color", "white")
.style("border", "1px solid black")
.style("padding", "10px")
.style("border-radius", "5px")
.style("font-size", "12px")
.style("z-index", "10")
.style("max-width", "300px");
const allBars = g.selectAll(".bar-total-import, .bar-neighbor-import, .bar-total-export, .bar-neighbor-export");
allBars.on("mouseover", function(event, d) {
const bar = d3.select(this);
const isImport = bar.classed("bar-total-import") || bar.classed("bar-neighbor-import");
const isNeighbor = bar.classed("bar-neighbor-import") || bar.classed("bar-neighbor-export");
const tradeType = isImport ? "Import" : "Export";
const scopeType = isNeighbor ? "with neighbors" : "total";
const totalValue = isImport ? d.imports.total : d.exports.total;
const neighborValue = isImport ? d.imports.neighbor : d.exports.neighbor;
const percent = isImport ? d.imports.neighborPercent : d.exports.neighborPercent;
const neighbors = isImport ? d.imports.neighbors : d.exports.neighbors;
const formatValue = (val) => {
if (val >= 1e12) {
return (val / 1e12).toFixed(2) + " trillion USD";
} else if (val >= 1e9) {
return (val / 1e9).toFixed(2) + " billion USD";
} else {
return (val / 1e6).toFixed(2) + " million USD";
}
};
let content = `<strong>${d.country} ${tradeType}s</strong><br>`;
content += `Total ${tradeType}s: ${formatValue(totalValue)}<br>`;
content += `${tradeType}s with Neighbors: ${formatValue(neighborValue)}<br>`;
content += `Percentage with Neighbors: ${percent.toFixed(1)}%<br>`;
content += `<br>Trading with ${neighbors.length} of ${d.totalNeighbors} neighbors:<br>`;
content += neighbors.length > 0 ? neighbors.join(", ") : "None";
tooltip
.style("visibility", "visible")
.html(content)
.style("left", (event.pageX + 10) + "px")
.style("top", (event.pageY - 10) + "px");
})
.on("mouseout", function() {
tooltip.style("visibility", "hidden");
});
return svg.node();
}