gpt4 book ai didi

javascript - "Hard"d3,js力向图中的节点过滤

转载 作者:行者123 更新时间:2023-11-30 14:30:48 25 4
gpt4 key购买 nike

我有一个力导向图,其中有(大量)节点和它们之间的许多链接。我想以交互方式应用过滤器,以便仅保留一部分节点(以及它们之间的任何链接)。但是,由于图形很大,我想从模拟中删除任何被过滤掉的节点,而不是仅仅隐藏它们(这样生成的图形具有更好的性能)。因此,我希望创建一个过滤后的节点列表作为一个新数组,并仅使用这些节点重新初始化模拟。同样适用于边缘——我还没有这样做,但我可能需要以编程方式确定要保留哪些边缘,并在使用新节点 边缘重新初始化图形之前进行相同类型的过滤.我想保留原始节点/边缘数组以允许“重置”回到起始状态。

我已经整理了一个简单的例子,此时它只是在做一些硬编码过滤,但我正在努力使用过滤后的数组重新初始化模拟。它似乎正在从模拟中删除过滤后的节点(圆圈“三”变得不再可拖动)但它仍然显示在渲染图中。

不过,到目前为止,我对“重置”逻辑的尝试似乎确实奏效了。

我做错了什么?有没有更好的方法来实现这一目标? (d3.js v3)

我的示例代码如下:

var links = [{
source: 0,
target: 1,
type: "c"
},
{
source: 1,
target: 2,
type: "d"
},
{
source: 2,
target: 0,
type: "d"
}
];

var nodes = [{
name: "one",
type: "a"
},
{
name: "two",
type: "a"
},
{
name: "three",
type: "b"
}
];

var width = 300;
var height = 300;

var force = d3.layout.force()
.nodes(nodes)
.links(links)
.size([width, height])
.linkDistance(200)
.charge(-400)
.on("tick", tick)
.start();

var svg = d3.select("#graph").append("svg")
.attr("width", width)
.attr("height", height);

function colours(n) {
var colours = ["#3366cc", "#dc3912", "#ff9900", "#109618", "#990099", "#0099c6", "#dd4477", "#66aa00", "#b82e2e", "#316395",
"#994499", "#22aa99", "#aaaa11", "#6633cc", "#e67300", "#8b0707", "#651067", "#329262", "#5574a6", "#3b3eac"
];
return colours[n % colours.length];
}

var path = svg.append("g").selectAll("path")
.data(force.links())
.enter().append("line")
.attr('class', 'link')
.attr('stroke', function(d, i) {
return colours(i);
})

var circles = svg.append("g");
var circle = circles.selectAll("circle")
.data(force.nodes())
.enter().append("circle")
.attr("r", 8)
.attr('class', 'circle')
.attr('fill', function(d, i) {
return colours(i + 3);
})
.call(force.drag);

var text = svg.append("g").selectAll("text")
.data(force.nodes())
.enter().append("text")
.attr("x", 14)
.attr("y", ".31em")
.text(function(d) {
return d.name;
});

function tick() {
path.attr({
x1: function(d) {
return d.source.x;
},
y1: function(d) {
return d.source.y;
},
x2: function(d) {
return d.target.x;
},
y2: function(d) {
return d.target.y;
}
});
circle.attr("transform", transform);
text.attr("transform", transform);
}

function transform(d) {
return "translate(" + d.x + "," + d.y + ")";
}

var nodeText = "";

function nodeTypeA(node) {
return (node.type == "a");
}

function linkTypeC(link) {
return (link.type == "c");
}

function applyFilter() {
force.nodes(nodes.filter(nodeTypeA));
force.links(links.filter(linkTypeC));
circle.data(force.nodes());
text.data(force.nodes());
path.data(force.links());
d3.selectAll("circle").each(
function(d) {
console.log(d.name);
}
);
console.log("");
}

function resetFilter() {
force.nodes(nodes);
force.links(links);
circle.data(force.nodes());
text.data(force.nodes());
path.data(force.links());
d3.selectAll("circle").each(
function(d) {
console.log(d.name);
}
)
console.log("");
}
#buttons {
position: absolute;
top: 10px;
left: 20px;
height: 100px;
width: 400px;
z-index: 99;
}

#graph {
position: absolute;
top: 50px;
left: 20px;
height: 300px;
width: 300px;
z-index: 98;
}
<script src="https://d3js.org/d3.v3.min.js"></script>
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<div id="root">
<div id="buttons">
<button id="filter" onclick="applyFilter()">Apply</button>
<button id="reset" onclick="resetFilter()">Reset</button>
</div>
<div id="graph">
</div>
</div>
</body>
<div id="node_details">
</div>
</body>

最佳答案

对于您的过滤器和重置功能,您更新选择的数据但不使用进入或退出选择来添加/删除新元素。

您在最初附加元素时确实使用了输入选择,例如:

var path = svg.append("g").selectAll("path")
.data(force.links())
.enter().append("line")
...

但是更新时你只需使用:

path.data(force.links());

您仍然需要指定要添加的内容以及添加方式,就像最初添加节点时所做的那样。

要退出节点非常简单,我们只需在应用过滤器时将 .exit().remove() 添加到上面的行。 .exit() 选择选择中不再具有数据数组中对应项的元素。 .remove() 只是将它们从 DOM 中移除:

var links = [
{source: 0, target: 1, type: "c"},
{source: 1, target: 2, type: "d"},
{source: 2, target: 0, type: "d"}
];

var nodes = [
{name: "one", type: "a"},
{name: "two", type: "a"},
{name: "three", type: "b"}
];


var width = 300;
var height = 300;

var force = d3.layout.force()
.nodes(nodes)
.links(links)
.size([width, height])
.linkDistance(200)
.charge(-400)
.on("tick", tick)
.start();

var svg = d3.select("#graph").append("svg")
.attr("width", width)
.attr("height", height);

function colours(n) {
var colours = ["#3366cc", "#dc3912", "#ff9900", "#109618", "#990099", "#0099c6", "#dd4477", "#66aa00", "#b82e2e", "#316395",
"#994499", "#22aa99", "#aaaa11", "#6633cc", "#e67300", "#8b0707", "#651067", "#329262", "#5574a6", "#3b3eac"];
return colours[n % colours.length];
}

var path = svg.append("g").selectAll("path")
.data(force.links())
.enter().append("line")
.attr('class', 'link')
.attr('stroke', function(d, i) {
return colours(i);
})

var circles = svg.append("g");
var circle = circles.selectAll("circle")
.data(force.nodes())
.enter().append("circle")
.attr("r", 8)
.attr('class', 'circle')
.attr('fill', function(d, i) {
return colours(i + 3);
})
.call(force.drag);

var text = svg.append("g").selectAll("text")
.data(force.nodes())
.enter().append("text")
.attr("x", 14)
.attr("y", ".31em")
.text(function(d) {
return d.name;
});

function tick() {
path.attr({
x1: function(d) {
return d.source.x;
},
y1: function(d) {
return d.source.y;
},
x2: function(d) {
return d.target.x;
},
y2: function(d) {
return d.target.y;
}
});
circle.attr("transform", transform);
text.attr("transform", transform);
}

function transform(d) {
return "translate(" + d.x + "," + d.y + ")";
}

var nodeText = "";

function nodeTypeA(node) {
return (node.type == "a");
}

function linkTypeC(link) {
return (link.type == "c");
}

function applyFilter() {
force.nodes(nodes.filter(nodeTypeA));
force.links(links.filter(linkTypeC));

circle.data(force.nodes()).exit().remove();
text.data(force.nodes()).exit().remove();
path.data(force.links()).exit().remove();

d3.selectAll("circle").each(
function(d) {
console.log(d.name);
}
);
console.log("");
}

function resetFilter() {
force.nodes(nodes);
force.links(links);
circle.data(force.nodes());
text.data(force.nodes());
path.data(force.links());
d3.selectAll("circle").each(
function(d) {
console.log(d.name);
}
)
console.log("");
}
#buttons {
position: absolute;
top: 10px;
left: 20px;
height: 100px;
width: 400px;
z-index: 99;
}

#graph {
position: absolute;
top: 50px;
left: 20px;
height: 300px;
width: 300px;
z-index: 98;
}
<div id="root">
<div id="buttons">
<button id="filter" onclick="applyFilter()">Apply</button>
<button id="reset" onclick="resetFilter()">Reset</button>
</div>
<div id="graph">
</div>
</div>
</body>
<script src="https://d3js.org/d3.v3.min.js"></script>

我们可以复制您在重置函数中用于初始输入以输入元素的代码(以及一些小的修改),但这有点重复 - 我们将有两部分代码执行相同的操作。

相反,让我们将进入和退出放入一个更新函数中。更新函数将从力布局中获取节点和链接,并根据需要进入/退出:

var links = [
{source: 0, target: 1, type: "c"},
{source: 1, target: 2, type: "d"},
{source: 2, target: 0, type: "d"}
];

var nodes = [
{name: "one", type: "a"},
{name: "two", type: "a"},
{name: "three", type: "b"}
];


var width = 300;
var height = 300;

var force = d3.layout.force()
.nodes(nodes)
.links(links)
.size([width, height])
.linkDistance(200)
.charge(-400)
.on("tick", tick)
.start();

var svg = d3.select("#graph").append("svg")
.attr("width", width)
.attr("height", height);

function colours(n) {
var colours = ["#3366cc", "#dc3912", "#ff9900", "#109618", "#990099", "#0099c6", "#dd4477", "#66aa00", "#b82e2e", "#316395",
"#994499", "#22aa99", "#aaaa11", "#6633cc", "#e67300", "#8b0707", "#651067", "#329262", "#5574a6", "#3b3eac"];
return colours[n % colours.length];
}



var paths = svg.append("g");
var circles = svg.append("g");
var texts = svg.append("g");

update();

function tick() {
paths.selectAll("line").attr({
x1: function(d) {
return d.source.x;
},
y1: function(d) {
return d.source.y;
},
x2: function(d) {
return d.target.x;
},
y2: function(d) {
return d.target.y;
}
});
circles.selectAll("circle").attr("transform", transform);
texts.selectAll("text").attr("transform", transform);
}

function transform(d) {
return "translate(" + d.x + "," + d.y + ")";
}

var nodeText = "";

function nodeTypeA(node) {
return (node.type == "a");
}

function linkTypeC(link) {
return (link.type == "c");
}

function applyFilter() {
force.nodes(nodes.filter(nodeTypeA));
force.links(links.filter(linkTypeC));

update();
}
function resetFilter() {
force.nodes(nodes);
force.links(links);

update();
force.start(); // start the force layout again.
}

function update() {

// update the data for the lines:
var path = paths.selectAll("line")
.data(force.links());

// enter new lines:
path.enter().append("line")
.attr('class', 'link')
.attr('stroke', function(d, i) {
return colours(i);
})

// exit unneeded lines:
path.exit().transition().style("opacity",0).remove();

// update the data for the circles:
var circle = circles.selectAll("circle")
.data(force.nodes());

// enter new circles:
circle.enter().append("circle")
.attr("r", 8)
.attr('class', 'circle')
.attr('fill', function(d, i) {
return colours(i + 3);
})
.call(force.drag);

// remove unneeded circles:
circle.exit().transition().style("opacity",0).remove();

// update the text data:
var text = texts.selectAll("text")
.data(force.nodes());

// enter new text
text.enter().append("text")
.attr("x", 14)
.attr("y", ".31em")
.text(function(d) {
return d.name;
});

// exit old text:
text.exit().transition().style("opacity",0).remove();
}
#buttons {
position: absolute;
top: 10px;
left: 20px;
height: 100px;
width: 400px;
z-index: 99;
}

#graph {
position: absolute;
top: 50px;
left: 20px;
height: 300px;
width: 300px;
z-index: 98;
}
<script src="https://d3js.org/d3.v3.min.js"></script>
<div id="root">
<div id="buttons">
<button id="filter" onclick="applyFilter()">Apply</button>
<button id="reset" onclick="resetFilter()">Reset</button>
</div>
<div id="graph">
</div>
</div>
</body>



<div id="node_details">
</div>

与原来相比的变化:过滤器和重置函数在设置力的节点和链接后调用更新函数(并且节点最初是用更新函数绘制的)。添加节点时会重置力(重新激活模拟,就好像它已经冷却一样,不会调用刻度,节点也不会正确定位)。

最后,文本、圆圈和线条在名为 texts circleslines< 的父 g 选择中分别。 tick 函数已被修改为在每次 tick 时重新选择每个父 g 的子级 - 尽管您可以以不同的方式优化它。

作为最后一点,可能值得查看为数据指定标识符或在数据中指定节点/链接属性 - 当删除/添加链接/节点着色和/或按索引设置属性时可能会出现问题。

关于javascript - "Hard"d3,js力向图中的节点过滤,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51200038/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com