【问题标题】:d3.js small multiples map with different filled country for every multipled3.js 小倍数地图,每个倍数都有不同的填充国家
【发布时间】:2017-11-19 19:48:28
【问题描述】:

我用 d3.js 创建了一个小的倍数地图。每个倍数都是一个不同的国家,在我的 csv 中被标记为出口商。我有 11 个出口国:加拿大、俄罗斯、美国、意大利、法国、英国、巴西、中国、印度、德国和日本。

我想填写每个国家/地区,但前提是它是代表倍数的国家/地区。因此,每个倍数只填充一个国家/地区。

你可以在这里查看我的块。倍数的国家(出口国)是线汇合的地方:https://bl.ocks.org/JulienAssouline/f899e2d6927aea6be65a109854b6f88a

现在我只为每个倍数填充了美国。我用这段代码填充了它:

svg.selectAll(".country")
            .data(countries)
            .enter()
            .append("path")
            .attr("class", "country")
            .attr("fill", function(d){
              if(d.properties.name == "United States"){
                return "red"
              } else {
                return "#eeedeb"
              }
            })
            .attr("d", path)
            .attr("opacity", function(d){
              if(d.properties.name == "United States"){
                return 0.4
              } else {
                return 1
              }
            })

这显然是错误的,但我不确定如何按倍数填写每个出口国。

这是我所有的代码:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>D3: Loading TopoJSON data and generating SVG paths</title>
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <script src="//d3js.org/topojson.v1.min.js"></script>
    <style type="text/css">
      /* No style rules here yet */
body,html{
    margin: 0;
    padding: 0;
    font-family: "Arial", sans-serif;
    font-size: 11px;
    text-align: center;
  }

#chart{
    background-color: white;
    stroke-width: 1;
  } 

  .country{
    stroke: white;
    stroke-width: 0.1;
  }  



    </style>
  </head>
  <body>
    <script type="text/javascript">
      //Width and height
      var w = 450;
      var h = 800;

      var margin = {
          top: 10,
          bottom: 0,
          left: 0,
          right: 30
        };

        var width = w - margin.left - margin.right;
        var height = h - margin.top - margin.bottom;


      var lineScale = d3.scaleSqrt()
        .domain([0, 332])
        .range([0.5, 3])

      var circleScale = d3.scaleSqrt()
        .domain([0, 4445])
        .range([0.5, 10])



      // define map projection
      var projection = d3.geoAzimuthalEquidistant()
        .translate([w/2, h/2 - 150])
        .scale(100)
      //   .scale([500]);

      //Define default path generator
      var path = d3.geoPath()
        .projection(projection)


      // var svg = d3.select("body")
      //   .append("svg")
      //   .attr("id", "chart")
      //   .attr("width", w)
      //   .attr("height", h)
      //   .append("g")
      //   .attr("transform", "translate(0" + margin.left + "," + margin.top + ")");

        d3.json("world.topojson", function(json){
          d3.csv("arms_transfer_2012_2016.csv", function(error, data){
            // data.forEach(function(d){
            //   d.Lon_Residence = +d.Lon_Residence
            //   d.Lat_Residence = +d.Lat_Residence
            // })
            var countries = topojson.feature(json, json.objects.countries).features

            var Exporters = d3.nest()
              .key(function(d){ return d.Exporter; })
              .entries(data)

              console.log(Exporters)

              var svg = d3.select("body")
                .selectAll("svg")
                .data(Exporters)
                .enter()
                .append("svg")
                .attr("id", "maps")
                .attr("width", w)
                .attr("height", h)
                .append("g")
                .attr("transform",  "translate(" + margin.left + "," + margin.top + ")")


            svg.selectAll(".country")
            .data(countries)
            .enter()
            .append("path")
            .attr("class", "country")
            .attr("fill", function(d){
              if(d.properties.name == "United States"){
                return "red"
              } else {
                return "#eeedeb"
              }
            })
            .attr("d", path)
            .attr("opacity", function(d){
              if(d.properties.name == "United States"){
                return 0.4
              } else {
                return 1
              }
            })



            svg.selectAll("circles")
              .data(function(d){ return d.values })
              .enter()
              .append("circle")
              .attr("class", "importer")
              .attr("r", function(d){
                return circleScale(d.Millions)
              })
              .attr("cx", function(d){
                var coords = projection([d.Longitude_imp, d.Latitude_imp])
                return coords[0];
              })
              .attr("cy", function(d){
                var coords = projection([d.Longitude_imp, d.Latitude_imp])
                return coords[1];
              })
              .style("fill", function(d){
                if(d.Millions > 20){
                  return "#cd0d0e"
                } else {
                  return "none"
                }
              })

            svg.selectAll(".arcs")
              .data(function(d){ return d.values})
              .enter()
              .append("path")
              .style("stroke", function(d){
                if(d.Millions > 20){
                  return "#cd0d0e"
                }
              })
              .attr("d", function(d){
                return lngLatToArc(d, 1)
              })
              .style("fill", "none")
              .style("opacity", 0.5)
              .style("stroke-width", "0.5px")

              svg.append("text")
                .attr("x", 100)
                .attr("y", 100)
                .text(function(d){
                  return d.Exporter
                })



          function lngLatToArc(d, bend){
                // If no bend is supplied, then do the plain square root
                bend = bend || 1;
                // `[d.Lon_Origin, d.Lat_Origin]` and `[d.Lon_Residence, d.Lat_Residence]` are arrays of `[lng, lat]`
                // Note, people often put these in lat then lng, but mathematically we want x then y which is `lng,lat`


                var sourceLngLat = [d.Longitude_exp, d.Latitude_exp],
                    targetLngLat = [d.Longitude_imp, d.Latitude_imp];



                if(targetLngLat && sourceLngLat){

                  var sourceXY = projection(sourceLngLat),
                      targetXY = projection(targetLngLat);

                  // Uncomment this for testing, useful to see if you have any null lng/lat values
                // if (!targetXY) console.log(d, targetLngLat, targetXY)
                var sourceX = sourceXY[0],
                    sourceY = sourceXY[1];

                  var targetX = targetXY[0],
                      targetY = targetXY[1];

                  var dx = targetX - sourceX,
                      dy = targetY - sourceY
                    dr = Math.sqrt(dx * dx + dy * dy) * bend;

                    // To avoid a whirlpool effect, make the bend direction consistent regardless of whether the source is east or west of the target
                var west_of_source = (targetX - sourceX) < 0;
                if (west_of_source) return "M" + targetX + "," + targetY + "A" + dr + "," + dr + " 0 0,1 " + sourceX + "," + sourceY;
                return "M" + sourceX + "," + sourceY + "A" + dr + "," + dr + " 0 0,1 " + targetX + "," + targetY;

                } else{
                  return "M0,0,l0,0z";
                }
              }

          })
        })


    </script>
  </body>
</html>

我的数据以及我使用的 Topojson 文件可以在我的公共要点中找到:https://gist.github.com/JulienAssouline/f899e2d6927aea6be65a109854b6f88a

【问题讨论】:

    标签: javascript d3.js maps


    【解决方案1】:

    在输入子元素时访问父元素的数据可能是一项挑战。但是,有几种方法可以实现这一点。

    一种方法是设置父元素的属性:

    .property("exporter",function(d) { return d.key; });
    

    这将为您附加的每个 svg 设置一个属性 - 由于基准键属性是国家名称,我们可以在附加数据时使用此属性为国家/地区着色:

    .attr("fill", function(d) {
        if(d.properties.name == d3.select(this.parentNode).property("exporter")){
    ...
    

    或者,我们可以使用 d3.local()。顺便说一句,API documentation 建议小倍数:

    D3 本地允许您定义独立于数据的本地状态。为了 例如,当渲染时间序列数据的小倍数时,您 可能需要所有图表相同的 x 比例,但不同的 y 比例 比较每个指标的相对表现。

    不过,它也声明:“如果您只是设置单个变量,请考虑使用 selection.property”,也就是上面介绍的方法。

    要遵循第二种方法,您需要声明一个局部变量:

    var local = d3.local();
    

    并在附加 svg 时这样设置:

    .each(function(d) { local.set(this, d.key); })
    

    然后在着色的时候得到:

    .attr("fill",function(d) {     
        if(d.properties.name == local.get(this)) {
          return "red";
        ...
    

    Here's 使用第二种方法(在 cmets 中使用第一种方法)的更新块。

    【讨论】:

    • 良好而完整的答案,+1。但我在“代码高尔夫”上击败了你。
    • 是的,你确实做到了,我考虑过将它包括在内,但我认为使用属性/本地方法会更有趣。
    • 如果我没记错的话,它们(属性/本地)要快得多......因此我的 +1。
    • 嗯,有趣,我想我得去做一些测试了。
    • 选择和遍历 DOM 通常是很慢的操作。
    【解决方案2】:

    最简单的选择就是获取父元素的数据:

    if(d.properties.name == d3.select(this.parentNode).datum().key)
    

    这里是更新的 bl.ocks:https://bl.ocks.org/anonymous/dbda1acf2f76012ab557d7a24b1b7ecb/34b1273419327ce258309f9bf4522ea2aa186d92

    【讨论】:

      猜你喜欢
      • 2019-03-30
      • 1970-01-01
      • 2015-04-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多