Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TreeMap support #129

Open
xhkong opened this issue Feb 22, 2013 · 19 comments
Open

TreeMap support #129

xhkong opened this issue Feb 22, 2013 · 19 comments
Milestone

Comments

@xhkong
Copy link

xhkong commented Feb 22, 2013

Nick,

Can we add TreeMap support as single (multiple when supported) selection chart? This can be really useful in some cases. And I assume this will not be too hard to achieve, but I might be wrong.

Thanks a lot!

Javy

@NickQiZhu
Copy link
Contributor

TreeMap is a good way to visualize hierarchical data however crossfilter currently does not support hierarchical data (drill down/drill up). I am doing some related hierarchical data visualization in some other project and hopefully will be able to incorporate some ideas into dc.js eventually. As of now I don’t think it is possible with crossfilter.

Will keep this in the backlog as a future feature request, cheers.

@marcrichter
Copy link

Nick,

Treemap uses are not limited to hierarchical data, especially the Marimekko / Mosaic variant is highly useful to assess interdependencies in data comprising several categorical dimensions. I like to think of it as the 2D+ variant of (normalised) stacked bars, and it's apparently well-implemented in D3 v3 (treemap.mode("slice-dice"), see d3/d3#169).

For some additional background check the following slide decks:
http://www.interactivegraphics.org/Slides_files/Chapter31.pdf
http://www.interactivegraphics.org/Slides_files/Chapter41.pdf

Cheers
Marc

@NickQiZhu
Copy link
Contributor

Agree, though not sure I have enough bandwidth to tackle this anytime soon (just had a new baby this week :) Will slot it in v1.5 for now.

@marcrichter
Copy link

Congrats on the baby, and good luck! :-) I only hope it won't make you lose interest in this other baby of yours entirely! ;-)

@jrideout
Copy link
Contributor

jrideout commented Oct 3, 2013

@ghost
Copy link

ghost commented Feb 3, 2014

I implemented one for my report, here is how I added the treemap to dc:

dc.treemapChart = function (parent, chartGroup) {

  var _chart = dc.baseMixin({});
  var _treemap;

  _chart._doRedraw = function() {
    var _cellData = {
      name:'tree',
      children: _chart.data()
    };
    _chart.root()
      .selectAll('.node')
      .data(_treemap.nodes)
      .transition().duration(1000)
      .call(_updateCell);

    _highlightFilters();
    return _chart;
  };

  _chart._doRender = function () {

    var color = d3.scale.category20c();

    _treemap = d3.layout.treemap()
      .size([_chart.width(), _chart.height()])
      .sticky(true)
      .value(function (d) { return d.value; });

    var _cellData = {
      name:'tree',
      children: _chart.data()
    };

    _chart.root()
      .classed('treemap', true)
      .style('position', 'relative')
      .style('height', _chart.height() + 'px')
      .style('width', _chart.width() + 'px');

    var _node = _chart.root()
      .datum(_cellData)
      .selectAll('.node')
      .data(_treemap.nodes)
      .enter()
      .append('div')
      .attr('class', 'node')
      .call(_updateCell)
      .style('background', function(d) { return color(d.key); })
      .style('position', 'absolute')
      .text(function(d) { return d.key; })
      .attr('title', _chart.title())
      .on('click', onClick);


    return _chart;
  };

  function _updateCell() {
    this.style('left', function (d) { return d.x + 'px'; })
    .style('top', function (d) { return d.y + 'px'; })
    .style('width', function (d) { return (d.dx - 1) + 'px'; })
    .style('height', function (d) { return (d.dy - 1) + 'px'; })
    .style('font-size', function (d) { return d.value > 0 ? 0.1 * Math.sqrt(d.dx * d.dy) +'px' : 0; });
  }

  function onClick(d, i) {
    _chart.onClick(d, i);
  }

  function _highlightFilters() {
    if (_chart.hasFilter()) {
      _chart.root().selectAll('.node').each(function (d) {
        if (_chart.hasFilter(d.key)) {
          _chart.highlightSelected(this);
        }
        else {
          _chart.fadeDeselected(this);
        }
      });
    }
    else {
      _chart.root().selectAll('.node').each(function (d) {
        _chart.resetHighlight(this);
      });
    }
  }

  return _chart.anchor(parent, chartGroup);
};

@marcrichter
Copy link

That's excellent! Any change to post this as a pull request for core?

@bwinchester
Copy link

pierco's example causes a call stack size limit error. Needs more work to incorporate the .margins method other box charts have in DC.

@ghost
Copy link

ghost commented Mar 20, 2014

Could you give me an example of this method implementation or point me to the right direction ? Thanks

@bwinchester
Copy link

There is a margins mixin for DC.js that takes top left right bottom. Also,
can you send an example of the data object you used and the crossfilter dimension
and group functions you used to get yours to work?

Margins mixin
https://github.com/dc-js/dc.js/blob/master/src/margin-mixin.js

BTW The margins is a suggestion to help place the chart and the call stack error was due to data input. I figured that out, but would like to see how you did the crossfilter dimensions and group to get this chart to display. I was using a demension from a pie chart with 5-6 groups with .group().reduceCount() as the group object, and the chart was displaying but caused a call stack exceeded error.

On Thursday, March 20, 2014, pierco notifications@github.com wrote:

Could you give me an example of this method implementation or point me to
the right direction ? Thanks

Reply to this email directly or view it on GitHubhttps://github.com//issues/129#issuecomment-38214264
.

@awjreynolds
Copy link

I was wondering if any further work had been done on this?

@jannah
Copy link

jannah commented Apr 1, 2015

Hi,
I made a minor modification to allow caps/valueAccessor/KeyAccessor.

dc.treemapChart = function (parent, chartGroup) {

  var _chart = dc.capMixin(dc.marginMixin(dc.baseMixin({})));
  var _treemap;
  _chart.rowsCap = _chart.cap;
  _chart._doRedraw = function() {
    var _cellData = {
      name:'tree',
      children: _chart.data()
    };
    _chart.root()
      .selectAll('.node')
      .data(_treemap.nodes)
      .transition().duration(1000)
      .call(_updateCell);

    _highlightFilters();
    return _chart;
  };

  _chart._doRender = function () {

    var color = d3.scale.category20c();

    _treemap = d3.layout.treemap()
      .size([_chart.width(), _chart.height()])
      .sticky(true)
      .value(function (d) { console.log('treemap value', d); return _chart.valueAccessor()(d); });

    var _cellData = {
      name:'tree',
      children: _chart.data()
    };

    _chart.root()
      .classed('treemap', true)
      .style('position', 'relative')
      .style('height', _chart.height() + 'px')
      .style('width', _chart.width() + 'px');

    var _node = _chart.root()
      .datum(_cellData)
      .selectAll('.node')
      .data(_treemap.nodes)
      .enter()
      .append('div')
      .attr('class', 'node')
      .call(_updateCell)
      .style('background', function(d) { return color(d.key); })
      .style('position', 'absolute')
      .text(function(d) { return _chart.keyAccessor()(d); })
      .attr('title', _chart.title())
      .on('click', onClick);


    return _chart;
  };

  function _updateCell() {
    this.style('left', function (d) { return d.x + 'px'; })
    .style('top', function (d) { return d.y + 'px'; })
    .style('width', function (d) { return (d.dx - 1) + 'px'; })
    .style('height', function (d) { return (d.dy - 1) + 'px'; })
    .style('font-size', function (d) { return d.value > 0 ? 0.1 * Math.sqrt(d.dx * d.dy) +'px' : 0; });
  }

  function onClick(d, i) {
    _chart.onClick(d, i);
  }

  function _highlightFilters() {
    if (_chart.hasFilter()) {
      _chart.root().selectAll('.node').each(function (d) {
        if (_chart.hasFilter(d.key)) {
          _chart.highlightSelected(this);
        }
        else {
          _chart.fadeDeselected(this);
        }
      });
    }
    else {
      _chart.root().selectAll('.node').each(function (d) {
        _chart.resetHighlight(this);
      });
    }
  }

  return _chart.anchor(parent, chartGroup);
};

@KatiRG
Copy link

KatiRG commented Jun 26, 2015

I tried both versions of dc.treemapChart and I got this error:

c.utils.GroupStack is not a constructor

Any ideas?

@gordonwoodhull
Copy link
Contributor

@KatiRG, I haven't tried the code myself, but I don't understand that error since GroupStack is not a symbol in either this code or dc.js

@KatiRG
Copy link

KatiRG commented Jun 26, 2015

ok that's weird! I'm trying the sunburst now, as you suggested. I'll let you know what happens, thanks!

@dderiso
Copy link

dderiso commented Aug 7, 2015

@jannah @ghost Thanks for adding treemap support. Did you submit a pull request?

@oakley808
Copy link

Wow! This is exactly what I need. I don't see a PR. Is there something I could do to contribute, @jannah @ghost ?

@gordonwoodhull
Copy link
Contributor

Since no one has submitted a PR, I think the best thing you can do is try this code out on your own @oakley808 and let us know how it works.

Then if you feel ambitious and neighborly, submit a PR yourself!

You should be able to just add this code in another script loaded after dc, as long as module gunk doesn't get in the way. dc is very extensible.

@rafaneri
Copy link

@jannah
I did some tests, but the filters aren't working properly. In https://codepen.io/rafaneri/pen/dKjqeJ is possible to see, that "Empresa 0143" has one data where gender is "M", but when I filter on pie chart for gender "M", the slot "Empresa 0143" is hidden.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests