如何在d3js中链接多个图形网络,以便其中一个事件调用其他网络中的同一事件

时间:2018-10-17 19:06:57

标签: d3.js

我正在绘制四个具有相同数据的网络图,但是使用不同的属性为节点着色。当前,我能够生成所有4个网络,并且对于每个网络,我都可以双击一个节点以查看该网络的连接节点。我的问题是如何扩展此功能,以便在单击任何图形网络上的节点时,其他3个节点也会向我显示我单击的节点以及其中的连接节点。

之前Mouseover event on two charts at the same time d3.js提出的这个问题对于饼图也做了类似的事情,但是我不确定如何使它适应我的代码。

var drawNetworkFunction = function(descriptor, chartId) {
var width = 470;
var height = 310;
var toggle = 0;
var activenode;

var svg = d3.select(chartId).append("svg")
    .attr("width", width)
    .attr("height", height);

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

if (descriptor == "id" || descriptor == "cluster") {
    var color = d3.scaleOrdinal(d3.schemeCategory10);
}

var simulation = d3.forceSimulation()
    .force("link", d3.forceLink().id(function(d) {
        return d.id;
    }))
    .force("charge", d3.forceManyBody().strength(-7))
    .force("center", d3.forceCenter(width / 2, height / 2));

d3.json("Perovskite_info.json", function(graph) {

    if (descriptor == "cohesive") {
        var cscale = d3.scaleLinear()
            .domain([1.6, 7.2])
            .range([0, 1]);
    } else if (descriptor == "similarity") {
        var cscale = d3.scaleLinear()
            .domain([0, 1])
            .range([0, 1]);
    }

    var link = svg.append("g")
        .attr("class", "links")
        .selectAll("line")
        .data(graph.links)
        .enter().append("line")
        .attr("stroke", "#000")
        .attr("stroke-opacity", "0.1");


    var node = svg.append("g")
        .attr("class", "nodes")
        .selectAll("circle")
        .data(graph.nodes)
        .attr("pvs_id", function(d) { return d.id; })
        .enter().append("circle")
        .attr("r", 5)
        .attr("fill", function(d) {
            var col = '';
            if (descriptor == "id") {
                col = color(d.group);
            } else if (descriptor == "cluster") {
                col = color(d.cluster);
            } else if (descriptor == "cohesive") {
                col = d3.interpolatePurples(cscale(d.cohesive));
            } else if (descriptor == "similarity") {
                col = d3.interpolateReds(cscale(d.similarity));
            }
            return col;
        })
        .call(d3.drag()
            .on("start", dragstarted)
            .on("drag", dragged)
            .on("end", dragended))
        .on("dblclick", connectedNodes);



    node.append("title")
        .text(function(d) {
            return ["Perovskite :" + d.id,
                "Cluster :" + d.cluster,
                "Cohesive Energy :" + d.cohesive,
                "Similarity to CaTiO3 :" + d.similarity
            ];
        });


    simulation
        .nodes(graph.nodes)
        .on("tick", ticked);

    simulation.force("link")
        .links(graph.links);

    function ticked() {
        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;
            });
    }

    var linkedByIndex = {};
    for (i = 0; i < graph.nodes.length; i++) {
        linkedByIndex[i + "," + i] = 1;
    };

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

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

    function HS_f(activenode) {

        if (activenode != null) {

            var fing_plot = d3.select("#HSA_fingerprint")
                .append("svg")
                .attr("id", "fin_pl")
                .attr("width", 300)
                .attr("height", 300);

            fing_plot.append('svg:image')
                .attr("xlink:href", function() { return "./finger_prints_lo/" + activenode + "_lo.png" })
                .attr("width", 250)
                .attr("height", 250);

            return fing_plot;

        }
    }

    function connectedNodes() {

        if (toggle == 0) {

            d = d3.select(this).node().__data__;

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

            toggle = 1;
            activenode = d.id;
            window.activenode = activenode;
            console.log(activenode);
            HS_f(activenode);
        } else {
            node.style("opacity", 1);;
            toggle = 0;
            d3.select("#fin_pl").remove();
        }

    }
});

function dragstarted(d) {
    if (!d3.event.active) simulation.alphaTarget(0.3).restart();
    d.fx = d.x;
    d.fy = d.y;
}

function dragged(d) {
    d.fx = d3.event.x;
    d.fy = d3.event.y;
}

function dragended(d) {
    if (!d3.event.active) simulation.alphaTarget(0);
    d.fx = null;
    d.fy = null;
}

return svg;

}

有关json文件,请参见链接:https://api.myjson.com/bins/m3gd8

我的html文件也在下面:

<!DOCTYPE html>
<html>

<head>
<meta charset="utf-8">
<script type=" text/javascript " src="http://code.jquery.com/jquery-latest.js "></script>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-color.v1.min.js"></script>
<script src="https://d3js.org/d3-interpolate.v1.min.js"></script>
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
<script src="https://d3js.org/d3-dispatch.v1.min.js"></script>
<script src="draw_net.js"></script>


<title>Library of Perovskite structures from Materials Project Database</title>
<div id="title_main">
    <h2>Graph Network of Perovskite structures from Materials Project Database</h2>
</div>
</head>

<body>
<link rel="stylesheet" href="pvs-net.css" />


<div style="width: 80%; float:left; overflow: auto;">
    <div id="Perovskite_Network">
        <div style="width: 50%; float:left;">
            <div id="Network1">
                <div id="title_1">
                    <h3>Isomap Graph Network</h3>
                    <script>
                        var Net1 = drawNetworkFunction('id', '#Network1');
                    </script>
                </div>
            </div>

            <div id="Network2">
                <div style="width: 50%; float:left; overflow: auto;"></div>
                <div id="title_2">
                    <h3>Spectral Clustering on Isomap Graph Network</h3>
                    <script>
                        var Net2 = drawNetworkFunction('cluster', '#Network2');
                    </script>
                </div>
            </div>
        </div>

        <div style="width: 50%; float:right; overflow: auto;">
            <div id="Network3">
                <div id="title_3">
                    <h3>Calculated Cohesive Energy</h3>
                    <script>
                        var Net3 = drawNetworkFunction('cohesive', '#Network3');
                    </script>
                </div>
            </div>
            <div id="Network4">
                <div id="title_4">
                    <h3>Similarity score against CaTiO3</h3>
                    <script>
                        var Net4 = drawNetworkFunction('similarity', '#Network4');
                    </script>
                </div>
            </div>
        </div>
    </div>
</div>
</div>

<div style="width: 20%; height: 500px; float:right;">
    <div id="HSA_fingerprint">
        <div id="title">
            <h3>Hirschfeld Surface Fingerprint Plot</h3>
        </div>

    </div>
</div>

</body>

</html>

1 个答案:

答案 0 :(得分:0)

很高兴您收到了数据和HTML。它使调试和提出解决方案变得更加容易。

我刚刚在connectedNodes函数中更改了几行代码:

我将其替换为node.style("opacity", function(o) {...})

d3.select('#Perovskite_Network').selectAll('.nodes').selectAll('circle').style("opacity", function(o) {
    return neighboring(d, o) | neighboring(o, d) ? 1 : 0.15;
});

这将从所有SVG中选择所有circle。让我知道这是否无济于事,或者您是否需要任何改进。

代码段:

var drawNetworkFunction = function(descriptor, chartId) {
var width = 470;
var height = 310;
var toggle = 0;
var activenode;

var svg = d3.select(chartId).append("svg")
    .attr("width", width)
    .attr("height", height);

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

if (descriptor == "id" || descriptor == "cluster") {
    var color = d3.scaleOrdinal(d3.schemeCategory10);
}

var simulation = d3.forceSimulation()
    .force("link", d3.forceLink().id(function(d) {
        return d.id;
    }))
    .force("charge", d3.forceManyBody().strength(-7))
    .force("center", d3.forceCenter(width / 2, height / 2));

d3.json("https://api.myjson.com/bins/m3gd8", function(graph) {

    if (descriptor == "cohesive") {
        var cscale = d3.scaleLinear()
            .domain([1.6, 7.2])
            .range([0, 1]);
    } else if (descriptor == "similarity") {
        var cscale = d3.scaleLinear()
            .domain([0, 1])
            .range([0, 1]);
    }

    var link = svg.append("g")
        .attr("class", "links")
        .selectAll("line")
        .data(graph.links)
        .enter().append("line")
        .attr("stroke", "#000")
        .attr("stroke-opacity", "0.1");


    var node = svg.append("g")
        .attr("class", "nodes")
        .selectAll("circle")
        .data(graph.nodes)
        .attr("pvs_id", function(d) { return d.id; })
        .enter().append("circle")
        .attr("r", 5)
        .attr("fill", function(d) {
            var col = '';
            if (descriptor == "id") {
                col = color(d.group);
            } else if (descriptor == "cluster") {
                col = color(d.cluster);
            } else if (descriptor == "cohesive") {
                col = d3.interpolatePurples(cscale(d.cohesive));
            } else if (descriptor == "similarity") {
                col = d3.interpolateReds(cscale(d.similarity));
            }
            return col;
        })
        .call(d3.drag()
            .on("start", dragstarted)
            .on("drag", dragged)
            .on("end", dragended))
        .on("dblclick", connectedNodes);



    node.append("title")
        .text(function(d) {
            return ["Perovskite :" + d.id,
                "Cluster :" + d.cluster,
                "Cohesive Energy :" + d.cohesive,
                "Similarity to CaTiO3 :" + d.similarity
            ];
        });


    simulation
        .nodes(graph.nodes)
        .on("tick", ticked);

    simulation.force("link")
        .links(graph.links);

    function ticked() {
        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;
            });
    }

    var linkedByIndex = {};
    for (i = 0; i < graph.nodes.length; i++) {
        linkedByIndex[i + "," + i] = 1;
    }

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

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

    function HS_f(activenode) {

        if (activenode != null) {

            var fing_plot = d3.select("#HSA_fingerprint")
                .append("svg")
                .attr("id", "fin_pl")
                .attr("width", 300)
                .attr("height", 300);

            fing_plot.append('svg:image')
                .attr("xlink:href", function() { return "./finger_prints_lo/" + activenode + "_lo.png" })
                .attr("width", 250)
                .attr("height", 250);

            return fing_plot;

        }
    }

    function connectedNodes() {

        if (toggle == 0) {

            d = d3.select(this).node().__data__;

            d3.select('#Perovskite_Network').selectAll('.nodes').selectAll('circle').style("opacity", function(o) {
                return neighboring(d, o) | neighboring(o, d) ? 1 : 0.15;
            });

            toggle = 1;
            activenode = d.id;
            window.activenode = activenode;
            //console.log(activenode);
            HS_f(activenode);
        } else {
            d3.select('#Perovskite_Network').selectAll('.nodes').selectAll('circle').style("opacity", 1);
            toggle = 0;
            d3.select("#fin_pl").remove();
        }

    }
});

function dragstarted(d) {
    if (!d3.event.active) simulation.alphaTarget(0.3).restart();
    d.fx = d.x;
    d.fy = d.y;
}

function dragged(d) {
    d.fx = d3.event.x;
    d.fy = d3.event.y;
}

function dragended(d) {
    if (!d3.event.active) simulation.alphaTarget(0);
    d.fx = null;
    d.fy = null;
}

return svg;

}

drawNetworkFunction('id', '#Network1');
drawNetworkFunction('cluster', '#Network2');
drawNetworkFunction('cohesive', '#Network3');
drawNetworkFunction('similarity', '#Network4');
<meta charset="utf-8">
<script type=" text/javascript " src="http://code.jquery.com/jquery-latest.js "></script>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-color.v1.min.js"></script>
<script src="https://d3js.org/d3-interpolate.v1.min.js"></script>
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
<script src="https://d3js.org/d3-dispatch.v1.min.js"></script>
<script src="draw_net.js"></script>


<title>Library of Perovskite structures from Materials Project Database</title>
<div id="title_main">
    <h2>Graph Network of Perovskite structures from Materials Project Database</h2>
</div>

<link rel="stylesheet" href="pvs-net.css" />


<div style="width: 80%; float:left; overflow: auto;">
    <div id="Perovskite_Network">
        <div style="width: 50%; float:left;">
            <div id="Network1">
                <div id="title_1">
                    <h3>Isomap Graph Network</h3>
                </div>
            </div>

            <div id="Network2">
                <div style="width: 50%; float:left; overflow: auto;"></div>
                <div id="title_2">
                    <h3>Spectral Clustering on Isomap Graph Network</h3>
                </div>
            </div>
        </div>

        <div style="width: 50%; float:right; overflow: auto;">
            <div id="Network3">
                <div id="title_3">
                    <h3>Calculated Cohesive Energy</h3>
                </div>
            </div>
            <div id="Network4">
                <div id="title_4">
                    <h3>Similarity score against CaTiO3</h3>
                </div>
            </div>
        </div>
    </div>
</div>

<div style="width: 20%; height: 500px; float:right;">
    <div id="HSA_fingerprint">
        <div id="title">
            <h3>Hirschfeld Surface Fingerprint Plot</h3>
        </div>

    </div>
</div>

还有一个小提琴链接:https://jsfiddle.net/shashank2104/bj76ckh4/4/

不过我有一些建议:

  1. 每个drawNetworkFunction调用都会提取文件,这会减慢页面的呈现速度。
  2. 在提取文件的同时,在每次调用中都会创建对象linkedByIndex,该对象独立于描述符或chartId

建议:在单个函数中移动上述步骤,并将linkedByIndexgraph(数据)传递到drawNetworkFunction,然后传递其余的。希望这会有所帮助。