Search code examples
javascriptsvgd3.jsexpressionenginedata-visualization

d3.js / svg - how to dynamically append text to my arcs


I am trying to complete the last bit of a d3 project which dynamically creates these blue arcs, over which I need to place arc text, as shown in this image:

enter image description here

The image above is something I've done by placing the arc text statically, through trial and error, but I want to place it dynamically, based on the blue arcs which sit beneath the text. This is the code that dynamically creates the arcs:

var groupData = data_group.selectAll("g.group")
    .data(nodes.filter(function(d) { console.log(d.__data__.key); return (d.key=='Employers' ||{exp:channel:entries category="13" backspace="2"} d.key == '{url_title}' ||{/exp:channel:entries}) && d.children; }))
    .enter().append("group")
    .attr("class", "group");

arc_group.selectAll("g.arc")
    .data(groupData[0])
    .enter().append("svg:path")
    .attr("d", groupArc)
    .attr("class", "groupArc")
    .style("fill", "#1f77b4")
    .style("fill-opacity", 0.5);

The {exp:} content is preparsed data I'm pulling from my content management system in expression engine if it looks confusing.

So, I have my arcs. Now you'll notice in the groupData code block I have a console.log statement, that will give me the names I want to appear in the arc text:

console.log(d.__data__.key);

Now, the code I was using to place the arc text statically was this:

var arcData = [
  {aS: 0, aE: 45,rI:radius - chartConfig.linePadding + chartConfig.arcPadding,rO:radius - chartConfig.linePadding + chartConfig.textPadding-chartConfig.arcPadding}
];

var arcJobsData = d3.svg.arc().innerRadius(arcData[0].rI).outerRadius(arcData[0].rO).startAngle(degToRad(1)).endAngle(degToRad(15));
var g = d3.select(".chart").append("svg:g").attr("class","arcs");
var arcJobs = d3.select(".arcs").append("svg:path").attr("d",arcJobsData).attr("id","arcJobs").attr("class","arc");
g.append("svg:text").attr("x",3).attr("dy",15).append("svg:textPath").attr("xlink:href","#arcJobs").text("JOBS").attr("class","arcText"); //x shifts x pixels from the starting point of the arc. dy shifts the text y units from the top of the arc

And in this above code, the only thing left that I should need to do is dynamically assign an ID to the arcs, and then reference that ID in the xlink:href attribute, as well as replace the text("JOBS") with text that pulls from d.data__key. Given the code above which dynamically creates the arcs, and given that I know how to dynamically create and retrieve the text I want to place in the arcs using d.__data.key, I should be able to finish this thing off, but I can't figure out how write code in d3 that will take the data and place it in the arcs. Can anybody help with this?


Solution

  • Figured this out. Changed the structure of things a bit so it made a little more sense, but essentially what I did is this:

    var groupData = data_group.selectAll("g.group")
        .data(nodes.filter(function(d) { return (d.key=='Employers' ||{exp:channel:entries category="13" backspace="2"} d.key == '{url_title}' ||{/exp:channel:entries}) && d.children; }))
        .enter().append("group")
        .attr("class", "group"); //MH - why do we need this group - these elements are empty. Shouldn't this just be an array? Find out how to delete the svg elements without getting rid of the data, which is needed below.
    
    
      var groupArc = d3.svg.arc()
        .innerRadius(ry - 177)
        .outerRadius(ry - 157)
        .startAngle(function(d) { return (findStartAngle(d.__data__.children)-2) * pi / 180;})
        .endAngle(function(d) { console.log(d.__data__.key); return (findEndAngle(d.__data__.children)+2) * pi / 180});
    
      var arc_and_text = arc_group.selectAll("g.arc")
        .data(groupData[0])
        .enter().append("svg:g")
        .attr("class","arc_and_text");
    
      var arc_path = arc_and_text.append("svg:path")
        .attr("d", groupArc)
        .attr("class", "groupArc")
        .attr("id", function(d, i) { return "arc" + i; })
        .style("fill", "#1f77b4")
        .style("fill-opacity", 0.5); //MH: (d.__data__.key) gives names of groupings
    
      var arc_text = arc_and_text.append("text")
        .attr("class","arc_text")
        .attr("x", 3)
        .attr("dy", 15);
    
      arc_text.append("textPath")
        .attr("xlink:href", function(d, i) { return "#arc" + i; })
        .attr("class","arc_text_path")
        .style("fill","#ffffff")
        .text(function(d, i) { return d.__data__.key; });
    

    D3 still mystifies me a bit, and I'm sure this code could be much improved, but it works.