[知识图谱实战篇] 七.HTML+D3实现关系图谱搜索功能
前面作者講解了很多知識圖譜原理知識,包括知識圖譜相關技術、Neo4j繪制關系圖譜等,但仍缺少一個系統全面的實例。為了加深自己對知識圖譜構建的認識,為后續創建貴州旅游知識圖譜打下基礎,作者深入學習了張宏倫老師的網易云課程(星球系列電影),并結合自己的理解和技術分享了該系列專欄,從數據采集、數據展示、數據分析到知識圖譜構建,文章后續還會講解中文數據的實體識別、關系抽取、知識計算等。
前面通過六篇文章基本構建了電影知識圖譜,并且能顯示選中節點相關聯的邊及屬性,如下圖所示:
本文主要增加了一個搜索功能,通過該搜索框能展示搜索節點的相關內容,同時在張老師的基礎上增加搜索節點相關聯的邊及節點。如下圖所示:
代碼下載地址:https://download.csdn.net/download/eastmount/10958879
Github下載地址:https://github.com/eastmountyxz/Knowledge-Graph-Movie
這是一系列基礎性文章,希望對您有所幫助 ,尤其是對知識圖譜感興趣和編程剛入門的同學。同時也因為最近準備博士考試,做題做吐了,寫點新專欄調節下心情,與君共勉,一起加油。
前文:
[知識圖譜實戰篇] 一.數據抓取之Python3抓取JSON格式的電影實體
[知識圖譜實戰篇] 二.Json+Seaborn可視化展示電影實體
[知識圖譜實戰篇] 三.Python提取JSON數據、HTML+D3構建基本可視化布局
[知識圖譜實戰篇] 四.HTML+D3+CSS繪制關系圖譜
[知識圖譜實戰篇] 五.HTML+D3添加鼠標響應事件顯示相關節點及邊
[知識圖譜實戰篇] 六.HTML+D3實現點擊節點顯示相關屬性及屬性值
文章目錄
- 一.HTML增加搜索框
- 二.JS增加搜索響應事件
- 三.優化代碼-顯示相關聯節點及邊
- 四.完整代碼
推薦作者的知識圖譜前文:
知識圖譜相關會議之觀后感分享與學習總結
中文知識圖譜研討會的學習總結 (上) 圖譜引入、百度知心、搜狗知立方
搜索引擎和知識圖譜那些事 (上).基礎篇
基于VSM的命名實體識別、歧義消解和指代消解
CSDN下載-第一屆全國中文知識圖譜研討會演講PPT 清華大學
CSDN下載-知識圖譜PDF資料 清華大學知識圖譜研討會匯報PPT
[知識圖譜構建] 一.Neo4j圖數據庫安裝初識及藥材供應圖譜實例
[知識圖譜構建] 二.《Neo4j基礎入門》基礎學習之創建圖數據庫節點及關系
[關系圖譜] 一.Gephi通過共線矩陣構建知網作者關系圖譜
[關系圖譜] 二.Gephi導入共線矩陣構建作者關系圖譜
再次強烈推薦大家閱讀張宏倫老師的網易云課程及Github源碼,受益匪淺。
https://github.com/Honlan/starwar-visualization/tree/master/star_war
https://study.163.com/course/courseLearn.htm?courseId=1003528010
一.HTML增加搜索框
首先,利用HTML繪制一個搜索框。
<!-- 繪制搜索框 --> <div id="search"><input type="text" class="form-control"> </div>注意,class為form-control,它是bootstrap的基本樣式,所以需要在前面導入基本樣式代碼,如下:
<link href="http://cdn.bootcss.com/bootstrap/3.3.4/css/bootstrap.min.css" rel="stylesheet"> <script src="http://cdn.bootcss.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>此時,你會發現新增的搜索框已經被置于底部,接著需要通過CSS設置其樣式。
CSS增加樣式代碼如下:絕對定位、位置、顏色、邊框去掉、outline去掉、陰影 去掉、跨度設置、背景顏色設置。
#search input {position: absolute;top: 220px;left: 60px;color: #fff;border: none;outline: none;box-shadow: none;width: 200px;background-color: #666; }此時就增加好了搜索框:
二.JS增加搜索響應事件
當搜索框設置完成之后,再設置其響應事件。添加事件 $(’#search1 input’).keyup(function(event) {})
其基本功能表示:
- 當輸入內容則響應該事件keyup
- 如果input值為空,即沒有輸入搜索內容則顯示所有圓和邊
- 如果有搜索內容,顯示搜索相關聯的節點
- 同時設置Texts、所有邊隱藏
此時的運行效果如下圖所示,比如搜索 luke 則反饋該節點,它是一個人物。
點擊“文字”的搜索也是如此,僅顯示單一節點。
三.優化代碼-顯示相關聯節點及邊
但我們更期待的結果是反饋搜索節點及其相關聯的邊及節點,下面作者結合之前的文章進行優化。此時原理很簡單,增加一個循環,判斷所有邊的起點(Source)或終點(Target)與該搜索節點相鄰,則顯示,否則設置其class屬性為’inactive’,即隱藏節點。
核心代碼如下:
<script> //搜索框中輸入內容則響應該事件 //keyup按鍵敲擊響應event $('#search input').keyup(function(event) {//如果Input值是空的顯示所有的圓和線(沒有進行篩選)if ($(this).val() == '') {d3.select('#svg1 .texts').selectAll('text').attr('class', '');d3.select('#svg1 .nodes').selectAll('circle').attr('class', '');d3.select('#svg1 .links').selectAll('line').attr('class', '');}//否則判斷判斷三個元素是否等于name值,等于則顯示該值else {var name = $(this).val();//搜索所有的節點d3.select('#svg1 .nodes').selectAll('circle').attr('class', function(d) {//輸入節點id的小寫等于name則顯示,否則隱藏if (d.id.toLowerCase().indexOf(name.toLowerCase()) >= 0) {return '';} else {//優化:與該搜索節點相關聯的節點均顯示//links鏈接的起始節點進行判斷,如果其id等于name則顯示這類節點//注意: graph=datafor (var i = 0; i < graph.links.length; i++) {//如果links的起點等于name,并且終點等于正在處理的則顯示if ((graph.links[i]['source'].id.toLowerCase().indexOf(name.toLowerCase()) >= 0) && (graph.links[i]['target'].id == d.id)) {return '';}//如果links的終點等于name,并且起點等于正在處理的則顯示if ((graph.links[i]['target'].id.toLowerCase().indexOf(name.toLowerCase()) >= 0) && (graph.links[i]['source'].id == d.id)) {return '';}} return 'inactive'; //隱藏其他節點 }});//搜索textsd3.select('#svg1 .texts').selectAll('text').attr('class', function(d) {if (d.id.toLowerCase().indexOf(name.toLowerCase()) >= 0) {return '';} else {//優化:與該搜索節點相關聯的節點均顯示//links鏈接的起始節點進行判斷,如果其id等于name則顯示這類節點//注意: graph=datafor (var i = 0; i < graph.links.length; i++) {//如果links的起點等于name,并且終點等于正在處理的則顯示if ((graph.links[i]['source'].id.toLowerCase().indexOf(name.toLowerCase()) >= 0) && (graph.links[i]['target'].id == d.id)) {return '';}//如果links的終點等于name,并且起點等于正在處理的則顯示if ((graph.links[i]['target'].id.toLowerCase().indexOf(name.toLowerCase()) >= 0) && (graph.links[i]['source'].id == d.id)) {return '';}} return 'inactive'; //隱藏其他節點}});//搜索links 所有與搜索name相關聯的邊均顯示//顯示相的鄰邊 注意 || //name=$(this).val():名字為鍵盤輸入的內容d3.select("#svg1 .links").selectAll('line').attr('class', function(d) {if ((d.source.id.toLowerCase().indexOf(name.toLowerCase()) >= 0) || (d.target.id.toLowerCase().indexOf(name.toLowerCase()) >= 0) ) {return '';} else {return 'inactive'; //隱藏}});} }); //end input </script>運行結果如下圖所示,同時如果有多個名字相關的節點,它們與其關聯的節點和邊均會顯示。
PS:但是也存在一個問題,即右邊顯示的屬性為鼠標選中顯示,而搜索通常不能顯示屬性,但這不會影響整體效果。
四.完整代碼
完整代碼如下所示:
<!DOCTYPE html> <html><head><meta charset="UTF-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1"><title>知識圖譜</title><meta name="description" content="" /><meta name="keywords" content="" /><meta name="author" content="" /><link rel="shortcut icon" href=""><script src="http://cdn.bootcss.com/jquery/2.1.4/jquery.min.js"></script><link href="http://cdn.bootcss.com/bootstrap/3.3.4/css/bootstrap.min.css" rel="stylesheet"><script src="http://cdn.bootcss.com/bootstrap/3.3.4/js/bootstrap.min.js"></script> </head> <style type="text/css"> body {background-color: #272b30;padding: 30px 40px;text-align: center;font-family: OpenSans-Light, PingFang SC, Hiragino Sans GB, Microsoft Yahei, Microsoft Jhenghei, sans-serif; }#indicator {position: absolute; left: 60px;bottom: 120px;text-align: left;color: #f2f2f2;font-size: 12px; }#indicator>div {margin-bottom: 4px; }#indicator span {display: inline-block;width: 30px;height: 14px;position: relative;top: 2px;margin-right: 8px; }.links line {stroke: rgb(240, 240, 240); stroke-opactity: 0.2; }.links line.inactive {/*display: none !important;*/stroke-opacity: 0; }.nodes circle {stroke: #fff;stroke-width: 1.5px; }.nodes circle:hover {cursor: pointer; }.nodes circle.inactive {display: none !important; }.texts text {display: none; }.texts text:hover {cursor: pointer; }.texts text.inactive {display: none !important; }#mode {position: absolute;top: 160px;left: 60px; }#mode span {display: inline-block;border: 1px solid #fff;color: #fff;padding: 6px 10px;border-radius: 4px;font-size: 14px;transition: color, background-color .3s;-o-transition: color, background-color .3s;-ms-transition: color, background-color .3s;-moz-transition: color, background-color .3s;-webkit-transition: color, background-color .3s; }#mode span.active, #mode span:hover {background-color: #fff;color: #333;cursor: pointer; }#info {position: absolute;bottom: 40px;right: 30px;text-align: right;width: 270px; }#info p {color: #fff;font-size: 12px;margin-top: 0;margin-bottom: 5px; }#info p span {color: #888;margin-right: 10px; }#info h4 {color: #fff; }#search input {position: absolute;top: 220px;left: 60px;color: #fff;border: none;outline: none;box-shadow: none;width: 200px;background-color: #666; } </style> <body><!-- 繪制標題樣式 --><h1 style="color:#fff;font-size:32px;margin-bottom:0px;text-align:center;margin-left:40px;">Star Wars</h1><!-- 第一個布局 繪制知識圖譜主圖 --><div style="text-align: center; position:relative;"><svg width="800" height="560" style="margin-right:80px;margin-bottom:-40px;" id="svg1"></svg><!-- 繪制圖例 --><div id="indicator"></div><!-- 繪制模式選擇 --><div id="mode"><span class="active" style="border-top-right-radius:0;border-bottom-right-radius:0;">節點</span><span style="border-top-left-radius:0;border-bottom-left-radius:0;position:relative;left:-5px;">文字</span></div><!-- 繪制搜索框 --><div id="search"><input type="text" class="form-control"></div><!-- 繪制右邊顯示結果 --><div id="info"><h4></h4></div></div><!-- 第二個布局 下部分時間點 文本居中 相對定位--><div style="text-align: center; position:relative;"><svg width="960" height="240" style="margin-right:60px;margin-bottom:-40px;" id="svg1"><g></g></svg></div></body><!-- 增加D3元素庫 --> <script src="https://d3js.org/d3.v4.min.js"></script> <!-- 補充JS代碼 --> <script type="text/javascript">$(document).ready(function() {//定義svg變量將布局svg1選出來 var svg = d3.select("#svg1"), width = svg.attr("width"), height = svg.attr("height");//定義name變量制作圖標var names = ['Films', 'Characters', 'Planets', 'Starships', 'Vehicles', 'Species'];var colors = ['#6ca46c', '#4e88af', '#ca635f', '#d2907c', '#d6744d', '#ded295'];//背景顏色設置 補充CSS樣式設置字體布局for (var i=0; i < names.length; i++) {$('#indicator').append("<div><span style='background-color:" + colors[i] + "'></span>" + names[i] + "</div>");}//利用d3.forceSimulation()定義關系圖 包括設置邊link、排斥電荷charge、關系圖中心點var simulation = d3.forceSimulation().force("link", d3.forceLink().id(function(d) {return d.id;})).force("charge", d3.forceManyBody()).force("center", d3.forceCenter(width / 2, height / 2));//存儲關系圖的數據var graph;//定義d3.json請求python處理好的節點及邊 請求成功返回數據,否則報錯d3.json("starwar_alldata.json", function(error, data) {if(error) throw error;graph = data;console.log(graph);//D3映射數據至HTML中//g用于繪制所有邊,selectALL選中所有的line,并綁定數據data(graph.links),enter().append("line")添加元素//數據驅動文檔,設置邊的粗細//前面定義var svg = d3.select("#svg1")var link = svg.append("g").attr("class","links").selectAll("line").data(graph.links).enter().append("line").attr("stroke-width", function(d) {//return Math.sqrt(d.value);return 1; //所有線寬度均為1});//添加所有的點//selectAll("circle")選中所有的圓并綁定數據,圓的直徑為d.size//再定義圓的填充色,同樣數據驅動樣式,圓沒有描邊,圓的名字為d.id//call()函數:拖動函數,當拖動開始綁定dragstarted函數,拖動進行和拖動結束也綁定函數var node = svg.append("g").attr("class", "nodes").selectAll("circle").data(graph.nodes).enter().append("circle").attr("r", function(d) {return d.size;}).attr("fill", function(d) {return colors[d.group];}).attr("stroke", "none").attr("name", function(d) {return d.id;}).call(d3.drag().on("start", dragstarted).on("drag", dragged).on("end", dragended));//顯示所有的文本 //設置大小、填充顏色、名字、text()設置文本//attr("text-anchor", "middle")文本居中var text = svg.append("g").attr("class", "texts").selectAll("text").data(graph.nodes).enter().append("text").attr("font-size", function(d) {return d.size;}).attr("fill", function(d) {return colors[d.group];}).attr('name', function(d) {return d.id;}).text(function(d) {return d.id;}).attr('text-anchor', 'middle').call(d3.drag().on("start", dragstarted).on("drag", dragged).on("end", dragended));//圓增加titlenode.append("title").text(function(d) {return d.id;})//simulation中ticked數據初始化,并生成圖形simulation.nodes(graph.nodes).on("tick", ticked);simulation.force("link").links(graph.links);//ticked()函數確定link線的起始點x、y坐標 node確定中心點 文本通過translate平移變化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;});text.attr('transform', function(d) {return 'translate(' + d.x + ',' + (d.y + d.size / 2) + ')';});}});// Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension// 本地json數據需要放置服務器中請求 XAMPP//該變量保證拖動鼠標時,不會影響圖形變換,默認為false未選中鼠標var dragging = false;//開始拖動并更新相應的點function dragstarted(d) {if (!d3.event.active) simulation.alphaTarget(0.3).restart();d.fx = d.x;d.fy = d.y;dragging = true;}//拖動進行中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;dragging = false;}//span點擊事件$('#mode span').click(function(event) {//span都設置為不激活狀態$('#mode span').removeClass('active');//點擊的span被激活$(this).addClass('active');//text隱藏 nodes顯示if ($(this).text() == '節點') {$('.texts text').hide();$('.nodes circle').show();} else {$('.texts text').show();$('.nodes circle').hide();}});//為svg1父元素下的.nodes circle元素綁定鼠標進入事件$('#svg1').on('mouseenter', '.nodes circle', function(event) {//通過變量dragging保證拖動鼠標時,其狀態不受影響,從而改變圖形//鼠標沒有拖動才能處理事件if(!dragging) {//獲取被選中元素的名字var name = $(this).attr("name");//設置#info h4樣式的顏色為該節點的顏色,文本為該節點name//$(this).attr('fill')表示當前懸浮圓的填充色$('#info h4').css('color', $(this).attr('fill')).text(name);//每次點擊添加屬性前把上次顯示的信息去除,否則會不斷疊加$('#info p').remove();//打印懸浮的節點信息//console.log(info[name]);//遍歷所有的for (var key in info[name]) {//類型復雜的不進行顯示if (typeof(info[name][key]) == 'object') {continue;}//比較復雜的超鏈接字段不顯示if (key == 'url' || key == 'title' || key == 'name' || key == 'edited' || key == 'created' || key == 'homeworld') {continue;}//顯示值及其字段名字$('#info').append('<p><span>' + key + '</span>' + info[name][key] + '</p>');}//選擇#svg1 .nodes中所有的circle,再增加個classd3.select('#svg1 .nodes').selectAll('circle').attr('class', function(d) {//數據的id是否等于name,返回空if(d.id==name) {return '';} //當前節點返回空,否則其他節點循環判斷是否被隱藏起來(CSS設置隱藏)else {//links鏈接的起始節點進行判斷,如果其id等于name則顯示這類節點//注意: graph=datafor (var i = 0; i < graph.links.length; i++) {//如果links的起點等于name,并且終點等于正在處理的則顯示if (graph.links[i]['source'].id == name && graph.links[i]['target'].id == d.id) {return '';}if (graph.links[i]['target'].id == name && graph.links[i]['source'].id == d.id) {return '';}}return "inactive"; //前面CSS定義 .nodes circle.inactive}});//處理相鄰的邊line是否隱藏 注意 || d3.select("#svg1 .links").selectAll('line').attr('class', function(d) {if (d.source.id == name || d.target.id == name) {return '';} else {return 'inactive';}});}});//鼠標移開還原原圖,顯示所有隱藏的點及邊$('#svg1').on('mouseleave', '.nodes circle', function(event) {//如果dragging為false才處理事件if(!dragging) {d3.select('#svg1 .nodes').selectAll('circle').attr('class', '');d3.select('#svg1 .links').selectAll('line').attr('class', '');} });//鼠標進入文本顯示相鄰節點及邊$('#svg1').on('mouseenter', '.texts text', function(event) {if (!dragging) {var name = $(this).attr('name');//同樣的代碼從選中圓中賦值過來$('#info h4').css('color', $(this).attr('fill')).text(name);$('#info p').remove();for (var key in info[name]) {if (typeof(info[name][key]) == 'object') {continue;}if (key == 'url' || key == 'title' || key == 'name' || key == 'edited' || key == 'created' || key == 'homeworld') {continue;}$('#info').append('<p><span>' + key + '</span>' + info[name][key] + '</p>');}d3.select('#svg1 .texts').selectAll('text').attr('class', function(d) {if (d.id == name) {return '';}for (var i = 0; i < graph.links.length; i++) {if (graph.links[i]['source'].id == name && graph.links[i]['target'].id == d.id) {return '';}if (graph.links[i]['target'].id == name && graph.links[i]['source'].id == d.id) {return '';}}return 'inactive';});d3.select("#svg1 .links").selectAll('line').attr('class', function(d) {if (d.source.id == name || d.target.id == name) {return '';} else {return 'inactive';}});}});//鼠標移除文本還原相應節點及邊$('#svg1').on('mouseleave', '.texts text', function(event) {if (!dragging) {d3.select('#svg1 .texts').selectAll('text').attr('class', '');d3.select('#svg1 .links').selectAll('line').attr('class', '');}});//搜索框中輸入內容則響應該事件//keyup按鍵敲擊響應event$('#search input').keyup(function(event) {//如果Input值是空的顯示所有的圓和線(沒有進行篩選)if ($(this).val() == '') {d3.select('#svg1 .texts').selectAll('text').attr('class', '');d3.select('#svg1 .nodes').selectAll('circle').attr('class', '');d3.select('#svg1 .links').selectAll('line').attr('class', '');}//否則判斷判斷三個元素是否等于name值,等于則顯示該值else {var name = $(this).val();//搜索所有的節點d3.select('#svg1 .nodes').selectAll('circle').attr('class', function(d) {//輸入節點id的小寫等于name則顯示,否則隱藏if (d.id.toLowerCase().indexOf(name.toLowerCase()) >= 0) {return '';} else {//優化:與該搜索節點相關聯的節點均顯示//links鏈接的起始節點進行判斷,如果其id等于name則顯示這類節點//注意: graph=datafor (var i = 0; i < graph.links.length; i++) {//如果links的起點等于name,并且終點等于正在處理的則顯示if ((graph.links[i]['source'].id.toLowerCase().indexOf(name.toLowerCase()) >= 0) && (graph.links[i]['target'].id == d.id)) {return '';}//如果links的終點等于name,并且起點等于正在處理的則顯示if ((graph.links[i]['target'].id.toLowerCase().indexOf(name.toLowerCase()) >= 0) && (graph.links[i]['source'].id == d.id)) {return '';}} return 'inactive'; //隱藏其他節點 }});//搜索textsd3.select('#svg1 .texts').selectAll('text').attr('class', function(d) {if (d.id.toLowerCase().indexOf(name.toLowerCase()) >= 0) {return '';} else {//優化:與該搜索節點相關聯的節點均顯示//links鏈接的起始節點進行判斷,如果其id等于name則顯示這類節點//注意: graph=datafor (var i = 0; i < graph.links.length; i++) {//如果links的起點等于name,并且終點等于正在處理的則顯示if ((graph.links[i]['source'].id.toLowerCase().indexOf(name.toLowerCase()) >= 0) && (graph.links[i]['target'].id == d.id)) {return '';}//如果links的終點等于name,并且起點等于正在處理的則顯示if ((graph.links[i]['target'].id.toLowerCase().indexOf(name.toLowerCase()) >= 0) && (graph.links[i]['source'].id == d.id)) {return '';}} return 'inactive'; //隱藏其他節點}});//搜索links 所有與搜索name相關聯的邊均顯示//顯示相的鄰邊 注意 || //name=$(this).val():名字為鍵盤輸入的內容d3.select("#svg1 .links").selectAll('line').attr('class', function(d) {if ((d.source.id.toLowerCase().indexOf(name.toLowerCase()) >= 0) || (d.target.id.toLowerCase().indexOf(name.toLowerCase()) >= 0) ) {return '';} else {return 'inactive'; //隱藏}});}}); //end input//加載Python獲取的Json信息:六類實體詳細屬性信息var info;//d3.json獲取數據d3.json("all_data.json", function(error, data) {if(error) throw error;info = data;});}); </script> </html>源碼下載地址:https://download.csdn.net/download/eastmount/10958879
希望這篇基礎性文章對你有所幫助,尤其是新手。
哎,英語真心太差了,博士也不一定能考上,但我至少奮斗過,這個寒假每日每夜都忙碌著。同時也希望未來能有更多時間學習新知識,分享新知識。未來的路誰又知道,作為一名大學老師,希望自己能始終保持初心,教好自己的每一個學生;作為一名程序員,希望自己知道學無止境,寫好自己的每一篇博文;作為一名主人公,希望自己能照顧好另一伴,愛她就像愛生命。我毫無閱歷,毫無準備,我一頭栽進我的命里,就像跌進一個深坑,從那一秒鐘起,我的心里只有一個,就是你。
(By:Eastmount 2019-02-14 下午4點 http://blog.csdn.net/eastmount/)
總結
以上是生活随笔為你收集整理的[知识图谱实战篇] 七.HTML+D3实现关系图谱搜索功能的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [知识图谱实战篇] 六.HTML+D3实
- 下一篇: [知识图谱实战篇] 八.HTML+D3绘