简体   繁体   中英

Direct Force Layout customization - d3.js

First question on Stack Overflow, so bear with me! I am new to d3.js, but have been consistently amazed by what others are able to accomplish with it... and almost as amazed by how little headway I've been able to make with it myself! Clearly I'm not grokking something, so I hope that the kind souls here can show me the light.

My intention is to make a connected graph based on the CSV data. My CSV data has different information some of them need to be represented as a nodes, some of them need to be used as an information for ltool-tip. I can read the data from CSV by using this snippet of code:

d3.csv("data/project.csv", function(links) {
  var nodesByName = {};

  // Create nodes for each unique source and target.
  links.forEach(function(link) {
    link.source = nodeByName(link.Project);
    link.target = nodeByName(link.Problem);
  });

Which will connect the node P1, to three nodes Problems (URLs). The problem comes after, I could not see the label on the selected nodes, How could I use the extract information in the CSV files such as "Date, Summary, URL". Is there any way to use mouse click, when I click the node I need its information appears on SVG board to be used for further analysis, such as showing the URL, short text of the summary, and when the user click on the board it should show the the complete information.

Here is the code that I used from this link

    var width = 800,
    height = 600;

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

var force = d3.layout.force()
    .gravity(0.2)
    .distance(200)
    .charge(-1500)
    .size([width, height]);

var container = svg.append("g")
    .attr("id", "container");

d3.csv("data/project.csv", function(links) {
  var nodesByName = {};

  // Create nodes for each unique source and target.
  links.forEach(function(link) {
    link.source = nodeByName(link.Project);
    link.target = nodeByName(link.Problem);
  });

  // Extract the array of nodes from the map by name.
  var nodes = d3.values(nodesByName);

  //define a scale for color mapping
  var colormapping = d3.scale.ordinal()
      .domain([0,nodes.length])
      .range(['#A700E6','#D95B96','#F4DA88','#22C1BE','#F24957','#DBEF91','#CF8EE8','#FF9B58','#B8FFC4','#91AEFF','#E873D3','#CCB298']);

  //create label node tooltip
  var labeltooltip = d3.select("body").append("div")
    .attr("class", "labeltooltip")
    .style("opacity", 1e-6);
  var zoom = d3.behavior.zoom()
     .scaleExtent([1, 10])
     .on("zoom", function() {
         container.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
     });

     svg.call(zoom);
  //links
  var link = container.selectAll(".line")
      .data(links)
      .enter()
      .append("line")
      .attr("stroke-width",4)
    //  .attr("stroke-width",function (d) { return linethickness(d.value); })
      .style("stroke", "gray");

  // Create the node circles.
  var node = container.selectAll(".node")
      .data(nodes)
      .enter()
      .append("circle")
      .attr("class", "node")
      .attr("r", 20)
      .attr("fill", function (d,i) {return d3.rgb(colormapping(i)); })
      .call(force.drag);


  // Start the force layout.
  force
      .nodes(nodes)
      .links(links)
      .on("tick", tick)
      .start();

 node.on("mousemove", function(d) {
    labeltooltip.selectAll("p").remove();
    labeltooltip.style("left", (d3.event.pageX+15) + "px").style("top", (d3.event.pageY-10) + "px");
    labeltooltip.append("p").attr("class", "tooltiptext").html("<span>Id: </span>" + d.Score );
        labeltooltip.append("p").attr("class", "tooltiptext").html("<span>Score: </span>" + d.Score);
  }); 


 node.on("mouseover", function(d) {
        labeltooltip.transition()
          .duration(500)
          .style("opacity", 1);
        link.style('stroke', function(l) {
            if (d === l.source || d === l.target)
              return d3.rgb('#C20606');
            else
              return 'gray';
            });
        link.style('opacity', function(o) {
            return o.source === d || o.target === d ? 1 : 0;
        });
        node.style("opacity", function(o) {
            if (o.id != d.id)
                return neighboring(d.id, o.id) ? 1 : 0;
        });
    }); 

 node.on("mouseout", function(d) {
        labeltooltip.transition()
          .duration(500)
          .style("opacity", 1e-6);
    link.style('stroke', 'gray');
    link.style('opacity', 1);
    node.style("opacity", 1);  
    });

var circletext = node.append("svg:text")
    .text(function(d) {return d.name;})
    .attr("class","labelText");
  function tick() {
    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; });
    circletext.attr("x", function(d) { return d.x-25; });
    circletext.attr("y", function(d) { return d.y-25;});
  }

  function nodeByName(name) {
    return nodesByName[name] || (nodesByName[name] = {name: name});
  }
    addZoomMoveIcon("#labelgraph");
});

UPDATE My CSV data

Project,Problem, Score,Data,Summary,id
p1,Problem1,10,2014-09-04T13:55:05.623-04:00, text text text text, 1
p1,Problem2,5,2014-09-04T13:55:05.623-04:00, text text text text,2
p1,Problem3,11,2014-09-04T13:55:05.623-04:00, text text text text,3

The final results that I am aiming for would be like the following figure: 在此处输入图片说明

Where the yellow box should appear on the SVG when the "problem" node clicked.

The first issue you need to address is where to correctly add the node text. Right now you are nesting the text inside the circles, but the text labels need to be siblings of the circles (they do not even need to live right next to each circle). You can replace this:

var circletext = node.append("svg:text")
.text(function(d) {return d.name;})
.attr("class","labelText");

With:

var circletext = container.selectAll("g")
.data(nodes)
.enter()
.append("text").text(function(d) {return d.name})
.attr("class", "labelText");

(Reference: https://www.dashingd3js.com/svg-text-element )

Next though, if you want to display the Score/Data/Summary note that these are attached to the links, not the nodes (there are only three of them, not four). However, at the point of node creation, you can pass this info from the link to the node. Modify the nodeByName function to add the new attributes for the nodes:

function nodeByName(name,score,data,summary) {
    return nodesByName[name] || (nodesByName[name] = {name: name, score: score, data: data, summary: summary});
}

Then modify the call to this function when the links are being created:

links.forEach(function(link) {
    link.source = nodeByName(link.Project);
    link.target = nodeByName(link.Problem,link.Score,link.Data,link.Summary);
});

To get the labeltooltip to display in a stationary spot, and not next to the nodes, you'll need to remove the bits on the node mouseover and mouseout events that move the div around, and probably just add the div to the html instead of dynamically adding it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM