JS D3更新力图带有Websocket

发布于 2025-02-06 13:39:13 字数 3253 浏览 3 评论 0原文

上下文:我拥有管理设备的Django服务器,我想在这些设备之间显示一个通信图,我决定将D3 Force Graph用于此目的,Django服务器将通过用Websocket重新使用,我希望客户端读取JSON并打印图形。
到目前为止,我已经能够打印静态图,但是我无法实时更新它。

有用链接:

目标:使用WebSocket实时更新力图。

我的JS代码:


var graph = {
  "nodes": [
            {"id": "Agent_1", "group": 1},
            {"id": "Agent_2", "group": 2},
            {"id": "Agent_3", "group": 1},
            {"id": "Agent_4", "group": 3}
        ],
  "links": []
};


const comSocket = new WebSocket(
  'ws://'
  + window.location.host
  + '/ws/com/'
);

comSocket.onmessage = function (e) {
  graph = JSON.parse(e.data).message;
  console.log(graph);

  simulation.nodes(graph.nodes);
  simulation.force("link").links(graph.links);
  simulation.alpha(1).restart();
  };


var svg = d3.select("svg"),
    width = +svg.attr("width"),
    height = +svg.attr("height");

var color = d3.scaleOrdinal(d3.schemeCategory20);

var simulation = d3.forceSimulation()
    .force("link", d3.forceLink().id(function(d) { return d.id; }))
    .force("charge", d3.forceManyBody().strength(-2500))
    .force("center", d3.forceCenter(width / 2, height / 2));

  var link = svg.append("g").attr("class", "links").selectAll("line")
  .data(graph.links).enter().append("line")
  .attr("stroke-width", function(d) { return Math.sqrt(d.value); });

  var node = svg.append("g")
      .attr("class", "nodes")
    .selectAll("g")
    .data(graph.nodes)
    .enter().append("g")

  var circles = node.append("circle")
    .attr("r", 20)
    .attr("fill", function(d) { return color(d.group); });

  var lables = node.append("text").text(function(d) {return d.id;}).attr('x', 16).attr('y', 13);

  node.append("title").text(function(d) { return d.id; });

  simulation.nodes(graph.nodes).on("tick", ticked);
  simulation.force("link").links(graph.links);

  function ticked() {
    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("transform", function(d) {
          return "translate(" + d.x + "," + d.y + ")";
        })
  };

使用上述代码会产生此错误:

Uncaught TypeError: Cannot read properties of undefined (reading 'length')

在行:

  simulation.nodes(graph.nodes);

在OnMessage()中,

数据值是JSON,其结构与VAR Graph相同(第1行)。因此,我不知道为什么它可以正确初始化图表,但不会以相同的值刷新。

{'nodes': [{'id': 'Agent_0', 'group': 1}, {'id': 'Agent_1', 'group': 2}, {'id': 'Agent_2', 'group': 1}, {'id': 'Agent_3', 'group': 3}], 'links': [{'source': 'Agent_0', 'target': 'Agent_2', 'value': 1}, {'source': 'Agent_0', 'target': 'Agent_1', 'value': 3}, {'source': 'Agent_0', 'target': 'Agent_3', 'value': 5}, {'source': 'Agent_1', 'target': 'Agent_3', 'value': 3}, {'source': 'Agent_2', 'target': 'Agent_3', 'value': 5}, {'source': 'Agent_1', 'target': 'Agent_2', 'value': 5}]}

Context : I have this Django server that manages devices, i want to show a communication graph between these devices, i've decide to use D3 force graph for this purpose, the Django server will send a json through Redis with a websocket, i want the client to read the json and print the graph.
So far i've been able to print static graph, but i can't manage to update it live.

Usefull link :

  • Core code from This example.
  • Tried to follow This, but i don't think it's the right direction.

Goal : Update a Force graph in real time using websocket.

My JS code :


var graph = {
  "nodes": [
            {"id": "Agent_1", "group": 1},
            {"id": "Agent_2", "group": 2},
            {"id": "Agent_3", "group": 1},
            {"id": "Agent_4", "group": 3}
        ],
  "links": []
};


const comSocket = new WebSocket(
  'ws://'
  + window.location.host
  + '/ws/com/'
);

comSocket.onmessage = function (e) {
  graph = JSON.parse(e.data).message;
  console.log(graph);

  simulation.nodes(graph.nodes);
  simulation.force("link").links(graph.links);
  simulation.alpha(1).restart();
  };


var svg = d3.select("svg"),
    width = +svg.attr("width"),
    height = +svg.attr("height");

var color = d3.scaleOrdinal(d3.schemeCategory20);

var simulation = d3.forceSimulation()
    .force("link", d3.forceLink().id(function(d) { return d.id; }))
    .force("charge", d3.forceManyBody().strength(-2500))
    .force("center", d3.forceCenter(width / 2, height / 2));

  var link = svg.append("g").attr("class", "links").selectAll("line")
  .data(graph.links).enter().append("line")
  .attr("stroke-width", function(d) { return Math.sqrt(d.value); });

  var node = svg.append("g")
      .attr("class", "nodes")
    .selectAll("g")
    .data(graph.nodes)
    .enter().append("g")

  var circles = node.append("circle")
    .attr("r", 20)
    .attr("fill", function(d) { return color(d.group); });

  var lables = node.append("text").text(function(d) {return d.id;}).attr('x', 16).attr('y', 13);

  node.append("title").text(function(d) { return d.id; });

  simulation.nodes(graph.nodes).on("tick", ticked);
  simulation.force("link").links(graph.links);

  function ticked() {
    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("transform", function(d) {
          return "translate(" + d.x + "," + d.y + ")";
        })
  };

Using the above code it produce this error :

Uncaught TypeError: Cannot read properties of undefined (reading 'length')

at the line :

  simulation.nodes(graph.nodes);

in onmessage()

The data value is a json with the same structure as var graph (line 1). So i don't know why it can initialize the graph correctly but canno't refresh with the same value.. :

{'nodes': [{'id': 'Agent_0', 'group': 1}, {'id': 'Agent_1', 'group': 2}, {'id': 'Agent_2', 'group': 1}, {'id': 'Agent_3', 'group': 3}], 'links': [{'source': 'Agent_0', 'target': 'Agent_2', 'value': 1}, {'source': 'Agent_0', 'target': 'Agent_1', 'value': 3}, {'source': 'Agent_0', 'target': 'Agent_3', 'value': 5}, {'source': 'Agent_1', 'target': 'Agent_3', 'value': 3}, {'source': 'Agent_2', 'target': 'Agent_3', 'value': 5}, {'source': 'Agent_1', 'target': 'Agent_2', 'value': 5}]}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

九公里浅绿 2025-02-13 13:39:13

这是服务器端问题,发送了错误的类型。

最后,我还将代码更新为最新版本(仅需要更新颜色)。这是最终的工作版本:

var graph = {
 "nodes": [
           {"id": "Agent_0", "group": 1},
           {"id": "Agent_1", "group": 2},
           {"id": "Agent_2", "group": 1},
           {"id": "Agent_3", "group": 3}
       ],
 "links": []
};

var svg = d3.select("svg"),
   width = +svg.attr("width"),
   height = +svg.attr("height");

var color = d3.schemeCategory10;

var simulation = d3.forceSimulation()
   .force("link", d3.forceLink().id(function(d) { return d.id; }))
   .force("charge", d3.forceManyBody().strength(-2500))
   .force("center", d3.forceCenter(width / 2, height / 2));

 var link = svg.append("g").attr("class", "links").selectAll("line").data(graph.links).enter().append("line")
 .attr("stroke-width", function(d) { return Math.sqrt(d.value); });

 var node = svg.append("g").attr("class", "nodes").selectAll("g").data(graph.nodes).enter().append("g")
 var circles = node.append("circle").attr("r", 20).attr("fill", function(d) { return color[d.group]; });
 var lables = node.append("text").text(function(d) {return d.id;}).attr('x', 16).attr('y', 13);

 node.append("title").text(function(d) { return d.id; });

 simulation.nodes(graph.nodes).on("tick", ticked);
 simulation.force("link").links(graph.links);

 function ticked() {
   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("transform", function(d) {
         return "translate(" + d.x + "," + d.y + ")";
       })
 };

const comSocket = new WebSocket(
 'ws://'
 + window.location.host
 + '/ws/com/'
);

comSocket.onmessage = function (e) {
 graph = JSON.parse(e.data).message;
 console.log(graph);
 console.log(typeof graph);

 svg.selectAll("*").remove();
 link = svg.append("g").attr("class", "links").selectAll("line").data(graph.links).enter().append("line")
 .attr("stroke-width", function(d) { return Math.sqrt(d.value); });

 node = svg.append("g").attr("class", "nodes").selectAll("g").data(graph.nodes).enter().append("g")
 circles = node.append("circle").attr("r", 20).attr("fill", function(d) { return color[d.group]; });
 lables = node.append("text").text(function(d) {return d.id;}).attr('x', 16).attr('y', 13);

 node.append("title").text(function(d) { return d.id; });

 simulation.nodes(graph.nodes).on("tick", ticked);
 simulation.force("link").links(graph.links);
 simulation.alpha(1).restart();
 };

It was a server side issue, wrong type was sent.

In the end i've also update the code to latest version (only color needed to be updated). Here's the final working version :

var graph = {
 "nodes": [
           {"id": "Agent_0", "group": 1},
           {"id": "Agent_1", "group": 2},
           {"id": "Agent_2", "group": 1},
           {"id": "Agent_3", "group": 3}
       ],
 "links": []
};

var svg = d3.select("svg"),
   width = +svg.attr("width"),
   height = +svg.attr("height");

var color = d3.schemeCategory10;

var simulation = d3.forceSimulation()
   .force("link", d3.forceLink().id(function(d) { return d.id; }))
   .force("charge", d3.forceManyBody().strength(-2500))
   .force("center", d3.forceCenter(width / 2, height / 2));

 var link = svg.append("g").attr("class", "links").selectAll("line").data(graph.links).enter().append("line")
 .attr("stroke-width", function(d) { return Math.sqrt(d.value); });

 var node = svg.append("g").attr("class", "nodes").selectAll("g").data(graph.nodes).enter().append("g")
 var circles = node.append("circle").attr("r", 20).attr("fill", function(d) { return color[d.group]; });
 var lables = node.append("text").text(function(d) {return d.id;}).attr('x', 16).attr('y', 13);

 node.append("title").text(function(d) { return d.id; });

 simulation.nodes(graph.nodes).on("tick", ticked);
 simulation.force("link").links(graph.links);

 function ticked() {
   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("transform", function(d) {
         return "translate(" + d.x + "," + d.y + ")";
       })
 };

const comSocket = new WebSocket(
 'ws://'
 + window.location.host
 + '/ws/com/'
);

comSocket.onmessage = function (e) {
 graph = JSON.parse(e.data).message;
 console.log(graph);
 console.log(typeof graph);

 svg.selectAll("*").remove();
 link = svg.append("g").attr("class", "links").selectAll("line").data(graph.links).enter().append("line")
 .attr("stroke-width", function(d) { return Math.sqrt(d.value); });

 node = svg.append("g").attr("class", "nodes").selectAll("g").data(graph.nodes).enter().append("g")
 circles = node.append("circle").attr("r", 20).attr("fill", function(d) { return color[d.group]; });
 lables = node.append("text").text(function(d) {return d.id;}).attr('x', 16).attr('y', 13);

 node.append("title").text(function(d) { return d.id; });

 simulation.nodes(graph.nodes).on("tick", ticked);
 simulation.force("link").links(graph.links);
 simulation.alpha(1).restart();
 };

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文