// Generated by CoffeeScript 1.4.0 /* define a fixed random seed, to avoid to have a different layout on each page reload. change the string to randomize */ (function() { var distance, graph_layer, height, land, panel, relations_layer, sea, svg, voronoi, width, zoom; Math.seedrandom('abcde'); panel = d3.select('#panel'); svg = d3.select('svg'); width = svg.node().getBoundingClientRect().width; height = svg.node().getBoundingClientRect().height; voronoi = d3.geom.voronoi().x(function(d) { return d.x; }).y(function(d) { return d.y; }); distance = d3.scale.linear().domain([0, 1]).range([100, 0]); /* store the graph in a zoomable layer */ graph_layer = svg.append('g'); /* define a zoom behavior */ zoom = d3.behavior.zoom().scaleExtent([0.01, 10]).on('zoom', function() { /* whenever the user zooms, */ /* modify translation and scale of the zoom group accordingly */ return graph_layer.attr('transform', "translate(" + (zoom.translate()) + ")scale(" + (zoom.scale()) + ")"); }); /* bind the zoom behavior to the main SVG */ svg.call(zoom); /* create groups for land and sea */ sea = graph_layer.append('g'); /* cover the sea with a pattern */ graph_layer.append('rect').attr('id', 'sea_pattern').attr('width', 10000).attr('height', 10000).attr('x', -5000).attr('y', -5000); land = graph_layer.append('g'); relations_layer = graph_layer.append('g'); d3.json('pink_floyd_matrix.json', function(data) { var cells_data, deep_cells, deep_clips, enter_land_cells, force, graph, i, l, land_cells, land_clips, links, n, nodes, shallow_cells, shallow_clips, _i, _j, _k, _len, _len1, _ref, _ref1; graph = { nodes: data.map(function(n) { return { uri: n.k1 }; }), links: [] }; data.forEach(function(e1) { return e1.similarities.forEach(function(e2) { return graph.links.push({ source: e1.k1, target: e2.k2, similarity: e2.sim }); }); }); /* objectify the graph */ /* resolve node IDs (not optimized at all!) */ _ref = graph.links; for (_i = 0, _len = _ref.length; _i < _len; _i++) { l = _ref[_i]; _ref1 = graph.nodes; for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { n = _ref1[_j]; if (l.source === n.uri) { l.source = n; } if (l.target === n.uri) { l.target = n; } } } /* run the force layout for a fixed number of iterations */ force = d3.layout.force().size([width, height]).charge(-10000).linkDistance(function(l) { return 5 * distance(l.similarity); }).chargeDistance(1000); force.nodes(graph.nodes).links(graph.links).start(); for (i = _k = 0; _k <= 5000; i = ++_k) { force.tick(); } force.stop(); /* update deep clip paths */ deep_clips = graph_layer.selectAll('.deep_clip').data(graph.nodes); deep_clips.enter().append('clipPath').attr('class', 'deep_clip').attr('id', function(d, i) { return "deep_clip-" + i; }).append('circle').attr('r', 52); deep_clips.select('circle').attr('cx', function(n) { return n.x; }).attr('cy', function(n) { return n.y; }); /* update shallow clip paths */ shallow_clips = graph_layer.selectAll('.shallow_clip').data(graph.nodes); shallow_clips.enter().append('clipPath').attr('class', 'shallow_clip').attr('id', function(d, i) { return "shallow_clip-" + i; }).append('circle').attr('r', 26); shallow_clips.select('circle').attr('transform', function(n) { return "translate(" + n.x + "," + n.y + ")"; }); /* update land clip paths */ land_clips = graph_layer.selectAll('.land_clip').data(graph.nodes); land_clips.enter().append('clipPath').attr('class', 'land_clip').attr('id', function(d, i) { return "land_clip-" + i; }).append('path').attr('d', 'M8,16 L18,4 L14,-11 L0,-18 L-14,-11 L-18,4 L-8,16 z'); land_clips.select('path').attr('transform', function(n) { return "translate(" + n.x + "," + n.y + ")"; }); /* update cells */ cells_data = voronoi(graph.nodes); deep_cells = sea.selectAll('.deep_cell').data(cells_data); deep_cells.enter().append('path').attr('class', 'deep_cell').attr('clip-path', function(d, i) { return "url(#deep_clip-" + i + ")"; }); deep_cells.attr('d', function(d) { return "M" + d.join(",") + "Z"; }); shallow_cells = sea.selectAll('.shallow_cell').data(cells_data); shallow_cells.enter().append('path').attr('class', 'shallow_cell').attr('clip-path', function(d, i) { return "url(#shallow_clip-" + i + ")"; }); shallow_cells.attr('d', function(d) { return "M" + d.join(",") + "Z"; }); land_cells = land.selectAll('.land_cell').data(cells_data); enter_land_cells = land_cells.enter().append('path').attr('class', 'land_cell').attr('clip-path', function(d, i) { return "url(#land_clip-" + i + ")"; }).on('click', function(d) { /* highlight selection */ land_cells.classed('selected', false); d3.select(this).classed('selected', true); /* query DBpedia for details */ return d3.json(d.point.uri.replace('/resource/', '/data/') + '.json', function(e) { /* load details into the panel */ var en_a, en_l, relations, relations_data; en_l = _.find(e[d.point.uri]['http://www.w3.org/2000/01/rdf-schema#label'], function(l) { return l.lang === 'en'; }); en_a = _.find(e[d.point.uri]['http://dbpedia.org/ontology/abstract'], function(a) { return a.lang === 'en'; }); panel.select('header').text(en_l != null ? en_l.value : '[no english label found]'); panel.select('section').text(en_a != null ? en_a.value : '[no english abstract found]'); /* show relational links */ relations_data = []; /* outgoing links */ _.map(e[d.point.uri], function(p, p_uri) { return _.map(p, function(o) { var obj; if (!o.type === ('uri' != null)) { return; } obj = _.find(graph.nodes, function(n) { return n.uri === o.value; }); if (!(obj != null)) { return; } return relations_data.push({ 's': d.point, 'p': p_uri, 'o': obj }); }); }); /* incoming links */ _.map(e, function(s, s_uri) { if (s_uri === d.point.uri) { return; } return _.map(s, function(p, p_uri) { var subj; subj = _.find(graph.nodes, function(n) { return n.uri === s.value; }); if (!(subj != null)) { return; } return _.map(p, function(o) { return relations_data.push({ 's': subj, 'p': p_uri, 'o': d.point }); }); }); }); relations = relations_layer.selectAll('.relation').data(relations_data.filter(function(r) { return r.s !== r.o; }), function(r) { return "" + r.s.uri + ">>>" + r.p + ">>>" + r.o.uri; }); relations.enter().append('path').attr('class', 'relation').attr('d', function(r) { return "M" + r.s.x + " " + r.s.y + " C" + r.s.x + " " + (r.s.y - 120) + " " + r.o.x + " " + (r.o.y - 120) + " " + r.o.x + " " + r.o.y; }); return relations.exit().remove(); }); }); enter_land_cells.append('title').text(function(n) { return n.point.uri.replace('http://dbpedia.org/resource/', ''); }); land_cells.attr('d', function(d) { return "M" + d.join(",") + "Z"; }); /* update links */ links = graph_layer.selectAll('.link').data(graph.links); links.enter().append('line').attr('class', 'link').attr('opacity', function(l) { return l.similarity; }); links.attr('x1', function(l) { return l.source.x; }).attr('y1', function(l) { return l.source.y; }).attr('x2', function(l) { return l.target.x; }).attr('y2', function(l) { return l.target.y; }); /* update nodes */ nodes = graph_layer.selectAll('.node').data(graph.nodes); nodes.enter().append('circle').attr('class', 'node').attr('r', 2); return nodes.attr('cx', function(n) { return n.x; }).attr('cy', function(n) { return n.y; }); }); }).call(this);