Подтвердить что ты не робот

Применить несколько событий наведения к соседним (связанным) узлам

У меня есть сетевая диаграмма (график с силовым движением), диаграмма рассеяния и таблица, которые все взаимосвязаны (см. jsFiddle). У меня есть межсоединения, работающие так, как я хочу их для событий mouseover. Я хотел бы изменить свой код, чтобы при наведении мыши на node на сетевой диаграмме выделен не только выделенный moused-over node (и его соединения в диаграмме рассеяния и таблице), но и его ближайшие соседние узлы (а также их соединения в диаграмме рассеяния и таблице).

Я просмотрел информацию в Выделите выделенную node, ее ссылки и ее дочерние элементы в диаграмме, направленной на D3, для получения справки. Где-то по пути (не совсем точно, где) я нашел пример функции, которая помогает определить связанные узлы, isConnected().

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

Я хотел бы включить эту функцию в мои события mouseover, возможно, с помощью оператора if(), чтобы я мог выполнять все "выделение", которое я хочу. Но я новичок в D3 и js и не знаю, как настроить его.

Ниже приведен фрагмент кода (из jsFiddle), который я хотел бы изменить. Я был бы признателен за любые предложения или указатели на другие примеры.

var node = svg.selectAll(".node")
    .data(graph.nodes)
    .enter().append("g")
    .attr("class", function(d) { return "node " + d.name + " " + d.location; })
    .call(force.drag)
    .on("mouseover", function(d) { 
        // I would like to insert an if statement to do all of these things to the connected nodes
        // if(isConnected(d, o)) {
            d3.select(this).select("circle").style("stroke-width", 6); 
            d3.select(this).select("circle").style("stroke", "orange"); 
            d3.select(this).select("text").style("font", "20px sans-serif");
            d3.selectAll("rect." + d.location).style("stroke-width", 6);
            d3.selectAll("rect." + d.location).style("stroke", "orange");
            d3.selectAll("text." + d.location).style("font", "20px sans-serif");
            d3.selectAll("tr." + d.name).style("background-color", "orange");
            //}
        })
    .on("mouseout",  function(d) { 
        // if(isConnected(d, o)) {
            d3.select(this).select("circle").style("stroke-width", 1.5); 
            d3.select(this).select("circle").style("stroke", "gray"); 
            d3.select(this).select("text").style("font", "12px sans-serif");
            d3.selectAll("rect." + d.location).style("stroke-width", 1.5);
            d3.selectAll("rect." + d.location).style("stroke", "gray");
            d3.selectAll("text." + d.location).style("font", "12px sans-serif");
            d3.selectAll("tr." + d.name).style("background-color", "white");
            //}
        });
4b9b3361

Ответ 1

В другом сценарии я бы поместил свои визуальные объекты в структуру данных графа и переместил их, чтобы эффективно обновлять соответствующие элементы. Но это d3, но мы сделаем то же самое, но вместо создаваемой нами структуры данных графа будем использовать выбор d3 (что может быть похоже на графики, но для этого они будут намного больше похожи на массивы). Алгоритмически этот подход не будет столь же эффективным, но наши графики малы.

Таким образом, работая назад, мне нужен выбор, который включает только выбранный node соседний узлы. Я сделаю это, выбрав все круги, а затем используя метод фильтра выбора d3, чтобы уменьшить это только к тем кругам, которые являются соседями.

Конечно, мне нужен список соседей, но несколько хороших методов массива js делают короткую работу. Окончательный соответствующий код (в наведении мыши) не так уж и длинный, но я добавил кучу комментариев:

// Figure out the neighboring node id with brute strength because the graph is small
var nodeNeighbors = graph.links.filter(function(link) {
    // Filter the list of links to only those links that have our target 
    // node as a source or target
    return link.source.index === d.index || link.target.index === d.index;})
.map(function(link) {
    // Map the list of links to a simple array of the neighboring indices - this is
    // technically not required but makes the code below simpler because we can use         
    // indexOf instead of iterating and searching ourselves.
    return link.source.index === d.index ? link.target.index : link.source.index; });

// Reset all circles - we will do this in mouseout also
svg.selectAll('circle').style('stroke', 'gray');

// now we select the neighboring circles and apply whatever style we want. 
// Note that we could also filter a selection of links in this way if we want to 
// Highlight those as well
svg.selectAll('circle').filter(function(node) {
    // I filter the selection of all circles to only those that hold a node with an
    // index in my listg of neighbors
    return nodeNeighbors.indexOf(node.index) > -1;
})
.style('stroke', 'orange');

Вы также можете попробовать fiddle

Я думаю, что важная концепция d3, которая здесь имеет значение, заключается в том, что когда вы связываете данные с элементом (обычно с использованием методов data() или datum() при выборе), тогда эти данные привязываются к этому элементу, и любые будущие выборы всегда будут использовать его.

Чтобы связать другие аспекты, вы можете потянуть эти атрибуты аналогичным образом и связать их через d3. Например, для прямоугольников местоположения, которые вы могли бы добавить для мыши:

var nodeLocations = graph.links.filter(function(link) {
        return link.source.index === d.index || link.target.index === d.index;})
    .map(function(link) {
        return link.source.index === d.index ? link.target.location : link.source.location; });

d3.selectAll("rect").filter(function(node) { return nodeLocations.indexOf(node.location) > -1; }) .style("stroke", "cyan");

Ответ 2

Эта вещь, которую я построил, делает это с помощью функции Ego Network:

https://gist.github.com/emeeks/4588962

Добавьте к вашим узлам .on( "mouseover", findEgo), и следующее должно работать, если у вас есть какой-то идентификатор uid, который вы могли бы сгенерировать, когда вы загружаете узлы, если вам не удобно, Это немного избыточно, поскольку оно позволяет сети эго n-степени и создает сводную таблицу для других функций сетевого анализа, но базовая функциональность даст вам то, что вы хотите, и вы или другие пользователи могли бы найти этот аспект полезным:

 function findEgo(d) {  
  var computedEgoArray = findEgoNetwork(d.id, 1, false,"individual");
  d3.selectAll("circle.node").style("fill", function(p) {return p.id == d.id ? "purple" : computedEgoArray.indexOf(p.id) > -1 ? "blue" : "pink"})
 }

 function findEgoNetwork(searchNode, egoNetworkDegree, isDirected, searchType) {
  var egoNetwork = {};
  for (x in nodes) {
  if (nodes[x].id == searchNode || searchType == "aggregate") {
   egoNetwork[nodes[x].id] = [nodes[x].id];
   var z = 0;
   while (z < egoNetworkDegree) {
    var thisEgoRing = egoNetwork[nodes[x].id].slice(0);
    for (y in links) {
     if (thisEgoRing.indexOf(links[y].source.id) > -1 && thisEgoRing.indexOf(links[y].target.id) == -1) {
     egoNetwork[nodes[x].id].push(links[y].target.id)
     }
    else if (isDirected == false && thisEgoRing.indexOf(links[y].source.id) == -1 && thisEgoRing.indexOf(links[y].target.id) > -1) {
    egoNetwork[nodes[x].id].push(links[y].source.id)
    }
 }
 z++;
 }
 }
 }
 if (searchType == "aggregate") {
 //if it checking the entire network, pass back the entire object of arrays
 return egoNetwork;
 }
 else {
 //Otherwise only give back the array that corresponds with the search node
 return egoNetwork[searchNode];
 }
 }