SLFl
SLFl

Reputation: 313

Handle with d3.json

I'm trying to make a d3 scatterplot recurring to json data. I know that d3 has d3.json to load json data but my code isn't working. I'm not that good dealing with js (it's my first time with this language), that's why I need help in this. Basically, I need to plot this data (date in xAxis and IADP_mGycm2 in yAxis):

[
    {
        "imageID": 1,
        "value": 288.3,
        "date": "2015-10-22"
    },
    {
        "imageID": 2,
        "value": 188.1,
        "date": "2015-10-22"
    }
]

JS:

var margin = { top: 50, right: 300, bottom: 50, left: 50 },
outerWidth = 1050,
outerHeight = 500,
width = outerWidth - margin.left - margin.right,
height = outerHeight - margin.top - margin.bottom;


var x = d3.scale.linear()
    .range([0, width]).nice();

var y = d3.scale.linear()
    .range([height, 0]).nice();

var xCat = "date",
    yCat = "value";


d3.json("CR.json", function(error, rawData) {
    if (error) {
        console.error(error);
        return;
    }

    var data = rawData.map(function (d) {
        return {
            date: d.date,
            value: d.value

        }
    });


    var xMax = d3.max(data, function(d) { return d["date"]; }),
        xMin = d3.min(data, function(d) { return d["date"]; }),
        xMin = xMin > 0 ? 0 : xMin,
        yMax = d3.max(data, function(d) { return d["value"]; }) ,
        yMin = d3.min(data, function(d) { return d["value"]; }),
        yMin = yMin > 0 ? 0 : yMin;

    x.domain([xMin, xMax]);
    y.domain([yMin, yMax]);

    var xAxis = d3.svg.axis()
      .scale(x)
      .orient("bottom")
      .tickSize(-height);

    var yAxis = d3.svg.axis()
      .scale(y)
      .orient("left")
      .tickSize(-width);

    var color = d3.scale.category10();

    var tip = d3.tip()
      .attr("class", "d3-tip")
      .offset([-10, 0])
      .html(function(d) {
        return xCat + ": " + d["date"] + "<br>" + yCat + ": " + d["value"];
      });

    var zoomBeh = d3.behavior.zoom()
      .x(x)
      .y(y)
      .scaleExtent([0, 500])
      .on("zoom", zoom);

    var svg = d3.select("#scatter")
        .append("svg")
            .attr("width", outerWidth)
            .attr("height", outerHeight)
        .append("g")
            .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
            .call(zoomBeh);

    svg.call(tip);

    svg.append("rect")
      .attr("width", width)
      .attr("height", height);

    svg.append("g")
        .classed("x axis", true)
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis)
    .append("text")
        .classed("label", true)
        .attr("x", width)
        .attr("y", margin.bottom - 10)
        .style("text-anchor", "end")
        .text(xCat);

    svg.append("g")
        .classed("y axis", true)
        .call(yAxis)
    .append("text")
        .classed("label", true)
        .attr("transform", "rotate(-90)")
        .attr("y", -margin.left)
        .attr("dy", ".71em")
        .style("text-anchor", "end")
        .text(yCat);

    var objects = svg.append("svg")
      .classed("objects", true)
      .attr("width", width)
      .attr("height", height);

    objects.append("svg:line")
      .classed("axisLine hAxisLine", true)
      .attr("x1", 0)
      .attr("y1", 0)
      .attr("x2", width)
      .attr("y2", 0)
      .attr("transform", "translate(0," + height + ")");

    objects.append("svg:line")
      .classed("axisLine vAxisLine", true)
      .attr("x1", 0)
      .attr("y1", 0)
      .attr("x2", 0)
      .attr("y2", height);

    objects.selectAll(".dot")
        .data(data)
    .enter().append("circle")
      .classed("dot", true)
      .attr("cy", function (d) { return d.value; })
      .attr("cx", function (d) { return d.date; })
      .attr("transform", transform)
      .style("fill", "red")
      .on("mouseover", tip.show)
      .on("mouseout", tip.hide);

    var legend = svg.selectAll(".legend")
      .data(color.domain())
    .enter().append("g")
      .classed("legend", true)
      .attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });

    legend.append("circle")
      .attr("r", 3.5)
      .attr("cx", width + 20)
      .attr("fill", color);

    legend.append("text")
      .attr("x", width + 26)
      .attr("dy", ".35em")
      .text(function(d) { return d; });

    d3.select("input").on("click", change);

    function change() {
        xCat = "date";
        xMax = d3.max(data, function(d) { return d["date"]; });
        xMin = d3.min(data, function(d) { return d["date"]; });

        zoomBeh.x(x.domain([xMin, xMax])).y(y.domain([yMin, yMax]));

        var svg = d3.select("#scatter").transition();

        svg.select(".x.axis").duration(750).call(xAxis).select(".label").text("date");

        objects.selectAll(".dot").transition().duration(1000).attr("transform", transform);
    }

    function zoom() {
        svg.select(".x.axis").call(xAxis);
        svg.select(".y.axis").call(yAxis);

        svg.selectAll(".dot")
            .attr("transform", transform);
    }

    function transform(d) {
        return "translate(" + x(d["date"]) + "," + y(d["value"]) + ")";
    }

});

HTML:

<html>
  <head>
    <meta charset="utf-8">
    <title>Visualization</title>
    <link rel="stylesheet" href="scatter.css" charset="utf-8">
  </head>
  <body>
    <div id="scatter"></div>

    <input type="button" name="xAxis" value="xAxis">

    <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
    <script src="http://labratrevenge.com/d3-tip/javascripts/d3.tip.v0.6.3.js"></script>
    <script src="scatter.js" charset="utf-8"></script>
  </body>
</html>

CSS:

rect {
  fill: transparent;
  shape-rendering: crispEdges;
}

.axis path,
.axis line {
  fill: none;
  stroke: rgba(0, 0, 0, 0.1);
  shape-rendering: crispEdges;
}

.axisLine {
  fill: none;
  shape-rendering: crispEdges;
  stroke: rgba(0, 0, 0, 0.5);
  stroke-width: 2px;
}

.dot {
  fill-opacity: .5;
}

.d3-tip {
  line-height: 1;
  font-weight: bold;
  padding: 12px;
  background: rgba(0, 0, 0, 0.8);
  color: #fff;
  border-radius: 2px;
}

/* Creates a small triangle extender for the tooltip */
.d3-tip:after {
  box-sizing: border-box;
  display: inline;
  font-size: 10px;
  width: 100%;
  line-height: 1;
  color: rgba(0, 0, 0, 0.8);
  content: "\25BC";
  position: absolute;
  text-align: center;
}

/* Style northward tooltips differently */
.d3-tip.n:after {
  margin: -1px 0 0 0;
  top: 100%;
  left: 0;
}

Thanks in advance!

Upvotes: 1

Views: 579

Answers (3)

Jeff Kilbride
Jeff Kilbride

Reputation: 2824

Ok, after looking a little further at this, I've identified a couple of problems. The main issue is that the chart you are trying to emulate has numeric values on both the x and y axes. However, you are trying to use dates. In order to do that, you have to use d3.time.scale() for the x-axis instead of a linear scale. You also have to transform the date strings from your data to date objects and use your time scale to scale your x-axis values. Here is scatter.js with the changes:

var margin      = { top: 50, right: 300, bottom: 50, left: 50 },
    outerWidth  = 1050,
    outerHeight = 500,
    width       = outerWidth - margin.left - margin.right,
    height      = outerHeight - margin.top - margin.bottom;

// Use a time scale for the x-axis
var x = d3.time.scale()
          .range([0, width]).nice();

var y = d3.scale.linear()
          .range([height, 0]).nice();

var xCat = "date",
    yCat = "Dose";


d3.json("CR.json", function(error, rawData) {
    if (error) {
        console.error(error);
        return;
    }

    var data = rawData.map(function(d) {
        return {
            // Create date objects, not strings
            date:        new Date(d.date),
            IADP_mGycm2: d.IADP_mGycm2
        }
    });


    var xMax = d3.max(data, function(d) { return d["date"]; }),
        xMin = d3.min(data, function(d) { return d["date"]; }),
        //xMin = xMin > 0 ? 0 : xMin,
        yMax = d3.max(data, function(d) { return d["IADP_mGycm2"]; }),
        yMin = d3.min(data, function(d) { return d["IADP_mGycm2"]; });
        //yMin = yMin > 0 ? 0 : yMin;

    x.domain([xMin, xMax]);
    y.domain([yMin, yMax]);

    var xAxis = d3.svg.axis()
                  .scale(x)
                  .orient("bottom")
                  .tickSize(-height);

    var yAxis = d3.svg.axis()
                  .scale(y)
                  .orient("left")
                  .tickSize(-width);

    var color = d3.scale.category10();

    var tip = d3.tip()
                .attr("class", "d3-tip")
                .offset([-10, 0])
                .html(function(d) {
                    return xCat + ": " + d["date"] + "<br>" + yCat + ": " + d["IADP_mGycm2"];
                });

    var zoomBeh = d3.behavior.zoom()
                    .x(x)
                    .y(y)
                    .scaleExtent([0, 500])
                    .on("zoom", zoom);

    var svg = d3.select("#scatter")
                .append("svg")
                .attr("width", outerWidth)
                .attr("height", outerHeight)
                .append("g")
                .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
                .call(zoomBeh);

    svg.call(tip);

    svg.append("rect")
       .attr("width", width)
       .attr("height", height);

    svg.append("g")
       .classed("x axis", true)
       .attr("transform", "translate(0," + height + ")")
       .call(xAxis)
       .append("text")
       .classed("label", true)
       .attr("x", width)
       .attr("y", margin.bottom - 10)
       .style("text-anchor", "end")
       .text(xCat);

    svg.append("g")
       .classed("y axis", true)
       .call(yAxis)
       .append("text")
       .classed("label", true)
       .attr("transform", "rotate(-90)")
       .attr("y", -margin.left)
       .attr("dy", ".71em")
       .style("text-anchor", "end")
       .text(yCat);

    var objects = svg.append("svg")
                     .classed("objects", true)
                     .attr("width", width)
                     .attr("height", height);

    objects.append("svg:line")
           .classed("axisLine hAxisLine", true)
           .attr("x1", 0)
           .attr("y1", 0)
           .attr("x2", width)
           .attr("y2", 0)
           .attr("transform", "translate(0," + height + ")");

    objects.append("svg:line")
           .classed("axisLine vAxisLine", true)
           .attr("x1", 0)
           .attr("y1", 0)
           .attr("x2", 0)
           .attr("y2", height);

    objects.selectAll(".dot")
           .data(data)
           .enter().append("circle")
           .classed("dot", true)
           .attr("cy", function(d) { return d.IADP_mGycm2; })
           // Use the time scale to scale the values for the x-axis
           .attr("cx", function(d) { return x(d.date); })
           .attr("transform", transform)
           .style("fill", "red")
           .on("mouseover", tip.show)
           .on("mouseout", tip.hide);

    var legend = svg.selectAll(".legend")
                    .data(color.domain())
                    .enter().append("g")
                    .classed("legend", true)
                    .attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });

    legend.append("circle")
          .attr("r", 3.5)
          .attr("cx", width + 20)
          .attr("fill", color);

    legend.append("text")
          .attr("x", width + 26)
          .attr("dy", ".35em")
          .text(function(d) { return d; });

    d3.select("input").on("click", change);

    function change() {
        xCat = "date";
        xMax = d3.max(data, function(d) { return d["date"]; });
        xMin = d3.min(data, function(d) { return d["date"]; });

        zoomBeh.x(x.domain([xMin, xMax])).y(y.domain([yMin, yMax]));

        var svg = d3.select("#scatter").transition();

        svg.select(".x.axis").duration(750).call(xAxis).select(".label").text("date");

        objects.selectAll(".dot").transition().duration(1000).attr("transform", transform);
    }

    function zoom() {
        svg.select(".x.axis").call(xAxis);
        svg.select(".y.axis").call(yAxis);

        svg.selectAll(".dot")
           .attr("transform", transform);
    }

    function transform(d) {
        return "translate(" + x(d["date"]) + "," + y(d["IADP_mGycm2"]) + ")";
    }
});

This gets rid of the errors you were seeing, but it still doesn't plot the circles correctly. Sorry, I don't have the time to work all that out. However, this should move you forward and get you closer to figuring it out yourself. To learn more about time scales, see https://github.com/mbostock/d3/wiki/Time-Scales. Also, if you really want to learn D3, I highly recommend the book D3.js in Action by Elijah Meeks. https://www.manning.com/books/d3-js-in-action. One of the better programming books I have read.

Upvotes: 0

Jeff Kilbride
Jeff Kilbride

Reputation: 2824

Well, you define xCat as:

var xCat = "Date"

but your mapping function uses:

date: d.date

so, when you reference d[xCat] what you are getting is d.Date (which is undefined and would cause NaN) instead of d.date. That's all I can see with a quick look through.

You can fix this by using d['date'] or d.date instead of d[xCat].

Upvotes: 1

adilapapaya
adilapapaya

Reputation: 4795

One issue I see is that you're missing a }) at the end of this code chunk:

var data = rawData.map(function (d) {
    return {
        date: d.date,
        IADP_mGycm2: d.IADP_mGycm2

    };

Try changing it to this:

 var data = rawData.map(function (d) {
    return {
             date: d.date,
             IADP_mGycm2: d.IADP_mGycm2
           }
    });

It also helps in debugging if you include the specific error message you're getting.

Upvotes: 1

Related Questions