我正在尝试使用带有动态运输数据的d3 sankey plugin创建sankey图表。在大多数情况下,它都能正常工作,除非获得如下数据集:

[{"DeparturePort":"CHARLESTON","ArrivalPort":"BREMERHAVEN","volume":5625.74},{"DeparturePort":"CHARLESTON","ArrivalPort":"ITAPOA","volume":2340},{"DeparturePort":"PT EVERGLADES","ArrivalPort":"PT AU PRINCE","volume":41.02},{"DeparturePort":"BREMERHAVEN","ArrivalPort":"CHARLESTON","volume":28}]


我的问题的关键是数据集中的第一个和最后一个条目。似乎在同一sankey图表中具有相反的方向会使javascript陷入无限循环并杀死浏览器。关于如何防止这种情况的任何想法?

这是我的图表代码,其中raw是上面的对象:

var data = raw;
                var units = "Volume";

                var margin = { top: 100, right: 0, bottom: 30, left: 0 },
                    width = $("#"+divID).width() - margin.left - margin.right,
                    height = divID == "enlargeChart" ? 800 - margin.top - margin.bottom : 600 - margin.top - margin.bottom;

                var formatNumber = d3.format(",.0f"),    // zero decimal places
                    format = function (d) { return ""; },
                    color = d3.scale.ordinal()
                    .range(["#0077c0", "#FF6600"]);

                // append the svg canvas to the page
                var svg = d3.select("#"+divID).append("svg")
                    .attr("width", width + margin.left + margin.right)
                    .attr("height", height + margin.top + margin.bottom)
                    .style("font-size", "12px")
                  .append("g")
                    .attr("transform",
                          "translate(" + margin.left + "," + margin.top + ")");

                // Set the sankey diagram properties
                var sankey = d3.sankey(width)
                    .nodeWidth(10)
                    .nodePadding(10)
                    .size([width, height]);

                var path = sankey.link();
                // load the data (using the timelyportfolio csv method)
                //d3.csv("sankey.csv", function (error, data) {

                    //set up graph in same style as original example but empty
                    graph = { "nodes": [], "links": [] };
                    var checklist = [];
                    data.forEach(function (d) {
                        if ($.inArray(d.DeparturePort, checklist) == -1) {
                            checklist.push(d.DeparturePort)
                            graph.nodes.push({ "name": d.DeparturePort });
                        }
                        if ($.inArray(d.ArrivalPort, checklist) == -1) {
                            checklist.push(d.ArrivalPort)
                            graph.nodes.push({ "name": d.ArrivalPort });
                        }
                        graph.links.push({
                            "source": d.DeparturePort,
                            "target": d.ArrivalPort,
                            "value": +d.volume
                        });
                    });

                    // return only the distinct / unique nodes
                    graph.nodes = d3.keys(d3.nest()
                      .key(function (d) { return d.name; })
                      .map(graph.nodes));

                    // loop through each link replacing the text with its index from node
                    graph.links.forEach(function (d, i) {
                        graph.links[i].source = graph.nodes.indexOf(graph.links[i].source);
                        graph.links[i].target = graph.nodes.indexOf(graph.links[i].target);
                    });

                    //now loop through each nodes to make nodes an array of objects
                    // rather than an array of strings
                    graph.nodes.forEach(function (d, i) {
                        graph.nodes[i] = { "name": d };
                    });


                    sankey
                      .nodes(graph.nodes)
                      .links(graph.links)
                      .layout(32);

                    // add in the links
                    var link = svg.append("g").selectAll(".link")
                        .data(graph.links)
                      .enter().append("path")
                        .attr("class", "link")
                        .attr("d", path)
                        .style("stroke-width", function (d) {  return Math.max(1, d.dy); })
                        .sort(function (a, b) { setTimeout(function () { return b.dy - a.dy; }, 10);});

                    // add the link titles
                    link.append("title")
                          .text(function (d) {
                            return d.source.name + " → " +
                                d.target.name + "\n" + d.value.toFixed(0) + " TEU";
                          });
                    $("#" + divID + " .loading").addClass("hide");


                    // add in the nodes
                    var node = svg.append("g").selectAll(".node")
                        .data(graph.nodes)
                      .enter().append("g")
                        .attr("class", "node")
                        .attr("transform", function (d) {
                                //setTimeout(function () {
                            return "translate(" + d.x + "," + d.y + ")";
                                //}, 10);
                        })
                      .call(d3.behavior.drag()
                        .origin(function (d) { return d; })
                        .on("dragstart", function () {
                            this.parentNode.appendChild(this);
                        })
                        .on("drag", dragmove));

                    // add the rectangles for the nodes
                    node.append("rect")
                        .attr("height", function (d) {  if (d.dy < 0) { d.dy = (d.dy * -1); } return d.dy; })
                        .attr("width", sankey.nodeWidth())
                        .style("fill", function (d) {
                            return d.color = color(d.name);
                        })
                        .style("stroke", function (d) {
                            return d3.rgb(d.color);
                        })
                      .append("title")
                        .text(function (d) {
                            return d.name + "\n" + format(d.value);
                        });
                    // add in the title for the nodes
                    node.append("text")
                        .attr("x", -6)
                        .attr("y", function (d) { return d.dy / 2; })
                        .attr("dy", ".35em")
                        .attr("text-anchor", "end")
                    .style("stroke", function (d) { return "#000000" })
                        .attr("transform", null)
                        .text(function (d) { return d.name; })
                      .filter(function (d) { return d.x < width / 2; })
                        .attr("x", 6 + sankey.nodeWidth())
                        .attr("text-anchor", "start");

                    // the function for moving the nodes
                    function dragmove(d) {
                        d3.select(this).attr("transform",
                            "translate(" + d.x + "," + (
                                    d.y = Math.max(0, Math.min(height - d.dy, d3.event.y))
                                ) + ")");
                        sankey.relayout();
                        link.attr("d", path);
                    }
            }, 0)

最佳答案

sankey.js脚本存在问题。

请参见此提交(在sankey.js的分支上)对其进行了修复:
https://github.com/soxofaan/d3-plugin-captain-sankey/commit/0edba18918aac3e9afadffd4a169c47f88a98f81

while (remainingNodes.length) {


变成:

while (remainingNodes.length && x < nodes.length) {


那应该防止无限循环。

09-25 20:02