Question

I need to add a line chart to my web project. It needs to have date values on the x axis and integers on the y axis. Currently the data displays like so:

enter image description here

I need it to display like in this excel version:

enter image description here

I can't get the data to align with the x axis properly, and I can't get the labels on the x axis to display the proper dates. My webpage (using Razor syntax) is set up like this:

<style>
svg {
display: block;
}

#chart1 svg{
height: 500px;
min-width: 100px;
min-height: 100px;

}
</style>
<script>

var chart;

var data2 = JSON.parse(@Html.Raw(Json.Encode(ViewBag.data)));

var data = data2[0];

nv.addGraph(function() {

  chart = nv.models.lineChart();

  chart
  .x(function(d,i) 
  {     
   return i;
    })


  chart.xAxis 
  .tickFormat(function(d) 
  {      
   return d3.time.format('%b %d')(new Date(d));
    })


  chart.yAxis
  .axisLabel('Voltage (v)')
  .tickFormat(d3.format(',.2f'));

    chart.showXAxis(true);

    d3.select('#chart1 svg')
    //.datum([]) //for testing noData
    .datum(data)
.transition().duration(500)
    .call(chart);

      nv.utils.windowResize(chart.update);


    chart.dispatch.on('stateChange', function(e) { nv.log('New State:', JSON.stringify(e)); });

      return chart;
       });

      </script>

The data is passed in in the correct format. Any ideas?

@ViewBag.name

Was it helpful?

Solution

OK I managed to fix it, but it was a bit of a hack. In the controller I replaced the dates in the JSON data with integer indices. I worked these out by finding the minimum date, setting it to x axis index of 0, the next date to 1 and so on. I then passed a JSON array of the formatted dates to the view from the controller, such that you can use the indices to look up the date. Controller code:

 public ActionResult MileageReport(DateTime startDate, DateTime endDate)
    {
        using (EventsManager manager = new EventsManager())
        {
            IEnumerable<Event> travelEvents = manager.GetEventsWhere(t => t.Eventtype == EventType.Travel.ToString() && t.Createddt > startDate && t.Createddt < endDate);
            Dictionary<DateTime, int> mappedDates = MapDatesToIndices(travelEvents);
            var groupedEvents = travelEvents.GroupBy(e => e.Userid);
            JArray jsonArray = new JArray();
            IColourPicker colourPicker = NinjectFactory.GetNinJectKernel().Get<IColourPicker>();
            int i = 0;

            List<Event> events, tempEvents;
            List<DateTime> days;
            Event travelEvent;

            foreach (IGrouping<string, Event> group in groupedEvents)
            {
                //we only want the last event for each day
                events = group.ToList();

                days = events.Select(e => e.Createddt.Value.Date).Distinct().OrderBy(o => o).ToList();

                dynamic tEvent = new ExpandoObject();
                tEvent.key = group.Key;
                tEvent.color = colourPicker.GetColour(i);

                JArray tEvents = new JArray();
                foreach (DateTime day in days)
                {
                    tempEvents = events.Where(e => e.Createddt.Value.Date == day).ToList();
                    travelEvent = tempEvents.Single(e => e.Mileage.Value == tempEvents.Select(m => m.Mileage.Value).Max());
                    object tvObj = new { x = mappedDates[travelEvent.Createddt.Value.Date], y = travelEvent.Mileage };

                    tEvents.Add(JObject.FromObject(tvObj));
                }
                tEvent.values = tEvents;
                jsonArray.Add(JObject.FromObject(tEvent));
                i++;
            }




            string jsonStringTemp = jsonArray.ToString();

            string jsonString = FormatJsonString(jsonStringTemp);
            ViewBag.xaxislabels = FormatDatesForIndices(mappedDates);
            ViewBag.data = jsonString;
            ViewBag.name = "Mileage Report";
            return PartialView("LineGraph");


        }
    }

    private dynamic FormatDatesForIndices(Dictionary<DateTime, int> mappedDates)
    {
        List<DateTime> dates = mappedDates.Keys.ToList();

        object[] items = new object[dates.Count];


        for (int i = 0; i < dates.Count; i++)
        {
            items[i] = new { date = dates[i].ToString(BaseFormats.DateDisplayFormatNoYear) };
        }

        JavaScriptSerializer js = new JavaScriptSerializer();
        string jsonString = js.Serialize(items);


        return jsonString;
    }

    private Dictionary<DateTime, int> MapDatesToIndices(IEnumerable<Event> travelEvents)
    {
        Dictionary<DateTime, int> mappedDates = new Dictionary<DateTime, int>();

        List<DateTime?> dts = travelEvents.Select(t => t.Createddt).ToList();

        dts = dts.Where(d => d != null).ToList();

        DateTime min = dts.Min().Value.Date;
        DateTime max = dts.Max().Value.Date;

        int i = 0;

        mappedDates.Add(min, i);

        min = min.AddDays(1);

        while (min.Date <= max.Date)
        {
            ++i;
            mappedDates.Add(min, i);
            min = min.AddDays(1);
        }

        return mappedDates;
    }

Then in the view I overrode the x function to return the x value I put into the JSON data. I also overrode the tickformat to return the formatted dates I passed to the view. View code:

<style>
 svg {
 display: block;
 }

 #chart1 svg{
  height: 500px;
  min-width: 100px;
  min-height: 100px;

}
</style>
<script>

var chart;

var data2 = JSON.parse(@Html.Raw(Json.Encode(ViewBag.data)));

var data = data2[0];

var m_xaxisLabels = JSON.parse(@Html.Raw(Json.Encode(ViewBag.xaxislabels)));

nv.addGraph(function() {

 chart = nv.models.lineChart();


 chart
  .x(function(d,i) 
  {     
   return d.x;
    })

   chart.xAxis.tickFormat(function(d, i){
     return m_xaxisLabels[d].date;
   });

  chart.yAxis
  .axisLabel('Voltage (v)')
  .tickFormat(d3.format(',.2f'));

  chart.showXAxis(true);

  d3.select('#chart1 svg')
  .datum(data)
.transition().duration(500)
  .call(chart);

  nv.utils.windowResize(chart.update);

 chart.dispatch.on('stateChange', function(e) { nv.log('New State:', JSON.stringify(e)); });

 return chart;
});

</script>


<h2>@ViewBag.name</h2>
<div id="chart1">
<svg></svg>
</div>

Now it works a treat!:

enter image description here

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top