Markieren Sie den ausgewählten Knoten, seine Verknüpfungen und seine untergeordneten Knoten in einem erzwungenen D3-Diagramm

70

Ich arbeite an einem kraftgerichteten Graphen in D3. Ich möchte den Mouseover-Knoten, seine Links und seine untergeordneten Knoten hervorheben, indem ich alle anderen Knoten und Links auf eine geringere Deckkraft setze.

In diesem Beispiel, http://jsfiddle.net/xReHA/ , kann ich alle Links und Knoten ausblenden und dann die verbundenen Links einblenden, aber bisher konnte ich die nicht elegant einblenden verbundene Knoten, die untergeordnete Knoten des aktuell mit der Maus übermittelten Knotens sind.

Dies ist die Schlüsselfunktion aus dem Code:

function fade(opacity) {
    return function(d, i) {
        //fade all elements
        svg.selectAll("circle, line").style("opacity", opacity);

        var associated_links = svg.selectAll("line").filter(function(d) {
            return d.source.index == i || d.target.index == i;
        }).each(function(dLink, iLink) {
            //unfade links and nodes connected to the current node
            d3.select(this).style("opacity", 1);
            //THE FOLLOWING CAUSES: Uncaught TypeError: Cannot call method 'setProperty' of undefined
            d3.select(dLink.source).style("opacity", 1);
            d3.select(dLink.target).style("opacity", 1);
        });
    };
}

Ich erhalte eine Uncaught TypeError: Cannot call method 'setProperty' of undefinedFehlermeldung, wenn ich versuche, die Deckkraft eines Elements festzulegen, das ich aus der Datei source.target geladen habe. Ich vermute, dass dies nicht der richtige Weg ist, um diesen Knoten als d3-Objekt zu laden, aber ich kann keinen anderen Weg finden, um ihn zu laden, ohne alle Knoten erneut zu durchlaufen, um diejenigen zu finden, die dem Ziel oder der Quelle des Links entsprechen. Um die Leistung angemessen zu halten, möchte ich nicht mehr als nötig über alle Knoten iterieren.

Ich habe das Beispiel des Ausblendens der Links von http://mbostock.github.com/d3/ex/chord.html genommen :

Geben Sie hier die Bildbeschreibung ein

Dies zeigt jedoch nicht, wie die verbundenen untergeordneten Knoten geändert werden können.

Alle guten Vorschläge, wie dies gelöst oder verbessert werden kann, werden heftig positiv bewertet :)

Christopher Manning
quelle

Antworten:

93

Der Fehler liegt darin, dass Sie die Datenobjekte (d.source und d.target) anstelle der diesen Datenobjekten zugeordneten DOM-Elemente auswählen.

Die Hervorhebung der Zeilen funktioniert, aber ich würde Ihren Code wahrscheinlich in einer einzigen Iteration wie folgt kombinieren:

 link.style("opacity", function(o) {
   return o.source === d || o.target === d ? 1 : opacity;
 });

Das Hervorheben der benachbarten Knoten ist schwieriger, da Sie die Nachbarn für jeden Knoten kennen müssen. Diese Informationen sind mit Ihren aktuellen Datenstrukturen nicht so einfach zu ermitteln, da Sie nur über ein Array von Knoten und ein Array von Links verfügen. Vergessen Sie das DOM für eine Sekunde und fragen Sie sich, wie Sie feststellen würden, ob zwei Knoten aund bNachbarn sind?

function neighboring(a, b) {
  // ???
}

Eine teure Möglichkeit, dies zu tun, besteht darin, alle Links zu durchlaufen und zu prüfen, ob es einen Link gibt, der a und b verbindet:

function neighboring(a, b) {
  return links.some(function(d) {
    return (d.source === a && d.target === b)
        || (d.source === b && d.target === a);
  });
}

(Dies setzt voraus, dass Links ungerichtet sind. Wenn Sie nur vorwärts verbundene Nachbarn hervorheben möchten, entfernen Sie die zweite Hälfte des OP.)

Eine effizientere Methode zur Berechnung ist, wenn Sie dies häufig tun müssen, eine Karte oder eine Matrix, mit der eine zeitlich konstante Suche ermöglicht, um zu testen, ob a und b Nachbarn sind. Zum Beispiel:

var linkedByIndex = {};
links.forEach(function(d) {
  linkedByIndex[d.source.index + "," + d.target.index] = 1;
});

Jetzt können Sie sagen:

function neighboring(a, b) {
  return linkedByIndex[a.index + "," + b.index];
}

Auf diese Weise können Sie jetzt die Knoten durchlaufen und ihre Deckkraft korrekt aktualisieren:

node.style("opacity", function(o) {
  return neighboring(d, o) ? 1 : opacity;
});

(Möglicherweise möchten Sie auch den Mouseover-Link selbst als Sonderfall festlegen, indem Sie entweder einen Self-Link für jeden Knoten in festlegen linkedByIndexoder ddirekt bei der Berechnung des Stils testen oder einen! Wichtigen CSS- :hoverStil verwenden.)

Das Letzte, was ich in Ihrem Code ändern würde, ist die Verwendung von Füll- und Strichopazität anstelle von Deckkraft, da diese eine viel bessere Leistung bieten.

mbostock
quelle
1
Das funktioniert super @mbostock, vielen Dank: D Ich habe die jsfiddle mit Ihrer Lösung aktualisiert .
Christopher Manning
Mike, diese Lösung war einfach wunderschön. Ich sag bloß'.
Vivek
Mike, gibt es eine Möglichkeit, vom Datenobjekt zum DOM-Element zu gelangen? Ich habe eine Methode geschrieben, um alle benachbarten Datenknoten zu einem 'mouseovered'-Knoten zu bringen. Wie kann ich CSS / Style / Class / ... für die DOM-Elemente ändern, die zu diesen Datenknoten gehören?
Fgysin wieder Monica
1
@fgysin Hier sind einige mögliche Methoden: stackoverflow.com/questions/11206015/…
mbostock
7
Wie kdazzle oben sagte, versucht diese Geige, json-Daten dynamisch zu laden, und beide Daten sind nicht mehr vorhanden und auch jsfiddle möchte keine X-Domäne laden. Hier ist eine aktualisierte Geige, die die Daten einfügt
Tristan Reid