// 描画処理
function view_map(){
// make_dataメソッドの結果を取得
var json = make_data(0);
// SVGが存在するならば削除
if (d3.select("#graph").select("svg")) {
d3.select("#graph").select("svg").remove();
}
if(json['nodes'].length != 0){
// 出来上がった結果を渡してマップ上のロケーションをセット
set_map_location(0, json['nodes'], json['links']);
// SVGの幅と高さを設定(幅:画面いっぱい 高さ:パスの数に応じ設定)
var width = window.innerWidth;
var height = ((NODEHEIGHT * 1.5) * PATHNUM) + (NODEHEIGHT / 2);
// カラーを取得
var color = d3.scale.category20();
// 画面サイズに合わせSVGの追加
var svg = d3.select("#graph").append("svg")
.attr("width", width)
.attr("height", height);
// 背景の追加
var bg = svg
.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", width)
.attr("height", height)
.attr("fill", "#fafafa");
// links配列を渡しリンクの作成
var link = svg.selectAll(".link")
.data(json.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) { return Math.sqrt(d.value);});
// nodes配列を渡しノードの作成
var node = svg.selectAll(".node")
.data(json.nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", (NODEHEIGHT / 2))
.attr("cx", function(d) { return d.x;} )
.attr("cy", function(d) { return d.y; })
.style("stroke", function(d) { return '#fafafa'; })
.style("stroke-width", function(d) { return '1.5px'; })
.style("fill", function(d) { return color(d.group); })
.style("cursor", function(d) { return 'pointer'; });
// nodes配列を渡しノードテキストの作成
var tnode = svg.selectAll("text.node")
.data(json.nodes)
.enter().append("svg:text")
.attr("class", "tnode")
.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; })
.text(function(d) { return d.name; })
.style("fill", function(d) { return '#000000'; })
.style("text-anchor", function(d) { return 'middle'; })
.style("pointer-events", "none");
// リンクテキストの作成
var tlink = svg.selectAll("text.link")
.data(json.links)
.enter().append("svg:text")
.attr("class", "tlink")
.attr("x", function(d) { return (json.nodes[d.source].x + json.nodes[d.target].x) / 2; })
.attr("y", function(d) { return (json.nodes[d.source].y + json.nodes[d.target].y) / 2; })
.style("fill", function(d) { return '#000000'; })
.style("text-anchor", function(d) { return 'middle'; });
// ノードへのオンマウスでパス探索、パス中のリンク文字を表示
node.on("mouseover", function(d){
// マウスの動きカウントをリセット
MOUSEMOVED = 0;
// 表示するパス保存用配列
var path = [];
// ツールチップへの名前表示用配列
var pathname = [];
// ルートノード以外なら
if(d.nodeid != 0){
// まずオンマウスされたノードのidと名前をそれぞれ追加
path.push(d.nodeid);
pathname.push(d.name);
// パス探索
do{
// リンクの数だけ繰り返し
for(var i = 0; i < link.data().length; i++){
// 現在の最後尾に繋がるリンクがあれば
if(path[(path.length-1)] == link.data()[i].target){
// そのリンクのソース側ノードのidを追加
path.push(link.data()[i].source);
// そのリンクの名前とソース側ノードの名前を追加
pathname.push(link.data()[i].property);
pathname.push(node.data()[link.data()[i].source].name);
}
}
// ルートノードに辿り着くまで繰り返す
}while(path[(path.length-1)] != 0);
// 末端ノードでないなら
if(d.path == "notend"){
// ツールチップを非表示に
document.getElementById("showpath").style.display = "none";
}else{
// 末端ノードならツールチップの情報を更新
var resultText = "
Selected Path
";
// パスの名前配列分後ろから繰り返しながら
for (var i = pathname.length;i > 0; i--){
// 奇数番目(ノードの名前)は太字に
if(i % 2 == 1){
resultText = resultText + "" + pathname[i - 1] + "
";
// 偶数番目(リンクの名前)はそのままで表示
}else{
resultText = resultText + pathname[i - 1] + "
";
}
}
// ツールチップの内容を書き換え
document.getElementById("selectpath").innerHTML=(resultText);
document.getElementById("showpath").style.display = "block";
// サーブレットに送り返すパスオブジェクトを保存
pathobj = d.path;
// ツールチップ表示時の座標(オンマウスされたノードの横にボタンが来るよう配置)
var xPosition = parseFloat(d3.select(this).attr("cx")) + parseFloat(d3.select(this).style("stroke-width")) + (NODEHEIGHT * 0.5);
var yPosition = parseFloat(d3.select(this).attr("cy") - document.getElementById("showpath").offsetHeight + document.getElementById("menu").offsetHeight) + (NODEHEIGHT * 0.5);
// ツールチップが画面外に出ないよう補正
if(xPosition < 0){
xPosition = 0;
}
if(yPosition < 0){
yPosition = 0;
}
// 生成した座標にツールチップを表示
document.getElementById("showpath").style.left = xPosition + "px"
document.getElementById("showpath").style.top = yPosition + "px"
}
// ルートノードだったなら
}else{
// ツールチップを非表示
document.getElementById("showpath").style.display = "none";
}
// オンマウスされたノードの高さに親を合わせるために合わせる高さを保存
var movey = d.y;
// 各ノードに対し
node
// 輪郭線の色を設定
.style("stroke", function(d){
// まずは背景色(デフォルト)を指定
var strokecolor = "#fafafa";
// パス判定の前処理
// 表示フラグがnow(前回オンマウスで動いていたノード)なら
if(d.view == "now"){
// ノードの表示フラグをnoに
d.view = "no";
}
// 表示フラグがmoved(クリックされ固定済みだが前回動いていたノード)なら
if(d.view == "moved"){
// 表示フラグをclickedに戻す
d.view = "clicked";
}
// 表示フラグがnoのものから確認
if(d.view == "no"){
// パスのノード数だけ繰り返しながら
for(var n = 0; n < path.length; n++){
// パス内に含まれるノードだったら
if(path[n] == d.nodeid){
// 輪郭線を赤に
strokecolor = "#ffaaaa";
// 表示フラグをnow(今回動いたノード)に
d.view = "now";
}
}
// 固定済みノードだったら
}else if(d.view == "clicked"){
// まずは輪郭線を赤に
strokecolor = "#ffaaaa";
// パス内に含まれるノードかチェック
for(var n = 0; n < path.length; n++){
if(path[n] == d.nodeid){
// 含まれていたなら今回動かすためフラグをmovedに
d.view = "moved";
}
}
}
// ここまでで得られた輪郭線の色を返す
return strokecolor;
})
// 高さの値
.attr("cy", function(d){
// ノードが今回オンマウスされた・または固定済みだが移動フラグをつけられていれば
if(d.view == "now" || d.view == "moved"){
// 現在の高さを取得
var curty = d.y;
// d.yに子ノードの高さをセット
d.y = movey;
// 現在の高さを返す(この時点では現在位置に描画され、redraw関数でd.yにアニメーションされる)
return curty;
// 移動対象でないなら
}else{
// 現在位置をそのまま返す
return d.y;
}
});
// 各リンクテキストに対し
tlink
// テキスト表示判定
.text(function(d) {
// デフォルトで空をセット
var linktext = "";
// 表示フラグがnow(前回表示されていたリンク)なら
if(d.view == "now"){
// 表示フラグを解除
d.view = "no";
}
// 表示フラグがnoなら
if(d.view == "no"){
// パスのノード数分繰り返し
for(var t = 0; t < path.length; t++){
// 自身がそのノードへ接続しているリンク(かつそのノードが折りたたまれていなければ)
if(path[t] == d.target && node.data()[d.target].view != "hide"){
// リンクテキストにプロパティの値をセット
linktext = d.property
// 表示フラグにnowをセット
d.view = "now";
}
}
// 表示フラグがfix(クリックされたパスのリンク)ならば(かつ繋がる先のノードが折りたたまれていなければ)
}else if(d.view == "fix" && node.data()[d.target].view != "hide"){
// リンクテキストにプロパティの値をセット
linktext = d.property
}
// ここまででできたリンクテキストを返す
return linktext;
});
// 各リンクに対し
link
// 線の色判定
.style("stroke", function(d){
// 表示フラグがnoならば
if(d.view == "no"){
// 色をデフォルトに
return "#999";
// それ以外(固定やオンマウスされたパスに含まれる)なら
}else{
// 色を赤に
return "#ffaaaa";
}
});
// ここまでの設定を元に再描画
redraw();
// ノードへのクリックで選択固定化(及び折り畳み処理)
}).on("click", function(d){
// 各ノードに対し
node
// 輪郭線の判定
.style("stroke", function(d) {
// デフォルトの色をセット
var strokecolor = "#fafafa"
// 表示フラグがオンマウス中・固定中・移動中(選択されているノード)ならば
if(d.view == "now" || d.view == "clicked" || d.view == "moved"){
// 色を赤に
strokecolor = "#ffaaaa"
// 表示フラグを固定中に
d.view = "clicked";
}
// ここまででできた色を返す
return strokecolor;
});
// ここから子ノードの畳み込み処理(現在は廃止、念のためコードは残しておく)
/*
var childs = [];
var prevchilds = [];
prevchilds.push(d.nodeid);
//
do{
var tmpchilds = [];
var curchilds = [];
for(var p = 0; p < prevchilds.length; p++){
tmpchilds = get_children(prevchilds[p], json['links']);
curchilds = curchilds.concat(tmpchilds);
}
prevchilds = curchilds;
childs = childs.concat(curchilds);
}while(curchilds.length != 0);
var childy = d.y;
for(var c = 0; c < childs.length; c++){
if(node.data()[childs[c]].y < childy){
childy = node.data()[childs[c]].y;
}
}
d.y = childy;
var maxdy = 0;
var mindy = 0;
for(var c = 0; c < childs.length; c++){
if(node.data()[childs[c]].view != "hide"){
var dy = node.data()[childs[c]].y - d.y;
if(dy > maxdy){
maxdy = dy;
}
node.data()[childs[c]].x = d.x;
node.data()[childs[c]].y = d.y;
node.data()[childs[c]].dy = dy;
node.data()[childs[c]].view = "hide";
node.data()[childs[c]].hideparent = d.nodeid;
}else{
if(node.data()[childs[c]].hideparent == d.nodeid){
node.data()[childs[c]].x = d.x + (TREESPACE * (node.data()[childs[c]].group - d.group));
node.data()[childs[c]].y = node.data()[childs[c]].y + node.data()[childs[c]].dy;
if(-node.data()[childs[c]].dy < mindy){
mindy = -node.data()[childs[c]].dy;
}
node.data()[childs[c]].view = "appear";
node.data()[childs[c]].hideparent = -1;
}
}
}
node
.attr("r", function(d){
if(d.view == "appear"){
d.view = "no";
}else if(d.y > childy){
d.y = d.y - maxdy - mindy;
}
if(d.view == "hide"){
d.x = node.data()[d.hideparent].x;
d.y = node.data()[d.hideparent].y;
}
if(d.nodeid == 0){
d.y = childy;
}
return (NODEHEIGHT / 2);
});
*/
// 各リンクに対し
tlink
// テキスト表示判定
.text(function(d) {
// デフォルトで空に
var linktext = "";
// 表示フラグが現在表示中または固定化済みならば(かつ折り畳み中でなければ)
if((d.view == "now" || d.view == "fix") && (node.data()[d.target].view != "hide")){
// リンクテキストにプロパティの値をセット
linktext = d.property
// 表示フラグを固定中に
d.view = "fix";
}
// テキストを返す
return linktext;
});
// ここまでの処理結果を元に再描画
redraw();
});
// 再描画関数
var redraw = function (duration){
// かける時間が未指定ならば
if(duration == undefined){
// 0.5秒かけてアニメーション
duration = 500;
}
// 各リンクについて設定された位置に再描画
link
.transition()
.duration(duration)
.attr("x1", function(d) {return node.data()[d.source].x;})
.attr("y1", function(d) {return node.data()[d.source].y;})
.attr("x2", function(d) {return node.data()[d.target].x;})
.attr("y2", function(d) {return node.data()[d.target].y;});
// 各リンクテキストについて設定された位置に再描画
tlink
.transition()
.duration(duration)
.attr("x", function(d) {return (node.data()[d.source].x + node.data()[d.target].x) / 2;})
.attr("y", function(d) {return ((node.data()[d.source].y + node.data()[d.target].y) / 2) + 5;});
// 各ノードについて設定された位置に再描画(かつ折りたたまれ中の場合の描画分岐処理)
node
.transition()
.duration(duration)
.attr("cx", function(d) {return d.x;})
.attr("cy", function(d) {return d.y;})
.style("opacity", function(d){
var opa = 1.0;
if(d.view == "hide"){
opa = 0.0;
}
return opa;
})
.style("fill", function(d) {
var fcolor = color(d.group);
for(var n = 0; n < node.data().length; n++){
if(d.nodeid == node.data()[n].hideparent){
fcolor = "ffaaaa";
}
}
return fcolor;
})
.style("pointer-events", function(d){
var pe = "auto";
if(d.view == "hide"){
pe = "none";
}
return pe;
});
// 各ノードテキストについて設定された位置に再描画、テキスト描画位置を上下に振る(かつ折りたたまれ中の場合の描画分岐処理)
tnode
.transition()
.duration(duration)
.attr("x", function(d) {return d.x;})
.attr("y", function(d) {
// デフォルトで少し下げる
var updown = (NODEHEIGHT * 0.4);
// 奇数番目の深さなら少し上げる
if(d.group % 2 == 1){
updown = -(NODEHEIGHT * 0.2);
}
// その値を高さに返すことでテキスト描画位置が互い違いになる
return d.y + updown;
})
// 折り畳み状態ならテキスト表示を消す
.text(function(d){
var nodetext = d.name
if(d.view == "hide"){
nodetext = "";
}
return nodetext;
});
};
// 背景部分がクリックされたら表示の固定化を解除
bg.on("click", function() {
// ツールチップを非表示
document.getElementById("showpath").style.display = "none";
d3.selectAll(".node").style("stroke-width", function(d) { return '1.5px'; });
d3.selectAll(".node").style("stroke", function(d) { return '#ffffff'; });
// 各ノードの輪郭線の色をデフォルトに
node
.style("stroke", function(d){
if(d.view != "hide"){
d.view = "no";
}
return "#fafafa";
});
// リンクテキストを全て空に
tlink
.text(function(d) {
d.view = "no";
return "";
});
// リンクの色を全てデフォルトに
link
.style("stroke", function(d){
return "#999";
});
});
// 背景上でマウスが動くごとに
bg.on("mousemove", function(){
// MOUSEMOVEDを追加(ノードにオンマウスされる度にカウントリセット)
MOUSEMOVED++;
// 30を超えたら
if(MOUSEMOVED > 30){
// ツールチップを非表示にしてカウントリセット
document.getElementById("showpath").style.display = "none";
MOUSEMOVED = 0;
}
});
// 初回のみdurationを0と指定し再描画(アニメーションなし)
redraw(0);
}
}
// データの作成メソッド
function make_data(tdepth, ret, parent, depth){
// retが未定義ならば定義して代入
if (ret == undefined){
ret = new Object();
ret['nodes'] = new Array();
ret['links'] = new Array();
}
PATHNUM = 0;
MAXDEPTH = 0;
TREESPACE = 0;
DRAWHEIGHT = NODEHEIGHT;
var viewnum;
var obj = JSON.parse(jsontext);
document.getElementById("resultmessage").style.color = "black";
document.getElementById("resultmessage").style.fontWeight = "normal";
document.getElementById("plural").innerHTML = "s";
if(obj['paths'].length == 0){
document.getElementById("resultmessage").style.color = "red";
document.getElementById("resultmessage").style.fontWeight = "bold";
document.getElementById("plural").innerHTML = "";
}else if(obj['paths'].length == 1){
document.getElementById("plural").innerHTML = "";
}
if(obj['paths'].length <= 10){
viewnum = obj['paths'].length;
document.getElementById("viewall").style.display = "none";
}else if(pathlimit == 10){
viewnum = 10;
document.getElementById("viewall").style.display = "block";
}else{
viewnum = obj['paths'].length;
document.getElementById("viewall").style.display = "none";
}
document.getElementById("pathnum").innerHTML = obj['paths'].length;
document.getElementById("resultmessage").style.display = "block";
// objトップ階層の数だけ繰り返しながら
for(var i = 0; i < viewnum; i++){
if(i == 0){
// 初回だけルートノードをプッシュ
ret['nodes'].push({'name': obj['paths'][0]['startClassLabel'], 'uri': obj['paths'][0]['startClassURI'], 'group': 0, 'x':50, 'y':50, 'nodeid':ret['nodes'].length, 'view' : 'no', 'path': 'notend'});
}
// 先にsourceに0(ルート)を代入
var source = 0;
// 共通ルート判定をtrueに
var isCommon = true;
// classLinksの数だけ繰り返しながら
for(var j = 0;j < obj['paths'][i]['classLinks'].length; j++){
// リンクの名前をURL末尾から取得
var propertytext = obj['paths'][i]['classLinks'][j]['propertyURI'];
var propertysplit1 = propertytext.split("/");
var propertysplit2 = propertysplit1[propertysplit1.length - 1];
var propertysplit3 = propertysplit2.split("#");
propertytext = propertysplit3[propertysplit3.length - 1];
if(MAXDEPTH < j+1){
MAXDEPTH = j+1;
}
// ここまで共通ルートなら
if(isCommon){
// 今回も共通か確認するためのフラグ
var isCommonNow = false;
// nodes配列に同じlinkedClassが既にあるか確認
var targets = [];
for(var k = 0; k < ret['nodes'].length; k++){
// 同階層かつ同じ名前のものがあったらtargets配列に番号を追加
if(ret['nodes'][k]['group'] == (j+1) && obj['paths'][i]['classLinks'][j]['linkedClassURI'] == ret['nodes'][k]['uri']){
targets.push(k);
}
}
// 既にあった場合はlinks配列に同じlinkが存在するか確認
if(targets.length != 0){
// 先ほど見つけたtargetsの数だけ繰り返しながら
for(var l = 0; l