Newer
Older
let tree = [];
function findPerson(json, id){
for(personIndex in json){
if(json[personIndex].id == id){
return json[personIndex];
}
}
}
/**
* conversion du json en paramètre en json pour l'outil de graphe généalogique
*/
function createJsonGlobal(json){
let jsonFamily = JSON.parse(JSON.stringify(json)); //clone de l'objet
for(personIndex in jsonFamily){
person = jsonFamily[personIndex];
person.parents = !person.hasOwnProperty('parents') ? new Array() : person.parents;
person.sex = !person.hasOwnProperty('sex') ? "person" : person.sex; // sexe indéfini pour l'instant
person.children = !person.hasOwnProperty('children') ? new Array() : person.children;
person.partners = !person.hasOwnProperty('partners') ? new Array() : person.partners;
if(person.prenom != null){
person.name = person.prenom;
delete person.prenom;
}
if(person.nom != null){
person.name += ' ' + person.nom;
delete person.nom;
}
if(person.mere != null && person.pere != null && person.mere !== "" && person.pere !== ""){
let mere = findPerson(jsonFamily, person.mere);
let pere = findPerson(jsonFamily, person.pere);
mere.partners.indexOf(pere.id) === -1 ? mere.partners.push(pere.id) :1;
pere.partners.indexOf(mere.id) === -1 ? pere.partners.push(mere.id) :1;
}
if(person.mere != null && person.mere !== ""){
let mere = findPerson(jsonFamily, person.mere);
person.parents = !person.hasOwnProperty('parents') ? new Array() : person.parents;
mere.sex = !mere.hasOwnProperty('sex') || mere.sex === "person" ? "female" : mere.sex;
person.parents.indexOf(mere.id) === -1 ? person.parents.push(mere.id) :1;
mere.children = !mere.hasOwnProperty('children') ? new Array() : mere.children;
mere.partners = !mere.hasOwnProperty('partners') ? new Array() : mere.partners;
mere.children.indexOf(person.id) === -1 ? mere.children.push(person.id) :1;
delete person.mere;
}
if(person.pere != null && person.pere !== ""){
let pere = findPerson(jsonFamily, person.pere);
person.parents = !person.hasOwnProperty('parents') ? new Array() : person.parents;
pere.sex = !pere.hasOwnProperty('sex') || pere.sex === "person" ? "male" : pere.sex;
person.parents.indexOf(pere.id) === -1 ? person.parents.push(pere.id) :1;
pere.children = !pere.hasOwnProperty('children') ? new Array() : pere.children;
pere.partners = !pere.hasOwnProperty('partners') ? new Array() : pere.partners;
pere.children.indexOf(person.id) === -1 ? pere.children.push(person.id) :1;
/**
* On crée un graphe à partir des données du JSON "tree". On créé 1 noeud "meta" pour
* pour chaque père/mère - enfant ou noeud partenaire, enfant. Ne représente pas une
* vraie généalogie mais fait une recherche en profondeur pour mettre en ordre les
* personnes "noeuds" plus pertinemment puisqu'on s'assure que tous les enfants sont
* vus avant les parents.
* On mappe un identifiant à l'index correspondant dans l'arbre du JSON.
*/
let horiz_margin = 75;
let vert_margin = 45;
function build_graph() {
for (let t = 0 ; t < tree.length; t++) {
graph[tree[t].id] = {id: tree[t].id,
index: t,
children: [],
partners: [],
parents: [],
layer: 0,
name: tree[t].name};
for (let t = 0 ; t < tree.length; t++) {
let person = tree[t];
let person_node = graph[person.id];
for (let c= 0; c < person.children.length; c++) {
let child_node = graph[person.children[c]];
if(child_node)
{
person_node.children.push(child_node);
child_node.parents.push(person_node);
}
}
for (let p = 0; p < person.partners.length; p++) {
person_node.partners.push(graph[person.partners[p]]);
}
function dfs() {
/* Recherche en profondeur dans le graphe. Chaque noeud est l'id d'une personne.
Retourne un tableau avec les id dans l'ordre où si u est un enfant de v,
u apparaît après v.
*/
let result = [];
let seen = [];
function impl(node) {
seen[node.id] = true;
for (let out = 0; out < node.children.length; out++) {
let child = node.children[out];
if (!seen[child.id]) {
impl(child);
}
for (let id in graph) {
if (!seen[Number(id)]) {
impl(graph[id]);
}
* Retourne le min et max entre A et B
*/
function min(a, b) {
if (a === undefined) {
return b;
return Math.min(a, b);
}
}
function max(a, b) {
if (a === undefined) {
return b;
} else {
return Math.max(a, b);
* On attribue un rang (niveau) à chaque personne.
* On commence par l'enfant et on assigne à chaque personne un niveau
* de telle sorte que la personne est au-dessus de ses enfants.
*/
function rank() {
let nodes = dfs();
/**
* On attribue le niveau 0 aux personnes sans enfants puis on met
* leurs parents au niveau du dessus et ainsi de suite.
*/
if (!node.children.length) {
node.layer = 0;
} else {
// Si le niveau a été défini sur un partenaire :
node.layer = 1;
for (let p = 0; p < node.partners.length; p++) {
if (node.partners[p].layer !== undefined) {
node.layer = node.partners[p].layer;
break;
}
}
for (let c = 0; c < node.children.length; c++) {
node.layer = max(node.layer, node.children[c].layer + 1);
}
}
/**
* Avec l'algo suivant il est possible d'avoir un enfant au niveau 0
* tandis que son parent est au niveau 2 ou plus pour être au niveau
* de son partenaire.
*/
let changed = true;
while (changed) {
changed = false;
for (let id = 0; id < nodes.length; id++) {
let node = nodes[id];
let parents_layer = undefined;
for (let p = 0; p < node.parents.length; p++) {
parents_layer = min(parents_layer, node.parents[p].layer);
}
if (parents_layer !== undefined && parents_layer - 1 > node.layer) {
node.layer = parents_layer - 1;
changed = true;
}
*/
function normalize_layers() {
let max_layer = undefined;
for (let id in graph) {
let node = graph[id];
max_layer = max(max_layer, node.layer);
}
for (let id in graph) {
let node = graph[id];
node.layer = max_layer - node.layer;
/**
* Pour chaque niveau, on liste les personnes présentes à ce niveau
* et on leur attribue une position de gauche à droite dans ce niveau.
*/
function per_layer() {
let layers = [];
for (let id in graph) {
let node = graph[id];
if (!layers[node.layer]) {
layers[node.layer] = [];
layers[node.layer].push(node);
node.pos_in_layer = layers[node.layer].length;
/**
* On trie les noeuds pour chaque niveau
*/
function sort_nodes_in_layers() {
let layers = per_layer();
for (let c = 0; c < layers[l - 1].length; c++) {
let child_node = layers[l - 1][c];
for (let p = 0; p < child_node.parents.length; p++) {
let parent_node = child_node.parents[p];
for (let p = 0; p < child_node.children.length; p++) {
let children = child_node.children[p];
total += children.pos_in_layer;
count ++;
child_node.weight_in_layer = total / count;
}
// on trie les noeuds du niveau
layers[l - 1].sort(
function(c1, c2) { return c1.weight_in_layer < c2.weight_in_layer});
for (let c = 0; c < layers[l - 1].length; c++) {
let child_node = layers[l - 1][c];
child_node.pos_in_layer = c;
}
/**
* Assigne les bonnes coordonnées à chaque noeud de personne
*/
function assign_coordinates() {
for (let id in graph) {
let node = graph[id];
tree[node.index].x = (node_width + horiz_margin) * node.pos_in_layer;
tree[node.index].y = (node_height + vert_margin) * node.layer;
let layers = per_layer();
for (let iteration = 0 ; iteration < 1; iteration++) {
// on calcule en fonction des parents
for (let l = 1; l < layers.length; l++) {
let min_x = 0;
for (let c = 0; c < layers[l].length; c++) {
let child_node = layers[l][c];
let total = 0;
let count = 0;
for (let p = 0; p < child_node.parents.length; p++) {
let parent_node = child_node.parents[p];
total += tree[parent_node.index].x;
count ++;
}
if (count != 0) {
tree[child_node.index].x = max(min_x, total / count);
} else {
tree[child_node.index].x = max(min_x, tree[child_node.index].x)
}
min_x = tree[child_node.index].x + node_width + horiz_margin;
}
// on calcule en fonction des enfants
for (let l = layers.length - 2; l >= 0; l--) {
let min_x = 0;
for (let c = 0; c < layers[l].length; c++) {
let parent_node = layers[l][c];
let total = 0;
let count = 0;
for (let p = 0; p < parent_node.children.length; p++) {
let child_node = parent_node.children[p];
total += tree[child_node.index].x;
count ++;
}
if (count != 0) {
tree[parent_node.index].x = max(min_x, total / count);
} else {
tree[parent_node.index].x = max(min_x, tree[parent_node.index].x);
}
min_x = tree[parent_node.index].x + node_width + horiz_margin;
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
/**
* Normalise les coordonnées, pour que l'enfant le plus à gauche soit à x=0
* Ensuite décale les personnes de pour faire un effet d'arbre généalogique.
*/
let min_x = undefined;
for (let l = 0; l < layers.length; l++) {
min_x = min(min_x, tree[layers[l][0].index].x);
}
for (let id in graph) {
tree[graph[id].index].x -= min_x;
}
let x = 0;
for(let i in layers){
for(let j in layers[i]){
tree[layers[i][j].index].x += x*node_width;
}
x++;
}
let min_xx = undefined;
for (let l = 0; l < layers.length; l++) {
min_xx = min(min_xx, tree[layers[l][0].index].x);
}
for (let id in graph) {
tree[graph[id].index].x -= min_xx;
}
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
* Va à la ligne pour tous les noms des personnes si le nom ou prénom est supérieur à la taille "width".
*/
function wrap(text, width) {
text.each(function() {
let text = d3.select(this),
words = text.text().split(/\s+/).reverse(),
word,
line = [],
lineNumber = 0,
lineHeight = 1.1, // ems
x = text.attr("x"),
y = text.attr("y"),
dy = 1.1,
tspan = text.text(null).append("tspan").attr("x", x).attr("y", y).attr("dy", dy + "em");
words = words.reverse();
for (word of words) {
line.push(word);
tspan.text(line.join(" "));
if (tspan.node().getComputedTextLength() > width) {
line.pop();
tspan.text(line.join(" "));
line = [word];
tspan = text.append("tspan").attr("x", x).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
}
}
words = [];
});
function estDirigeant(preNom)
{
let estDirig = false;
window.souverainete.forEach(function(lien)
{
let souverain = lien.idSouverain;
window.listePersonnes.forEach(function(personne)
{
if(personne.id == souverain)
{
if(preNom === personne.prenom + " " + personne.nom)
estDirig = true;
}
});
});
return estDirig;
/**
* retourne un string du pays du dirigeant au nom donné en paramètre
*/
function paysDirigeant(preNom)
{
let pays = null;
window.souverainete.forEach(function(lien)
{
let souverain = lien.idSouverain;
window.listePersonnes.forEach(function(personne)
{
if(personne.id == souverain)
{
if(preNom === personne.prenom + " " + personne.nom)
pays = lien.idPays;
}
});
});
return pays;
/**
* retourne si la personne au nom donné en paramètre est dirigeante du pays donné
*/
function isCurrentLeader(preNom, pays)
{
let p = getPersonne(document.querySelector('#slider').value, pays);
if(p && preNom === p.prenom + " " + p.nom) return "Actuel";
else return "";
/**
* récupère seulement les personnes de la famille de la personne dont le nom est donné en paramètre
*/
function getFamily(nomPersonne){
let family = [];
let seen = [];
const dirigeant = getPersByName(nomPersonne);
family.push(dirigeant);
seen[dirigeant.id] = true;
seen[i] = true;
for(let j of parent.parents){ // grands-parents dirigeant
let grandparent = getPersById(j);
if(!seen[j]){
seen[j] = true;
for(let u of grandparent.children){ // oncles et tantes du dirigeant
let uncle = getPersById(u);
if(!seen[u]){
seen[u] = true;
for(let jU of uncle.children){ // cousins du dirigeant
let nephew = getPersById(jU);
if(!seen[jU]){ family.push(nephew); seen[jU] = true;}
for(let jP of uncle.partners){ // femmes et maris des oncles et tantes
let partner = getPersById(jP);
if(!seen[jP]){ family.push(partner); seen[jP] = true;}
for(let broId of parent.children){ // freres soeurs du dirigeant
let bro = getPersById(broId);
if(!seen[broId]){
seen[broId] = true;
for(let nephewId of bro.children){ // neveux et nièces du dirigeant
let nephew = getPersById(nephewId);
if(!seen[nephewId]){ family.push(nephew); seen[nephewId] = true;}
for(let j of bro.partners){ // mari ou femme des freres et soeur du dirigeant
let partner = getPersById(j);
if(!seen[j]){ family.push(partner); seen[j] = true;}
for(let j of dirigeant.children){ //enfants dirigeant
let enfantDirigeant = getPersById(j);
if(!seen[j]){
seen[j] = true;
for(let gchId of enfantDirigeant.children){ //petits enfants dirigeant
let gch = getPersById(gchId);
if(!seen[gchId]){
family.push(gch);
seen[gchId] = true;
for(let spId of gch.partners){ // époux des petits enfants du dirigeant
let sp = getPersById(spId);
if(!seen[gchId]){
family.push(sp);
seen[spId] = true;
}
}
}
}
}
for(let chSpId of enfantDirigeant.partners){ // gendre et bru du dirigeant
let chSp = getPersById(chSpId);
if(!seen[chSpId]){
family.push(chSp);
seen[chSpId] = true;
}
for(let j of dirigeant.partners){ // mari ou femme du dirigeant
let partner = getPersById(j);
if(!seen[j]){
// on enlève les liens vers des personnes extérieures au graphe choisi précédemment
let identifiants = [];
for(let p of family){
identifiants.push(p.id);
}
for(let p of family){
for(let i in p.children){
if(identifiants.indexOf(p.children[i]) == -1){
p.children.splice(i, 1);
}
}
for(let i in p.partners){
if(identifiants.indexOf(p.partners[i]) == -1){
p.partners.splice(i, 1);
}
}
for(let i in p.parents){
if(identifiants.indexOf(p.parents[i]) == -1){
p.parents.splice(i, 1);
}
}
}
return family;
function getMultipleFamily(leaders){
if(leaders.length == 1){
return getFamily(leaders[0]);
}
else{
let bigFamily = [];
for(let leader of leaders){
const family = getFamily(leader);
for(let p of family){
bigFamily.push(p)
}
}
// on enlève les doublons
for(let i = 0; i < bigFamily.length; i++){
for(let j = 0; j < bigFamily.length; j++){
if(bigFamily[i] !== bigFamily[j] && bigFamily[i].id === bigFamily[j].id){
let set = new Set(bigFamily[i].children.concat(bigFamily[j].children));
bigFamily[i].children = Array.from(set);
set = new Set(bigFamily[i].parents.concat(bigFamily[j].parents));
bigFamily[i].parents = Array.from(set);
set = new Set(bigFamily[i].partners.concat(bigFamily[j].partners));
bigFamily[i].partners = Array.from(set);
bigFamily.splice(j,1);
}
}
}
return bigFamily;
}
}
let degree;
let relations;
function isInFamily(person1, person2, list){
if(person1.name === person2.name){
return true;
}
if(list.indexOf(person1.id) < 0){
list.push(person1.id)
if(person1.hasOwnProperty('parents')){
for(let idParent of person1.parents){
let parent = getPersById(idParent);
if(list.indexOf(parent.id) < 0 && (parent.hasOwnProperty('parents') || parent.hasOwnProperty('children'))){
if(isInFamily(parent, person2, list)){
relations.push("parent's");
trouve = true;
return trouve;
}
}
}
}
if(person1.hasOwnProperty('children')){
for(let idChildren of person1.children){
let child = getPersById(idChildren);
if(list.indexOf(child.id) < 0 && (child.hasOwnProperty('children') || child.hasOwnProperty('parents'))){
if(isInFamily(child, person2, list)){
relations.push("child's");
trouve = true;
return trouve;
}
}
}
}
if(person1.hasOwnProperty('partners')){
for(let idPartner of person1.partners){
let partner = getPersById(idPartner);
if(list.indexOf(partner.id) < 0 && (partner.hasOwnProperty('parents') || partner.hasOwnProperty('children'))){
if(isInFamily(partner, person2, list)){
relations.push("partner's");
}
}
}
}
}
function getPersByName(nom){
for(let i of window.jsonGlobal){
if(i.name === nom){
return JSON.parse(JSON.stringify(i));
}
function getPersById(id){
for(let i of window.jsonGlobal){
if(i.id === id){
return JSON.parse(JSON.stringify(i));
}
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
function listRelations(traveled){
for(let i = 0; i < traveled.length-1; i++){
const pers = traveled[i];
for(let idParent of pers.parents){
const parent = getPersById(idParent);
if(parent.name === traveled[i+1].name){
console.log("parent");
}
}
for(let idChild of pers.children){
const child = getPersById(idChild);
if(child.name === traveled[i+1].name){
console.log("child");
}
}
for(let idPartner of pers.partners){
const partner = getPersById(idPartner);
if(partner.name === traveled[i+1].name){
console.log("partner");
}
}
}
}
/**
* construction du graphe pour le dirigeant donné en paramètre
*/
degree = 0;
relations = [];
if(typeof(leaders) === "object"){
tree = getMultipleFamily(leaders);
console.log(isInFamily(getPersByName(leaders[0]),getPersByName(leaders[1]),new Array()));
relations = relations.reverse().join(" ");
/* relations = relations.replace("parent's parent's parent's","great-grandparent's");
relations = relations.replace("parent's parent's","grandparent's");
relations = relations.replace("child's child's child's","great-grandchild's");
relations = relations.replace("child's child's","grandchild's");
relations = relations.replace("parent's child's","sibling's");
relations = relations.replace("grandparent's grandchild's", "cousin's")
relations = relations.replace("cousin's parent's", "uncles or aunt 's")*/
const p1 = getPersByName(leaders[0]);
const p2 = getPersByName(leaders[1]);
const traveled = Dijkstra(roads,p1.id + "", p2.id + "");
listRelations(traveled)
}
else if(typeof(leaders) === "string"){
tree = getFamily(leaders);
}
build_graph();
rank();
normalize_layers();
sort_nodes_in_layers();
assign_coordinates();
d3.select('#canvas').select('svg')
.remove()
let svg = d3.select('#canvas')
.append('svg')
.attr('preserveAspectRatio', 'xMinYMin meet')
.append('g');
let links = [];
for (let id in graph) {
let node = graph[id];
let prefixM = 'M' + (tree[node.index].x + node_width / 2) +
' ' + (tree[node.index].y + node_height / 2);
let prefixCh = 'M' + (tree[node.index].x + node_width / 2) +
' ' + (tree[node.index].y + node_height);
for (let c = 0; c < node.partners.length; c++) {
let partner = tree[node.partners[c].index];
let path = prefixM + 'T' + (partner.x + node_width / 2) +
' ' + (partner.y + node_height / 2);
links.push({path: path, class:'partner'});
}
for (let c = 0; c < node.children.length; c++) {
let child = tree[node.children[c].index];
let path = prefixCh + 'T' + (child.x + node_width / 2) +
' ' + (child.y + 0);
links.push({path: path, class:'child'});
}
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
// Dessine les liens entre personnes
svg.selectAll('path')
.data(links)
.enter()
.append('path')
.attr('class', function(d) { return d.class})
.attr('d', function(d) { return d.path});
// Dessine les personnes
let enter_g = svg.selectAll('g.person')
.data(tree)
.enter()
.append('g')
.attr('class', function(d){
if(estDirigeant(d.name)){
return "leader";
}
return d.sex})
.attr('transform', function(d) {return 'translate(' + d.x + ',' + d.y + ')'});
enter_g
.append('rect')
.attr('width', node_width)
.attr('height', node_height)
.attr('rx', '0px')
.attr('ry', '0px')
.on("click", function(d) { window.open('https://fr.wikipedia.org/wiki/' + d.name); });
enter_g
.append('text')
.attr('x', 2)
.attr('y', 2)
.text(function(d) {
if(estDirigeant(d.name)){
const pays = paysDirigeant(d.name);
const actuelDirigeant = isCurrentLeader(d.name, pays);
return d.name + "\n"+ actuelDirigeant +"("+pays +")";
}
return d.name;})
.on("click", function(d) { window.open('https://fr.wikipedia.org/wiki/' + d.name); })
.call(wrap, node_width);
enter_g
.each(function() {
let w = this.getBBox().width,
h = this.getBBox().height;
d3.select(this).select('rect').attr('width', w).attr('height', h);
});
const taille = d3.select('#canvas').select('svg').select('g').node().getBBox();
d3.select('#canvas').select('svg').attr('viewBox', '0 0 '+(taille.width)+' '+(taille.height));
document.querySelector("#canvas > svg").style.width = taille.width;