Использование Protovis с динамически загружаемыми данными через JQuery
Я динамически загружаю некоторые данные социальной сети в веб-страницу, которую я хочу визуализировать с помощью Protovis.(На самом деле, данные загружаются в двухпроходном процессе - сначала список имен пользователей извлекается из Twitter, а затем список социальных соединения извлекаются из Google Social API.) Кажется, что код protovis выполняется внутри цикла событий, что означает, что код загрузки данных должен находиться вне этого цикла.
Как загрузить данные на страницу и проанализировать их перед "включением" цикла событий protovis? На данный момент, я думаю, что существует состояние гонки, при котором Protovis пытается визуализировать сетевые данные, которые еще не были загружены и проанализированы?
<html><head><title></title>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js"></script>
<script type="text/javascript" src="../protovis-3.2/protovis-r3.2.js"></script>
<script type="text/javascript">
//getNet is where we get a list of Twitter usernames
function getNet(){
url="http://search.twitter.com/search.json?q=jisc11&callback=?"
$.getJSON(url,function(json){
users=[]
uniqusers={}
for (var u in json['results']) {
uniqusers[json['results'][u]['from_user']]=1
}
for (var uu in uniqusers)
users.push(uu)
getConnections(users)
})
}
//getConnections is where we find the connections between the users identified by the list of Twitter usernames
function getConnections(users){
//Google social API limits lookup to 50 URLs; need to page this...
if (users.length>50)
users=users.slice(0,49)
str=''
for (var uic=0; uic<users.length; uic++)
str+='http://twitter.com/'+users[uic]+','
url='http://socialgraph.apis.google.com/lookup?q='+str+'&edo=1&callback=?';
$.getJSON(url,function(json){
graph={}
graph['nodes']=[]
userLoc={}
for (var uic=0; uic<users.length; uic++){
graph['nodes'].push({nodeName:users[uic]})
userLoc[users[uic]]=uic
}
graph['links']=[]
for (u in json['nodes']) {
name=u.replace('http://twitter.com/','')
for (var i in json['nodes'][u]['nodes_referenced']){
si=i.replace('http://twitter.com/','')
if ( si in userLoc ){
if (json['nodes'][u]['nodes_referenced'][i]['types'][0]=='contact')
graph['links'].push({source:userLoc[name], target:userLoc[si]})
}
}
}
followers={}
followers={nodes:graph['nodes'],links:graph['links']}
});
}
$(document).ready(function() {
users=['psychemedia','mweller','mhawksey','garethm','gconole','ambrouk']
//getConnections(users)
getNet()
})
</script>
</head>
<body>
<div id="center"><div id="fig">
<script type="text/javascript+protovis">
// This code is taken directly from the protovis example
var w = document.body.clientWidth,
h = document.body.clientHeight,
colors = pv.Colors.category19();
var vis = new pv.Panel()
.width(w)
.height(h)
.fillStyle("white")
.event("mousedown", pv.Behavior.pan())
.event("mousewheel", pv.Behavior.zoom());
var force = vis.add(pv.Layout.Force)
.nodes(followers.nodes)
.links(followers.links);
force.link.add(pv.Line);
force.node.add(pv.Dot)
.size(function(d) (d.linkDegree + 4) * Math.pow(this.scale, -1.5))
.fillStyle(function(d) d.fix ? "brown" : colors(d.group))
.strokeStyle(function() this.fillStyle().darker())
.lineWidth(1)
.title(function(d) d.nodeName)
.event("mousedown", pv.Behavior.drag())
.event("drag", force)
//comment out the next line to remove labels
//.anchor("center").add(pv.Label).textAlign("center").text(function(n) n.nodeName)
vis.render();
</script>
</div></div>
</body></html>
2 ответа
vis.render()
в настоящее время вызывается до того, как вы получите данные. Могут быть и другие проблемы, но это должно быть после getNet()
,
РЕДАКТИРОВАТЬ 1:
vis.render()
сейчас после getNet()
, Я поместил код создания макета прототипа в функцию, чтобы я мог контролировать его выполнение, и сделал vis
а также followers
переменные, видимые как для кода инициализации, так и для createLayout
код.
Protovis, особенно силовое расположение, очень не прощает ошибок - например, неправильная структура или количество элементов для структуры данных узлов / связей, и не сообщает вам, что происходит, поэтому при разработке лучше всего сначала использовать известные вам статические данные имеет правильный вид, а затем заменить на динамически созданные данные.
Одна из проблем, с которыми вы столкнулись, заключается в том, что использование type="text/javascript+protovis"
вызывает переписывание javascript с помощью protovis. Код ниже использует type="text/javascript"
и имеет дополнительный {}
с и return
с использованием +protovis
спасает. Это позволяет getJSON()
и протоис сосуществуют в браузере Chrome, без getNet()
вызывается неоднократно.
<html><head><title></title>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="protovis-d3.2.js"></script>
<body>
<div id="center"><div id="fig">
<script type="text/javascript">
var vis;
var followers={};
function createLayout(){
var w = document.body.clientWidth,
h = document.body.clientHeight,
colors = pv.Colors.category19();
vis = new pv.Panel()
.width(w)
.height(h)
.fillStyle("white")
.event("mousedown", pv.Behavior.pan())
.event("mousewheel", pv.Behavior.zoom());
var force = vis.add(pv.Layout.Force)
.nodes(followers.nodes)
.links(followers.links);
force.link.add(pv.Line);
force.node.add(pv.Dot)
.size(function(d){ return (d.linkDegree + 4) * Math.pow(this.scale, -1.5);})
.fillStyle(function(d){ return d.fix ? "brown" : colors(d.group);})
.strokeStyle(function(){ return this.fillStyle().darker();})
.lineWidth(1)
.title(function(d){return d.nodeName;})
.event("mousedown", pv.Behavior.drag())
.event("drag", force);
//comment out the next line to remove labels
//.anchor("center").add(pv.Label).textAlign("center").text(function(n) n.nodeName)
vis.render();
}
function getNet(){
// OK to have a getJSON function here.
followers={nodes:[{nodeName:'mweller', group:6},
{nodeName:'mhawksey', group:6},
{nodeName:'garethm', group:6},
{nodeName:'gconole', group:6},
{nodeName:'ambrouk', group:6}
],
links:[
{source:0, target:1, value:1},
{source:1, target:2, value:1},
{source:1, target:4, value:1},
{source:2, target:3, value:1},
{source:2, target:4, value:1},
{source:3, target:4, value:1}]};
}
$(document).ready(function() {
getNet();
createLayout();
})
</script>
</head>
</div></div>
</body></html>
РЕДАКТИРОВАТЬ 2:
Если вы заинтересованы в копании немного глубже, проблема заключается в следующем коде в protovis:
pv.listen(window, "load", function() {
pv.$ = {i:0, x:document.getElementsByTagName("script")};
for (; pv.$.i < pv.$.x.length; pv.$.i++) {
pv.$.s = pv.$.x[pv.$.i];
if (pv.$.s.type == "text/javascript+protovis") {
try {
window.eval(pv.parse(pv.$.s.text));
} catch (e) {
pv.error(e);
}
}
}
delete pv.$;
});
Техника, которую я использовал "text/javascript"
и избегать использования "text/javascript+protovis"
и то, и другое решает вашу проблему и облегчает отладку кода, используя Protovis в Firefox.
Отличная работа, Джеймс - только одна вещь, на которую стоит обратить внимание: если вы сохраните createLayout()
; позвонить в JQuery $(document).ready()
Если вы хотите, чтобы ваша панель появлялась внутри div, в котором находится ваш скрипт, удалите ссылки jQuery, и все должно быть в порядке.
Изменить: я не знал о параметре холста в Protovis в то время, когда я написал это - просто добавив холст divid
на панель, плюс div с этим идентификатором, полностью решает проблемы с позиционированием.