Nantes Université

Skip to content
Extraits de code Groupes Projets
familyGraph.js 27,38 Kio
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;

            delete person.pere;
         }
   }

   window.jsonGlobal = jsonFamily;
}


   /**
    * 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 graph = {};
   let node_width = 135;
   let node_height = 45;
   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);
            }
         }
         result.push(node);
      }
      for (let id in graph) {
         if (!seen[Number(id)]) {
            impl(graph[id]);
         }
      }
      return result;
   }
   
   /**
    * Retourne le min et max entre A et B
    */
   function min(a, b) {
      if (a === undefined) {
         return b;
      } else {
         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.
      */
      for (let id = 0; id < nodes.length; id++) {
         let node = nodes[id];
         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;
            }
         }
      }
   }
   
   /**
    * Normalise les niveaux
    */
   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;
      }
      return layers;
   }
   
   /**
    * On trie les noeuds pour chaque niveau
    */ 
   
   function sort_nodes_in_layers() {
      let layers = per_layer();
      
      for (let l = 1; l < layers.length; l++) {
         for (let c = 0; c < layers[l - 1].length; c++) {
            let child_node = layers[l - 1][c];
            let total = 0;
            let count = 1;
   
            for (let p = 0; p < child_node.parents.length; p++) {
               let parent_node = child_node.parents[p];
               total += parent_node.pos_in_layer;
               count ++;
            }
            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;
            }
         }
      }
   
      /**
       * 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;
      }
   }
   
   /**
    * 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;

      for(let i of dirigeant.parents)
      {   
         let parent = getPersById(i);
         if(!seen[i]){
            family.push(parent);
            seen[i] = true;
            for(let j of parent.parents){                      // grands-parents dirigeant
               let grandparent = getPersById(j);
               if(!seen[j]){
                  family.push(grandparent);
                  seen[j] = true;
                  for(let u of grandparent.children){          // oncles et tantes du dirigeant
                     let uncle = getPersById(u);
                     if(!seen[u]){
                        family.push(uncle);
                        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]){
                  family.push(bro);
                  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]){
            family.push(enfantDirigeant);
            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]){
            family.push(partner);
            seen[j] = true;
         }
      }

      // 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;
      }
      let trouve = false;
      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");
                     trouve = true;
                     return trouve;
                  }
               }

            }
         }
         
      }
      return trouve;

   }



   /**
    * getter de personne par nom
    */
   function getPersByName(nom){
      for(let i of window.jsonGlobal){
         if(i.name === nom){
            return JSON.parse(JSON.stringify(i));
         }
      }
   }
   /**
    * getter de personne par id
    */
   function getPersById(id){
      for(let i of window.jsonGlobal){
         if(i.id === id){
            return JSON.parse(JSON.stringify(i));
         }
      }
   }

   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 
    */
   function graph_main(leaders) {
      graph = {};
      tree = [];
      degree = 0;
      relations = [];
      roads = {}
      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')
        .attr('viewBox', '0 0 1300 500')
        .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'});
         }
      }
      // 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;
   }