Использование Protovis с динамически загружаемыми данными через JQuery - PullRequest
5 голосов
/ 13 марта 2011

Я динамически загружаю некоторые данные социальной сети в веб-страницу, которую я хочу визуализировать с помощью 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 ]

5 голосов
/ 14 марта 2011

vis.render() в настоящее время вызывается до того, как вы получите данные. Могут быть и другие проблемы, но они должны быть после getNet().


РЕДАКТИРОВАТЬ 1:

vis.render() теперь после getNet(). Я поместил код создания макета прототипа в функцию, чтобы я мог контролировать его выполнение, и сделал переменные vis и followers видимыми как для кода инициализации, так и для кода createLayout.

Протовис, в частности расклад сил, очень не прощает ошибок - например, неправильная структура или количество элементов для структуры данных узлов / ссылок, и не говорит вам, что происходит, поэтому при разработке лучше сначала использовать статические данные, которые, как вы знаете, имеют правильный вид, а затем заменить на динамически созданные данные .

Одна часть проблемы, с которой вы столкнулись, заключается в том, что type="text/javascript+protovis" вызывает переписывание javascript с помощью protovis. Приведенный ниже код использует type="text/javascript" и имеет дополнительные {} с и return с, которые сохраняются с помощью +protovis. Это позволяет 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", и решает вашу проблему, и облегчает отладку кода с использованием Protovis в Firefox.

0 голосов
/ 18 апреля 2011

Отличная работа, Джеймс - только одна вещь, на которую стоит обратить внимание: если вы сохраните createLayout(); вызов внутри функции jQuery $(document).ready(), вы можете обнаружить, что ваша панель появляется не в том месте ... если вы хотите, чтобы панель отображалась внутри div, в котором находится ваш скрипт, удалите ссылки jQuery и все должно быть в порядке.

Edit: Я не знал о параметре canvas в Protovis в то время, когда писал это - простое добавление canvas divid на панель, плюс div с этим идентификатором, полностью решает проблемы с позиционированием.

...