Skip to content
Zhang Baofeng edited this page Jun 11, 2013 · 3 revisions

D3使用情况

D3是js的库,借助CSS3,HTML5以及svg,有一些比较赞的example。我选取了里面的WordCloud带fisheye效果的力导向图矩阵图。其实词云的效果并不好,后两者尝试了下还好,算是对可视化的初步探索,关键还是在于你想表达什么关系,套用什么图来表现,而难点就是在于数据串的计算,最后传给前端js代码生成就可以了。

java计算模块

词云

词云相对简单,需要传递给前端的是一个String list或者String[]。在action包的WordCloudAction里,示意了使用方法。

public String[] abs;

public String execute() throws Exception {
	if (user == null && name != null) {
		SolrjHelper solrj = new SolrjHelper(1);
		List<String> list = solrj.getAuthorAbstraction(name, start, rows);
		abs = StringUtil.removeDupl(list);
	} else if (user != null && name == null) {
		ArrayList<String> list = DAOfactory.getUserDAO().getUserKeys(user);
		abs = list.toArray(new String[list.size()]);
	}
	return SUCCESS;
}

通过别的类的接口计算得到list,然后处理一下变成abs这个String[]就可以了。

共生关系

共生关系采用的是力导向图和矩阵图两个可视化效果,两者可以共用一份计算结果。生成符合力导向图和矩阵图的数据串比较复杂。下面举Andrew Y. Ng与若干个学者共同发表论文的数据关系为例。这是一组json数据对,生成自后台java代码,会传送给前端js模块来生成两个可视化关系图。主要由nodes和links两部分组成。Nodes代表力导向图中每个点,group用以区分学者的不同工作地,在图中会成为颜色区分的根据。Nodes在矩阵图中则代表矩阵中横竖两列坐标。Links代表的是在nodes中某两个node有关系,他们的关系强度由value的相对值来比较区分。在力导向图中,links表现为两点之间是否有连线且连线的深浅。在矩阵图中,links表现为矩阵某个坐标小方格内是否上色且颜色有多深。

{
"nodes":[
  {"name":"Adam Coates","group":0},
  {"name":"Andrew Y. Ng","group":0},
  {"name":"Anna Petrovskaya","group":0},
  {"name":"Ashutosh Saxena","group":1},
  {"name":"Chuong Do","group":2},
  {"name":"Honglak Lee","group":3},
  {"name":"J. Zico Kolter","group":4},
  {"name":"Pieter Abbeel","group":5}
],
"links":[
  {"source":0,"target":1,"value":1},
  {"source":3,"target":1,"value":1},
  {"source":1,"target":5,"value":1},
  {"source":7,"target":1,"value":3},
  {"source":2,"target":1,"value":1},
  {"source":1,"target":7,"value":3},
  {"source":1,"target":0,"value":1},
  {"source":1,"target":3,"value":1},
  {"source":1,"target":6,"value":5},
  {"source":1,"target":2,"value":1},
  {"source":6,"target":1,"value":5},
  {"source":1,"target":4,"value":1},
  {"source":5,"target":1,"value":1},
  {"source":4,"target":1,"value":1}
]
}

从计算角度看,后台link的生成由solr请求完成,即nodes中每两人是否共同发表论文或共同发表过几次论文都会是一次solr搜索请求,所以将进行O(n*n)次计算,在数据量很大的情况下将花费很长时间。我做了两方面优化考虑。一方面我限定了图中nodes个数,最大为50,另一方面,计算过的json串都将存储在mysql表中,避免下一次重复计算。
详细计算过程在dcd.academic.recommend的BtwAuthor.java内。具体计算上面这份json数据对的代码在coauthorNodes()和coauthorLinks()两个函数内,且通过getCoauthorJson()将两份结果整合起来。当然这个.java里还有四个findCoAuthorsByXXX()的函数,对应的是discover.jsp里的四种共生关系的发现接口,这几个函数在计算过程中都会被使用,主要是打通了solr的搜索请求。具体在dcd.academic.action里有四个对应的action,分别是CoauthorAction,CofieldAction,CopaperAction和CoplaceAction。比如拿CoauthorAction举个例子,其他几个都是相似的处理方法:

@Override
public String execute() throws Exception {
	DAOfactory factory = new DAOfactory();
	SaveDAO dao = factory.getSaveDAO();
	String s = dao.getDiscover(name, "author");
	if ( s != null) {
		json = JSONObject.fromObject(s);
	} else {
		BtwAuthor ba = new BtwAuthor();
		ArrayList<String> list = ba.findCoAuthorsByName(name, 0, 50);
		json = ba.getCoauthorJson(list);
		dao.saveDiscover(name, "author", json.toString());
	}
	return SUCCESS;
}

重点关注else{}内action是怎么使用BtwAuthor这个类,把name这个参数传进去,最后直接用ba.getCoauthorJson(list)得到了json计算结果,然后就回传给前端了。整个计算过程都封装在了BtwAuthor内,调用的时候就像上面那么简单。

js模块

词云

词云的js生成代码单独在cloud.js里,我们不需要知道cloud.js里是怎么用d3代码生成图形的,我们只需要知道怎么调用这个js并做好传参工作。在index.js里有这个函数:

function wordcloud(str) {
    var WIDTH = 850;
    var HEIGHT = 300;
    var ANGLE = 30;
    var fill = d3.scale.category20();

    d3.layout.cloud().size([WIDTH, HEIGHT]).words(str.map(function(d) {
        return {text: d, size: 20 + Math.random() * 50};
    })).rotate(function() { 
    	return ~~(Math.random() * 2) * ANGLE; 
    }).font("Impact").fontSize(function(d) { 
    	return d.size; 
    }).on("end", draw).start();

    $('#load_three').remove();
    $('#load_tag').remove();
	
    function draw(words) {
        d3.select(".wordcloud").append("svg").attr("width", WIDTH).attr("height", HEIGHT)
          .append("g").attr("transform", "translate(450,150)").selectAll("text").data(words)
          .enter().append("text").style("font-size", function(d) { return d.size + "px"; })
          .style("font-family", "Impact").style("fill", function(d, i) { return fill(i); })
          .attr("text-anchor", "middle").attr("transform", function(d) {
            return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")";
        }).text(function(d) { return d.text; });
    }
}

WIDTH,HEIGHT定义了整个svg图的长宽尺寸,ANGLE定义了word的最小旋转角度。传参str来自后台action,完成这个绘制工作,我们关心的也就是传参和调用工作罢了。

共生关系

说明一下生成共生关系的整个逻辑。首先在discover.jsp里有四种生成方式,分别是coauthor,cofield,copaper和coplace,这四个输入框和button,会对应四个action,coauthorAction,cofieldAction,copaperAction和coplaceAction。在index.js的入口处,设置了四个button的event触发效果:

//discover.jsp
$('.coauthor_btn').on("click", function(e) {
  showVisualization("coauthor", $("#dname").val());
});
$('.cofield_btn').on("click", function(e) {
  showVisualization("cofield", $("#dfield").val());
});
$('.coplace_btn').on("click", function(e) {
  showVisualization("coplace", $("#dplace").val());
});
$('.copaper_btn').on("click", function(e) {
  showVisualization("copaper", $("#dpaper").val());
});

而在showVisualization()里其实是设置了其他一些内容,然后主要做可视化工作的是coauthorOne()和coauthorTwo()两个函数,

function showWordCloud(user) {
	var the_url = "";
	if (user != undefined) {
		the_url =  "wordcloud?user="+user;
	} else {
		the_url = "wordcloud?name="+$("#rname").val();
	}
	alert(the_url);
	$.ajax({
		type : 'GET',
		url : the_url,
		dataType : 'json',
		success : function(data) {
			wordcloud(data.abs);
		},
		error : function(XmlHttpRequest, textStatus, errorThrown) {
			alert("showWordCloud ajax error!");
		}
	});
}

分别对应力导向图的生成和矩阵图的生成,其生成数据上面介绍过了,就是后台的json计算方式。在两个coauthorXXX()里,我简单包装了下,具体两段代码比较长,就不帖出来了。