在vis.js网络中,如何根据深度自动设置节点的级别?
In a vis.js network, how to set the level of a node automatically based on its depth?
我正在尝试使用 vis.js 创建层次图。我可以创建层次结构图,但布局不是我要找的:节点位于看似任意的级别上。我希望节点根据它们与根之间的边数处于不同的级别。
数据最初来自SQL。目前,我有一个 Python 脚本将数据处理成 DOT 语言(并且我可以使用 Graphviz 以我想要的布局显示图形),所以这就是我使用 .convertDot 方法的原因。我可以在导入 vis.js 后重新处理网络,并分别为每个节点添加正确的 "level" 属性,但必须有更好的方法。
这是我目前拥有的完整 HTML/JS 文档:
<!DOCTYPE HTML>
<html>
<head>
<script src="./vis/dist/vis.js"></script>
<link href="./vis/dist/vis.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="graph", style="height: 1000px"></div>
<script>
// provide data in the DOT language
var DOTstring = 'digraph {"13332500" -> "13483400" "13567500" -> "13483400" "10037901" -> "10037902" "10037902" -> "13483400" "15038400" -> "13455700" "13455700" -> "13455702" "13455702" -> "13483400" "13567300" -> "13483400" "11890500" -> "13483400" "13483400" -> "13554900"}';
var parsedData = vis.network.convertDot(DOTstring);
var data = {
nodes: parsedData.nodes,
edges: parsedData.edges
}
var container = document.getElementById('graph');
var options = parsedData.options;
// you can extend the options like a normal JSON variable:
options.layout = {
"hierarchical": true
}
// create a network
var network = new vis.Network(container, data, options);
</script>
</body>
</html>
代码将生成以下内容:
这是我正在寻找的布局,由 graphviz 生成:
如果你想知道为什么我在 vis.js 中尝试这样做,而我已经在 graphviz 中工作了:原因是我想让它具有交互性。
类似的内容可能对您有用。这是针对 React 的,但是有用的功能是分开的。
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
graphData: {
nodes: [
{"id": -1, "label": "Licenciatura en Sistemas", "approved": true},
{"id": 0, "label": "Introducción a la Programación"},
{"id": 1, "label": "Introducción a la Matemática"},
{"id": 2, "label": "Taller de Lectura y Escritura"},
{"id": 3, "label": "Programación I"},
{"id": 4, "label": "Lógica y Teoría de Números"},
{"id": 5, "label": "Organización del Computador I"},
{"id": 6, "label": "Programación II"},
{"id": 7, "label": "Álgebra Lineal"},
{"id": 8, "label": "Sistemas Operativos y Redes I"},
{"id": 9, "label": "Programación III"},
{"id": 10, "label": "Cálculo para Computación"},
{"id": 11, "label": "Problemas Socioeconómicos Contemporáneos"},
{"id": 12, "label": "Bases de Datos I"},
{"id": 13, "label": "Matemática Discreta"},
{"id": 14, "label": "Especificaciones y Verificación de Software"},
{"id": 15, "label": "Teoría de la Computación"},
{"id": 16, "label": "Ingeniería de Software I"},
{"id": 17, "label": "Probabilidad y Estadística"},
{"id": 18, "label": "Proyecto Profesional I"},
{"id": 19, "label": "Ingeniería de Software II"},
{"id": 20, "label": "Organización del Computador II"},
{"id": 21, "label": "Proyecto Profesional II"},
{"id": 22, "label": "Bases de Datos II"},
{"id": 23, "label": "Sistemas Operativos y Redes II"},
{"id": 24, "label": "Práctica Profesional Supervisada I"},
{"id": 25, "label": "Modelado y Optimización"},
{"id": 26, "label": "Informática y Sociedad"},
{"id": 27, "label": "Taller de Tesina de Licenciatura"},
{"id": 28, "label": "Gestión de Proyectos"},
{"id": 29, "label": "Laboratorio Interdisciplinario"},
{"id": 30, "label": "Taller de Utilitarios"},
{"id": 31, "label": "Inglés Lectocomprensión I"},
{"id": 32, "label": "Inglés Lectocomprensión II"},
{"id": 33, "label": "Inglés Lectocomprensión III"},
{"id": 34, "label": "TIC Lectura y Lectoescritura"},
{"id": 35, "label": "TIC Ciencias exactas"},
{"id": 36, "label": "TIC Matemáticas"},
],
edges: [
{"from": -1, "to": 29},
{"from": -1, "to": 30},
{"from": -1, "to": 34},
{"from": -1, "to": 35},
{"from": -1, "to": 36},
{"from": 0, "to": 3},
{"from": 0, "to": 5},
{"from": 1, "to": 4},
{"from": 1, "to": 7},
{"from": 1, "to": 10},
{"from": 3, "to": 6},
{"from": 4, "to": 13},
{"from": 4, "to": 12},
{"from": 4, "to": 14},
{"from": 5, "to": 20},
{"from": 5, "to": 8},
{"from": 5, "to": 12},
{"from": 5, "to": 15},
{"from": 6, "to": 9},
{"from": 6, "to": 12},
{"from": 7, "to": 10},
{"from": 8, "to": 23},
{"from": 9, "to": 14},
{"from": 9, "to": 15},
{"from": 9, "to": 16},
{"from": 9, "to": 22},
{"from": 10, "to": 13},
{"from": 10, "to": 17},
{"from": 12, "to": 22},
{"from": 13, "to": 17},
{"from": 14, "to": 18},
{"from": 16, "to": 19},
{"from": 16, "to": 26},
{"from": 16, "to": 18},
{"from": 17, "to": 25},
{"from": 18, "to": 21},
{"from": 19, "to": 28},
{"from": 21, "to": 24},
{"from": 21, "to": 27},
{"from": 31, "to": 32},
{"from": 32, "to": 33},
{"from": 34, "to": 3},
{"from": 34, "to": 11},
{"from": 34, "to": 2},
{"from": 35, "to": 0},
{"from": 35, "to": 1},
{"from": 36, "to": 0},
{"from": 36, "to": 1}
]
}
};
this.calculateLevels(this.state.graphData.nodes, this.state.graphData.edges);
}
calculateLevels(nodes, edges) {
let reverseEdgesMap = new Map();
let nodesMap = new Map();
for (let edge of edges) {
let from = edge.from;
let to = edge.to;
if (reverseEdgesMap.has(to)) {
reverseEdgesMap.get(to).push(from);
} else {
reverseEdgesMap.set(to, [edge.from]);
}
}
for (let node of nodes) {
let id = node.id;
nodesMap.set(id, node);
}
for (let node of nodes) {
node.level = this.calculateMaxNodeLength(nodesMap, reverseEdgesMap, node.id);
}
console.log(nodes);
}
/**
*
* @param nodesMap
* @param reverseEdgesMap
* @param nodeId
* @param parents
* @returns {number}
*/
calculateMaxNodeLength(nodesMap, reverseEdgesMap, nodeId) {
if (!(nodesMap instanceof Map)) {
throw new Error("nodesMap parameter should be an instance of Map")
}
if (!(reverseEdgesMap instanceof Map)) {
throw new Error("reverseEdgesMap parameter should be an instance of Map")
}
let parents = [];
let longestParentDepth = 0;
if (reverseEdgesMap.has(nodeId)) {
parents = reverseEdgesMap.get(nodeId);
for (let parentId of parents) {
let parentDepth = 1;
parentDepth += this.calculateMaxNodeLength(nodesMap, reverseEdgesMap, parentId);
if (parentDepth > longestParentDepth) {
longestParentDepth = parentDepth;
}
}
}
return longestParentDepth;
}
componentDidMount() {
}
render() {
return (
<div className="App">
<VisNetwork nodes={this.state.graphData.nodes} edges={this.state.graphData.edges}/>
</div>
)
}
}
从 vis.js 版本 6.2 开始,有一个新选项允许按需要显示图形。使用 sortMethod: 'directed'
和 shakeTowards: 'roots'
.
来源
- vis-network github issue #176
- vis-network docs for layout options(您必须单击“完整选项”选项卡才能真正看到选项)
例子
在我的原始脚本中修改 options.layout
得到了想要的结果:
<!DOCTYPE HTML>
<html>
<head>
<script src="./vis/dist/vis.js"></script>
<link href="./vis/dist/vis.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="graph", style="height: 1000px"></div>
<script>
// provide data in the DOT language
var DOTstring = 'digraph {"13332500" -> "13483400" "13567500" -> "13483400" "10037901" -> "10037902" "10037902" -> "13483400" "15038400" -> "13455700" "13455700" -> "13455702" "13455702" -> "13483400" "13567300" -> "13483400" "11890500" -> "13483400" "13483400" -> "13554900"}';
var parsedData = vis.network.convertDot(DOTstring);
var data = {
nodes: parsedData.nodes,
edges: parsedData.edges
}
var container = document.getElementById('graph');
var options = parsedData.options;
// you can extend the options like a normal JSON variable:
options.layout = {
hierarchical: {
sortMethod: 'directed', // hubsize, directed
shakeTowards: 'roots', // roots, leaves
direction: 'DU' // UD, DU, LR, RL
}
}
// create a network
var network = new vis.Network(container, data, options);
</script>
</body>
</html>
我正在尝试使用 vis.js 创建层次图。我可以创建层次结构图,但布局不是我要找的:节点位于看似任意的级别上。我希望节点根据它们与根之间的边数处于不同的级别。
数据最初来自SQL。目前,我有一个 Python 脚本将数据处理成 DOT 语言(并且我可以使用 Graphviz 以我想要的布局显示图形),所以这就是我使用 .convertDot 方法的原因。我可以在导入 vis.js 后重新处理网络,并分别为每个节点添加正确的 "level" 属性,但必须有更好的方法。
这是我目前拥有的完整 HTML/JS 文档:
<!DOCTYPE HTML>
<html>
<head>
<script src="./vis/dist/vis.js"></script>
<link href="./vis/dist/vis.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="graph", style="height: 1000px"></div>
<script>
// provide data in the DOT language
var DOTstring = 'digraph {"13332500" -> "13483400" "13567500" -> "13483400" "10037901" -> "10037902" "10037902" -> "13483400" "15038400" -> "13455700" "13455700" -> "13455702" "13455702" -> "13483400" "13567300" -> "13483400" "11890500" -> "13483400" "13483400" -> "13554900"}';
var parsedData = vis.network.convertDot(DOTstring);
var data = {
nodes: parsedData.nodes,
edges: parsedData.edges
}
var container = document.getElementById('graph');
var options = parsedData.options;
// you can extend the options like a normal JSON variable:
options.layout = {
"hierarchical": true
}
// create a network
var network = new vis.Network(container, data, options);
</script>
</body>
</html>
代码将生成以下内容:
如果你想知道为什么我在 vis.js 中尝试这样做,而我已经在 graphviz 中工作了:原因是我想让它具有交互性。
类似的内容可能对您有用。这是针对 React 的,但是有用的功能是分开的。
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
graphData: {
nodes: [
{"id": -1, "label": "Licenciatura en Sistemas", "approved": true},
{"id": 0, "label": "Introducción a la Programación"},
{"id": 1, "label": "Introducción a la Matemática"},
{"id": 2, "label": "Taller de Lectura y Escritura"},
{"id": 3, "label": "Programación I"},
{"id": 4, "label": "Lógica y Teoría de Números"},
{"id": 5, "label": "Organización del Computador I"},
{"id": 6, "label": "Programación II"},
{"id": 7, "label": "Álgebra Lineal"},
{"id": 8, "label": "Sistemas Operativos y Redes I"},
{"id": 9, "label": "Programación III"},
{"id": 10, "label": "Cálculo para Computación"},
{"id": 11, "label": "Problemas Socioeconómicos Contemporáneos"},
{"id": 12, "label": "Bases de Datos I"},
{"id": 13, "label": "Matemática Discreta"},
{"id": 14, "label": "Especificaciones y Verificación de Software"},
{"id": 15, "label": "Teoría de la Computación"},
{"id": 16, "label": "Ingeniería de Software I"},
{"id": 17, "label": "Probabilidad y Estadística"},
{"id": 18, "label": "Proyecto Profesional I"},
{"id": 19, "label": "Ingeniería de Software II"},
{"id": 20, "label": "Organización del Computador II"},
{"id": 21, "label": "Proyecto Profesional II"},
{"id": 22, "label": "Bases de Datos II"},
{"id": 23, "label": "Sistemas Operativos y Redes II"},
{"id": 24, "label": "Práctica Profesional Supervisada I"},
{"id": 25, "label": "Modelado y Optimización"},
{"id": 26, "label": "Informática y Sociedad"},
{"id": 27, "label": "Taller de Tesina de Licenciatura"},
{"id": 28, "label": "Gestión de Proyectos"},
{"id": 29, "label": "Laboratorio Interdisciplinario"},
{"id": 30, "label": "Taller de Utilitarios"},
{"id": 31, "label": "Inglés Lectocomprensión I"},
{"id": 32, "label": "Inglés Lectocomprensión II"},
{"id": 33, "label": "Inglés Lectocomprensión III"},
{"id": 34, "label": "TIC Lectura y Lectoescritura"},
{"id": 35, "label": "TIC Ciencias exactas"},
{"id": 36, "label": "TIC Matemáticas"},
],
edges: [
{"from": -1, "to": 29},
{"from": -1, "to": 30},
{"from": -1, "to": 34},
{"from": -1, "to": 35},
{"from": -1, "to": 36},
{"from": 0, "to": 3},
{"from": 0, "to": 5},
{"from": 1, "to": 4},
{"from": 1, "to": 7},
{"from": 1, "to": 10},
{"from": 3, "to": 6},
{"from": 4, "to": 13},
{"from": 4, "to": 12},
{"from": 4, "to": 14},
{"from": 5, "to": 20},
{"from": 5, "to": 8},
{"from": 5, "to": 12},
{"from": 5, "to": 15},
{"from": 6, "to": 9},
{"from": 6, "to": 12},
{"from": 7, "to": 10},
{"from": 8, "to": 23},
{"from": 9, "to": 14},
{"from": 9, "to": 15},
{"from": 9, "to": 16},
{"from": 9, "to": 22},
{"from": 10, "to": 13},
{"from": 10, "to": 17},
{"from": 12, "to": 22},
{"from": 13, "to": 17},
{"from": 14, "to": 18},
{"from": 16, "to": 19},
{"from": 16, "to": 26},
{"from": 16, "to": 18},
{"from": 17, "to": 25},
{"from": 18, "to": 21},
{"from": 19, "to": 28},
{"from": 21, "to": 24},
{"from": 21, "to": 27},
{"from": 31, "to": 32},
{"from": 32, "to": 33},
{"from": 34, "to": 3},
{"from": 34, "to": 11},
{"from": 34, "to": 2},
{"from": 35, "to": 0},
{"from": 35, "to": 1},
{"from": 36, "to": 0},
{"from": 36, "to": 1}
]
}
};
this.calculateLevels(this.state.graphData.nodes, this.state.graphData.edges);
}
calculateLevels(nodes, edges) {
let reverseEdgesMap = new Map();
let nodesMap = new Map();
for (let edge of edges) {
let from = edge.from;
let to = edge.to;
if (reverseEdgesMap.has(to)) {
reverseEdgesMap.get(to).push(from);
} else {
reverseEdgesMap.set(to, [edge.from]);
}
}
for (let node of nodes) {
let id = node.id;
nodesMap.set(id, node);
}
for (let node of nodes) {
node.level = this.calculateMaxNodeLength(nodesMap, reverseEdgesMap, node.id);
}
console.log(nodes);
}
/**
*
* @param nodesMap
* @param reverseEdgesMap
* @param nodeId
* @param parents
* @returns {number}
*/
calculateMaxNodeLength(nodesMap, reverseEdgesMap, nodeId) {
if (!(nodesMap instanceof Map)) {
throw new Error("nodesMap parameter should be an instance of Map")
}
if (!(reverseEdgesMap instanceof Map)) {
throw new Error("reverseEdgesMap parameter should be an instance of Map")
}
let parents = [];
let longestParentDepth = 0;
if (reverseEdgesMap.has(nodeId)) {
parents = reverseEdgesMap.get(nodeId);
for (let parentId of parents) {
let parentDepth = 1;
parentDepth += this.calculateMaxNodeLength(nodesMap, reverseEdgesMap, parentId);
if (parentDepth > longestParentDepth) {
longestParentDepth = parentDepth;
}
}
}
return longestParentDepth;
}
componentDidMount() {
}
render() {
return (
<div className="App">
<VisNetwork nodes={this.state.graphData.nodes} edges={this.state.graphData.edges}/>
</div>
)
}
}
从 vis.js 版本 6.2 开始,有一个新选项允许按需要显示图形。使用 sortMethod: 'directed'
和 shakeTowards: 'roots'
.
来源
- vis-network github issue #176
- vis-network docs for layout options(您必须单击“完整选项”选项卡才能真正看到选项)
例子
在我的原始脚本中修改 options.layout
得到了想要的结果:
<!DOCTYPE HTML>
<html>
<head>
<script src="./vis/dist/vis.js"></script>
<link href="./vis/dist/vis.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="graph", style="height: 1000px"></div>
<script>
// provide data in the DOT language
var DOTstring = 'digraph {"13332500" -> "13483400" "13567500" -> "13483400" "10037901" -> "10037902" "10037902" -> "13483400" "15038400" -> "13455700" "13455700" -> "13455702" "13455702" -> "13483400" "13567300" -> "13483400" "11890500" -> "13483400" "13483400" -> "13554900"}';
var parsedData = vis.network.convertDot(DOTstring);
var data = {
nodes: parsedData.nodes,
edges: parsedData.edges
}
var container = document.getElementById('graph');
var options = parsedData.options;
// you can extend the options like a normal JSON variable:
options.layout = {
hierarchical: {
sortMethod: 'directed', // hubsize, directed
shakeTowards: 'roots', // roots, leaves
direction: 'DU' // UD, DU, LR, RL
}
}
// create a network
var network = new vis.Network(container, data, options);
</script>
</body>
</html>