1

I'm a newbie at D3. I have this nice example of a candlestick chart that loads its data from a csv file. I got that example to work but now I want to do the same thing except load the data from an ajax call which returns json data. I can't figure out how to do it.

After reading a few comments, here is my second attempt:

function showChart() {
    var margin = {top: 20, right: 20, bottom: 30, left: 50},
        width = $(window).width()*0.6 - margin.left - margin.right,
        height = 500 - margin.top - margin.bottom;
    var parseDate = d3.timeParse("%Y-%m-%d");
    var x = techan.scale.financetime().range([0, width]);
    var y = d3.scaleLinear().range([height, 0]);
    var candlestick = techan.plot.candlestick()
        .xScale(x)
        .yScale(y);
    var xAxis = d3.axisBottom().scale(x);
    var yAxis = d3.axisLeft().scale(y);

    $.ajax("http://www.mycom.net/getQuoteHistory.php?symbol='A'", {
        success: function(data) {
            console.log("getQuoteHistory:data="+JSON.stringify(data));
            var accessor = candlestick.accessor();
            data = JSON.parse(data);

            var newData = [];
            for (var i=0; i<data.length; i++) {
                var o = data[i];
                var newObj = {};
                newObj.date = parseDate(o.Date);
                newObj.open = o.Open;
                newObj.high = o.High;
                newObj.low  = o.Low;
                newObj.close = o.Close;
                newObj.volume = o.Volume;
                newData.push(newObj);
            }
            console.log("getQuoteHistory:newData="+JSON.stringify(newData));
            var svg = d3.select("svg")
                .data(newData)
                .attr("width", width + margin.left + margin.right)
                .attr("height", height + margin.top + margin.bottom)
                .append("g")
                .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

            svg.append("g").attr("class", "candlestick");
            svg.append("g")
                .attr("class", "x axis")
                .attr("transform", "translate(0," + height + ")");
            svg.append("g")
                .attr("class", "y axis")
                .append("text")
                .attr("transform", "rotate(-90)")
                .attr("y", 6)
                .attr("dy", ".71em")
                .style("text-anchor", "end")
                .text("Price ($)");

            // Data to display initially
            draw(newData.slice(0, newData.length-20));
            // Only want this button to be active if the data has loaded
            d3.select("button").on("click", function() { 
                draw(newData); 
            }).style("display", "inline");
        },
        error: function() {
            console.log("something went wrong in ajax call");
        }
    });     
    function draw(data) {
        x.domain(data.map(candlestick.accessor().d));
        y.domain(techan.scale.plot.ohlc(data, candlestick.accessor()).domain());

        svg.selectAll("g.candlestick").datum(data).call(candlestick);
        svg.selectAll("g.x.axis").call(xAxis);
        svg.selectAll("g.y.axis").call(yAxis);
    }
}

The ajax call returns good json array data and it is converted to newData which has the date parsed correctly and the field names in lower case as reqd. Here is a snippet of each:

getQuoteHistory:data="[{\"Symbol\":\"A\",\"Date\":\"2018-06-28\",\"Open\":\"61.13\",\"High\":\"61.64\",\"Low\":\"60.42\",\"Close\":\"61.29\",\"Volume\":\"15641\"},{\"Symbol\":\"A\",\"Date\":\"2018-06-29\",\"Open\":\"61.68\",\"High\":\"62.47\",\"Low\":\"61.57\",\"Close\":\"61.84\",\"Volume\":\"18860\"},

getQuoteHistory:newData=[{"date":"2018-06-28T06:00:00.000Z","open":"61.13","high":"61.64","low":"60.42","close":"61.29","volume":"15641"},{"date":"2018-06-29T06:00:00.000Z","open":"61.68",

Now the failure happens in the draw function on this line:

svg.selectAll("g.candlestick").datum(data).call(candlestick);  

where the chrome javascript console shows "svg is not defined".

But it is defined in the html:

    <svg></svg>

Even if I pass svg as a parameter to draw method, then it says "cannot read property selectAll of undefined".

Any ideas how to get this to work from a json array instead of a csv file?

10
  • 2
    It seems that you are using jquery to get the data. You don't need to! If it is a json file, use d3.json and if it is csv, use d3.csv to fetch the remote data. Commented Nov 4, 2018 at 6:45
  • 2
    Also please show what you see when you log data. The error is probably telling you that data is not an array. Commented Nov 4, 2018 at 6:51
  • make sure you have the same fields in your JSON records otherwise the candlestick.accessor() would not find the data. Commented Nov 4, 2018 at 10:47
  • I had a lowercase "y" instead of uppercase "Y" in timeParse. Also needed to parse the incoming json to javascript with data = JSON.parse(data). Now the problem has changed to the edited version. Commented Nov 4, 2018 at 15:58
  • @jrook: how to do it with d3.json(... ? It requires a file as far as I can tell. I don't have a file, I have a variable. Commented Nov 4, 2018 at 16:13

2 Answers 2

1

You need to reselect your svg in function draw(data), because your variable var svg is a local variable which is only defined within the success function from your ajax call.

Just add:

function draw(data) {
  var svg = d3.select("svg");
  // The rest of your function
}
Sign up to request clarification or add additional context in comments.

2 Comments

I tried your suggestion and it did end that error, but produced a new one "Error: <path> attribute d: Expected number, "…700742432895511 NaN l 10.4671616…"." I am pondering giving you credit for the answer but @rioV8 comment above worries me that it might not be the right answer.
Your svg variable must be defined in the draw(data) function. In your code it is undefined. @rioV8 is also right: you can define it on the parent scope, pass it as an argument, or reselect it in the draw function. But your second error message has to do with your data itself or with the scaling methods because there is a NaN in the error message.
0

Here is the working code. Thanks to all the contributors. I learned from each of you to get this to work.

function showChart(symbol) {
    var margin = {top: 20, right: 20, bottom: 30, left: 50},
        width = $(window).width()*0.6 - margin.left - margin.right,
        height = 500 - margin.top - margin.bottom;
    var parseDate = d3.timeParse("%Y-%m-%d");
    var x = techan.scale.financetime().range([0, width]);
    var y = d3.scaleLinear().range([height, 0]);
    var candlestick = techan.plot.candlestick()
        .xScale(x)
        .yScale(y);
    var xAxis = d3.axisBottom().scale(x);
    var yAxis = d3.axisLeft().scale(y);

    $.ajax("http://www.mycom.net/getQuoteHistory.php?symbol='" +symbol +"'", {
        success: function(data) {
            console.log("getQuoteHistory:data="+JSON.stringify(data));
            var accessor = candlestick.accessor();
            data = JSON.parse(data);
            data = data.slice(0, 200).map(function(d) {
                return {
                    date: parseDate(d.Date),
                    open: +d.Open,
                    high: +d.High,
                    low: +d.Low,
                    close: +d.Close,
                    volume: +d.Volume
                };
            }).sort(function(a, b) { return d3.ascending(accessor.d(a), accessor.d(b)); });

            console.log("getQuoteHistory:newData="+JSON.stringify(data));
            var svg = d3.select("svg")
                .data(data)
                .attr("width", width + margin.left + margin.right)
                .attr("height", height + margin.top + margin.bottom)
                .append("g")
                .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

            svg.append("g").attr("class", "candlestick");
            svg.append("g")
                .attr("class", "x axis")
                .attr("transform", "translate(0," + height + ")");
            svg.append("g")
                .attr("class", "y axis")
                .append("text")
                .attr("transform", "rotate(-90)")
                .attr("y", 6)
                .attr("dy", ".71em")
                .style("text-anchor", "end")
                .text("Price ($)");

            draw(data,svg);
        },
        error: function() {
            console.log("something went wrong in ajax call");
        }
    });

    function draw(data,svg) {
        x.domain(data.map(candlestick.accessor().d));
        y.domain(techan.scale.plot.ohlc(data, candlestick.accessor()).domain());

        svg.selectAll("g.candlestick").datum(data).call(candlestick);
        svg.selectAll("g.x.axis").call(xAxis);
        svg.selectAll("g.y.axis").call(yAxis);
    }
}

There's probably a way to do it with d3.json() and avoid the extra ajax stuff.

Comments

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.