StepTNT
StepTNT

Reputation: 3967

D3 force layout only shows one link

I'm working with the graph taken from this sample and I'm trying to change its data to show some stats taken from Spotify.

enter image description here

I've made a sample JSON file and edited the script to make it work with the new data format, and everything seems fine. The only problem is that my links are not shown, I've got only one edge between two nodes.

Here's my sample JSON:

{
    "tracks": [{
        "date": "2014-05-11",
        "country": "global",
        "track_url": "https:\/\/play.spotify.com\/track\/7b71WsDLb8gG0cSyDTFAEW",
        "track_name": "Summer",
        "artist_name": "Calvin Harris",
        "artist_url": "https:\/\/play.spotify.com\/artist\/7CajNmpbOovFoOoasH2HaY",
        "album_name": "Summer",
        "album_url": "https:\/\/play.spotify.com\/album\/0IGsZsrvIe5AQKvMmVobYq",
        "artwork_url": "http:\/\/o.scdn.co\/300\/ca67ba1425e4308104ecae9fc493f3ef96ceb72a",
        "num_streams": 8350015,
        "index": 0,
        "links": [1,
        3]
    },
    {
        "date": "2014-05-11",
        "country": "global",
        "track_url": "https:\/\/play.spotify.com\/track\/3s4U7OHV7gnj42VV72eSZ6",
        "track_name": "Rather Be feat. Jess Glynne",
        "artist_name": "Clean Bandit",
        "artist_url": "https:\/\/play.spotify.com\/artist\/6MDME20pz9RveH9rEXvrOM",
        "album_name": "Rather Be feat. Jess Glynne",
        "album_url": "https:\/\/play.spotify.com\/album\/4UB0J5V3JsZZtNR360pZ6r",
        "artwork_url": "http:\/\/o.scdn.co\/300\/a1babd2524962d4703a6c1aa9dfddcc2018713fd",
        "num_streams": 7946735,
        "index": 1,
        "links": [4,
        0,
        8]
    },
    {
        "date": "2014-05-11",
        "country": "global",
        "track_url": "https:\/\/play.spotify.com\/track\/5Sf3GyLEAzJXxZ5mbCPXTu",
        "track_name": "Waves - Robin Schulz Radio Edit",
        "artist_name": "Mr. Probz",
        "artist_url": "https:\/\/play.spotify.com\/artist\/33W1pnW9zScZtYTnAoWnOT",
        "album_name": "Waves",
        "album_url": "https:\/\/play.spotify.com\/album\/7l4LGPXk2mB80WgXy4VeuB",
        "artwork_url": "http:\/\/o.scdn.co\/300\/ccfb05bea829a2dc0c736f591a22eac50c18aa56",
        "num_streams": 7117296,
        "index": 2,
        "links": [3,
        6]
    },
    {
        "date": "2014-05-11",
        "country": "global",
        "track_url": "https:\/\/play.spotify.com\/track\/6PtXobrqImYfnpIxNsJApa",
        "track_name": "Bad (feat. Vassy) - Radio Edit",
        "artist_name": "David Guetta",
        "artist_url": "https:\/\/play.spotify.com\/artist\/1Cs0zKBU1kc0i8ypK3B9ai",
        "album_name": "Bad",
        "album_url": "https:\/\/play.spotify.com\/album\/6CvEd1L1KJZ8g3wIwCZYvF",
        "artwork_url": "http:\/\/o.scdn.co\/300\/2591b40c4b58e2b5ef831f4266b4b96b46c0462d",
        "num_streams": 6350375,
        "index": 3,
        "links": [2,
        0,
        4,
        9]
    },
    {
        "date": "2014-05-11",
        "country": "global",
        "track_url": "https:\/\/play.spotify.com\/track\/3bTZ9geYjyj9uBIT6gL5N6",
        "track_name": "All of Me",
        "artist_name": "John Legend",
        "artist_url": "https:\/\/play.spotify.com\/artist\/5y2Xq6xcjJb2jVM54GHK3t",
        "album_name": "All of Me",
        "album_url": "https:\/\/play.spotify.com\/album\/1YdXQgntClL3BhIXB0xpgs",
        "artwork_url": "http:\/\/o.scdn.co\/300\/d4b391b75d9f80032fb7124e47ff44291181f63d",
        "num_streams": 6295389,
        "index": 4,
        "links": [1,
        3,
        5]
    },
    {
        "date": "2014-05-11",
        "country": "global",
        "track_url": "https:\/\/play.spotify.com\/track\/5jrdCoLpJSvHHorevXBATy",
        "track_name": "Dark Horse",
        "artist_name": "Katy Perry",
        "artist_url": "https:\/\/play.spotify.com\/artist\/6jJ0s89eD6GaHleKKya26X",
        "album_name": "PRISM",
        "album_url": "https:\/\/play.spotify.com\/album\/5MQBzs5YlZlE28mD9yUItn",
        "artwork_url": "http:\/\/o.scdn.co\/300\/96fb8b4d972bba2ac566820985437aa39109b512",
        "num_streams": 5982051,
        "index": 5,
        "links": [4,
        6]
    },
    {
        "date": "2014-05-11",
        "country": "global",
        "track_url": "https:\/\/play.spotify.com\/track\/3oxO64VclwEDBoJWkeneBW",
        "track_name": "Fancy",
        "artist_name": "Iggy Azalea",
        "artist_url": "https:\/\/play.spotify.com\/artist\/5yG7ZAZafVaAlMTeBybKAL",
        "album_name": "Fancy",
        "album_url": "https:\/\/play.spotify.com\/album\/5oX3sr8ft9IwnI090Xf35t",
        "artwork_url": "http:\/\/o.scdn.co\/300\/d71d05ded1704bcf849465dd5e76e196847d1c0a",
        "num_streams": 5746330,
        "index": 6,
        "links": [5,
        2]
    },
    {
        "date": "2014-05-11",
        "country": "global",
        "track_url": "https:\/\/play.spotify.com\/track\/27jdUE1EYDSXZqhjuNxLem",
        "track_name": "Magic",
        "artist_name": "Coldplay",
        "artist_url": "https:\/\/play.spotify.com\/artist\/4gzpq5DPGxSnKTe4SA8HAU",
        "album_name": "Magic",
        "album_url": "https:\/\/play.spotify.com\/album\/4cCfFozyo6JC8acN8uIP7u",
        "artwork_url": "http:\/\/o.scdn.co\/300\/95f5cbdb03db43c16046562c5f85cc2e3f77b596",
        "num_streams": 5422672,
        "index": 7,
        "links": [9,
        8]
    },
    {
        "date": "2014-05-11",
        "country": "global",
        "track_url": "https:\/\/play.spotify.com\/track\/2stPxcgjdSImK7Gizl8ZUN",
        "track_name": "The Man",
        "artist_name": "Aloe Blacc",
        "artist_url": "https:\/\/play.spotify.com\/artist\/0id62QV2SZZfvBn9xpmuCl",
        "album_name": "Lift Your Spirit",
        "album_url": "https:\/\/play.spotify.com\/album\/14JRI2yc9nKosojndoQxTv",
        "artwork_url": "http:\/\/o.scdn.co\/300\/788afaaa762c8a613c21b883ae4f00019cd471ad",
        "num_streams": 5351668,
        "index": 8,
        "links": [1,
        7]
    },
    {
        "date": "2014-05-11",
        "country": "global",
        "track_url": "https:\/\/play.spotify.com\/track\/3cySlItpiPiIAzU3NyHCJf",
        "track_name": "Problem",
        "artist_name": "Ariana Grande",
        "artist_url": "https:\/\/play.spotify.com\/artist\/66CXWjxzNUsdJxJ2JdwvnR",
        "album_name": "Problem",
        "album_url": "https:\/\/play.spotify.com\/album\/5A6pzfa725ZA3rhDllmX58",
        "artwork_url": "http:\/\/o.scdn.co\/300\/667dfa17ba7e03a4a3d99a07ae1c6efb398cf947",
        "num_streams": 4590768,
        "index": 9,
        "links": [7,
        3]
    }],
    "links": [{
        "source": 0,
        "target": 1,
        "weight": 5
    },
    {
        "source": 0,
        "target": 3,
        "weight": 5
    },
    {
        "source": 1,
        "target": 4,
        "weight": 5
    },
    {
        "source": 1,
        "target": 0,
        "weight": 5
    },
    {
        "source": 1,
        "target": 8,
        "weight": 5
    },
    {
        "source": 2,
        "target": 3,
        "weight": 5
    },
    {
        "source": 2,
        "target": 6,
        "weight": 5
    },
    {
        "source": 3,
        "target": 2,
        "weight": 5
    },
    {
        "source": 3,
        "target": 0,
        "weight": 5
    },
    {
        "source": 3,
        "target": 4,
        "weight": 5
    },
    {
        "source": 3,
        "target": 9,
        "weight": 5
    },
    {
        "source": 4,
        "target": 1,
        "weight": 5
    },
    {
        "source": 4,
        "target": 3,
        "weight": 5
    },
    {
        "source": 4,
        "target": 5,
        "weight": 5
    },
    {
        "source": 5,
        "target": 4,
        "weight": 5
    },
    {
        "source": 5,
        "target": 6,
        "weight": 5
    },
    {
        "source": 6,
        "target": 5,
        "weight": 5
    },
    {
        "source": 6,
        "target": 2,
        "weight": 5
    },
    {
        "source": 7,
        "target": 9,
        "weight": 5
    },
    {
        "source": 7,
        "target": 8,
        "weight": 5
    },
    {
        "source": 8,
        "target": 1,
        "weight": 5
    },
    {
        "source": 8,
        "target": 7,
        "weight": 5
    },
    {
        "source": 9,
        "target": 7,
        "weight": 5
    },
    {
        "source": 9,
        "target": 3,
        "weight": 5
    }]
}

and here's my code (I'm not pasting everything, just the bits that I've changed from the original)

d3.json(
    'latest-new.json',
    function(data) {
    // Declare the variables pointing to the node & link arrays
    var nodeArray = data.tracks;
    var linkArray = data.links;
    console.log(data.links);
    var streamsRange = d3.extent(data.tracks, function(el){
                                            return el.num_streams;
                                        });
    minLinkWeight = 
      Math.min.apply( null, linkArray.map( function(n) {return n.weight;} ) );
    maxLinkWeight = 
      Math.max.apply( null, linkArray.map( function(n) {return n.weight;} ) );

    // Add the node & link arrays to the layout, and start it
    force
      .nodes(nodeArray)
      .links(linkArray)
      .start();

    // A couple of scales for node radius & edge width
    var node_size = d3.scale.linear()
      .domain(streamsRange) // we know score is in this domain
      .range([10,25])
      .clamp(true);
    var edge_width = d3.scale.pow().exponent(8)
      .domain( [minLinkWeight,maxLinkWeight] )
      .range([1,3])
      .clamp(true);

    /* Add drag & zoom behaviours */
    svg.call( d3.behavior.drag()
          .on("drag",dragmove) );
    svg.call( d3.behavior.zoom()
          .x(xScale)
          .y(yScale)
          .scaleExtent([1, 6])
          .on("zoom", doZoom) );

    // ------- Create the elements of the layout (links and nodes) ------

    var networkGraph = svg.append('svg:g').attr('class','grpParent');
    // links: simple lines
    var graphLinks = networkGraph.append('svg:g').attr('class','grp gLinks')
      .selectAll("line")
      .data(linkArray, function(d) {console.log(d); return d.source.id+'-'+d.target.id;} )
      .enter().append("line")
      .style('stroke-width', function(d) { return edge_width(d.weight);} )
      .attr("class", "link");

    // nodes: an SVG circle
    var graphNodes = networkGraph.append('svg:g').attr('class','grp gNodes')
      .selectAll("circle")
      .data( nodeArray, function(d){return d.track_name} )
      .enter().append("svg:circle")
      .attr('id', function(d) { return "c" + d.index; } )
      .attr('class', function(d) { return 'node level'+Math.round(Math.random()*10);} )
      .attr('r', function(d) { return node_size(d.num_streams); } )
      .attr('pointer-events', 'all')
      //.on("click", function(d) { highlightGraphNode(d,true,this); } )    
      .on("click", function(d) { showMoviePanel(d); } )
      .on("mouseover", function(d) { highlightGraphNode(d,true,this);  } )
      .on("mouseout",  function(d) { highlightGraphNode(d,false,this); } );

    // labels: a group with two SVG text: a title and a shadow (as background)
    var graphLabels = networkGraph.append('svg:g').attr('class','grp gLabel')
      .selectAll("g.label")
      .data( nodeArray, function(d){return d.track_name} )
      .enter().append("svg:g")
      .attr('id', function(d) { return "l" + d.index; } )
      .attr('class','label');
   ...
);

My guess is that there's a problem in this lines:

var graphLinks = networkGraph.append('svg:g').attr('class','grp gLinks')
  .selectAll("line")
  .data(linkArray, function(d) {console.log(d); return d.source.id+'-'+d.target.id;} )
  .enter().append("line")
  .style('stroke-width', function(d) { return edge_width(d.weight);} )
  .attr("class", "link");

because d.source.id and d.targer.id are null(there's no id field in my data). So I thought to replace them with track_url or index, just to give some existing property and see what happens.

Using one of the previous properties makes my graph a complete graph, because EVERY node is connected to the others.

I'm quite sure that I'm doing something really wrong, but my JSON seems quite similar to every example and I don't understand where the problem is.

Upvotes: 1

Views: 302

Answers (1)

jithinpt
jithinpt

Reputation: 1244

In the force layout, each link in the links array needs to indicate its source and target nodes in one of following 2 ways:

(i) Set source and target attributes of the link to the indices of the corresponding nodes in the nodes array. Eg: If array of nodes is [X,Y,Z] and there is a link from X -> Z, then that link should be represented as {source: 0, target: 2}

(ii) Set source and target attributes of the link to references to the actual nodes Eg: If array of nodes is [X,Y,Z] and there is a link from X -> Z, then that link should be represented as {source: X, target: Z}

In your case, it seems that the links array has already been created with the right format. So, you merely need to pass the links array as the data binding when creating the edge lines.

So, replacing data(linkArray, function(d) {console.log(d); return d.source.id+'-'+d.target.id;}) with just .data(linkArray) in the graphLinks snippet should most probably fix the issue.

Since the entire code wasn't available, I didn't test this solution. So, let me know in case it doesn't fix the issue.

Upvotes: 1

Related Questions