简体   繁体   English

多个强制布局图,d3在单独的svg / div中

[英]Multiple force-layout graphs with d3 in seperate svg/div's

I've a problem with creating multiple force layout graphs using d3 and reading data from a json file. 我使用d3创建多个强制布局图并从json文件中读取数据时遇到问题。 I use a for loop to iterate over the graphs, create a separate div containing a svg for each. 我使用for循环遍历图形,创建一个单独的div,每个div包含一个svg。 The problem is, that the force layout is only applied to the last one created, so basically the others just show a dot in the upper left corner. 问题是,力布局仅应用于最后创建的布局,因此基本上其他布局只在左上角显示一个点。 I could solve it partly by putting a for loop at the end of each iteration, but I still lose the interaction capabilities of the separate figures. 我可以通过在每次迭代结束时放置一个for循环来解决它,但我仍然失去了单独数字的交互功能。

Find the code below, thanks in advance. 请提前感谢以下代码。

Cheers, Michael 干杯,迈克尔

var color = d3.scale.category20();

var force = new Array();
var div = new Array();
var svg = new Array();
var graph = new Array();
var link;
var node;
var width = 360;
var height = 360;
var brush = new Array();
var shiftKey;
var count = 0;

//loop through the different subsystems in the json-file
for(name_subsystem in graphs) {
    //add a div for each subsystem
    div[count] = document.createElement("div");
    div[count].style.width = "360px";
    div[count].style.height = "360px";
    div[count].style.cssFloat="left";
    div[count].id = name_subsystem;

    document.body.appendChild(div[count]);


    //force is called. all attributes with default values are noted. see API reference on github.
    force[count] = d3.layout.force()
        .size([width, height])
        .linkDistance(20)
        .linkStrength(1)
        .friction(0.9)
        .charge(-30)
        .theta(0.8)
        .gravity(0.1);

    div[count].appendChild(document.createTextNode(name_subsystem));

    //create the svg rectangle in which other elements can be visualised
    svg[count] = d3.select("#"+name_subsystem)
        .on("keydown.brush", keydown)
        .on("keyup.brush", keyup)
      .append("svg")
        .attr("width", width)
        .attr("height", height)
        .attr("id",name_subsystem);

    brush[count] = svg[count].append("g")
        .datum(function() { return {selected: false, previouslySelected: false}; })
        .attr("class", "brush");

    //force is started
    force[count]
        .nodes(graphs[name_subsystem].nodes)
        .links(graphs[name_subsystem].links)
        .start();

    //link elements are called, joined with the data, and links are created for each link object in links
    link = svg[count].selectAll(".link")
        .data(graphs[name_subsystem].links)
        .enter().append("line")
        .attr("class", "link")
        .style("stroke-width", function(d) { return Math.sqrt(d.thickness); })
        .style("stroke", function(d){
            if (d.linktype === 'reactant'){
                return "black";
            } else {
                return "red";
            }
        });

    //node elements are called, joined with the data, and circles are created for each node object in nodes
    node = svg[count].selectAll(".node")
        .data(graphs[name_subsystem].nodes)
        .enter().append("circle")
        .attr("class", "node")
        //radius
        .attr("r", 5)
        //fill
        .attr("fill", function(d) {
            if (d.type === 'metabolite') {
                return "blue";
            } else {
                return "red";
            }
        })
        .on("mousedown", function(d) {
            if (!d.selected) { // Don't deselect on shift-drag.
                if (!shiftKey) node.classed("selected", function(p) { return p.selected = d === p; });
            else d3.select(this).classed("selected", d.selected = true);
            }
        })
        .on("mouseup", function(d) {
            if (d.selected && shiftKey) d3.select(this).classed("selected", d.selected = false);
        })
        .call(force[count].drag()
            .on("dragstart",function dragstart(d){
                d.fixed=true;
                d3.select(this).classed("fixed",true);
            })
        );


    //gives titles to nodes. i do not know why this is separated from the first node calling.
    node.append("title")
        .text(function(d) { return d.name; });

    //enable brushing of the network
    brush[count].call(d3.svg.brush()
        .x(d3.scale.identity().domain([0, width]))
        .y(d3.scale.identity().domain([0, height]))
        .on("brushstart", function(d) {
            node.each(function(d) { d.previouslySelected = shiftKey && d.selected; });
        })
        .on("brush", function() {
            var extent = d3.event.target.extent();
            node.classed("selected", function(d) {
                return d.selected = d.previouslySelected ^
                (extent[0][0] <= d.x && d.x < extent[1][0]
                && extent[0][1] <= d.y && d.y < extent[1][1]);
            });
        })
        .on("brushend", function() {
            d3.event.target.clear();
            d3.select(this).call(d3.event.target);
        })
    );

    //applies force per step or 'tick'. 
    force[count].on("tick", function() {
        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; });
    });
    //with this it works partly
    //for (var i = 0; i < 5000; ++i)force[count].tick();
    count++;
};

function keydown() {
  if (!d3.event.metaKey) switch (d3.event.keyCode) {
    case 38: nudge( 0, -1); break; // UP
    case 40: nudge( 0, +1); break; // DOWN
    case 37: nudge(-1,  0); break; // LEFT
    case 39: nudge(+1,  0); break; // RIGHT
  }
  shiftKey = d3.event.shiftKey || d3.event.metaKey;
}

function keyup() {
  shiftKey = d3.event.shiftKey || d3.event.metaKey;
}

edit: updated the code after the comments, still the same problem. 编辑:评论后更新代码,仍然是同样的问题。

i am working on force layout only, with many graphs at same time. 我正在进行力布局,同时有许多图形。

1 You don't need to have a count variable for each graph. 1您不需要为每个图形都有一个计数变量。

2 Don't make these variable(force, svg, graph) as array. 2不要将这些变量(force,svg,graph)作为数组。 There is no need for it. 没有必要。 just declare them above as (var svg;) and further on. 只需将它们声明为(var svg;)并继续进行。 As you call the function, it automatically makes its different copy and DOM maintain them separately. 当您调用该函数时,它会自动使其不同的副本和DOM分开维护它们。 So every variable you are using in graph, make it declare on top of function. 因此,您在图表中使用的每个变量都会使其声明在函数之上。

3 You are drawing all the graphs at same time, so as the new one is called, the previous one stops from being making on svg, that's why only last graph built successfully. 3您正在同时绘制所有图形,因此在调用新图形时,前一个图形停止在svg上生成,这就是为什么只有最后一个图形成功构建的原因。 So draw them after small time intervals. 所以在很短的时间间隔后绘制它们。

<html>
<script>
function draw_graphs(graphs){

var color = d3.scale.category20();

var force;
var div;
var svg;
var graph;
var link;
var node;
var width = 360;
var height = 360;
var brush = new Array();
var shiftKey;


//loop through the different subsystems in the json-file
for(name_subsystem in graphs) {
//add a div for each subsystem
div = document.createElement("div");
div.style.width = "360px";
div.style.height = "360px";
div.style.cssFloat="left";
div.id = name_subsystem;

document.body.appendChild(div); 


//force is called. all attributes with default values are noted. see API reference on github.
force = d3.layout.force()
    .size([width, height])
    .linkDistance(20)
    .linkStrength(1)
    .friction(0.9)
    .charge(-30)
    .theta(0.8)
    .gravity(0.1);

div.appendChild(document.createTextNode(name_subsystem));

//create the svg rectangle in which other elements can be visualised
svg = d3.select("#"+name_subsystem)
    .on("keydown.brush", keydown)
    .on("keyup.brush", keyup)
  .append("svg")
    .attr("width", width)
    .attr("height", height)
    .attr("id",name_subsystem); 

brush = svg.append("g")
    .datum(function() { return {selected: false, previouslySelected: false}; })
    .attr("class", "brush"); 

//force is started
force
    .nodes(graphs[name_subsystem].nodes)
    .links(graphs[name_subsystem].links)
    .start();

//link elements are called, joined with the data, and links are created for each link object in links
link = svg.selectAll(".link")
    .data(graphs[name_subsystem].links)
    .enter().append("line")
    .attr("class", "link")
    .style("stroke-width", function(d) { return Math.sqrt(d.thickness); })
    .style("stroke", function(d){
        if (d.linktype === 'reactant'){
            return "black";
        } else {
            return "red";
        }
    });

//node elements are called, joined with the data, and circles are created for each node object in nodes
node = svg.selectAll(".node")
    .data(graphs[name_subsystem].nodes)
    .enter().append("circle")
    .attr("class", "node")
    //radius
    .attr("r", 5)
    //fill
    .attr("fill", function(d) {
        if (d.type === 'metabolite') {
            return "blue";
        } else {
            return "red";
        }
    })
    .on("mousedown", function(d) {
        if (!d.selected) { // Don't deselect on shift-drag.
            if (!shiftKey) node.classed("selected", function(p) { return p.selected = d === p; });
        else d3.select(this).classed("selected", d.selected = true);
        }
    })
    .on("mouseup", function(d) {
        if (d.selected && shiftKey) d3.select(this).classed("selected", d.selected = false);
    })
    .call(force.drag()
        .on("dragstart",function dragstart(d){
            d.fixed=true;
            d3.select(this).classed("fixed",true);
        })
    );


//gives titles to nodes. i do not know why this is separated from the first node calling.
node.append("title")
    .text(function(d) { return d.name; });

//enable brushing of the network
brush.call(d3.svg.brush()
    .x(d3.scale.identity().domain([0, width]))
    .y(d3.scale.identity().domain([0, height]))
    .on("brushstart", function(d) {
        node.each(function(d) { d.previouslySelected = shiftKey && d.selected; });
    })
    .on("brush", function() {
        var extent = d3.event.target.extent();
        node.classed("selected", function(d) {
            return d.selected = d.previouslySelected ^
            (extent[0][0] <= d.x && d.x < extent[1][0]
            && extent[0][1] <= d.y && d.y < extent[1][1]);
        });
    })
    .on("brushend", function() {
        d3.event.target.clear();
        d3.select(this).call(d3.event.target);
    })
);

//applies force per step or 'tick'. 
force.on("tick", function() {
    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; });
});
//with this it works partly
//for (var i = 0; i < 5000; ++i)force[count].tick();
};

function keydown() {
if (!d3.event.metaKey) switch (d3.event.keyCode) {
case 38: nudge( 0, -1); break; // UP
case 40: nudge( 0, +1); break; // DOWN
case 37: nudge(-1,  0); break; // LEFT
case 39: nudge(+1,  0); break; // RIGHT
}
shiftKey = d3.event.shiftKey || d3.event.metaKey;
}

function keyup() {
shiftKey = d3.event.shiftKey || d3.event.metaKey;
}

}
</script>


<script>
$(document).ready(function() {
draw_graphs("pass here the json file");


// this will drawn 2nd graph after 1 second.              
var t = setTimeout(function(){
draw_graphs("pass here json file");
}, 1000)

});

Here the code I finally used with the help of the comments above, maybe helpful for others as well: 这里我最终在上述评论的帮助下使用的代码也可能对其他人有用:

<script type="text/javascript" src="d3_splitted_var.json"></script>
<script>
function draw_graphs(name_subsystem){   

    var force;
    var div;
    var svg;
    var link;
    var node;
    var width = 360;
    var height = 360;
    var r=5;
    var brush = new Array();
    var shiftKey;

    //add a div for each subsystem
    div = document.createElement("div");
    div.style.width = "360px";
    div.style.height = "360px";
    div.style.cssFloat="left";
    div.id = name_subsystem;

    document.body.appendChild(div);

    force = d3.layout.force()
        .size([width, height])
        .linkDistance(20)
        .linkStrength(1)
        .friction(0.9)
        .charge(-50)
        .theta(0.8)
        .gravity(0.1);

    div.appendChild(document.createTextNode(name_subsystem));

    //create the svg rectangle in which other elements can be visualised
    svg = d3.select("#"+name_subsystem)
      .append("svg")
        .attr("width", width)
        .attr("height", height)
        .attr("id",name_subsystem);

    //force is started
    force
            .nodes(graphs[name_subsystem].nodes)
        .links(graphs[name_subsystem].links)
        .start();

    //link elements are called, joined with the data, and links are created for each link object in links
    link = svg.selectAll(".link")
        .data(graphs[name_subsystem].links)
        .enter().append("line")
        .attr("class", "link")
        .style("stroke-width", function(d) { return Math.sqrt(d.thickness); })
        .style("stroke", function(d){
            if (d.linktype === 'reactant'){
                return "black";
            } else {
                return "red";
            }
        });

    //node elements are called, joined with the data, and circles are created for each node object in nodes
    node = svg.selectAll(".node")
        .data(graphs[name_subsystem].nodes)
        .enter().append("circle")
        .attr("class", "node")
        //radius
        .attr("r", r)
        //fill
        .attr("fill", function(d) {
            if (d.type === 'metabolite') {
                return "blue";
            } else {
                return "red";
            }
        })
        .call(force.drag()
            .on("dragstart",function dragstart(d){
                d.fixed=true;
                d3.select(this).classed("fixed",true);
            })
        );

    //gives titles to nodes. i do not know why this is separated from the first node calling.
    node.append("title")
        .text(function(d) { return d.name; });


    //applies force per step or 'tick'.
    force.on("tick", function() {
        node.attr("cx", function(d) { return d.x = Math.max(r, Math.min(width - r, d.x)); })
            .attr("cy", function(d) { return d.y = Math.max(r, Math.min(height - r, d.y)); });

        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; });
    });
};
for(name_subsystem in graphs) {
    draw_graphs(name_subsystem);
}
</script>

Note: graphs is the name of the variable in my json file. 注意:graph是我的json文件中变量的名称。 You need to include the d3-library. 您需要包含d3库。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM