:
当前位置:   article > 正文

d3 - 建立力引导图将知识图谱可视化 (一)_d3知识图谱

d3知识图谱

本文要实现的功能与这个网页比较类似: KGBuilder知识图谱可视化

使用到的插件为: d3
没有采用echarts等实现的原因是: echarts比较死板, 有些需求不能实现, 而d3可以灵活的制作出想要的图表.

d3的引用/安装

  1. 在线方式:
    可以在 <head></head>中插入 <script src="https://d3js.org/d3.v5.min.js"></script>:
<head>
	<script src="https://d3js.org/d3.v5.min.js"></script>
</head>
  • 1
  • 2
  • 3
  1. 下载安装
npm install d3 --save-dev
  • 1

或者

cnpm install d3 --save-dev
  • 1

组件中引入:

import * as d3 from 'd3';
  • 1

vue中直接使用下面的可能会遇到一些问题, 可以参考: d3 -力引导图(四) vue项目中的使用及可能遇到的问题

基础知识

在绘制d3图形之前, 有一些基础知识需要掌握, 后续绘制图表的时候才不会太吃力.
主要是 SVG图形d3的一些基础知识.

带 * 号的是推荐必看的, 对d3的基础语法讲解的比较细致清晰, 可以帮助很快上手.

实例

有了一些基础后, 我们来看一个实例, 本实例的来源于: D3.js的v5版本入门教程(第六章)——做一个简单的图表, 如果能理解下面的代码在做什么, 就已经掌握了d3基础的语法.
在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://d3js.org/d3.v5.min.js"></script>
</head>
<body>
    <svg></svg>
    <script>
        var marge = {top:60,bottom:60,left:0,right:60}//设置边距
        var dataset = [ 250 , 210 , 170 , 130 , 90 ];  //数据(表示矩形的宽度)

        var svg = d3.select("svg");//得到svg画布
    	var g = svg.append("g")//定义一个用来装整个图表的一个分组,并设置他的位置
    		.attr("transform","translate("+marge.top+","+marge.left+")");

        var rectHeight = 30;//设置每一个矩形的高度
    	
    	g.selectAll("rect")
    		.data(dataset)
    		.enter()
    		.append("rect") // 添加足够的条形表
    		.attr("x",20)//设置左上点的x
    		.attr("y",function(d,i){//设置左上点的y, i表示的是索引号
    			return i*rectHeight;
    		})
    		.attr("width",function(d){//设置宽
    			return d;
    		})
    		.attr("height",rectHeight-5)//设置长
    		.attr("fill","blue");//颜色填充
    </script>
</body>
</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

d3绘制力引导图

为了绘制力引导图, 我们还需要新学习几个知识点:

  1. 如何新建一个力导向图: d3.forceSimulation()

  2. 如何添加或者移除一个力: d3.forceSimulation().force()

  3. 如何绘制节点
    绘制节点采用的是SVG图中的 circle 标签, 阮一峰SVG教程中给出的介绍如下:
    在这里插入图片描述

  4. 如何绘制连线
    绘制连线采用的SVG图中的 line 标签, 阮一峰SVG教程中给出的介绍如下:
    阮一峰SVG教程

  5. 如何绑定数据: d3绑定数据

  6. 如何绘制两个节点之间的连线, 将连线与节点联系起来:

举个例子, 有下面的数据:

当我们绘制节点后, nodes中会新增一些数据:
在这里插入图片描述
在这里插入图片描述

那么, 我们在绘制连线时, 就可以将连线的起始位置分别设置为两个节点的位置, 来实现在两个节点之间连线的目的.

比如有下面的数据:
在这里插入图片描述
我们在绘制连线时, 将 line 标签的 x1,y1,x2,y2 分别设置为source和target节点的x和y, 就可以实现节点间的连线了 (注: 绿色方框这里的n是指的每条连线.)
在这里插入图片描述
(其他参考例子: 基础知识二:网页端利用d3.js将json数据进行可视化展示 )

注意: 力引导图要想实现节点之间的连线, 在连线的对象中必须有source和target (分别指向起始节点), 不然会报错; 默认是索引.

下面是两个例子:
(1) 默认是索引
(源代码来自: CSDN)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://d3js.org/d3.v5.min.js"></script>
</head>
<body>
    <svg width="960" height="600"></svg>
    <script>
    	var marge = {top:60,bottom:60,left:60,right:60}
    	var svg = d3.select("svg")
    	var width = svg.attr("width")
    	var height = svg.attr("height")
    	var g = svg.append("g")
    		.attr("transform","translate("+marge.top+","+marge.left+")");
    		
    	//准备数据
    	var nodes = [
    		{name:"湖南邵阳",id: 001},
    		{name:"山东莱州",id: 002},
    		{name:"广东阳江",id: 003},
    		{name:"山东枣庄",id: 004},
    		{name:"泽",id: 005},
    		{name:"恒",id: 006},
    		{name:"鑫",id: 007},
    		{name:"明山",id: 011},
    		{name:"班长",id: 012}
    	];
    	
    	var edges = [
    		{source:1,target:4,relation:"籍贯",value:1.3},
    		{source:4,target:5,relation:"舍友",value:1},
    		{source:4,target:6,relation:"舍友",value:1},
    		{source:4,target:7,relation:"舍友",value:1},
    		{source:1,target:6,relation:"籍贯",value:2},
    		{source:2,target:5,relation:"籍贯",value:0.9},
    		{source:3,target:7,relation:"籍贯",value:1},
    		{source:5,target:6,relation:"同学",value:1.6},
    		{source:6,target:7,relation:"朋友",value:0.7},
    		{source:6,target:8,relation:"职责",value:2}
    	];
    	//设置一个color的颜色比例尺,为了让不同的扇形呈现不同的颜色
    	var colorScale = d3.scaleOrdinal()
    		.domain(d3.range(nodes.length))
    		.range(d3.schemeCategory10);
    	
    	//新建一个力导向图
    	var forceSimulation = d3.forceSimulation()
    		.force("link",d3.forceLink())
    		.force("charge",d3.forceManyBody())
            // .force('collide', d3.forceCollide().strength(-30))
    		.force("center",d3.forceCenter());;
    		
    	//初始化力导向图,也就是传入数据
    	//生成节点数据
    	forceSimulation.nodes(nodes)
    		.on("tick",ticked);//这个函数很重要,后面给出具体实现和说明
    	//生成边数据
    	forceSimulation.force("link")
    		.links(edges)
    		.distance(function(d){//每一边的长度
    			return d.value*100;
    		})    	
    	//设置图形的中心位置	
    	forceSimulation.force("center")
    		.x(width/2)
    		.y(height/2);
    	//在浏览器的控制台输出
    	console.log(nodes);
    	console.log(edges);
    	
    	//有了节点和边的数据后,我们开始绘制
    	//绘制边
    	var links = g.append("g")
    		.selectAll("line")
    		.data(edges)
    		.enter()
    		.append("line")
    		.attr("stroke",function(d,i){
    			return colorScale(i);
    		})
    		.attr("stroke-width",1);
    	var linksText = g.append("g")
    		.selectAll("text")
    		.data(edges)
    		.enter()
    		.append("text")
    		.text(function(d){
    			return d.relation;
    		})
    	
    	//绘制节点
    	//老规矩,先为节点和节点上的文字分组
    	var gs = g.selectAll(".circleText")
    		.data(nodes)
    		.enter()
    		.append("g")
    		.attr("transform",function(d,i){
    			var cirX = d.x;
    			var cirY = d.y;
    			return "translate("+cirX+","+cirY+")";
    		})
    		.call(d3.drag()
    			.on("start",started)
    			.on("drag",dragged)
    			.on("end",ended)
    		);
    		
    	//绘制节点
    	gs.append("circle")
    		.attr("r",10)
    		.attr("fill",function(d,i){
    			return colorScale(i);
    		})
    	//文字
    	gs.append("text")
    		.attr("x",-10)
    		.attr("y",-20)
    		.attr("dy",10)
    		.text(function(d){
    			return d.name;
    		})
    	
    	function ticked(){
    		links
    			.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;});
    			
    		linksText
    			.attr("x",function(d){
    			return (d.source.x+d.target.x)/2;
    		})
    		.attr("y",function(d){
    			return (d.source.y+d.target.y)/2;
    		});
    			
    		gs
    			.attr("transform",function(d) { return "translate(" + d.x + "," + d.y + ")"; });
    	}
    	function started(d){
    		if(!d3.event.active){
    			forceSimulation.alphaTarget(0.8).restart();
    		}
    		d.fx = d.x;
    		d.fy = d.y;
    	}
    	function dragged(d){
    		d.fx = d3.event.x;
    		d.fy = d3.event.y;
    	}
    	function ended(d){
    		if(!d3.event.active){
    			forceSimulation.alphaTarget(0);
    		}
    		d.fx = null;
    		d.fy = null;
    	}
    </script>
</body>
</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165

在这里插入图片描述

(2) 设置source和target
(源代码来自: GitHub)

<!DOCTYPE html>
<!--draw the graph completely-->
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <!--import d3 version 5-->
    <script type="text/javascript" src="https://d3js.org/d3.v5.min.js"></script>
    <!--import jquery3.3.1-->
    <!-- <script type="text/javascript" src="js/jquery-3.3.1.min.js"></script> -->
    <script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
</head>

<body>
    <svg></svg>
    <script>
        //get json file

        const data = {
            "nodes": [{
                "entity_name": "林黛玉", 
                "entity_id": "entity/2870013", 
                "ontargetlogy_name": "人员",
                "relation_num": 4
            }, {
                "entity_name": "贾宝玉", 
                "entity_id": "entity/2870127", 
                "ontargetlogy_name": "人员",
                "relation_num": 2
            }],
            "links": [{
                "from": "entity/2870013",
                "to": "entity/2870127", 
                "relation_id": "relation/815478", 
                "relation_name": "表哥"
            }]
        }
        //GroupExplorer constructing function
        //this is one way to create a javascript object
        function GroupExplorer(data) {
            console.log(data)
            //create an object-include some data
            //this is an another way to create a javascript object
            var defaultConfig = {
                windowWidth: window.innerWidth,
                windowHeight: window.innerHeight,
                defaultLinkDistance: 150,
                data: data
            }

            console.log(defaultConfig)

            var svg = d3.select("svg");
            svg.attr("width", defaultConfig.windowWidth);
            svg.attr("height", defaultConfig.windowHeight);

            defaultConfig.data.links.forEach(function (e) {
                var sourceNode = defaultConfig.data.nodes.filter(function (n) {
                    return n.entity_id === e.from;
                })[0];
                var targetNode = defaultConfig.data.nodes.filter(function (n) {
                    return n.entity_id === e.to;
                })[0];
                e.source = sourceNode;
                e.target = targetNode;

                console.log(e)
            });

            //create a force graph
            var forceSimulation = d3.forceSimulation()
                .force("link", d3.forceLink())
                .force("charge", d3.forceManyBody())
                .force("center", d3.forceCenter(defaultConfig.windowWidth / 2, defaultConfig.windowHeight / 2));

            //transform nodes data
            forceSimulation.nodes(defaultConfig.data.nodes)
                .on("tick", ticked);
            //tranform links data
            forceSimulation.force("link")
                .links(defaultConfig.data.links)
                .distance(defaultConfig.defaultLinkDistance);

            console.log(defaultConfig.data.nodes);
            console.log(defaultConfig.data.links);

            //define arrow
            svg.append("svg:defs")
                .append("svg:marker")
                .attr("id", "marker")
                .attr('viewBox', '0 -5 10 10')
                .attr("refX", 20)
                .attr("refY", 0)
                .attr('markerWidth', 10)
                .attr('markerHeight', 10)
                .attr('orient', 'auto')
                .append('svg:path')
                .attr('d', 'M0,-5L10,0L0,5')
                .attr("fill", "grey");
            //draw links
            var links = svg.append("g")
                .selectAll("line")
                .data(defaultConfig.data.links)
                .enter()
                .append("line")
                .attr("x1", function (n) { return n.source.x })
                .attr("y1", function (n) { return n.source.y })
                .attr("x2", function (n) { return n.target.x })
                .attr("y2", function (n) { return n.target.y })
                .attr("stroke", "grey")
                .attr("stroke-width", 1)
                .attr("marker-end", "url(#marker)");
            //draw links-text
            var links_text = svg.append("g")
                .selectAll("text")
                .data(defaultConfig.data.links)
                .enter()
                .append("text")
                .attr("x", function (e) {
                    return (e.source.x + e.target.x) / 2;
                })
                .attr("y", function (e) {
                    console.log(e.source.y + "+" + e.target.y)
                    return (e.source.y + e.target.y) / 2;
                })
                .attr("font-size", 10)
                .text(function (e) { return e.relation_name });
            //draw nodes group = node+node-text
            var nodes_g = svg.append("g")
                .selectAll("g")
                .data(defaultConfig.data.nodes)
                .enter()
                .append("g")
                .attr("transform", function (e) {
                    return "translate(" + e.x + "," + e.y + ")";
                })
                .call(d3.drag()
                    .on("start", started)
                    .on("drag", dragged)
                    .on("end", ended));
            //draw nodes
            nodes_g.append("circle")
                .attr("r", function (e) {
                    return e.relation_num * 5
                })
                .attr("fill", "yellow");
            //draw node-text
            nodes_g.append("text")
                .attr("x", -15)
                .attr("y", 20)
                .attr("font-size", 10)
                .text(function (e) { return e.entity_name });

            function started(d) {
                if (!d3.event.active) {
                    forceSimulation.alphaTarget(0.8).restart();
                }
                d.fx = d.x;
                d.fy = d.y;
            }
            function dragged(d) {
                d.fx = d3.event.x;
                d.fy = d3.event.y;
            }
            function ended(d) {
                if (!d3.event.active) {
                    forceSimulation.alphaTarget(0);
                }
                d.fx = null;
                d.fy = null;
            }

            function ticked() {
                links
                    .attr("x1", function (n) { return n.source.x })
                    .attr("y1", function (n) { return n.source.y })
                    .attr("x2", function (n) { return n.target.x })
                    .attr("y2", function (n) { return n.target.y })
                links_text
                    .attr("x", function (e) {
                        return (e.source.x + e.target.x) / 2;
                    })
                    .attr("y", function (e) {
                        return (e.source.y + e.target.y) / 2;
                    })
                nodes_g
                    .attr("transform", function (e) {
                        return "translate(" + e.x + "," + e.y + ")";
                    })
            }

        }
        //because in the way of creating a javascript object,
        //you need to use "new" to use it
        new GroupExplorer(data);

    </script>
</body>

</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201

在这里插入图片描述

通过上面两段代码 (特别是林黛玉和贾宝玉的例子), 引用接口数据已经可以简单的绘制一个力引导图了.

更多内容可以查看我的d3专栏: d3绘制力引导图
下一篇: d3 - 力引导图(二) 节点多种颜色方案

参考源码:
KGBuilder GitHub源码

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/489483
推荐阅读
相关标签
  

闽ICP备14008679号