graph = {
const width = 960;
const height = 600;
const svg = d3.select(DOM.svg(width, height));
const g = svg.append("g");
const panningLimit = [[-100, 0], [width + 100, height]];
const zoom = d3.zoom()
.scaleExtent([1, 8])
.translateExtent(panningLimit)
.on("zoom", (event) => {
g.attr("transform", event.transform);
});
svg.call(zoom);
const listContainer = d3.create("div")
.style("width", "200px")
.style("height", "500px")
.style("overflow", "auto")
.style("border", "1px solid #ccc")
.style("padding", "10px")
.style("display", "none");
const tooltip = d3.select("body").append("div")
.attr("class", "tooltip")
.style("position", "absolute")
.style("visibility", "hidden")
.style("background", "#f9f9f9")
.style("border", "1px solid #e0e0e0")
.style("border-radius", "5px")
.style("padding", "10px")
.style("font-family", "Arial, sans-serif")
.style("font-size", "14px")
.style("box-shadow", "2px 2px 6px 0px rgba(0,0,0,0.3)")
.style("font-weight", "bold")
.style("text-align", "center");
// Add two span elements within the tooltip for text and count
tooltip.selectAll("span")
.data(["text", "count"])
.enter().append("span")
.style("display", "block")
.style("margin-top", "8px");
// Function to display the tooltip on mouseover
function showTooltip(event, d) {
const countryName = d.properties.name;
const count = new_billionaires_counts.get(countryName) || 0;
let text = `<span style="font-size: 18px; color: #333;">${countryName}</span><br><span>Number of billionaires: <span style="color: #ff8c00; font-weight: bold;">${count}</span></span><br><span style="font-size: 10px; color: #999;">Double click to see more information</span>`;
tooltip.html(text)
.style("visibility", "visible")
.style("left", (event.pageX + 10) + "px")
.style("top", (event.pageY - 10) + "px");
}
// Function to hide the tooltip on mouseout
function hideTooltip() {
tooltip.style("visibility", "hidden");
}
let max = 0;
// Select and display the map data with interactive features
g.selectAll("path")
.data(topojson.feature(world, world.objects.countries).features)
.enter().append("path")
.attr("d", path)
.style("fill", d => {
const value = new_billionaires_counts.get(d.properties.name) || 0;
max = Math.max(max, value);
return colorScale(value);
})
.style("stroke", "black")
.on("mouseover", showTooltip)
.on("mouseout", hideTooltip)
.on("dblclick", function(event, d) {
tooltip.style("visibility", "hidden");
const clickedCountryName = d.properties.name;
const billionairesInCountry = billionaires_map_counts.get(clickedCountryName) || 0;
updateBillionairesList(billionairesInCountry);
g.selectAll("path").remove();
g.append("path")
.datum(d)
.attr("d", path)
.style("fill", d => {
const value = new_billionaires_counts.get(d.properties.name) || 0;
return colorScale(value);
})
.style("stroke", "black");
listContainer.style("display", "block");
// Event listener for the 'Escape' key
d3.select("body").on("keydown", (event) => {
if (event.key === "Escape") {
listContainer.style("display", "none");
g.selectAll("path").remove();
svg.call(zoom);
// Rebind the country data to path elements and re-apply attributes and styles
g.selectAll("path")
.data(topojson.feature(world, world.objects.countries).features)
.enter().append("path")
.attr("d", path)
.style("fill", d => {
const value = new_billionaires_counts.get(d.properties.name) || 0;
return colorScale(value);
})
.style("stroke", "black")
.on("mouseover", showTooltip)
.on("mouseout", hideTooltip)
.on("dblclick", function(event, d) {
tooltip.style("visibility", "hidden");
const clickedCountryName = d.properties.name;
const billionairesInCountry = billionaires_map_counts.get(clickedCountryName) || 0;
updateBillionairesList(billionairesInCountry);
g.selectAll("path").remove();
g.append("path")
.datum(d)
.attr("d", path)
.style("fill", d => {
const value = new_billionaires_counts.get(d.properties.name) || 0;
return colorScale(value);
})
.style("stroke", "black");
listContainer.style("display", "block");
});
}
});
});
// Function to update the list of billionaires based on the selected country
function updateBillionairesList(billionaires) {
// List update logic based on the billionaires data
listContainer.selectAll("div").remove();
if (!billionaires || billionaires.length === 0) {
listContainer.append("div")
.text("Be the first one to pave the way!")
.style("padding", "20px 20px 10px 20px");
listContainer.append("div")
.text("Press 'esc' to go back")
.style("padding", "10px 20px 20px 20px")
.style("color", "grey")
.style("font-size", "14px");
} else {
const billionairesDiv = listContainer.selectAll("div")
.data(billionaires, d => d.name);
billionairesDiv.enter()
.append("div")
.merge(billionairesDiv)
.style("padding", "20px")
.style("border-bottom", "1px solid #ddd")
.html(d => `
<div style="font-size: 22px;">${d.name}</div>
<div style="color: orange;">Rank: ${d.rank}</div>
<div style="color: orange;">Worth: $${d.worth/1000}B</div>
<div style="font-size: 14px;">Gender: ${d.gender}</div>
<div style="font-size: 14px;">Age: ${d.age}</div>
<div style="font-size: 14px;">Industry: ${d.industry}</div>
`);
billionairesDiv.exit().remove();
listContainer.style("overflow", "auto");
// Addition of a title for the list container
const titleContainer = listContainer.insert("div", ":first-child")
.style("font-size", "24px")
.style("font-weight", "bold");
titleContainer.append("span")
.text("Billionaires List")
.style("color", "black")
.append("br");
titleContainer.append("span")
.text("Press 'esc' to go back")
.style("font-size", "14px")
.style("color", "grey");
titleContainer.style("position", "sticky")
.style("top", "0")
.style("background", "white");
}
}
// Append a color legend to the SVG
const legend = svg.append("g")
.attr("transform", `translate(${width - 450}, 400)`);
if (max === 1) {
legend.append(() => color_legend1);
} else if (max === 2){
legend.append(() => color_legend2);
} else if (max === 3) {
legend.append(() => color_legend3);
} else if (max === 4) {
legend.append(() => color_legend4);
} else {
legend.append(() => color_legend);
}
// Create a main container for the visualization with a white background and flex display
const container = d3.create("div")
.style("display", "flex")
.style("background-color", "white");
// Append the SVG and list container to the main container
container.append(() => svg.node());
container.append(() => listContainer.node());
// Return the main container for display
return container.node();
}