Подтвердить что ты не робот

Добавление легенды диаграммы в D3

У меня возникли проблемы с добавлением легенды диаграммы в мой график d3js. Вот мой нынешний подход:

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; });

Затем я пытаюсь создать стиль .legend:

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

Но мне не повезло.

Кто-нибудь знаком с добавлением легенд в диаграммы, способные обеспечить лучший способ сделать это? Я не нахожу много ресурсов для этого онлайн.

Вот мой весь график: http://jsbin.com/ewiwag/2/edit

4b9b3361

Ответ 1

Вам необходимо привязать данные к узлам (прямоугольники и текстовые элементы), которые составляют легенду.

В настоящее время вы получаете ошибку при попытке создать прямоугольники:

Uncaught TypeError: Cannot read property '1' of undefined 

Причина: нет связанных данных

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

Обратите внимание, что D3 фокусируется на преобразовании данных и работает с выборами. Итак, сначала выберите набор узлов, а затем привяжите данные

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

Как только вы введете выделение с помощью enter, вы можете добавлять узлы и применять свойства динамически. Обратите внимание, что, чтобы избежать создания прямоугольников поверх других, при настройке свойства y передайте счетчик i и умножьте его на целое число.

  /*.....*/
      .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;
      });

Здесь фиксированный пример: http://jsbin.com/ubafur/3

Ответ 2

Хорошо, здесь один из способов сделать это: http://jsbin.com/isuris/1/edit

Извините, пришлось сделать слишком много изменений, чтобы иметь возможность объяснить все это. Посмотрите, сможете ли вы это понять. Если у вас есть вопросы, спросите их в комментариях, и я изменю ответ.

<!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>