Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pan/zoom ordinal scale?

I am using d3 to render a simplified gantt chart, with panning and zooming using d3.behavior.zoom.

The x scale is a time scale (slightly modified to center calendar days in columns, etc) and works beatifully, but I'm having problems deciding how to zoom/pan the y scale, whose domain is a list of tasks which will often be too many to fit in the chart area, so the need for panning/zooming.

Is there a way to tell the default ordinal scale to react to zoom/pan events, or should I write a custom scale? And if I need to write a custom scale, would it be better to base it on d3.scale.ordinal (having it store the whole list of tasks, and use only the visible subset as its domain), or on d3.scale.linear (and then implement something similar to the ordinal scale for rangebands etc?).

Or is there something I'm missing (entirely probable as it's my first project using d3)?

like image 705
ludoo Avatar asked Nov 12 '12 10:11

ludoo


1 Answers

Expanding slightly from my previous answer as I have been contacted privately to explain how I did this.

First, a screenshot of the app in question, whos emain job is aggregating and displaying planning data gathered from various sources (PowerPoint files, corporate databases, etc.).

screenshot

The relevant bit is the right vertical axis with the colored dots, where each dot represents a project's effort and involved organization. The gray area on the axis is a d3.js brush, and can be panned/resized to change the chart/table data in real time.

// the axis is a regular ordinal axis, with a brush
y2 = d3.scale.ordinal(),
brush = d3.svg.brush().y(y2).on('brush', brushReact),
// [...]
// brush event handler
function brushReact() {
    if (tasksSlice == null)
        return;
    var yrb = y2.rangeBand(),
        extent = brush.extent(),
        s0 = Math.round(extent[0]/yrb),
        s1 = Math.round(extent[1]/yrb);
    if (s0 == tasksSlice[0] && s1 == tasksSlice[1])
        return;
    tasksSlice = [s0, s1];
    inner.refresh();
}

What happens inside the handler is pretty simple:

  • get the brush extent
  • transpose it to indexes in my data array
  • slice my data array and set the result as the data to display
  • refresh the chart and table

I hope this clears it up.

like image 145
ludoo Avatar answered Sep 22 '22 23:09

ludoo