0

I'm having some trouble loading JSON-data from an external API into a D3-graph. I'm using Quandl's API to load brent oil price-rates over a period of time, which is the data I'm trying to graph. I'm making use of Angular in my project, so I have a service set up to make the AJAX-call and then provide it to the scope. This I can do successfully as long as I don't try to involve my D3-code.

When I do, I get several errors:

GET http://localhost:3000/[object%20Object] 404 (Not Found)

and this:

Uncaught TypeError: Cannot read property 'forEach' of undefined

which is tied to some error in my d3-code I cannot well understand.

Here is the full code producing the errors:

app.service('oilService', function($http) {
  delete $http.defaults.headers.common['X-Requested-With'];
  this.getData = function() {
  var URL = "https://www.quandl.com/api/v1/datasets/CHRIS/ICE_B1.json";
  return $http({
    method: 'GET',
    url: URL
  })
}})

.directive('oilGraph', function() {
  return {
   scope: {},
   controller: function(oilService) {
    var width = 200;
  var height = 100;

  var parseTime = d3.timeParse("%y-%m-%d");

  var x = d3.scaleTime().range([0, width]);
  var y = d3.scaleLinear().range([height, 0]);

  var valueline = d3.line()
      .x(function(d) { return x(d.date); })
      .y(function(d) { return y(d.settle); });
  var svg = d3.select("#oilgraph").append("svg")
        .attr("width", width)
        .attr("height", height);

  oilService.getData()
  .then(function(data) {
    d3.json(data, function(error, data) {
      if (error) {
        console.log(error);
      }

      data.forEach(function(d) {
        d.date = parseTime(d[0]);
        d.settle = d[4];
      })

      x.domain(d3.extent(data, function(d) { return d.date; }));
      y.domain([0, d3.max(data, function(d) { return d.settle; })]);

      svg.append("path")
          .data([data])
          .attr("class", "line")
          .attr("d", valueline);

      svg.append("g")
          .attr("transform", "translate(0," + height + ")")
          .call(d3.axisBottom(x));

      svg.append("g")
          .call(d3.axisLeft(y));
    })
   })
  }
 }
})

(nevermind the curly brackets-mess)

11
  • 1
    The second error is because data in data.forEach(function(d) {... is undefined - due to the first error. Could you log the result of oilService.getData() by putting a console.log here: .then(function(data) { console.log("data:", data)? Could you also post what the result of console.log(error); is? (I am guessing it is the first error but just in case) Commented Jun 29, 2017 at 13:36
  • 1
    $http most probably deserializes the JSON data (that would be the default), so d3.json already gets an object, not a string Commented Jun 29, 2017 at 13:41
  • 1
    @ccprog that's definitely one of the issues, but what I cannot understand at this point is why the localhost part in the first error: http://localhost:3000/[object%20Object] . I mean this is not constructed anywhere in the code and d3.json takes as input the data from oilService.getData() so something there must be fishy.. Commented Jun 29, 2017 at 13:45
  • 1
    cf. the signature: d3.json(url[, callback]). Your data object is interpreted as a file system uri. Commented Jun 29, 2017 at 13:51
  • 1
    @ccprog indeed! So just remove the d3.json and load directly the data Commented Jun 29, 2017 at 13:55

1 Answer 1

2

Angular $http allready deserializes the data. Either do not call d3.json, or let it handle the request and do not call $http.

The data received from https://www.quandl.com/api/v1/datasets/CHRIS/ICE_B1.json follow another format than you are expecting. They look like this:

{
  //...
  "column_names": [ "Date","Open","High","Low","Settle","Change","Wave","Volume","Prev. Day Open Interest","EFP Volume","EFS Volume","Block Volume" ],
  "data": [
     ["2017-06-28",46.25,47.47,46.25,47.31,0.66,46.85,158984.0,149270.0,2218.0,null,1379.0],
     //...
}

To get at what you want you need to reference

oilService.getData()
  .then(function(response) {
    var mapped_data = response.data.data.map(function(d) {
        return {
           date: parseTime(d[0]),
           settle: d[4]
        };
     })

    //...

    svg.append("path")
       .data([mapped_data])
       //etc.
  })
Sign up to request clarification or add additional context in comments.

3 Comments

data.data.map is not a function
I could reference the data just fine if I write it like this data.data.data.forEach(function(d) { .... }) This also works for: data.data.data.map My only problem now is the graph is not showing anything except a vertical y-axis line.
ah - Angular $http also wraps the JSON object in a .data property. To avoid naming that is too silly, maybe the callback parameter should be called response, as the Angular doc does.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.