user1977072
user1977072

Reputation: 51

D3 Library — How To Access Data from JSON in this example

I didn't explain the issue properly in the title... Sorry.

I'm following the D3 Tag Cloud Simple example https://github.com/jasondavies/d3-cloud/blob/master/examples/simple.html

I have a JSON file that consists of tweet tags, sentimental values and tweet text; (excerpt)

var words = [{"word":"disgrace","sentiment":"0.975","tweet":"Wheres Fred the weatherman? \nIn jail #disgrace #dirtyman @MrJimmyCorkhill"},{"word":"dirtyman","sentiment":"0.975","tweet":"Wheres Fred the weatherman? \nIn jail #disgrace #dirtyman @MrJimmyCorkhill"}];

I want to use the "tweet" value as a 'title' element of each 'text' element. I tried doing this by putting my tweet in the .words function (or .map, I don't know :s), as other data is accessed that way, but I can't extract my 'tweet' data;

var fill = d3.scale.category20();
var words = <?php echo $tweets->getTweetTags(); ?>;

d3.layout.cloud().size([1000, 1000])
.words(words.map(function(d) {
return {text: d.word, size: d.sentiment * 40, tweet: d.tweet};
}))
.rotate(function() { return ~~(Math.random() * 2) * Math.random() * 1; })
.font("Impact")
.fontSize(function(d) { return d.size; })
.on("end", draw)
.start();

function draw(words) {
d3.select("#vis").append("svg")
.attr("width", 1000)
.attr("height", 1000)
.append("g")
.attr("transform", "translate(500,400)")
.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; })
.append("svg:title").text(function(d) { return d.tweet; } );
}

d.tweet returns undefined

I can probably understand why I can't use my tweet in the .words or .map function, as they only expect certain parameters, but I don't know how else to get the 'tweet' data into the 'title' tags of each 'text' element.

Can anyone help me with this?

Edit: Code with inside d3.json function;

var data; // a global
            d3.json("../../../assets/json/tweetTags.json", function(error, json) {
              if (error) return console.warn(error);
              data = json;

              var fill = d3.scale.category20();

              d3.layout.cloud().size([1000, 1000])
                  .words(data.map(function(d) {
                    return {text: d.word, size: d.sentiment * 40, tweet: d.tweet};
                  }))
                  .rotate(function() { return ~~(Math.random() * 2) * Math.random() * 1; })
                  .font("Impact")
                  .fontSize(function(d) { return d.size; })
                  .on("end", draw)
                  .start();

              function draw(data) {
                d3.select("#vis").append("svg")
                    .attr("width", 1000)
                    .attr("height", 1000)
                  .append("g")
                    .attr("transform", "translate(500,400)")
                  .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; })
                    .append("svg:title").text(function(d) { return d.tweet; } );
              }

              d3.layout.cloud().size([300, 300])
              .data.map(function(d) {
                return {text: d, size: 10 + Math.random() * 90};
              })
              .rotate(function() { return ~~(Math.random() * 2) * 90; })
              .font("Impact")
              .fontSize(function(d) { return d.size; })
              .on("end", draw)
              .start();
            });

I don't know how to get it working in like that. I have made the json file but now I can't access the data or run the cloud.

Edit: The text elements are appending outside of the closing body tag now :(

var data; // a global
            d3.json("../../../assets/json/tweetTags.json", function(error, json) {
              if (error) return console.warn(error);
              data = json;

              var fill = d3.scale.category20();

             d3.select("#vis").append("svg")
                 .attr("width", 1000)
                 .attr("height", 1000)
                 .append("g")
                 .attr("transform", "translate(500,400)")
                 .data(json)
                 .enter().append("text")
                 .style("font-size", function(d) { return d.sentiment * 40 + "px"; })
                 .style("font-family", "Impact")
                 .style("fill", function(d, i) { return fill(i); })

                 .text(function(d) {return d.word;})
                 .append("svg:title").text(function(d) { return d.tweet; } );

            });

Edit: @Chris This is your exampled code in the D3 json function and with my customisations

d3.json("../../../assets/json/tweetTags.json", function(error, json) {
  if (error) return console.warn(error);

  var fill = d3.scale.category20();

  d3.layout.cloud().size([600, 500])
      .words(json.map(function(d) {;
            return {text: d.word, size: d.sentiment * 40, tweet: d.tweet};
       }))
      .rotate(function(d) { return ~~(Math.random() * 2) * 90; })
      .font("Impact")
      .fontSize(function(d) { return d.size; })
      .on("end", draw)
      .start();

  function draw(words) { 
      d3.select("body").append("svg")
          .attr("width", 600).attr("height", 500)
          .append("g").attr("transform", "translate(350,350)")
          .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; }).append("svg:title")
          .text(function(d) { return d.tweet; } );
      }
})

But I can't access d.tweet from the draw function :s

Upvotes: 4

Views: 96176

Answers (3)

Igorovsky
Igorovsky

Reputation: 1

I had the exact same problem with the words going out of the svg container and i solved it by inserting a variable called origin that i put to (width/2) in the translate method. The border problems were caused by the fact that the alghorithm is made to keep them inside as long as you use the correct center ot the projection field for example: width=300, height=300 that means origin=150. I hope that helps you.

Upvotes: 0

Christopher Chiche
Christopher Chiche

Reputation: 15325

I suggest you should use the built-in import function for json instead of php. I think that the problem you have is that the reading of the data is asynchronous, so words is not filled before using it in clouds.

The principle behind d3.json() is to do everything in this function, which will be executed when the json is loaded:

var data; // a global
d3.json("path/to/file.json", function(error, json) {
  if (error) return console.warn(error);
  data = json;
  visualizeit();
});

EDIT

Here is an example of the code that should work, I just added the d3 cloud example inside the json function.

d3.json("../../../assets/json/tweetTags.json", function(error, json) {
  if (error) return console.warn(error);

  var fill = d3.scale.category20();

  d3.layout.cloud().size([300, 300])    
      .words([  // To be replaced with data
        "Hello", "world", "normally", "you", "want", "more", "words",
        "than", "this"].map(function(d) {
            return {text: d, size: 10 + Math.random() * 90};
       }))
      .rotate(function() { return ~~(Math.random() * 2) * 90; })
      .font("Impact")
      .fontSize(function(d) { return d.size; })
      .on("end", draw)
      .start();

  function draw(words) {
      d3.select("body").append("svg")
          .attr("width", 300).attr("height", 300)
          .append("g").attr("transform", "translate(150,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; });
      }
})

Then, when this version works you can just replace the .words() by:

.words(json.map(function(d) {
  return {text: d.word, size: d.sentiment * 40, tweet: d.tweet};
}))

Upvotes: 8

lephix
lephix

Reputation: 1174

Can you extract other entries of your data? e.g. words and sentiment?
Maybe you could try it like following code. If it works, you can change it to your codes.

var data; // a global
d3.json("path/to/file.json", function(error, json) {
  if (error) return console.warn(error);
  d3.select("#vis").append("svg")
    .data(json)
    .enter().append("text")
    .text(function(d) {return d.tweets;});
});

Upvotes: 0

Related Questions