async function processData(dataType, viewType, countryLimit = 10, categoryFilter = "all") {
let tradeData, categoriesData, countriesData;
if (dataType === "exports") {
tradeData = await FileAttachment("exports.csv").csv();
if (viewType === "categories" || categoryFilter !== "all") {
const categoriesData1 = await FileAttachment("export_categories.csv").csv();
const categoriesData2 = await FileAttachment("export_categories@1.csv").csv();
categoriesData = [...categoriesData1, ...categoriesData2];
}
} else {
tradeData = await FileAttachment("imports.csv").csv();
if (viewType === "categories" || categoryFilter !== "all") {
const categoriesData1 = await FileAttachment("import_categories.csv").csv();
const categoriesData2 = await FileAttachment("import_categories@1.csv").csv();
categoriesData = [...categoriesData1, ...categoriesData2];
}
}
countriesData = await FileAttachment("countries.csv").csv();
const sourceColumn = "from";
const targetColumn = "to";
const volumeColumn = "value";
let items = [];
let volumeByItem = {};
let countryToContinent = {};
countriesData.forEach(d => {
countryToContinent[d.Country] = d.Continent;
});
if (categoryFilter !== "all" && viewType !== "categories") {
if (!categoriesData) {
if (dataType === "exports") {
const categoriesData1 = await FileAttachment("export_categories.csv").csv();
const categoriesData2 = await FileAttachment("export_categories@1.csv").csv();
categoriesData = [...categoriesData1, ...categoriesData2];
} else {
const categoriesData1 = await FileAttachment("import_categories.csv").csv();
const categoriesData2 = await FileAttachment("import_categories@1.csv").csv();
categoriesData = [...categoriesData1, ...categoriesData2];
}
}
// Filter trade data to only include entries related to the selected category
const categoryFilteredData = categoriesData.filter(d => d.category === categoryFilter);
const relatedPairs = {};
categoryFilteredData.forEach(d => {
const key = `${d.from}_${d.to}`;
relatedPairs[key] = true;
});
tradeData = tradeData.filter(d => {
const key = `${d[sourceColumn]}_${d[targetColumn]}`;
return relatedPairs[key];
});
}
if (viewType === "countries") {
// Regular logic - find volume for all countries
tradeData.forEach(d => {
const source = d[sourceColumn];
const volume = parseVolume(d[volumeColumn]);
volumeByItem[source] = (volumeByItem[source] || 0) + volume;
});
// Sort countries by volume in descending order
const sortedEntries = Object.entries(volumeByItem)
.sort((a, b) => b[1] - a[1]);
// Take top N countries
items = sortedEntries
.slice(0, parseInt(countryLimit))
.map(d => d[0]);
// If showing less than all countries, add "Other"
if (parseInt(countryLimit) < sortedEntries.length && countryLimit !== "197") {
// Calculate total volume for other countries
let othersVolume = 0;
for (let i = parseInt(countryLimit); i < sortedEntries.length; i++) {
othersVolume += sortedEntries[i][1];
}
// Add "Other" item
items.push("Other");
volumeByItem["Other"] = othersVolume;
}
} else if (viewType === "regions") {
tradeData.forEach(d => {
const source = d[sourceColumn];
const continent = countryToContinent[source] || "Unknown";
const volume = parseVolume(d[volumeColumn]);
volumeByItem[continent] = (volumeByItem[continent] || 0) + volume;
});
items = Object.entries(volumeByItem)
.sort((a, b) => b[1] - a[1])
.map(d => d[0]);
} else if (viewType === "categories") {
// For categories view, filter categories if a specific one is selected
if (categoryFilter !== "all") {
categoriesData = categoriesData.filter(d => d.category === categoryFilter);
}
categoriesData.forEach(d => {
const category = d.category;
const volume = parseVolume(d[volumeColumn]);
volumeByItem[category] = (volumeByItem[category] || 0) + volume;
});
items = Object.entries(volumeByItem)
.sort((a, b) => b[1] - a[1])
.slice(0, 10)
.map(d => d[0]);
}
const n = items.length;
const matrix = Array(n).fill().map(() => Array(n).fill(0));
const linkValues = {};
if (viewType === "countries") {
// We need to handle the "Other" category when building the matrix
const otherIdx = items.indexOf("Other");
tradeData.forEach(d => {
const source = d[sourceColumn];
const target = d[targetColumn];
let sourceIdx = items.indexOf(source);
let targetIdx = items.indexOf(target);
// Check if source country is in "Other" category
if (sourceIdx === -1 && otherIdx !== -1) {
sourceIdx = otherIdx;
}
// Check if target country is in "Other" category
if (targetIdx === -1 && otherIdx !== -1) {
targetIdx = otherIdx;
}
if (sourceIdx !== -1 && targetIdx !== -1) {
const volume = parseVolume(d[volumeColumn]);
const maxValue = 1e10;
// We need to accumulate for "Other_Other" case, not replace
const sourceKey = sourceIdx === otherIdx ? "Other" : source;
const targetKey = targetIdx === otherIdx ? "Other" : target;
const linkKey = `${sourceKey}_${targetKey}`;
linkValues[linkKey] = (linkValues[linkKey] || 0) + volume;
matrix[sourceIdx][targetIdx] += volume / maxValue;
}
});
} else if (viewType === "regions") {
tradeData.forEach(d => {
const source = countryToContinent[d[sourceColumn]] || "Unknown";
const target = countryToContinent[d[targetColumn]] || "Unknown";
const sourceIdx = items.indexOf(source);
const targetIdx = items.indexOf(target);
if (sourceIdx !== -1 && targetIdx !== -1) {
const volume = parseVolume(d[volumeColumn]);
const maxValue = 1e10;
linkValues[`${source}_${target}`] = volume;
matrix[sourceIdx][targetIdx] += volume / maxValue;
}
});
} else if (viewType === "categories") {
categoriesData.forEach(d => {
const category = d.category;
const target = d[targetColumn];
const sourceIdx = items.indexOf(category);
const targetIdx = items.indexOf(target);
if (sourceIdx !== -1 && targetIdx !== -1) {
const volume = parseVolume(d[volumeColumn]);
const maxValue = 1e10;
const linkKey = `${category}_${target}`;
linkValues[linkKey] = (linkValues[linkKey] || 0) + volume;
matrix[sourceIdx][targetIdx] += volume / maxValue;
}
});
}
const formattedData = Object.assign(matrix, {
names: items,
linkValues: linkValues,
countryToContinent: countryToContinent
});
return formattedData;
}