block by nitaku f6f42f7f6b5dea054d18

Opinion treemap word cloud (OpeNER - Paris, es)

Full Screen

This treemap word cloud shows opinion target words from reviews of accommodations in Paris. Bigger words were identified more often by sentiment analyzers. Color represents the aggregated sentiment towards the target words (green is positive, red is negative and yellow is neutral).

Use the mouse to pan and zoom.

Data is obtained from the Tour-Pedia APIs, that in turn get their input from the sentiment analysis pipeline of the OpeNER project.

index.js

(function() {
  var correct_x, correct_y, height, svg, treemap, vis, width, zoom, zoomable_layer;

  svg = d3.select('svg');

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

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

  treemap = d3.layout.treemap().size([width, height]).value(function(node) {
    return node.count;
  }).sort(function(a, b) {
    return a.polarity - b.polarity;
  }).ratio(4);

  correct_x = d3.scale.linear().domain([0, width]).range([0, width * 1.05]);

  correct_y = d3.scale.linear().domain([0, height]).range([0, height * 3 / 4]);

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

  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(" + (-width / 2) + "," + (-height / 2) + ")"
  });

  d3.json('http://tour-pedia.org/api/getOpinions?location=Paris&language=es', function(data) {
    var NEUTRAL, best, color, enter_labels, fake_tree, labels, nodes_data, words, words_frequency, worst;

    words = data.target;
    words_frequency = [];
    best = 0;
    worst = 0;
    Object.keys(words).forEach(function(word_lemma) {
      var o;

      o = words[word_lemma];
      o.lemma = word_lemma.trim();
      if (o.lemma.length <= 1) {
        return;
      }
      words_frequency.push(o);
      best = Math.max(best, o.polarity);
      return worst = Math.min(worst, o.polarity);
    });
    fake_tree = {
      children: words_frequency
    };
    NEUTRAL = 80;
    color = d3.scale.linear().domain([worst, 0, best]).range([d3.hcl(NEUTRAL - 80, 85, 50), d3.hcl(NEUTRAL, 85, 70), d3.hcl(NEUTRAL + 80, 85, 50)]).interpolate(d3.interpolateHcl);
    nodes_data = treemap.nodes(fake_tree);
    labels = vis.selectAll('.label').data(nodes_data.filter(function(node) {
      return node.depth === 1;
    }));
    enter_labels = labels.enter().append('svg').attr({
      "class": 'label'
    });
    enter_labels.append('text').text(function(node) {
      return node.lemma.toUpperCase();
    }).attr({
      dy: '0.35em',
      fill: function(node) {
        return color(node.polarity);
      }
    }).each(function(node) {
      var bbox, bbox_aspect, node_bbox, node_bbox_aspect, rotate;

      bbox = this.getBBox();
      bbox_aspect = 2;
      node_bbox = {
        width: node.dx,
        height: node.dy
      };
      node_bbox_aspect = node_bbox.width / node_bbox.height;
      rotate = bbox_aspect >= 1 && node_bbox_aspect < 1 || bbox_aspect < 1 && node_bbox_aspect >= 1;
      node.label_bbox = {
        x: bbox.x + (bbox.width - correct_x(bbox.width)) / 2,
        y: bbox.y + (bbox.height - correct_y(bbox.height)) / 2,
        width: correct_x(bbox.width),
        height: correct_y(bbox.height)
      };
      if (rotate) {
        node.label_bbox = {
          x: node.label_bbox.y,
          y: node.label_bbox.x,
          width: node.label_bbox.height,
          height: node.label_bbox.width
        };
        return d3.select(this).attr('transform', 'rotate(-90)');
      }
    });
    return enter_labels.attr({
      x: function(node) {
        return node.x;
      },
      y: function(node) {
        return node.y;
      },
      width: function(node) {
        return node.dx;
      },
      height: function(node) {
        return node.dy;
      },
      viewBox: function(node) {
        return "" + node.label_bbox.x + " " + node.label_bbox.y + " " + node.label_bbox.width + " " + node.label_bbox.height;
      },
      preserveAspectRatio: 'none'
    });
  });

}).call(this);

index.html

<!DOCTYPE html>
<html>
	<head>
        <meta charset="utf-8">
        <meta name="description" content="Opinion treemap word cloud (OpeNER - Paris, es)" />
        <title>Opinion treemap word cloud (OpeNER - Paris, es)</title>
		<link type="text/css" href="index.css" rel="stylesheet"/>
        <script src="//davidbau.com/encode/seedrandom-min.js"></script>
        <script src="//d3js.org/d3.v3.min.js"></script>
	</head>
	<body>
        <svg height="500" width="960"></svg>
        <script src="index.js"></script>
	</body>
</html>

index.coffee

index.css

svg {
  background: white;
}
.node {
  shape-rendering: crispEdges;
  vector-effect: non-scaling-stroke;
  stroke: white;
  stroke-width: 2;
}
.label {
  pointer-events: none;
  text-anchor: middle;
  font-family: Impact;
}