0

I know it's there're a few topics that discuss the issue but non of the found results worked for me. I'm trying to modify the classic d3 network graph Les miserables example (d3v4 version HERE: https://bl.ocks.org/mbostock/4062045) to add different images for different nodes - the relative path of the file being given as one of the node attributes, eg.

 {"id": "Valjean", "group": 1, img: "images/male.png"},

What I'm trying to achieve is similar to this:https://bl.ocks.org/mbostock/950642 but made in d3v4, and different images for different nodes.

All examples that I found (also this promissing code snippet, which unfortunately doesnt't work for me) point me to similar approach, which looks more or less like this(both in d3 v4 and v3):

node.append("image")
      .attr("xlink:href", function(d) { return d.img })
      .attr("x", -8)
      .attr("y", -8)
      .attr("width", 16)
      .attr("height", 16);

However, despite a few hours spent I can not make it work. Any ideas?

Dominix
  • 935
  • 8
  • 14

1 Answers1

2

Here's a quick example which combines your v4 example with an image based on your v3 example:

<!DOCTYPE html>
<meta charset="utf-8">
<style>
  .links line {
    stroke: #999;
    stroke-opacity: 0.6;
  }
  
  .nodes circle {
    stroke: #fff;
    stroke-width: 1.5px;
  }
</style>
<svg width="960" height="600"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
  var svg = d3.select("svg"),
    width = +svg.attr("width"),
    height = +svg.attr("height");

  var color = d3.scaleOrdinal(d3.schemeCategory20);

  var simulation = d3.forceSimulation()
    .force("link", d3.forceLink().id(function(d) {
      return d.id;
    }))
    .force("charge", d3.forceManyBody())
    .force("center", d3.forceCenter(width / 2, height / 2));

  d3.json("https://jsonblob.com/api/9f7e2c48-8ccd-11e7-8b46-ef38640909a4", function(error, graph) {
    if (error) throw error;

    var link = svg.append("g")
      .attr("class", "links")
      .selectAll("line")
      .data(graph.links)
      .enter().append("line")
      .attr("stroke-width", function(d) {
        return Math.sqrt(d.value);
      });

    var node = svg.append("g")
      .attr("class", "nodes")
      .selectAll("image")
      .data(graph.nodes)
      .enter().append("image")
      .attr("xlink:href", "https://github.com/favicon.ico")
      .attr("x", -8)
      .attr("y", -8)
      .attr("width", 16)
      .attr("height", 16)
      .call(d3.drag()
        .on("start", dragstarted)
        .on("drag", dragged)
        .on("end", dragended));


    node.append("title")
      .text(function(d) {
        return d.id;
      });

    simulation
      .nodes(graph.nodes)
      .on("tick", ticked);

    simulation.force("link")
      .links(graph.links);

    function ticked() {
      link
        .attr("x1", function(d) {
          return d.source.x;
        })
        .attr("y1", function(d) {
          return d.source.y;
        })
        .attr("x2", function(d) {
          return d.target.x;
        })
        .attr("y2", function(d) {
          return d.target.y;
        });

      node
        .attr("transform", function(d) {
          return "translate(" + d.x + "," + d.y + ")";

        });
    }
  });

  function dragstarted(d) {
    if (!d3.event.active) simulation.alphaTarget(0.3).restart();
    d.fx = d.x;
    d.fy = d.y;
  }

  function dragged(d) {
    d.fx = d3.event.x;
    d.fy = d3.event.y;
  }

  function dragended(d) {
    if (!d3.event.active) simulation.alphaTarget(0);
    d.fx = null;
    d.fy = null;
  }
</script>  d.fx = d3.event.x;
    d.fy = d3.event.y;
  }

  function dragended(d) {
    if (!d3.event.active) simulation.alphaTarget(0);
    d.fx = null;
    d.fy = null;
  }
</script>

Adding in your images should then be as simple as changing and .attr("xlink:href" to:

.attr("xlink:href", function(d){
   return d.img;
)};
Mark
  • 106,305
  • 20
  • 172
  • 230
  • Works like a charm. Thanks! However if someone else runs into similar problem and wants to place images inside circles you can try defs as mentioned here: https://stackoverflow.com/questions/25881186/d3-fill-shape-with-image-using-pattern, and then check some node attribute in a function using if then conditions. – Dominix Aug 31 '17 at 07:55