根据json中的坐标定位节点
Locating the nodes according to the coordinates in the json
我试图让定向网络的节点在页面启动时看起来位于 json 文档中反映的坐标处。但是,实际代码重新计算了节点位置,这不符合我的应用程序的要求。有帮助吗?
主要代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style type="text/css">
.node {}
.link {
stroke: #999;
stroke-opacity: .6;
stroke-width: 1px;
}
line {
stroke: rgb(212, 212, 212);
stroke-width: 1px;
shape-rendering: crispEdges;
}
svg {
box-sizing: border-box;
border: 1px solid rgb(212, 212, 212);
}
</style>
</head>
<body>
<svg width="960" height="600"></svg>
<script src="https://d3js.org/d3.v4.min.js" type="text/javascript"></script>
<script src="https://d3js.org/d3-selection-multi.v1.js"></script>
<script type="text/javascript">
var width = 960,
height = 500,
resolution = 150,
r = 15;
var colors = d3.scaleOrdinal(d3.schemeCategory10);
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
node,
link;
svg.selectAll('.vertical')
.data(d3.range(1, width / resolution))
.enter().append('line')
.attr('class', 'vertical')
.attr('x1', function(d) { return d * resolution; })
.attr('y1', 0)
.attr('x2', function(d) { return d * resolution; })
.attr('y2', height);
svg.selectAll('.horizontal')
.data(d3.range(1, height / resolution))
.enter().append('line')
.attr('class', 'horizontal')
.attr('x1', 0)
.attr('y1', function(d) { return d * resolution; })
.attr('x2', width)
.attr('y2', function(d) { return d * resolution; });
svg.append('defs').append('marker')
.attrs({'id':'arrowhead',
'viewBox':'-0 -5 10 10',
'refX':13,
'refY':0,
'orient':'auto',
'markerWidth':13,
'markerHeight':13,
'xoverflow':'visible'})
.append('svg:path')
.attr('d', 'M 0,-5 L 10 ,0 L 0,5')
.attr('fill', '#999')
.style('stroke','none');
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function (d) {return d.id;}).distance(150).strength(1))
//.force("charge", d3.forceManyBody())
//.force("center", d3.forceCenter(width / 2, height / 2));
d3.json("graph.json", function (error, graph) {
if (error) throw error;
update(graph.links, graph.nodes);
})
function update(links, nodes) {
link = svg.selectAll(".link")
.data(links)
.enter()
.append("line")
.attr("class", "link")
.attr('marker-end','url(#arrowhead)')
link.append("title")
.text(function (d) {return d.type;});
edgepaths = svg.selectAll(".edgepath")
.data(links)
.enter()
.append('path')
.attrs({
'class': 'edgepath',
'fill-opacity': 0,
'stroke-opacity': 0,
'id': function (d, i) {return 'edgepath' + d.type}
})
.style("pointer-events", "none");
edgelabels = svg.selectAll(".edgelabel")
.data(links)
.enter()
.append('text')
.style("pointer-events", "none")
.attrs({
'class': 'edgelabel',
'id': function (d, i) {return 'edgelabel' + i},
'font-size': 15,
'fill': '#000'
});
edgelabels.append('textPath')
.attr('xlink:href', function (d, i) {return '#edgepath' + i})
.style("text-anchor", "middle")
.style("pointer-events", "none")
.attr("startOffset", "50%")
.text(function (d) {return d.type});
node = svg.selectAll(".node")
.data(nodes)
.enter()
.append("g")
.attr("class", "node")
.attr('cx', function(d) { return d.x; })
.attr('cy', function(d) { return d.y; })
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
//.on("end", dragended)
);
node.append("circle")
.attr("r", 15)
.style("fill", function (d, i) {return colors(i);})
node.append("title")
.text(function (d) {return d.social;});
node.append("text")
.attr("dy", -3)
.text(function (d) {return d.name+":"+d.social;});
simulation
.nodes(nodes)
.on("tick", ticked);
simulation.force("link")
.links(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 + ")";});
edgepaths.attr('d', function (d) {
return 'M ' + d.source.x + ' ' + d.source.y + ' L ' + d.target.x + ' ' + d.target.y;
});
}
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(1.0).restart()
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
gridX = round(Math.max(r, Math.min(width - r, d.fx)), resolution);
gridY = round(Math.max(r, Math.min(height - r, d.fy)), resolution);
d3.select(this).attr('cx', d.fx = gridX).attr('cy', d.fy = gridY);
}
function round(p, n) {
return p % n < n / 2 ? p - (p % n) : p + n - (p % n);
}
</script>
</body>
</html>
Here I attach the data of the json document that hosts the directed
graph data:
{
"nodes": [
{
"name": "1",
"label": "1",
"social": "I",
"id": 1,
"x": 150,
"y": 450
},
{
"name": "2",
"label": "2",
"social": "G",
"id": 1,
"x": 300,
"y": 150
},
{
"name": "3",
"label": "3",
"social": "T",
"id": 1,
"x": 450,
"y": 300
}
],
"links": [
{
"source": 1,
"target": 2,
"type": "N:1"
},
{
"source": 2,
"target": 3,
"type": "1:N"
}
]
}
此代码无效:
node = svg.selectAll(".node")
.data(nodes)
.enter()
.append("g")
.attr("class", "node")
.attr('cx', function(d) { return d.x; })
.attr('cy', function(d) { return d.y; })
...
替换为:
node = svg.selectAll(".node")
.data(nodes)
.enter()
.append('g')
.classed('node', true)
.attr('transform', d => `translate(${d.x},${d.y})`)
...
元素没有 cx 和 cy 属性,它使用变换反而。此外,d3 classed 例程比 attr('class')
.
更有用
UPD:新创建的链接缺少初始坐标:
link = svg.selectAll(".link")
.data(links)
.enter()
.append("line")
.classed("link", true)
.attr('marker-end','url(#arrowhead)')
.attr("x1", d => d.source.x)
.attr("x2", d => d.target.x)
.attr("y1", d => d.source.y)
.attr("y1", d => d.target.y)
我试图让定向网络的节点在页面启动时看起来位于 json 文档中反映的坐标处。但是,实际代码重新计算了节点位置,这不符合我的应用程序的要求。有帮助吗?
主要代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style type="text/css">
.node {}
.link {
stroke: #999;
stroke-opacity: .6;
stroke-width: 1px;
}
line {
stroke: rgb(212, 212, 212);
stroke-width: 1px;
shape-rendering: crispEdges;
}
svg {
box-sizing: border-box;
border: 1px solid rgb(212, 212, 212);
}
</style>
</head>
<body>
<svg width="960" height="600"></svg>
<script src="https://d3js.org/d3.v4.min.js" type="text/javascript"></script>
<script src="https://d3js.org/d3-selection-multi.v1.js"></script>
<script type="text/javascript">
var width = 960,
height = 500,
resolution = 150,
r = 15;
var colors = d3.scaleOrdinal(d3.schemeCategory10);
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
node,
link;
svg.selectAll('.vertical')
.data(d3.range(1, width / resolution))
.enter().append('line')
.attr('class', 'vertical')
.attr('x1', function(d) { return d * resolution; })
.attr('y1', 0)
.attr('x2', function(d) { return d * resolution; })
.attr('y2', height);
svg.selectAll('.horizontal')
.data(d3.range(1, height / resolution))
.enter().append('line')
.attr('class', 'horizontal')
.attr('x1', 0)
.attr('y1', function(d) { return d * resolution; })
.attr('x2', width)
.attr('y2', function(d) { return d * resolution; });
svg.append('defs').append('marker')
.attrs({'id':'arrowhead',
'viewBox':'-0 -5 10 10',
'refX':13,
'refY':0,
'orient':'auto',
'markerWidth':13,
'markerHeight':13,
'xoverflow':'visible'})
.append('svg:path')
.attr('d', 'M 0,-5 L 10 ,0 L 0,5')
.attr('fill', '#999')
.style('stroke','none');
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function (d) {return d.id;}).distance(150).strength(1))
//.force("charge", d3.forceManyBody())
//.force("center", d3.forceCenter(width / 2, height / 2));
d3.json("graph.json", function (error, graph) {
if (error) throw error;
update(graph.links, graph.nodes);
})
function update(links, nodes) {
link = svg.selectAll(".link")
.data(links)
.enter()
.append("line")
.attr("class", "link")
.attr('marker-end','url(#arrowhead)')
link.append("title")
.text(function (d) {return d.type;});
edgepaths = svg.selectAll(".edgepath")
.data(links)
.enter()
.append('path')
.attrs({
'class': 'edgepath',
'fill-opacity': 0,
'stroke-opacity': 0,
'id': function (d, i) {return 'edgepath' + d.type}
})
.style("pointer-events", "none");
edgelabels = svg.selectAll(".edgelabel")
.data(links)
.enter()
.append('text')
.style("pointer-events", "none")
.attrs({
'class': 'edgelabel',
'id': function (d, i) {return 'edgelabel' + i},
'font-size': 15,
'fill': '#000'
});
edgelabels.append('textPath')
.attr('xlink:href', function (d, i) {return '#edgepath' + i})
.style("text-anchor", "middle")
.style("pointer-events", "none")
.attr("startOffset", "50%")
.text(function (d) {return d.type});
node = svg.selectAll(".node")
.data(nodes)
.enter()
.append("g")
.attr("class", "node")
.attr('cx', function(d) { return d.x; })
.attr('cy', function(d) { return d.y; })
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
//.on("end", dragended)
);
node.append("circle")
.attr("r", 15)
.style("fill", function (d, i) {return colors(i);})
node.append("title")
.text(function (d) {return d.social;});
node.append("text")
.attr("dy", -3)
.text(function (d) {return d.name+":"+d.social;});
simulation
.nodes(nodes)
.on("tick", ticked);
simulation.force("link")
.links(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 + ")";});
edgepaths.attr('d', function (d) {
return 'M ' + d.source.x + ' ' + d.source.y + ' L ' + d.target.x + ' ' + d.target.y;
});
}
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(1.0).restart()
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
gridX = round(Math.max(r, Math.min(width - r, d.fx)), resolution);
gridY = round(Math.max(r, Math.min(height - r, d.fy)), resolution);
d3.select(this).attr('cx', d.fx = gridX).attr('cy', d.fy = gridY);
}
function round(p, n) {
return p % n < n / 2 ? p - (p % n) : p + n - (p % n);
}
</script>
</body>
</html>
Here I attach the data of the json document that hosts the directed graph data:
{
"nodes": [
{
"name": "1",
"label": "1",
"social": "I",
"id": 1,
"x": 150,
"y": 450
},
{
"name": "2",
"label": "2",
"social": "G",
"id": 1,
"x": 300,
"y": 150
},
{
"name": "3",
"label": "3",
"social": "T",
"id": 1,
"x": 450,
"y": 300
}
],
"links": [
{
"source": 1,
"target": 2,
"type": "N:1"
},
{
"source": 2,
"target": 3,
"type": "1:N"
}
]
}
此代码无效:
node = svg.selectAll(".node")
.data(nodes)
.enter()
.append("g")
.attr("class", "node")
.attr('cx', function(d) { return d.x; })
.attr('cy', function(d) { return d.y; })
...
替换为:
node = svg.selectAll(".node")
.data(nodes)
.enter()
.append('g')
.classed('node', true)
.attr('transform', d => `translate(${d.x},${d.y})`)
...
attr('class')
.
UPD:新创建的链接缺少初始坐标:
link = svg.selectAll(".link")
.data(links)
.enter()
.append("line")
.classed("link", true)
.attr('marker-end','url(#arrowhead)')
.attr("x1", d => d.source.x)
.attr("x2", d => d.target.x)
.attr("y1", d => d.source.y)
.attr("y1", d => d.target.y)