Обновите существующие узлы в d3 Force Layout

Я создал силовой макет с помощью d3, и он работает хорошо. Мои исходные данные загружаются из файла json, и диаграмма рисуется с использованием методов, аналогичных этот пример d3.js:

введите здесь описание изображения

Теперь, когда диаграмма находится на экране, мне нужно добавлять, обновлять и удалять узлы на лету из данных, которые я получаю через веб-сокет. У меня работают методы добавления и удаления, но я не могу найти правильный способ обновить свойства существующего узла.

Из прочитанного я понял, что правильная техника состоит в том, чтобы изменить источник данных, а затем обновить диаграмму с помощью метода enter().

Чтобы обновить узел, я делаю следующее:

function updateNode(id, word, size, trend, parent_id){    
  var updateNode = nodes.filter(function(d, i) { return d.id == id ? this : null; });
  if(updateNode[0]){   
    updateNode.size = Number(size);
    updateNode.trend = trend;
    nodes[updateNode.index] = updateNode;      
    update();
  }
}

Затем функция обновления обновляет узлы с помощью:

function update(){
  node = vis.selectAll('.node')
  .data(nodes, function(d) {
    return d.id;
  })

  createNewNodes(node.enter());
  node.exit().remove();
  force.start();
}

function createNewNodes(selection){    
  var slct = selection.append('g')
  .attr('class', 'node')
  .call(force.drag);

  slct.append('circle')
    .transition()
    .duration(500)
    .attr('r', function(d) {
      if(d.size){
        return Math.sqrt(sizeScale(d.size)*40);
      }
   })
}

Правильно ли я подхожу к этому? Когда я пробую этот код, узел, который я получаю в качестве точки отсчета при попытке установить атрибут радиуса для круга, является последним узлом в массиве узлов. т.е. тот, который содержит данные иерархического узла, а не один объект узла.

Любые указатели будут очень признательны, я потратил слишком много времени на это :)


person Slate8    schedule 18.09.2012    source источник
comment
У меня такая же проблема, только в гораздо более простом контексте. Вы нашли решение?   -  person    schedule 01.03.2013
comment
можно поместить ваш код в онлайн-редактор, например phrogz.net/JS/d3-playground/# BlankDefault   -  person ppoliani    schedule 11.02.2014


Ответы (1)


Есть несколько точек, которые вам нужны. Что я получаю от ваших вопросов: «как мне использовать многоразовый шаблон»

Простым ответом на этот вопрос было бы порекомендовать вам прочитать этот превосходный учебник от Майка Бостока: диаграммы

Если вы хотите получить больше информации, эта подборка документов может быть интересна:

Теперь вот черновик реализации, которую я бы сделал для вашей конкретной проблемы:

function ForceGraph(selector,data) {
   // This is the class that will create a graph

   var _data = data  
   // Local variable representing the forceGraph data 

   svg = d3.select(selector)
      .append('svg')
   // Create the initial svg element and the utilities you will need. 
   // These are the actions that won't be necessary on update. 
   // For example creating the axis objects, your variables, or the svg container

   this.data = function(value) {
      if(!arguments.length) {
         // accessor
         return _data;
      }
      _data = value;
      return this;
      // setter, returns the forceGraph object
   }

   this.draw = function() {
      // draw the force graph using data

      // the method here is to use selections in order to do: 
      // selection.enter().attr(...) // insert new data
      // selection.exit().attr(...) // remove data that is not used anymore
      // selection.attr(...) // 

   }
}

var selector = "#graphId";
var data = {nodes: [...],links: [...]};
myGraph = new ForceGraph(selector,data);
// Create the graph object
// Having myGraph in the global scope makes it easier to call it from a json function or anywhere in the code (even other js files). 
myGraph.draw();
// Draw the graph for the first time 

// wait for something or for x seconds  

var newData = {nodes: [...],links: [...]};
// Get a new data object via json, user input or whatever
myGraph.data(newData);
// Set the graph data to be `newData`
myGraph.draw();
// redraw the graph with `newData`

Как вы, вероятно, видите, цель не в том, чтобы иметь функцию для добавления нового узла. Цель состоит в том, чтобы перерисовать весь граф направленной силы, только обновив или удалив существующие узлы и добавив новые узлы. Таким образом, код рисования записывается только один раз, после чего меняются только данные.

Для дальнейшего чтения этот вопрос был моей золотой жилой, когда я впервые занялся этой проблемой: Обновление ссылок на принудительно ориентированном графе из динамических данных json

person Christopher Chiche    schedule 12.02.2014