如何使用d3.js计算同心圆弧/闭合路径内的等距点

时间:2016-01-03 14:26:35

标签: javascript d3.js data-visualization

我正在研究雷达图表,我需要在每个弧内显示一些圆圈。我可以在任何给定的时间点(内半径,外半径,起始角和终止角)获得以下信息。 有了这些信息,我需要在这个封闭的弧形路径中添加一些圆圈。请让我知道如何实现这一目标。

以下是我想要实现的示例image

    var width = 600;
    var height= 600;
    var radius = 300;
    var color = d3.scale.category20b();

    var svg = d3.select("#radarchart")
          .append("svg")
          .attr("height",height)
          .attr("width",width);
    var radargroup = svg.append("g").attr('transform','translate('+ (width/2)+','+(height/2)+')');

    var p = Math.PI *2;
    var innercircles=3;
    var innerradius = [200,100,0];  
    var outerradius =[radius, radius - 100,radius - 200];
    var startangle = [0 ,25,50,75];
    var endangle = [25,50,75,100]
    var fillcolor = ['#E4E5E4','#D7D8D6','#BFC0BF'];
    var counter = 0;
    svg.append("rect")
          .attr("x",width/2 - 7)
          .attr("y","0")
          .attr("width","15px")
          .attr("height",height)
          .style("fill","#fff")
          .style("opacity",0.5);

    var labelcontainer = svg.append("g").attr('transform',"translate(0,"+(height/2 - 10 )+')');
    labelcontainer.append("rect")
          .attr("x","0")
          .attr("y","0")
          .attr("width",width)
          .attr("height","15px")
          .style("fill","#fff")
          .style("opacity",0.5);

    var data = [  
       {  
            "name":"A",
            "status":[
              {
                "statusname":"section1",
                "statusvalue":[
                  {
                    "radar_id":"1",
                    "radar_value":"Consumer-driven contract testing"
                  },
                  {
                    "radar_id":"2",
                    "radar_value":"NoPSD"
                  }
                ]
              },
            
              {
                "statusname":"section2",
                "statusvalue":[
                  {
                    "radar_id":"4",
                    "radar_value":"BEM"
                  },
                  {
                    "radar_id":"5",
                    "radar_value":"NPM for all the things"
                  }
                ]
              },
              {
                "statusname":"section3",
                "statusvalue": null
              }
             ]  
             
       },
       {
          "name":"B",  
          "status":[
              {
                "statusname":"section1",
                "statusvalue":null
              },
              {
                "statusname":"section2",
                "statusvalue":null
              },
              {
                "statusname":"section3",
                "statusvalue":[
                  {
                    "radar_id":"6",
                    "radar_value":"Composer"
                  },
                  {
                    "radar_id":"7",
                    "radar_value":" Postman"
                  }
                ]
              }
             ]
       },
       {  
            "name":"C", 
            "status":[
              {
                "statusname":"section1",
                "statusvalue":null
              },
              {
                "statusname":"section2",
                "statusvalue":[
                  {
                    "radar_id":"10",
                    "radar_value":"H2O"
                  }
                ]
              },
              {
                "statusname":"section3",
                "statusvalue":null
              }
             ]
       },
       {  
            "name":"D",
            "status":[
              {
                "statusname":"section1",
                "statusvalue":null
              },
              {
                "statusname":"section2",
                "statusvalue":null
              },
              {
                "statusname":"section3",
                "statusvalue":[
                  {
                    "radar_id":"12",
                    "radar_value":"ECMAScript 6"
                  },
                  {
                    "radar_id":"13",
                    "radar_value":"React"
                  }
                ]
              }
             ]
       }
    ];

    var myScale = d3.scale.linear().domain([0, 100]).range([0, 2 * Math.PI]);   
      data.map(function(quadrant,index){
           
          var svgquadrant = radargroup.append("g").attr("id","quadrant"+index);
          //creating arcs in each quadrant 

          quadrant.status.map(function(eachstate,i){ console.log(eachstate);
            counter = counter + 1; 
            var arcname = "arc" + counter;
            arcname = d3.svg.arc()
            .innerRadius(innerradius[i])
            .outerRadius(outerradius[i])
            .startAngle(myScale(startangle[index]))
            .endAngle(myScale(endangle[index]));

            svgquadrant.append("path").attr('id','p'+index+i).attr("d", arcname).attr('fill',fillcolor[i]);

            if(eachstate.statusvalue != null){
              var strlen = 20;
              // logic goes here
            }
            
          });

      })
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<div id="radarchart">

</div>

1 个答案:

答案 0 :(得分:0)

你真正要问的是半径和角度,我如何计算圆(弧)上的x,y位置。这只是simple trigonometry

使用你的变量,这个数学成为:

var r = (innerradius[i] +outerradius[i]) / 2,
    a = (myScale(startangle[index]) + myScale(endangle[index])) / 2 - (Math.PI /2),
    coors = [Math.cos(a) * r, Math.sin(a) * r];

如果您只有一个圈子d3提供了一个带有弧的辅助函数来计算centroid(基本上是上面的数学运算)。这恰好是放置圈子的一个非常方便的地方:

if (eachstate.statusvalue != null) {
  var strlen = 20;

  var coors = arcname.centroid();

  svgquadrant.append('circle')
    .style('fill','red')
    .attr('r', 20)
    .attr('cx', coors[0])
    .attr('cy', coors[1]);
}

如果你有一个以上的圆圈,你必须“抖动”这个角度,使它们彼此分开。类似的东西:

for (var j = 0; j < eachstate.statusvalue.length; j++){
  var r = (innerradius[i] +outerradius[i]) / 2,
      a = (myScale(startangle[index]) + myScale(endangle[index])) / 2 - (Math.PI /2);

      a += (0.436332 * j); // 25 degrees apart

      coors = [Math.cos(a) * r, Math.sin(a) * r];
}

完整代码:

<!DOCTYPE html>
<html>

<head>
  <script data-require="d3@3.5.3" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
</head>

<body>
  <div id="radarchart">

  </div>

  <script>
    var width = 600;
    var height = 600;
    var radius = 300;
    var color = d3.scale.category20b();

    var svg = d3.select("#radarchart")
      .append("svg")
      .attr("height", height)
      .attr("width", width);
    var radargroup = svg.append("g").attr('transform', 'translate(' + (width / 2) + ',' + (height / 2) + ')');

    var p = Math.PI * 2;
    var innercircles = 3;
    var innerradius = [200, 100, 0];
    var outerradius = [radius, radius - 100, radius - 200];
    var startangle = [0, 25, 50, 75];
    var endangle = [25, 50, 75, 100]
    var fillcolor = ['#E4E5E4', '#D7D8D6', '#BFC0BF'];
    var counter = 0;
    svg.append("rect")
      .attr("x", width / 2 - 7)
      .attr("y", "0")
      .attr("width", "15px")
      .attr("height", height)
      .style("fill", "#fff")
      .style("opacity", 0.5);

    var labelcontainer = svg.append("g").attr('transform', "translate(0," + (height / 2 - 10) + ')');
    labelcontainer.append("rect")
      .attr("x", "0")
      .attr("y", "0")
      .attr("width", width)
      .attr("height", "15px")
      .style("fill", "#fff")
      .style("opacity", 0.5);

    var data = [{
      "name": "A",
      "status": [{
          "statusname": "section1",
          "statusvalue": [{
            "radar_id": "1",
            "radar_value": "Consumer-driven contract testing"
          }, {
            "radar_id": "2",
            "radar_value": "NoPSD"
          }]
        },

        {
          "statusname": "section2",
          "statusvalue": [{
            "radar_id": "4",
            "radar_value": "BEM"
          }, {
            "radar_id": "5",
            "radar_value": "NPM for all the things"
          }]
        }, {
          "statusname": "section3",
          "statusvalue": null
        }
      ]

    }, {
      "name": "B",
      "status": [{
        "statusname": "section1",
        "statusvalue": null
      }, {
        "statusname": "section2",
        "statusvalue": null
      }, {
        "statusname": "section3",
        "statusvalue": [{
          "radar_id": "6",
          "radar_value": "Composer"
        }, {
          "radar_id": "7",
          "radar_value": " Postman"
        }]
      }]
    }, {
      "name": "C",
      "status": [{
        "statusname": "section1",
        "statusvalue": null
      }, {
        "statusname": "section2",
        "statusvalue": [{
          "radar_id": "10",
          "radar_value": "H2O"
        }]
      }, {
        "statusname": "section3",
        "statusvalue": null
      }]
    }, {
      "name": "D",
      "status": [{
        "statusname": "section1",
        "statusvalue": null
      }, {
        "statusname": "section2",
        "statusvalue": null
      }, {
        "statusname": "section3",
        "statusvalue": [{
          "radar_id": "12",
          "radar_value": "ECMAScript 6"
        }, {
          "radar_id": "13",
          "radar_value": "React"
        }]
      }]
    }];

    var myScale = d3.scale.linear().domain([0, 100]).range([0, 2 * Math.PI]);
    data.map(function(quadrant, index) {

      var svgquadrant = radargroup.append("g").attr("id", "quadrant" + index);
      //creating arcs in each quadrant 

      quadrant.status.map(function(eachstate, i) {
        //console.log(eachstate);
        counter = counter + 1;
        var arcname = "arc" + counter;
        arcname = d3.svg.arc()
          .innerRadius(innerradius[i])
          .outerRadius(outerradius[i])
          .startAngle(myScale(startangle[index]))
          .endAngle(myScale(endangle[index]));

        svgquadrant.append("path").attr('id', 'p' + index + i).attr("d", arcname).attr('fill', fillcolor[i]);

        if (eachstate.statusvalue != null) {
          var strlen = 20;
          
          var N = eachstate.statusvalue.length;
          
          for (var j = 0; j < N; j++){
            
             var r = (innerradius[i] +outerradius[i]) / 2,
                 a = (myScale(startangle[index]) + myScale(endangle[index])) / 2 - (Math.PI /2);
              
              a += (0.436332 * j);
              
              var coors = [Math.cos(a) * r, Math.sin(a) * r];
                
            svgquadrant.append('circle')
              .style('fill','red')
              .attr('r', 10)
              .attr('cx', coors[0])
              .attr('cy', coors[1]);
            
          }
        }

      });

    })
  </script>
</body>

</html>