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/
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