Importing JSON Data into D3

I just wanted to write a quick post showing a relatively simple example of importing and visualizing data stored in JSON format. I’m using shot location data from a basketball game between the Golden State Warriors and Denver Nuggets that took place recently. I wrote a Ruby scraper which pulls the shot data from ESPN (using the Nokogiri gem), and simply pipes it to a JSON data file (using the json gem).

The JSON data looks like…

[{"id":"4004005102","player_name":"Andre  Iguodala","desc":"Made 17ft jumper 11:37 in 1st Qtr.","player_id":"2386","quarter":"1","x":"42","y":"86","made":"true","home":"h"},{"id":"4004005103","player_name":"Stephen Curry","desc":"Miss 22ft jumper 11:19 in 1st Qtr.","player_id":"3975","quarter":"1","x":"37","y":"25","made":"false","home":"a"},...

Result

The "h" and "a" stand for home and away, respectively. Made shots are colored blue and missed shots are red. If you hover over a shot, a tooltip will give you some more detail about the shooter and time of the game.

Method

First define the svg drawing area:

var screenHeight = 400,
        screenWidth = 800,
        vis = d3.select("#chart").append("svg")
    .attr("width", screenWidth)
    .attr("height", screenHeight),
    padding = 20;

Next, call the function to import the data and set up a couple of scales to transform the raw (x,y) coordinates (given in ft) to screen coordinates:


d3.json("shots.json", function(shots) {
        var scaleScreenX = d3.scale.linear()
                                .domain([0,100])
                                .range([padding,screenWidth-padding]),
                scaleScreenY = d3.scale.linear()
                                .domain([0,50])
                                .range([padding,screenHeight-padding]);

Draw a filled circle for each shot by binding the data to svg elements:


        vis.selectAll("circle")
                .data(shots)
                .enter()
                .append("circle")
                .attr("cx", function(d) {
                return scaleScreenY(d.y)+5*(Math.random()-0.5);
                        })
                .attr("cy", function(d) {
                return scaleScreenX(d.x)+5*(Math.random()-0.5);
                        })
                .attr("r", function() {return 2*Math.random()+6;})
                .attr("fill", function(d) { return d.desc.match(/Made/) ? "steelblue" : "red" })
                .attr("opacity",0.60)
                .attr("stroke-width", 2)
                .attr("stroke","black")
                .append("svg:title")
                .text(function(d) { return d.player_name+" "+d.desc; });

And do the same thing for adding the text labels to each shot:


        vis.selectAll("text")
                .data(shots)
                .enter()
                .append("text")
                .text(function(d) {
                 return d.home;
                         })
                .attr("x", function(d) {
                        return scaleScreenY(d.y)-2;
                   })
            .attr("y", function(d) {
                        return scaleScreenX(d.x) + 3;
                        })
                .attr("font-family", "serif")
                .attr("font-size", "12px")
                .attr("fill", "black");
});

That’s it, really. In the future, I need to overlay the shots onto the background of an actual basketball court. Also, I’d like to add some filters for selecting games, players, teams, seasons, etc, and maybe add some whiz-bang D3 transitions in there as well.

Posted on by
Tagged :

Categories :data joinsjsonUncategorized

Post comment as twitter logo facebook logo
Sort: Newest | Oldest
5 pts

Thanks for posting this tutorial. Total newbie to d3 so I have a very basic question: if we save this script as user/d3/index.html, where do you save shots.json? I've been trying to edit code from the d3.org library and running into problems rendering any script that references data that I've saved to the same local directory as the index.html file. Thoughts??