1

I am having trouble using d3's symbol mechanism to specify a unique symbol for each set of data. The data's like this: [[{x: 1, y:1},{x: 2, y:2},{x: 3, y:3}], [{x: 1, y:1},{x: 2, y:4},{x: 3, y:9}], etc.]

The part of the code that writes out the symbols looks like this: I create a series group for each vector of points. Then:

series.selectAll("g.points") 
//this selects all <g> elements with class points (there aren't any yet)
.data(Object) //drill down into the nested Data
.enter() 
.append("g") //create groups then move them to the data location
.attr("transform", function(d, i) {
return "translate(" + xScale(d.x) + "," + yScale(d.y) + ")";
        })
.append("path")
.attr("d", function(d,i,j){
        return (d3.svg.symbol().type(d3.svg.symbolTypes[j]));
    }
 );

Or at least that's how I'd like it to work. The trouble is that I can't return the function d3.svg.symbol() from the other function. If I try to just put the function in the "type" argument, then data is no longer scoped correctly to know what j is (the index of the series).

right, but I don't want a unique symbol for each datapoint, I want a unique symbol for each series. The data consists of multiple arrays (series), each of which can have an arbitrary number of points (x,y). I'd like a different symbol for each array, and that's what j should give me. I associate the data (in the example, two arrays shown, so i is 0 then 1 for that) with the series selection. Then I associate the data Object with the points selection, so i becomes the index for the points in each array, and j becomes the index of the original arrays/series of data. I actually copied this syntax from somewhere else, and it works ok for other instances (coloring series of bars in a grouped bar chart for example), but I couldn't tell you exactly why it works...

Any guidance would be appreciated. Thanks!

user1885761
  • 11
  • 1
  • 3
  • Could you clarify the usage of j as a third parameter in your "d" attribute function? I do not understand what it passes in. – Andrew Staroscik Mar 12 '13 at 23:59
  • As Andrew points out, given .attr("d", function(d,i,j) {}), j should always be 0. Use d3.svg.symbolTypes[i] instead, as i refers to the (unique) index value for each data point. – widged Mar 13 '13 at 08:56

2 Answers2

6

What is the question exactly? The code that you give answers your question. My bad, j does return a reference to the series. Simpler example.

   var data = [
        {id: 1, pts: [{x:50, y:10},{x:50, y:30},{x:50, y:20},{x:50, y:30},{x:50, y:40}]},
        {id: 2, pts: [{x:10, y:10},{x:10, y:30},{x:40, y:20},{x:30, y:30},{x:10, y:30}]}
    ];
    var vis = d3.select("svg");
    var series = vis.selectAll("g.series") 
        .data(data, function(d, i) { return d.id; })
        .enter() 
        .append("svg:g")
        .classed("series", true);

    series.selectAll("g.point")
        .data(function(d, i) { return d.pts })
        .enter()
        .append("svg:path")
        .attr("transform", function(d, i) { return "translate(" + d.x + "," + d.y + ")"; })
        .attr("d", function(d,i, j) { return d3.svg.symbol().type(d3.svg.symbolTypes[j])(); })

The only difference is that I added parenthesis after d3.svg.symbol().type(currentType)() to return the value rather than the function. D3js uses chaining, jquery style. This let you use symbol().type('circle') to set a value and symbol().type() to get it. Whenever accessors are used, what is returned is a reference to a function that has methods and attributes. Keep in mind that, in Javascript functions are first class objects - What is meant by 'first class object'?. In libraries that use that approach, often, there is an obvious getter for retrieving meaningful data. With symbol, you have to use symbol()().

The code beyond the symbol functionality can be seen at: https://github.com/mbostock/d3/blob/master/src/svg/symbol.js

d3.svg.symbol = function() {
  var type = d3_svg_symbolType,
      size = d3_svg_symbolSize;

  function symbol(d, i) {
    return (d3_svg_symbols.get(type.call(this, d, i))
        || d3_svg_symbolCircle)
        (size.call(this, d, i));
  }

  ...

  symbol.type = function(x) {
    if (!arguments.length) return type;
    type = d3_functor(x);
    return symbol;
  };


  return symbol;
};
Community
  • 1
  • 1
widged
  • 2,749
  • 21
  • 25
0

Just in case you haven't. Have you tried?

.append("svg:path")
.attr("d", d3.svg.symbol()) 

as per https://github.com/mbostock/d3/wiki/SVG-Shapes.

widged
  • 2,749
  • 21
  • 25
  • yep, tried that, which gives me the same symbol for every data point, not quite what's desired. – user1885761 Mar 13 '13 at 14:29
  • Indeed "[0,1,2].forEach(function(d, i) { console.log(i, d3.svg.symbol()()); });" returns the same symbol on each iteration. – widged Mar 13 '13 at 23:19
  • symbol(datum[, index]) doesn't seem to use the i value. [0,1,2].forEach(function(d, i) { console.log(i, d3.svg.symbol(d, i)()); }); returns the same symbol. The workaround that you propose appears to be the only one that returns a different symbol. – widged Mar 13 '13 at 23:21