SiddP
SiddP

Reputation: 1663

Incomplete line chart with missing points and missing lines

var maindata=[
{"date":"21-APR-16 04:19 AM","Delhi":30,"Mumbai":28},
{"date":"21-APR-16 05:19 AM","Delhi":32,"Mumbai":30},
{"date":"21-APR-16 06:19 AM","Delhi":34,"Mumbai":34},
{"date":"21-APR-16 10:19 AM","Kolkata":34},
{"date":"21-APR-16 11:19 AM","Delhi":48,"Chennai":40,"Kolkata":36}
];


            var that = this;
            var deepClonedCopy = jQuery.extend(true, {}, maindata);
            var data;
            data = $.map(deepClonedCopy, function(el) { return el });
            //  var passedheight = this.getHeight();
            //var containerWidth = jQuery.sap.byId(this.oParent.sId).width() || 800; // gets super parent width
            var margin = {top: 15, right: 30, bottom: 20, left: 30},
            width = 960- margin.left - margin.right,
             height = 500 - margin.top - margin.bottom;
            if(data.length >= 1){
                //alert(JSON.stringify(data));
            var parseDate = d3.time.format("%d-%b-%y %H:%M %p").parse;
            var maxDate = d3.time.hour.offset(parseDate(d3.max(data, function(d) { return d.date; })),+1);
            var minDate = d3.time.hour.offset(parseDate(d3.min(data, function(d) { return d.date; })),-1);

            var div = d3.select("#toolTip");

            var x = d3.time.scale()
            .domain([minDate, maxDate])
            .range([0, width]);

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

            var color = d3.scale.category10();

            var xAxis = d3.svg.axis()
                .scale(x)
                .orient("bottom").ticks(4);

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

            var line = d3.svg.line()
                .x(function(d) { return x(d.date); })
                .y(function(d) { return y(d.tonneValue); });

            var svg = d3.select("body").append("svg")
                .attr("width", width + margin.left + margin.right)
                .attr("height", height + margin.top + margin.bottom)
              .append("g")
                .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

              color.domain(d3.keys(data[0]).filter(function(key) { return key !== "date" && key !== "maxLength"; }));

              data.forEach(function(d) {
                d.date = parseDate(d.date);
              });

              var wsfs = color.domain().map(function(name) {
                return {
                  name: name,
                  values: data.map(function(d) {
                    return {date: d.date, tonneValue: +d[name]};
                  })
                };
              });

              //x.domain(d3.extent(data, function(d) { return d.date; }));

              y.domain([
                //d3.min(wsfs, function(c) { return d3.min(c.values, function(v) { return v.tonneValue; }); }),
                0,
                Math.ceil(d3.max(wsfs, function(c) { return d3.max(c.values, function(v) { return v.tonneValue; }); }))
              ]);

              svg.append("g")
                  .attr("class", "x axis")
                  .attr("transform", "translate(0," + height + ")")
                  .call(xAxis);

              svg.append("g")
                  .attr("class", "y axis")
                  .call(yAxis);

              var wsf = svg.selectAll(".wsf")
                  .data(wsfs)
                  .enter().append("g")
                  .attr("class", "wsf");

              wsf.append("path")
                  .attr("class", "line")
                  .attr("d", function(d) { return line(d.values); })
                  .style("stroke", function(d) { return color(d.name); });
              wsf.selectAll("dot")                                  
                 .data(function(d) { return d.values;})                                         
                 .enter().append("circle")
                 .attr("class", "dot")
                 .attr("r", 3)  
                 .attr("cx", function(d) { return x(d.date); })      
                 .attr("cy", function(d) { return y(d.tonneValue); })
                 .attr("stroke", function (d) {
                        return color(this.parentNode.__data__.name) })
                 .attr("fill", function (d) {
                        return color(this.parentNode.__data__.name) })
                 //.attr("fill-opacity", .5)
                 //.attr("stroke-width", 2)
                 .on("mouseover", function (d) {
                     formatDate = d3.time.format("%d-%m-%Y %H:%M %p");
                        div.transition().duration(100).style("opacity", .9);
                        div.html(/*this.parentNode.__data__.name + "<br/>" +*/ d.tonneValue /*+ "<br/>" + "<br/>"*/ +" Tonne handled at "+ formatDate(d.date))
                        .style("left", (d3.event.pageX) + "px").style("top", (d3.event.pageY - 28) + "px").attr('r', 8);
                        d3.select(this).attr('r', 8)
                    }).on("mouseout", function (d) {
                        div.transition().duration(600).style("opacity", 0)
                        d3.select(this).attr('r', 3);
                    });
              var legendNames = d3.keys(data[0]).filter(function(key) { return key !== "date" });

              data.forEach(function(d) {
                d.ages = legendNames.map(function(name) { return {name: name, value: +d[name]}; });
              });
             var legend = svg.selectAll(".legend")
                  .data(legendNames.slice())
                .enter().append("g")
                  .attr("class", "legend")
                  .attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });

              legend.append("rect")
                  .attr("x", width - 18)
                  .attr("width", 18)
                  .attr("height", 4)
                  .style("fill", function(d) {return color(d); });

              legend.append("text")
                  .attr("x", width - 24)
                  .attr("y", 6)
                  .attr("dy", ".35em")
                  .style("text-anchor", "end")
                  .text(function(d) {  return d; });
            }

The above code gives 2 lines, until first 3 json elements till the data format is similar.
but from 4th element there is no Delhi / Mumbai and there is just Kolkata, but I don't find a line for Kolkata 34, Kolkata 36 neither Delhi's 48 point, where Delhi's line should extend from 34 to 48 and Chennai a simple dot is expected as there is only one point of Chennai i.e. 40.
Can any one tell where am I doing wrong. Here is a fiddle.

Upvotes: 2

Views: 222

Answers (2)

David Guan
David Guan

Reputation: 4300

Here is the updated fiddle

some keys(i.e. Kolkata)aren't included in binding data

Original code:

          color.domain(d3.keys(data[0]).filter(function(key) { return key !== "date" && key !== "maxLength"; }));

          data.forEach(function(d) {
            d.date = parseDate(d.date);
          });

          var wsfs = color.domain().map(function(name) {
            return {
              name: name,
              values: data.map(function(d) {
                return {date: d.date, tonneValue: +d[name]};
              })
            };
          });

To(you need change ['Delhi', 'Mumbai', 'Kolkata', 'Chennai'] to code more formally):

          var wsfs = ['Delhi', 'Mumbai', 'Kolkata', 'Chennai'].map(function(name) {
            return {
              name: name,
              values: data.map(function(d) {
                return {date: d.date, tonneValue: +d[name]};
              }).filter(function(d) {
            return !isNaN(d.tonneValue)
        })
            };
          });

Filter data
Original code:

          var wsfs = color.domain().map(function(name) {
            return {
              name: name,
              values: data.map(function(d) {
                return {date: d.date, tonneValue: +d[name]};
              })
            };
          });

To:

          var wsfs = ['Delhi', 'Mumbai', 'Kolkata', 'Chennai'].map(function(name) {
            return {
              name: name,
              values: data.map(function(d) {
                return {date: d.date, tonneValue: +d[name]};
              }).filter(function(d) {
                return !isNaN(d.tonneValue)
            })};
          });

Upvotes: 1

Cyril Cherian
Cyril Cherian

Reputation: 32327

The main problem in the code (missing lines and points and incomplete legend) is that your data needs some structuring.

You dataset some times has cities which may or may not be present.

So The first task will be to make a proper data set

From this:

var maindata = [{
  "date": "21-APR-16 04:19 AM",
  "Delhi": 30,
  "Mumbai": 28
}, {
  "date": "21-APR-16 05:19 AM",
  "Delhi": 32,
  "Mumbai": 30
}, {
  "date": "21-APR-16 06:19 AM",
  "Delhi": 34,
  "Mumbai": 34
}, {
  "date": "21-APR-16 10:19 AM",
  "Kolkata": 34
}, {
  "date": "21-APR-16 11:19 AM",
  "Delhi": 48,
  "Chennai": 40,
  "Kolkata": 36
}];

To this:

[
   {
      "name":"Delhi",
      "values":[
         {
            "date":"2016-04-20T22:49:00.000Z",
            "value":30
         },
         {
            "date":"2016-04-20T23:49:00.000Z",
            "value":32
         },
         {
            "date":"2016-04-21T00:49:00.000Z",
            "value":34
         },
         {
            "date":"2016-04-21T05:49:00.000Z",
            "value":48
         }
      ]
   },
   {
      "name":"Mumbai",
      "values":[
         {
            "date":"2016-04-20T22:49:00.000Z",
            "value":28
         },
         {
            "date":"2016-04-20T23:49:00.000Z",
            "value":30
         },
         {
            "date":"2016-04-21T00:49:00.000Z",
            "value":34
         }
      ]
   },
   {
      "name":"Kolkata",
      "values":[
         {
            "date":"2016-04-21T04:49:00.000Z",
            "value":34
         },
         {
            "date":"2016-04-21T05:49:00.000Z",
            "value":36
         }
      ]
   },
   {
      "name":"Chennai",
      "values":[
         {
            "date":"2016-04-21T05:49:00.000Z",
            "value":40
         }
      ]
   }
]

So now all cities have their respective data. Number of cities x so number of lines will be x.

To make the dataset do this:

var cities = []
var parseDate = d3.time.format("%d-%b-%y %H:%M %p").parse;
maindata.forEach(function(d) {
  cities.push(d3.keys(d).filter(function(key) {
      return key !== "date" && key !== "maxLength";
    }));
  });
cities = d3.set(d3.merge(cities)).values();//get all  cites and make it unique
console.log(cities)
var myData = [];
var allValues = [];
var allDates =[];
//generate cities and its values as shown in my dataset above.
cities.forEach(function(city){
    var cityData = {};
    cityData.name = city;
    cityData.values = [];
    maindata.forEach(function(md){
        if (md[city]){
          allValues.push(md[city])
            allDates.push(parseDate(md.date))
            cityData.values.push({date: parseDate(md.date), value: md[city]})
        }
    })
    myData.push(cityData)
});

Next make lines like this:

  var wsf = svg.selectAll(".wsf")
    .data(myData)
    .enter().append("g")
    .attr("class", "wsf");

  wsf.append("path")
    .attr("class", "line")
    .attr("d", function(d) {
      return line(d.values);
    })
    .style("stroke", function(d) {
      return color(d.name);
    });

And Legends like this:

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

  legend.append("rect")
    .attr("x", width - 18)
    .attr("width", 18)
    .attr("height", 4)
    .style("fill", function(d) {
      return color(d);
    });

  legend.append("text")
    .attr("x", width - 24)
    .attr("y", 6)
    .attr("dy", ".35em")
    .style("text-anchor", "end")
    .text(function(d) {
      return d;
    });
}

working code here

Upvotes: 1

Related Questions