You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何获取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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 04:25:04