2

I want to create a "Timeline" type rowchart using dc.js. I am able to produce this chart in d3, but cannot replicate in dc.

The rowchart will have have a timeline x-axis and the bars will start at a point in time on the axis and end at another point in time on the axis.

The D3 version looks like this

Each row of data has an ID, a startDate and an endDate:

{
"id": 31,
"startDate": "2016-9-22",
"endDate": "2019-1-15"
},
{
"id": 29,
"startDate": "2016-9-21",
"endDate": "2016-9-28"
}

To achieve this in D3 I set up a scale:

let xScale = d3.scaleTime()
            .domain([d3.min(data, function(d){ return new Date(d.startDate)}), d3.max(data, function(d){ return new Date(d.endDate)})])
            .range([0, 500]); 

Then I append rects, setting the x and width attributes using the xScale:

svg.selectAll('rect')
          .data(data)
          .enter()
          .append('rect')
          .attr('x', function(d){ return xScale(new Date(d.startDate))})
          .attr('width', function(d) { return xScale(new Date(d.startDate)) - xScale(new Date(d.endDate))})

In dc.js I have gotten part way, but there is some behaviour that I do not understand

I define the crossfilter, dimension and xScale:

let ndx = crossfilter(data);
let idDim = ndx.dimension(function(d){ return d.id});
let xScale = d3.scaleTime()
            .domain([d3.min(data, function(d){ return new Date(d.dateCreate)}), d3.max(data, function(d){ return new Date(d.dateCompleted)})])
            .range([0, 500])

Then I create a group:

let idGroup = idDim.group().reduceSum(function(d){ return new Date(d.dateCompleted)});

(For this example I am only trying to get the width of the bar to extend from the 0 point to the dateCompleted, i will worry about setting the x attribute later)

And I create the chart like this:

idChart
            .dimension(idDim)
            .group(idGroup)
            .height(500)
            .width(500)
            .x(xScale)
dc.renderAll()

The result is that the bars appear, and are roughly in the right spot, except that they are much longer than they should be and are translated by -9746 pixels.

dc.js version looks like this

I assume that I am not parsing dates correctly, does anyone have any ideas where I am going wrong? How do I stop this translation? Like I said, I think that once I have this working, setting the x attribute should be easy.

abd
  • 75
  • 6
  • The row chart always starts at zero, so I think you will have a tough time turning it into a timeline. It's [not all that hard to link external charts](https://stackoverflow.com/questions/25336528/dc-js-listening-for-chart-group-render) to dc.js. – Gordon Jan 23 '19 at 11:40
  • Thanks Gordon, I got a bit lost with connecting the charts the "right" way, and instead, I fire a function that updates the d3 chart from a dc.js .on('renderlet') function. It is unorthodox but effective. I can post a detailed question about how I would connect the charts using dc.register if you think it will be useful for people. – abd Feb 22 '19 at 04:35
  • Sure, thanks, that would be great! Maybe something we could also turn into [an example](https://dc-js.github.io/dc.js/examples/)? – Gordon Feb 22 '19 at 12:02

1 Answers1

1

I found out that "cleaning" and normalising the data before feeding it to crossfilter works better, makes it easier to understand and way faster.

eg instead of doing new Date(d.xxx) everytime you need the date, start your code with looping through each of your data and transform it once for all Also, I use d3 to transform the date as string to js Date

var day = d3.time.format("%Y-%m-%d");
data.forEach(function(d){
{ 
  d.id = +d.id; //convert the id to an int, json is string only
  d.startDate = day(d.startDate)
  d.endDate = day(d.endDate)
})
Xavier
  • 1,157
  • 9
  • 29
  • Thanks Xavier, I have implemented this now. Although it doesn't help with making a floating dc row chart it certainly makes my code a lot saner. – abd Feb 22 '19 at 04:28