D3: Использование атрибута узла для ссылок вместо индекса в массиве

Я новичок в D3.js, работаю над визуализацией сети, и я не могу понять, как правильно указать мои ссылки для принудительного макета. Проблема в том, что по умолчанию d3 интерпретирует «источник» и «цель» ссылок как индексы узлов в массиве «узлов». Однако в моих данных источник и цель относятся к идентификационным номерам узлов, которые я сохранил в атрибуте узла. Как сделать так, чтобы ссылки указывали на node.id вместо node.index? Вот где я думаю, что я должен сделать это:

var nodes = data.nodes;
var edges = data.edges;
var force = d3.layout.force()
            .nodes(d3.values(nodes))
            .links(edges)
            .size([w, h])
            .linkDistance(80)
            .charge(-500)
            .gravity(0.2)
            .on("tick", tick)
            .start();

... но я не уверен, должен ли я изменять часть .nodes() или часть .links(). Я попытался добавить это непосредственно перед этим:

var nodes = data.nodes;
for (var i = 0; i < nodes.length; i++) {
    nodes[i].index = nodes[i].id;
    console.log(i + " " + nodes[i].index + " " + nodes[i].id);
}

... но атрибут index просто перезаписывается, когда я создаю силу.

Я прочитал этот вопрос, но единственный ответ кажется немного взломать. Они также упоминают «ключи данных», но я не могу найти много о них, и я не знаю, как их включить, поскольку на самом деле я не использую функцию enter().


person FrancesKR    schedule 29.05.2013    source источник


Ответы (1)


Я не думаю, что вы можете заставить D3 использовать атрибут node.id в качестве индекса, но вы можете обработать ссылки, чтобы они имели правильные ссылки:

var edges = [];

data.edges.forEach(function(e) { 
    // Get the source and target nodes
    var sourceNode = data.nodes.filter(function(n) { return n.id === e.source; })[0],
        targetNode = data.nodes.filter(function(n) { return n.id === e.target; })[0];

    // Add the edge to the array
    edges.push({source: sourceNode, target: targetNode});
});

var force = d3.layout.force()
    .nodes(data.nodes)
    .links(edges)
    // the rest of your code here
    .start();

Макет принудительно перезаписывает индекс, если вы его установили, но сохраняет ссылки на источник и цель каждого узла в ссылках.

person Pablo Navarro    schedule 29.05.2013
comment
Это не сработало... что вы подразумеваете под предположением, что ключом в объекте узлов является идентификатор? Разве data.nodes[] не просто обращается к узлам по индексу массива, когда я должен как-то делать это по идентификатору? - person FrancesKR; 30.05.2013
comment
Да, извините за это, я сделал неправильное предположение о вашей структуре данных. Я обновил код соответственно. - person Pablo Navarro; 30.05.2013
comment
Хорошо, спасибо, это работает! У меня также был атрибут type для ребер, поэтому мне пришлось добавить его, когда я нажимал каждое ребро, т.е. edges.push({source: sourceNode, target: targetNode, type: e.type}) - person FrancesKR; 30.05.2013
comment
Что возвращает data.nodes.filter(function(n) { return n.id === e.source; })[0]? Ссылка на объект в исходном массиве узлов? Разве вы не должны иметь возможность указывать ссылки только на узлы, существующие в графе силового макета? То есть кажется странным, что sourceNode и targetNode могут быть объектами в следующей строке: edges.push({source: sourceNode, target: targetNode}); - person LazerSharks; 31.05.2015
comment
О, я думаю, это возможно, потому что в конечном итоге элементы DOM просто привязаны к тем же самым объектам узлов в массиве узлов. Верно? - person LazerSharks; 31.05.2015