D3.js的嵌套数据

时间:2015-08-12 23:37:26

标签: javascript d3.js svg nested

我正在尝试使用父数据和嵌套数据生成SVG元素,但是在出现问题时遇到了问题。这与Combining Parent and Nested Data with d3.js非常相似,这非常有用,但并未完全解决我的问题。

对于这个例子,我正在绘制由多个点组成的SVG路径。我想绘制每个路径的线段的切线。它主要是工作,但我不能为切线路径创建组,而是最终在SVG的根目录。我希望结构看起来像这样:

<svg width="100" height="100">

  <g id="0" class="linegroup">
    <g class="linepath">
      <path d="M0,0L10,10L20,20"></path>
    </g>
    <g class="tangentgroup">
      <path class="tan" d="M5,5L-5,15"></path>
      <path class="tan" d="M15,15L5,25"></path>
    </g>
  </g>

  <g id="1" class="linegroup">
    <g class="linepath">
      <path d="M30,30L40,40L50,50"></path>
    </g>
    <g class="tangentgroup">
      <path class="tan" d="M35,35L25,45"></path>
      <path class="tan" d="M45,45L35,55"></path>
    </g>
  </g>

</svg>

但相反,所有切线路径都被附加到svg,我无法创建“切线组”SVG组:

<svg width="100" height="100">

  <g id="0" class="linegroup">
    <g class="linepath">
      <path d="M0,0L10,10L20,20"></path>
    </g>
  </g>

  <g id="1" class="linegroup">
    <g class="linepath">
      <path d="M30,30L40,40L50,50"></path>
    </g>
  </g>

  <path class="tan" d="M5,5L-5,15"></path>
  <path class="tan" d="M15,15L5,25"></path>
  <path class="tan" d="M35,35L25,45"></path>
  <path class="tan" d="M45,45L35,55"></path>

</svg>

这是我目前的代码。我也愿意接受有关如何以其他方式改进它的建议。

// test data
var lines = [{
      id: 0,
      coordinates: [[0,0],[10,10],[20,20]]},
    {
      id: 1,
      coordinates: [[30,30],[40,40],[50,50]]
    }];

var diameter = 100;

var d3line = d3.svg.line();

var svg = d3.select("body").append("svg")
    .attr("width", diameter)
    .attr("height", diameter);

// create groups for each line and its tangents
var linegroups = svg.selectAll('.linegroup')
    .data(lines)
  .enter().append('g')
    .attr('class', 'linegroup')
    .attr('id', function(d) { return d.id; });

// add the line path
linegroups.append('g')
  .attr('class', 'linepath')
.append('path')
  .attr("d", function(d) { return d3line(d.coordinates); });

/////// Problem section ////////

// create a group for the line's segment's tangents,
//  and create a tangent path for each segment
linegroups.each(function(line, i) {
  d3.selectAll(this)
      // The "tangentgroup" groups never show up
  .append('g')
    .attr('class', 'tangentgroup')
  .data(lineSegments(line.coordinates))
      // 'tan' paths get appended to parent svg, not to 'linegroup'.
  .enter().append('path')
    .attr('class', 'tan')
    .attr('d', function (d) {
      return d3line(tangentFromMidpoint(d));
    });
});

////////////////////////////////

// returns lineSegments, comprised of pairs of points
function lineSegments (coordinates) {
  return d3.range(coordinates.length - 1).map(function(i) {
    return [coordinates[i], coordinates[i + 1]];
  });
}

// returns a tangent line starting from the mid-point of the original line
function tangentFromMidpoint (line) {
  var p1 = line[0];
  var p2 = line[1];
  var midPoint = [(p1[0] + p2[0]) / 2, (p1[1] + p2[1]) / 2];
  var tv = tangentVectors(p1,p2)[0];
  return [midPoint, [tv[0] + midPoint[0], tv[1] + midPoint[1]]];
}

// Returns both tangent vectors (not unit-vector) for a line
function tangentVectors (p1,p2) {
  // if we define dx=x2-x1 and dy=y2-y1,
  //  then the normals are (-dy, dx) and (dy, -dx)
  var dx = p2[0] - p1[0];
  var dy = p2[1] - p1[1];
  return [[-dy, dx, dy, -dx]];
}

1 个答案:

答案 0 :(得分:1)

问题在于,您首先在没有.data()的情况下致电.selectAll(),然后对.enter()选项进行操作。代码应该是

linegroups.each(function(line, i) {
  d3.select(this)
  .append('g')
    .attr('class', 'tangentgroup')
  .selectAll("path")
  .data(lineSegments(line.coordinates))
  .enter().append('path')
    .attr('class', 'tan')
    .attr('d', function (d) {
      return d3line(tangentFromMidpoint(d));
    });
});

完整演示here。你甚至不需要.each()

linegroups.append('g')
   .attr('class', 'tangentgroup')
   .selectAll("path")
   .data(function(line) { return lineSegments(line.coordinates); })
   .enter().append('path')
   .attr('class', 'tan')
   .attr('d', function (d) {
     return d3line(tangentFromMidpoint(d));
   });

完整演示here