Alex Lenail
Alex Lenail

Reputation: 14460

How to use d3.symbols with an ordinal scale

TL;DR: How do I get a scaleOrdinal over the d3.symbols?

I'm hoping to use d3.symbols for an ordinal scale, corresponding to a categorical attribute on some data.

d3.symbols

An array containing the set of all built-in symbol types: circle, cross, diamond, square, star, triangle, and wye. Useful for constructing the range of an ordinal scale should you wish to use a shape encoding for categorical data.

Here's what I thought would work:

var svg = d3.select('#graph-container').append('svg').attr('xmlns', 'http://www.w3.org/2000/svg');
var g = svg.append('g');

var data = [{type: 'a'}, {type: 'a'}, {type: 'a'}, {type: 'b'}, {type: 'b'}, {type: 'c'}];

var shape = d3.scaleOrdinal(d3.symbols);

var s = g.selectAll('.symbol').data(data);

/* Does not render */
s.enter()
 .append('path')
 .attr('d', d => shape(d.type))
 .attr('x', (d, i) => i * 20)
 .attr('y', 50);

/* Sanity Check: Properly renders */
s.enter()
 .append('circle')
 .attr('r',7)
 .attr('cx', (d, i) => (i+1)*20)
 .attr('cy', 100);
<script src="https://d3js.org/d3.v5.min.js"></script>
<div id="graph-container"></div>

(alternately see fiddle).

Unfortunately the above fails with:

Error: <path> attribute d: Expected moveto path command ('M' or 'm'), "[object Object]".

Clearly shape(d.type) is not returning a valid path. It's actually returning an object with a single function named draw, i.e. it looks like { draw: function() { ... } }, which presumably refers to this draw method. Calling that draw method doesn't yield a valid path either, though, unfortunately.

Upvotes: 4

Views: 1212

Answers (1)

Gerardo Furtado
Gerardo Furtado

Reputation: 102188

You're missing the symbol generator itself...

var symbol = d3.symbol();

... to which you'll pass the type:

s.enter()
    .append('path')
    .attr('d', d => symbol.type(shape(d.type))())
    //etc...

Also, <path>s don't have x or y attributes.

Here is the code with those changes:

var svg = d3.select('#graph-container').append('svg').attr('xmlns', 'http://www.w3.org/2000/svg');
var g = svg.append('g');

var data = [{
  type: 'a'
}, {
  type: 'a'
}, {
  type: 'a'
}, {
  type: 'b'
}, {
  type: 'b'
}, {
  type: 'c'
}];

var shape = d3.scaleOrdinal(d3.symbols);

var symbol = d3.symbol();

var s = g.selectAll('.symbol').data(data);

/* Does not render */
s.enter()
  .append('path')
  .attr('d', d => symbol.type(shape(d.type))())
  .attr("transform", (_, i) => "translate(" + (20 + i * 20) + ",50)")
  .attr('fill', 'black');

/* Properly renders */
s.enter()
  .append('circle')
  .attr('r', 7)
  .attr('cx', (d, i) => (i + 1) * 20)
  .attr('cy', 100)
  .attr('fill', 'black');
<div id="graph-container"></div>
<script src="https://d3js.org/d3.v5.min.js"></script>

Upvotes: 3

Related Questions