如何获取d3js.org/world-50m.v1地图国家代码并实现贸易国着色?
问题
我的数据中有一列CountryCode,值为"US"、"DE"等字母代码。我希望为所有交易过指定物种(比如"Iguana",对应列名Genus)的国家在地图上着色,思路是将地图的国家代码与数据中的国家代码匹配后高亮,但当前代码中d.properties.id未定义,无法完成匹配,请问如何获取地图的国家代码并实现着色功能?
我的代码如下:
const map = svg.append("g").attr("class", "map"); // create projection const projection = d3.geoMercator(); // create path generator const path = d3.geoPath().projection(projection); d3.csv("./data/wild-life-trade-live.csv").then(function(data) { // load data and render map const colorScale = d3.scaleSequential() .domain(d3.extent(data, function(d) { return d.value; })) .interpolator(d3.interpolateBlues); d3.json("https://d3js.org/world-50m.v1.json").then(function(world) { // create map features map.selectAll("path") .data(topojson.feature(world, world.objects.countries).features) .enter() .append("path") .attr("d", path) .attr("fill", "lightgray") .attr("stroke", "white") .on("mouseover", function() { d3.select(this).attr("fill", "#D7B895"); }) .on("mouseout", function() { d3.select(this).attr("fill", "lightgray"); }) }); const buttons = d3.select(".section-4") .append("div") .attr("class", "button-container") .selectAll("button") .data(["Iguana", "Pelicans", "Elephants", "Tigers"]) .enter() .append("button") .text(d => d) .style("margin-right", "25px") .style("margin-left", "25px") .style("margin-top", "15px") .style("height", "30px") .style("width", "188px") .on("click", function() { const button = d3.select(this); const animal = button.text(); // Get the text of the clicked button const isClicked = button.classed("selected"); // Check if the button has "selected" class // Reset all buttons to default style buttons.style("background-color", "#D7B895") .style("color", "black") .classed("selected", false); if (!isClicked) { // Apply selected style to clicked button button.style("background-color", "white") .style("color", "black") .classed("selected", true); // Update fill color of countries based on animal map.selectAll("path") .attr("fill", function(d) { const countries = data.filter(function(row) { return row.Genus.toLowerCase() === animal.toLowerCase() && (row.Importer === d.properties.id || row.Exporter === d.properties.id); //this doesn't work! }).map(function(row) { return row.CountryCode; }); if (countries.length > 0) { if (countries.includes(d.properties.id)) { return "#D7B895"; } else { return "lightgray"; } } else { return "lightgray"; } }); svg.transition(); }});
我尝试了这段过滤逻辑但无效,核心问题是d.properties.id未定义,无法获取地图的国家代码进行匹配。
解决方案
1. 纠正地图国家代码的获取方式
你使用的world-50m.v1.json是TopoJSON格式,其中每个国家的ID是直接挂载在feature对象上的d.id(数字类型的ISO 3166-1数值代码,比如美国是840、德国是276),而不是d.properties.id,这是代码里的第一个错误。
2. 处理字母代码与数字ID的映射
你的数据中用的是"US"、"DE"这类字母代码,而地图用的是数字ID,需要先建立两者的映射关系:
// 按需补充你需要的国家代码映射 const countryCodeMap = { "US": 840, "DE": 276, "CN": 156, "GB": 826, // 其他国家... };
3. 预处理数据提升性能
不要在点击事件中反复过滤原始数据,提前把每个物种对应的交易国家数字ID整理成集合:
d3.csv("./data/wild-life-trade-live.csv").then(function(data) { // 新增:预处理物种-国家映射 const animalCountryMap = {}; data.forEach(row => { const genus = row.Genus.toLowerCase(); // 将Importer/Exporter的字母代码转为数字ID const importerId = countryCodeMap[row.Importer]; const exporterId = countryCodeMap[row.Exporter]; if (!animalCountryMap[genus]) { animalCountryMap[genus] = new Set(); } if (importerId) animalCountryMap[genus].add(importerId); if (exporterId) animalCountryMap[genus].add(exporterId); }); // 后续地图渲染和按钮逻辑放在这里... });
4. 修改点击事件的着色逻辑
利用预处理好的映射集合,直接判断当前国家是否需要高亮,同时修复鼠标悬停的颜色冲突问题:
// 地图渲染时修改鼠标事件 map.selectAll("path") .data(topojson.feature(world, world.objects.countries).features) .enter() .append("path") .attr("d", path) .attr("fill", "lightgray") .attr("stroke", "white") .on("mouseover", function(event, d) { // 保存当前颜色,避免覆盖高亮色 d3.select(this).attr("data-original-fill", d3.select(this).attr("fill")) .attr("fill", "#D7B895"); }) .on("mouseout", function(event, d) { // 恢复原始颜色 const originalFill = d3.select(this).attr("data-original-fill"); d3.select(this).attr("fill", originalFill); }); // 点击事件的着色逻辑修改 .on("click", function() { const button = d3.select(this); const animal = button.text().toLowerCase(); const isClicked = button.classed("selected"); buttons.style("background-color", "#D7B895") .style("color", "black") .classed("selected", false); if (!isClicked) { button.style("background-color", "white") .style("color", "black") .classed("selected", true); // 着色逻辑简化 map.selectAll("path") .transition() // 添加过渡动画 .attr("fill", function(d) { // 检查当前国家ID是否在该物种的交易国家集合中 if (animalCountryMap[animal] && animalCountryMap[animal].has(+d.id)) { return "#D7B895"; } return "lightgray"; }); } });
内容的提问来源于stack exchange,提问作者juhuui
相关产品推荐
相关产品推荐

