// Generated by CoffeeScript 1.10.0 (function() { var color, data_tiles, font_size, labels_index, levels, log, precision_multiplier, ql, scale, start; start = 0; levels = 3; data_tiles = []; labels_index = {}; scale = 480; precision_multiplier = 10000; ql = quad_layout(hquad, scale * precision_multiplier); font_size = d3.scale.pow().exponent(.5).domain([0, 1000000000000]).range([12, 22]); color = d3.scale.ordinal().domain([0, 1, 2, 3, 4]).range(["#fbb4ae", "#b3cde3", "#ccebc5", "#decbe4", "#fed9a6"]); log = function(msg) { d3.select('#msg').text(msg); return console.log(msg); }; log('Loading labels'); d3.csv('names.csv', function(error, labels_data) { labels_data.forEach(function(d) { return labels_index[d.id] = d.label; }); log('Loading nodes'); return d3.csv('ncbi_ordered.csv', function(error, data) { var color_i, features, final_data, height, hierarchy, j, labels_layer, last_parent, lod, merge, nodes, order, path_generator, prepare, redraw_regions, redraw_tiles, ref, regions_layer, result, results, squares, svg, tile_size, tiles_layer, topologies, tree, union, vis, width, zoom, zoomable_layer; union = function(total, next) { return turf.union(total, next); }; prepare = function(node, squares) { var _tiles, child, count, j, len, ref; count = 0; node.tiles = []; ref = node.children; for (j = 0, len = ref.length; j < len; j++) { child = ref[j]; if (child.children != null) { _tiles = prepare(child, squares); node.tiles = node.tiles.concat(_tiles); count += _tiles.length; } else { node.tiles.push(child.id); count += 1; } } if (node.depth === levels) { node.start = start; node.end = start + count; start += count; node.z = get_z(node); node.quads = get_quads(node, order).map(quad_layout(hquad, scale * precision_multiplier)); return squares.push({ polygons: node.quads.map(function(q) { return { type: "Feature", properties: {}, geometry: { type: "Polygon", coordinates: [[[q.x, q.y], [q.x + q.dx, q.y], [q.x + q.dx, q.y + q.dy], [q.x, q.y + q.dy], [q.x, q.y]]] } }; }), node: node }); } else if (node.parent != null) { return node.tiles; } else { return squares; } }; last_parent = function(node) { if (node.depth === 1) { return node.id; } else { return last_parent(node.parent); } }; merge = function(level, polygons, features, final_data, topologies) { var nested_polygons, new_polygons, result; if (level >= 0) { features[level] = []; final_data[level] = []; topologies[level] = []; new_polygons = []; polygons.forEach(function(p) { var geojson; geojson = JSON.parse(JSON.stringify(p.polygons.reduce(union))); geojson.properties["class"] = p.node.id; geojson.properties.parent = last_parent(p.node); geojson.properties.tiles = p.node.tiles; new_polygons.push({ polygons: geojson, node: p.node }); return features[level].push(geojson); }); nested_polygons = d3.nest().key(function(d) { return d.node.parent.id; }).entries(new_polygons).map((function(d) { return { node: d.values[0].node.parent, polygons: d.values.map(function(v) { return v.polygons; }) }; }), d3.map); result = merge(level - 1, nested_polygons, features, final_data, topologies); features = result.features; final_data = result.final_data; topologies = result.topologies; /* Update Structures */ final_data[level] = { feature_collection: { collection: { type: "FeatureCollection", features: features[level] } }, spatial: features[level].map(function(f) { return { centroid: path_generator.centroid(f), bbox: turf.envelope(f), "class": f.properties["class"] }; }) }; topologies[level] = topojson.server.topology(JSON.parse(JSON.stringify(final_data[level].feature_collection)), { 'coordinate-system': 'cartesian', quantization: 0, 'property-transform': function(feature) { return feature.properties; } }); return { features: features, final_data: final_data, topologies: topologies }; } else { return { features: features, final_data: final_data, topologies: topologies }; } }; /* INITIALIZATION */ log('Stratify data'); tree = (d3.stratify().id(function(d) { return d.id; }).parentId(function(d) { return d.parent_id; }))(data); order = Math.ceil(Math.log(tree.leaves().length) / Math.log(4)); tile_size = scale * precision_multiplier / Math.pow(2, order); path_generator = d3.geo.path().projection(null); log('Quads calculation'); squares = prepare(tree, []); log('Merging'); result = merge(levels - 1, squares, [], [], []); features = result.features; final_data = result.final_data; topologies = result.topologies; log('Tiles calculation'); hierarchy = d3.layout.hierarchy(); nodes = hierarchy(tree); (nodes.filter(function(node) { return node.depth === levels && (node.start != null); })).forEach(function(d, i) { var j, n, n_4, obj, ref, ref1, results, tiles; tiles = []; results = []; for (n = j = ref = d.start, ref1 = d.end; ref <= ref1 ? j < ref1 : j > ref1; n = ref <= ref1 ? ++j : --j) { n_4 = n.toString(4); n_4 = Array(order - n_4.length + 1).join('0') + n_4; obj = ql(n_4); obj.tile = labels_index[d.tiles[n - d.start]]; obj.name = d.id; results.push(data_tiles.push(obj)); } return results; }); /* VISUALIZATION */ log('Visualization'); width = document.body.getBoundingClientRect().width; height = document.body.getBoundingClientRect().height; svg = d3.select('svg').attr({ width: width, height: height }); zoomable_layer = svg.append('g'); vis = zoomable_layer.append('g').attr({ transform: "translate(" + ((width - scale) / 2) + ", " + ((height - scale) / 2) + ") scale(" + (1 / precision_multiplier) + ")" }); regions_layer = vis.append('g'); tiles_layer = vis.append('g'); labels_layer = vis.append('g'); zoom = d3.behavior.zoom().scaleExtent([1, Infinity]).on('zoom', function() { zoomable_layer.attr({ transform: "translate(" + (zoom.translate()) + ")scale(" + (zoom.scale()) + ")" }); return lod(zoom.translate(), zoom.scale()); }); svg.call(zoom); color_i = d3.scale.ordinal().domain(final_data[0].feature_collection.collection.features.map(function(d) { return d.properties["class"]; })).range((function() { results = []; for (var j = 0, ref = final_data[0].feature_collection.collection.features.length; 0 <= ref ? j < ref : j > ref; 0 <= ref ? j++ : j--){ results.push(j); } return results; }).apply(this)); /* tiles VISUALIZATION */ redraw_tiles = function(x_start, x_end, y_start, y_end) { var _data_tiles, enter_tiles, rect, tile_labels, tile_texts, tiles; if (x_start == null) { _data_tiles = []; } else { _data_tiles = data_tiles.filter(function(d) { return x_start - d.dx <= d.x + d.dx / 2 && x_end + d.dx >= d.x + d.dx / 2 && y_start - d.dy <= d.y + d.dy / 2 && y_end + d.dy >= d.y + d.dy / 2; }); } tiles = tiles_layer.selectAll('.tile').data(_data_tiles, function(d) { return d.digits; }); enter_tiles = tiles.enter().append('g').attr({ "class": 'tile' }); tiles.attr({ transform: function(d) { return "translate(" + d.x + ", " + d.y + ")"; } }); rect = enter_tiles.append('rect').attr({ fill: 'transparent' }); rect.attr({ x: function(d) { return 0; }, y: function(d) { return 0; }, width: function(d) { return d.dx; }, height: function(d) { return d.dy; } }); tile_texts = enter_tiles.append('a').attr({ href: function(d) { return "http://www.ncbi.nlm.nih.gov/Taxonomy/Browser/wwwtax.cgi?mode=Undef&name=" + (d.tile.replace(/ /g, '+')); }, target: '_blank' }).append('text').attr({ 'font-size': 250, 'text-anchor': 'middle' }); tile_labels = tile_texts.selectAll('tspan').data(function(d) { return d.tile.split(' '); }); tile_labels.enter().append('tspan').attr({ x: function(d) { return tile_size / 2; }, y: function(d, i) { return tile_size / 4 + i * 250; } }).text(function(d) { return d; }); return tiles.exit().remove(); }; /* regions VISUALIZATION */ redraw_regions = function(z, level, x_start, x_end, y_start, y_end) { var enter_labels, enter_regions, geo_json, labels, regions; geo_json = topojson.client.feature(topologies[level], topologies[level].objects.collection).features; regions = regions_layer.selectAll('.region').data(final_data[level].feature_collection.collection.features.filter(function(d, i) { return final_data[level].spatial[i].bbox.geometry.coordinates[0][0][0] <= x_end && final_data[level].spatial[i].bbox.geometry.coordinates[0][1][0] >= x_start && final_data[level].spatial[i].bbox.geometry.coordinates[0][0][1] <= y_end && final_data[level].spatial[i].bbox.geometry.coordinates[0][3][1] >= y_start; }), function(d) { return d.properties["class"] + "_" + level; }); enter_regions = regions.enter().append('g').attr({ "class": 'region' }); enter_regions.append('path').attr({ stroke: function(d) { return '#333'; }, fill: function(d) { var index; index = color_i(d.properties.parent); return color(index); } }); regions.selectAll('path').attr({ d: function(d) { return path_generator(topojson.client.merge(topologies[level], topologies[level].objects.collection.geometries.filter(function(g) { return g.properties["class"] === d.properties["class"]; }))); } }); regions.exit().remove(); labels = labels_layer.selectAll('.label').data(final_data[level].spatial.filter(function(d) { return d.centroid[0] >= x_start && d.centroid[0] <= x_end && d.centroid[1] >= y_start && d.centroid[1] <= y_end; }), (function(d) { return d["class"] + "_" + level; })); enter_labels = labels.enter().append('g').attr({ "class": 'label', 'font-size': function(d) { return font_size((d.bbox.geometry.coordinates[0][1][0] - d.bbox.geometry.coordinates[0][0][0]) * (d.bbox.geometry.coordinates[0][3][1] - d.bbox.geometry.coordinates[0][0][1])); } }); enter_labels.append('text').attr({ "class": 'halo' }); enter_labels.append('text'); labels.selectAll('text').text(function(d) { return labels_index[d["class"]]; }); labels.attr({ transform: function(d) { return "translate(" + d.centroid[0] + ", " + d.centroid[1] + ") scale(" + (z === 0 ? 0 : precision_multiplier / z) + ")"; } }); labels.exit().remove(); }; /* Viewport and Level of details */ lod = function(translation, z) { var dx, dy, level, x, y; if (z < 3) { level = 0; } else if (z < 8) { level = 1; } else { level = 2; } x = (-(width - scale) / 2 - translation[0] / z) * precision_multiplier; y = (-(height - scale) / 2 - translation[1] / z) * precision_multiplier; dx = width * precision_multiplier / z; dy = height * precision_multiplier / z; if (tile_size * 15 > dx) { redraw_tiles(x, x + dx, y, y + dy); return redraw_regions(0, level, x, x + dx, y, y + dy); } else { redraw_tiles(); return redraw_regions(z, level, x, x + dx, y, y + dy); } }; lod([0, 0], 1); return d3.select('#loading_box').remove(); }); }); }).call(this);