规范网站建设,平面设计服务方案,app设计案例,网站建设sz886## 人物关系知识图谱#### 一、背景将结构化数据通过关系预处理程序处理为图数据库可以查询的数据#xff0c;示例是将其中一部分(人物关系数据)可视化表示。#### 二、用到的技术技术点#xff1a;图数据库Neo4j#xff0c;d3.js#xff0c;java#xff0c;css#xff0c;…## 人物关系知识图谱#### 一、背景将结构化数据通过关系预处理程序处理为图数据库可以查询的数据示例是将其中一部分(人物关系数据)可视化表示。#### 二、用到的技术技术点图数据库Neo4jd3.jsjavacssspring boot开发工具IDEA专业版(可找学生账号注册免费使用一年社区版不支持WEB开发)#### 三、项目结构以及代码实现过程实现思路这样1先定义基础的展示页面index.html2、完成画图js(graph.js)3提供一个基础的拿数据接口加载测试绘图数据和绘图需要的数据(例如节点选中之后的小图标加载)4、页面从数据接口请求数据之后调用绘图JS在页面完成画图操作(请求数据的接口可以很方便的改为从图数据库拿取数据进行展示)主要文件目录说明1、data目录bg.jpg可视化背景图片数据CircularPartition.json节点圆形分区图工具栏需要加载的数据test.json可视化需要展示的数据格式2、images此目录存储节点属性图片数据3、jsd3.js version-3.2.84、srcJS以及其它HTML等源码5、index.html知识图谱可视化入口文件6、拿数据接口通过数据Type id加载圆形分区图数据和测试知识图谱构图数据(type等于1加载圆形分区数据type是等于2加载测试知识图谱展示数据)GET:http://localhost:7476/knowledge-graph/hello/dataSource/type/{id}做图过程(graph.js)// 定义画布 (radius是鼠标点击生成圆形分区图的半径)var width 1345, height 750, color d3.scale.category20();var svg d3.select(body).append(svg).attr(id, svgGraph).attr(width, width).attr(height, height).append(g).attr(id, svgOne).call(d3.behavior.zoom() // 自动创建事件侦听器.scaleExtent([0.1, 10]) // 缩放允许的级数.on(zoom, zoom)).on(dblclick.zoom, null); // remove双击缩放// 实时获取SVG画布坐标function printPosition() {var position d3.mouse(svg.node());return position;}// 缩放函数function zoom() {// translate变换矢量(使用二元组标识)scale当前尺度的数字svg.attr(transform, translate( d3.event.translate )scale( d3.event.scale )); // 画布缩放与移动// svg.attr(transform, scale( d3.event.scale )); // 画布缩放}// 设置连线箭头属性function setMarkers() {svg.append(g).attr(id, lineAndText).selectAll(marker).data(edges).enter().append(marker).attr(id, function (d) {return d.id;}).attr(viewBox, 0 -5 10 10) // 坐标系的区域.attr(class, arrow).attr(refX, 27) // refX,refY在viewBox内的基准点绘制时此点在直线端点上(要注意大小写).attr(refY, 0).attr(markerWidth, 10) // 标识的大小.attr(markerHeight, 18) // 标识的大小.attr(markerUnits, userSpaceOnUse) // 标识大小的基准有两个值strokeWidth(线的宽度)和userSpaceOnUse(图形最前端的大小).attr(orient, auto) // 绘制方向可设定为auto(自动确认方向)和 角度值.append(path).attr(d, M0,-5L10,0L0,5).attr(fill, #ccc);}// 添加连线function add_edges() {setMarkers(); // 设置连线箭头属性var svg_edges svg.select(#lineAndText).selectAll(line).data(edges).enter().append(line).attr(id, function (d) {return d.id;}).style(stroke, #ccc).style(stroke_width, 1).attr(marker-end, function (d) {return url(# d.id );}).attr(stroke, #999).on(mouseover, function (d) { // 鼠标选中时触发mouseSelectLine(d);addToolTip(d); //添加提示框的div}).on(mouseout, function () {d3.select(#relation).remove();d3.select(#tooltip).remove();});return svg_edges;}// 求直线与圆的交点// 函数参数说明:cx:圆X轴坐标 cy:圆y轴坐标 r:圆半径 stx:起点直线的X轴坐标 sty:起点直线的轴坐标 edx:终点直线的X轴坐标 edy:终点直线的Y轴坐标// 返回值:交点坐标(x,y)function getPoint(cx, cy, r, stx, sty, edx, edy) {// 求直线var k (edy - sty) / (edx - stx);var b edy - k * edx;//列方程var x1, y1, x2, y2;var c cx * cx (b - cy) * (b - cy) - r * r;var a (1 k * k);var b1 (2 * cx - 2 * k * (b - cy));var tmp Math.sqrt(b1 * b1 - 4 * a * c);x1 (b1 tmp) / (2 * a);y1 k * x1 b;x2 (b1 - tmp) / (2 * a);y2 k * x2 b;// 过滤距离最近的坐标var p {};function lineIf(lx, ly, lxx, lyy) {var d Math.sqrt((lx - lxx) * (lx - lxx) (ly - lyy) * (ly - lyy));return d;}if (cx ! stx) { // stx, styvar d1 lineIf(x1, y1, stx, sty);var d2 lineIf(x2, y2, stx, sty);if (d1 d2) {p.x x1;p.y y1;} else {p.x x2;p.y y2;}} else { // edx, edyvar d1 lineIf(x1, y1, edx, edy);var d2 lineIf(x2, y2, edx, edy);if (d1 d2) {p.x x1;p.y y1;} else {p.x x2;p.y y2;}}return p;}// 鼠标选中关系添加显示效果function mouseSelectLine(d) {var p1 getPoint(d.source.x, d.source.y, 20, d.source.x, d.source.y, d.target.x, d.target.y);var p2 getPoint(d.target.x, d.target.y, 20, d.source.x, d.source.y, d.target.x, d.target.y);var json [p1, p2];//构造默认线性生成器var line d3.svg.line().x(function (d) { //指定x存取器为取每个数据元素的x属性的值return d.x;}).y(function (d) { //指定y存取器为取每个数据元素的y属性的值return d.y;});svg.append(path).attr({d: function () { //生成路径数据return line(json);},id: relation}).style({stroke: #87CEFA, //path颜色stroke-width: 6 //path粗细});}// 添加节点function add_nodes() {var svg_nodes svg.append(g).attr(id, circleAndText).selectAll(circle).data(nodes).enter().append(g).call(force.drag().on(dragstart, function (d) {d3.select(#eee).remove(); // 删除节点扇形d3.select(#sel).remove(); // 删除节点选中d3.event.sourceEvent.stopPropagation(); // 画布拖动与节点拖动分离d3.select(this).attr(r, 20 * 2);}).on(dragend, function (d) {d3.select(#eee).remove(); // 删除节点扇形d3.select(#sel).remove(); // 删除节点选中d.fixed true; // 拖动结束后节点固定d3.select(this).attr(r, 20);})).on(click, function (d) { // 鼠标点击时触发// 在当前节点处画三页扇形d3.select(#eee).remove();drawCirclePartition(d);}).on(mouseover, function (d) { // 光标放在某元素上smouseSelect(d); // 鼠标选中效果addToolTip(d); //添加提示框的div}).on(mouseout, function (d) {d3.select(#sel).remove(); // 删除节点选中d3.select(#tooltip).remove();d3.select(#tooltipCir).remove();});svg_nodes.append(circle).attr(id, function (d) {return d.index;}).attr(r, 20).attr(fill, function (d, i) {return color(i);});svg_nodes.append(image).attr(class, circle).attr(xlink:href, function (d) { var img d.image; if (img ! undefined) {return http://222.216.195.154:7476/knowledge-graph/path/images/ d.image} else {return null;}}).attr(x, -20px).attr(y, -20px).attr(width, 40px).attr(height, 40px);svg_nodes.append(svg:text).style(fill, #ccc).attr(dx, 20).attr(dy, 8).text(function (d) {return d.name});return svg_nodes;}//添加提示框的divfunction addToolTip(d) {var htmlStr;if (d.source d.target d.type) {htmlStr name: d.type ;} else {htmlStr id: d.id name: d.name ;}var position printPosition(d);var tooltip d3.select(body).append(div).attr(class, tooltip) //用于css设置类样式.attr(opacity, 0.0).attr(id, tooltip);htmlStr htmlStr locx: position[0] locy: position[1] ;if (d.image ! undefined) {htmlStr htmlStr ;}tooltip.html(htmlStr).style(left, (d3.event.pageX) px).style(top, (d3.event.pageY 20) px).style(opacity, 0.75);}function addToolTipCir(d) {var htmlStr;if (d.name ☿) {htmlStr notes:解锁当前节点;}if (d.name ✂) {htmlStr notes:裁剪当前节点与关系;}if (d.name ✠) {htmlStr notes:拓展当前节点与关系;}if (d.name ◎) {htmlStr notes:释放所有锁定的节点;}if (d.name 오) {htmlStr notes:锁定所有节点;}var tooltip d3.select(body).append(div).attr(class, tooltip) //用于css设置类样式.attr(opacity, 0.0).attr(id, tooltipCir);tooltip.html(htmlStr).style(left, (d3.event.pageX) px).style(top, (d3.event.pageY 20) px).style(opacity, 0.75);}// 生成圆弧需要的角度数据var arcDataTemp [{startAngle: 0, endAngle: 2 * Math.PI}];var arc_temp d3.svg.arc().outerRadius(26).innerRadius(20);// 鼠标选中节点添加显示效果var svg_selectNode;function mouseSelect(d) {svg_selectNode svg.append(g).attr(id, sel).attr(transform, translate( d.x , d.y )).selectAll(path.arc).data(arcDataTemp).enter().append(path).attr(fill, #87CEFA).attr(d, function (d, i) {return arc_temp(d, i);});}// 全局停止力作用之间的影响function stopForce() {for (var i 0; i nodes.length; i) {var obj nodes[i];obj.fixed true;}}// 全局开始力作用之间的影响function startForce() {for (var i 0; i nodes.length; i) {var obj nodes[i];obj.fixed false;}force.resume();}var nodesMark [], edgesMark [], indexNodeMark []; // 缓存中所有已加载的数据标记// 节点添加圆形分区(添加三页扇形)function drawCirclePartition(d) {// 圆形分区布局(数据转换)var radius 40;var partition d3.layout.partition().sort(null).size([2 * Math.PI, radius * radius]) // 第一个值域时2 PI第二个值时圆半径的平方.value(function (d) {return 1;});// 绘制圆形分区图// 如果以圆形的形式来转换数据那么d.x和d.y分别代表圆弧的绕圆心// 方向的起始位置和由圆心向外的起始位置d.dx和d.dy分别代表各自的宽度var arc d3.svg.arc().startAngle(function (d) {return d.x;}).endAngle(function (d) {return d.x d.dx;}).innerRadius(function (d) {return 26;}).outerRadius(function (d) {return 80;});var circlePart partition.nodes(dataCirclePartition);// ☿ 释放固定的节点function releaseNode() {d.fixed false;// force.start(); // 开启或恢复结点间的位置影响force.resume();}// ✂ 删除当前节点以及当前节点到其它节点之间的关系function removeNode() {var newNodes [];for (var i 0; i nodes.length; i) {var obj nodes[i];if (obj.id ! d.id) {newNodes.push(obj);}}var newedges [];for (var i 0; i edges.length; i) {var obj edges[i];if ((d.index ! obj.source.index) (d.index ! obj.target.index)) {newedges.push(obj);}}nodes newNodes;edges newedges;var nIndex function (d) {return d.index;};var lIndex function (d) {return d.id;};// 通过添加g元素分组删除svg.select(#circleAndText).selectAll(circle).data(nodes, nIndex).exit().remove();svg.select(#circleAndText).selectAll(image).data(nodes, nIndex).exit().remove();svg.select(#circleAndText).selectAll(text).data(nodes, nIndex).exit().remove();svg.select(#lineAndText).selectAll(line).data(edges, lIndex).exit().remove();svg.select(#lineAndText).selectAll(text).data(edges, lIndex).exit().remove();}// 扩展当前节点距离为1// 1.从rawData(rawNodes/rawEdges)中找出当前节点需要扩展的节点与关系数据// 2.拿出需要扩展的数据到node/edges中去除已经绑定图形元素的数据// 3.将过滤出的未绑定图形元素需要扩展的数据重新调用构图方法进行构图// 添加从服务器实时加载数据的功能基本思想与1~3类似function extendNode() {var index d.index;var arrEdges [], arrIndex [], arrNodes [];for (var i 0; i rawEdges.length; i) {if ((index rawEdges[i].source.index) || (index rawEdges[i].target.index)) {arrEdges.push(rawEdges[i]);if (index ! rawEdges[i].source.index) {arrIndex.push(rawEdges[i].source.index);} else if (index ! rawEdges[i].target.index) {arrIndex.push(rawEdges[i].target.index);}}edgesMark.push(rawEdges[i].id);}for (var i 0; i rawNodes.length; i) {for (var j 0; j arrIndex.length; j) {var obj arrIndex[j];if (rawNodes[i].index obj) {arrNodes.push(rawNodes[i]);}}nodesMark.push(rawNodes[i].id);indexNodeMark.push(rawNodes[i].index);}// nodes.push(arrNodes);// edges.push(arrEdges);var nodesRemoveIndex [];for (var i 0; i arrNodes.length; i) {var obj arrNodes[i];for (var j 0; j nodes.length; j) {var obj2 nodes[j];if (obj.index obj2.index) {nodesRemoveIndex.push(i);}}}var edgesRemoveIndex [];for (var i 0; i arrEdges.length; i) {var obj arrEdges[i];for (var j 0; j edges.length; j) {var obj2 edges[j];if (obj.id obj2.id) {edgesRemoveIndex.push(obj.id);}}}var coverNodes [];for (var i 0; i arrNodes.length; i) {var obj arrNodes[i];if (!isInArray(nodesRemoveIndex, i)) {nodes.push(obj);coverNodes.push(obj);}}var coverEdges [];for (var i 0; i arrEdges.length; i) {var obj arrEdges[i];if (!isInArray(edgesRemoveIndex, obj.id)) {edges.push(obj);coverEdges.push(obj);}}// console.log(找出需要扩展的数据);// console.log(arrEdges);// console.log(arrNodes);// console.log(添加到原始需要绑定图形元素的数据集集合/与rawNodes,rawEdges服务器加载的原始数据保持区分);// console.log(nodes);// console.log(edges);// 添加从服务器请求扩展数据// var url http://222.216.195.154:7476/knowledge-graph/hello/dataSource/node/extend/ d.id ;// d3.json(url, function (error, json) { // 服务器加载知识图谱数据// if (error) {// return console.warn(error);// }// console.log(从服务器请求的扩展数据);// var serverNodes json.nodes;// var serverEdges json.links;// console.log(serverNodes);// console.log(serverEdges);// console.log(nodesMark);// console.log(edgesMark);// // 重新设置INDEX// var maxIndex Math.max.apply(null, indexNodeMark);// console.log(MAX: maxIndex);//// for (var i 0; i serverNodes.length; i) {// if (!isInArray(nodesMark, serverNodes[i].id)) {// serverNodes[i].index maxIndex 1// maxIndex maxIndex 1;// nodes.concat(serverNodes[i]);// console.log(serverNodes[i]);// }// }// for (var i 0; i serverEdges.length; i) {// if (!isInArray(edgesMark, serverEdges[i].id)) {// edges.concat(serverEdges);// console.log(serverEdges[i]);// }// }// console.log(服务器加载并且合并之后的数据);// console.log(nodes);// console.log(edges);// d3.select(#svgGraph).select(#svgOne).selectAll(*).remove(); // 清空SVG中的内容// buildGraph();// });d3.select(#svgGraph).select(#svgOne).selectAll(*).remove(); // 清空SVG中的内容buildGraph();}var arcs svg.append(g).attr(id, eee).attr(transform, translate( d.x , d.y )).selectAll(g).data(circlePart).enter().append(g).on(click, function (d) { // 圆形分区绑定Click事件if (d.name ☿) {releaseNode();}if (d.name ✂) {removeNode();}if (d.name ✠) {extendNode();}if (d.name ◎) {startForce();}if (d.name 오) {stopForce();}d3.select(#eee).remove();d3.select(#tooltipCir).remove();});arcs.append(path).attr(display, function (d) {return d.depth ? null : none; // hide inner ring}).attr(d, arc).style(stroke, #fff).style(fill, #A9A9A9).on(mouseover, function (d) {d3.select(this).style(fill, #747680);addToolTipCir(d); //添加提示框的div}).on(mouseout, function () {d3.select(#tooltipCir).remove();d3.select(this).transition().duration(200).style(fill, #ccc)var array printPosition();var distance Math.sqrt(Math.pow((d.x - array[0]), 2) Math.pow((d.y - array[1]), 2));if (distance 80) {d3.select(#eee).remove(); // 删除节点扇形}});arcs.append(text).style(font-size, 16px).style(font-family, simsun).style(fill, white).attr(text-anchor, middle).attr(transform, function (d, i) {// 平移和旋转var r 0;if ((d.x d.dx / 2) / Math.PI * 180 180) // 0-180度以内的r 180 * ((d.x d.dx / 2 - Math.PI / 2) / Math.PI);else // 180-360度r 180 * ((d.x d.dx / 2 Math.PI / 2) / Math.PI);return translate( arc.centroid(d) ) rotate( r );}).text(function (d) {return d.name;});return arcs;}// 添加描述关系文字function add_text_edges() {var svg_text_edges svg.select(#lineAndText).selectAll(line.text).data(edges).enter().append(text).attr(id, function (d) {return d.id;}).style(fill, #ccc).attr(x, function (d) {return (d.source.x d.target.x) / 2}).attr(y, function (d) {return (d.source.y d.target.y) / 2}).text(function (d) {return d.type;}).on(mouseover, function (d) { // 鼠标选中时触发mouseSelectLine(d);addToolTip(d); //添加提示框的div}).on(mouseout, function () {d3.select(#relation).remove();d3.select(#tooltip).remove();}).on(click, function () {});return svg_text_edges;}// 对于每一个时间间隔进行更新function refresh() {force.on(tick, function () { // 对于每一个时间间隔// 更新连线坐标·svg_edges.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;});// 更新节点以及文字坐标svg_nodes.attr(transform, function (d) {return translate( d.x , d.y );});// 更新关系文字坐标svg_text_edges.attr(x, function (d) {return (d.source.x d.target.x) / 2}).attr(y, function (d) {return (d.source.y d.target.y) / 2});});}var force, nodes [], edges [], rawNodes, rawEdges, mapNodes new Map(); // 构建知识图谱需要操作的数据 (rawNodes, rawEdges将加载的原始构图数据缓存一份)// 知识图谱可视化构建function graph(data) {// 先清空布局中的图形元素// d3.select(#svgGraph).select(#svgOne).selectAll(*).remove();// var serverD data.nodes;// var serverE data.links;// 去除NODES中重复的节点如果有节点重复即将EDGES中的数据重新设置source值和target值// serverD,serverE,nodes,edges// var filterServerD [];// for (var i 0; i serverD.length; i) {// if (!isInArray(nodesIndexId, serverD[i].id)) {// filterServerD.push(serverD[i]);// }// }// 去重之后重新调整filterServerD的NODE index值// mapNodes.forEach(function (value, key) {// console.log(value);// console.log(key);// if (isInArray(nodesIndexId,key)){//// }// });// recordNodesIndex(serverD);// console.log(nodesIndexValue);// 多数组连接// nodes nodes.concat(data.nodes);// edges edges.concat(data.links);// console.log(nodes);// console.log(edges);// rawNodes nodes;// rawEdges edges;// // 定义力布局(数据转换)// force d3.layout.force()// .nodes(nodes) // 指定节点数组// .links(edges) // 指定连线数组// .size([width, height]) // 指定范围// .linkDistance(150) // 指定连线长度// // .gravity(0.02) // 设置引力避免跃出布局// .friction(0.9) // 设置摩擦力速度衰减// .charge(-400); // 相互之间的作用力// force.start(); // 开始作用// buildGraph();}var svg_edges, svg_nodes, svg_text_edges; // 需要动态更新的函数(dynamic update function)// Strat build Knowledge Graph/Vaultfunction buildGraph() {console.log(开始构建可视化知识图谱);console.log(nodes);console.log(edges);svg_edges add_edges(); // 添加连线与箭头svg_nodes add_nodes(); // 添加节点与文字svg_text_edges add_text_edges(); // 添加描述关系的文字refresh(); // 对于每一个时间间隔进行更新force.resume(); // 必须添加否则图形元素更新不及时}// 服务器加载数据var dataCirclePartition;function load() {d3.json(http://222.216.195.154:7476/knowledge-graph/hello/dataSource/type/1, function (error, root) { // 服务器加载节点圆形分区数据if (error) {return console.warn(error);}dataCirclePartition root;});d3.json(http://222.216.195.154:7476/knowledge-graph/hello/dataSource/type/2, function (error, json) { // 服务器加载知识图谱数据if (error) {return console.warn(error);}console.log(初始加载);console.log(json.nodes);console.log(json.links);graph(json);});// d3.json(http://222.216.195.154:7476/knowledge-graph/hello/dataSource/node/extend/99817, function (error, json) { // 服务器加载知识图谱数据// if (error) {// return console.warn(error);// }// console.log(初始加载);// console.log(json);// graph(json);// });}// 初始化图数据库配置信息startNeo4j();// 执行知识图谱数据可视化load();// 传入NODE ID与NODE INDEX,节点的INDEX与构图时数据加载的顺序密切相关function loadById(id, maxNodeIndex, nodesIdList) {// var para [id: id, maxNodeIndex: maxNodeIndex, nodesIdList: nodesIdList];var para {id: id, maxNodeIndex: maxNodeIndex, nodesIdList: nodesIdList};console.log(para);d3.json(http://222.216.195.154:7476/knowledge-graph/hello/dataSource/node/idIndex/ para, function (error, data) { // 服务器加载知识图谱数据if (error) {return console.warn(error);}console.log(动态ID加载的数据);console.log(nodesMark);console.log(edgesMark);console.log(nodes);console.log(edges);console.log(data);graph(data);});}function loadZdrSearch(json) {d3.json(http://222.216.195.154:7476/knowledge-graph/hello/dataSource/type/1, function (error, root) { // 服务器加载节点圆形分区数据if (error) {return console.warn(error);}dataCirclePartition root;});graph(json);}// 执行知识图谱数据可视化// loadById(id);启动入口类KnowledgeGraphApplication之后调用接口http://localhost:7476/knowledge-graph/hello/index此接口调用控制类加载index.htmlHTML中调用了js文件加载展示数据详细的实现过程请看完整的代码注释。代码目录结构说明一:代码目录结构说明二:#### 四、可视化效果(所有可视化效果均带有力布局效果)######1.节点与关系均带有选中效果节点关系裁剪与扩展☿解锁当前节点✂ 剪切当前节点于关系✠ 扩展当前节点与关系오 固定所有节点◎ 解锁所有节点实体扩展功能:节点效果:######2.完整示例完整效果显示示例一完整效果显示示例二