D3 Force Directed Graph ajax更新

d97*_*996 12 ajax json d3.js force-layout

我正在使用d3.js和jquery与PHP后端(基于yii框架)来创建动态力导向图,以表示我们使用Nagios监视的网络上的主机和服务的当前状态.

该图显示了root - > hostgroups - > hosts - > services.我已经创建了一个服务器端函数来以下列格式返回JSON对象

{
    "nodes": [
        {
            "name": "MaaS",
            "object_id": 0
        },
        {
            "name": "Convergence",
            "object_id": "531",
            "colour": "#999900"
        },
        {
            "name": "maas-servers",
            "object_id": "719",
            "colour": "#999900"
        },
        {
            "name": "hrg-cube",
            "object_id": "400",
            "colour": "#660033"
        }
    ],
    "links": [
        {
            "source": 0,
            "target": "531"
        },
        {
            "source": 0,
            "target": "719"
        },
        {
            "source": "719",
            "target": "400"
        }
    ]
}
Run Code Online (Sandbox Code Playgroud)

节点包含在链接中使用的对象id和用于显示节点状态的颜色(OK =绿色,WARNING =黄色等)链接具有节点的源对象id和目标对象id.在监视系统中添加或删除新主机时,节点和链接可能会发生变化

我有以下代码设置初始SVG,然后每10秒

  1. 检索当前的JSON对象
  2. 创建链接的地图
  3. 选择当前节点和链接并将它们绑定到JSON数据
  4. 添加输入链接并删除退出链接
  5. 更新和添加的节点将更改其填充颜色并添加其名称的工具提示
  6. 力量开始了

    $ .ajaxSetup({cache:false}); width = 960,height = 500; node = []; link = []; force = d3.layout.force().charge(-1000).linkDistance(1).size([width,height]);

    svg = d3.select("body").append("svg")
        .attr("width", width)
        .attr("height", height)
      .append("g");
    
    setInterval(function(){
        $.ajax({
            url: "<?php echo $url;?>",
            type: "post",
            async: false,
            datatype: "json",
            success: function(json, textStatus, XMLHttpRequest) 
            {
                json = $.parseJSON(json);
    
                var nodeMap = {};
                json.nodes.forEach(function(x) { nodeMap[x.object_id] = x; });
                json.links = json.links.map(function(x) {
                    return {
                        source: nodeMap[x.source],
                        target: nodeMap[x.target],
                    };
                });
    
                link = svg.selectAll("line")
                    .data(json.links);
    
                node = svg.selectAll("circle")
                    .data(json.nodes,function(d){return d.object_id})
    
                link.enter().append("line").attr("stroke-width",1).attr('stroke','#999');
                link.exit().remove();
    
                node.enter().append("circle").attr("r",5);
                node.exit().remove();
    
                node.attr("fill",function(d){return d.colour});
    
                node.append("title")
                  .text(function(d) { return d.name; });
    
                node.call(force.drag);
    
                force
                    .nodes(node.data())
                    .links(link.data()) 
                    .start()
    
                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 = Math.max(5, Math.min(width - 5, d.x));  })
                        .attr("cy", function(d) { return d.y = Math.max(5, Math.min(height - 5, d.y)); });
    
                });
            }
        });
    },10000);
    
    Run Code Online (Sandbox Code Playgroud)

可以在网络可视化中看到输出的示例

所有上述操作都正常,但每次代码循环时都会导致可视化重新启动,节点都会反弹直到它们稳定下来.我需要的是任何当前项目保持不变,但任何新节点和链接都添加到可视化中,并且可点击和拖动等.

如果有人可以提供帮助,我将永远感激不尽.

d97*_*996 5

我已经设法使用上述所有建议的混合物找到问题的解决方案,下面是我使用的代码

    var width = $(document).width();
    var height = $(document).height();

    var outer = d3.select("#chart")
        .append("svg:svg")
            .attr("width", width)
            .attr("height", height)
            .attr("pointer-events", "all");

    var vis = outer
        .append('svg:g')
            .call(d3.behavior.zoom().on("zoom", rescale))
            .on("dblclick.zoom", null)
        .append('svg:g')

        vis.append('svg:rect')
            .attr('width', width)
            .attr('height', height)
            .attr('fill', 'white');

        var force = d3.layout.force()
            .size([width, height])
            .nodes([]) // initialize with a single node
            .linkDistance(1)
            .charge(-500)
            .on("tick", tick);

        nodes = force.nodes(),
            links = force.links();

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

       redraw();

       setInterval(function(){
           $.ajax({
                url: "<?php echo $url;?>",
                type: "post",
                async: false,
                datatype: "json",
                success: function(json, textStatus, XMLHttpRequest) 
                {
                    var current_nodes = [];
                    var delete_nodes = [];
                    var json = $.parseJSON(json);

                    $.each(json.nodes, function (i,data){

                        result = $.grep(nodes, function(e){ return e.object_id == data.object_id; });
                        if (!result.length)
                        {
                            nodes.push(data);
                        }
                        else
                        {
                            pos = nodes.map(function(e) { return e.object_id; }).indexOf(data.object_id);
                            nodes[pos].colour = data.colour;
                        }
                        current_nodes.push(data.object_id);             
                    });

                    $.each(nodes,function(i,data){
                        if(current_nodes.indexOf(data.object_id) == -1)
                        {
                            delete_nodes.push(data.index);
                        }       
                    });
                    $.each(delete_nodes,function(i,data){
                        nodes.splice(data,1); 
                    });

                    var nodeMap = {};
                    nodes.forEach(function(x) { nodeMap[x.object_id] = x; });
                    links = json.links.map(function(x) {
                        return {
                            source: nodeMap[x.source],
                            target: nodeMap[x.target],
                            colour: x.colour,
                        };
                    });
                    redraw();
                }
            });
       },2000);


       function redraw()
       {
           node = node.data(nodes,function(d){ return d.object_id;});
           node.enter().insert("circle")
                .attr("r", 5)
           node.attr("fill", function(d){return d.colour})
           node.exit().remove();

           link = link.data(links);
           link.enter().append("line")
               .attr("stroke-width",1)
           link.attr('stroke',function(d){return d.colour});
           link.exit().remove();
           force.start();

       }

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

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

       function rescale() {
            trans=d3.event.translate;
            scale=d3.event.scale;

            vis.attr("transform",
                "translate(" + trans + ")"
                + " scale(" + scale + ")"); 
        }
Run Code Online (Sandbox Code Playgroud)