Reputation: 992
So far I've used d3.svg.symbol() in a force-directed graph for distinguishing different node types from one another.
I'd now like to distinguish different node types by displaying node as a svg image. Following one of the examples I used the following code to display the node images:
var node = svg.selectAll("image.node").data(json.nodes);
var nodeEnter = node.enter().append("svg:g").append("svg:image")
.attr("class", "node")
.attr("xlink:href", function(d)
{
switch( d.source )
{
case "GEXP": return "img/node_gexp.svg";
case "CNVR": return "img/node_cnvr.svg";
case "METH": return "img/node_meth.svg";
case "CLIN": return "img/node_clin.svg";
case "GNAB": return "img/node_gnab.svg";
case "MIRN": return "img/node_mirn.svg";
case "SAMP": return "img/node_samp.svg";
case "RPPA": return "img/node_rppa.svg";
}
})
.attr("x", function(d) { return d.px; } )
.attr("y", function(d) { return d.py; } )
.attr("width", "50")
.attr("height", "50")
.attr("transform", "translate(-25,-25)")
.on("click", function(d) {
console.log("nodeclick");
} )
.on("mouseover", fade(0.10) )
.on("mouseout", fade(default_opacity) )
.call(force.drag);
This does display the svg images but I've two problems:
1) I want to scale the node sizes based on an attribute. From what I understand this can be done by supplying the "scale" attribute
transform="scale(something)"
to a suitable place, like to the image tag itself or to the group containing the image:
var node = svg.selectAll("image.node").data(json.nodes);
var nodeEnter = node.enter()
.append("svg:g")
.attr("transform", function(d)
{
var str = "scale(";
if( d.gene_interesting_score && d.gene_interesting_score > 0 )
{
return str + ( (d.gene_interesting_score - minScore ) / ( maxScore - minScore ) ) + ")";
}
return str + 0.7 + ")";
})
.append("svg:image")
....
As it happens, the scale() transform displaces the images: they are no longer on the endpoint of the edge. How do I resize the images properly when initializing the graph, preferrably so that the controlling mechanism is within a single function (e.g. so that I don't have to control x,y,width,height separately)?
2) When the graph is zoomed, Chrome blurs the images whereas in Firefox the image remains crispy (picture). How can this blur be avoided?
Edit: Based on duopixel's suggestions, the code is now:
var nodeGroup = svg.selectAll("image.node").data(json.nodes).enter()
.append("svg:g")
.attr("id", function(d) { return d.id; })
.attr("class", "nodeGroup")
.call(force.drag);
var node = nodeGroup.append("svg:image")
.attr("viewBox", "0 0 " + nodeImageW + " " + nodeImageH)
.attr("class", "node")
.attr("xlink:href", function(d)
{
switch( d.source )
{
case "GEXP": return "img/node_gexp.svg";
case "CNVR": return "img/node_cnvr.svg";
case "METH": return "img/node_meth.svg";
case "CLIN": return "img/node_clin.svg";
case "GNAB": return "img/node_gnab.svg";
case "MIRN": return "img/node_mirn.svg";
case "SAMP": return "img/node_samp.svg";
case "RPPA": return "img/node_rppa.svg";
}
})
.attr("width", nodeImageW)
.attr("height", nodeImageH)
.attr("transform", function(d)
{
var matrix = "matrix(";
var scale = 0.7; // sx & sy
if( d.gene_interesting_score && d.gene_interesting_score > 0 )
{
scale = ( (d.gene_interesting_score - minScore ) / ( maxScore - minScore ) ) * 0.5 + 0.25;
}
//console.log("id=" + d.id + ", score=" + scale );
matrix += scale + ",0,0," + scale + "," + ( d.x - ( scale*nodeImageW/2 ) ) + "," + ( d.y - ( scale*nodeImageH/2 ) ) + ")";
return matrix;
})
// .attr("transform", "translate(-25,-25)")
.on("click", function(d) {
console.log("nodeclick");
} )
.on("mouseover", fade(0.10) )
.on("mouseout", fade(node_def_opacity) );
It solves the problem #1, but not the second one: by selecting
var nodeImageH = 300;
var nodeImageW = 300;
The resulting svg image contains a lot empty space (seen by selecting the image with firebug's selection tool). The images were created in Inkscape, canvas size cropped to 50x50 which should be the correct viewing pixel size.
Upvotes: 4
Views: 1811
Reputation: 72385
#1
You need to set the transform origin. In SVG this means that you are going to have to use a transform matrix as answered here: https://stackoverflow.com/a/6714140/524555
#2
To solve the blurry scaling modify the viewBox, width, height
of your svg
files to start as large images (i.e. <svg viewBox="0 0 300 300" width="300" height="300">
.
Upvotes: 3