5

To the esteemed readers. I'm reasonably new in javascript and I have come across this problem. I'm trying to implement a modified version of this force directed graph:

http://mbostock.github.com/d3/ex/force.html

The json data is generated on the fly from a php script. The idea is to color all lines connecting to one specific node ( defined in a php script) in one color and all the others in shades of gray. I'm attempting to do it by matching the source variable in the json file to the variable from the php script and changing color when that is true like this:

  var link = svg.selectAll("line.link")
  .data(json.links)
  .enter().append("line")
  .attr("class", "link")
  .style("stroke-width", function(d) { return Math.sqrt(d.value);})
  .style("stroke-opacity", function(d) { return d.value/10;})
  .style("stroke", function(d) { 
  x = (tested == d.source) ?  return '#1f77b4' : '#707070';// <-- Attempt to change the color of the link when this is true.
  })

however this does not work. The script works fine but without the color change if I just do this

  var link = svg.selectAll("line.link")
  .data(json.links)
  .enter().append("line")
  .attr("class", "link")
  .style("stroke-width", function(d) { return Math.sqrt(d.value);})
  .style("stroke-opacity", function(d) { return d.value/10;})
  .style("stroke", function(d) { 
  return '#707070';
  })

I've been staring at this for days trying to figure out to get this done and I'm stuck. Any help would be greatly appreciated!!

Here is my complete script

<script type="text/javascript">

var width = 1200,
    height = 1200;

var color = d3.scale.category20();

var tested=<?php echo $tested_source;?>; //<-- the variable from php

var svg = d3.select("#chart").append("svg")
    .attr("width", width)
    .attr("height", height);

d3.json("data.json", function(json) {

var force = d3.layout.force()
    .charge(-130)
    .linkDistance(function(d) { return 500-(50*d.value);})
    .size([width, height]);

  force
      .nodes(json.nodes)
      .links(json.links)
      .start();

  var link = svg.selectAll("line.link")
      .data(json.links)
      .enter().append("line")
      .attr("class", "link")
      .style("stroke-width", function(d) { return Math.sqrt(d.value);})
      .style("stroke-opacity", function(d) { return d.value/10;})
      .style("stroke", function(d) { 
      x = (tested == d.source) ?  return '#1f77b4' : '#707070'; //<-- Attempt to change the color of the link when this is true. But is is not working...  :(
      })


  var node = svg.selectAll("circle.node")
      .data(json.nodes)
    .enter().append("circle")
      .attr("class", "node")
      .attr("r", 12)
      .style("fill", function(d) { return color(d.group); })
      .call(force.drag);

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

  force.on("tick", function() {
    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("cx", function(d) { return d.x; })
        .attr("cy", function(d) { return d.y; });
  });
});

</script>
VividD
  • 10,456
  • 6
  • 64
  • 111
user1378824
  • 207
  • 4
  • 13

1 Answers1

8

d.source is an object, you can't use == to determine if tested is a similar object. Have a look at this answer for more details on object equality.

If you want to test for a specific value of the d.source object described below, which I assume you want, you need to specify it.

Here is the source object architecture : (I'm using the example you pointed so the data comes from the miserables.json)

source: Object
    group: 4
    index: 75
    name: "Brujon"
    px: 865.6440689638284
    py: 751.3426708796574
    weight: 7
    x: 865.9584580575608
    y: 751.2658636251376

Now, here is the broken part in your code :

x = (tested == d.source) ?  return '#1f77b4' : '#707070';// <-- Attempt to change the color of the link when this is true.

It doesn't work because the return is misplaced. You're mixing ternary and return statements but you don't put them in the right order :

return test ? value_if_true : value_if_false;

if you want to assign the value to x anyway, you can do

x = test ? value_if_true : value_if_false;
return x;

You should do something like this :

return (tested == d.source) ? '#1f77b4' : '#707070';// <-- Attempt to change the color of the link when this is true.

That's for the general syntax, but this won't work as is You need to pick one of the value for your test for example :

return (tested === d.source.name) ? '#1f77b4' : '#707070';

Also, if the variable from PHP is a string you should do

var tested="<?php echo $tested_source;?>"; //<-- the variable from php

and in most cases you should use json_encode to map PHP variables into javascript ones.

As a final note, I would recommend using console functions coupled with Firebug's console panel if you're using Firefox, or the Chrome Developer Tool's console panel if you're using a Chromium based browser. It would allow you to debug your code more easily.


Working code

var width = 960,
  height = 500;

var color = d3.scale.category20();

var force = d3.layout.force().charge(-120).linkDistance(30).size([width, height]);

var svg = d3.select("#chart").append("svg").attr("width", width).attr("height", height);

var tested = 20;

d3.json("miserables.json", function (json) {
  force.nodes(json.nodes).links(json.links).start();

  var link = svg.selectAll("line.link")
  .data(json.links)
  .enter()
  .append("line")
  .attr("class", "link")
  .style("stroke-width", function (d) {
    return Math.sqrt(d.value);
  }).style("stroke-opacity", function (d) {
    return d.value / 10;
  }).style("stroke", function (d) {
    return (tested == d.source.index) ? '#ee3322' : '#707070'; //'#1f77b4'
  });

  var node = svg.selectAll("circle.node")
  .data(json.nodes)
  .enter()
  .append("circle")
  .attr("class", "node")
  .attr("r", 5)
  .style("fill", function (d) {
    return color(d.group);
  }).call(force.drag);

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

  force.on("tick", function () {
    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("cx", function (d) {
      return d.x;
    }).attr("cy", function (d) {
      return d.y;
    });
  });
});
Community
  • 1
  • 1
Jérémie Parker
  • 3,184
  • 2
  • 20
  • 33
  • Thanks! I'm a complete rookie when it comes to javascript. The suggestion does make the script work however it still does not change the color of the connecting lines. I think there might be a problem retrieving d.source from the json array. Help?? – user1378824 May 07 '12 at 03:36
  • I've edited my answer with some code regarding the source object that you're testing against `tested`. I think the problem comes from there. – Jérémie Parker May 07 '12 at 10:38
  • Thanks for the quick reply. Maybe I'm misunderstanding something but the object you are referring to is the node. I'm trying to change color of the connecting lines between the nodes. This value is defined in the "Links" in the miserables.json file. Am I misunderstanding something completely? – user1378824 May 07 '12 at 14:22
  • `.style("stroke", function(d) { console.dir(d.source); return (tested == d.source) ? '#1f77b4' : '#707070'; })` This will help you find why colors are not changing. Using firefox + firebug or chrome and the chrome developer tools. I don't think I can help you more than that. – Jérémie Parker May 07 '12 at 16:12
  • and fyi the source object I printed above is exactly the result of the `console.dir(d.source)` in my previous comment – Jérémie Parker May 07 '12 at 18:37
  • Thanks again for helping out. I really appreciate it. However, your suggestion still does not work. As I wrote before I am **not** looking to change the color of the node but only of the lines going to that node. You can see it here [link] (http://jsfiddle.net/HdqsU/) – user1378824 May 07 '12 at 19:04
  • What I'm saying is that the object you get there is `d` and it's a source. If you want to manipulate the link, then use `this` but I don't know what you are trying to test against `tested` so it's kind of hard to make meaningful test. You put `tested = 20`, but what should be equal to 20 ? – Jérémie Parker May 07 '12 at 20:16
  • Sorry. That was unclear. "tested" is the index of the node. So I can match the index value to the "source" and "target" in the link array. – user1378824 May 07 '12 at 20:39
  • I've added a working code. I've tested it and links that were from the node with the `index` equal to `20` were red (using `#ee3322` instead of your color `#1f77b4`, it was easiest to notice.) http://jsfiddle.net/HdqsU/1/ you can see that on one of the orange circle, the links are red/orange too. – Jérémie Parker May 08 '12 at 16:04
  • Phenomenal. I ended up adding this to get both source and targets: `return (tested == d.source.index || tested == d.target.index) ? '#ee3322' : '#707070';` . Thank you soooo much for your help! – user1378824 May 08 '12 at 19:27