Dans ce dernier chapitre je vais vous présenter deux fonctionnalités que je trouve intéressantes
- Les
hierarchicals layouts
qui permettent de créer des graphes de manière ordonnée. - Les
clusters
qui permettent de créer des groupes de noeuds.
Les placements hiérarchiques
Les herarchicals layouts qu’on peut traduire par placements hiérarchiques permettent d’ordonner des noeuds de droite à gauche ou de haut en bas.
Cela permet de gérer pas mal de représentations différentes par exemple un arbre généalogique, une hiérarchie sociale ou encore une liste d’instructions.
Pour les activer il faut personnaliser les options layout
de notre graphe, voici les plus importantes :
hierarchical.enabled
: Définit si l’on utilise ou pas les placements hiérarchiques.-
hierarchical.direction
: Sens dans lequel est ordonnée le graphe.UD
: De haut en bas (up-down).DU
: De bas en haut (down-up).LR
: De gauche à droite (left-right).RL
: De droite à gauche (right-left).
-
hierarchical.sortMethod
: Méthode de tri du graphe.hubsize
: Met en premier le noeud avec le plus de lien.directed
: Ordonné selon l’ordre des noeuds (A->B = A puis B)
Il y a également beaucoup de paramètres qui permettent de gérer l’espacement entre les noeuds à découvrir dans la doc.
On peut définir la propriété level
des noeuds par un nombre qui déterminera son niveau de placement dans la hiérarchie.
L’ajout du level est optionnel, s’il n’y a pas de level de déterminé, c’est la propriété directed
qui déterminera la manière dont les noeuds seront placés..
Voici un exemple de création d’un graphe positionné de gauche à droite :
let nodes = new vis.DataSet([]);
let edges = new vis.DataSet([]);
let container = document.getElementById('graphe');
let data = {
nodes: nodes,
edges: edges
};
let options = {
layout: {
hierarchical : {
enabled: true,
direction : 'LR',
sortMethod : 'hubsize'
}
}
};
let network = new vis.Network(container, data, options);
nodes.add(
[
{
id: 1,
label: "A",
level: 1
},
{
id: 2,
label: "B",
level: 2
},
{
id: 3,
label: "C",
level: 2
},
{
id: 4,
label: "D",
level: 3
},
{
id: 5,
label: "E",
level: 3
}
]
);
edges.add(
[
{
from: 1,
to: 2
},
{
from: 1,
to: 3
},
{
from: 2,
to: 4
},
{
from: 2,
to: 5
},
{
from: 3,
to: 5
}
]
);
Les clusters
Les clusters sont des groupes de noeuds, ils permettent de regrouper plusieurs noeuds en un seul à partir de leurs propriétés, par exemple tous les noeuds de couleur verte réunis en un seul.
Pour faire un cluster on utilise la méthode cluster()
de notre network, cette méthode prend en paramètre un objet contenant :
-
joinCondition(...)
: Seul paramètre indispensable, cette fonction permet de définir la ou les conditions qui définissent si le noeud sera groupé avec les autres, elle a pour arguments :nodeOptions
: Propriétés du noeud.- Retour : Condition de validité du regroupement (voir exemple).
-
processProperties(...)
: Méthode appelé avant l’affichage, elle permet de mettre à jour les propriétés du cluster en fonction des éléments qu’il contient, par exemple la taille du noeud par rapport au nombre de noeuds qu’il contient, cette méthode a pour arguments :clusterOptions
: Propriétés du cluster.childNodesOptions
: Propriétés des noeuds contenus dans le cluster.childEdgesOptions
: Propriétés des liens contenus dans le cluster.- Retour : Propriétés du cluster mises à jour.
clusterNodeProperties
: Objet permettant de personnaliser les options du noeud clusterisé, par exemple pour afficher une couleur ou un label personnalisé.clusterEdgeProperties
: Objet permettant de personnaliser les options des liens du cluster.
Voici un exemple où les noeuds de niveau 2 sont regroupés dans un cluster ayant pour label "Mon cluster".
let clusterOptionsByData = {
joinCondition: function (nodeOptions)
{
return nodeOptions.level === 2; // Sélection des noeuds dont le level est 2
},
clusterNodeProperties: {id: 'cluster', label:'Mon cluster'} // Propriété de notre noeud de cluster
};
network.cluster(clusterOptionsByData);
Je vous présente un second exemple dans lequel allons reprendre notre exemple précédent mais en y ajoutant deux choses :
- On va gérer la clusterisation et la déclusterisation via des boutons.
- On va afficher le nombre de noeuds contenu dans notre cluster (via la méthode
processProperties
).
Code HTML des boutons :
<br/>
<button type="button" onclick="cluster();">Grouper</button>
<button type="button" onclick="uncluster();">Dégrouper</button>
Code Javascript :
// Action sur le bouton "Grouper", active la clustersation des noeuds de niveau 2
function cluster()
{
let clusterOptionsByData = {
joinCondition: function (nodeOptions)
{
return nodeOptions.level === 2; // Sélection des noeuds dont le level est 2
},
processProperties: function (clusterOptions, childNodes, childEdges)
{
let nbNodes = childNodes.length; // Récupération du nombre de noeuds de notre cluster
clusterOptions.label = 'Mon cluster (' + nbNodes + ')'; // Mise à jour du label de notre cluster
return clusterOptions; // On retourne les options de notre cluster
},
clusterNodeProperties: {id: 'cluster', label:'Mon cluster'} // Propriétés de notre noeud de cluster
};
network.cluster(clusterOptionsByData);
}
// Action sur le bouton "Dégrouper", désactive la clustersation, en mettant à jour les données du graphe
function uncluster()
{
network.setData(data);
}
Si vous ajoutez d’autres noeuds au niveau 2 de votre graphe (level:2
) vous verrez que le nombre affiché lors de la clusterisation se mettre à jour selon le nombre de noeuds contenus dans le groupe.
Il y existe également des types de clusterisation particuliers comme par exemple le clusterByConnection
qui permet de créer un groupe à partir de tous les noeuds connecté au noeud cible, vous retrouverez comment les utiliser dans la doc.
Exercice
Nous terminons ce chapitre par le traditionnel exercice qui vous permettra de mettre en pratique les placements hiérarchiques et les clusters.
Dans ce chapitre nous allons créer un graphe généré aléatoirement, le but va être de le trier en le hiérarchisant et en regroupant les noeuds.
Nous allons générer un graphe de 20 noeuds avec 7 couleurs attribuées aléatoirement et 30 liens placés eux aussi aléatoirement entre ces noeuds.
Le but de l’exercice va être de réaliser ces trois boutons :
-
Grouper : Lance un cluster des noeuds selon leur couleur :
- Mettre le groupe de noeuds à la couleur cible avec pour label le nom de la couleur.
- Afficher entre parenthèses le nombre de noeuds du groupe.
- Modifier la taille des groupes selon leur nombre de noeuds (10 de base avec +3 par noeud regroupé).
- Hiérarchiser : Hiérarchise les noeuds selon leur couleur (de haut en bas, avec une ligne par couleur).
- Ré initialiser : Ré-initialise le graph à son état initiale.
Je vous donne le code HTML de nos boutons :
<button type="button" onclick="grouper();">Grouper</button>
<button type="button" onclick="hierarchiser();">Hiérarchiser</button>
<button type="button" onclick="reinit();">Ré initialiser</button>
Je vous donne également le code javascript de génération du graph :
let nodes = new vis.DataSet([]);
let edges = new vis.DataSet([]);
let container = document.getElementById('graphe');
// Initialisation du graphe
let data = {
nodes: nodes,
edges: edges
};
let options = {};
let network = new vis.Network(container, data, options);
// Définition du nombre de noeuds et de liens à générer
let nbNodes = 20;
let nbEdges = 30;
// Définition du tableau des couleurs et des noms de celle-ci (pour les labels des groupes)
let colors = [
"#f99fa6", // Rouge
"#ffc389", // Orange
"#8fea96", // Vert
"#8587e5", // Bleu
"#eeadef", // Violet
"#dfe585", // Jaune
"#85d4e5" // Cyan
];
let colorsName = ["Rouge", "Orange", "Vert", "Bleu", "Violet", "Jaune", "Cyan"];
// Génération de nos noeuds
for(let i = 0; i < nbNodes; i++)
{
// Génération aléatoire de la couleur
let numColor = Math.floor(Math.random() * (colors.length - 0) + 0);
let color = colors[numColor];
// Création du noeud
nodes.add(
[
{
id: i,
label: "N"+i,
color: color
}
]);
}
// Génération de nos liens
for(let i = 0; i < nbEdges; i++)
{
let n1 = Math.floor(Math.random() * (nbNodes - 0) + 0); // Génération aléatoire de l'id du premier noeud
let n2 = Math.floor(Math.random() * (nbNodes - 0) + 0); // Génération aléatoire de l'id du second noeud
if(n1 != n2) // Ne pas prendre en compte les noeuds liés à eux-mêmes
{
// Ajout du lien entre les deux noeuds
edges.add(
[
{
from: n1,
to: n2
}
]);
}
else
{
i--;
}
}
Lors de la clusterisation, votre graphe doit resembler à ceci :
Lors de la hiérarchisation,, vous devriez obtenir un résultat qui ressembler à celui-ci :
Si vous êtes bloqué, voici la solution :
Code HTML
<!doctype html>
<html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<head>
<title>Tuto VisNetwork</title>
<style>
#graphe
{
display: inline-block;
width: 800px;
height: 600px;
background-color: #f0f0f0;
}
</style>
<script type="text/javascript" src="vis.min.js"></script>
</head>
<body>|
<div id="graphe"></div>
</br>
<button type="button" onclick="grouper();">Grouper</button>
<button type="button" onclick="hierarchiser();">Hiérarchiser</button>
<button type="button" onclick="reinit();">Ré initialiser</button>
<script type="text/javascript" src="graph.js"></script>
</body>
</html>
Code Javascript :
let nodes = new vis.DataSet([]);
let edges = new vis.DataSet([]);
let container = document.getElementById('graphe');
// Initialisation du graphe
let data = {
nodes: nodes,
edges: edges
};
let options = {};
let network = new vis.Network(container, data, options);
// Définition du nombre de noeuds et de liens à générer
let nbNodes = 20;
let nbEdges = 30;
// Définition du tableau des couleurs et des noms de celle-ci (pour les labels des groupes)
let colors = [
"#f99fa6", // Rouge
"#ffc389", // Orange
"#8fea96", // Vert
"#8587e5", // Bleu
"#eeadef", // Violet
"#dfe585", // Jaune
"#85d4e5" // Cyan
];
let colorsName = ["Rouge", "Orange", "Vert", "Bleu", "Violet", "Jaune", "Cyan"];
// Génération de nos noeuds
for(let i = 0; i < nbNodes; i++)
{
// Génération aléatoire de la couleur
let numColor = Math.floor(Math.random() * (colors.length - 0) + 0);
let color = colors[numColor];
// Création du noeud
nodes.add(
[
{
id: i,
label: "N"+i,
color: color,
level: numColor
}
]);
}
// Génération de nos liens
for(let i = 0; i < nbEdges; i++)
{
let n1 = Math.floor(Math.random() * (nbNodes - 0) + 0); // Génération aléatoire de l'id du premier noeud
let n2 = Math.floor(Math.random() * (nbNodes - 0) + 0); // Génération aléatoire de l'id du second noeud
if(n1 != n2) // Ne pas prendre en compte les noeuds liés à eux-mêmes
{
// Ajout du lien entre les deux noeuds
edges.add(
[
{
from: n1,
to: n2
}
]);
}
else
{
i--;
}
}
// Action de "Clustering" des noeuds
function grouper()
{
// Parcours nos couleurs afin de créer un cluster par couleur
for (let i = 0; i < colors.length; i++)
{
let clusterOptionsByData = {
joinCondition: function (nodeOptions)
{
return nodeOptions.color.background === colors[i]; // Sélection des noeuds de la couleur cible
},
processProperties: function (clusterOptions, childNodes, childEdges)
{
// Mise à jour de la taille et du label du noeud selon le nombre d'éléments groupés
let size = 10;
for (let j = 0; j < childNodes.length; j++)
{
size += 3;
}
clusterOptions.size = size;
clusterOptions.label = colorsName[i] + " (" + childNodes.length + ")";
return clusterOptions;
},
clusterNodeProperties:
{
// Définition des propriétés de notre noeud de cluster
id: 'cluster_'+colors[i],
shape:'dot',
color:colors[i],
level:i
},
clusterEdgeProperties:
{
// Définition des propriétés des liens de nos clusters
color:{
color : colors[i]
},
width : 3
}
};
network.cluster(clusterOptionsByData);
}
}
// Hiérarchisation du graph
function hierarchiser()
{
// Mise à jour des options (activation de la hiérarchie)
options.layout = {
hierarchical : {
enabled: true,
direction : 'UD'
}
}
// Ré-initialisation du réseau avec les nouvelles options
network = new vis.Network(container, data, options);
}
// Réinitialisation de graphe
function reinit()
{
// Mise à jour des options (retrait de la hiérarchie)
options.layout = {
hierarchical : {
enabled: false
}
}
// Ré-initialisation du réseau avec les nouvelles options
network = new vis.Network(container, data, options);
}
Cette dernière partie est terminée, de nouveaux chapitres viendront peut-être rejoindre le tutoriel plus tard.
Je félicite les plus courageux qui ont été jusqu’au bout de ce petit tutoriel, en espérant qu’il vous a plu