2

I've been attempting to solve this problem for days, and am completely stumped.

I'm using this network implementation walk-through: http://flowingdata.com/2012/08/02/how-to-make-an-interactive-network-visualization/

enter image description here

I have successfully created my visualization through this walk-through, and now would like to replace a node with a small picture, based on the node's value.

This is a great example of code to work from, where every node was replaced with an image. http: //bl.ocks .org/mbostock/950642

Specifically:

node.append("image")
.attr("xlink:href", "https://github.com/favicon.ico")
.attr("x", -8)
.attr("y", -8)
.attr("width", 16)
.attr("height", 16);

The only issue is that this code is in JavaScript, and my implementation of the network graph is written in CoffeeScript.

I tried creating my own JavaScript file with the code above and linking to it, however this did not work for me, and quite frankly I don't know if that is the best approach to take anyway.

I tried converting the code from JavaScript into CoffeeScript through this JS to CoffeeScript tool, http://js2coffee.org/, however I am not familiar with CoffeeScript enough to add the code in the correct location... and I feel like I've tried every spot with no luck.

What I'm exactly trying to do is to put a specific picture in place of a node based on the data contained by the node. I would prefer to have an if statement in the CoffeeScript itself to insert a picture based on the node selected, (could be the name or group or whatever.) I would also like to have the text label for each node as well, displaying, say, "amount," however I still need to research more on how to do that one.

Sample node:

"nodes" : [{
"match" : "1.0",
"name" : "Airplane",
"virtualAmount" : "1000",
"artist" : "Airplane",
"amount" : "999.99",
"id" : "a1234",
"playcount" : "500",
"group" : "airplanePic.jpg"
}, {

Thanks! Any help would be very much appreciated!

Edit: (with my code)

Thank you Lars, I was unaware of the inability to use an image with an SVG. Here is the code I am working with:

This is the CoffeeScript section that I believe I need to edit to get my desired SVG file to replace what is currently a circle for a node.

# enter/exit display for nodes
updateNodes = () ->
  node = nodesG.selectAll("circle.node")
    .data(curNodesData, (d) -> d.id)

node.enter().append("circle")
  .attr("class", "node")
  .attr("cx", (d) -> d.x)
  .attr("cy", (d) -> d.y)
  .attr("r", (d) -> d.radius)
  .style("fill", (d) -> nodeColors(d.artist))
  .style("stroke", (d) -> strokeFor(d))
  .style("stroke-width", 1.0)

I have been trying to use an if statement, like this, however I'm new to CoffeeScript so be gentle.

if d.group is "airplane"     #comment: or whatever group name I'm looking for
.attr("src", "tinyWhale.jpg")

However, I'm now aware that this won't work since I can't import an image to an SVG. I'm still very confused as to how to replace the node with an SVG, even after reading Lar's comment and linked question.

Would I be able to just create an if statement and replace the circle with a googled svg file?

Thanks again for the help.

Update 2: Thanks so much Lars, I am trying to add this to the vis.coffee file, however it breaks when I add any of the code. Here is how I am adding the code:

The 4th .attr is the code added.

node.enter().append("circle")
  .attr("class", "node")
  .attr("cx", (d) -> d.x)
  .attr("cy", (d) -> d.y)
  .attr("r", (d) -> d.radius) #this is the code added
  .attr("fill", (d) -> "url(#" + d.group + ")")
  .style("fill", (d) -> nodeColors(d.artist))
  .style("stroke", (d) -> strokeFor(d))
  .style("stroke-width", 1.0)

And I added this here, which also breaks the code. Am I putting this in the entirely wrong spot?

# Starting point for network visualization
# Initializes visualization and starts force layout
network = (selection, data) ->
# format our data
allData = setupData(data)

# create our svg and groups
vis = d3.select(selection).append("svg")
  .attr("width", width)
  .attr("height", height)
linksG = vis.append("g").attr("id", "links")
nodesG = vis.append("g").attr("id", "nodes")

defs = svg.append("defs")

defs.selectAll("pattern")
  .data(curNodesData)
  .append("pattern")
  .attr("id", (d) -> d.group)
  .append("image")
  .attr("xlink:href", (d) -> d.group)

Thanks for your help and patience!

Here is my vis.coffee file: https:// dl.dropboxusercontent .com/u/18496047/vis.coffee Added spaces because it won't let me have more than one link in the question.

Edit 3: Using this to go off of, which I hope will help me figure out the CoffeeScript node implementation.

# create node objects from original data
# that will serve as the data behind each
# bubble in the vis, then add each node
# to @nodes to be used later
create_nodes: () =>
  @data.forEach (d) =>
    node = {
      id: d.id
      radius: @radius_scale(d.total_amount)
      value: d.total_amount
      name: d.tweet_rate
      org: d.organization
      group: d.tweet_amount
      top_conv: d.top_conv
      x: Math.random() * 900
      y: Math.random() * 800
    }
    @nodes.push node

  @nodes.sort (a,b) -> b.value - a.value


# create svg at #vis and then 
# create circle representation for each node
create_vis: () =>
  @vis = d3.select("#vis").append("svg")
    .attr("width", @width)
    .attr("height", @height)
    .attr("id", "svg_vis")

  @circles = @vis.selectAll("circle")
    .data(@nodes, (d) -> d.id)

  # used because we need 'this' in the 
  # mouse callbacks
  that = this

  # radius will be set to 0 initially.
  # see transition below
  @circles.enter().append("circle")
    .attr("r", 0)
    .attr("fill", (d) => @fill_color(d.group))
    .attr("stroke-width", 2)
    .attr("stroke", (d) => d3.rgb(@fill_color(d.group)).brighter(5))
    .attr("id", (d) -> "bubble_#{d.id}")
    .on("mouseover", (d,i) -> that.show_details(d,i,this))
    .on("mouseout", (d,i) -> that.hide_details(d,i,this))

  # Fancy transition to make bubbles appear, ending with the
  # correct radius
  @circles.transition().duration(2000).attr("r", (d) -> d.radius)

Edit 4:

I converted the CoffeeSctipt to JavaScript for readability and my own comfortability.

Any answers can be contributed via JS or CoffeeScript.

Thanks... this problem is killing me.

Anyone who wants to help: plnkr.co/edit/DeI4a0gjg0p8ypRS2HUn?p=preview

VividD
  • 10,456
  • 6
  • 64
  • 111
SeaData
  • 21
  • 1
  • 3

2 Answers2

2

Why not replacing circles with your image:

node.enter().append("image")
  .attr("class", "node")
  .attr("href", "tinyWhale.jpg")
  .attr("x", function(d) { return d.x;})
  .attr("y", function(d) { return d.y;})
  .attr("width", function(d) { return d.radius;})
  .attr("height", function(d) { return d.radius;})

instead of:

node.enter().append('circle')...
sepans
  • 1,372
  • 13
  • 24
1

You can't use images like that directly in SVG, you need to define them as a pattern. See this question for more information. You haven't shown us your code, but I suspect that this is the source of your problem.

What you need to do is append a pattern for each image you need to the defs section of the SVG and then reference those patterns when you add the node. The code would look something like this.

defs = svg.append("defs")
defs.selectAll("pattern")
    .data(curNodesData)
    .append("pattern")
    .attr("id", (d) -> d.group)
    .append("image")
    .attr("xlink:href", (d) -> d.group)

You may need to set the dimensions for the patterns and images. Then later you can do the following

node.enter().append("circle")
    .attr("fill", (d) -> "url(#" + d.group + ")")

You may need to adjust the way the pattern IDs are generated, especially if you start using absolute URLs.

Community
  • 1
  • 1
Lars Kotthoff
  • 107,425
  • 16
  • 204
  • 204
  • Thanks Lars, I have updated my questing with some of my code, and am trying to figure out how to append a pattern for each image in the defs section. Thanks for pointing me in the right direction! You are saving me lots of time already. This is my first time posting, long time lurking on StackOverflow, so please let me know if there is anything i can do to help clarify my question, code, or anything else. I have added the code to my html section with an image xlink to the image, however am totally stuck there. – SeaData Jul 02 '13 at 15:42
  • Thanks Lars! I updated my attempted implementation, however, my lack of experience with CoffeeScript is really hurting me. I must be putting the code in the wrong spot. – SeaData Jul 02 '13 at 16:23
  • At this point it would be really helpful if you could provide a complete example of what you're trying to do on something like [jsfiddle](http://jsfiddle.net/). I'm slightly confused as to how the various bits of code you've posted fit together. – Lars Kotthoff Jul 02 '13 at 18:37
  • Hi Lars, I tried to insert my progress in jsfiddle, however am having major issues getting it to show up. The code is fundamentally the same as http://flowingdata.com/2012/08/02/how-to-make-an-interactive-network-visualization/ , as the only major changes made were to get my own data in the files, which I'm not able to upload. :/ Good news, I found the author's comments that he has seen an attempt to impliment images for nodes at this link, but I'm still struggling to impliment. http://apike.ca/prog_svg_images.html Poor JS Fiddle Attempt. jsfiddle.net/seadata/5nLX8 Thanks for the help. – SeaData Jul 02 '13 at 20:02
  • I wish I could make the question more simple and straight forward, and could give you a better code base in jsfiddle to work from. Thanks for your help Lars, and I realize this is a pretty convoluted mess of information, so if I'm not giving you the right info to go on, I apologize. Have a good one man. – SeaData Jul 02 '13 at 20:04
  • The code would go into the `updateNodes` function (line 311). Can't really help you without a working example. – Lars Kotthoff Jul 02 '13 at 20:36
  • Okay, thanks man. I tried uploading everything to JsFiddle, but it didn't work. http://jsfiddle.net/seadata/p8sMb/1/ – SeaData Jul 02 '13 at 21:05
  • YES! Okay Lars, thanks a ton, took a while, but I got it up and running! http://plnkr.co/edit/DeI4a0gjg0p8ypRS2HUn?p=preview So to reiterate after all the mess I've made :P, I am trying to make a node into an image on my server, (or another svg or whatever), depending on a property of the node. You can use whatever right now since I uploaded the template and not the changed version I made. Thanks so much man, seriously. This has been driving me crazy and I'm determined to get it figured out!!! :D – SeaData Jul 02 '13 at 21:53