D3 JSON文件,源和索引为字符串而不是索引

时间:2017-01-29 01:48:08

标签: javascript json d3.js

此代码与JSON文件完美配合,其中源和索引采用索引的形式。但是,当我切换到源和目标为字符串的格式时,它会抛出TypeError:e [u.source.index]未定义。我该如何克服这个问题?

<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script>

var width = 960,
    height = 500,
    active = d3.select(null);

var zoom = d3.behavior.zoom()
    .scaleExtent([1, 8])
    .on("zoom", zoomed);    

var force = d3.layout.force()
    .size([width, height])
    .charge(-400)
    .linkDistance(40)
    .on("tick", tick);

var drag = force.drag()
    .on("dragstart", dragstart);

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height)
   // .on("click", reset);

var g = svg.append("g");

var link = g.selectAll(".link"),
    node = g.selectAll(".node");

svg
    .call(zoom) // delete this line to disable free zooming
    .call(zoom.event);

d3.json("data/miserables.json", function(error, graph) {
  if (error) throw error;

  force
      .nodes(graph.nodes)
      .links(graph.links)
      .start();

  link = link.data(graph.links)
    .enter().append("line")
      .attr("class", "links")
      .style("stroke", "#999");

  node = node.data(graph.nodes)
    .enter().append("circle")
      .attr("class", "node")
      .attr("r", 12)
      .on("click", clicked)
      //.call(drag);
});

function tick() {
  link.attr("x1", function(d) { return d.source.x; })
      .attr("y1", function(d) { return d.source.y; })
      .attr("x2", function(d) { return d.target.x; })
      .attr("y2", function(d) { return d.target.y; });

  node.attr("cx", function(d) { return d.x; })
      .attr("cy", function(d) { return d.y; });
}

function clicked(d){
  if (active.node() === this) return reset();
  active.classed("active", false);
  active = d3.select(this).classed("active", true);

  var bbox = active.node().getBBox(),
      bounds = [[bbox.x, bbox.y],[bbox.x + bbox.width, bbox.y + bbox.height]];

  var dx = bounds[1][0] - bounds[0][0],
      dy = bounds[1][1] - bounds[0][1],
      x = (bounds[0][0] + bounds[1][0]) / 2,
      y = (bounds[0][1] + bounds[1][1]) / 2,
      scale = Math.max(1, Math.min(8, 0.9 / Math.max(dx / width, dy / height))),
      translate = [width / 2 - scale * x, height / 2 - scale * y];

  svg.transition()
      .duration(750)
      .call(zoom.translate(translate).scale(scale).event);
} 

function reset() {
  active.classed("active", false);
  active = d3.select(null);

  svg.transition()
      .duration(750)
      .call(zoom.translate([0, 0]).scale(1).event);
}    


function dragstart(d) {
  d3.select(this).classed("fixed", d.fixed = true);
}

function zoomed() {
  console.log(d3.event)
  g.style("stroke-width", 1.5 / d3.event.scale + "px");
  g.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}    

</script>

1 个答案:

答案 0 :(得分:1)

您正在使用D3 v3.x.虽然在D3 v4.x中按名称链接是一项简单的任务,但似乎在D3 v3.x中它是不可能的。请参阅D3 v3.x中的this issue以及API中的此说明:

  

注意:source和target属性的值最初可以指定为nodes数组的索引;在调用start之后,这些将被引用替换。

因此,下面的代码段不起作用(代码不是我的代码,我只是在网上找到并将链接数组从索引更改为名称):

var nodes = [{
    name: "node1"
}, {
    name: "node2"
}, {
    name: "node3"
}, {
    name: "node4"
}, {
    name: "node5"
}, {
    name: "node6"
}, {
    name: "node7"
}];

var edges = [{
    source: "node1",
    target: "node3"
}, {
    source: "node1",
    target: "node2"
}, {
    source: "node1",
    target: "node4"
}, {
    source: "node2",
    target: "node3"
}, {
    source: "node2",
    target: "node5"
}, {
    source: "node2",
    target: "node6"
}, {
    source: "node3",
    target: "node"
}];

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

var force = d3.layout.force()
    .nodes(nodes)
    .links(edges)
    .size([width, height])
    .linkDistance(150)
    .charge(-400);

force.start();
var svg_edges = svg.selectAll("line")
    .data(edges)
    .enter()
    .append("line")
    .style("stroke", "#ccc")
    .style("stroke-width", 1);

var color = d3.scale.category20();
var svg_nodes = svg.selectAll("circle")
    .data(nodes)
    .enter()
    .append("circle")
    .attr("r", 20)
    .style("fill", function(d, i) {
        return color(i);
    })
    .call(force.drag);
var svg_texts = svg.selectAll("text")
    .data(nodes)
    .enter()
    .append("text")
    .style("fill", "black")
    .attr("dx", 20)
    .attr("dy", 8)
    .text(function(d) {
        return d.name;
    });

force.on("tick", function() {
    svg_edges.attr("x1", function(d) {
            return d.source.x;
        })
        .attr("y1", function(d) {
            return d.source.y;
        })
        .attr("x2", function(d) {
            return d.target.x;
        })
        .attr("y2", function(d) {
            return d.target.y;
        });

    svg_nodes.attr("cx", function(d) {
            return d.x;
        })
        .attr("cy", function(d) {
            return d.y;
        });

    svg_texts.attr("x", function(d) {
            return d.x;
        })
        .attr("y", function(d) {
            return d.y;
        });
});
<script src="https://d3js.org/d3.v4.min.js"></script>

如果点击“运行代码段”,则只会看到错误:

  

未捕获的TypeError:无法读取未定义的属性'force'

解决方案:使用索引保持链接数组。但是,如果您已经拥有/接收带有名称的数组,则可以将其更改为索引:

var nodeByName = d3.map(nodes, function(d) {
    return d.name;
});

edges.forEach(function(d) {
    d.source = nodeByName.get(d.source);
    d.target = nodeByName.get(d.target);
});

以下是具有上述更改的第一个代码段的相同代码。现在它起作用了:

var nodes = [{
    name: "node1"
}, {
    name: "node2"
}, {
    name: "node3"
}, {
    name: "node4"
}, {
    name: "node5"
}, {
    name: "node6"
}, {
    name: "node7"
}];

var edges = [{
    source: "node1",
    target: "node3"
}, {
    source: "node1",
    target: "node2"
}, {
    source: "node1",
    target: "node4"
}, {
    source: "node2",
    target: "node3"
}, {
    source: "node2",
    target: "node5"
}, {
    source: "node2",
    target: "node6"
}, {
    source: "node2",
    target: "node7"
}];

var nodeByName = d3.map(nodes, function(d) {
    return d.name;
});

edges.forEach(function(d) {
    d.source = nodeByName.get(d.source);
    d.target = nodeByName.get(d.target);
});

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

var force = d3.layout.force()
    .nodes(nodes)
    .links(edges)
    .size([width, height])
    .linkDistance(150)
    .charge(-400);

force.start();
var svg_edges = svg.selectAll("line")
    .data(edges)
    .enter()
    .append("line")
    .style("stroke", "#ccc")
    .style("stroke-width", 1);

var color = d3.scale.category20();
var svg_nodes = svg.selectAll("circle")
    .data(nodes)
    .enter()
    .append("circle")
    .attr("r", 20)
    .style("fill", function(d, i) {
        return color(i);
    })
    .call(force.drag);
var svg_texts = svg.selectAll("text")
    .data(nodes)
    .enter()
    .append("text")
    .style("fill", "black")
    .attr("dx", 20)
    .attr("dy", 8)
    .text(function(d) {
        return d.name;
    });

force.on("tick", function() {
    svg_edges.attr("x1", function(d) {
            return d.source.x;
        })
        .attr("y1", function(d) {
            return d.source.y;
        })
        .attr("x2", function(d) {
            return d.target.x;
        })
        .attr("y2", function(d) {
            return d.target.y;
        });

    svg_nodes.attr("cx", function(d) {
            return d.x;
        })
        .attr("cy", function(d) {
            return d.y;
        });

    svg_texts.attr("x", function(d) {
            return d.x;
        })
        .attr("y", function(d) {
            return d.y;
        });
});
<script src="https://d3js.org/d3.v3.min.js"></script>