Обновление layout.pack в d3.js

Я пытаюсь обратить внимание на макет d3 pack (http://bl.ocks.org/4063530).

У меня есть основной макет, но я хотел бы обновить его новыми данными. т.е. собирать новые данные, привязывать их к текущему layout.pack и соответственно обновлять (update/exit/enter).

Мои попытки здесь (http://jsfiddle.net/emepyc/n4xk8/14/):

var bPack = function(vis) {
    var pack = d3.layout.pack()
    .size([400,400])
    .value(function(d) {return d.time});

    var node = vis.data([data])
    .selectAll("g.node")
    .data(pack.nodes)
    .enter()
    .append("g")
    .attr("class", function(d) { return d.children ? "node" : "leaf node"; })
    .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });

node.append("circle")
    .attr("r", function(d) { return d.r });

    node.filter(function(d) { return !d.children; }).append("text")
    .attr("text-anchor", "middle")
    .attr("dy", ".3em")
    .text(function(d) { return d.analysis_id });

    bPack.update = function(new_data) {
        console.log("UPDATE");

        node
        .data([new_data])
        .selectAll("g.node")
        .data(pack.nodes);

        node
        .transition()
        .duration(1000)
        .attr("class", function(d) { return d.children ? "node" : "leaf node" })
    .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")" });

        node.selectAll("circle")
        .data(new_data)
        .transition()
    .duration(1000)
    .attr("r", function(d) { return d.r; });

    };

Конкретные вопросы...

Как мне привязать данные? (поскольку данные не являются сложной структурой, а не массивом данных)

Как добавить новые узлы/листья в макет? А старые удалены?

Указатели на рабочий пример будут очень благодарны.

Ответ 1

Рабочий пример здесь.

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

Затем, при каждом нажатии кнопки новые данные загружаются в пакет, а пакет пересчитывается. Этот важный код здесь:

Здесь вы связываете (загружаете) теперь данные в макет пакета: (в моем примере его случайные данные, конечно, у вас будут данные из json или кода или аналогичные):

pack.value(function(d) { return 1 +
             Math.floor(Math.random()*501); });

Здесь вычисляется новый макет:

pack.nodes(data);

После этого элементы переходят в новые позиции, а его атрибуты изменяются по мере их определения.

Я просто хочу подчеркнуть, что я не использую шаблон ввода/обновления/выхода или преобразования (что вы можете увидеть в других решениях), так как я считаю, что это приводит к излишней сложности для таких примеров.

Вот несколько фото с переходом в действии:

Начало:

start

Переход:

transition

Конец:

end

Ответ 2

У меня была такая же проблема в последнее время, и натолкнулся на учебные пособия General Update Pattern. Это не помогло моей цели. У меня было несколько сотен элементов DOM на графике (ForceLayout), и я возвращал данные REST со свойствами для каждого отдельного node. Обновление с помощью перезаписи данных привело к восстановлению всего графика, как вы сказали в ответ на предложение mg1075. Потребовалось несколько минут, чтобы закончить обновление DOM в моем случае.

В конце концов я назначил уникальные идентификаторы элементам, которые нуждаются в обновлении позже, и я вишневый взял их с помощью JQuery. Вся моя настройка графика использует D3, но тогда мои обновления не работают. Это плохо, но все работает отлично. Вместо того, чтобы тратить минуты на уничтожение и воссоздание большей части моего DOM, требуется примерно 3 секунды (исключая время для вызовов REST). Я не вижу причины, что в D3 невозможно сделать что-то вроде обновлений свойств.

Возможно, если бы Майк Босток добавил функцию remove() или правильную функцию подсекремента к выбору enter(), мы могли бы следовать за чистым шаблоном D3, чтобы делать обновления. Выяснив это, я пытался связать подмножество данных, данные с новыми добавленными свойствами, а затем подбирать элементы для обновления, но это не сработало из-за ограниченности и специфики ввода().

Ответ 3

Актуальность, если вы еще не просмотрели:
http://bl.ocks.org/3808218 - Общий шаблон обновления, я
http://bl.ocks.org/3808221 - общий шаблон обновления, II
http://bl.ocks.org/3808234 - общий шаблон обновления, III

В этом примере скрипта нет переходов, но здесь есть хотя бы один подход для обновления данных.

http://jsfiddle.net/jmKH6/

//  VISUALIZATION
var svg = d3.select("#kk")
    .append("svg")
    .attr("width", 500)
    .attr("height", 600)
    .attr("class", "pack"); 

var g = svg.append("g")
    .attr("transform", "translate(2,2)");

var pack = d3.layout.pack()
        .size([400,400])
        .value(function(d) {return d.time});

function update(data) {

    var nodeStringLenth = d3.selectAll("g.node").toString().length; 
    if ( nodeStringLenth > 0) {
        d3.selectAll("g.node")
            .remove();
    }

    var node = g.data([data]).selectAll("g.node")
            .data(pack.nodes);

        node.enter()
          .append("g")
            .attr("class", function(d) { return d.children ? "node" : "leaf node"; })
            .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });

        node.append("circle")
            .attr("r", function(d) { return d.r });

        node.filter(function(d) { return !d.children; }).append("text")
            .attr("text-anchor", "middle")
            .attr("dy", ".3em")
            .text(function(d) { return d.analysis_id });

       node
            .exit()
            .remove();
}


var myData = [data1, data2, data3];
update(data1); 
setInterval(function() {
    update( myData[Math.floor(Math.random() * myData.length)] );  // http://stackoverflow.com/info/4550505/getting-random-value-from-an-array?lq=1
}, 1500);