Nayana_Das
Nayana_Das

Reputation: 1817

D3 network chart with arrow head pointing towards source node

Here I'm working with D3 Network chart, in my fiddle, you can see arrow head pointing towards target node (right side), see the below screenshot: enter image description here

But what i need is the opposite, like shown below: enter image description here

Does anybody know how I can accomplish this?

Here is my code:

var width = 500
height = 550;

var cluster = d3.layout.cluster()
.size([height - height * 0, width - width * 0.5]);

var diagonal = d3.svg.diagonal()
.projection(function(d) {
    return [d.y, d.x];
});

var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.attr("style", "padding-left:5%")
.attr("pointer-events", "all")
.call(d3.behavior.zoom().on("zoom", redraw));

 // Define the div for the tooltip
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);

var vis = svg
.append("svg:g");

vis.append("svg:rect")
.attr("width", width)
.attr("height", height)
.attr("fill", 'white');

function redraw() {
   vis.attr("transform",
     "translate(" + d3.event.translate + ")" +
     " scale(" + d3.event.scale + ")");
 }

 var root = {
  "name": "output",
  "children": [{
    "name": "sum",
    "children": [{
        "name": "Base",
        "children": [],
        "weight": 1.0000002576768052
    }, {
        "name": "H0",
        "children": [],
        "weight": 2.5767680512326025e-7
    }]
  }]
};

var nodes = cluster.nodes(root),
 links = cluster.links(nodes);

vis.append("svg:defs").selectAll("marker")
.data(["end"]) // Different link/path types can be defined here
.enter().append("svg:marker") // This section adds in the arrows
.attr("id", String)
.attr("viewBox", "0 -5 10 10")
.attr("refX", 15)
.attr("refY", -1.5)
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("orient", "auto")
.append("svg:path")
.attr("d", "M0,-5L10,0L0,5");

var link = vis.selectAll(".link")
.data(links)
.enter().append("path")
.attr("class", "link")
.attr("marker-end", "url(#end)")
.attr("d", diagonal);


var node = vis.selectAll(".node")
.data(nodes)
.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) {
    return "translate(" + d.y + "," + d.x + ")";
})
.on("mouseover", mouseover)
.on("mouseout", mouseout);

node.append("circle")
.attr("r", 4.5)
.style("fill", "#3182bd");

node.append("svg:text")
 .attr("dx", function(d) {
    return d.children ? -8 : 8;
})
 .attr("dy", 3)
 .style("text-anchor", function(d) {
    return d.children ? "end" : "start";
 })
 .style("fill", "#3182bd")
 .text(function(d) {
    return d.name;
 });

function mouseover() {
  d3.select(this).select("circle").transition()
    .duration(750)
    .attr("r", 9)
  d3.select(this).select("text").transition()
    .duration(750)
    .style("stroke-width", ".5px")
    .style("font", "22.8px serif")
    .style("opacity", 1);
}

function mouseout() {
  d3.select(this).select("circle").transition()
    .duration(750)
    .attr("r", 4.5)
  d3.select(this).select("text").transition()
    .duration(750)
    .style("font", "12px serif")
    .style("opacity", 0.8);
}

d3.select(self.frameElement).style("height", height + "px");

Upvotes: 0

Views: 945

Answers (2)

Mauricio Poppe
Mauricio Poppe

Reputation: 4876

You can achieve this in two simple steps

  • put the marker on the other end of the path .attr("marker-start", "url(#end)")
  • rotate the marker 180 degrees .attr("orient", 180)

There was not enough info on MDN about the attributes of the marker however this page has more than enough :)

var width = 500
height = 550;

var cluster = d3.layout.cluster()
  .size([height - height * 0, width - width * 0.5]);

var diagonal = d3.svg.diagonal()
  .projection(function(d) {
    return [d.y, d.x];
  });

var svg = d3.select("body").append("svg")
  .attr("width", width)
  .attr("height", height)
  .attr("style", "padding-left:5%")
  .attr("pointer-events", "all")
  .call(d3.behavior.zoom().on("zoom", redraw));

// Define the div for the tooltip
var div = d3.select("body").append("div")
  .attr("class", "tooltip")
  .style("opacity", 0);

var vis = svg
  .append("svg:g");

vis.append("svg:rect")
  .attr("width", width)
  .attr("height", height)
  .attr("fill", 'white');



function redraw() {
  vis.attr("transform",
    "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
}

//var root = {"name":"Canada","children":[{"name":"Newfoundland","children":[{"name":"St. John's"}]},{"name":"PEI","children":[{"name":"Charlottetown"}]},{"name":"Nova Scotia","children":[{"name":"Halifax"}]},{"name":"New Brunswick","children":[{"name":"Fredericton"}]},{"name":"Quebec","children":[{"name":"Montreal"},{"name":"Quebec City"}]},{"name":"Ontario","children":[{"name":"Toronto"},{"name":"Ottawa"}]},{"name":"Manitoba","children":[{"name":"Winnipeg"}]},{"name":"Saskatchewan","children":[{"name":"Regina"}]},{"name":"Nunavuet","children":[{"name":"Iqaluit"}]},{"name":"NWT","children":[{"name":"Yellowknife"}]},{"name":"Alberta","children":[{"name":"Edmonton"}]},{"name":"British Columbia","children":[{"name":"Victoria"},{"name":"Vancouver"}]},{"name":"Yukon","children":[{"name":"Whitehorse"}]}]} ; 
var root = {
  "name": "output",
  "children": [{
    "name": "sum",
    "children": [{
      "name": "Base",
      "children": [],
      "weight": 1.0000002576768052
    }, {
      "name": "H0",
      "children": [],
      "weight": 2.5767680512326025e-7
    }]
  }]
};

var nodes = cluster.nodes(root),
  links = cluster.links(nodes);

vis.append("svg:defs").selectAll("marker")
  .data(["end"]) // Different link/path types can be defined here
  .enter().append("svg:marker") // This section adds in the arrows
  .attr("id", String)
  .attr("viewBox", "0 -5 10 10")
  .attr("refX", 15)
  .attr("refY", 0)
  .attr("markerWidth", 6)
  .attr("markerHeight", 6)
  .attr("orient", 180)
  .append("svg:path")
  .attr("d", "M0,-5L10,0L0,5");

var link = vis.selectAll(".link")
  .data(links)
  .enter().append("path")
  .attr("class", "link")
  .attr("marker-start", "url(#end)")
  .attr("d", diagonal);
/*.on("mouseover", function(d) {
	//console.log(d.children.weight);	
	        div.transition()		
                .duration(200)		
                .style("opacity", .9);		
            div	.html(d.y)	
                .style("left", (d3.event.pageX) + "px")		
                .style("top", (d3.event.pageY - 28) + "px");	
            })					
        .on("mouseout", function(d) {		
            div.transition()		
                .duration(500)		
                .style("opacity", 0);	
        });*/


var node = vis.selectAll(".node")
  .data(nodes)
  .enter().append("g")
  .attr("class", "node")
  .attr("transform", function(d) {
    return "translate(" + d.y + "," + d.x + ")";
  })
  .on("mouseover", mouseover)
  .on("mouseout", mouseout);

node.append("circle")
  .attr("r", 4.5)
  .style("fill", "#3182bd");

node.append("svg:text")
  .attr("dx", function(d) {
    return d.children ? -8 : 8;
  })
  .attr("dy", 3)
  .style("text-anchor", function(d) {
    return d.children ? "end" : "start";
  })
  .style("fill", "#3182bd")
  .text(function(d) {
    return d.name;
  });


/*var linktext = vis.append("svg:g").selectAll("g.linklabelholder").data(links);
	
    linktext.enter().append("g").attr("class", "linklabelholder")
     .append("text")
     .attr("class", "linklabel")
	 .style("font-size", "13px")
     .attr("x", "50")
	 .attr("y", "-20")
     .attr("text-anchor", "start")
	   .style("fill","#000")
	 .append("textPath")
    .attr("xlink:href",function(d,i) { return "#linkId_" + i;})
     .text(function(d) { 
	 return d.type; 
	 });*/

function mouseover() {
  d3.select(this).select("circle").transition()
    .duration(750)
    .attr("r", 9)
  d3.select(this).select("text").transition()
    .duration(750)
    .style("stroke-width", ".5px")
    .style("font", "22.8px serif")
    .style("opacity", 1);
}

function mouseout() {
  d3.select(this).select("circle").transition()
    .duration(750)
    .attr("r", 4.5)
  d3.select(this).select("text").transition()
    .duration(750)
    .style("font", "12px serif")
    .style("opacity", 0.8);
}

d3.select(self.frameElement).style("height", height + "px");
.link {
  fill: none;
  stroke: #ccc;
  opacity: 0.4;
  stroke-width: 1.5px;
}
.node circle {
  stroke: #fff;
  opacity: 0.8;
  stroke-width: 1.5px;
}
.node:not(:hover) .nodetext {
  display: none;
}
text {
  font: 12px serif;
  opacity: 0.8;
  pointer-events: none;
}
div.tooltip {
  position: absolute;
  text-align: center;
  width: 60px;
  height: 28px;
  color: black;
  padding: 2px;
  font: 12px sans-serif;
  background: lightsteelblue;
  border: 0px;
  border-radius: 8px;
  pointer-events: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

Upvotes: 2

user01
user01

Reputation: 901

Perhaps you could use the attr marker-start where you mark arrow at line

.attr("marker-end", "url(#end)")

.attr("marker-start", "url(#end)")

IIRC, the hash id is tied to some CSS defined path (ie, the arrow). This would put the shape at the start. You may have to tweak the path if the arrow is 'backwards'

Upvotes: 2

Related Questions