首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何将绑定到D3中的不同数据的元素分组(与强制模拟一起使用),以便对它们进行排序

如何将绑定到D3中的不同数据的元素分组(与强制模拟一起使用),以便对它们进行排序
EN

Stack Overflow用户
提问于 2020-07-04 09:15:41
回答 2查看 52关注 0票数 1

我试图创建一个散点图,使用强制模拟将标签放置在我的数据点周围。到目前为止,这还不错(感谢对堆栈溢出和一些博客的良好帮助:)。这是到目前为止的样子. 散点图

然而,我现在不得不重新排序我的元素,以便每个数据点的圆、线和文本元素在z轴上相邻。

从我目前的情况来看:

代码语言:javascript
复制
<g class="circles">
   <circle dp-1></circle>
   <circle dp-2></circle>
   ...
</g>
<g class="labels">
   <text dp-1></text>
   <text dp-2></text>
   ...
</g>
<g class="links">
   <line dp-1></line>
   <line dp-2></line>
   ...
</g>

我想去..。

代码语言:javascript
复制
<g id="dp-1">
   <circle dp-1></circle>
   <text dp-1></text>
   <line dp-1></line>
</g>
<g id="dp-2">
   <circle dp-2></circle>
   <text dp-2></text>
   <line dp-2></line>
</g>
<g>
...

我知道如何在“静态”的情况下做到这一点,而不用强迫模拟。然而,在我的例子中,我没有办法做到这一点,我在标签(节点)和线(链接)上运行了一个力模拟,而不是在圆圈上。

如何在D3中正确地实现这一点?下面是我代码中最重要的片段。我遇到的主要问题是,对于我的圆圈(数据)和节点(forceData),我使用不同的数据数组。后者基本上是一个数组,长度是数据的两倍(每个数据点有两个节点)。

我也不知道该怎么做

  • 让D3绘制一个绑定到两个不同数据的"g“,或者
  • 进行基于数据数组的力模拟(该数据数组仅为节点数组长度的一半)。

当然,解决我的问题的其他想法也是受欢迎的。谢谢你的想法和帮助。

代码语言:javascript
复制
/**
* Updates the chart. To be used when the data stayed the same, but is sliced differently (filter, ...)
*/
public update() {
this.svg.select('.dataPoints')
  .selectAll("circle")
  .data(this.data,
    function (d: any) { return d.category }
  )
  .join(
    function (enter) {
      // what is to be done with new items ...
      return enter
        .append("circle")
        .style("opacity", 0)
    },
    // function (update) { return update },
  )
  .attr("cx", d => this.xScale()(d.x))
  .attr("cy", d => this.yScale()(d.y))
  .style("fill", d => this.color(d.color))
  .style("stroke-width", this.settings.dataPoints.stroke.width)
  .style("stroke-opacity", this.settings.dataPoints.stroke.opacity)
  .style("stroke", this.settings.dataPoints.stroke.color)
  .transition()
  .duration(this.settings.dataPoints.duration)
  .style('opacity', 1)
  .attr("r", d => this.rScale()(d.r))

if (this.settings.labels.show) {
  this.svg.select(".labels")
    .call(this.labelPlacement)
}

private labelPlacement = (g) => {
// we need to create our node and link array. We need two nodes per datapoint. One for the point
// itself which has a fixed x and y (fx/fy) and one for the label, which will be floating ...
var forceData = {
  'nodes': [],
  'links': [],
};

var myXscale = this.xScale()
var myYscale = this.yScale()

this.data.forEach(function (d, i) {
  // doing the two nodes per datapoint ...
  forceData.nodes.push({
    id: d.category,
    label: d.label,
    fx: myXscale(d.x),
    fy: myYscale(d.y)
  });
  forceData.nodes.push({
    id: d.category,
    label: d.label,
    x: myXscale(d.x),
    y: myYscale(d.y),
    dataX: myXscale(d.x),
    dataY: myYscale(d.y)
  });
  // and also adding a link between the datapoint and its label ...
  forceData.links.push({
    source: i * 2,
    target: i * 2 + 1,
  });
});

// now drawing them labels and links ...
if (this.settings.labels.showLinks) {
  var labelLink = this.svg.select('.label-links')
    .selectAll("line")
    .data(forceData.links, (d: any) => { return (d.source + "-" + d.target) })
    .join("line")
    .attr("stroke", this.settings.labels.linkStroke.color)
    .attr("stroke-width", this.settings.labels.linkStroke.width)
    .attr("opacity", this.settings.labels.linkStroke.opacity)
}

var labelNode = this.svg.select('.labels')
  .selectAll("text")
  .data(forceData.nodes, (d: any) => { return d.id })
  .join("text")
  .text((d, i) => { return i % 2 == 0 ? "" : TextService.textLimit(d.label, this.settings.labels.maxTextLength) })
  .style("fill", this.settings.labels.label.fill)
  .style("font-family", this.settings.labels.label.fontFamily)
  .style("font-size", this.settings.labels.label.fontSize)
  .call(d3.drag()
    .on("drag", dragged)
    .on("end", dragended)
  )

// adding and doing the force simulation ...
if (this.settings.labels.force) {
  d3.forceSimulation(forceData.nodes)
    .alphaTarget(this.settings.labels.alphaTarget)
    .alphaDecay(this.settings.labels.alphaDecay)
    .force("charge", d3.forceManyBody().strength(this.settings.labels.chargeStrength))
    .force("link", d3.forceLink(forceData.links)
      .distance(this.settings.labels.linkDistance)
      .strength(this.settings.labels.linkStrength))
    .on("tick", ticked);
}
EN

回答 2

Stack Overflow用户

发布于 2020-07-05 09:39:53

谢谢大家的阅读。我又过了一个不眠之夜,终于解决了这个问题:)

我跳过了"g“元素,因为它们并不是真正必要的,而是试图获得这样的结构:

代码语言:javascript
复制
<line dp-1></line>
<circle dp-1></circle>
<line dp-2></line>
<circle dp-2></line>
...

为此,我可以使用两个单独的数据绑定和数据数组(这是使用力模拟所必需的)。实现校正排序(行-圆-线-圆-.)我使用了"d3.insert“,而不是用动态计算的”前面“元素来追加。下面是代码中最重要的部分。希望这最终能对某人有所帮助。

问候

代码语言:javascript
复制
// Drawing the data ...
public update() {
this.dataPoints
  .selectAll("circle")
  .data(this.data,
    function (d: any) { return d.category }
  )
  .join(
    enter => enter.append("circle")
      .style('opacity', 0)
  )
  .call(this.drawData)

if (this.settings.labels.showLinks && this.settings.labels.showLinks) {
  this.dataPoints
    .selectAll("line")
    .data(this.forceData.links)
    .join(
      enter => enter.insert('line', (d, i) => {
        console.log("JOIN", d)
        return document.getElementById('dP_' + d.id)
      })
        // .style('opacity', 0)
    )
    .call(this.drawLabelLine)
}
}


/**
   * Draws the data circles ...
   * @param circle 
*/
  private drawData = (circle) => {
circle
  .attr("id", (d, i) => { return 'dP_' + d.category })
  .attr("class", "dataPoint")
  .style("fill", d => this.color(d.color))
  .style("stroke-width", this.settings.dataPoints.stroke.width)
  .style("stroke-opacity", this.settings.dataPoints.stroke.opacity)
  .style("stroke", this.settings.dataPoints.stroke.color)
  .transition()
  .duration(this.settings.dataPoints.duration)
  .style('opacity', 1)
  .attr("r", d => this.rScale()(d.r))
  .attr("cx", d => this.xScale()(d.x))
  .attr("cy", d => this.yScale()(d.y))
  }

/**
   * draws the lines to connect labels to data points
   * @param g 
   */
private drawLabelLine = (line) => {
line
  .attr("class", "label-link")
  .attr("stroke", this.settings.labels.linkStroke.color)
  .attr("stroke-width", this.settings.labels.linkStroke.width)
  .attr("opacity", this.settings.labels.linkStroke.opacity)
}

// adding and doing the force simulation ...
if (this.settings.labels.force) {
  d3.forceSimulation(forceData.nodes)
    .alphaTarget(this.settings.labels.alphaTarget)
    .alphaDecay(this.settings.labels.alphaDecay)
    .force("charge", d3.forceManyBody().strength(this.settings.labels.chargeStrength))
    .force("link", d3.forceLink(forceData.links)
      .distance(this.settings.labels.linkDistance)
      .strength(this.settings.labels.linkStrength))
    .on("tick", ticked);
}
票数 0
EN

Stack Overflow用户

发布于 2020-07-05 11:14:19

由于您没有包含您的数据,所以我可以在数据方面给出一种高层次的解决方法:

本质上,将这3个数组合并到一个对象中,按照“color”属性(可以是任何属性)进行归约。然后将每个圆圈、线条和文本附加到我们为每种颜色创建的每一个'g‘元素中。

注意:links数组没有任何意义,因为我们可以从circleslabels数组中获得它们,因此有x1x2y1y2值。此外,如果可能的话,您可以从一开始就像我的combinedData一样定义数据。

代码语言:javascript
复制
const circles = [
  {shape: "circle", color: "green", x: 2, y: 2, r: 0.5},
  {shape: "circle", color: "blue", x: 4, y: 4, r: 1},
  {shape: "circle", color: "red", x: 8, y: 8, r: 1.5},
];
const links = [
  {shape: "line", color: "green", x1: 2, y1: 2, x2: 1, y2: 1},
  {shape: "line", color: "blue", x1: 4, y1: 4, x2: 2, y2: 6},
  {shape: "line", color: "red", x1: 8, y1: 8, x2: 9, y2: 4},
];
const labels = [
  {shape: "text", color: "green", x: 1, y: 1, text: "A"},
  {shape: "text", color: "blue", x: 2, y: 6, text: "B"},
  {shape: "text", color: "red", x: 9, y: 4, text: "C"},
];

const combinedData = [...circles, ...links, ...labels].reduce((aggObj, item) => {
  
  if (!aggObj[item.color]) aggObj[item.color] = {};
  
  aggObj[item.color][item.shape] = item;
  
  return aggObj;
}, {});

//console.log(combinedData);

const groups = d3.select('svg').selectAll('g')
  .data(Object.entries(combinedData))
  .enter()
  .append('g')
  .attr('class', ([k,v]) => k);
  
groups  
    .append('circle')
    .attr('fill', ([k,v]) => v.circle.color)
    .attr('r', ([k,v]) => v.circle.r)
    .attr('cx', ([k,v]) => v.circle.x)
    .attr('cy', ([k,v]) => v.circle.y)
    
groups  
    .append('line')
    .attr('stroke', ([k,v]) => v.line.color)
    .attr('stroke-width', 0.1)
    .attr('x1', ([k,v]) => v.line.x1)
    .attr('y1', ([k,v]) => v.line.y1) 
    .attr('x2', ([k,v]) => v.line.x2)
    .attr('y2', ([k,v]) => v.line.y2)     
    
groups  
    .append('rect')
    .attr('fill', "#cfcfcf")
    .attr('x', ([k,v]) => v.text.x - 0.6)
    .attr('y', ([k,v]) => v.text.y - 0.6)
    .attr('width', 1.1)
    .attr('height', 1.1)
    
groups  
    .append('text')
    .attr('alignment-baseline', "middle")
    .attr('text-anchor', "middle")
    .attr('fill', ([k,v]) => v.text.color)
    .attr('font-size', 1)
    .attr('x', ([k,v]) => v.text.x)
    .attr('y', ([k,v]) => v.text.y)
    .text(([k,v]) => v.text.text)
代码语言:javascript
复制
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg width="100%" viewbox="0 0 12 12">

</svg>

分组要素:

扩大:

代码语言:javascript
复制
<svg width="100%" viewBox="0 0 12 12">
  <g class="green">
    <circle fill="green" r="0.5" cx="2" cy="2"></circle>
    <line stroke="green" stroke-width="0.1" x1="2" y1="2" x2="1" y2="1"></line>
    <rect fill="#cfcfcf" x="0.4" y="0.4" width="1.1" height="1.1"></rect>
    <text alignment-baseline="middle" text-anchor="middle" fill="green" font-size="1" x="1" y="1">A</text>
  </g>
  <g class="blue">
    <circle fill="blue" r="1" cx="4" cy="4"></circle>
    <line stroke="blue" stroke-width="0.1" x1="4" y1="4" x2="2" y2="6"></line>
    <rect fill="#cfcfcf" x="1.4" y="5.4" width="1.1" height="1.1"></rect>
    <text alignment-baseline="middle" text-anchor="middle" fill="blue" font-size="1" x="2" y="6">B</text>
  </g>
  <g class="red">
    <circle fill="red" r="1.5" cx="8" cy="8"></circle>
    <line stroke="red" stroke-width="0.1" x1="8" y1="8" x2="9" y2="4"></line>
    <rect fill="#cfcfcf" x="8.4" y="3.4" width="1.1" height="1.1"></rect>
    <text alignment-baseline="middle" text-anchor="middle" fill="red" font-size="1" x="9" y="4">C</text>
  </g>
</svg>

产出(粗略例子):

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/62727373

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档