0

I'm trying to create the following bubble chart with fruit images for each of the bubbles. I'm not getting an error in the console and the console is correctly printing the fruit name but the fruit images do not show. What am I missing. I think it might be the say I'm adding the fill attribute inside the for loop but I've played around with the order with no success.

var diameter = 500, //max size of the bubbles
    color    = d3.scale.category20b(); //color category

var bubble = d3.layout.pack()
    .sort(null)
    .size([diameter, diameter])
    .padding(1.5);

var svg = d3.select("body")
    .append("svg")
    .attr("width", diameter)
    .attr("height", diameter)
    .attr("class", "bubble");

d3.csv("fruit.csv", function(error, data){

    //convert numerical values from strings to numbers
    data = data.map(function(d){ d.value = +d["Amount"]; return d; });


    //bubbles needs very specific format, convert data to this.
    var nodes = bubble.nodes({children:data}).filter(function(d) { return !d.children; });

    //setup the chart
    var bubbles = svg.append("g")
        .attr("transform", "translate(0,0)")
        .selectAll(".bubble")
        .data(nodes)
        .enter();

    //create the bubbles
    bubbles.append("circle")
        .attr("id", function(d) { return d.Fruit; })
        .attr("r", function(d){ return d.r; })
        .attr("cx", function(d){ return d.x; })
        .attr("cy", function(d){ return d.y; })
        .style("fill", function(d) { return color(d.value); });

    //format the text for each bubble
    bubbles.append("text")
        .attr("x", function(d){ return d.x; })
        .attr("y", function(d){ return d.y + 5; })
        .attr("text-anchor", "middle")
        .text(function(d){ return d["Fruit"]; })
        .style({
            "fill":"white", 
            "font-family":"Helvetica Neue, Helvetica, Arial, san-serif",
            "font-size": "12px"
        });

    var defs = svg.append("defs");

    var circles = document.getElementsByTagName("circle");

    for (var i = 0; i < circles.length; i++) {

        var fruit = circles[i].id;

        defs.append("pattern")
            .attr("height","100%")
            .attr("width", "100%")
            .attr("patternContentUnits", "objectBoundingBox")
            .append("image")
            .attr("height",1)
            .attr("width",1)
            .attr("preserveAspectRatio","none")
            .attr("xlink:href", fruit+".jpg"); 

        d3.select("#"+fruit)
            .attr("fill", "url(#" + fruit + ")");

        console.log(circles[i].id);

    }

})
Gerardo Furtado
  • 100,839
  • 9
  • 121
  • 171
charlie
  • 187
  • 4
  • 15

1 Answers1

2

The problem with your code right now is that you're not setting any ID to the <patterns>.

Besides that, you're trying to override a fill previously set using style()...

.style("fill", function(d) { return color(d.value); });

... with an attr() method:

.attr("fill", "url(#" + fruit + ")");

Finally, get rid of that for loop. As a general rule, avoid loops when appending/creating elements with D3.

So, summarising a solution, you could use a simple "enter" selection to append the patterns, setting their IDs using d.Fruits and setting the circles' fill according to the patterns' IDs:

var defs = svg.append("defs");

defs.selectAll(null)
    .data(nodes)
    .enter()
    .append("pattern")
    .attr("id", function(d){
        return d.Fruit
    })//set the id here
    .attr("height", "100%")
    .attr("width", "100%")
    .attr("patternContentUnits", "objectBoundingBox")
    .append("image")
    .attr("height", 1)
    .attr("width", 1)
    .attr("preserveAspectRatio", "none")
    .attr("xlink:href", function(d) {
        return d.Fruit + ".jpg"
    });

bubbles.append("circle")
    .attr("id", function(d) {
        return d.Fruit;
    })
    .attr("r", function(d) {
        return d.r;
    })
    .attr("cx", function(d) {
        return d.x;
    })
    .attr("cy", function(d) {
        return d.y;
    })
    .style("fill", function(d) {
        return "url(#" + d.Fruit + ")";
    });//fill the circles according to the pattern's id
Gerardo Furtado
  • 100,839
  • 9
  • 121
  • 171
  • Thanks for your feedback Gerardo. In the console I'm now seeing the circles have a fill of the image id and hovering over the the element in the console highlights the element on the page. But there are now no images or fill colors being displayed on the page. – charlie Oct 22 '17 at 15:58
  • In that case, the only way to help you is if you create a [MCVE], with your real data. – Gerardo Furtado Oct 22 '17 at 21:11