d3.js

Introduction à la visualisation de données

d3 signifie Data-Driven Documents

d3.js est une librairie JavaScript qui permet essentiellement de visualiser de façon dynamique des données. Elle utilise pour cela le HTML, le CSS et le format SVG (Scalable Vector Graphics). 

Au programme : 

  • Les bases de d3.js

  • Création de diagrammes

  • Travailler avec des données

  • Améliorer le visuel

  • Mapping

  • Projets intégrant les notions étudiées

Ressources : 

Les bases de d3.js

  1. Créer un espace de travail

  2. Ajouter un élément

  3. Lier les données

  4. Utiliser les données

  5. Dessiner des div

  6. Introduction à SVG

  7. Dessiner des SVG

1

1. Créer un espace de travail

  • Créez sur votre bureau un dossier tuto_d3
  • Ouvrez Brackets, créez un fichier index.html et enregistrez-le dans le dossier tuto_d3
  • Tapez ou collez le code ci-dessous dans le fichier index.html
<!DOCTYPE html>
<html lang="fr">
    <head>
        <meta charset="utf-8">
        <title>D3 Test</title>
         <script src="https://d3js.org/d3.v4.min.js"></script>
    </head>
    <body>
        <script type="text/javascript">
            // Votre joli code D3 viendra se loger ici
        </script>
    </body>
</html>

2. Ajouter un élément

  • Remplacez le commentaire entre les balises script par :
d3.select("body").append("p").text("New paragraph!");
  1. La méthode select() de D3 sélectionne un élément du DOM à partir d’un sélecteur CSS (ici body).
  2. La méthode append() créé un nouvel élément p, et l’ajoute à la fin de l'élément sélectionné, (ici avant la balise fermante </body>).
  3. La méthode text() définit le texte de ce nouveau paragraphe vide à “New paragraph!”.
<!DOCTYPE html>
<html lang="fr">
    <head>
        <meta charset="utf-8">
        <title>D3 Test</title>
         <script src="https://d3js.org/d3.v4.min.js"></script>
    </head>
    <body>
        <script type="text/javascript">
            d3.select("body").append("p").text("New paragraph!");
        </script>
    </body>
</html>

Le code à tester :

3. Lier les données

La visualisation de données est un processus qui consiste à lier des données (mapping data) à une représentation graphique

Scott Murray

En entrée : des données

En sortie : une représentation graphique

On définit des règles de liaison  (mapping rules)

Avec D3 on lie les données à des éléments du DOM (binding).

 

Exemple : 

On va lier un tableau de nombres  à des paragraphes (création d'un nouveau paragraphe pour chaque valeur du tableau).

1. Mise en place des données

var dataset = [ 5, 10, 15, 20, 25 ];

2. Sélection des éléments du DOM à associer

d3.select("body").selectAll("p")

La méthode selectAll() sélectionne tous les paragraphes dans le DOM. Pour l'instant il n'en existe aucun.

3. Liaison avec les données

d3.select("body").selectAll("p")
    .data(dataset)

4. Création des éléments du DOM à associer aux données

d3.select("body").selectAll("p")
    .data(dataset)
    .enter()
    .append("p")
    .text("New paragraph!");

La méthode data() compte les données et analyse leurs valeurs. Notre tableau contient cinq valeurs donc les instructions suivantes seront exécutées cinq fois.

Il est nécessaire d'utiliser la méthode enter() pour créer de nouveaux éléments liés à des données.

Cette méthode scrute le DOM, puis les données et si ces dernières sont plus nombreuses que les éléments DOM à lier, alors elle crée autant "d'emplacements"  (placeholder) que nécessaire pour "recevoir" les nouveaux éléments créés par la méthode append().

Ici la méthode enter() compte 0 paragraphe dans le DOM et 5 valeurs dans l'ensemble de données. Chaînée avec la méthode append(), elle va donc créer 5 paragraphes.

Testez le code suivant :

<!DOCTYPE html>
<html lang="fr">
    <head>
        <meta charset="utf-8">
        <title>D3 Test</title>
        <script src="https://d3js.org/d3.v4.min.js"></script>
    </head>
    <body>
        <script type="text/javascript">
            var dataset = [ 5, 10, 15, 20, 25 ];
            d3.select("body").selectAll("p")
                .data(dataset)
                .enter()
                .append("p")
                .text("New paragraph!");
        </script>
    </body>
</html>

4. Utiliser les données

Dans le code précédent remplacez la ligne

.text("New paragraph!");

par

.text(function(d) { return d; });
function(d) { return d; }

Une variable d est créée et va prendre tour à tour l'ensemble des valeur de nos données.

Une fonction anonyme est également créée. Elle renvoie la valeur de chacune des données du tableau.

<!DOCTYPE html>
<html lang="fr">
    <head>
        <meta charset="utf-8">
        <title>D3 Test</title>
        <script src="https://d3js.org/d3.v4.min.js"></script>
    </head>
    <body>
        <script type="text/javascript">
            var dataset = [ 5, 10, 15, 20, 25 ];
            d3.select("body").selectAll("p")
                .data(dataset)
                .enter()
                .append("p")
                .text(function(d) { return d; });
        </script>
    </body>
</html>

Le code à tester :

.text(function(d) { return "je sais compter jusqu'à " + d; });

Une variante à tester :

5. Dessiner des divs

Nous allons créer un diagramme en barres à l'aide d'éléments div munis d'une couleur de fond.

<div style="display: inline-block;
        width: 20px;
        height: 75px;
        background-color: teal;"></div>
  • Ouvrez Brackets, créez un fichier style.css et enregistrez-le dans le dossier tuto_d3
  • Tapez ou collez le code ci-dessous dans le fichier style.css
.bar {
    display: inline-block;
    width: 20px;
    height: 75px;   /* On écrasera ça plus tard */
    background-color: teal;
}
<link rel="stylesheet" href="style.css">
  • Liez le fichier style.css au fichier index.html en ajoutant la ligne suivante entre les balises <head> et </head> du fichier index.html

Pour définir des attributs aux éléments dans D3 on utilise la méthode attr(). Par exemple pour attribuer la classe bar à un élément on écrit :

.attr("class", "bar")
<!DOCTYPE html>
<html lang="fr">
    <head>
        <meta charset="utf-8">
        <title>D3 Test</title>
        <script src="https://d3js.org/d3.v4.min.js"></script>
        <link rel="stylesheet" href="style.css" />
    </head>
    <body>
        <script type="text/javascript">
            var dataset = [ 5, 10, 15, 20, 25 ];

            d3.select("body").selectAll("div")
                .data(dataset)
                .enter()
                .append("div")
                .attr("class", "bar");
        </script>
    </body>
</html>

Le code à tester :

On obtient 5 div de 20 px de large et 75 px de haut côte à côte.

Dans D3 pour définir des styles de façon dynamique aux éléments on utilise la méthode style()

height: 75px; 
  • Dans votre fichier style.css supprimez la ligne de code suivante :
.style("height", function(d) {
    return(d + "px");
})

On donne à chaque div une hauteur en px égale à la valeur de la donnée correspondante (5px, 10px, 15px, 20px et 25px). C'est un peu petit...

<!DOCTYPE html>
<html lang="fr">
    <head>
        <meta charset="utf-8">
        <title>D3 Test</title>
         <script src="https://d3js.org/d3.v4.min.js"></script>
         <link rel="stylesheet" href="style.css" />
    </head>
    <body>
        <script type="text/javascript">
            var dataset = [ 5, 10, 15, 20, 25 ];
            d3.select("body").selectAll("div")
                .data(dataset)
                .enter()
                .append("div")
                .attr("class", "bar")
                .style("height", function(d) {
                    return(d + "px");
                });
        </script>
    </body>
</html>

Le code à tester :

margin-right: 2px;
  • Dans votre fichier style.css ajoutez la ligne de code suivante :
.style("height", function(d) {
    var barHeight = d * 5;  // On agrandit 5 fois l'échelle
    return barHeight + "px";
});

On espace légèrement nos barres :

Mise à l'échelle :

<!DOCTYPE html>
<html lang="fr">
    <head>
        <meta charset="utf-8">
        <title>D3 Test</title>
         <script src="https://d3js.org/d3.v4.min.js"></script>
         <link rel="stylesheet" href="style.css" />
    </head>
    <body>
        <script type="text/javascript">
            var dataset = [ 5, 10, 15, 20, 25 ];
            d3.select("body").selectAll("div")
                .data(dataset)
                .enter()
                .append("div")
                .attr("class", "bar")
                .style("height", function(d) {
                    var barHeight = d * 5;  
                    return barHeight + "px";
                });
        </script>
    </body>
</html>

Le code à tester :

var dataset = [];                        // Crée un tableau vide
for (var i = 0; i < 25; i++) {           // Itère 25 fois
    var newNumber = Math.random() * 30;  // Nouveau nombre aléatoire (0-30)
    dataset.push(newNumber);             // Ajoute le nouveau nombre au tableau
}

Un peu de JavaScript :

  • Actualisez votre navigateur...
  • Remplacez la ligne suivante :
var dataset = [ 5, 10, 15, 20, 25 ];

Par :

6. Introduction à SVG

SVG (Scalable Vector Graphics) est un format d'image.

Il existe deux types d'image : bitmap et vectorielle.

  • Une image bitmap (jpeg, png, gif) est constituée d'un certain nombre de points (pixels) correspondant à sa résolution. 
  • Une image vectorielle (svg) est constituée de formes géométriques.

Le plus gros avantage du format vectoriel sur le format bitmap c'est que l'on peut zoomer sur l'image sans perte de qualité...

Lors d'un agrandissement, les images bitmap pixelisent contrairement aux images vectorielles.

Il existe des logiciels comme Illustrator ou Inscape et des outils en ligne comme Method Draw qui permettent de créer des  images vectorielles.

On peut également générer des graphiques vectoriels avec du code. En effet, en plus d'être un format d'images, SVG est aussi un langage proche du HTML.

Le code SVG est inclus directement dans le HTML à l'aide de l'élément SVG.

<svg width="500" height="50">
</svg>

Le code ci-dessus crée à la manière de l'élément canvas "une zone de dessin" de 500 px de large et de 50 px de haut.

Système de coordonnées :

Création de formes simples :

<rect x="0" y="0" width="500" height="50"/>
  • Rectangle
<circle cx="250" cy="25" r="25"/>
  • Cercle
<ellipse cx="250" cy="25" rx="100" ry="25"/>
  • Ellipse
<line x1="0" y1="0" x2="500" y2="50" stroke="black"/>
  • Ligne

La propriété stroke qui définit la couleur du contour des formes doit être spécifiée pour que la ligne soit visible.

<text x="125" y="25">Un texte en SVG</text>
  • Texte

L'élément text hérite par défaut des styles CSS de fonte de son élément parent.

Un texte en SVG
path d="Mx,y Lx,y Lx,y Lx,y Lx,y Lx,y"
  • Path (chemin)

Mx,y  "move to" : le début du chemin est le point de coordonnées (x ; y)

Lx,y  "line to" : ligne vers le point de coordonnées (x ; y)

<svg width="100" height="100">
        <path d=" M10 25
                  L10 75
                  L60 75
                  L10 25"
                  stroke="orange" stroke-width="2" fill="none" />
</svg>

Utilisons l'élément path pour par exemple dessiner un triangle :

N.B. : les virgules peuvent être remplacées par des espaces.

path d="Mx,y Lx,y Lx,y Lx,y Lx,y Lx,y z"

Pour fermer le chemin :

On peut également dessiner le triangle comme ceci :

<svg width="100" height="100">
        <path d=" M10 25
                  L10 75
                  L60 75
                  z"
                  stroke="orange" stroke-width="2" fill="none" />
</svg>

Styliser des éléments SVG :

Les propriétés SVG les plus courantes sont :

  • fill — Le remplissage, une valeur de couleur. 

    • couleur nommée — orange
    • valeur hexadécimale — #3388aa ou #38a
    • valeur RGB — rgb(10, 150, 20)
    • valeurs RGBa (transparence) — rgba(10, 150, 20, 0.5)
  • stroke — Le  contour, une valeur de couleur.
  • stroke-width — La largeur du trait, une valeur numérique de mesure ( en px).
  • opacity — L’opacité, une valeur numérique comprise entre 0.0 (complètement transparent) et 1.0 (complètement opaque).

Avec text, vous pouvez aussi utiliser les propriétés suivantes, qui fonctionnent comme avec CSS :

  • font-family
  • font-size
<circle cx="25" cy="250" r="22"
 fill="yellow" stroke="orange" stroke-width="5"/>

Il y a deux moyens d’appliquer un style à un élément SVG : 

<circle cx="25" cy="250" r="22" class="soleil"/>
.soleil {
    fill: yellow;
    stroke: orange;
    stroke-width: 5;
 }
  • Directement comme un attribut de l’élément :
  • Avec un attribut class et une règle CSS :

La superposition des formes :

<rect x="0" y="0" width="30" height="30" fill="purple"/>
<rect x="20" y="5" width="30" height="30" fill="blue"/>
<rect x="40" y="10" width="30" height="30" fill="green"/>
<rect x="60" y="15" width="30" height="30" fill="yellow"/>
<rect x="80" y="20" width="30" height="30" fill="red"/>

La dernière forme définie est positionnée sur les précédentes...

La transparence :

<circle cx="25" cy="25" r="20" fill="rgba(128, 0, 128, 1.0)"/>
<circle cx="50" cy="25" r="20" fill="rgba(0, 0, 255, 0.75)"/>
<circle cx="75" cy="25" r="20" fill="rgba(0, 255, 0, 0.5)"/>
<circle cx="100" cy="25" r="20" fill="rgba(255, 255, 0, 0.25)"/>
<circle cx="125" cy="25" r="20" fill="rgba(255, 0, 0, 0.1)"/>
<

Il y a deux moyens d’appliquer une transparence : utiliser une couleur RGB avec alpha, ou utiliser l'attribut opacity.

<circle cx="25" cy="25" r="20"
        fill="rgba(128, 0, 128, 0.75)" 
        stroke="rgba(0, 255, 0, 0.25)" stroke-width="10"/>
<circle cx="75" cy="25" r="20"
        fill="rgba(0, 255, 0, 0.75)"
        stroke="rgba(0, 0, 255, 0.25)" stroke-width="10"/>
<circle cx="125" cy="25" r="20"
        fill="rgba(255, 255, 0, 0.75)"
        stroke="rgba(255, 0, 0, 0.25)" stroke-width="10"/>
<circle cx="25" cy="25" r="20" fill="purple" 
        stroke="green" stroke-width="10"
        opacity="0.9"/>
<circle cx="65" cy="25" r="20" fill="green"
        stroke="blue" stroke-width="10"
        opacity="0.5"/>
<circle cx="105" cy="25" r="20" fill="yellow"
        stroke="red" stroke-width="10"
        opacity="0.1"/>

7. Dessiner des SVG

var svg = d3.select("body")
            .append("svg")
            .attr("width", 500)
            .attr("height", 100);
  • Création du SVG :
// Largeur et hauteur
var w = 500; //  width == largeur
var h = 100;  // height == hauteur

var svg = d3.select("body")
            .append("svg")
            .attr("width", w)   // <-- Ici
            .attr("height", h); // <-- et ici !

Il est pratique de placer la hauteur et la largeur du SVG dans des variables :

var dataset = [ 5, 10, 15, 20, 25 ];

var circles = svg.selectAll("circle")
                 .data(dataset)
                 .enter()
                 .append("circle");
  • Création des formes :
circles.attr("cx", function(d, i) {
            return (i * 50) + 25;
        })
       .attr("cy", h/2)
       .attr("r", function(d) {
            return d;
       });

On définit de façon dynamique la position et le rayon de chaque cercle grâces aux fonctions. La variable d va prendre successivement les valeurs 5, 10, 15, 20 et 25. L'index i va prendre successivement les valeurs 0, 1, 2, 3 et 4.

Donnons un peu de style à nos cercles :

.attr("fill", "yellow")
.attr("stroke", "orange")
.attr("stroke-width", function(d) {
    return d/2;
});
<!DOCTYPE html>
<html lang="fr">
    <head>
        <meta charset="utf-8">
        <title>D3 Test</title>
         <script src="https://d3js.org/d3.v4.min.js"></script>
    </head>
    <body>
        <script type="text/javascript">
            var dataset = [5, 10, 15, 20, 25];      
            var w = 500; 
            var h = 100;  
            var svg = d3.select("body")
                        .append("svg")
                        .attr("width", w)   
                        .attr("height", h); 
            var circles = svg.selectAll("circle")
                .data(dataset)
                .enter()
                .append("circle")
                .attr("cx", function(d, i) {
                    return (i * 50) + 25;
                })
                .attr("cy", h/2)
                .attr("r", function(d) {
                    return d;
                })
                .attr("fill", "yellow")
                .attr("stroke", "orange")
                .attr("stroke-width", function(d) {
                    return d/2;
                });
        </script>
    </body>
</html>

Le code à tester :

Prochainement :

  • Créer et exporter un SVG en ligne avec des outils comme Gravit Designer

  • Outils en ligne pour "nettoyer" le code exporté...

Si vous voulez avoir une idée de ce que l'on peut faire avec des SVG...

Création de diagrammes

  1. Diagramme en barres

  2. Nuage de points

  3. Diagramme en ligne

2

1. Diagramme en barres

var w = 500; 
var h = 100;  

var svg = d3.select("body")
            .append("svg")
            .attr("width", w)   
            .attr("height", h); 

Création du SVG :

var dataset = [ 5, 10, 13, 19, 21, 25, 22, 18, 15, 13,
                11, 12, 15, 20, 18, 17, 16, 18, 23, 25 ];

Nos données :

svg.selectAll("rect")
   .data(dataset)
   .enter()
   .append("rect")
   .attr("x", function(d, i) {
        return i * 21;  // Largeur de barre de 20 plus 1 pour la marge
    })
   .attr("y", 0)
   .attr("width", 20)
   .attr("height", 100);

Création des barres :

<!DOCTYPE html>
<html lang="fr">
    <head>
        <meta charset="utf-8">
        <title>D3 Test</title>
         <script src="https://d3js.org/d3.v4.min.js"></script>
    </head>
    <body>
        <script type="text/javascript">
            var dataset = [ 5, 10, 13, 19, 21, 25, 22, 18, 15, 13, 11, 12, 15, 20, 18, 17, 16, 18, 23, 25 ]; 
            var w = 500; 
            var h = 100;  
            var svg = d3.select("body")
                        .append("svg")
                        .attr("width", w)   
                        .attr("height", h);     
                svg.selectAll("rect")
                   .data(dataset)
                   .enter()
                   .append("rect")
                   .attr("x", function(d, i) {
                        return i * 21;  // Largeur de barre de 20 plus 1 pour la marge
                    })
                   .attr("y", 0)
                   .attr("width", 20)
                   .attr("height", 100);
        </script>
    </body>
</html>

Le code à tester :

Un problème se pose si le nombre de données est trop élevé, les barres sortent du SVG...

Nous allons redéfinir la position des barres de façon plus fluide :

return i * (w / dataset.length); // largeur du SVG divisée par le nombre de données
  • Remplacez la ligne suivante :
return i * 21;  // Largeur de barre de 20 plus 1 pour la marge
 

Par :

Il faut maintenant faire en sorte que la largeur des barres s'adapte au nombre de données pour éviter un cas comme celui-ci :

  • Définissez une nouvelle variable :
var barPadding = 1;  // On règle la marge entre les barres à 1 px.
.attr("width", w / dataset.length - barPadding)
  • Remplacez la ligne suivante :
.attr("width", 20)

Par :

Lier la hauteur des barres aux données :

.attr("height", function(d) {
    return d * 4;  // agrandissement des barres x 4
});
  • Remplacez la ligne suivante :
.attr("height", 100);

Par :

Remettre les choses à l'endroit :

.attr("y", function(d) {
    return h - 4*d; // Hauteur moins la valeur de la donnée x 4
})
  • Remplacez la ligne suivante :
.attr("y", 0)

Par :

h - 4d

Il s'agit de pousser les barres vers le bas d'une hauteur égale à la différence entre la hauteur du SVG et celle de chaque barre.

<!DOCTYPE html>
<html lang="fr">

    <head>
        <meta charset="utf-8">
        <title>D3 Test</title>
        <script src="https://d3js.org/d3.v4.min.js"></script>
    </head>
    
    <body>
        <script type="text/javascript">
            var dataset = [5, 10, 13, 19, 21, 25, 22, 18, 15, 13, 11, 12, 15, 20, 18, 17, 16, 18, 23, 25];
            var w = 500;
            var h = 100;
            var barPadding = 1;
            var svg = d3.select("body")
                .append("svg")
                .attr("width", w)
                .attr("height", h);
            svg.selectAll("rect")
                .data(dataset)
                .enter()
                .append("rect")
                .attr("x", function(d, i) {
                    return i * (w / dataset.length);
                })
                .attr("y", function(d) {
                    return h - 4*d; // Hauteur moins la valeur de la donnée
                })
                .attr("width", w / dataset.length - barPadding)
                .attr("height", function(d) {
                    return 4*d; // agrandissement des barres x 4
                });
        </script>
    </body>

</html>

Le code à tester :

Mettre de la couleur : 

.attr("fill","BlueViolet");
.attr("fill", function(d) {
    return "rgb(0, " + (d * 10) + ", 0)";
});
.attr("fill", function(d) {
    if (d<=20) { 
    return "#778899";
    }
    else {
    return "#DC143C";
    }
});

Mettre en avant un seuil : 

Dernière étape : les étiquettes (labels)

.attr("x", function(d, i) {
        return i * (w / dataset.length);
})
.attr("y", function(d) {
        return h - (d * 4);
});

Il s'agit d'afficher sur chaque barre la valeur de la donnée correspondante. Pour cela nous allons ajouter des éléments text dans notre SVG :

Création des étiquettes :

svg.selectAll("text")
   .data(dataset)
   .enter()
   .append("text")
   .text(function(d) {
        return d;
   })

Positionnement des étiquettes :

<!DOCTYPE html>
<html lang="fr">

    <head>
        <meta charset="utf-8">
        <title>D3 Test</title>
        <script src="https://d3js.org/d3.v4.min.js"></script>
    </head>
    
    <body>
        <script type="text/javascript">
            var dataset = [5, 10, 13, 19, 21, 25, 22, 18, 15, 13, 11, 12, 15, 20, 18, 17, 16, 18, 23, 25];
            var w = 500;
            var h = 100;
            var barPadding = 1;
            var svg = d3.select("body")
                .append("svg")
                .attr("width", w)
                .attr("height", h);
            svg.selectAll("rect")
                .data(dataset)
                .enter()
                .append("rect")
                .attr("x", function(d, i) {
                    return i * (w / dataset.length);
                })
                .attr("y", function(d) {
                    return h - 4 * d; // Hauteur moins la valeur de la donnée
                })
                .attr("width", w / dataset.length - barPadding)
                .attr("height", function(d) {
                    return 4 * d; // agrandissement des barres x 4
                })
                .attr("fill", function(d) {
                    return "rgb(0, " + (d * 10) + ", 0)";
                });
            svg.selectAll("text")
                .data(dataset)
                .enter()
                .append("text")
                .text(function(d) {
                    return d;
                })
                .attr("x", function(d, i) {
                    return i * (w / dataset.length);
                })
                .attr("y", function(d) {
                    return h - (d * 4);
                });
        </script>
    </body>

</html>

Le code à tester :

Nous allons ajuster la position des étiquettes ainsi que modifier leur police et leur couleur :  

L’attribut SVG text-anchor permet de centrer le texte horizontalement par rapport à x :

.attr("text-anchor", "middle")

Nous ajoutons ensuite la moitié de la largeur d'une barre à l'abscisse de chaque étiquette  (centrage horizontal) et 14 px à l'ordonnée (ajustement vertical) :

.attr("x", function(d, i) {
        return i * (w / dataset.length) + (w / dataset.length - barPadding) / 2;
})
.attr("y", function(d) {
        return h - (d * 4) + 14;  
})

La touche finale =) :

.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "white");
<!DOCTYPE html>
<html lang="fr">

    <head>
        <meta charset="utf-8">
        <title>D3 Test</title>
        <script src="https://d3js.org/d3.v4.min.js"></script>
    </head>
    
    <body>
        <script type="text/javascript">
            var dataset = [5, 10, 13, 19, 21, 25, 22, 18, 15, 13, 11, 12, 15, 20, 18, 17, 16, 18, 23, 25];
            var w = 500;
            var h = 100;
            var barPadding = 1;
            var svg = d3.select("body")
                .append("svg")
                .attr("width", w)
                .attr("height", h);
            svg.selectAll("rect")
                .data(dataset)
                .enter()
                .append("rect")
                .attr("x", function(d, i) {
                    return i * (w / dataset.length);
                })
                .attr("y", function(d) {
                    return h - 4 * d; // Hauteur moins la valeur de la donnée
                })
                .attr("width", w / dataset.length - barPadding)
                .attr("height", function(d) {
                    return 4 * d; // agrandissement des barres x 4
                })
                .attr("fill", function(d) {
                    return "rgb(0, " + (d * 10) + ", 0)";
                });
            svg.selectAll("text")
                .data(dataset)
                .enter()
                .append("text")
                .text(function(d) {
                    return d;
                })
                .attr("text-anchor", "middle")
                .attr("x", function(d, i) {
                    return i * (w / dataset.length) + (w / dataset.length - barPadding) / 2;
                })
                .attr("y", function(d) {
                    return h - (d * 4) + 14;
                })
                .attr("font-family", "sans-serif")
                .attr("font-size", "11px")
                .attr("fill", "white");
        </script>
    </body>

</html>

Le code à tester :

2. Nuage de points

var w = 500; 
var h = 100;  

var svg = d3.select("body")
            .append("svg")
            .attr("width", w)   
            .attr("height", h); 

Création du SVG :

var dataset = [
                  [ 5,     20 ],
                  [ 480,   90 ],
                  [ 250,   50 ],
                  [ 100,   33 ],
                  [ 330,   95 ],
                  [ 410,   12 ],
                  [ 475,   44 ],
                  [ 25,    67 ],
                  [ 85,    21 ],
                  [ 220,   88 ]
              ];

Nos données :

svg.selectAll("circle")
   .data(dataset)
   .enter()
   .append("circle")
   .attr("cx", function(d) {
       return d[0];
   })
   .attr("cy", function(d) {
       return d[1];
   })
   .attr("r", function(d) {
       return Math.sqrt(d[1]);// rayon = racine carrée de l'ordonnée
   });

Création des points :

<!DOCTYPE html>
<html lang="fr">

    <head>
        <meta charset="utf-8">
        <title>D3 Test</title>
        <script src="https://d3js.org/d3.v4.min.js"></script>
    </head>
    
    <body>
        <script type="text/javascript">
            var dataset = [
                              [ 5,     20 ],
                              [ 480,   90 ],
                              [ 250,   50 ],
                              [ 100,   33 ],
                              [ 330,   95 ],
                              [ 410,   12 ],
                              [ 475,   44 ],
                              [ 25,    67 ],
                              [ 85,    21 ],
                              [ 220,   88 ]
                          ];
            var w = 500;
            var h = 100;
            var svg = d3.select("body")
                .append("svg")
                .attr("width", w)
                .attr("height", h);
            svg.selectAll("circle")
                .data(dataset)
                .enter()
                .append("circle")
                .attr("cx", function(d) {
                    return d[0];
                })
                .attr("cy", function(d) {
                    return d[1];
                })
                .attr("r", function(d) {
                    return Math.sqrt(d[1]);
                });
        </script>
    </body>
</html>

Le code à tester :

Deuxième étape : les étiquettes (labels)

.attr("x", function(d) {
        return d[0] + Math.sqrt(d[1]); //décalage vers la droite de r
})
.attr("y", function(d) {
        return d[1];
});

Création :

svg.selectAll("text")
   .data(dataset)
   .enter()
   .append("text")
   .text(function(d) {
       return d[0] + "," + d[1];
   });
  

Positionnement  :

Un peu de style...

.attr("fill", "#EE92C2"); //couleur des points
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "#22181C");   //couleur des étiquettes

Un problème subsiste... Certains points et étiquettes sont tronqués. Il est temps de mettre tout cela à l'échelle (scale).

<!DOCTYPE html>
<html lang="fr">

    <head>
        <meta charset="utf-8">
        <title>D3 Test</title>
        <script src="https://d3js.org/d3.v4.min.js"></script>
    </head>
    
    <body>
        <script type="text/javascript">
            var dataset = [
                [5, 20],
                [480, 90],
                [250, 50],
                [100, 33],
                [330, 95],
                [410, 12],
                [475, 44],
                [25, 67],
                [85, 21],
                [220, 88]
            ];
            var w = 500;
            var h = 100;
            var svg = d3.select("body")
                .append("svg")
                .attr("width", w)
                .attr("height", h);
            svg.selectAll("circle")
                .data(dataset)
                .enter()
                .append("circle")
                .attr("cx", function(d) {
                    return d[0];
                })
                .attr("cy", function(d) {
                    return d[1];
                })
                .attr("r", function(d) {
                    return Math.sqrt(d[1]);
                })
                .attr("fill", "#F45B69");
            svg.selectAll("text")
                .data(dataset)
                .enter()
                .append("text")
                .text(function(d) {
                    return d[0] + "," + d[1];
                })
                .attr("x", function(d) {
                    return d[0] + Math.sqrt(d[1]);
                })
                .attr("y", function(d) {
                    return d[1];
                })
                .attr("font-family", "sans-serif")
                .attr("font-size", "11px")
                .attr("fill", "#22181C");
        </script>
    </body>

</html>

Le code à tester :

Troisième étape : mise à l'échelle

Il s'agit de faire correspondre l'ensemble des données, le domaine d'entrée (domain) avec les dimensions de la visualisation, la plage de sortie (range).

Créer une échelle :

Pour définir une échelle dans D3, on utilise d3.scale suivi du type d’échelle souhaité. Il en existe plusieurs sortes, nous utiliserons une échelle linéaire : d3.scaleLinear().

var scale = d3.scaleLinear()
              .domain([100, 500])   // domaine d’entrée 100,500 
              .range([10, 350]);    // plage de sortie 10,350
scale(100);  // Retourne 10
scale(300);  // Retourne 180
scale(500);  // Retourne 350

Par exemple pour notre nuage de points, [0, 500] est un domaine d'entrée pertinent pour les abscisses des points.

d3.max(dataset, function(d) {    // Retourne 480
    return d[0];  // Référence la première valeur de chaque sous-tableau
});

Nous allons néanmoins le définir de façon dynamique afin que notre visualisation s'adapte en cas de variation des données...

On utilise pour cela les fonctions min() et max() :

var dataset = [
    [5, 20], [480, 90], [250, 50], [100, 33], [330, 95],
    [410, 12], [475, 44], [25, 67], [85, 21], [220, 88]
];
var xScale = d3.scaleLinear()
               .domain([0, d3.max(dataset, function(d) { return d[0]; })])
               .range([0, w]);

Echelle pour les abscisses : 

var yScale = d3.scaleLinear()
               .domain([0, d3.max(dataset, function(d) { return d[1]; })])
               .range([0, h]);

Echelle pour les ordonnées : 

Définition des échelles pour notre exemple :

.attr("cx", function(d) {
    return d[0]
})
.attr("cy", function(d) {
    return d[1];
})

On remplace le code suivant : 

.attr("cx", function(d) {
    return xScale(d[0]);
})
.attr("cy", function(d) {
    return yScale(d[1]);
})

par : 

Utilisation des échelles :

Position des points :

.attr("x", function(d) {
     return d[0] + Math.sqrt(d[1]);
 
})
.attr("y", function(d) {
    return d[1];
})

On remplace le code suivant : 

.attr("x", function(d) {
    return xScale(d[0]) + Math.sqrt(xScale(d[1]));
})
.attr("y", function(d) {
    return yScale(d[1]);
})

par : 

Position des étiquettes :

<!DOCTYPE html>
<html lang="fr">

    <head>
        <meta charset="utf-8">
        <title>D3 Test</title>
        <script src="https://d3js.org/d3.v4.min.js"></script>
    </head>
    
    <body>
        <script type="text/javascript">
            var dataset = [
                [5, 20],
                [480, 90],
                [250, 50],
                [100, 33],
                [330, 95],
                [410, 12],
                [475, 44],
                [25, 67],
                [85, 21],
                [220, 88]
            ];
            var w = 500;
            var h = 100;
            var xScale = d3.scaleLinear()
                .domain([0, d3.max(dataset, function(d) {
                    return d[0];
                })])
                .range([0, w]);
            var yScale = d3.scaleLinear()
                .domain([0, d3.max(dataset, function(d) {
                    return d[1];
                })])
                .range([0, h]);
            var svg = d3.select("body")
                .append("svg")
                .attr("width", w)
                .attr("height", h);
             svg.selectAll("circle")
                .data(dataset)
                .enter()
                .append("circle")
                .attr("cx", function(d) {
                    return xScale(d[0]);
                })
                .attr("cy", function(d) {
                    return yScale(d[1]);
                })
                .attr("r", function(d) {
                    return Math.sqrt(d[1]);
                })
                .attr("fill", "#F45B69");
             svg.selectAll("text")
                .data(dataset)
                .enter()
                .append("text")
                .text(function(d) {
                    return d[0] + "," + d[1];
                })
                .attr("x", function(d) {
                    return xScale(d[0]) + Math.sqrt(xScale(d[1]));
                })
                .attr("y", function(d) {
                    return yScale(d[1]);
                })
                .attr("font-family", "sans-serif")
                .attr("font-size", "11px")
                .attr("fill", "#22181C");
        </script>
    </body>

</html>

Le code à tester :

Et là vous vous dites...

Nous allons essayer d'arranger ça...

Pour inverser les ordonnées des points afin de se retrouver dans un repère plus "classique", il suffit d'inverser les bornes de la plage de sortie : 

  • Remplacer la ligne de code suivante :
  • par
.range([0, h]);
.range([h, 0]);

Ajout d'une marge

  • Remplacer les lignes de code suivantes :
  • par
.range([0, w]);
.range([padding, w - padding*2]);
var padding = 26;
.range([h, 0]);
.range([h - padding, padding]);
<!DOCTYPE html>
<html lang="fr">

    <head>
        <meta charset="utf-8">
        <title>D3 Test</title>
        <script src="https://d3js.org/d3.v4.min.js"></script>
    </head>
    
    <body>
        <script type="text/javascript">
            var dataset = [
                [5, 20],
                [480, 90],
                [250, 50],
                [100, 33],
                [330, 95],
                [410, 12],
                [475, 44],
                [25, 67],
                [85, 21],
                [220, 88]
            ];
            var w = 500;
            var h = 100;
            var padding = 26;
            var xScale = d3.scaleLinear()
                .domain([0, d3.max(dataset, function(d) {
                    return d[0];
                })])
                .range([padding, w - padding*2]);
            var yScale = d3.scaleLinear()
                .domain([0, d3.max(dataset, function(d) {
                    return d[1];
                })])
                .range([h - padding, padding]);
            var svg = d3.select("body")
                .append("svg")
                .attr("width", w)
                .attr("height", h);
            svg.selectAll("circle")
                .data(dataset)
                .enter()
                .append("circle")
                .attr("cx", function(d) {
                    return xScale(d[0]);
                })
                .attr("cy", function(d) {
                    return yScale(d[1]);
                })
                .attr("r", function(d) {
                    return Math.sqrt(d[1]);
                })
                .attr("fill", "#F45B69");
            svg.selectAll("text")
                .data(dataset)
                .enter()
                .append("text")
                .text(function(d) {
                    return d[0] + "," + d[1];
                })
                .attr("x", function(d) {
                    return xScale(d[0]) + Math.sqrt(xScale(d[1]));
                })
                .attr("y", function(d) {
                    return yScale(d[1]);
                })
                .attr("font-family", "sans-serif")
                .attr("font-size", "11px")
                .attr("fill", "#22181C");
        </script>
    </body>
    
</html>

Le code à tester :

Pour tester la fluidité de votre diagramme, vous pouvez par exemple ajouter un point  [550, 150] et modifier la hauteur du SVG : h = 300.

3. Diagramme en ligne

var monthlySales = [
         {"month":10, "sales":100},
         {"month":20, "sales":130},
         {"month":30, "sales":250},
         {"month":40, "sales":300},
         {"month":50, "sales":265},
         {"month":60, "sales":225},
         {"month":70, "sales":100},
         {"month":80, "sales":120},
         {"month":90, "sales":145},
         {"month":100, "sales":130}
            ];

Nos données :

Notation objet...

var line = d3.line()
                .x(function(d,i){ return d.month*3; })
                .y(function(d,i){ return h-d.sales; });
     

Nous allons utiliser un line generator 

Ne pas confondre l'élément SVG line (segment de ligne droite) et le générateur D3 line (lignes par forcément droites).

Création de la ligne :

var w = 400; 
var h = 350;  

var svg = d3.select("body")
            .append("svg")
            .attr("width", w)   
            .attr("height", h); 

Création du SVG :

var viz = svg.append("path")
             .attr("d",line(monthlySales))
             .attr("fill","none")
             .attr("stroke","blue");
<!DOCTYPE html>
<html lang="fr">

    <head>
        <meta charset="utf-8">
        <title>D3 Test</title>
        <script src="https://d3js.org/d3.v4.min.js"></script>
    </head>
    
    <body>
        <script type="text/javascript">
            var h = 350;
            var w = 400;
            var monthlySales = [
                     {"month":10, "sales":100},
                     {"month":20, "sales":130},
                     {"month":30, "sales":250},
                     {"month":40, "sales":300},
                     {"month":50, "sales":265},
                     {"month":60, "sales":225},
                     {"month":70, "sales":100},
                     {"month":80, "sales":120},
                     {"month":90, "sales":145},
                     {"month":100, "sales":130}
                ];
    
            var svg = d3.select("body")
                        .append("svg")
                        .attr("width", w)
                        .attr("height", h);
            
            var line = d3.line()
                            .x(function(d){ return d.month*3; })
                            .y(function(d){ return h-d.sales; });
            
            var viz = svg.append("path")
                         .attr("d",line(monthlySales))
                         .attr("fill","none")
                         .attr("stroke","blue");
        </script>
    </body>

</html>

Le code à tester :

Exemples : 

Introduction à la visualisation de données : d3.js

By icn

Introduction à la visualisation de données : d3.js

Utilisation de la bibliothèque d3.js

  • 3,197