2012-11-26 49 views
35

d3jsグラフにグラフ凡例を追加する際に問題があります。ここに私の現在のアプローチです:D3でグラフ凡例を追加

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

答えて

30

あなたは伝説を構成するノード(長方形やテキスト要素)にデータをバインドする必要があります。スタイルの長方形にしようとしたとき

現在、あなたはエラーを取得する:

Uncaught TypeError: Cannot read property '1' of undefined 

理由:D3が選択にデータ変換に焦点を当て、動作することを何もバインドされたデータ

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

お知らせはありません。だから、最初のノードのセットを選択し、次にあなたがenterで選択を入力したら、ノードを追加し、プロパティを動的に適用することができます

legend.selectAll('rect') 
     .data(dataset) 
     .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

+0

ああ、それは意味があります! 1つの大きな問題はまだあります。フォントはありますが、.legendの背景と枠線のスタイルは適用されません。私はsvg 要素が他のdivと同じ方法でスタイル設定できると仮定しています。これは間違っていますか? – darko

+0

@ddarko、それは間違っています。 CSS(セレクタ)を使用してSVG要素をスタイルする場合、CSSプロパティ名ではなく、[SVG属性](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute)のみを使用できます。 [この本](http://chimera.labs.oreilly.com/books/1230000000345/ch03.html#_styling_svg_elements)で提案されているように、SVG固有のスタイルシート内のルールを区別するために、 'svg 'セレクタに:' svg .legend {...} ' –

11

[OK]を、ここでそれを行うための一つの方法だ: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> 
+0

jm-への私のコメントを参照してください。 – darko

+0

いいえ、背景とボーダーは適用できません。SVG要素のCSS。 SVGの主なスタイル可能なプロパティは、ストローク、塗りつぶし、フォントファミリー、フォントサイズです。それぞれの凡例のエントリに対して、必要なサイズ、背景、境界線を持つ別の 'svg:rect'を作成する必要があります。あるいは、HTMLを使って全凡例を作ることができます。これは簡単なオプションです。そのHTMLをSVG要素の中にネストすることは可能かもしれませんが(私はそれを試みたことはありません)、SVG要素の上にオーバーレイされた兄弟ノードにするだけです。 – meetamit

+0

あなたのjsbinの少し編集されたバージョン(ポイントが 'push()によって追加されました)http://jsbin.com/isuris/437/edit –

関連する問題