【问题标题】:Link source doesn't match the node and duplicates in nodes don't duplicate links链接源与节点不匹配,节点中的重复项不重复链接
【发布时间】:2018-12-22 15:03:48
【问题描述】:

我需要帮助修复 D3 力模拟节点和链接,我有 2 个问题,请你帮我找到正确的方向吗?

问题1:节点数组中的重复不会复制流量,请看下图,有2个“哺乳动物”节点和4个哺乳动物连接的哺乳动物,我该如何实现?

http://jsfiddle.net/3hL48pxg/3/

var nodes = [
  { id: "mammal", group: 0, label: "Mammals", level: 1 },
  { id: "mammal", group: 0, label: "Mammals", level: 1 },
  { id: "dog"   , group: 0, label: "Dogs"   , level: 2 },
  { id: "cat"   , group: 0, label: "Cats"   , level: 2 },
  { id: "fox"   , group: 0, label: "Foxes"  , level: 2 },
  { id: "elk"   , group: 0, label: "Elk"    , level: 2 },
  { id: "insect", group: 1, label: "Insects", level: 1 },
  { id: "ant"   , group: 1, label: "Ants"   , level: 2 },
  { id: "bee"   , group: 1, label: "Bees"   , level: 2 },
  { id: "fish"  , group: 2, label: "Fish"   , level: 1 },
  { id: "carp"  , group: 2, label: "Carp"   , level: 2 },
  { id: "pike"  , group: 2, label: "Pikes"  , level: 2 }
]
var links = [
	{ target: "mammal", source: "dog" , strength: 0.7 },
	{ target: "mammal", source: "cat" , strength: 0.7 },
  { target: "mammal", source: "fox" , strength: 0.7 },
  { target: "mammal", source: "elk" , strength: 0.7 },
  { target: "insect", source: "ant" , strength: 0.7 },
  { target: "insect", source: "bee" , strength: 0.7 },
  { target: "fish"  , source: "carp", strength: 0.7 },
  { target: "fish"  , source: "pike", strength: 0.7 },
  { target: "cat"   , source: "elk" , strength: 0.1 },
  { target: "carp"  , source: "ant" , strength: 0.1 },
  { target: "elk"   , source: "bee" , strength: 0.1 },
  { target: "dog"   , source: "cat" , strength: 0.1 },
  { target: "fox"   , source: "ant" , strength: 0.1 },
	{ target: "pike"  , source: "cat" , strength: 0.1 }
]
function getNodeColor(node) {
  return node.level === 1 ? 'red' : 'gray'
}
var width = window.innerWidth
var height = window.innerHeight
var svg = d3.select('svg')
svg.attr('width', width).attr('height', height)
// simulation setup with all forces
var linkForce = d3
  .forceLink()
  .id(function (link) { return link.id })
  .strength(function (link) { return link.strength })
var simulation = d3
  .forceSimulation()
  .force('link', linkForce)
  .force('charge', d3.forceManyBody().strength(-120))
  .force('center', d3.forceCenter(width / 2, height / 2))
var linkElements = svg.append("g")
  .attr("class", "links")
  .selectAll("line")
  .data(links)
  .enter().append("line")
    .attr("stroke-width", 1)
	  .attr("stroke", "rgba(50, 50, 50, 0.2)")
var nodeElements = svg.append("g")
  .attr("class", "nodes")
  .selectAll("circle")
  .data(nodes)
  .enter().append("circle")
    .attr("r", 10)
    .attr("fill", getNodeColor)
var textElements = svg.append("g")
  .attr("class", "texts")
  .selectAll("text")
  .data(nodes)
  .enter().append("text")
    .text(function (node) { return  node.label })
	  .attr("font-size", 15)
	  .attr("dx", 15)
    .attr("dy", 4)
simulation.nodes(nodes).on('tick', () => {
  nodeElements
    .attr('cx', function (node) { return node.x })
    .attr('cy', function (node) { return node.y })
  textElements
    .attr('x', function (node) { return node.x })
    .attr('y', function (node) { return node.y })
  linkElements
    .attr('x1', function (link) { return link.source.x })
    .attr('y1', function (link) { return link.source.y })
    .attr('x2', function (link) { return link.target.x })
    .attr('y2', function (link) { return link.target.y })
})
simulation.force("link").links(links)
<!DOCTYPE html>
<meta charset="utf-8">

<svg width="960" height="600"></svg>

<script src="https://d3js.org/d3.v4.min.js"></script>

问题2:当链接的来源与节点不匹配时,会抛出错误,如何避免错误?

http://jsfiddle.net/3hL48pxg/5/

var nodes = [
  { id: "mammals", group: 0, label: "Mammals", level: 1 },
  { id: "mammals", group: 0, label: "Mammals", level: 1 },
  { id: "dog"   , group: 0, label: "Dogs"   , level: 2 },
  { id: "cat"   , group: 0, label: "Cats"   , level: 2 },
  { id: "fox"   , group: 0, label: "Foxes"  , level: 2 },
  { id: "elk"   , group: 0, label: "Elk"    , level: 2 },
  { id: "insect", group: 1, label: "Insects", level: 1 },
  { id: "ant"   , group: 1, label: "Ants"   , level: 2 },
  { id: "bee"   , group: 1, label: "Bees"   , level: 2 },
  { id: "fish"  , group: 2, label: "Fish"   , level: 1 },
  { id: "carp"  , group: 2, label: "Carp"   , level: 2 },
  { id: "pike"  , group: 2, label: "Pikes"  , level: 2 }
]
var links = [
	{ target: "mammals1111", source: "dog" , strength: 0.7 },
	{ target: "mammals", source: "cat" , strength: 0.7 },
  { target: "mammals", source: "fox" , strength: 0.7 },
  { target: "mammals", source: "elk" , strength: 0.7 },
  { target: "insect", source: "ant" , strength: 0.7 },
  { target: "insect", source: "bee" , strength: 0.7 },
  { target: "fish"  , source: "carp", strength: 0.7 },
  { target: "fish"  , source: "pike", strength: 0.7 },
  { target: "cat"   , source: "elk" , strength: 0.1 },
  { target: "carp"  , source: "ant" , strength: 0.1 },
  { target: "elk"   , source: "bee" , strength: 0.1 },
  { target: "dog"   , source: "cat" , strength: 0.1 },
  { target: "fox"   , source: "ant" , strength: 0.1 },
	{ target: "pike"  , source: "cat" , strength: 0.1 }
]
function getNodeColor(node) {
  return node.level === 1 ? 'red' : 'gray'
}
var width = window.innerWidth
var height = window.innerHeight
var svg = d3.select('svg')
svg.attr('width', width).attr('height', height)
// simulation setup with all forces
var linkForce = d3
  .forceLink()
  .id(function (link) { return link.id })
  .strength(function (link) { return link.strength })
var simulation = d3
  .forceSimulation()
  .force('link', linkForce)
  .force('charge', d3.forceManyBody().strength(-120))
  .force('center', d3.forceCenter(width / 2, height / 2))
var linkElements = svg.append("g")
  .attr("class", "links")
  .selectAll("line")
  .data(links)
  .enter().append("line")
    .attr("stroke-width", 1)
	  .attr("stroke", "rgba(50, 50, 50, 0.2)")
var nodeElements = svg.append("g")
  .attr("class", "nodes")
  .selectAll("circle")
  .data(nodes)
  .enter().append("circle")
    .attr("r", 10)
    .attr("fill", getNodeColor)
var textElements = svg.append("g")
  .attr("class", "texts")
  .selectAll("text")
  .data(nodes)
  .enter().append("text")
    .text(function (node) { return  node.label })
	  .attr("font-size", 15)
	  .attr("dx", 15)
    .attr("dy", 4)
simulation.nodes(nodes).on('tick', () => {
  nodeElements
    .attr('cx', function (node) { return node.x })
    .attr('cy', function (node) { return node.y })
  textElements
    .attr('x', function (node) { return node.x })
    .attr('y', function (node) { return node.y })
  linkElements
    .attr('x1', function (link) { return link.source.x })
    .attr('y1', function (link) { return link.source.y })
    .attr('x2', function (link) { return link.target.x })
    .attr('y2', function (link) { return link.target.y })
})
simulation.force("link").links(links)
<!DOCTYPE html>
<meta charset="utf-8">

<svg width="960" height="600"></svg>

<script src="https://d3js.org/d3.v4.min.js"></script>

提前致谢!

【问题讨论】:

    标签: javascript d3.js force-layout d3-force-directed


    【解决方案1】:

    source code 中的这几行可以解释您的两个问题:

    function find(nodeById, nodeId) {
        var node = nodeById.get(nodeId);
        if (!node) throw new Error("missing: " + nodeId);
        return node;
    }
    

    问题 1

    关于问题 1,很容易看出 nodeById 是一张地图,因为我们有 nodeById.get。这是一张 D3 地图,不是普通的 JavaScript 地图,但它们几乎是一样的。

    在 D3 地图中,如果您使用两个相同的键,则最后一个将覆盖第一个。根据API

    如果映射以前有相同键字符串的条目,则旧条目将替换为新值。

    因此,上面 sn-p 中的node 将始终引用第二个mammal

    解决方案:使用唯一 ID。

    问题 2

    关于问题2,问题就更明显了:如果代码没有找到对应的节点,就会报错:

    if (!node) throw new Error("missing: " + nodeId);
    

    您将在 JSFiddle 中看到哪个错误(查看控制台)。这是预期的行为。

    解决方案:始终确保链接具有正确的来源和目标。

    【讨论】:

      猜你喜欢
      • 2020-06-12
      • 2020-01-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-29
      • 2017-12-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多