1

I'm having a tough time figuring out how to place images inside a circle using links in my data set. I know a pattern is required to add images to nodes - related SO questions on this topic append the def, pattern and image elements before the introduction of nodes and data.

In my case, I couldn't find a way out of appending the tags inside the selector function because data is added dynamically to each node. Here's the code to the project, each black dot is supposed to contain a different image of an insect (url is in the tsv file): https://plnkr.co/edit/Ydrxogbfm9JvqrgeQaQ6?p=preview

I tried changing xlink:href in the body tags with the code below:

<body>
<svg width="1000" height="600">
    <defs id="mdef">
    <pattern id="image" x="0" y="0" height="40" width="40">
      <image x="0" y="0" width="40" height="40" ></image>
    </pattern>
  </defs>
</svg>

</body>

and this snippet of JS within the code block adding the nodes. :

.attr('"xlink:href", function(d){return d[1];}) //d is an array and the d[1] is the link

However, the images didn't appear. I then tried adding the pattern using js:

for (i=0;i<insects.length;i++){
  g.selectAll("circle")
      .data(insects[i],function(d) {console.log(d); return d }) //each insect
    .enter().append("circle")
      .attr('cx', function(d,index) {return x(insects[i].length)/insects[i].length*index; })
      .attr("r", 20)
      .attr("cy", function(d,index){return y.bandwidth()*i})
    .append('svg:defs') //adding pattern
    .append('svg:pattern')
      .attr('id','pattern')
        .attr("x",0)
        .attr("y",0)
        .attr("width",40)
        .attr("height",40)
      .append("svg:image")
        .attr("x",0)
        .attr("y",0)
        .attr("width",40)
        .attr("height",40)
        .attr("xlink:href", function(d){console.log(d[1]); return d[1];})
    .style("fill", "url(#pattern)");
  }
})

But I get the same result. Would really appreciate any pointers as I'm a beginner in d3. Happy holidays

I Like
  • 1,711
  • 2
  • 27
  • 52

1 Answers1

2

You cannot append a <defs>, a <pattern> and a <image> to a circle. That won't work.

Instead of that, you have to create the <defs>, append the patterns and images, and fill the circles according to their unique IDs:

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

defs.selectAll(".patterns")
    .data(insects[i], function(d) {
        return d
    })
    .enter().append("pattern")
    .attr("id", function(d) {
        return "insect" + (d[0].split(" ").join(""))
    })
    .attr("width", 1)
    .attr("height", 1)
    .append("svg:image")
    .attr("xlink:href", function(d) {
        return d[1]
    })
    .attr("width", 40)
    .attr("height", 40);


g.selectAll("circle")
    .data(insects[i], function(d) {
        return d
    })
    .enter().append("circle")
    .attr('cx', function(d, index) {
        return x(insects[i].length) / insects[i].length * index;
    })
    .attr("r", 20)
    .attr("cy", function(d, index) {
        return y.bandwidth() * i
    })
    .style("fill", function(d) {
        return "url(#insect" + (d[0].split(" ").join("")) + ")"
    });
}

Here is your updated plunker: http://plnkr.co/edit/WLC2ihpzsjDUgcuu910O?p=preview

PS: Your code is working, but I have to say that your for loop is unnecessary (and even awkward) in a D3 dataviz. This is not the D3 way of accessing the data. Thus, I suggest you completely refactor your code in that block.

Gerardo Furtado
  • 100,839
  • 9
  • 121
  • 171
  • I tried getting rid of the outer for loop by adding .forEach loops to `.data`, but unfortunately I couldn't get it to work. – I Like Dec 25 '16 at 21:28