Я создал скрипку для репликации проблемы: скрипка ссылки
До этого мы использовали хороший плоский json файл в нашем проекте. Новый файл, с которым я должен работать, гораздо более вложен.
Моя функция d3 выглядит так:
/* chart data */
// return data for relationships between database tables
returnTableRelationshipData = function(){
var url = 'https://api.myjson.com/bins/7ovnb.json';
d3.json(url, function(data){
//find the node index
function find(f){
var i = -1
data.p.nodes.forEach(function(node, index){
node.x = 200 + Math.random()*200;
node.y = 150 + Math.random()*200;
if(node.properties.nodeID.val == f)
i = index;
});
return i;
}
//set the source and target index
data.p.relationships.forEach(function(d){
d.start = find(d.start);
d.end = find(d.end);
});
// used to store the number of links between two nodes.
var mLinkNum = {};
// sort links first
sortLinks();
// set up linkIndex and linkNumer, because it may possible multiple links share the same source and target node
setLinkIndexAndNum();
// check that we don't have empty or null values
checkDataNotEmpty();
var w = 1000;
var h = 400;
var force = d3.layout.force()
.nodes(data.p.nodes)
.links(data.p.relationships)
.alpha(.1)
.gravity(1)
.charge(-10000)
.size([w, h])
.start();
var svg = d3.select('.node-wrapper').append('svg')
.attr('width', w)
.attr('height', h);
var path = svg.append('svg:g')
.selectAll('path')
.data(force.links())
.enter().append('line')
.attr('class', 'link')
.attr('x1', function(d) {
return d.start.x;
})
.attr('y1', function(d) {
return d.start.y;
})
.attr('x2', function(d) {
return d.end.x;
})
.attr('y2', function(d) {
return d.end.y;
});
var node_drag = d3.behavior.drag()
.on('dragstart', dragstart)
.on('drag', dragmove)
.on('dragend', dragend);
var circle = svg.append('svg:g')
.selectAll('circle')
.data(force.nodes())
.enter().append('svg:circle')
.attr('r', 6)
.call(node_drag);
var text = svg.append('svg:g')
.selectAll('g')
.data(force.nodes())
.enter().append('svg:g');
text.append('svg:text')
.text(function(d){
return d.description;
});
force.on('tick', tick);
function tick() {
path.attr('x1', function(d) {
return d.start.x;
})
.attr('y1', function(d) {
return d.start.y;
})
.attr('x2', function(d) {
return d.end.x;
})
.attr('y2', function(d) {
return d.end.y;
});
circle.attr('transform', function(d){
return 'translate(' + d.x + ',' + d.y + ')';
});
text.attr('transform', function(d){
return 'translate(' + d.x + ',' + d.y + ')';
});
}
function dragstart(d, i) {
force.stop(); // stops the force auto positioning before you start dragging
}
function dragmove(d, i) {
d.px += d3.event.dx;
d.py += d3.event.dy;
d.x += d3.event.dx;
d.y += d3.event.dy;
tick();
}
function dragend(d, i) {
d.fixed = true; // of course set the node to fixed so the force doesn't include the node in its auto positioning stuff
tick();
}
// sort the links by source, then target
function sortLinks(){
if(data.p.relationships != null){
data.p.relationships.sort(function(a,b){
if(a.start > b.start){
return 1;
}else if(a.start < b.start){
return -1;
}else{
if(a.end > b.end){
return 1;
}if(a.end < b.end){
return -1;
}else{
return 0;
}
}
});
}
}
//any links with duplicate source and target get an incremented 'linknum'
function setLinkIndexAndNum(){
for(var i = 0; i < data.p.relationships.length; i++){
if(i != 0 &&
data.p.relationships[i].start == data.p.relationships[i-1].start &&
data.p.relationships[i].end == data.p.relationships[i-1].end){
data.p.relationships[i].linkindex = data.p.relationships[i-1].linkindex + 1;
}else{
data.p.relationships[i].linkindex = 1;
}// save the total number of links between two nodes
if(mLinkNum[data.p.relationships[i].end + ',' + data.p.relationships[i].start] !== undefined){
mLinkNum[data.p.relationships[i].end + ',' + data.p.relationships[i].start] = data.p.relationships[i].linkindex;
}else{
mLinkNum[data.p.relationships[i].start + ',' + data.p.relationships[i].end] = data.p.relationships[i].linkindex;
}
}
}
function checkDataNotEmpty(){
data.p.relationships.forEach(function(link, index, list) {
if (typeof link.start === 'undefined') {
console.log('undefined link', data.p.nodes[link.start]);
}
if (typeof link.end === 'undefined') {
console.log('undefined source', data.p.nodes[link.end]);
}
});
}
});
}();
В соответствии с этим ответом, если я прокомментирую эти строки:
var force = d3.layout.force()
//.nodes(data.p.nodes)
//.links(data.p.relationships)
.alpha(.1)
.gravity(1)
...
Затем объект svg добавляется к html.
Ответ, связанный с этим, на самом деле не дает решения.
Как узлы, так и ссылки, по-видимому, повторяются правильно.
Моя единственная догадка заключается в том, что мне как-то приходится сопоставлять "start"
и "end"
source
и target
, или мне нужно каким-то образом преобразовать data.p.nodes
и data.p.relationships
. Или, возможно, индексирование работает неправильно.
Я могу работать с сторонним разработчиком, чтобы изменить некоторые свойства и типы json (строка, целое число и т.д.).
Здесь вызывается json файл: http://myjson.com/7ovnb
Определите штрих для ссылок, чтобы сделать их видимыми:
.node-wrapper line {
stroke: #0D9E1E;
}
Переименуйте все вхождения .start
с .source
и .end
с .target
в функции tick()
.
В вашей функции find()
вы должны сравнить с свойством node.id
:
function find(f){
var i; // do not return an existing value as default
data.p.nodes.forEach(function(node, index){
node.x = 200 + Math.random()*200;
node.y = 150 + Math.random()*200;
if(node.id == f)
i = index;
});
return i;
}
Index 0 if - это существующий индекс узла, поэтому было бы лучше не устанавливать его как значение по умолчанию.