使用下拉菜单 D3​​ 突出显示国家/地区

Highlight country once selected with dropdown D3

我有一张非洲地图,我使用 D3 加载了该地图,该地图具有多个列,其中包括一个名为 ADMIN 的列,该列具有每个国家/地区的名称。我试图在我的地图上突出显示下拉列表中 selected 的国家/地区。例如,如果我 select 阿尔及利亚在我的下拉列表中,阿尔及利亚将在我的地图上突出显示。地图本身以及下拉菜单都可以正常加载。我只是在协调下拉功能和突出显示功能之间的交互时遇到了麻烦,因此 selected 国家实际上突出显示了。

//begin script when window loads
window.onload = setMap();

var attrArray = ["Algeria", "Angola", "Benin", "Botswana", "Burkina Faso", "Burundi", "Cabo Verde", "Cameroon", "Central African Republic", "Chad", "Comoros", "Congo", "Côte d'Ivoire", "Djibouti", "DR Congo", "Egypt", "Equatorial Guinea", "Eritrea", "Eswatini",
  "Ethiopia", "Gabon", "Gambia", "Ghana", "Guinea", "Guinea-Bissau", "Kenya", "Lesotho", "Liberia", "Libya", "Madagascar", "Malawi", "Mali", "Mauritania", "Mauritius", "Morocco", "Mozambique", "Namibia", "Niger", "Nigeria", "Rwanda", "Sao Tome & Principe", "Senegal",
  "Seychelles", "Sierra Leone", "Somalia", "South Africa", "South Sudan", "Sudan", "Tanzania", "Togo", "Tunisia", "Uganda", "Zambia", "Zimbabwe"
];

var expressed = attrArray[0];

//set up choropleth map
function setMap() {
  //map frame dimensions
  var width = 1200,
    height = 1000;

  //create new svg container for the map
  var map = d3.select("body")
    .append("svg")
    .attr("class", "map")
    .attr("width", width)
    .attr("height", height);

  //create Albers equal area conic projection centered on France
  var projection = d3.geoOrthographic()
    .scale(600)
    .translate([width / 2, height / 2.7])
    .clipAngle(85)
    .precision(.1);

  var path = d3.geoPath()
    .projection(projection)

  var graticule = d3.geoGraticule()
    .step([5, 5]); //place graticule lines every 5 degrees of longitude and latitude

  //create graticule background
  var gratBackground = map.append("path")
    .datum(graticule.outline()) //bind graticule background
    .attr("class", "gratBackground") //assign class for styling
    .attr("d", path) //project graticule

  //Example 2.6 line 5...create graticule lines
  var gratLines = map.selectAll(".gratLines")
    .data(graticule.lines()) //bind graticule lines to each element to be created
    .enter() //create an element for each datum
    .append("path") //append each element to the svg as a path element
    .attr("class", "gratLines") //assign class for styling
    .attr("d", path); //project graticule lines

  //use queue to parallelize asynchronous data loading
  d3.queue()
    .defer(d3.csv, "https://raw.githubusercontent.com/Sharitau/website/main/Portfolio/GovernanceFinal/data/AfricaCountries.csv") //load attributes from csv
    .defer(d3.json, "https://raw.githubusercontent.com/Sharitau/website/main/Portfolio/GovernanceFinal/data/world.topojson") //load background spatial data
    .defer(d3.json, "https://raw.githubusercontent.com/Sharitau/website/main/Portfolio/GovernanceFinal/data/Africa.TOPOJSON")
    .await(callback);

  function callback(error, csvData, world, africa) {
    //translate europe TopoJSON
    var basemapCountries = topojson.feature(world, world.objects.ne_10m_admin_0_countries),
      choroplethCountries = topojson.feature(africa, africa.objects.ne_10m_admin_0_countries).features;

    //select graticule elements that will be created
    //add Europe countries to map
    var countries = map.append("path")
      .datum(basemapCountries)
      .attr("class", "countries")
      .attr("d", path);

    //add France regions to map
    var regions = map.selectAll(".regions")
      .data(choroplethCountries)
      .enter()
      .append("path")
      .attr("class", function(d) {
        return "regions " + d.properties.ADMIN;
      })
      .attr("d", path)

    console.log(countries);
    console.log(regions)

    createDropdown(csvData);
  };

  function createDropdown(csvData) {
    //add select element
    var dropdown = d3.select("body")
      .append("select")
      .attr("class", "dropdown")
      .attr("d", path)
      .on("change", function() {
        highlight(d.properties);
      });

    //add initial option
    var titleOption = dropdown.append("option")
      .attr("class", "titleOption")
      .attr("disabled", "true")
      .text("Select Country");

    //add attribute name options
    var attrOptions = dropdown.selectAll("attrOptions")
      .data(attrArray)
      .enter()
      .append("option")
      .attr("value", function(d) {
        return d
      })
      .text(function(d) {
        return d
      });
  };

  //function to highlight enumeration units and bars
  function highlight(props) {
    //change stroke
    var selected = d3.selectAll("." + props.ADMIN)
      .style("stroke", "blue")
      .style("stroke-width", "2")
    console.log(props.ADMIN);
  };
};
.countries {
  fill: #0B1E38;
  stroke: #fff;
  stroke-width: 2px;
}

.map {
  position: absolute;
  right: 0;
}

.regions {
  fill: #316331;
}

.gratLines {
  fill: none;
  stroke: #999;
  stroke-width: 1px;
}

.gratBackground {
  fill: #0B1E38;
}

.dropdown {
  position: absolute;
  top: 30px;
  left: 500px;
  z-index: 10;
  font-family: sans-serif;
  font-size: 1em;
  font-weight: bold;
  padding: 2px;
  border: 1px solid #999;
  box-shadow: 2px 2px 4px #999;
}

option {
  font-weight: normal;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-geo-projection.v2.min.js"></script>
<script src="https://d3js.org/topojson.v3.min.js"></script>

要使用 this answer 回答您的问题,您需要使用 select 元素上的属性 value 获取所选选项的值 - 可用 [=18] =] 在这个函数中。

var attrArray = ["Algeria", "Angola", "Benin", "Botswana", "Burkina Faso", "Burundi", "Cabo Verde", "Cameroon", "Central African Republic", "Chad", "Comoros", "Congo", "Côte d'Ivoire", "Djibouti", "DR Congo", "Egypt", "Equatorial Guinea", "Eritrea", "Eswatini",
  "Ethiopia", "Gabon", "Gambia", "Ghana", "Guinea", "Guinea-Bissau", "Kenya", "Lesotho", "Liberia", "Libya", "Madagascar", "Malawi", "Mali", "Mauritania", "Mauritius", "Morocco", "Mozambique", "Namibia", "Niger", "Nigeria", "Rwanda", "Sao Tome & Principe", "Senegal",
  "Seychelles", "Sierra Leone", "Somalia", "South Africa", "South Sudan", "Sudan", "Tanzania", "Togo", "Tunisia", "Uganda", "Zambia", "Zimbabwe"
];

//map frame dimensions
var width = 1200,
  height = 1000;

//create new svg container for the map
var map = d3.select("body")
  .append("svg")
  .attr("class", "map")
  .attr("width", width)
  .attr("height", height);

//create Albers equal area conic projection centered on France
var projection = d3.geoOrthographic()
  .scale(600)
  .translate([width / 2, height / 2.7])
  .clipAngle(85)
  .precision(.1);

var path = d3.geoPath()
  .projection(projection)

var graticule = d3.geoGraticule()
  .step([5, 5]); //place graticule lines every 5 degrees of longitude and latitude

//create graticule background
var gratBackground = map.append("path")
  .datum(graticule.outline()) //bind graticule background
  .attr("class", "gratBackground") //assign class for styling
  .attr("d", path) //project graticule

//Example 2.6 line 5...create graticule lines
var gratLines = map.selectAll(".gratLines")
  .data(graticule.lines()) //bind graticule lines to each element to be created
  .enter() //create an element for each datum
  .append("path") //append each element to the svg as a path element
  .attr("class", "gratLines") //assign class for styling
  .attr("d", path); //project graticule lines

//use queue to parallelize asynchronous data loading
d3.queue()
  .defer(d3.json, "https://raw.githubusercontent.com/Sharitau/website/main/Portfolio/GovernanceFinal/data/world.topojson") //load background spatial data
  .defer(d3.json, "https://raw.githubusercontent.com/Sharitau/website/main/Portfolio/GovernanceFinal/data/Africa.TOPOJSON")
  .await(callback);

function callback(error, world, africa) {
  //translate europe TopoJSON
  var basemapCountries = topojson.feature(world, world.objects.ne_10m_admin_0_countries),
    choroplethCountries = topojson.feature(africa, africa.objects.ne_10m_admin_0_countries).features;

  //select graticule elements that will be created
  //add Europe countries to map
  var countries = map.append("path")
    .datum(basemapCountries)
    .attr("class", "countries")
    .attr("d", path);

  //add France regions to map
  var regions = map.selectAll(".regions")
    .data(choroplethCountries)
    .enter()
    .append("path")
    .attr("class", function(d) {
      return "regions " + d.properties.ADMIN;
    })
    .attr("d", path);

  createDropdown();
};

function createDropdown() {
  //add select element
  var dropdown = d3.select("body")
    .append("select")
    .attr("class", "dropdown")
    .on("change", function() {
      // The value is the name of the country
      var countryName = this.value;
      highlight(countryName);
    });

  //add initial option
  var titleOption = dropdown.append("option")
    .attr("class", "titleOption")
    .attr("disabled", "true")
    .text("Select Country");

  //add attribute name options
  var attrOptions = dropdown.selectAll("attrOptions")
    .data(attrArray)
    .enter()
    .append("option")
    .attr("value", function(d) {
      return d;
    })
    .text(function(d) {
      return d;
    });
};

//function to highlight enumeration units and bars
function highlight(countryName) {
  //change stroke
  var selected = d3.selectAll("." + countryName)
    .style("stroke", "blue")
    .style("stroke-width", "2")
  console.log(countryName);
};
.countries {
  fill: #0B1E38;
  stroke: #fff;
  stroke-width: 2px;
}

.map {
  position: absolute;
  right: 0;
}

.regions {
  fill: #316331;
}

.gratLines {
  fill: none;
  stroke: #999;
  stroke-width: 1px;
}

.gratBackground {
  fill: #0B1E38;
}

.dropdown {
  position: absolute;
  top: 30px;
  left: 500px;
  z-index: 10;
  font-family: sans-serif;
  font-size: 1em;
  font-weight: bold;
  padding: 2px;
  border: 1px solid #999;
  box-shadow: 2px 2px 4px #999;
}

option {
  font-weight: normal;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-geo-projection.v2.min.js"></script>
<script src="https://d3js.org/topojson.v3.min.js"></script>

请注意,class 不是存储国家/地区名称的正确位置,尽管它是一个简单的选择器。多个单词的国家/地区名称未正确注册,例如“Sierra Leone”的选择器是 ".Sierra.Leone" 而不是 ".Sierra Leone"。此外,尝试选择“刚果”;每个包含“刚果”一词的国家/地区都会突出显示。通过创建自定义属性“data-country”并使用 d3.select("[data-country='" + countryName + "']") 选择正确的国家/地区,您的生活会变得更加轻松。另见 the docs 关于属性选择器。

此外,attrArray中的名字和国家地图中的名字没有对齐,所以一些突出显示根本不起作用。我建议只从 TopoJSON 中获取名称或创建从 CSV 名称到 TopoJSON 名称的映射。

//map frame dimensions
var width = 1200,
  height = 1000;

//create new svg container for the map
var map = d3.select("body")
  .append("svg")
  .attr("class", "map")
  .attr("width", width)
  .attr("height", height);

//create Albers equal area conic projection centered on France
var projection = d3.geoOrthographic()
  .scale(600)
  .translate([width / 2, height / 2.7])
  .clipAngle(85)
  .precision(.1);

var path = d3.geoPath()
  .projection(projection)

var graticule = d3.geoGraticule()
  .step([5, 5]); //place graticule lines every 5 degrees of longitude and latitude

//create graticule background
var gratBackground = map.append("path")
  .datum(graticule.outline()) //bind graticule background
  .attr("class", "gratBackground") //assign class for styling
  .attr("d", path) //project graticule

//Example 2.6 line 5...create graticule lines
var gratLines = map.selectAll(".gratLines")
  .data(graticule.lines()) //bind graticule lines to each element to be created
  .enter() //create an element for each datum
  .append("path") //append each element to the svg as a path element
  .attr("class", "gratLines") //assign class for styling
  .attr("d", path); //project graticule lines

//use queue to parallelize asynchronous data loading
d3.queue()
  .defer(d3.json, "https://raw.githubusercontent.com/Sharitau/website/main/Portfolio/GovernanceFinal/data/world.topojson") //load background spatial data
  .defer(d3.json, "https://raw.githubusercontent.com/Sharitau/website/main/Portfolio/GovernanceFinal/data/Africa.TOPOJSON")
  .await(callback);

function callback(error, world, africa) {
  //translate europe TopoJSON
  var basemapCountries = topojson.feature(world, world.objects.ne_10m_admin_0_countries),
    choroplethCountries = topojson.feature(africa, africa.objects.ne_10m_admin_0_countries).features;

  //select graticule elements that will be created
  //add Europe countries to map
  var countries = map.append("path")
    .datum(basemapCountries)
    .attr("class", "countries")
    .attr("d", path);

  //add France regions to map
  var regions = map.selectAll(".regions")
    .data(choroplethCountries)
    .enter()
    .append("path")
    .attr("class", "regions")
    .attr("data-country", function(d) {
      return d.properties.ADMIN;
    })
    .attr("d", path);

  var countryNames = choroplethCountries.map(function(d) {
    return d.properties.ADMIN;
  }).sort();
  createDropdown(countryNames);
};

function createDropdown(countryNames) {
  //add select element
  var dropdown = d3.select("body")
    .append("select")
    .attr("class", "dropdown")
    .on("change", function() {
      // The value is the name of the country
      var countryName = this.value;
      highlight(countryName);
    });

  //add initial option
  var titleOption = dropdown.append("option")
    .attr("class", "titleOption")
    .attr("disabled", "true")
    .text("Select Country");

  //add attribute name options
  var attrOptions = dropdown.selectAll("attrOptions")
    .data(countryNames)
    .enter()
    .append("option")
    .attr("value", function(d) {
      return d;
    })
    .text(function(d) {
      return d;
    });
};

//function to highlight enumeration units and bars
function highlight(countryName) {
  //change stroke
  var selected = d3.selectAll("[data-country='" + countryName + "']")
    .style("stroke", "blue")
    .style("stroke-width", "2")
  console.log(countryName);
};
.countries {
  fill: #0B1E38;
  stroke: #fff;
  stroke-width: 2px;
}

.map {
  position: absolute;
  right: 0;
}

.regions {
  fill: #316331;
}

.gratLines {
  fill: none;
  stroke: #999;
  stroke-width: 1px;
}

.gratBackground {
  fill: #0B1E38;
}

.dropdown {
  position: absolute;
  top: 30px;
  left: 500px;
  z-index: 10;
  font-family: sans-serif;
  font-size: 1em;
  font-weight: bold;
  padding: 2px;
  border: 1px solid #999;
  box-shadow: 2px 2px 4px #999;
}

option {
  font-weight: normal;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-geo-projection.v2.min.js"></script>
<script src="https://d3js.org/topojson.v3.min.js"></script>

最后,您的代码中出现了一些不必要的复杂情况。

  • 不需要使用 c3 库;
  • 您提供了 select 元素 .attr('d', path),它没有任何作用。它仅适用于 SVG 生态系统中的 path 元素。

在我看来,它更像是一个 JS 相关问题,而不是 d3 相关问题:

这里有一个 d 变量似乎不知从哪里冒出来

function createDropdown(csvData) {
    //add select element
    var dropdown = d3.select("body")
      .append("select")
      .attr("class", "dropdown")
      .attr("d", path) // I don't think a select can use this
      .on("change", function() {
        highlight(d.properties); // where does this d come from a actually ? It is not provided by the function itself so what it is ?
      });
  // rest of code
}

这里:

    function highlight(props) {
        //change stroke
        var selected = d3.selectAll("." + props.ADMIN) // <= here
          .style("stroke", "blue")
          .style("stroke-width", "2")
        console.log(props.ADMIN);
      };

您 select 所有具有 class 且匹配 props.ADMIN 的元素,但您的代码实际上从未在任何元素上使用此 class。从 choroplethCountries 数据创建的路径使用“regions”+d.properties.ADMINclass,所以如果你想突出显示那些,你应该使用“.regions”+d.properties.ADMINselect或者.

您是否尝试过 运行 您的代码在调试模式下检查它的行为方式? 另外,鉴于您是 JS 初学者,我建议您考虑不使用 d3 的可能性。这是一个非常通用的库,这给了它很大的灵活性,但也意味着你必须学习很多东西才能有效地使用它。 如果您可以自由选择技术,也许值得花一些时间研究更专门针对您要寻求的可视化类型的替代解决方案。