简体   繁体   中英

Legend in force-directed graph

I have a force-directed graph that grabs some php-generated json and displays it. It can be seen here: http://www.testbed.medievalgeek.com/test_forcedirect.php .

I'd like to add a legend to the graph, and attempted to do so via modifying the tail bit of the example here: How to add a dynamic legend to a D3 force directed graph in Apex?

My overall javascript is here:

function nearest(value, min, max, steps) {
    var zerone = Math.round((value - min) * steps / (max - min)) / steps; // bring to 0-1 range
    return zerone * (max - min) + min;
}

var clientHeight = document.getElementById('dropdown').clientHeight;

var width = window.innerWidth - 10,
    height = window.innerHeight - clientHeight;

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height)
    .style("background-color", "black");

var force = d3.layout.force()
    .gravity(0.8)
    .distance(100)
    .size([width, height]);

var index = -1;

force.charge(function (d) {return d.weight * -250;});

d3.json(<?php echo "\"" . $url_string . "\""?>, function(error, json) {
    if (error) throw error;

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

    var link = svg.selectAll(".link")
   .data(json.links)
   .enter().append("line")
   .attr("class", "link")
   .attr('stroke', function(d) {return d.color; })
   .attr('stroke-width', 1)      
   .on("mouseover", function(d) {
       d3.select(this).attr('stroke-width', 2);      
    })
   .on("mouseout", function(d) {
       d3.select(this).attr('stroke-width',1);
    });

   var node = svg.selectAll(".node")
  .data(json.nodes)
  .enter().append("g")
  .attr("class", "node")
  .call(force.drag);

node.append("circle")
  .attr("r", function (d) {return nearest((Math.log(d.weight) *10), 1, 50, 10) || 10;})
  .style('fill', function(d) { return d.color; })
  .on("mouseover", function(d) {
        link.style('stroke-width', function(l) {
        if (d === l.target || d === l.source)
        return 2;
      })
        link.style('stroke', function(l) {
        if (d === l.target || d === l.source)
        return 'aqua'; 
      })
      .duration(150);      
  })
  .on("mouseout", function(d) {
    link.style('stroke-width', 1)
    link.style('stroke', function(d) {return d.color; })
    .duration(150);
  });


node.append("text")
  .attr("dx", function(d) {return  (nearest((Math.log(d.weight) *10), 1, 50, 10) + 5);})
  .attr("dy", ".35em")
  .text(function(d) {return d.name})
  .style('fill', 'lightcyan');

var legend = svg.selectAll(".legend")
 .data(color.domain())
 .enter().append("g")
 .attr("class", "legend")
 .attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });

legend.append("circle")
 .attr("r", 10)
 .style("fill", function(d) { return d.color; });

legend.append("text")
 .attr("dx", 10)
 .attr("dy", ".35em")
 .style("text-anchor", "end")
 .text(function(d) { return d.name; });
 .style('fill', 'lightcyan');

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("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });



  });
});

However, this results in a blank screen. If I try to more closely replicate the example by changing the legend text:

legend.append("text")
    .attr("x", width - 24)
    .attr("y", 9)
    .attr("dy", ".35em")
    .style("text-anchor", "end")
    .text(function(d) { return d; });

The nodes appear, but they no longer distribute themselves according to weight and instead just appear in the upper left corner. There also appears to be no legend.

What would be the best way to lay the legend out and still have the force-directed aspects work? Most of the examples don't seem to work with what I already have.

This is actually really simple and I'm dumb for asking the question. But in case someone else is wondering, all you have to do is plot out each item. The following code might serve as a basis for someone to start. d.type and d.color are values passed in from the json that represent the item and the color it is denoted by the the graph, respectively.

 var listTypes = d3.set(json.nodes.map(function(d){return d.type})).values();

  listColors = [];

  listPositions = [];

  lineWidths = [];

  for(l = 0; l < listTypes.length; l++){
        for (var key in json.nodes){
          if (listTypes[l] === json.nodes[key].type) {
            if (listColors.indexOf(json.nodes[key].color) > -1)
            {}
            else
              {
                lineWidths.push(json.nodes[key].type.length);
                listColors.push(json.nodes[key].color);
                var xlegend = (Math.floor(l / 10) * 100 );
                var ycounter;
                var ylegend;
                var oldxlegend;

                if (l===0) {
                  ycounter = 1;
                }

                if (ycounter < 10) {
                    listPositions.push(ycounter * 20);
                    ycounter++;
                }
                else 
                {
                    listPositions.push(ycounter * 20);
                    ycounter = 1;                    
                }

              }
          }
          else {}
       }
      }

  console.log(listTypes);
  console.log(listColors);
  console.log(listPositions);

  svg.append("rect")
  .attr("class", "overlay")
  .attr("x",5)
  .attr("y", 10)
  .attr("width", Math.max.apply(Math,lineWidths) * 10.6)
  .attr("height", listTypes.length * 21.667)
  .style ("fill", "aliceblue")

  var legend = svg.selectAll(".legend")
          .data(listTypes)
        .enter().append("g")
          .attr("class", "legend")
          .attr("transform", function(d, i) { return "translate(" + (Math.floor(i / 10)  * 105) + ", " + listPositions[i] + ")"; })

        legend.append("rect")
          .attr("x", 10)
          .attr("width", 15)
          .attr("height", 15)
          .attr("fill", function(d,i) {return listColors[i];});

        legend.append("text")
          .attr("x", 30 )
          .attr("y", 6)
            .attr("dy", ".35em")
          .text(function(d,i){ return titleCase(listTypes[i].replace(/_/g, " "))});

          svg.append("g")
        .attr("class", "legend")

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