3
votes

I have created a line chart using dc.js and crossfilter. The chart currently looks like this:

line chart

Required: I want the legend of active inactive on top left to position below(bottom) of the chart in center and the x-axis tick label should start from Jan to Dec. I am adding my code below. Thank you.

const dateNames = ["January", "February", "March", "April", "May", "June",
  "July", "August", "September", "October", "November", "December"
];


var data = [
    {date: "2011-11-14T16:17:54Z", quantity: 2, active: 1000, inactive: 100, type: "tab", city: " "},
    {date: "2011-11-14T16:20:19Z", quantity: 2, active: 190, inactive: 100, type: "tab", city: "Berlin"},
    {date: "2011-11-14T16:28:54Z", quantity: 1, active: 300, inactive: 200, type: "visa", city: " "},
    {date: "2011-11-14T16:30:43Z", quantity: 2, active: 90, inactive: 0, type: "tab", city: "Amsterdam"},
    {date: "2011-11-14T16:48:46Z", quantity: 2, active: 90, inactive: 0, type: "tab", city: " "},
    {date: "2011-11-14T16:53:41Z", quantity: 2, active: 90, inactive: 0, type: "tab", city: " "},
    {date: "2011-11-14T16:54:06Z", quantity: 1, active: 100, inactive: 0, type: "cash", city: " "},
    {date: "2011-11-14T16:58:03Z", quantity: 2, active: 90, inactive: 0, type: "tab", city: " "},
    {date: "2011-11-14T17:07:21Z", quantity: 2, active: 90, inactive: 0, type: "tab", city: " "},
    {date: "2011-11-14T17:22:59Z", quantity: 2, active: 90, inactive: 0, type: "tab", city: " "},
    {date: "2011-11-14T17:25:45Z", quantity: 2, active: 200, inactive: 0, type: "cash", city: " "},
    {date: "2011-11-14T17:29:52Z", quantity: 1, active: 200, inactive: 100, type: "visa", city: ""}
  ];

  data.forEach(function(d){
    var tempDate = new Date(d.date);
    d.date = tempDate;
  
  })
  
  var facts = crossfilter(data);
  var all = facts.groupAll();
  
  
  //table
  var dateDimension = facts.dimension(function(d){ return d.date; });
  //line chart
  
  var dateGroup = dateDimension.group().reduceSum(function(d){ return d.active; });
  var dateGroupTip = dateDimension.group().reduceSum(function(d){ return d.inactive; });
  
  var minDate = dateDimension.bottom(1)[0].date;
  var maxDate = dateDimension.top(1)[0].date;

  var lineChart = dc.lineChart("#test")
    .width(700)
    .height(200)
    .brushOn(false)
    .margins({top:10,bottom:30,right:10,left:70})
    .dimension(dateDimension)
    .group(dateGroupTip,"inactive")
    .stack(dateGroup,"active")

    .renderHorizontalGridLines(true)
    .renderArea(true)
    .renderDataPoints(true)
    // // .interpolate('basis')
    // lineChart.xAxis().ticks(d3.timeMonth, 1)
    // lineChart.xAxis().tickFormat(d3.timeFormat('%b'))
    .clipPadding(data.length)
    .legend(dc.legend().y(10).x(0).itemHeight(12).gap(5))
    // .xAxis()
    // .ticks(d3.time.month, 7)
    // .tickFormat(d3.time.format('%e'));
    // .xAxis().tickFormat(d3.time. format('%B'))
    // .xUnits(d3.time.months)
    .x(d3.scaleLinear().domain([minDate,maxDate]))
    lineChart.yAxis().ticks(6);
    lineChart.xAxis().ticks(12);

dc.renderAll();
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <link rel="stylesheet" type="text/css" href="dc.css"/>

    <script src="d3.js"></script>
    <script src="crossfilter.js"></script>
    <script src="dc.js"></script>
</head>
<body>
    <div id="test"></div>
    <!-- <script src="app.js"></script> -->
    <script src="play.js"></script>
</body>
</html>
1
Just a tip: "not able to create" is not very descriptive - it's better to spell out the expected and observed behavior.Gordon
@Gordon Ok and could you help me with thisTales
Oh, I see... it's a broad question because you have made a whole lot of guesses about how things might work. It doesn't look like you have even started to debug this. Could I ask you to please look at the annotated source for the main dc.js stock example - the monthly move and volume chart uses a time scale, which I think is what you want. (The right axis example is a simpler example.) Then, once you've looked at time scales, please edit your question. Thanks!Gordon
@Gordon I have edited my question above. Please help me with the solution Thank you.Tales
You've made a lot of progress, congrats! I'll take a look soon.Gordon

1 Answers

1
votes

Looks like you've figured most of it out, and got confused by the D3 version 3 to version 4 API changes?

Many of the lines you have commented out were correct except for these changes - stuff like d3.time.months changing to d3.timeMonths. You're not the only one - these changes caused a lot of confusion for all of us.

The last steps are

  1. Use time scales: .x(d3.scaleTime().domain([minDate,maxDate]))
  2. Round the dates down to the beginning of the month using d3.timeMonth, both in the dimension definition and in the minDate/maxDate calculation, e.g. var dateDimension = facts.dimension(function(d){ return d3.timeMonth(d.date); });
  3. Tell the chart how to calculate the number of points to show: .xUnits(d3.timeMonths)
  4. Format the x axis ticks: lineChart.xAxis().tickFormat(d3.timeFormat('%B'))

You had most of these, only commented out. Probably because you found examples using the D3v3 API, and they caused errors?

As for the legend, dc.js doesn't do anything sophisticated here - you just have to lay it out manually, setting the margins on the chart to allow enough space, and setting x and y on the legend to put the legend where you want it.

I found that

lineChart
    .margins({top:10,bottom:60,right:25,left:40});
    .legend(dc.legend().y(165).x(325).itemHeight(12).gap(5))

worked pretty well, but you'll have to adjust it to taste (and, unfortunately, when changes to your data cause the sizes of things to change).

screenshot with time scale and bottom legend

Here's a working fiddle.. I took the liberty of changing your example data so that it includes the desired months.

You're going to run into trouble with this design if your data spans multiple years, but I guess you can cross that bridge when you come to it.