block by nitaku 7dc03ae7fd1c5c337c5a

Treemaps: boundary coloring

Full Screen

An experiment on treemap coloring, using color encoding for depth. Instead of regions, boundaries are colored. A categorical color scale is used to help find nodes at the same level in other parts of the map, while some padding between lines is used together with decreasing thickness to convey a sense of nesting.

index.js

// Generated by CoffeeScript 1.10.0
(function() {
  var boundary_color, boundary_thickness, height, size, svg, treemap, vis, width, zoom, zoomable_layer;

  svg = d3.select('svg');

  width = svg.node().getBoundingClientRect().width;

  height = svg.node().getBoundingClientRect().height;

  size = Math.round(Math.min(width, height) * 0.8);

  boundary_thickness = d3.scale.sqrt().range([3, 0.5]);

  boundary_color = function(i) {
    return d3.hcl(50 + 100 * i, 50, 50);
  };

  treemap = d3.layout.treemap().size([size, size]).value(function(node) {
    return node.size;
  });

  svg.attr({
    viewBox: (-width / 2) + " " + (-height / 2) + " " + width + " " + height
  });

  zoomable_layer = svg.append('g');

  zoom = d3.behavior.zoom().scaleExtent([1, 10]).on('zoom', function() {
    return zoomable_layer.attr({
      transform: "translate(" + (zoom.translate()) + ")scale(" + (zoom.scale()) + ")"
    });
  });

  svg.call(zoom);

  vis = zoomable_layer.append('g').attr({
    transform: "translate(" + (-size / 2) + "," + (-size / 2) + ")"
  });

  d3.json('http://wafi.iit.cnr.it/webvis/tmp/flare.json', function(tree) {
    var data, enter_lines, enter_strips, levels, levels_data, lines, strips;
    data = treemap.nodes(tree);
    boundary_thickness.domain([
      d3.min(data, function(d) {
        return d.depth;
      }), d3.max(data, function(d) {
        return d.depth;
      })
    ]);
    levels_data = d3.nest().key(function(d) {
      return d.depth;
    }).entries(data);
    levels_data.forEach(function(d) {
      return d.key = +d.key;
    });
    levels_data.sort(function(a, b) {
      return d3.descending(a.key, b.key);
    });
    levels = vis.selectAll('.level').data(levels_data);
    levels.enter().append('g').attr({
      "class": 'level'
    });
    strips = levels.selectAll('.boundary_strip').data(function(d) {
      return d.values;
    });
    enter_strips = strips.enter().append('rect').attr({
      "class": 'boundary_strip',
      x: function(node) {
        return node.x;
      },
      y: function(node) {
        return node.y;
      },
      width: function(node) {
        return node.dx;
      },
      height: function(node) {
        return node.dy;
      },
      'stroke-width': function(node) {
        return 12;
      }
    });
    lines = levels.selectAll('.boundary_line').data(function(d) {
      return d.values;
    });
    return enter_lines = lines.enter().append('rect').attr({
      "class": 'boundary_line',
      x: function(node) {
        return node.x;
      },
      y: function(node) {
        return node.y;
      },
      width: function(node) {
        return node.dx;
      },
      height: function(node) {
        return node.dy;
      },
      stroke: function(node) {
        return boundary_color(node.depth);
      },
      'stroke-width': function(node) {
        return boundary_thickness(node.depth);
      }
    });
  });

}).call(this);

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Treemaps: boundary coloring</title>
    <link type="text/css" href="index.css" rel="stylesheet"/>
    <script src="//d3js.org/d3.v3.min.js"></script>
  </head>
  <body>
    <svg></svg>
    <script src="index.js"></script>
  </body>
</html>

index.coffee

svg = d3.select('svg')
width = svg.node().getBoundingClientRect().width
height = svg.node().getBoundingClientRect().height

size = Math.round(Math.min(width, height) * 0.8)

boundary_thickness = d3.scale.sqrt()
  .range([3,0.5])
  
boundary_color = (i) -> d3.hcl(50+100*i,50,50)

treemap = d3.layout.treemap()
  .size([size, size])
  .value((node) -> node.size)

# translate the viewBox to have (0,0) at the center of the vis
svg
  .attr
    viewBox: "#{-width/2} #{-height/2} #{width} #{height}"
    
# append a group for zoomable content
zoomable_layer = svg.append('g')
 
# define a zoom behavior
zoom = d3.behavior.zoom()
  .scaleExtent([1,10]) # min-max zoom
  .on 'zoom', () ->
    # GEOMETRIC ZOOM
    zoomable_layer
      .attr
        transform: "translate(#{zoom.translate()})scale(#{zoom.scale()})"

# bind the zoom behavior to the main SVG
svg.call(zoom)

# group the visualization
vis = zoomable_layer.append('g')
  .attr
    transform: "translate(#{-size/2},#{-size/2})"

    
d3.json 'http://wafi.iit.cnr.it/webvis/tmp/flare.json', (tree) ->
  data = treemap.nodes(tree)
  
  boundary_thickness
    .domain([d3.min(data, (d) -> d.depth), d3.max(data, (d) -> d.depth)])
    
  levels_data = d3.nest()
    .key (d) -> d.depth
    .entries data
    
  levels_data.forEach (d) -> d.key = +d.key
  
  # drawing order: deep to shallow
  levels_data.sort (a,b) -> d3.descending(a.key,b.key)
  
  levels = vis.selectAll '.level'
    .data levels_data
    
  levels.enter().append 'g'
    .attr
      class: 'level'
      
  
  strips = levels.selectAll '.boundary_strip'
    .data (d) -> d.values
    
  enter_strips = strips.enter().append 'rect'
    .attr
      class: 'boundary_strip'
      x: (node) -> node.x
      y: (node) -> node.y
      width: (node) -> node.dx
      height: (node) -> node.dy
      'stroke-width': (node) -> 12
      
  lines = levels.selectAll '.boundary_line'
    .data (d) -> d.values
  
  enter_lines = lines.enter().append 'rect'
    .attr
      class: 'boundary_line'
      x: (node) -> node.x
      y: (node) -> node.y
      width: (node) -> node.dx
      height: (node) -> node.dy
      stroke: (node) -> boundary_color(node.depth)
      'stroke-width': (node) -> boundary_thickness(node.depth)
  

index.css

html, body {
  padding: 0;
  margin: 0;
  width: 100%;
  height: 100%;
  overflow: hidden;
}
svg {
  width: 100%;
  height: 100%;
  background: white;
}
.boundary_line {
  vector-effect: non-scaling-stroke;
  fill: none;
}
.boundary_strip {
  stroke: white;
  vector-effect: non-scaling-stroke;
  fill: none;
}