使用 Protovis 通过 JQuery 动态加载数据

发布于 2024-10-21 20:36:27 字数 3562 浏览 7 评论 0原文

我正在动态地将一些社交网络数据加载到我想要使用 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>

I am dynamically loading some social network data into a web page that I want to visualise using protovis.(Actually, the data is loaded in in a two pass process - first a list of user names is grabbed from Twitter, then a list of social connections is grabbed from the Google Social API.) The protovis code seems to run inside an event loop, which means the data loading code needs to be outside this loop.

How do I load the data into the page and parse it, before "switching on" the protovis event loop? At the moment, I think there's a race condition whereby protovis tries to visualise network data that hasn't been loaded and parsed yet?

<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>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

在获取数据之前,当前正在调用 vis.render()。可能还有其他问题,但需要在 getNet() 之后。


编辑1:

vis.render() 现在位于getNet() 之后。我已将 protovis 强制布局创建代码放入函数内,以便我可以控制它的执行时间,并使 vis 和 followers 变量对初始化代码和createLayout 代码。

Protovis,特别是力布局,对错误非常不宽容 - 例如错误的结构或节点/链接数据结构的元素计数,并且不会告诉您发生了什么,因此在开发时最好首先使用您知道的静态数据是正确的类型,然后用动态创建的数据替换。

您遇到的问题之一是使用 type="text/javascript+protovis"
通过 protovis 调用 javascript 重写。下面的代码使用 type="text/javascript" 并具有使用 +protovis< 的额外 {}return /code> 保存。这允许 getJSON() 和 protovis 在 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" 既可以解决您的问题,又可以更轻松地在 Firefox 中使用 protovis 调试代码。

vis.render() is currently being called before you get the data. There may be other issues too, but it needs to be after getNet().


EDIT 1:

vis.render() is now after getNet(). I've put the protovis force layout creation code inside a function so that I can control when it executes, and made the vis and followers variables visible to both the initialization code and the createLayout code.

Protovis, particularly the force layout, is very unforgiving about errors - e.g. wrong structure or count of elements for nodes/links datastructure, and does not tell you what is going on, so in developing it is best to first use static data that you know is of the right kind, and then later replace with dynamically created data.

One part of the problem you were having is that using type="text/javascript+protovis"
invokes javascript rewriting by protovis. The code below uses type="text/javascript" and has the extra {}s and returns that using +protovis saves. This allows getJSON() and protovis to coexist in Chrome browser, without getNet() being called repeatedly.

<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>

EDIT 2:

In case you are interested in digging a bit deeper, the problem comes from this code in 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.$;
 });

The technique I've used to use "text/javascript" and avoid using "text/javascript+protovis" both solves your problem AND makes it easier to debug code using protovis in Firefox.

阳光下慵懒的猫 2024-10-28 20:36:27

干得好,James - 只需要注意一件事:如果保留 createLayout();在 jQuery $(document).ready() 函数中调用,您可能会发现面板出现在错误的位置...如果您希望面板出现在脚本所在的 div 中,请删除jQuery refs 一切都应该没问题。

编辑:
在我写这篇文章时,我并不知道 Protovis 中的 canvas 参数 - 只需将 canvas divid 添加到面板,再加上具有该 id 的 div,就可以完全解决定位问题。

Great job James - only one thing to watch out for: If you keep the createLayout(); call within the jQuery $(document).ready() function you might find your panel appearing in the wrong place... if you want the panel to appear within the div your script is in, remove the jQuery refs and all should be fine.

Edit:
I wasn't aware of the canvas parameter in Protovis at the time I wrote this - simply adding canvas divid to the panel, plus a div with that id, takes care of positioning issues completely.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文