2013-07-19 8 views
9

Ich habe eine Reihe von Daten, die ich mit d3.js visualisieren. Ich repräsentiereDatenPunkte in Form von Blasen, wobei die Konfiguration für Blasen ist wie folgt:Convert d3.js Blasen in zwangsweise/Schwerkraft basierte Layout

var dot = svg.selectAll("g") 
      .data(data) 
      .enter() 
      .append("g"); 

dot.append("circle") 
    .attr("class", "dot") 
    .attr("cx", function(d) { return xp(x(d)); }) 
    .attr("cy", function(d) { return yp(y(d)); }) 
    .style("fill", function(d) { return colorp(color(d)); }) 
    .attr("r", function(d) { return radiusp(radius(d)*2000000); }); 

dot.append("text") 
    .attr("x", function(d) { return xp(x(d)); }) 
    .attr("y", function(d) { return yp(y(d)); }) 
    .text(function(d) { return d.name; }) 

Wo xp, yp, colorp und radiusp wie folgt definiert sind:

var xp = d3.scale.log().domain([300, 1e5]).range([0, width]), 
    yp = d3.scale.linear().domain([10, 85]).range([height, 0]), 
    radiusp = d3.scale.sqrt().domain([0, 5e8]).range([0, 40]), 
    colorp = d3.scale.category10(); 

An diesem Punkt Die Blasen werden an ihren Positionen als statisch angezeigt (wobei die Position durch xp und yp definiert ist), während die Größe der Blase im Wesentlichen von Radiusp und die Farbe von colorp abhängt.

Im Moment bin ich ihnen zu zeigen, genau wie dieses Beispiel: http://bl.ocks.org/mbostock/4063269

enter image description here

Was ich brauche, ist sie in dieser Form angezeigt werden: http://jsfiddle.net/andycooper/PcjUR/1/

enter image description here

Das heißt: Sie sollten unter Verwendung der Schwerkraftfunktion gepackt werden, etwas Ladung haben, können gezogen werden und sich gegenseitig abstoßen. Ich kann sehen, dass es einen Weg durch d3.layout.force() gibt, aber nicht wirklich in der Lage das zu integrieren .. Ich werde wirklich dankbar sein, wenn Sie mir den richtigen Weg oder ein funktionierendes Beispiel oder sogar einen Hinweis vorschlagen können. Vielen Dank.

+0

noch keine Antworten? – user2480542

+0

Was ist das Problem, das Sie bei der Integration in 'd3.layout.force()' – user568109

+0

konfrontiert Das Problem ist, dass d3.layout.force() definiert cx- und cy-Koordinaten automatisch und ich bin nicht in der Lage, genau herauszufinden, wie ich es angehen und implementieren kann. Auch wenn ich xp- und yp-Variablen entferne, die die Positionierung von Blasen erzwingen, kann ich das nicht erreichen. Auf der ersten Seite habe ich versucht, das erzwungene Layout für diese Blasen zu implementieren, aber das war nicht erfolgreich. : -/ – user2480542

Antwort

2

Ich denke, Sie waren fast da, aber die Spezifikation Ihrer dot Variable ist nicht die beste. Ich würde es so verwandeln:

var dot = svg.selectAll(".dot") 
     .data(data) 
     .enter() 

Danach, sobald die Kreise aufgetragen worden sind, was Sie tun, ist, dass Sie ein force Layout erstellen, instanziieren es mit den Knoten, die Sie gerade erstellt haben, fügen Sie ein on("tick") Methode, und dann start das Layout. Ein Beispiel ist die folgende:

var force = d3.layout.force().nodes(data).size([width, height]) 
      .gravity(0) 
      .charge(0) 
      .on("tick", function(e){ 
       dot 
       .each(gravity(.2 * e.alpha)) 
        .each(collide(.5)) 
        .attr("cx", function (d) {return d.x;}) 
        .attr("cy", function (d) {return d.y;}); 
      }) 
      .start(); 

Um eine vollständige Antwort, werde ich auch hinzufügen, die gravity und collide Methoden von Ihrer Geige (mit angepassten Variablennamen)

function gravity(alpha) { 
     return function (d) { 
      d.y += (d.cy - d.y) * alpha; 
      d.x += (d.cx - d.x) * alpha; 
     }; 
    } 

    function collide(alpha) { 
     var padding = 6 
     var quadtree = d3.geom.quadtree(dot); 
     return function (d) { 
      var r = d.r + radiusp.domain()[1] + padding, 
       nx1 = d.x - r, 
       nx2 = d.x + r, 
       ny1 = d.y - r, 
       ny2 = d.y + r; 
      quadtree.visit(function (quad, x1, y1, x2, y2) { 
       if (quad.point && (quad.point !== d)) { 
        var x = d.x - quad.point.x, 
         y = d.y - quad.point.y, 
         l = Math.sqrt(x * x + y * y), 
         r = d.r + quad.point.r + (d.color !== quad.point.color) * padding; 
        if (l < r) { 
         l = (l - r)/l * alpha; 
         d.x -= x *= l; 
         d.y -= y *= l; 
         quad.point.x += x; 
         quad.point.y += y; 
        } 
       } 
       return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1; 
      }); 
     }; 
    } 

Ich denke, das Problem, das Sie hatte war, dass Sie vielleicht das Force-Layout auf das Element g jedes der Kreise angewendet haben, was leider nicht funktionierte. Ich hoffe, das wird dir eine Idee geben, wie es weitergeht. Ihre letzte Zeile der dot Deklaration fügte ein g Element für jeden Kreis hinzu, was ein wenig schwierig zu handhaben war.

Danke.

PS Ich gehe davon aus, dass die x, y und r Attribute Ihrer data enthalten die x, y und radius.