how to align text and labels in D3

Viewed 45

I am trying to make a custom SVG that looks like this.

SPEC

So far I have got this.

WIP

All of the data will be dynamic so I am trying to figure out how to use the radius to set the x/y axis. I have a example

const w = 400,
  h = 400,
  r = 160;

const STREAMS = [{
  label: 'Emissions',
  isSelected: true,
  yAxis: -40
}, {
  label: 'Energy Produced',
  isSelected: false,
  yAxis: -20
}, {
  label: 'Energy Consumed',
  isSelected: false,
  yAxis: 0
}, {
  label: 'Intensity',
  isSelected: false,
  yAxis: 20
}]

const SUB_STREAMS = [{
  value: 0.15,
  label: 'Total',
  isSelected: true
}, {
  value: 0.2,
  label: 'CO2',
  isSelected: false
}, {
  value: 0.25,
  label: 'Methane',
  isSelected: false
}, {
  value: 0.30,
  label: 'N2O',
  isSelected: false
}, {
  value: 0.35,
  label: 'Other',
  isSelected: false
}];

const svg = d3.select("#foo")
  .append("svg")
  .attr("width", w)
  .attr("height", h);

const g = svg.append("g")
  .attr("transform", "translate(" + [w / 2, h / 2] + ")");

g.append("circle")
  .attr("r", r)
  .style("fill", "none")
  .style("stroke", "black");

const points = g.selectAll(null)
  .data(SUB_STREAMS)
  .enter()
  .append("circle")
  .attr('stroke', 'dodgerblue')
  .attr('stroke-width', 1)
  .style("fill", function(d) {
    return d.isSelected ? 'dodgerblue' : 'white'
  })
  .attr("r", 12)
  .attr("cx", function(d) {
    return r * Math.cos(d.value * Math.PI * 2 - Math.PI / 2)
  })
  .attr("cy", function(d) {
    return r * Math.sin(d.value * Math.PI * 2 - Math.PI / 2)
  })

points.on("click", function(d) {
  console.log(d)
})

g.selectAll(null)
  .data(SUB_STREAMS)
  .enter()
  .append('text')
  .style('cursor', 'pointer')
  .style('fill', 'black')
  .attr('text-anchor', 'right')
  .attr('font-size', '1.3em')
  .attr('dx', (d) => r * Math.cos(d.value * Math.PI * 2 - Math.PI / 2))
  .attr('dy', (d) => r * Math.sin(d.value * Math.PI * 2 - Math.PI / 2))
  .text((d) => d.label)

const text = g
  .selectAll('path')
  .data(STREAMS)
  .enter()
  .append("text")
  .attr("text-anchor", "left")
  .attr('font-size', '1em')
  .attr("y", function(d, a) {
    return d.yAxis
  })

  .text((d) => d.label);

text.on("click", function(d) {
  console.log(d)
})


var arc = d3.symbol().type(d3.symbolTriangle)
var line = g.selectAll('path')
  .data(STREAMS)
  .enter()
  .append('path')
  .attr('d', arc)
  .attr('fill', 'red')
  .attr('stroke', '#000')
  .attr('stroke-width', 1)
  .attr('transform', function(d) {
    return `translate(-10,${d.yAxis - 5}) rotate(210)`;
  });
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div id="foo" />

1 Answers

Set the dominant-baseline (e.g., central) accordingly, and move the texts by the size of the circles, plus a little padding.

Here is your code with those changes:

const w = 500,
  h = 400,
  r = 160;

const STREAMS = [{
  label: 'Emissions',
  isSelected: true,
  yAxis: -40
}, {
  label: 'Energy Produced',
  isSelected: false,
  yAxis: -20
}, {
  label: 'Energy Consumed',
  isSelected: false,
  yAxis: 0
}, {
  label: 'Intensity',
  isSelected: false,
  yAxis: 20
}]

const SUB_STREAMS = [{
  value: 0.15,
  label: 'Total',
  isSelected: true
}, {
  value: 0.2,
  label: 'CO2',
  isSelected: false
}, {
  value: 0.25,
  label: 'Methane',
  isSelected: false
}, {
  value: 0.30,
  label: 'N2O',
  isSelected: false
}, {
  value: 0.35,
  label: 'Other',
  isSelected: false
}];

const svg = d3.select("#foo")
  .append("svg")
  .attr("width", w)
  .attr("height", h);

const g = svg.append("g")
  .attr("transform", "translate(" + [w / 2, h / 2] + ")");

g.append("circle")
  .attr("r", r)
  .style("fill", "none")
  .style("stroke", "black");

const points = g.selectAll(null)
  .data(SUB_STREAMS)
  .enter()
  .append("circle")
  .attr('stroke', 'dodgerblue')
  .attr('stroke-width', 1)
  .style("fill", function(d) {
    return d.isSelected ? 'dodgerblue' : 'white'
  })
  .attr("r", 12)
  .attr("cx", function(d) {
    return r * Math.cos(d.value * Math.PI * 2 - Math.PI / 2)
  })
  .attr("cy", function(d) {
    return r * Math.sin(d.value * Math.PI * 2 - Math.PI / 2)
  })

points.on("click", function(d) {
  console.log(d)
})

g.selectAll(null)
  .data(SUB_STREAMS)
  .enter()
  .append('text')
  .style('cursor', 'pointer')
  .style('fill', 'black')
  .attr('text-anchor', 'right')
  .attr('font-size', '1.3em')
  .attr('dx', (d) => 14 + r * Math.cos(d.value * Math.PI * 2 - Math.PI / 2))
  .attr('dy', (d) => r * Math.sin(d.value * Math.PI * 2 - Math.PI / 2))
  .text((d) => d.label)

const text = g
  .selectAll('path')
  .data(STREAMS)
  .enter()
  .append("text")
  .attr("text-anchor", "left")
  .attr('font-size', '1em')
  .attr("y", function(d, a) {
    return d.yAxis - 5
  })
  .text((d) => d.label);

text.on("click", function(d) {
  console.log(d)
})


var arc = d3.symbol().type(d3.symbolTriangle)
var line = g.selectAll('path')
  .data(STREAMS)
  .enter()
  .append('path')
  .attr('d', arc)
  .attr('fill', 'red')
  .attr('stroke', '#000')
  .attr('stroke-width', 1)
  .attr('transform', function(d) {
    return `translate(-10,${d.yAxis - 5}) rotate(210)`;
  });
text {
  dominant-baseline: central;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div id="foo" />

Related