web-dev-qa-db-fra.com

Ajout d'une légende de graphique dans D3

J'ai du mal à ajouter une légende de graphique à mon graphique d3js. Voici mon approche actuelle:

var legend = svg.append("g")
  .attr("class", "legend")
  .attr("x", w - 65)
  .attr("y", 25)
  .attr("height", 100)
  .attr("width", 100);

legend.append("rect")
  .attr("x", w - 65)
  .attr("y", 25)
  .attr("width", 10)
  .attr("height", 10)
  .style("fill", function(d) { return color_hash[dataset.indexOf(d)][1] });

legend.append("text")
  .attr("x", w - 65)
  .attr("y", 25)
  .text(function(d) { return color_hash[dataset.indexOf(d)][0] + ": " + d; });

Ensuite, j'essaie de styliser le .legend classe:

.legend {
            padding: 5px;
            font: 10px sans-serif;
            background: yellow;
            box-shadow: 2px 2px 1px #888;
        }

Mais je n'ai pas beaucoup de chance.

Quelqu'un sait-il comment ajouter des légendes aux graphiques pour fournir la meilleure façon de le faire? Je ne trouve pas beaucoup de ressources pour cela en ligne.

Voici mon graphique entier: http://jsbin.com/ewiwag/2/edit

41
darko

Vous devez lier des données aux nœuds (rectangles et éléments de texte) qui composent la légende.

Actuellement, vous obtenez une erreur lorsque vous essayez de styliser des rectangles:

Uncaught TypeError: Cannot read property '1' of undefined 

La raison: il n'y a pas de données liées

legend.append("rect")
      /*...*/
      .style("fill", function(d) { 
         // d <---- is undefined
         return color_hash[dataset.indexOf(d)][1] 
      });

Notez que D3 se concentre sur la transformation des données et opère sur les sélections. Donc, sélectionnez d'abord un ensemble de nœuds, puis liez les données

legend.selectAll('rect')
      .data(dataset)
      .enter()

Une fois que vous avez entré la sélection avec enter, vous pouvez ajouter des nœuds et appliquer des propriétés dynamiquement. Notez que pour éviter de créer des rectangles au-dessus des autres, lorsque vous définissez la propriété y, passez le compteur i et multipliez-le par un entier.

  /*.....*/
      .append("rect")
      .attr("x", w - 65)
      .attr("y", function(d, i){ return i *  20;})
      .attr("width", 10)
      .attr("height", 10)
      .style("fill", function(d) { 
         var color = color_hash[dataset.indexOf(d)][1];
         return color;
      });

Voici l'exemple fixe: http://jsbin.com/ubafur/

37
jaime

Ok, voici une façon de le faire: http://jsbin.com/isuris/1/edit

Désolé, j'ai dû faire trop de changements pour pouvoir tout expliquer. Voyez si vous pouvez le découvrir. Si vous avez des questions, posez-les dans les commentaires et je modifierai la réponse.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <script type="text/javascript" src="http://mbostock.github.com/d3/d3.js"></script>
    <style type="text/css">

      .axis path,
      .axis line {
        fill: none;
        stroke: black;
        shape-rendering: crispEdges;
      }

      .axis text {
        font-family: sans-serif;
        font-size: 11px;
      }

      .y1 {
        fill: white;
        stroke: orange;
        stroke-width: 1.5px;
      }

      .y2 {
        fill: white;
        stroke: red;
        stroke-width: 1.5px;
      }

      .y3 {
        fill: white;
        stroke: steelblue;
        stroke-width: 1.5px;
      }

      .line {
        fill: none;
        stroke-width: 1.5px;
      }

      div.tooltip {
              position: absolute;
              text-align: center;
              width: 50px;
              height: 10px;
              padding: 5px;
              font: 10px sans-serif;
              background: whiteSmoke;
              border: solid 1px #aaa;
              pointer-events: none;
              box-shadow: 2px 2px 1px #888;
            }

            .legend {
              padding: 5px;
              font: 10px sans-serif;
              background: yellow;
              box-shadow: 2px 2px 1px #888;
            }

            .title {
              font: 13px sans-serif;
            }

    </style>
  </head>
  <body>
    <script type="text/javascript">

    //Width and height
    var w = 500;
    var h = 300;
    var padding = 50;

    var now = d3.time.hour.utc(new Date);
    var dataset = [ [ ],[ ] ];
    dataset[0].Push({x: d3.time.hour.utc.offset(now, -5), y: 0});
    dataset[0].Push({x: d3.time.hour.utc.offset(now, -4), y: 0});
    dataset[0].Push({x: d3.time.hour.utc.offset(now, -3), y: 2});
    dataset[0].Push({x: d3.time.hour.utc.offset(now, -2), y: 0});
    dataset[0].Push({x: d3.time.hour.utc.offset(now, -1), y: 0});
    dataset[0].Push({x: now, y: 0});

    dataset[1].Push({x: d3.time.hour.utc.offset(now, -5), y: 3});
    dataset[1].Push({x: d3.time.hour.utc.offset(now, -4), y: 1});
    dataset[1].Push({x: d3.time.hour.utc.offset(now, -3), y: 3});
    dataset[1].Push({x: d3.time.hour.utc.offset(now, -2), y: 1});
    dataset[1].Push({x: d3.time.hour.utc.offset(now, -1), y: 5});
    dataset[1].Push({x: now, y: 1});

    var color_hash = {  0 : ["Apple", "green"],
              1 : ["mango", "orange"],
              2 : ["cherry", "red"]
            }                      

    // Define axis ranges & scales        
    var yExtents = d3.extent(d3.merge(dataset), function (d) { return d.y; });
    var xExtents = d3.extent(d3.merge(dataset), function (d) { return d.x; });

  var xScale = d3.time.scale()
         .domain([xExtents[0], xExtents[1]])
         .range([padding, w - padding * 2]);

  var yScale = d3.scale.linear()
         .domain([0, yExtents[1]])
         .range([h - padding, padding]);


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


  // Define lines
  var line = d3.svg.line()
         .x(function(d) { return x(d.x); })
         .y(function(d) { return y(d.y1, d.y2, d.y3); });

  var pathContainers = svg.selectAll('g.line')
  .data(dataset);

  pathContainers.enter().append('g')
  .attr('class', 'line')
  .attr("style", function(d) {
    return "stroke: " + color_hash[dataset.indexOf(d)][1]; 
  });

  pathContainers.selectAll('path')
  .data(function (d) { return [d]; }) // continues the data from the pathContainer
  .enter().append('path')
    .attr('d', d3.svg.line()
      .x(function (d) { return xScale(d.x); })
      .y(function (d) { return yScale(d.y); })
    );

  // add circles
  pathContainers.selectAll('circle')
  .data(function (d) { return d; })
  .enter().append('circle')
  .attr('cx', function (d) { return xScale(d.x); })
  .attr('cy', function (d) { return yScale(d.y); })
  .attr('r', 3); 

    //Define X axis
  var xAxis = d3.svg.axis()
          .scale(xScale)
          .orient("bottom")
          .ticks(5);

  //Define Y axis
  var yAxis = d3.svg.axis()
          .scale(yScale)
          .orient("left")
          .ticks(5);

  //Add X axis
  svg.append("g")
  .attr("class", "axis")
  .attr("transform", "translate(0," + (h - padding) + ")")
  .call(xAxis);

  //Add Y axis
  svg.append("g")
  .attr("class", "axis")
  .attr("transform", "translate(" + padding + ",0)")
  .call(yAxis);

  // Add title    
  svg.append("svg:text")
       .attr("class", "title")
     .attr("x", 20)
     .attr("y", 20)
     .text("Fruit Sold Per Hour");


  // add legend   
  var legend = svg.append("g")
    .attr("class", "legend")
    .attr("x", w - 65)
    .attr("y", 25)
    .attr("height", 100)
    .attr("width", 100);

  legend.selectAll('g').data(dataset)
      .enter()
      .append('g')
      .each(function(d, i) {
        var g = d3.select(this);
        g.append("rect")
          .attr("x", w - 65)
          .attr("y", i*25)
          .attr("width", 10)
          .attr("height", 10)
          .style("fill", color_hash[String(i)][1]);

        g.append("text")
          .attr("x", w - 50)
          .attr("y", i * 25 + 8)
          .attr("height",30)
          .attr("width",100)
          .style("fill", color_hash[String(i)][1])
          .text(color_hash[String(i)][0]);

      });
    </script>
  </body>
</html>
12
meetamit