From 57c6d38c8688836226b3c9b9f6973772a580eced Mon Sep 17 00:00:00 2001 From: jjgao Date: Mon, 15 Dec 2014 13:25:58 -0500 Subject: [PATCH 001/343] Initial commit --- packages/oncoprintjs/README.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 packages/oncoprintjs/README.md diff --git a/packages/oncoprintjs/README.md b/packages/oncoprintjs/README.md new file mode 100644 index 00000000000..6ad0df10ba5 --- /dev/null +++ b/packages/oncoprintjs/README.md @@ -0,0 +1,4 @@ +oncoprint.js +============ + +OncoPrint visualization From 28c2e91c675da692f212066807d0843e3d4abd29 Mon Sep 17 00:00:00 2001 From: Gideon Dresdner Date: Wed, 11 Mar 2015 12:50:51 +0200 Subject: [PATCH 002/343] Merge pull request #1 from cBioPortal/wip-core-4merge Core module structure --- packages/oncoprintjs/.gitignore | 42 + packages/oncoprintjs/gulpfile.js | 66 + packages/oncoprintjs/package.json | 53 + packages/oncoprintjs/src/js/genomic.js | 70 + packages/oncoprintjs/src/js/renderers.js | 38 + .../oncoprintjs/src/js/rendering_engine.js | 149 ++ packages/oncoprintjs/src/js/utils.js | 5 + .../test/data/prostate-ar-signalling.json | 1224 +++++++++++++++++ .../test/data/tp53-mdm2-mdm4-gbm.json | 1145 +++++++++++++++ packages/oncoprintjs/test/index.html | 28 + packages/oncoprintjs/test/js/test_page.js | 21 + 11 files changed, 2841 insertions(+) create mode 100644 packages/oncoprintjs/.gitignore create mode 100644 packages/oncoprintjs/gulpfile.js create mode 100644 packages/oncoprintjs/package.json create mode 100644 packages/oncoprintjs/src/js/genomic.js create mode 100644 packages/oncoprintjs/src/js/renderers.js create mode 100644 packages/oncoprintjs/src/js/rendering_engine.js create mode 100644 packages/oncoprintjs/src/js/utils.js create mode 100644 packages/oncoprintjs/test/data/prostate-ar-signalling.json create mode 100644 packages/oncoprintjs/test/data/tp53-mdm2-mdm4-gbm.json create mode 100644 packages/oncoprintjs/test/index.html create mode 100644 packages/oncoprintjs/test/js/test_page.js diff --git a/packages/oncoprintjs/.gitignore b/packages/oncoprintjs/.gitignore new file mode 100644 index 00000000000..9c65dc24b53 --- /dev/null +++ b/packages/oncoprintjs/.gitignore @@ -0,0 +1,42 @@ +# Created by https://www.gitignore.io + +### Node ### +# Logs +logs +*.log + +# Runtime data +pids +*.pid +*.seed + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directory +# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- +node_modules + + +### vim ### +[._]*.s[a-w][a-z] +[._]s[a-w][a-z] +*.un~ +Session.vim +.netrwhist +*~ + +# Compiled assets +dist diff --git a/packages/oncoprintjs/gulpfile.js b/packages/oncoprintjs/gulpfile.js new file mode 100644 index 00000000000..edcd3bf0f9f --- /dev/null +++ b/packages/oncoprintjs/gulpfile.js @@ -0,0 +1,66 @@ +'use strict'; + +var autoprefixer = require('gulp-autoprefixer'); +var browserify = require('browserify'); +var cache = require('gulp-cache'); +var concat = require('gulp-concat'); +var del = require('del'); +var gulp = require('gulp'); +var jshint = require('gulp-jshint'); +var livereload = require('gulp-livereload'); +var minifycss = require('gulp-minify-css'); +var notify = require('gulp-notify'); +var rename = require('gulp-rename'); +var source = require('vinyl-source-stream'); +var streamify = require('gulp-streamify'); +var uglify = require('gulp-uglify'); + +// Test Page +gulp.task('test', function() { + + // JavaScript + browserify({entries: './test/js/test_page.js', + debug: true + }).bundle() + .pipe(source('test.js')) + .pipe(rename('test_page_bundle.js')) + .pipe(gulp.dest('dist/test/')) + .pipe(streamify(uglify())) + .pipe(notify("Done with building code for testing.")) + + // Copy over the HTML. + gulp.src('test/index.html') + .pipe(gulp.dest('dist/test/')); + + // Copy over the data. + gulp.src('test/data/**') + .pipe(gulp.dest('dist/test/')); +}); + +// JavaScript +// TODO need to figure out what to compile other than test code... +//gulp.task('js', function() { +// browserify({entries: './src/js/main.js', +// debug: !process.env.production +// }).bundle() +// .pipe(source('genomic.js')) +// .pipe(rename('genomic-oncoprint-bundle.js')) +// .pipe(gulp.dest('dist/asset/js')) +// .pipe(streamify(uglify())) +// .pipe(notify("Done with JavaScript.")) +//}); + +// Clean +gulp.task('clean', function(cb) { + del(['dist'], cb) +}); + +// Default +gulp.task('default', ['clean'], function() { + gulp.start('js'); +}); + +// Watch +gulp.task('watch', function() { + gulp.watch(['src/js/**/*.js', 'test/*.html', 'test/js/**/*.js'], ['test']); +}); diff --git a/packages/oncoprintjs/package.json b/packages/oncoprintjs/package.json new file mode 100644 index 00000000000..231173b4526 --- /dev/null +++ b/packages/oncoprintjs/package.json @@ -0,0 +1,53 @@ +{ + "name": "oncoprintjs", + "version": "1.0.0", + "description": "A data visualization for cancer genomic data.", + "scripts": { + "test": "test" + }, + "repository": { + "type": "git", + "url": "https://github.com/cBioPortal/oncoprintjs.git" + }, + "keywords": [ + "cancer", + "genomics", + "visualization", + "d3js" + ], + "author": { + "name": "Gideon Dresdner", + "email": "oncoprintjs@gideonite.com" + }, + "contributors": [ + { + "name": "JJ Gao", + "email": "jianjiong.gao@gmail.com" + } + ], + "license": "ISC", + "bugs": { + "url": "https://github.com/cBioPortal/oncoprintjs/issues" + }, + "homepage": "https://github.com/cBioPortal/oncoprintjs", + "devDependencies": { + "browserify": "^8.1.1", + "del": "^1.1.1", + "gulp": "^3.8.10", + "gulp-autoprefixer": "^2.1.0", + "gulp-cache": "^0.2.4", + "gulp-concat": "^2.4.3", + "gulp-jshint": "^1.9.0", + "gulp-livereload": "^3.4.0", + "gulp-minify-css": "^0.4.0", + "gulp-notify": "^2.1.0", + "gulp-rename": "^1.2.0", + "gulp-streamify": "0.0.5", + "gulp-uglify": "^1.1.0", + "vinyl-source-stream": "^1.0.0" + }, + "dependencies": { + "d3": "^3.5.3", + "underscore": "^1.7.0" + } +} diff --git a/packages/oncoprintjs/src/js/genomic.js b/packages/oncoprintjs/src/js/genomic.js new file mode 100644 index 00000000000..9d50e06a77f --- /dev/null +++ b/packages/oncoprintjs/src/js/genomic.js @@ -0,0 +1,70 @@ +var d3 = require('d3'); +var _ = require('underscore'); + +var renderers = require('./renderers'); +var rendering_engine = require('./rendering_engine'); +var utils = require('./utils'); + +var rendering_engine = rendering_engine(); + +var config = { rect_height: 20, + rect_padding: 3, + rect_width: 10, + mutation_fill: 'green', + + cna_fills: { + null: 'grey', + undefined: 'grey', + AMPLIFIED: 'red', + HOMODELETED: 'blue' + } +}; + +var gene_renderer = renderers.gene(config); + +function is_sample_genetically_altered(datum) { + return datum.cna !== undefined + || datum.mutation !== undefined + || datum.rna !== undefined + || datum.protein !== undefined; +}; + +function calculate_row_label(row) { + var percent_altered = _.filter(row, is_sample_genetically_altered).length / row.length; + percent_altered = Math.round(percent_altered*100); + return [{align: 'left', text: row[0].gene}, {align: 'right', text: percent_altered + "%"}]; +}; + +function rows_to_labels(rows) { + return _.flatten(_.map(rows, calculate_row_label)); +} + +var genomic = function() { + var row_height = 25; + var width = 500; + + var me = function(container) { + rendering_engine.config({row_height: row_height}); + rendering_engine.container_width(width); + rendering_engine.element_width(config.rect_width); + rendering_engine.element_padding(config.rect_padding); + rendering_engine.labels(rows_to_labels(rows)); + container.call(rendering_engine); + }; + + me.row_height = function(value) { + if (!arguments.length) return row_height; + row_height = value; + return me; + }; + + me.width = function(value) { + if (!arguments.length) return width; + width = value; + return me; + }; + + return me; +}; + +module.exports = genomic; diff --git a/packages/oncoprintjs/src/js/renderers.js b/packages/oncoprintjs/src/js/renderers.js new file mode 100644 index 00000000000..26b99abd006 --- /dev/null +++ b/packages/oncoprintjs/src/js/renderers.js @@ -0,0 +1,38 @@ +var utils = require('./utils'); + +var gene_rule = function(config) { + return function(selection) { + var row_elements = selection.selectAll('g') + // binds the row-wise data to the row group, . See Bostock's + // explaination on nested selections: http://bost.ocks.org/mike/nest/#data + .data(function(d) { return d; }) + .enter().append('g'); + + row_elements.attr('transform', function(d, i) { + return utils.translate(i * (config.rect_width + config.rect_padding), 0); + }); + + row_elements.append('rect') + .attr('fill', function(d) { return config.cna_fills[d.cna]; }) + .attr('height', config.rect_height) + .attr('width', config.rect_width); + + var one_third_height = config.rect_height / 3; + + var mutation = row_elements.append('rect') + .attr('y', one_third_height) + .attr('fill', function(d) { + // leave the ones without mutations uncolored + return d.mutation !== undefined ? config.mutation_fill : 'none'; + }) + .attr('height', one_third_height) + .attr('width', config.rect_width); + + // remove the ones without mutations + mutation.filter(function(d) { + return d.mutation === undefined; + }).remove(); + }; +}; + +exports.gene = gene; diff --git a/packages/oncoprintjs/src/js/rendering_engine.js b/packages/oncoprintjs/src/js/rendering_engine.js new file mode 100644 index 00000000000..6fb17beacd2 --- /dev/null +++ b/packages/oncoprintjs/src/js/rendering_engine.js @@ -0,0 +1,149 @@ +var d3 = require('d3'); +var _ = require('underscore'); +var utils = require('./utils'); +var renderer_functions = require('./renderers'); + +function compute_svg_width(rect_width, rect_padding, row_length) { + return (rect_width + rect_padding) * row_length; +} + +var rendering_engine = function() { + var config = { row_height: 15 }; + var container_width = 100; + var element_padding = 1; + var element_width = 1; + var labels = []; + var svg_height = 95; + + function infer_row_length(container) { + var rows = container.datum(); + + if (rows === undefined) + throw "Cannot infer row length from a container without rows."; + + var is_well_formed_matrix = _.every(rows, function(row) { + return row.length === rows[0].length; + }); + + if (!is_well_formed_matrix) + throw "Uneven rows, cannot infer row length." + + return rows[0].length; + } + + // styles, appends, does all the right stuff to the container + // so that we can go on to work with the inner . + function oncoprint_container_to_svg(container) { + container.style('width', container_width + "px") + .style('display', 'inline-block') + .style('overflow-x', 'auto') + .style('overflow-y', 'hidden'); + + // infer from the data that is already bound to the div. + var row_length = infer_row_length(container) + + return container.append('svg') + .attr('width', compute_svg_width(element_width, element_padding, row_length)) + .attr('height', config.row_height * rows.length); + } + + var me = function(container) { + + container = container.append('table').append('tr') + var label_container = container.append('td') + var oncoprint_container = container.append('td').append('div') + var svg = oncoprint_container_to_svg(oncoprint_container); + + // TODO this was never a good way to bind renderers + // Should probably think more along the line of renderering rules, and perhaps an indicator function + // which tell you when to use which rendering rule. + + // note that this is removing the renderer from each row. +// var renderers = _.map(rows, function(row) { return row.pop(); }); + _.extend(config, { rect_height: 20, + rect_padding: 3, + rect_width: 10, + mutation_fill: 'green', + + cna_fills: { + null: 'grey', + undefined: 'grey', + AMPLIFIED: 'red', + HOMODELETED: 'blue' + } + }); + var gene_renderer = renderer_functions.gene(config); + var renderers = []; + for (var i = 0; i + + +Oncoprint Test Page + + + + +

TP53 MDM2 MDM4 GBM with CNA and mutation data.

+
+ +

Prostate AR Signalling with CNA and mutation data.

+
+ + + + + diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js new file mode 100644 index 00000000000..c0e85c8de7c --- /dev/null +++ b/packages/oncoprintjs/test/js/test_page.js @@ -0,0 +1,21 @@ +var _ = require("underscore"); + +var genomic_oncoprint = require('../../src/js/genomic'); + +// TODO this is dirty. +window.test_for_genomic_data = function(filename, div_selector_string) { + return d3.json(filename, function(data) { + + // break into rows + rows = _.chain(data).groupBy(function(d) { return d.gene; }).values().value(); + + d3.select(div_selector_string).datum(rows); + + var oncoprint = genomic_oncoprint(); + + oncoprint.width(750); + oncoprint.row_height(25); + + d3.select(div_selector_string).call(oncoprint); + }); +}; From 15ad34549cbacae4ec4612b976575e0eed776d33 Mon Sep 17 00:00:00 2001 From: Gideon Dresdner Date: Wed, 18 Mar 2015 20:15:57 +0200 Subject: [PATCH 003/343] Merge pull request #2 from cBioPortal/sample-sorting-pr Sample-wise genomic sorting --- packages/oncoprintjs/src/js/genomic.js | 5 +- packages/oncoprintjs/src/js/renderers.js | 7 +- .../oncoprintjs/src/js/rendering_engine.js | 1 + packages/oncoprintjs/src/js/sorting.js | 67 +++++++++++++++++++ packages/oncoprintjs/test/index.html | 5 ++ packages/oncoprintjs/test/js/test_page.js | 5 +- 6 files changed, 85 insertions(+), 5 deletions(-) create mode 100644 packages/oncoprintjs/src/js/sorting.js diff --git a/packages/oncoprintjs/src/js/genomic.js b/packages/oncoprintjs/src/js/genomic.js index 9d50e06a77f..a6a450e942d 100644 --- a/packages/oncoprintjs/src/js/genomic.js +++ b/packages/oncoprintjs/src/js/genomic.js @@ -27,13 +27,13 @@ function is_sample_genetically_altered(datum) { || datum.mutation !== undefined || datum.rna !== undefined || datum.protein !== undefined; -}; +} function calculate_row_label(row) { var percent_altered = _.filter(row, is_sample_genetically_altered).length / row.length; percent_altered = Math.round(percent_altered*100); return [{align: 'left', text: row[0].gene}, {align: 'right', text: percent_altered + "%"}]; -}; +} function rows_to_labels(rows) { return _.flatten(_.map(rows, calculate_row_label)); @@ -56,6 +56,7 @@ var genomic = function() { if (!arguments.length) return row_height; row_height = value; return me; + }; me.width = function(value) { diff --git a/packages/oncoprintjs/src/js/renderers.js b/packages/oncoprintjs/src/js/renderers.js index 26b99abd006..d21a127e507 100644 --- a/packages/oncoprintjs/src/js/renderers.js +++ b/packages/oncoprintjs/src/js/renderers.js @@ -32,7 +32,12 @@ var gene_rule = function(config) { mutation.filter(function(d) { return d.mutation === undefined; }).remove(); + + // TODO delete me + row_elements.on("click", function(d) { + d3.selectAll('.selected_sample').text(JSON.stringify(d)); + }); }; }; -exports.gene = gene; +exports.gene = gene_rule; diff --git a/packages/oncoprintjs/src/js/rendering_engine.js b/packages/oncoprintjs/src/js/rendering_engine.js index 6fb17beacd2..2c286726235 100644 --- a/packages/oncoprintjs/src/js/rendering_engine.js +++ b/packages/oncoprintjs/src/js/rendering_engine.js @@ -103,6 +103,7 @@ var rendering_engine = function() { return utils.translate(0, i * config.row_height); }) .each(function(d,i) { + // TODO btw this completely defeats the purpose of different renderers... d3.select(this).call(renderers[0]); }) }); diff --git a/packages/oncoprintjs/src/js/sorting.js b/packages/oncoprintjs/src/js/sorting.js new file mode 100644 index 00000000000..c5531e3c0b1 --- /dev/null +++ b/packages/oncoprintjs/src/js/sorting.js @@ -0,0 +1,67 @@ +var _ = require('underscore'); + +var exports = module.exports = {}; + +function copy_array(array) { + return array.slice(); +} + +function rows_to_indexers(rows) { + return _.range(rows.length) + .reverse() // least significant first + .map(function(ith_row) { + return function(index) { return rows[ith_row][index]; }; + }); +} + +exports.genomic_metric = function genomic_metric(x) { + var cna_order = {AMPLIFIED:4, HOMODELETED:3, GAINED:2, HEMIZYGOUSLYDELETED:1, DIPLOID: 0, undefined: 0}; + var regulated_order = {UPREGULATED: 2, DOWNREGULATED: 1, undefined: 0}; + var mutation_order_f = function(m) { + // fusion > non-fusion mutations. + return m === undefined ? 0 : (/fusion($|,)/i.test(m)?2:1); + }; + + // need -1 to flip the order. + return -1 * (1000 * cna_order[x.cna] + + 100 * regulated_order[x.mrna] + + 10 * regulated_order[x.rppa] + + mutation_order_f(x.mutation)); +}; + +// indexers is least significant first. +exports.radixSort = function radixSort(datums, compare, indexers) { + var to_return = copy_array(datums); + + indexers.forEach(function(indexer) { + to_return = _.sortBy(to_return, function(x) { + return compare(indexer(x)); + }); + }); + + return to_return; +}; + +exports.sort_rows = function sort_rows(rows, metric) { + var indexers = rows_to_indexers(rows); + var sorted_column_indices = exports.radixSort(_.range(rows[0].length), metric, indexers); + return _.map(rows, function(row) { + return sorted_column_indices.map(function(i) { return row[i]; }); + }); +}; + +// +// BASIC TESTS +// + +var indexers = [function(d) { return d[4]; }, + function(d) { return d[3]; }, + function(d) { return d[2]; }, + function(d) { return d[1]; }, + function(d) { return d[0]; }]; + +// radixSort +exports.radixSort(["hello", "asdfd", "dafds", "aaafa"], function(x,y) { return x < y; }, indexers); +exports.radixSort(["aaaaa", "bbbbb", "aaaaa", "aaaaa"], function(x,y) { return x < y; }, indexers); +exports.radixSort([], function(x,y) { return x < y; }, indexers); +exports.radixSort(["hello", "asdfd", "dafds", "aaafa"], function(x,y) { return x > y; }, indexers); diff --git a/packages/oncoprintjs/test/index.html b/packages/oncoprintjs/test/index.html index bafde54e888..aa1a117d571 100644 --- a/packages/oncoprintjs/test/index.html +++ b/packages/oncoprintjs/test/index.html @@ -13,9 +13,14 @@ + + Selected:

TP53 MDM2 MDM4 GBM with CNA and mutation data.

+
+ + Selected:

Prostate AR Signalling with CNA and mutation data.

diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index c0e85c8de7c..8aaf9ed724c 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -1,4 +1,5 @@ var _ = require("underscore"); +var sorting = require("../../src/js/sorting"); var genomic_oncoprint = require('../../src/js/genomic'); @@ -8,8 +9,8 @@ window.test_for_genomic_data = function(filename, div_selector_string) { // break into rows rows = _.chain(data).groupBy(function(d) { return d.gene; }).values().value(); - - d3.select(div_selector_string).datum(rows); + sorted_rows = sorting.sort_rows(rows, sorting.genomic_metric); + d3.select(div_selector_string).datum(sorted_rows); var oncoprint = genomic_oncoprint(); From 083c1d32760a303d9dda05c0115b538ee07267f8 Mon Sep 17 00:00:00 2001 From: Gideon Dresdner Date: Wed, 18 Mar 2015 20:26:39 +0200 Subject: [PATCH 004/343] Merge pull request #3 from cBioPortal/test-framework-pr Bring in the Jasmine Testing Framework --- packages/oncoprintjs/gulpfile.js | 10 +++++- packages/oncoprintjs/package.json | 1 + packages/oncoprintjs/src/js/sorting.js | 20 ++---------- packages/oncoprintjs/src/js/utils.js | 6 ++-- .../oncoprintjs/test/spec/sorting_spec.js | 31 +++++++++++++++++++ packages/oncoprintjs/test/spec/utils_spec.js | 15 +++++++++ 6 files changed, 61 insertions(+), 22 deletions(-) create mode 100644 packages/oncoprintjs/test/spec/sorting_spec.js create mode 100644 packages/oncoprintjs/test/spec/utils_spec.js diff --git a/packages/oncoprintjs/gulpfile.js b/packages/oncoprintjs/gulpfile.js index edcd3bf0f9f..a716f8d74c4 100644 --- a/packages/oncoprintjs/gulpfile.js +++ b/packages/oncoprintjs/gulpfile.js @@ -6,6 +6,7 @@ var cache = require('gulp-cache'); var concat = require('gulp-concat'); var del = require('del'); var gulp = require('gulp'); +var jasmine = require('gulp-jasmine'); var jshint = require('gulp-jshint'); var livereload = require('gulp-livereload'); var minifycss = require('gulp-minify-css'); @@ -15,8 +16,15 @@ var source = require('vinyl-source-stream'); var streamify = require('gulp-streamify'); var uglify = require('gulp-uglify'); +gulp.task('spec', function() { + return gulp.src('test/spec/*.js') + .pipe(jasmine()); +}); + // Test Page gulp.task('test', function() { + // Unit tests + gulp.start('spec'); // JavaScript browserify({entries: './test/js/test_page.js', @@ -62,5 +70,5 @@ gulp.task('default', ['clean'], function() { // Watch gulp.task('watch', function() { - gulp.watch(['src/js/**/*.js', 'test/*.html', 'test/js/**/*.js'], ['test']); + gulp.watch(['src/js/**/*.js', 'test/*.html', 'test/js/**/*.js', 'test/spec/*.js'], ['test']); }); diff --git a/packages/oncoprintjs/package.json b/packages/oncoprintjs/package.json index 231173b4526..55a2456ec8c 100644 --- a/packages/oncoprintjs/package.json +++ b/packages/oncoprintjs/package.json @@ -37,6 +37,7 @@ "gulp-autoprefixer": "^2.1.0", "gulp-cache": "^0.2.4", "gulp-concat": "^2.4.3", + "gulp-jasmine": "^2.0.0", "gulp-jshint": "^1.9.0", "gulp-livereload": "^3.4.0", "gulp-minify-css": "^0.4.0", diff --git a/packages/oncoprintjs/src/js/sorting.js b/packages/oncoprintjs/src/js/sorting.js index c5531e3c0b1..f26c343afd8 100644 --- a/packages/oncoprintjs/src/js/sorting.js +++ b/packages/oncoprintjs/src/js/sorting.js @@ -30,7 +30,7 @@ exports.genomic_metric = function genomic_metric(x) { }; // indexers is least significant first. -exports.radixSort = function radixSort(datums, compare, indexers) { +exports.radix_sort = function radix_sort(datums, compare, indexers) { var to_return = copy_array(datums); indexers.forEach(function(indexer) { @@ -44,24 +44,8 @@ exports.radixSort = function radixSort(datums, compare, indexers) { exports.sort_rows = function sort_rows(rows, metric) { var indexers = rows_to_indexers(rows); - var sorted_column_indices = exports.radixSort(_.range(rows[0].length), metric, indexers); + var sorted_column_indices = exports.radix_sort(_.range(rows[0].length), metric, indexers); return _.map(rows, function(row) { return sorted_column_indices.map(function(i) { return row[i]; }); }); }; - -// -// BASIC TESTS -// - -var indexers = [function(d) { return d[4]; }, - function(d) { return d[3]; }, - function(d) { return d[2]; }, - function(d) { return d[1]; }, - function(d) { return d[0]; }]; - -// radixSort -exports.radixSort(["hello", "asdfd", "dafds", "aaafa"], function(x,y) { return x < y; }, indexers); -exports.radixSort(["aaaaa", "bbbbb", "aaaaa", "aaaaa"], function(x,y) { return x < y; }, indexers); -exports.radixSort([], function(x,y) { return x < y; }, indexers); -exports.radixSort(["hello", "asdfd", "dafds", "aaafa"], function(x,y) { return x > y; }, indexers); diff --git a/packages/oncoprintjs/src/js/utils.js b/packages/oncoprintjs/src/js/utils.js index 8d7d771f6c4..4825ee16923 100644 --- a/packages/oncoprintjs/src/js/utils.js +++ b/packages/oncoprintjs/src/js/utils.js @@ -1,5 +1,5 @@ -var translate = function(x,y) { +var exports = module.exports = {}; + +exports.translate = function translate(x,y) { return "translate(" + x + "," + y + ")"; } - -exports.translate = translate; diff --git a/packages/oncoprintjs/test/spec/sorting_spec.js b/packages/oncoprintjs/test/spec/sorting_spec.js new file mode 100644 index 00000000000..3188ec8daee --- /dev/null +++ b/packages/oncoprintjs/test/spec/sorting_spec.js @@ -0,0 +1,31 @@ +"use strict"; + +var sorting = require('../../src/js/sorting.js'); + +var indexers = [function(d) { return d[4]; }, + function(d) { return d[3]; }, + function(d) { return d[2]; }, + function(d) { return d[1]; }, + function(d) { return d[0]; }]; + +describe("radix sorting", function() { + it("sorts an empty list (guess what, it's already sorted...)", function() { + expect(sorting.radix_sort([], function(x) { return x; }, indexers).length).toEqual([].length); + }); + + it("sorts some strings of equal length", function() { + expect(sorting.radix_sort(["hello", "asdfd", "dafds", "aaafa"], + function(x) { return x; }, indexers)) + .toEqual(["aaafa", "asdfd", "dafds", "hello"]); + + expect(sorting.radix_sort(["aaaaa", "bbbbb", "aaaaa", "aaaaa"], + function(x) { return x; }, indexers)) + .toEqual(["aaaaa", "aaaaa", "aaaaa", "bbbbb"]); + }); + + it("sorts strings backwards when the indicator function is negative", function() { + expect(sorting.radix_sort(["hello", "asdfd", "dafds", "aaafa"], + function(x) { return -1 * x.charCodeAt(0); }, indexers)) + .toEqual(["hello", "dafds", "asdfd", "aaafa"]); + }); +}); diff --git a/packages/oncoprintjs/test/spec/utils_spec.js b/packages/oncoprintjs/test/spec/utils_spec.js new file mode 100644 index 00000000000..2cf6ff712cc --- /dev/null +++ b/packages/oncoprintjs/test/spec/utils_spec.js @@ -0,0 +1,15 @@ +"use strict"; +var utils = require('../../src/js/utils.js'); + +describe("translate function", function() { + it("does the appropriate string manipulation", function() { + expect(utils.translate(10,42)).toBe("translate(10,42)"); + expect(utils.translate(-10,42)).toBe("translate(-10,42)"); + }); +}); + +describe("This test fails", function() { + it("FAIL", function() { + expect(true).toBe(true); + }); +}); From c80cc2b19bdb9e45c9580b988995875e8ee0791d Mon Sep 17 00:00:00 2001 From: Gideon Dresdner Date: Mon, 30 Mar 2015 13:01:20 +0300 Subject: [PATCH 005/343] Merge pull request #5 from cBioPortal/readme updated README with build instructions for devs. --- packages/oncoprintjs/README.md | 40 +++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/packages/oncoprintjs/README.md b/packages/oncoprintjs/README.md index 6ad0df10ba5..b6447225af2 100644 --- a/packages/oncoprintjs/README.md +++ b/packages/oncoprintjs/README.md @@ -1,4 +1,42 @@ oncoprint.js ============ -OncoPrint visualization +Home of the OncoPrint visualization as used on the [cBioPortal](www.cbioportal.org). + +## Development + +### Getting Started + +1. Install `nodejs`. `npm` is bundled together with `nodejs`. `nodejs` is almost + certainly provided by your favorite package manager or you can find it + [here][nodejs]. Here is a [blog post][install-npm] from people at nodejs + with a bit more detail. +2. Install gulp globally: +```sh + npm install --global gulp +``` +This is not exactly a perfect solution to getting the gulp binary into your +`PATH` but it seems to be a standard followed by many. Not to fear, gulp knows +how to resolve version mismatch between global and local (i.e. what is +specified in `package.json`) versions. + +3. Run `npm install` to install the dependencies as described in + `package.json`. +4. Run `gulp test` to both run unit tests and also build the `dist/test` directory + containing artifacts for development and testing. +5. Run your favorite lightweight static resource server from `dist/test`. I use + [http-server][http-server] because I've found it to be faster and more reliable than + `python -m SimpleHttpServer`. + +You should be able to navigate your browser to wherever your local server is +running ([http://localhost:8080](http://localhost:8080) is likely) and see some sample OncoPrints. + +While developing, I recommend using + +```gulp watch``` + +to rebuild the project every time a relevant file is modified. + +[nodejs]:https://nodejs.org/ +[http-server]:https://github.com/indexzero/http-server +[install-npm]:http://blog.npmjs.org/post/85484771375/how-to-install-npm From 4751f6dc3d5dff4cbb14e8ccf311b4c5f809eb04 Mon Sep 17 00:00:00 2001 From: Gideon Dresdner Date: Mon, 13 Apr 2015 10:51:44 +0300 Subject: [PATCH 006/343] Merge pull request #4 from cBioPortal/clinical-data-pr Lays out how clinical data will get added to the OncoPrint. The first clinical data type to get implemented is gender data. --- packages/oncoprintjs/src/js/genomic.js | 75 ++- packages/oncoprintjs/src/js/renderers.js | 111 +++-- .../oncoprintjs/src/js/rendering_engine.js | 187 ++++--- packages/oncoprintjs/src/js/utils.js | 62 +++ .../oncoprintjs/test/data/gbm/gender-gbm.json | 467 ++++++++++++++++++ .../data/{ => gbm}/tp53-mdm2-mdm4-gbm.json | 0 packages/oncoprintjs/test/index.html | 4 +- packages/oncoprintjs/test/js/test_page.js | 61 ++- packages/oncoprintjs/test/spec/utils_spec.js | 103 +++- 9 files changed, 902 insertions(+), 168 deletions(-) create mode 100644 packages/oncoprintjs/test/data/gbm/gender-gbm.json rename packages/oncoprintjs/test/data/{ => gbm}/tp53-mdm2-mdm4-gbm.json (100%) diff --git a/packages/oncoprintjs/src/js/genomic.js b/packages/oncoprintjs/src/js/genomic.js index a6a450e942d..18e28e754a6 100644 --- a/packages/oncoprintjs/src/js/genomic.js +++ b/packages/oncoprintjs/src/js/genomic.js @@ -7,65 +7,48 @@ var utils = require('./utils'); var rendering_engine = rendering_engine(); -var config = { rect_height: 20, - rect_padding: 3, - rect_width: 10, - mutation_fill: 'green', - - cna_fills: { - null: 'grey', - undefined: 'grey', - AMPLIFIED: 'red', - HOMODELETED: 'blue' - } -}; - -var gene_renderer = renderers.gene(config); - -function is_sample_genetically_altered(datum) { - return datum.cna !== undefined - || datum.mutation !== undefined - || datum.rna !== undefined - || datum.protein !== undefined; -} - -function calculate_row_label(row) { - var percent_altered = _.filter(row, is_sample_genetically_altered).length / row.length; - percent_altered = Math.round(percent_altered*100); - return [{align: 'left', text: row[0].gene}, {align: 'right', text: percent_altered + "%"}]; -} - -function rows_to_labels(rows) { - return _.flatten(_.map(rows, calculate_row_label)); -} - -var genomic = function() { - var row_height = 25; - var width = 500; +module.exports = function genomic() { + var config = {}; + var rendering_rules = []; var me = function(container) { - rendering_engine.config({row_height: row_height}); - rendering_engine.container_width(width); + rendering_engine.config(config); + rendering_engine.container_width(config.width); rendering_engine.element_width(config.rect_width); rendering_engine.element_padding(config.rect_padding); - rendering_engine.labels(rows_to_labels(rows)); + rendering_engine.label_function(rows_to_labels); + rendering_engine.renderers(rendering_rules); container.call(rendering_engine); }; - me.row_height = function(value) { - if (!arguments.length) return row_height; - row_height = value; + me.config = function(value) { + if (!arguments.length) return config; + config = value; return me; - }; - me.width = function(value) { - if (!arguments.length) return width; - width = value; + // expose this function + me.insert_row = rendering_engine.insert_row; + + me.rendering_rules = function(value) { + if (!arguments.length) return rendering_rules; + rendering_rules = value; return me; }; return me; }; -module.exports = genomic; +// +// HELPER FUNCTIONS +// + +function calculate_row_label(row) { + var percent_altered = _.filter(row, utils.is_sample_genetically_altered).length / row.length; + percent_altered = Math.round(percent_altered*100); + return [{align: 'left', text: row[0].gene}, {align: 'right', text: percent_altered + "%"}]; +} + +function rows_to_labels(rows) { + return _.flatten(_.map(rows, calculate_row_label)); +} diff --git a/packages/oncoprintjs/src/js/renderers.js b/packages/oncoprintjs/src/js/renderers.js index d21a127e507..1c42b482b94 100644 --- a/packages/oncoprintjs/src/js/renderers.js +++ b/packages/oncoprintjs/src/js/renderers.js @@ -1,43 +1,92 @@ var utils = require('./utils'); +var exports = module.exports = {}; -var gene_rule = function(config) { - return function(selection) { - var row_elements = selection.selectAll('g') - // binds the row-wise data to the row group, . See Bostock's - // explaination on nested selections: http://bost.ocks.org/mike/nest/#data - .data(function(d) { return d; }) - .enter().append('g'); - - row_elements.attr('transform', function(d, i) { - return utils.translate(i * (config.rect_width + config.rect_padding), 0); - }); - - row_elements.append('rect') - .attr('fill', function(d) { return config.cna_fills[d.cna]; }) - .attr('height', config.rect_height) - .attr('width', config.rect_width); +exports.continuous_data_rule = function continuous_data_rule(config) { +}; - var one_third_height = config.rect_height / 3; +exports.discrete_data_rule = function discrete_data_rule(config) { +}; - var mutation = row_elements.append('rect') - .attr('y', one_third_height) +exports.gender_rule = function gender_rule(config) { + return function(selection) { + selection.selectAll('rect') + .data(function(d) { return d; }) + .enter() + .append('rect') + .attr('x', function(d, i) { + return i * (config.rect_width + config.rect_padding); + }) .attr('fill', function(d) { - // leave the ones without mutations uncolored - return d.mutation !== undefined ? config.mutation_fill : 'none'; + if (d.attr_val === "MALE") + return 'black'; + if (d.attr_val === "FEMALE") + return 'pink'; + return 'grey'; }) - .attr('height', one_third_height) + .attr('height', config.rect_height) .attr('width', config.rect_width); - // remove the ones without mutations - mutation.filter(function(d) { - return d.mutation === undefined; - }).remove(); + update(selection.selectAll('rect')); + }; +}; + +exports.gene_rule = function gene_rule(config) { + return function(selection) { + var sample_group = bind_sample_group(selection); + align_sample_group_horizontally(sample_group, config.rect_width, config.rect_padding); + cna_visualization(sample_group, config.cna_fills, config.rect_width, config.rect_height); + mutation_visualization(sample_group, config.rect_height / 3, config.rect_width, config.mutation_fill); - // TODO delete me - row_elements.on("click", function(d) { - d3.selectAll('.selected_sample').text(JSON.stringify(d)); - }); + update(sample_group); }; }; -exports.gene = gene_rule; +// +// HELPER FUNCTIONS +// + +function align_sample_group_horizontally(sample_group, rect_width, rect_padding) { + return sample_group.attr('transform', function(d, i) { + return utils.translate(i * (rect_width + rect_padding), 0); + }); +} + +function bind_sample_group(selection) { + // binds the row-wise data to the row group, . See Bostock's + // explaination on nested selections: http://bost.ocks.org/mike/nest/#data + return selection.selectAll('g') + .data(function(d) { return d; }) + .enter().append('g'); +} + +// copy number alteration "subrule" +function cna_visualization(sample_group, cna_fills, rect_width, rect_height) { + return sample_group.append('rect') + .attr('fill', function(d) { return cna_fills[d.cna]; }) + .attr('height', rect_height) + .attr('width', rect_width); +} + +// mutation "subrule" +function mutation_visualization(sample_group, one_third_height, width, fill) { + var mutation = sample_group.append('rect') + .attr('y', one_third_height) + .attr('fill', function(d) { + // leave the ones without mutations uncolored + return d.mutation !== undefined ? fill : 'none'; + }) + .attr('height', one_third_height) + .attr('width', width); + + // remove the ones without mutations + mutation.filter(function(d) { + return d.mutation === undefined; + }).remove(); +} + +// TODO dev only +function update(sample_group) { + sample_group.on("click", function(d) { + d3.selectAll('.selected_sample').text(JSON.stringify(d)); + }); +} diff --git a/packages/oncoprintjs/src/js/rendering_engine.js b/packages/oncoprintjs/src/js/rendering_engine.js index 2c286726235..cfa34d1de6c 100644 --- a/packages/oncoprintjs/src/js/rendering_engine.js +++ b/packages/oncoprintjs/src/js/rendering_engine.js @@ -3,43 +3,120 @@ var _ = require('underscore'); var utils = require('./utils'); var renderer_functions = require('./renderers'); -function compute_svg_width(rect_width, rect_padding, row_length) { - return (rect_width + rect_padding) * row_length; -} - -var rendering_engine = function() { +module.exports = function rendering_engine() { var config = { row_height: 15 }; var container_width = 100; var element_padding = 1; var element_width = 1; - var labels = []; - var svg_height = 95; + var label_function = undefined; + var renderers = []; + + var me = function(container) { + + container = container.append('table').append('tr') + var label_container = container.append('td') + var oncoprint_container = container.append('td').append('div') + var svg = create_svg_for_container(oncoprint_container); + + var element_height = 20; + + // TODO! + label_container.append('svg').append('g').selectAll('text') + .data(label_function(container.datum())) + .enter() + .append('text') + .attr('text-anchor', function(d) { + return d.align === 'right' ? 'end' : 'start'; + }) + .attr('x', function(d) { return d.align === 'right' ? 50 : 0 }) + .attr('y', function(d, i) { + return (element_padding + 20 - 12 / 2) + i * 1.5 * (element_padding + 20 - 12 / 2); + }) + .attr('font-size', '12px') + .append('tspan') + .text(function(d) { return d.text; }) + + var bind_renderers_to_config = _.map(renderers, function(r) { + return r(config); + }); + + svg.selectAll('g') + .data(svg.data()[0], function(d) { + return oncoprint_key_function(d[0]); + }) + .enter().append('g') + .attr('transform', function(d,i) { + return utils.translate(0, i * config.row_height); + }) + .each(function(d,i) { + d3.select(this).call(bind_renderers_to_config[i]); + }) + .attr('class', 'oncoprint-row'); + }; + + me.insert_row = function(container, row, rendering_rule) { + var internal_data = container.datum(); + + utils.validate_row_against_rows(row, internal_data); + + var svg = get_svg_from_container(container); + + // make the svg one row taller + svg.attr('height', parseInt(svg.attr('height')) + config.row_height); + + // slide the current rows down + svg.selectAll('.oncoprint-row') + .attr('transform', function(d, i) { + return utils.translate(0, config.row_height + (i * config.row_height)); + }); + + // update the data which is bound to the container + internal_data.unshift(row); + container.datum(internal_data); + + // use d3 to detect which row is new and use the rendering function to render. + svg.selectAll('.oncoprint-row') + .data(svg.data()[0], function(d) { + return oncoprint_key_function(d[0]) + }) + .enter() + .append('g') + .attr('transform', utils.translate(0,0)) + .each(function(d,i) { + d3.select(this).call(rendering_rule(config)) + }) + }; + + // + // HELPER FUNCTIONS + // + + function compute_svg_width(rect_width, rect_padding, row_length) { + return (rect_width + rect_padding) * row_length; + } function infer_row_length(container) { var rows = container.datum(); - - if (rows === undefined) - throw "Cannot infer row length from a container without rows."; + if (rows === undefined) throw "Cannot infer row length from a container without rows."; var is_well_formed_matrix = _.every(rows, function(row) { return row.length === rows[0].length; }); - if (!is_well_formed_matrix) - throw "Uneven rows, cannot infer row length." - + if (!is_well_formed_matrix) throw "Uneven rows, cannot infer row length." return rows[0].length; } // styles, appends, does all the right stuff to the container // so that we can go on to work with the inner . - function oncoprint_container_to_svg(container) { + function create_svg_for_container(container) { container.style('width', container_width + "px") .style('display', 'inline-block') .style('overflow-x', 'auto') .style('overflow-y', 'hidden'); // infer from the data that is already bound to the div. + var rows = container.datum(); var row_length = infer_row_length(container) return container.append('svg') @@ -47,70 +124,17 @@ var rendering_engine = function() { .attr('height', config.row_height * rows.length); } - var me = function(container) { - - container = container.append('table').append('tr') - var label_container = container.append('td') - var oncoprint_container = container.append('td').append('div') - var svg = oncoprint_container_to_svg(oncoprint_container); - - // TODO this was never a good way to bind renderers - // Should probably think more along the line of renderering rules, and perhaps an indicator function - // which tell you when to use which rendering rule. - - // note that this is removing the renderer from each row. -// var renderers = _.map(rows, function(row) { return row.pop(); }); - _.extend(config, { rect_height: 20, - rect_padding: 3, - rect_width: 10, - mutation_fill: 'green', - - cna_fills: { - null: 'grey', - undefined: 'grey', - AMPLIFIED: 'red', - HOMODELETED: 'blue' - } - }); - var gene_renderer = renderer_functions.gene(config); - var renderers = []; - for (var i = 0; i Prostate AR Signalling with CNA and mutation data.
diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index 8aaf9ed724c..08fe7cc9bce 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -1,22 +1,71 @@ var _ = require("underscore"); + +var renderers = require("../../src/js/renderers"); var sorting = require("../../src/js/sorting"); var genomic_oncoprint = require('../../src/js/genomic'); +var config = { rect_height: 20, + rect_padding: 3, + rect_width: 10, + row_height: 25, + mutation_fill: 'green', + width: 750, + cna_fills: { + null: 'grey', + undefined: 'grey', + AMPLIFIED: 'red', + HOMODELETED: 'blue' + } +}; + // TODO this is dirty. -window.test_for_genomic_data = function(filename, div_selector_string) { - return d3.json(filename, function(data) { +window.test_for_genomic_data = function(filenames, div_selector_string) { + // filenames has length 2. + var genomic_file = filenames[0]; + var additional_file = filenames[1]; + // genomic data + return d3.json(genomic_file, function(data) { // break into rows - rows = _.chain(data).groupBy(function(d) { return d.gene; }).values().value(); - sorted_rows = sorting.sort_rows(rows, sorting.genomic_metric); + var rows = _.chain(data).groupBy(function(d) { return d.gene; }).values().value(); + var sorted_rows = sorting.sort_rows(rows, sorting.genomic_metric); d3.select(div_selector_string).datum(sorted_rows); var oncoprint = genomic_oncoprint(); - oncoprint.width(750); - oncoprint.row_height(25); + oncoprint.config(config); + + var rendering_rules = _.map(rows, function(row) { + // at the cBioPortal OncoPrints always start as just genomic data. + return renderers.gene_rule; + }); + oncoprint.rendering_rules(rendering_rules); d3.select(div_selector_string).call(oncoprint); + + // additional clinical data if it has been specified. + if (additional_file !== undefined) { + d3.json(additional_file, function(payload) { + var gender_data = payload.data; + + gender_data = _.sortBy(gender_data, function(d) { + // grab the sorted sampleids + var sampleids = sorted_rows[0].map(function(d) { + return d.sample_id || d.sample; + }); + + // sort the new gender data based on the previous sorting + var sampleid_to_array_index = sampleids.reduce(function(curr, next, index) { + curr[next] = index; + return curr; + }, {}); + + return sampleid_to_array_index[d.sample_id || d.sample]; + }); + + oncoprint.insert_row(d3.select(div_selector_string), gender_data, renderers.gender_rule); + }); + } }); }; diff --git a/packages/oncoprintjs/test/spec/utils_spec.js b/packages/oncoprintjs/test/spec/utils_spec.js index 2cf6ff712cc..7e9f6d04713 100644 --- a/packages/oncoprintjs/test/spec/utils_spec.js +++ b/packages/oncoprintjs/test/spec/utils_spec.js @@ -8,8 +8,105 @@ describe("translate function", function() { }); }); -describe("This test fails", function() { - it("FAIL", function() { - expect(true).toBe(true); +describe("is sample genetically altered", function() { + it("shows that an empty sample (no data) is not genetically altered", function() { + expect(utils.is_sample_genetically_altered({})).toBe(false); + }); + + it("shows that a clinical-ish sample is not genetically altered", function() { + expect(utils.is_sample_genetically_altered({sample_id: "ABC123", + attr_id: "MY_SPECIAL_ATTR", + attr_val: 42})).toBe(false); + }); + + it("shows that a genetically altered sample as altered", function() { + expect(utils.is_sample_genetically_altered({sample_id: "ABC123", + gene: "TP53", + cna: "AMPLIFIED"})).toBe(true); + }); +}); + + + +describe("validate_row_against_rows", function() { + it("error if lengths don't match", function() { + (function() { + // row.length < rows[0].length + var row = [2, 2]; + var rows = [[3, 3, 3], [3, 3, 3]]; + expect(function() { + utils.validate_row_against_rows(row, rows); + }).toThrow("Row lengths don't match: 2 and 3"); + })(); + + (function() { + // row.length > rows[0].length + var row = [3,3,3]; + var rows = [[2,2], [2,2]]; + expect(function() { + utils.validate_row_against_rows(row, rows); + }).toThrow("Row lengths don't match: 3 and 2"); + })(); + + (function() { + // rows is the empty matrix + var row = [3,3,3]; + var rows = []; + expect(function() { + utils.validate_row_against_rows(row, rows); + }).toThrow("Rows are empty"); + })(); + + }); + + it("error if samples don't match up exactly", function() { + (function() { + var row = [ + {sample_id: "A"}, + {sample_id: "Z"}, + {sample_id: "C"} + ]; + + var rows = [[{sample_id: "A"}, {sample_id: "B"}, {sample_id: "C"}], + [{sample_id: "A"}, {sample_id: "B"}, {sample_id: "C"}], + [{sample_id: "A"}, {sample_id: "B"}, {sample_id: "C"}]]; + + expect(function() { + utils.validate_row_against_rows(row, rows); + }).toThrow("Sample ids do not match between new row and given rows."); + })(); + + (function() { + var row = [ + {sample_id: "A"}, + {sample_id: "B"}, + {sample_id: "C"} + ]; + + var rows = [[{sample_id: "A"}, {sample_id: "Z"}, {sample_id: "C"}], + [{sample_id: "A"}, {sample_id: "Z"}, {sample_id: "C"}], + [{sample_id: "A"}, {sample_id: "Z"}, {sample_id: "C"}]]; + + expect(function() { + utils.validate_row_against_rows(row, rows); + }).toThrow("Sample ids do not match between new row and given rows."); + })(); + + }); + + it("returns true if all is well", function() { + (function() { + var row = [ + {sample_id: "B"}, + {sample_id: "A"}, // order shouldn't matter. + {sample_id: "C"} + ]; + + var rows = [[{sample_id: "A"}, {sample_id: "B"}, {sample_id: "C"}], + [{sample_id: "A"}, {sample_id: "B"}, {sample_id: "C"}], + [{sample_id: "A"}, {sample_id: "B"}, {sample_id: "C"}]]; + + expect(utils.validate_row_against_rows(row, rows)).toBe(true); + })(); }); }); From f0efb93e23f2a3a17046f849a3c5ca7c3e7d13c9 Mon Sep 17 00:00:00 2001 From: Gideon Dresdner Date: Mon, 13 Apr 2015 11:00:48 +0300 Subject: [PATCH 007/343] Merge pull request #6 from cBioPortal/transitions-interface Transitions interface --- packages/oncoprintjs/src/js/genomic.js | 14 ++++++++++ packages/oncoprintjs/src/js/renderers.js | 26 +++++++++++++++++-- .../oncoprintjs/src/js/rendering_engine.js | 1 + packages/oncoprintjs/src/js/utils.js | 1 + packages/oncoprintjs/test/index.html | 1 + packages/oncoprintjs/test/js/test_page.js | 20 +++++++++++--- 6 files changed, 58 insertions(+), 5 deletions(-) diff --git a/packages/oncoprintjs/src/js/genomic.js b/packages/oncoprintjs/src/js/genomic.js index 18e28e754a6..f2600fd7596 100644 --- a/packages/oncoprintjs/src/js/genomic.js +++ b/packages/oncoprintjs/src/js/genomic.js @@ -36,6 +36,20 @@ module.exports = function genomic() { return me; }; + me.resort = function(container, sample_id_to_array_index) { + var row_groups = container.selectAll('.oncoprint-row'); + row_groups = row_groups[0].map(d3.select); + utils.assert(row_groups.length === rendering_rules.length, + "Rows don't matchup with rendering rules."); + row_groups = row_groups.reverse(); + + _.each(_.zip(row_groups, rendering_rules), function(row_group_and_rr) { + var row_group = row_group_and_rr[0]; + var rr = row_group_and_rr[1]; + rr(config).resort(row_group, sample_id_to_array_index); + }); + }; + return me; }; diff --git a/packages/oncoprintjs/src/js/renderers.js b/packages/oncoprintjs/src/js/renderers.js index 1c42b482b94..ab4ba92adac 100644 --- a/packages/oncoprintjs/src/js/renderers.js +++ b/packages/oncoprintjs/src/js/renderers.js @@ -8,7 +8,7 @@ exports.discrete_data_rule = function discrete_data_rule(config) { }; exports.gender_rule = function gender_rule(config) { - return function(selection) { + var ret = function(selection) { selection.selectAll('rect') .data(function(d) { return d; }) .enter() @@ -28,10 +28,21 @@ exports.gender_rule = function gender_rule(config) { update(selection.selectAll('rect')); }; + + ret.resort = function(selection, sample_order) { + selection.selectAll('rect') + .transition(function(d, i) { return i; }) + .attr('x', function(d, i) { + return sample_order[d.sample_id || d.sample] * + (config.rect_width + config.rect_padding); + }); + }; + + return ret; }; exports.gene_rule = function gene_rule(config) { - return function(selection) { + var ret = function(selection) { var sample_group = bind_sample_group(selection); align_sample_group_horizontally(sample_group, config.rect_width, config.rect_padding); cna_visualization(sample_group, config.cna_fills, config.rect_width, config.rect_height); @@ -39,6 +50,17 @@ exports.gene_rule = function gene_rule(config) { update(sample_group); }; + + ret.resort = function(selection, sample_order) { + selection.selectAll('g') + .transition(function(d, i) { return i; }) + .attr('transform', function(d, i) { + return utils.translate(sample_order[d.sample_id || d.sample] + * (config.rect_width + config.rect_padding), 0); + }); + } + + return ret; }; // diff --git a/packages/oncoprintjs/src/js/rendering_engine.js b/packages/oncoprintjs/src/js/rendering_engine.js index cfa34d1de6c..03f4e62b7a8 100644 --- a/packages/oncoprintjs/src/js/rendering_engine.js +++ b/packages/oncoprintjs/src/js/rendering_engine.js @@ -81,6 +81,7 @@ module.exports = function rendering_engine() { }) .enter() .append('g') + .attr('class', 'oncoprint-row') .attr('transform', utils.translate(0,0)) .each(function(d,i) { d3.select(this).call(rendering_rule(config)) diff --git a/packages/oncoprintjs/src/js/utils.js b/packages/oncoprintjs/src/js/utils.js index 46e3acbdc0b..f8ca78c2a70 100644 --- a/packages/oncoprintjs/src/js/utils.js +++ b/packages/oncoprintjs/src/js/utils.js @@ -61,6 +61,7 @@ function assert(bool, msg) { if (bool) return; throw msg; } +exports.assert = assert; function pluck_sample_id(datum) { return datum.sample || datum.sample_id; diff --git a/packages/oncoprintjs/test/index.html b/packages/oncoprintjs/test/index.html index ddd70d7f6ce..f18989180c5 100644 --- a/packages/oncoprintjs/test/index.html +++ b/packages/oncoprintjs/test/index.html @@ -17,6 +17,7 @@ Selected:

TP53 MDM2 MDM4 GBM with CNA and mutation data.

+
diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index 08fe7cc9bce..ec8e62bb472 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -30,7 +30,7 @@ window.test_for_genomic_data = function(filenames, div_selector_string) { // break into rows var rows = _.chain(data).groupBy(function(d) { return d.gene; }).values().value(); var sorted_rows = sorting.sort_rows(rows, sorting.genomic_metric); - d3.select(div_selector_string).datum(sorted_rows); + var container = d3.select(div_selector_string).datum(sorted_rows); var oncoprint = genomic_oncoprint(); @@ -42,7 +42,7 @@ window.test_for_genomic_data = function(filenames, div_selector_string) { }); oncoprint.rendering_rules(rendering_rules); - d3.select(div_selector_string).call(oncoprint); + container.call(oncoprint); // additional clinical data if it has been specified. if (additional_file !== undefined) { @@ -64,7 +64,21 @@ window.test_for_genomic_data = function(filenames, div_selector_string) { return sampleid_to_array_index[d.sample_id || d.sample]; }); - oncoprint.insert_row(d3.select(div_selector_string), gender_data, renderers.gender_rule); + // update the list of renderers + rendering_rules.unshift(renderers.gender_rule); + oncoprint.rendering_rules(rendering_rules); + + oncoprint.insert_row(container, gender_data, renderers.gender_rule); + + d3.select('#shuffle-gbm').on('click', function() { + var sampleids = rows[0].map(function(d) { return d.sample_id || d.sample; }); + var shuffled_sampleids = d3.shuffle(sampleids); + var sampleid_to_array_index = shuffled_sampleids.reduce(function(curr, next, index) { + curr[next] = index; + return curr; + }, {}); + oncoprint.resort(container, sampleid_to_array_index); + }); }); } }); From 78be2b0efd712855f7b49e08bfbc95c200df5960 Mon Sep 17 00:00:00 2001 From: Gideon Dresdner Date: Thu, 16 Apr 2015 10:23:04 +0300 Subject: [PATCH 008/343] Merge pull request #7 from cBioPortal/expose-api-pr Create a main script with clean programmer API. --- packages/oncoprintjs/gulpfile.js | 34 ++-- packages/oncoprintjs/src/js/genomic.js | 68 -------- packages/oncoprintjs/src/js/main.js | 183 ++++++++++++++++++++++ packages/oncoprintjs/src/js/utils.js | 7 + packages/oncoprintjs/test/index.html | 7 +- packages/oncoprintjs/test/js/test_page.js | 61 ++------ 6 files changed, 221 insertions(+), 139 deletions(-) delete mode 100644 packages/oncoprintjs/src/js/genomic.js create mode 100644 packages/oncoprintjs/src/js/main.js diff --git a/packages/oncoprintjs/gulpfile.js b/packages/oncoprintjs/gulpfile.js index a716f8d74c4..82e4dcc314d 100644 --- a/packages/oncoprintjs/gulpfile.js +++ b/packages/oncoprintjs/gulpfile.js @@ -23,19 +23,19 @@ gulp.task('spec', function() { // Test Page gulp.task('test', function() { - // Unit tests - gulp.start('spec'); - // JavaScript browserify({entries: './test/js/test_page.js', - debug: true + debug: true, + standalone: 'test_script' }).bundle() .pipe(source('test.js')) - .pipe(rename('test_page_bundle.js')) + .pipe(rename('test-oncoprint-bundle.js')) .pipe(gulp.dest('dist/test/')) - .pipe(streamify(uglify())) .pipe(notify("Done with building code for testing.")) + // Unit tests + gulp.start('spec'); + // Copy over the HTML. gulp.src('test/index.html') .pipe(gulp.dest('dist/test/')); @@ -45,18 +45,14 @@ gulp.task('test', function() { .pipe(gulp.dest('dist/test/')); }); -// JavaScript -// TODO need to figure out what to compile other than test code... -//gulp.task('js', function() { -// browserify({entries: './src/js/main.js', -// debug: !process.env.production -// }).bundle() -// .pipe(source('genomic.js')) -// .pipe(rename('genomic-oncoprint-bundle.js')) -// .pipe(gulp.dest('dist/asset/js')) -// .pipe(streamify(uglify())) -// .pipe(notify("Done with JavaScript.")) -//}); +gulp.task('prod', function() { + browserify('./src/js/main.js', + {standalone: 'oncoprint'}).bundle() + .pipe(source('oncoprint-bundle.js')) + .pipe(streamify(uglify())) + .pipe(gulp.dest('dist/prod/')) + .pipe(notify("Done with generating production code.")); +}); // Clean gulp.task('clean', function(cb) { @@ -65,7 +61,7 @@ gulp.task('clean', function(cb) { // Default gulp.task('default', ['clean'], function() { - gulp.start('js'); + gulp.start('prod'); }); // Watch diff --git a/packages/oncoprintjs/src/js/genomic.js b/packages/oncoprintjs/src/js/genomic.js deleted file mode 100644 index f2600fd7596..00000000000 --- a/packages/oncoprintjs/src/js/genomic.js +++ /dev/null @@ -1,68 +0,0 @@ -var d3 = require('d3'); -var _ = require('underscore'); - -var renderers = require('./renderers'); -var rendering_engine = require('./rendering_engine'); -var utils = require('./utils'); - -var rendering_engine = rendering_engine(); - -module.exports = function genomic() { - var config = {}; - var rendering_rules = []; - - var me = function(container) { - rendering_engine.config(config); - rendering_engine.container_width(config.width); - rendering_engine.element_width(config.rect_width); - rendering_engine.element_padding(config.rect_padding); - rendering_engine.label_function(rows_to_labels); - rendering_engine.renderers(rendering_rules); - container.call(rendering_engine); - }; - - me.config = function(value) { - if (!arguments.length) return config; - config = value; - return me; - }; - - // expose this function - me.insert_row = rendering_engine.insert_row; - - me.rendering_rules = function(value) { - if (!arguments.length) return rendering_rules; - rendering_rules = value; - return me; - }; - - me.resort = function(container, sample_id_to_array_index) { - var row_groups = container.selectAll('.oncoprint-row'); - row_groups = row_groups[0].map(d3.select); - utils.assert(row_groups.length === rendering_rules.length, - "Rows don't matchup with rendering rules."); - row_groups = row_groups.reverse(); - - _.each(_.zip(row_groups, rendering_rules), function(row_group_and_rr) { - var row_group = row_group_and_rr[0]; - var rr = row_group_and_rr[1]; - rr(config).resort(row_group, sample_id_to_array_index); - }); - }; - - return me; -}; - -// -// HELPER FUNCTIONS -// - -function calculate_row_label(row) { - var percent_altered = _.filter(row, utils.is_sample_genetically_altered).length / row.length; - percent_altered = Math.round(percent_altered*100); - return [{align: 'left', text: row[0].gene}, {align: 'right', text: percent_altered + "%"}]; -} - -function rows_to_labels(rows) { - return _.flatten(_.map(rows, calculate_row_label)); -} diff --git a/packages/oncoprintjs/src/js/main.js b/packages/oncoprintjs/src/js/main.js new file mode 100644 index 00000000000..bbdf5d90252 --- /dev/null +++ b/packages/oncoprintjs/src/js/main.js @@ -0,0 +1,183 @@ +var d3 = require('d3'); +var _ = require('underscore'); + +var renderers = require('./renderers'); +var rendering_engine = require('./rendering_engine'); +var sorting = require('./sorting'); +var utils = require('./utils'); + +module.exports = function() { + var cna_fills = { + AMPLIFIED: 'red', + HOMODELETED: 'blue', + null: 'grey', + undefined: 'grey' + }; + var rect_height = 20; + var rect_padding = 3; + var rect_width = 10; + var rendering_rules = []; + var row_height = 25; + var mutation_fill = 'green'; + var width = 750; + + var engine = rendering_engine(); + + var me = function(container_selector_string, data) { + var container = prepare_container(d3.select(container_selector_string), data) + + engine.config(get_config()); + engine.container_width(width); + engine.element_width(rect_width); + engine.element_padding(rect_padding); + engine.label_function(rows_to_labels); + engine.renderers(rendering_rules_or_default(container)); + container.call(engine); + }; + +// me.insert_row = engine.insert_row; + + me.insert_row = function(container_selector_string, row, rendering_rule) { + var container = d3.select(container_selector_string); + + // grab the sorted sampleids + var sampleids = container.datum()[0].map(function(d) { + return d.sample_id || d.sample; + }); + + var sampleid_to_array_index = utils.invert_array(sampleids); + + // sort the new gender data based on the previous sorting + var sorted_data = _.sortBy(row, function(d) { + return sampleid_to_array_index[d.sample_id || d.sample]; + }); + + // update the list of renderers + rendering_rules.unshift(renderers.gender_rule); + engine.renderers(rendering_rules); + + engine.insert_row(container, sorted_data, rendering_rule); + } + + me.resort = function(container_selector_string, sample_id_to_array_index) { + var container = d3.select(container_selector_string); + + var row_groups = container.selectAll('.oncoprint-row'); + row_groups = row_groups[0].map(d3.select); + utils.assert(row_groups.length === rendering_rules.length, + "Rows don't matchup with rendering rules."); + row_groups = row_groups.reverse(); + + _.each(_.zip(row_groups, rendering_rules), function(row_group_and_rr) { + var row_group = row_group_and_rr[0]; + var rr = row_group_and_rr[1]; + rr(get_config()).resort(row_group, sample_id_to_array_index); + }); + }; + + // + // getters and setters + // + + me.cna_fills = function(value) { + if (!arguments.length) return cna_fills; + cna_fills = value; + return me; + }; + + me.rect_height = function(value) { + if (!arguments.length) return rect_height; + rect_height = value; + return me; + }; + + me.rect_padding = function(value) { + if (!arguments.length) return rect_padding; + rect_padding = value; + return me; + }; + + me.rect_width = function(value) { + if (!arguments.length) return rect_width; + rect_width = value; + return me; + }; + + me.rendering_rules = function(value) { + if (!arguments.length) return rendering_rules; + rendering_rules = value; + return me; + }; + + me.row_height = function(value) { + if (!arguments.length) return row_height; + row_height = value; + return me; + }; + + me.mutation_fill = function(value) { + if (!arguments.length) return mutation_fill; + mutation_fill = value; + return me; + }; + + me.width = function(value) { + if (!arguments.length) return width; + width = value; + return me; + }; + + // + // HELPER FUNCTIONS + // + + function calculate_row_label(row) { + var percent_altered = _.filter(row, utils.is_sample_genetically_altered).length / row.length; + percent_altered = Math.round(percent_altered*100); + return [{align: 'left', text: row[0].gene}, {align: 'right', text: percent_altered + "%"}]; + } + + function rendering_rules_or_default(container) { + if (rendering_rules.length === 0) { + rendering_rules = _.map(container.datum(), function(row) { + return renderers.gene_rule; + }); + } + + return rendering_rules; + } + + function get_config() { + return { + cna_fills: cna_fills, + rect_height: rect_height, + rect_padding: rect_padding, + rect_width: rect_width, + rendering_rules: rendering_rules, + row_height: row_height, + mutation_fill: mutation_fill, + width: width + }; + } + + function rows_to_labels(rows) { + return _.flatten(_.map(rows, calculate_row_label)); + } + + function prepare_container(container, data) { + var rows = _.chain(data).groupBy(function(d) { return d.gene; }).values().value(); + var sorted_rows = sorting.sort_rows(rows, sorting.genomic_metric); + container.datum(sorted_rows); + return container; + }; + + + return me; +}; + + + + + + + diff --git a/packages/oncoprintjs/src/js/utils.js b/packages/oncoprintjs/src/js/utils.js index f8ca78c2a70..818af3ff84c 100644 --- a/packages/oncoprintjs/src/js/utils.js +++ b/packages/oncoprintjs/src/js/utils.js @@ -2,6 +2,13 @@ var _ = require("underscore"); var exports = module.exports = {}; +exports.invert_array = function invert_array(arr) { + return arr.reduce(function(curr, next, index) { + curr[next] = index; + return curr; + }, {}); +}; + exports.is_sample_genetically_altered = function is_sample_genetically_altered(datum) { return datum.cna !== undefined || datum.mutation !== undefined diff --git a/packages/oncoprintjs/test/index.html b/packages/oncoprintjs/test/index.html index f18989180c5..0b201ab3396 100644 --- a/packages/oncoprintjs/test/index.html +++ b/packages/oncoprintjs/test/index.html @@ -10,7 +10,7 @@ /* ]]> */ --> - + @@ -26,8 +26,9 @@

Prostate AR Signalling with CNA and mutation data.

diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index ec8e62bb472..bbb7dc9ea0b 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -1,9 +1,8 @@ var _ = require("underscore"); +var OncoPrint = require('../../src/js/main'); var renderers = require("../../src/js/renderers"); -var sorting = require("../../src/js/sorting"); - -var genomic_oncoprint = require('../../src/js/genomic'); +var utils = require("../../src/js/utils"); var config = { rect_height: 20, rect_padding: 3, @@ -19,65 +18,29 @@ var config = { rect_height: 20, } }; -// TODO this is dirty. -window.test_for_genomic_data = function(filenames, div_selector_string) { +module.exports = function test_script(filenames, div_selector_string) { // filenames has length 2. var genomic_file = filenames[0]; var additional_file = filenames[1]; // genomic data return d3.json(genomic_file, function(data) { - // break into rows - var rows = _.chain(data).groupBy(function(d) { return d.gene; }).values().value(); - var sorted_rows = sorting.sort_rows(rows, sorting.genomic_metric); - var container = d3.select(div_selector_string).datum(sorted_rows); - - var oncoprint = genomic_oncoprint(); - - oncoprint.config(config); - - var rendering_rules = _.map(rows, function(row) { - // at the cBioPortal OncoPrints always start as just genomic data. - return renderers.gene_rule; - }); - oncoprint.rendering_rules(rendering_rules); - - container.call(oncoprint); + var oncoprint = OncoPrint(); + oncoprint(div_selector_string, data); // additional clinical data if it has been specified. if (additional_file !== undefined) { d3.json(additional_file, function(payload) { - var gender_data = payload.data; - - gender_data = _.sortBy(gender_data, function(d) { - // grab the sorted sampleids - var sampleids = sorted_rows[0].map(function(d) { - return d.sample_id || d.sample; - }); - - // sort the new gender data based on the previous sorting - var sampleid_to_array_index = sampleids.reduce(function(curr, next, index) { - curr[next] = index; - return curr; - }, {}); - - return sampleid_to_array_index[d.sample_id || d.sample]; - }); - - // update the list of renderers - rendering_rules.unshift(renderers.gender_rule); - oncoprint.rendering_rules(rendering_rules); - - oncoprint.insert_row(container, gender_data, renderers.gender_rule); + oncoprint.insert_row(div_selector_string, payload.data, renderers.gender_rule); d3.select('#shuffle-gbm').on('click', function() { - var sampleids = rows[0].map(function(d) { return d.sample_id || d.sample; }); + // get and shuffle order + var container = d3.select(div_selector_string); + var sampleids = container.datum()[0].map(function(d) { return d.sample_id || d.sample; }); var shuffled_sampleids = d3.shuffle(sampleids); - var sampleid_to_array_index = shuffled_sampleids.reduce(function(curr, next, index) { - curr[next] = index; - return curr; - }, {}); - oncoprint.resort(container, sampleid_to_array_index); + + var sampleid_to_array_index = utils.invert_array(shuffled_sampleids); + oncoprint.resort(div_selector_string, sampleid_to_array_index); }); }); } From c836035dc23710730c0b8fb588573ce16d7a20d9 Mon Sep 17 00:00:00 2001 From: Gideon Dresdner Date: Thu, 16 Apr 2015 10:24:44 +0300 Subject: [PATCH 009/343] Merge pull request #8 from cBioPortal/readme Bring README up to date. --- packages/oncoprintjs/README.md | 43 +++++++++++++++++----------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/packages/oncoprintjs/README.md b/packages/oncoprintjs/README.md index b6447225af2..d4f4ef068e3 100644 --- a/packages/oncoprintjs/README.md +++ b/packages/oncoprintjs/README.md @@ -3,39 +3,38 @@ oncoprint.js Home of the OncoPrint visualization as used on the [cBioPortal](www.cbioportal.org). -## Development +## Latest Build + +Download the latest build here (TODO). -### Getting Started +## Setup -1. Install `nodejs`. `npm` is bundled together with `nodejs`. `nodejs` is almost - certainly provided by your favorite package manager or you can find it - [here][nodejs]. Here is a [blog post][install-npm] from people at nodejs - with a bit more detail. +1. Install `nodejs`. `npm` is bundled together with `nodejs`. `nodejs` is almost certainly provided by your favorite package manager or you can find it [here][nodejs]. Here is a [blog post][install-npm] from people at nodejs with a bit more detail. 2. Install gulp globally: ```sh npm install --global gulp ``` -This is not exactly a perfect solution to getting the gulp binary into your -`PATH` but it seems to be a standard followed by many. Not to fear, gulp knows -how to resolve version mismatch between global and local (i.e. what is -specified in `package.json`) versions. - -3. Run `npm install` to install the dependencies as described in - `package.json`. -4. Run `gulp test` to both run unit tests and also build the `dist/test` directory - containing artifacts for development and testing. -5. Run your favorite lightweight static resource server from `dist/test`. I use - [http-server][http-server] because I've found it to be faster and more reliable than - `python -m SimpleHttpServer`. - -You should be able to navigate your browser to wherever your local server is -running ([http://localhost:8080](http://localhost:8080) is likely) and see some sample OncoPrints. +This is not exactly a perfect solution to getting the gulp binary into your `PATH` but it seems to be a standard followed by many. Not to fear, gulp knows how to resolve version mismatch between global and local (i.e. what is specified in `package.json`) versions. + +3. Run `npm install` to install the dependencies as described in `package.json`. + +## Build + +Run `gulp spec && gulp` to run unit tests and generate a minified artifact in `dist/prod`. + +## Tests + +Run `gulp spec` for unit tests. + +Run `gulp test` to run both unit tests and to build development environment in `dist/test`. Run your favorite lightweight static resource server from `dist/test`. I use [http-server][http-server] because I've found it to be faster and more reliable than `python -m SimpleHttpServer`. You should be able to navigate your browser to wherever your local server is running ([http://localhost:8080](http://localhost:8080) is likely) and see some sample OncoPrints. + +## Development While developing, I recommend using ```gulp watch``` -to rebuild the project every time a relevant file is modified. +This will run `gulp test` every time a source file is modified. [nodejs]:https://nodejs.org/ [http-server]:https://github.com/indexzero/http-server From 82c5fbd7d8e23cb175ef86a41bbbd1a6416d96f6 Mon Sep 17 00:00:00 2001 From: Gideon Dresdner Date: Thu, 16 Apr 2015 10:57:51 +0300 Subject: [PATCH 010/343] insert gender data button - This created a bug when you resorted oncoprint before inserting the row. - The top level container, the parent of the table, is what has the freshest data. Nothing wrong with that. What happens below with the nested selections and whatnot is a different issue. And yet more TODOs --- packages/oncoprintjs/src/js/main.js | 26 ++++++++-------- .../oncoprintjs/src/js/rendering_engine.js | 2 +- packages/oncoprintjs/src/js/utils.js | 12 +++++++ packages/oncoprintjs/test/index.html | 1 + packages/oncoprintjs/test/js/test_page.js | 31 +++++++++++++------ 5 files changed, 48 insertions(+), 24 deletions(-) diff --git a/packages/oncoprintjs/src/js/main.js b/packages/oncoprintjs/src/js/main.js index bbdf5d90252..307661573a5 100644 --- a/packages/oncoprintjs/src/js/main.js +++ b/packages/oncoprintjs/src/js/main.js @@ -40,28 +40,27 @@ module.exports = function() { me.insert_row = function(container_selector_string, row, rendering_rule) { var container = d3.select(container_selector_string); - // grab the sorted sampleids - var sampleids = container.datum()[0].map(function(d) { - return d.sample_id || d.sample; - }); - - var sampleid_to_array_index = utils.invert_array(sampleids); - - // sort the new gender data based on the previous sorting - var sorted_data = _.sortBy(row, function(d) { - return sampleid_to_array_index[d.sample_id || d.sample]; - }); + var sorted_row = utils.sort_row_by_rows(row, container.datum()); // update the list of renderers rendering_rules.unshift(renderers.gender_rule); engine.renderers(rendering_rules); - engine.insert_row(container, sorted_data, rendering_rule); + engine.insert_row(container, sorted_row, rendering_rule); } me.resort = function(container_selector_string, sample_id_to_array_index) { + // TODO this function should live more in the rendering_engine than here. + var container = d3.select(container_selector_string); + var resorted_rows = container.datum().map(function(row) { + return _.sortBy(row, function(d) { + return sample_id_to_array_index[d.sample || d.sample_id]; + })}); + + container.datum(resorted_rows); + var row_groups = container.selectAll('.oncoprint-row'); row_groups = row_groups[0].map(d3.select); utils.assert(row_groups.length === rendering_rules.length, @@ -164,6 +163,8 @@ module.exports = function() { return _.flatten(_.map(rows, calculate_row_label)); } + // reorganize the flat data into a list of sorted rows + // bind those rows to the container using .datum() function prepare_container(container, data) { var rows = _.chain(data).groupBy(function(d) { return d.gene; }).values().value(); var sorted_rows = sorting.sort_rows(rows, sorting.genomic_metric); @@ -171,7 +172,6 @@ module.exports = function() { return container; }; - return me; }; diff --git a/packages/oncoprintjs/src/js/rendering_engine.js b/packages/oncoprintjs/src/js/rendering_engine.js index 03f4e62b7a8..2d7910781f8 100644 --- a/packages/oncoprintjs/src/js/rendering_engine.js +++ b/packages/oncoprintjs/src/js/rendering_engine.js @@ -76,7 +76,7 @@ module.exports = function rendering_engine() { // use d3 to detect which row is new and use the rendering function to render. svg.selectAll('.oncoprint-row') - .data(svg.data()[0], function(d) { + .data(internal_data, function(d) { return oncoprint_key_function(d[0]) }) .enter() diff --git a/packages/oncoprintjs/src/js/utils.js b/packages/oncoprintjs/src/js/utils.js index 818af3ff84c..84c850eae58 100644 --- a/packages/oncoprintjs/src/js/utils.js +++ b/packages/oncoprintjs/src/js/utils.js @@ -16,6 +16,18 @@ exports.is_sample_genetically_altered = function is_sample_genetically_altered(d || datum.protein !== undefined; }; +exports.sort_row_by_rows = function(row, rows) { + // TODO test this + var ordering = exports.invert_array( + rows[0].map(function(d) { + return d.sample || d.sample_id; + })); + + return _.sortBy(row, function(d) { + return ordering[d.sample || d.sample_id]; + }); +}; + exports.translate = function translate(x,y) { return "translate(" + x + "," + y + ")"; }; diff --git a/packages/oncoprintjs/test/index.html b/packages/oncoprintjs/test/index.html index 0b201ab3396..78bbf7ce8f5 100644 --- a/packages/oncoprintjs/test/index.html +++ b/packages/oncoprintjs/test/index.html @@ -18,6 +18,7 @@

TP53 MDM2 MDM4 GBM with CNA and mutation data.

+
diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index bbb7dc9ea0b..47e7dfa3a2b 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -29,20 +29,31 @@ module.exports = function test_script(filenames, div_selector_string) { oncoprint(div_selector_string, data); // additional clinical data if it has been specified. + var is_inserted = false; + // TODO is_inserted is a hack. At some point, + // should add a remove row function too and test these together. if (additional_file !== undefined) { d3.json(additional_file, function(payload) { - oncoprint.insert_row(div_selector_string, payload.data, renderers.gender_rule); + d3.select("#insert-gender-data-gbm").on('click', function() { + if (!is_inserted) { + oncoprint.insert_row(div_selector_string, payload.data, renderers.gender_rule); + is_inserted = true; + } + }); + }); + } - d3.select('#shuffle-gbm').on('click', function() { - // get and shuffle order - var container = d3.select(div_selector_string); - var sampleids = container.datum()[0].map(function(d) { return d.sample_id || d.sample; }); - var shuffled_sampleids = d3.shuffle(sampleids); + // shuffle order button + if (genomic_file.indexOf("gbm") !== -1) + d3.select('#shuffle-gbm').on('click', function() { + // get and shuffle order + var container = d3.select(div_selector_string); + var sampleids = container.datum()[0].map(function(d) { return d.sample_id || d.sample; }); + var shuffled_sampleids = d3.shuffle(sampleids); - var sampleid_to_array_index = utils.invert_array(shuffled_sampleids); - oncoprint.resort(div_selector_string, sampleid_to_array_index); - }); + var sampleid_to_array_index = utils.invert_array(shuffled_sampleids); + oncoprint.resort(div_selector_string, sampleid_to_array_index); }); - } + }); }; From 57bb3df220033452eb606acb57d8c1c71322a9c7 Mon Sep 17 00:00:00 2001 From: Gideon Dresdner Date: Wed, 22 Apr 2015 13:37:13 +0300 Subject: [PATCH 011/343] added naming conventions picture --- .../oncoprint-naming-conventions.jpg | Bin 0 -> 22519 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 packages/oncoprintjs/oncoprint-naming-conventions.jpg diff --git a/packages/oncoprintjs/oncoprint-naming-conventions.jpg b/packages/oncoprintjs/oncoprint-naming-conventions.jpg new file mode 100644 index 0000000000000000000000000000000000000000..452adad64e025bc1b1b7de17308873824dd2929a GIT binary patch literal 22519 zcmeHu1yEewmhQnJ5G=SuH}38pECf$*ch_LS2@;_30D<7{?(U5fBoN#!xI?hVotc{X z?|m}w&8t`Szgz$5T3vnW?Cv_>K5Kt#t#9pq`uVg9V7`%-lLla50RZOt1w1VPk^nLy zA`&73G7=IJ3JUTIG@O@csHkX!*sm~fNQubFNQp>DD8MYV6jV$g5)wLIdL}k@E-o%| zT7F?Z4j~p!E{?zb2n-4e3K}XJ!ONEf9F!!K9RJ71Qzw9h3`+vn2?s+3z+%C`VZl80 z0_4y4i2(C22k>tP3@jWx0wNMJ$_v!z0d<%FEDRhREIb?n0zCZlXrJfn06Z1~HYK|_ z;wu#+Bq}Eyj)2%KWROHv7q05W37FH^IS}Oq9zFpf5j71h9X$gVHxDl#zkuXxDQOv5 zxi@O>-m7b9YH6F8nweWzT3Ne%aCLL{@cj5GC^#hab69v>{Fj8pq_4>-**Up+`2~eV z#nm;nb@dI6P0igsy?y=Afx)54sp%gxvp?tN*VZ>Sx3+h7_kNw8onKsDUEkdP{u?eB z0PbI5J-`1euz$dX^^6M^9v%)J>2J7TVBMb$4htTEk{uCSTm{L<=@k`605Xn5Y*tkl z3W!tn1lQPk;sqX$2ZsfS0@uIkvi)ED zW&h(jU<|(r182Wv!AaMQ(+MOWc5b@$DT%$qco1IpTb0yEgN)J{ZfB`pf82FlJ9+Pf z&2M+w zSHVT0R!T*46T0sUf|zKk12vkad4;T@_zKhE-yShX1s{D~G45YjuPpRJW7B+ahdkJc zmUncjUnzijuEkBTn~V`x)O3l!4K+)n+*@-lP892IKy5K8FfWooBpiclqDM7G&&9hU9gLbLaI6gTBctr`T7NBY4G`a)~$=*9lLKm0r1Q1 z2I>%!EgP~lDHjpJxYsAUnp<(A9NShsqb_hAp2Es?N)KH=B|Ono1S*G*mNd-(2X)P+;rMrLU)n8f)SdUp+=La@fQk4-D9_%G}96fauR(cxfjsY=3X?Hc5S>k9~e2lY=tYcch}9 z9NsYc3&gYvN`X4l&N?H~lVjbWtoe=9n%B$H7>&9?M1JJk&nk=r33S-i*qLUYlm3|B zUy=@Op8(6aEtZArGu!i69>#;ETGCcB{?0K+24VvwOE;I>%6Yl7(g+diL|e?r@Q1^)ENKtA*Ia*H~DuII)S{Q(OJnKh2iecqsMa z3ydVyU&k$ci*O<)n3=TEQgie0$Qy)XaP0bQ|IxUjBZSM!&aTX~N+WQ7@PJC%jndFD zAZGJCmdM%JV}G!Ss|>bBCeF%E-7UrJooBhdX}Uy7T1m4st>vPlfU}P!oMJd#COT-H z_P(oQf2-qyXve1NG(F6(RNQ#}WAB;Hh;C_g0|i8b`w2iE4;S4T8-IKPW=D6l+(5Y6 z9J%Fj$J<_nZY&8e?77L+MEq{9lOB5ROLCup6wxQZ-e9KMpnI9lV@?EW&I>C#JNy3O zllv3!;t9BW#r{xn&~C7N>+afblLPKj@9wv*t~)SGHny5|`mj99y7vT}y{vlz%IWWe zwaT60)^C{CgRHbYu`-nv26y0aY11|^e#1YHy1}+-n@H{(3%yZ$lq3T^0To?OK;|r} z$rHe8aCy%97-aLkl{Md*N7dS0;EN-advjf!M5z%5;#w~Mh((UAGwxWqFfT{^_Ndgn zU!4D<+;#y)<5kGJJ;s)t92k8Z8~)Kf<2>Dy@%w0=jLSo6@)O8S&ZGCi6L9=sTMy^c1ELgq{*+8o#(x zl(x*nCthTb6OI(1%dX@6_ei|SA|zSV0gNGk`rT{irMaOg(s9y}???T-@UdUW%YpHB8;P8d=NZ6~ z2*AX46qXwlRh8o}q64zS|IT2lMU!4Ns`kB0%PG1l=b7qD;^kG>cDr-p{?W+nNMO!S z1jln23Y@b(K_REZEnFaKE@8dD^gC)DR1!h(^{H9wC$} z9=mT#k;hYg{fSQ>BTfj9ghX&j8U=9VpZYvRUXI{PPvk{Oe`Xl0=N6@b0lW5K^Kt|t zUzH@1(KlvDw)`-YozPyNGuF~Uft5!?lV>9dwj$U$*=Uf-U-q-yb4$t+s&aZEU&Fu9 zB;N0JS@I61*RR{k)5Ysb*3M9+9c#N!pk0z+(6Inb`Iv#&A?vvJ+(sa?;bc177;pQB zdGmZRku(i1ltGLzCY{aW7h`3%KbsJ3k-oLtt6?viYxN-ddHiD?iJgKwMd)eSZ@Ns_ z=Y{{zYVc<0d77}fhM$(33O|hEttdvF3D#ra)*Cl-@Xu$V+3<`Uf+_p&Ka88`fy1_6 zpT>!qfk1_$&MF@2znFR6zqFnjw5DP>K{SgIqr5d8$6B>tfSh@5h6A&$4HE|+EX(ZW z^S;K>WxtD)7+UoYc$3x{n3K=`>G-|*lsCVTkQ~dUl{QDAMMAx_UNS{b zzERz#?;9;s*)_4>nOsoBWvf!LJ$H^>hTZx34z=*wQ4?vxj<~^bvdf^ahgYy58)rn+ z^bT{=W20anW%EUIYs@6%$0oyfPJD%J@y1FHQOSK z5eJE_CYl6nIiiSqkv0KKckkO#&euEba*s*KmC6_v#dc7+Y#q_z;JWz8%bXIQ0Q3nw zgIWV9r*KmcmQ5`qj_ESK6L7zT4;RI*!E%oiud77eHmzQ)JL<5t@bYB1>b>(q8 z;Hoo{kTR0=^-PfqZi`}|*1KkUQ4d&{2Q5DVFWt`YgDGiT^jYc7P z)4^5T>2)lqkB=JEa&QH*&ss8|h-7&B38XiTVssZscZ9DlNwI~aP$3=MnHnDwV~;m( z!jS<`ARs<`clRHO>(6oyQ$h7Qc+>SYsa6zF-1BZ~V4%Go^w`3BGs}4@z;AY9J-eb{1Sb6HtN0Zq!WZDcWqQG9qbc*rENUkCM_?sQr~HJlYw?GOWP)uJ{9^ z(%l>Aqo3V#82t4FM0yBE_T{DvCJtRu$C|tI`Kk6=oI`i?>Sh9R^KwV(*%6U@Nj~sp zV!TmD%lZE8N^Z$gaq5Q$0I!Us=%Bcz{3XIR5>7 zKU#s?sD<~#o*T)WkrRzZ8HE)XQ#q66-VUDm-#)O@QJM_i%8v7o`$X!k^bEu;pBn^H zFW|Vpz;*fV{X>7~OVNbJ=-T8A(zv^I8b8Fwe-eWj@HiJ zM~QT+bPSvEtKRrYseej(yZZ_Jq$uR#a7^h~=8XG7%H~aR37M(mHUZ(PP4g?OI=j~H zaJn*dihl^E|LBu2)edJF!xVD0hvRRr%JG(`l;d8C$p0)~Pzq!|Tbr^jnA0NR*zrsn zH6KT+(4BP!-8u;dGrgCa7s#;QDD{5t{DVGY$t^xH48~1o?Ftf!$7(@@7V!QAaPnCQX`M z>70p-5n&VM36&LZMpDqN#Lkb<@BUg%Q{07I4`^CYzLxqa{UwO1OdSmsbIUb(GDl=u zlaSXW)lD^Fq9pD}G5Pi2w&Z!0T!_i05gdUc!KZ*J{t*i(FYGDvWdE|+B2pUZWKb$T z!&{xt%(ZBhz(zqEj{&ZA?4Xs~X?Kk5=jxu8&B!@Pdke;x;_7}m=t&*$raqlXcFmKv zDW)0AiAWC9D2%dG!V!jX$0ehev~fJVr{C@BRA(;Y^LwgTjx&iRW9Nd3m)kf8GL0nf zp{GkD4mc)nwcdWKm&eKNe*y%h2$9I46k@_~pW>s;D^DY@atfhVWNEO3EJ6G>G5zfI z=9E92-&bKsM(L3E#KGic*JrwZyYnp9w;|@B3X$ou(h96vbmV_)5;Zzb)vzD`!&{Ff z!`%zbQM;)j7KGJ$XWHZG*JdaUi@|m#d)MZ<#zl^4@T>X4X(XG`lX)wCNVObB#i2Ho zROXl@%Jb!+7^xkOiWtJ5#?kyqky%FmVj>) zUY1)-DaUJYl)5-A8WFvB)4PD!9^5YY9qzJ!2SU=&i*bwp>al(8Ih`&p)4mCK0;tyg zW*0?&T zlknlVCm%;Tsc^(+^vots*keMd+Dq6o5V}{`&{Ic38cBKrBr0@I&?~te;CXadN$g_x zVqBhpXv9nmg%gMSu-^s`PUIuAtcBPeSlp^5O~J*O!isp!(TGpLKt|qkJ%x2Y>Vn^K zrgACz`zrdD`NP^HQO4SX%3Sb`^%D?!Df4Z2nRM3n-h6(sD&GDe3wrJ7YpkSn?Cbig z9cD-M6#v1y;&D`WG%>lj&&h;ylp+v0fkqtkaxCw@rv_f+OdP3b+!pQ7n=wSk!&TE< zwYqD3}pPq9DYrlvK8I_akM;Qgd$L8pL1F6}u3=F@`4^ticg^zU?dTxdt zfzhe!ez$~56TY&~(#yVx_kSk9NK?LxPlZWGv`I9;y>Y6YE1NL4(VTpr2o4xe3@;vy z5qMcmOLo&Qjids$;!?s1CnSASoxXZmu0OQUZ7x$?Kb*XFw%{FCY5IZ0!6Zr*7nB~3 zym|)X;ZgBM0=|q3WdH-rR1(vrX+bbl!;s+;1;r6vqxpVpuTZ=o$6cR=T|0TJ0I3Xn z#|I^O^J$?&GQvGYBQ$qfWY87;_IOen3qK}HZ(vLe$=a-*1Y-I-#kDLtw0(s+p;qoH z!6J4-{*ooGYFw%HG@}=$vk%Y^5k9&8Y1)c!`(36kd4@ww9X{kYW?9q@LBB^Cs=`Oq zL$d>laMdCbh=mzLsmc|bZj_@l=i#n+am89RhP-Y45yTk2v*c>fSNUC0q^~bNAu$r! z^aA}ozj;z~Uuh{e_OM`LLLffnz&rlUICLCmr83~dyZ5LH=H0(H7rHnJ{h3TG=a2zP zel#SjSLY=cfQl5(pTt}wp#n1VrjZ*RQ8>*uJS|@QLIDf|CZLgiV3}w0GzHkREO*=K z|L9Yn!vDIZ$xMB1~UImiIQ(dd6izGmo0l(N&;{FWyXA9Ij-Z5niLM^zJ?7iZc@ z9T?CJfyDcBAgPHFCwwd3t^Cz!5xee0EY$>?@g>22_3Q;bQsH5E)NF5x*>a?Nap^c| zTly1FhmvFJ@dB;V6+{s*{ymV7TR5b~q{wo@`nw|D*lX5p`7>KMilgx*ia_*_hTq|d z6a(yXZO?KO)nB`+QNjnF5t2M;eICEdt}Bj;E}65LH`R_xV@sJ-cOvn}B(}i?dzmTd zKNnnmm8Vi6d4+NoHZSiyo#n6^d7P1XrTa8dWak~W>~j^j3^;Qu1*hU7=4}&5Ui47skV-<`aN}8&ZbFAZBIJ1*#ZfMCxvcv(l&W=d+yqHsop)h+!LDh&yfX6Slf? z`|6_O_qu3ux$*Vm8SV@)aYwRaZ=DQKc6*1uUF=m}T; zKzL!EVT~-8iyzU>^%qV&?&_d$i z=m|-sZIYfffz!G9*WFk&yF%wR3_@RrATH;K6i(0~1y}4pq=*&5^=&O$wzlQ^2MeB} zd&=dD$RYy^*A*)(I~GiQgK+!|8~bv!l-+8f<7Rl`?_)2cRdG^kYccG1Lg zwVP;$qVVThoZ23fl$OjEX?Z%zW}fLZkP#D~&M4%bF9$t1tA$`a{2D{(L!OR=zZc-Kw2zcBRRQ<7BFGy z%M?K&+ewR_35}&Cq)q?YFL3x`#*3L~d3nuk=vxEYrypUzgd+jVi9ZSTYD6t*=?g~{ zSU8Y}5vx7UH8nO=+t4FG3Jz^|^+t@|ni_?zJmMa_Q$|W1Uu*k#u6!CimR$Txz^*r` z@Tv+ZLKwjN4%sX}z!9_9?#WZWbMRS8+$~-mtIEQy0Z}sQyI25({at|BD zj;iU)GJLH-;ugVV#4NO``RavuN)b~J<4(-|4)~1A$Q1lv~f(Iw12Y8K; zWj|?cbF1XB|9Tlh>m|xyPI7HfJ5Lq+nsFYPikJ`BxbU){vOG<3!rd z7Mh+8NLYYqEi+13qaRNW1sklQQ&N3l9u8Lj9XKTIHPg`t_|>ipx86Ph7nF)O8hJi7 z8jVe6x5kk33*zWs_~+$4nZKjE{4tQM3^8s^GNxzQpSg~j{5d96SUv%9vr7L{22U03 zQM(r{7G&#qX>jsbI8k_y@B~nbV)@MuJpl(lsAX1JZ&7X{9^0SwZ}aTvlj4u8qln}; z+&=h^Um(X2(KEQ}XPjmCC4G$ZkI#+30rcU==7zHhoQjMQ5$;`|4x}fbdHQ)(??&aU z9p6W#xIeg(&$LHWq=}F{kKQZrn^k$R*=;`P@RJ&+^tccmFj1Thy(@{2WUM^y=0sj`2u#5`coH0z4**jhSFMz9I9-GRlJyNForT~ z?(`)g!QIA0vU+*ztX6D*=y`p4-h7b&lOG2)$`yCx&KBx0^ zL`#O8$=aG4gFaV&DZhOyElm=cL%sF#M`rqAAFe4n zBkur)?0m$pJbIMEe@!ySd%CLI8!610As=D#a)i@t!E!9nYJ zP2Ior$=mDd)tDEVs%5K;lusKsxDWA89@6LO-0jc4(>Tq-;qLfSJQu8j4!`dr{#MM& zWwSv(gd^aRMn^x=`3*?0{yr9c3}S4$QOJEr>TYm{G+G(kk{6Djq3AmFdJyfX*9C~& zC#h!M>);-=lUM4Ac2)J3G<-n2xdKH`;Yten{ZC8X$Cpn)tmt!QQx3XK*=F_I%=zVa zc}RYyz1MTkE!y)trv~-p#}k0@E0XAsX`AXhU)X2r%lQ1yFZ6(I#!bhI!aYy!%dqiM;rc&B4|*p2NdMUY3lU zN=7DZO~m$$|7VxDvQ`+}rhXbbR86~98PS`FIk;8>OXk(B#tW4VtF$ll<3pKoINs3U z&f-ZA4e1nu^|%>n%^4~3J#5o3_Ez?##IYY^E|c|^nP@I+KBN(NwdP}vL{`X(a!jxdh}~%@)za_ed8-RLIyZ8L}6c(TMaqdU~f6F z9kKO{peClD<3}Q3;SUnfKLkdbY%8eTgdG!+I7pm8V%3DEwRyi=1l025keksH7i1ZK z7)`Gi#t`z#9w|PFkO1EB!6pZTuH3T0ro@--SN9NOI)~e~`n}o%dVS6b{(aEB)DvL9 z_yp9b+b66=6V%6=oBm+N8lihLOt{>NPQ10hf2_wEbT8zVO>Ahq;wC$t92cx3q57VZegfl$pyo3xF6&7~?5|5j<{xga4WxU^)M=e-| z0{f2^bxs1l8D1|T8qRglpSe=_m5HivjlYM2eGVTjmAx)6x#veI!%@I+)HwB=kW~#- zj40Nl^q)NliRKTp3EM}AlHAP}A2%_xjTI+dqH;2UiMHszm*^qP_v754pyg&RFRcdJ zugJz2ODhB8`pI3Thms1XrYW!M%cz$I(mRo`hF^5dYxAISI0{ud#RyAtaH_}myT=d3 z=*-QxR`am$u%C&@ea-b}#kfn)IP@~@eB^FxNn9qDWnPPOiwv@3*)t)xYJi05I+B`$ z$={M^BDPl{EdHKE{w@$yZ`lC6LuistO+8Huj5I^%5^-OF*-J-cM&9JuZv>X>d3FkZ^mk@IZ1r>-IB%~-c2SH*si(3r^vjpfFm9!T4h$vu`g}c!Y1y5c!DjLVCBC%`VGsDLOq9$B zeS;UjRDNZfCpij!&A*P=I%g}?A#s2TD*=v-);2mdWpeKJn5;kFs??AtjV#5+&f<

lvw`HQP zrHxfRd|fU-`C!wA!%T{O5sPC@wqHbAe4IQ=L7(WM_Yv!HsX;SBqI%#Y@Xc_|$HSKk z+rXwh2_^@+btE*B#nvp@o@q7y;I8#%^-D77#cptTY3X%X zvTc693n%?2-Wvcv&x_YNNI;YLUyD&N@v@YGa_DT~IvGZq87E15^!ZRPraqPFbes*! zSmZ&tn_QLnsHXLLmMohH{xuooN$CNC^3`031tDQ6@&{J*1)Lz+N27p{%a&NN_w1n{ z8z87Mny=qa0H|)K3DMDM`COJq@8E*aaf~?nX!$v$i;w_23V9qi-fZ0t(6z>|rU+lB z#Eeat>(q*))inja&2{c_R#xrxPZLRpnOQMmft?nPg9h@==gSDh$+728I-Q4V%;*HP4(q zGm+%i7pU2xX!sm&r~1%hzb1R9oqLKkK}kYDvkr|p8mD^sNu=Lp^V2<)5(_bdJlW7Y zZ$K1mv=xN=f%K-8C-=Wo+EI0rl!~a1SOlH#s$SBYZ0dgK#vB<2i)Cq)|olw8ZlRN zJ(=9j-fe87%ZOf$b||T+{K6AZ=-c_m6-l~L-CHfm$yj3R*zw`S@fdPC=Ji7U?j@2= za^x?aMIp*e!$}hdhJ##-Dk+i0-zh+;kWQ{%@+96XpZE_oB}E9>9FmprrU{0KeEfUr zw)bjz2G$vKRd4s*qc2sZ-|bj>yR29}*SXV<@pHRwZb>yGH}y>+GG7UoVsgxK8JZ|8 zz;T%t+{VkpiVE6Rnb@eAaI)~xHL;bXO>-?MkPRVa#DxDr68oQp+yD31H8@N32g$>X z>aEg@oM3~oIRN7EWpPyw-`D%8zFaURoGWj3}4$xW~ma8WX@5M%5q`4ruBESKzZa%iZ ztIE4D6x>V8dKTwN&Fbm{IB+r6;F#~J5M~d-F2JaNLrON}f-_7#n6M4s z3*%_y(QCSMv~|Aw(S6i%?<3RU+P8hw&iYCHx-KKw;IrPKRezM{C>Q8?WP`5bKvGvQ z>bOT?Eb2^U4ld!d<)oV@?8ZvXpd-Olbht^M>g9F*`r#B@e>T^uN>xqeVCIyXIPSWE z)G*Sz3De+25>CQA#XeZ@45Wkoeo7jX^(!Bfit>fXQN#lT_kpL_(*YfrN>ID#H7vub&lv>;PEJ_nT)yR!O*f?b{ngtlQt*7Cd* znm7<;xO8g1qT7y3uOf!YY@Wp}8TT-~=TIeNptGk1Vy5gsDC{h!2y+KT^rtZZ?5fhI z4o`8?`BgS;R3~Y!5<(NV29<@@1>>V~XqqEwm59`nUxp8EgZbYXHwrpCWvD^ST2j+5 zM{S6tn+mlof9ZSD$~z}15$Afu)G9P3f&Jhv#!xPbn3-GOlQjV8>uSbv96PqQM4~*p z4uYTT>9-7nMMFnp(3}5^X+(D7JW3jwrb%oJX6X=$E`;Qx>ATv$Yp48Ud*wenhogz< zgO7uW3)dm+cVrBWSVxPaPWsJU6=xG1{!Yi=5mmZL?OKP-Tye%et=jEc@l6h{YA;$a z=|c(HO^p(SEse#*qbE5uZlo%xGbRxZp573JtV0{<_am})yHe2`%@Z4BlBdd5<5_D) z`U;oO2{%lU;eECJ1l&{eGOlHn5ECqh&68HWEwA{YT%^~t?Gphxjh`i13hlkZZ-AZY z8oAcVL8N+ti-w9VM`9%DOM#A3AkrxFd3_R8zpUfw=^g`m@p&<_HO^E8@+e#g_d=9p zoI#59Csi02>r1$nY@))LiCsyQ8%GWA9iPy<#B|1%#%ZqI^7cOS@`ZxvvEi?yE%J@_ zaGOY$%+QhC_rt72+Io7e9P@f&SC_c zw*mE0iIEM)+y>$nRSUv34pRfj=yBgZSm6BJxSsNqCCw!}QX4NtUXAK?3l+FUkf9K8 z9W>j?Jcf|GPwH=GqHo*!%5qBfmI?78FWlu`j1gPVWEKbLDYCY4ayI^u^dL0Xhx5t0 zkfD?yhQsOHX@wg%I{e7qTT~=R_=}2c_ELiPRF%4!^l<$$?6F{!06o{Pw`(5vM7TR9 z6r9DJJ1AnEmLaML!vbvnK7r{i=$~f`Gc|8~?J@b96^_nu^Po$}dqFeWP_7R!<&Dml zjzJaloTnT9Y?zwN!ZyXSn?zD^BtD+wzRNXpJ3F&+1i>@-)rjpcPN<0vkvwa-M7zqQ@4QPqaFG+h5`TW;I=pSVB zKgni;|G=Vd1`Vxkskp4~-O6RI*U(!wLg+EPAL>ObTHVOAnh+zLNx@4tDU|cGvy5Gm z>+tqZt-nH>+i3#s+=^xuYW2ickj0uPKGAF}(g|6RV)-%DNdczhXqJZvz04NPf{@$r z$)+*+RM%DC(;Kq(?hKo?Lyoc4ad-|CS;w)dfxb`hYN=J@SeZNd%k0`;lo7ew2`lYb z zEgJ;{(J03Ad7o5)6kt5M=EFT{KyMZ%D>t#9a_N;ve_3KC;Y2SN+9``E5>C=PGpEfpZ`c0R!Dv9MfJBofi*uQ(Xclw^m- zpE4dQz}?)kC{)?o;#SiyeNeu=Q8Va=wwP&y?~JwBIOj7jCdyl9&7 zK3d4lOc;5DH$^Fw`Ea{E4Wg3oK8QOV0YO|uoEevJM$$#wfx|k_Nec_WqWpH5%xySjNp{a4 z_JOYeJB?Iw6k{)&*v&VRj$Pj0Lb-`8)5)h4XP|Dv%l8k9)eC-FBf%xve5CT^hq0DB^{1ee;U08JlGv-U@>iZV90{T%{+Z z-6n05QI@j)xguFOkNTO{mT6bANVT-|eE?FU8R(=`{d+4nLa*WgTD&YQoD0a_5yyh~ z^B9)1Pt(4Ro8y9{Lxe)Re9LfxU1v>|IsdgejX%@>&BFPUeiw$cmuh`pQ=S6_Gs~0@ z1R_M5X`*XI!cXE$S)Xo=X|>wY3VO{HlPTy$5}VA?Bua z^OCval$wtwTSd1n*A1o@MHQ`NvmbwqWhcc-o0~cj>m(s0Z4z4+E!Ej80ptBRlEOe(pJ1u51dW*147fKFdqqF1SocSCfIu=>NRY3UHQPLXsl>KqR zoyJ#t_4i0NM`R{Rg4J6(@TbB0B6Z0xDLCGS;Trcxf%guf^|z>`lVD>k0x~u4QcCs@ z==etPae0bz_MYnoHxfH@951DpxtbFBEi@0V0#!{o4sIXyH9w{9%vb1W|4K}!Tfmm( z@(E(tf|Yi}AW5Wk^FlMd?=?iTDZ#*fv&SW|s`pyQr8VZuW=uadJl2^bFV+}mlpIjS z>u+S^K!y6vW4hDO@G8I`kuTmkvIpZXf#MvJQO4ZizoDSKtfw#Km@f``ZC6!Q4$bv*cS|4VPAt*V4PVVP9k=J&SwUgxxl*8^p(T{b}zhRG8b!THW!w z`O0!aB(9PzOe3cpDMP+58-ZO`(rs)cChfUwj4Wrd6cDw}suC!}#`w*_CQab@<+|N} z%5)6z?O3JkgugC5V|p?acW35NsI^(1*rTsSo@2o?zI!!h#E9iZdarCx6r#XRz%J+i zo`~Lxapopz30B-yI)gX?Z13D@05Sq5>13PeiLvT4Y zTdldtwyDGK- z1`~)#YowBW`i?DP9mh&0=&8h{shx>E<3?>#cc9c`$m zMRxo=v?KD6sG{yxa3b{fms6!u>*O0J92FSvi(A~~bw7s3YQ?v*)g3|8bYQ&>4spzs z8yD@FcANav+ibd{io*CQzaZI}h~q`Y=$OD00OvFt?esPG)I0$#2Fj`QYLKsC0nC)A zd(g>GVlg5a)o6Rg2lR??wl%ihfz>PNR$kmr=rSI?+=~14{MXe`2J{ulbdM_9@S}_4 zm%Hu_c=^9{hPe_okQ_#^$P|J7t=n>>JAz~NoYOo=1ADf(BZWttS;D%b9wCjRiWoRm zOv(1sepkvuU!q2RbMTLBp~^WO&jT5C$~@IfFM?=(ifw;}-ABge>AJegBt=c!95fe! z+la3-Z@;NLuPOXK`BAwRs@b*l!N{5*o znVVra{juKvkU=WM<{LNoR;FZ^xmO>n+xN1?u2~}9K4GzuIvO1JvBlRlPYSS*%(AM~ zqaIPuGM7d#Yipzs_%u8Dymhg?Jt5~$vj6u_n(h7Rl%l`p>7QRR`0E+}^^E`Wfxmp< cFCX~J2mbPbzkJ{?ANb1${z-iR=4tM~0fWW~^8f$< literal 0 HcmV?d00001 From cbf4ff4a804113b7d9267a488f187d4bbfe9e160 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Wed, 22 Apr 2015 13:24:40 -0400 Subject: [PATCH 012/343] first commit, putting in stashed files --- packages/oncoprintjs/src/js/cell.js | 49 ++++++++++++++ packages/oncoprintjs/src/js/oncoprint.js | 39 +++++++++++ packages/oncoprintjs/src/js/track.js | 84 ++++++++++++++++++++++++ 3 files changed, 172 insertions(+) create mode 100644 packages/oncoprintjs/src/js/cell.js create mode 100644 packages/oncoprintjs/src/js/oncoprint.js create mode 100644 packages/oncoprintjs/src/js/track.js diff --git a/packages/oncoprintjs/src/js/cell.js b/packages/oncoprintjs/src/js/cell.js new file mode 100644 index 00000000000..674431023d0 --- /dev/null +++ b/packages/oncoprintjs/src/js/cell.js @@ -0,0 +1,49 @@ +var utils = require('utils'); +// cellDecoratorTypes is an enum as well as giving a rendering order +var cellDecoratorTypes = ['CELL', 'ONE_THIRD_FILL', 'LARGE_RIGHTARROW', 'UPPER_UPARROW', 'LOWER_DOWNARROW'] +// a decorator attribute is of the form {'type':T, 'stroke':CS, 'fill':CF, selector:F} +// where T is in cellDecoratorTypes, CS and CF are hex color strings, and F is a function from datum to boolean that decides whether its active +var defaultConfig = { + cell_width: 10, + cell_height: 20, +}; +function CellDecorator(config) { + var rules = []; + this.config = $.extend({}, defaultConfig, config); + + this.decorate = function(container, data) { + rules.sort(function(a,b) { + return cellDecoratorTypes.index(a.type) - cellDecoratorTypes.index(b.type); + }); + var rule, filtered_data; + for (var i=0; i 2; + }, + id_accessor: 'sample', + cell_width: 10, + cell_height: 20, + cell_padding: 3, +}; + +var geneConfig = { +}; + +var genderConfig = { + labelDecorator: false +}; + +// Predefined label decorators +var labelDecoratorPercentAltered = function(data, isAltered) { + if (!isAltered || typeof isAltered != "function") { + return -1; + } + var alteredCt = _.reduce(_.map(data, isAltered), function(memo, bool) { + return memo+bool; + }, 0); + var percentAltered = alteredCt/data.length; + // TODO: precision + return ''+percentAltered+'%'; +} + +function Track(data, config) { + if (typeof config === "object") { + // Use user-specified config + this.config = $.extend({}, defaultConfig, config); + } else if (typeof config === "string") { + // Select from predefined configs + if (config === "gender") { + this.config = genderConfig; + } else if (config === "gene") { + this.config = geneConfig; + } else { + this.config = defaultConfig; + } + } else { + this.config = defaultConfig; + } + this.baseLabel = baseLabel; + this.data = data; + + this.getLabel = function() { + var ret = this.baseLabel; + if (this.config.labelDecorator) { + ret += " "; + if (typeof this.config.labelDecorator === "function") { + ret += this.config.labelDecorator(this.data); + } else if (typeof this.config.labelDecorator === "string") { + if (this.config.labelDecorator === "percent_altered") { + ret += labelDecoratorPercentAltered(this.data, this.config.isAltered); + } + } + } + return ret; + }; + this.sort = function(cmp, id_accessor) { + // sorts and returns the new order + + } + + this.update = function() { + // to be called after render has already been called + + }; + this.render = function() { + + } + +} \ No newline at end of file From 287895683d553129769cc6e364161c21c70f6d59 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Wed, 22 Apr 2015 21:59:05 -0400 Subject: [PATCH 013/343] first stage of refactor is done - basic structure is in, and we can insert data, decorate it in a couple of ways, and resort it. rendering strategies still not great, i.e. when we add a new track for example should we be rerendering everything? --- packages/oncoprintjs/gulpfile.js | 8 + packages/oncoprintjs/package.json | 3 +- packages/oncoprintjs/src/js/cell.js | 88 ++-- packages/oncoprintjs/src/js/oncoprint.js | 44 +- packages/oncoprintjs/src/js/track.js | 78 +++- packages/oncoprintjs/test/data/gbm/tp53.json | 398 +++++++++++++++++++ packages/oncoprintjs/test/index.html | 23 +- packages/oncoprintjs/test/js/test_page.js | 35 +- 8 files changed, 602 insertions(+), 75 deletions(-) create mode 100644 packages/oncoprintjs/test/data/gbm/tp53.json diff --git a/packages/oncoprintjs/gulpfile.js b/packages/oncoprintjs/gulpfile.js index 82e4dcc314d..7ac7378ea66 100644 --- a/packages/oncoprintjs/gulpfile.js +++ b/packages/oncoprintjs/gulpfile.js @@ -54,6 +54,14 @@ gulp.task('prod', function() { .pipe(notify("Done with generating production code.")); }); +gulp.task('refact', function() { + browserify('./src/js/oncoprint.js', + {standalone: 'oncoprint'}).bundle() + .pipe(source('oncoprint-bundle.js')) + .pipe(streamify(uglify())) + .pipe(gulp.dest('dist/refact/')) + .pipe(notify('Done with generating refactor code.')) +}); // Clean gulp.task('clean', function(cb) { del(['dist'], cb) diff --git a/packages/oncoprintjs/package.json b/packages/oncoprintjs/package.json index 55a2456ec8c..a2802383d0a 100644 --- a/packages/oncoprintjs/package.json +++ b/packages/oncoprintjs/package.json @@ -49,6 +49,7 @@ }, "dependencies": { "d3": "^3.5.3", - "underscore": "^1.7.0" + "underscore": "^1.7.0", + "jquery":"^1.11.1" } } diff --git a/packages/oncoprintjs/src/js/cell.js b/packages/oncoprintjs/src/js/cell.js index 674431023d0..c8e2bb6fc78 100644 --- a/packages/oncoprintjs/src/js/cell.js +++ b/packages/oncoprintjs/src/js/cell.js @@ -1,39 +1,47 @@ -var utils = require('utils'); +var utils = require('./utils'); +var $ = require('jquery'); // cellDecoratorTypes is an enum as well as giving a rendering order -var cellDecoratorTypes = ['CELL', 'ONE_THIRD_FILL', 'LARGE_RIGHTARROW', 'UPPER_UPARROW', 'LOWER_DOWNARROW'] +var cellDecoratorTypes = utils.invert_array(['CELL', 'ONE_THIRD_FILL', 'LARGE_RIGHTARROW', 'UPPER_UPARROW', 'LOWER_DOWNARROW']); // a decorator attribute is of the form {'type':T, 'stroke':CS, 'fill':CF, selector:F} -// where T is in cellDecoratorTypes, CS and CF are hex color strings, and F is a function from datum to boolean that decides whether its active -var defaultConfig = { - cell_width: 10, - cell_height: 20, -}; -function CellDecorator(config) { +// where T is in cellDecoratorTypes, CS and CF are hex color strings or functions that take a datum and return a hex color string, +// and F is a function from datum to boolean that decides whether its active +function CellRenderer(track) { + var defaultRules = []; var rules = []; - this.config = $.extend({}, defaultConfig, config); + this.track = track; + this.config = this.track.config; + + this.update_order = function(container, data, id_order) { + var config = this.config; + id_order = utils.invert_array(id_order); + container.selectAll('g.cell').transition(function(d,i) { return i;}) + .attr('transform', function(d, i) { return utils.translate( + id_order[d[config.id_accessor]]*(config.cell_width + config.cell_padding) + , 0);}) + }; this.decorate = function(container, data) { + var config = this.config; + // decorate according to rules rules.sort(function(a,b) { - return cellDecoratorTypes.index(a.type) - cellDecoratorTypes.index(b.type); + return cellDecoratorTypes[a.type] - cellDecoratorTypes[b.type]; }); var rule, filtered_data; - for (var i=0; i 2; }, + baseLabel: 'Gene', id_accessor: 'sample', - cell_width: 10, cell_height: 20, - cell_padding: 3, + track_height: 20, }; var geneConfig = { @@ -23,19 +30,19 @@ var genderConfig = { }; // Predefined label decorators -var labelDecoratorPercentAltered = function(data, isAltered) { - if (!isAltered || typeof isAltered != "function") { +var labelDecoratorPercentAltered = function(data, isAlteredFn) { + if (!isAlteredFn || typeof isAlteredFn != "function") { return -1; } - var alteredCt = _.reduce(_.map(data, isAltered), function(memo, bool) { + var alteredCt = _.reduce(_.map(data, isAlteredFn), function(memo, bool) { return memo+bool; }, 0); - var percentAltered = alteredCt/data.length; + var percentAltered = 100*(alteredCt/data.length); // TODO: precision - return ''+percentAltered+'%'; + return ''+Math.floor(percentAltered)+'%'; } -function Track(data, config) { +function Track(data, config, oncoprint) { if (typeof config === "object") { // Use user-specified config this.config = $.extend({}, defaultConfig, config); @@ -51,11 +58,20 @@ function Track(data, config) { } else { this.config = defaultConfig; } - this.baseLabel = baseLabel; + this.oncoprint = oncoprint; + this.config = $.extend({}, this.oncoprint.config, this.config); this.data = data; + this.cellRenderer = new CellRenderer(this); + this.d3_table = false; + var makeCellArea = $.proxy(function(ctr) { + return ctr.append('svg') + .attr('width', (this.config.cell_width + this.config.cell_padding)*this.data.length) + .attr('height', this.config.track_height); + }, this); + this.getLabel = function() { - var ret = this.baseLabel; + var ret = this.config.baseLabel; if (this.config.labelDecorator) { ret += " "; if (typeof this.config.labelDecorator === "function") { @@ -68,17 +84,41 @@ function Track(data, config) { } return ret; }; - this.sort = function(cmp, id_accessor) { - // sorts and returns the new order - - } - - this.update = function() { - // to be called after render has already been called + + this.getIds = function() { + var id_accessor = this.config.id_accessor; + return _.map(this.data, function(d) { return d[id_accessor];}); + }; + this.sort = function(cmp) { + // returns result of sorting on comparator: a list of ids + this.data.sort(cmp); + return this.getIds(); + }; + this.addRenderRule = function(type, stroke, fill, selector) { + this.cellRenderer.addRule(type, stroke, fill, selector); + return this; }; - this.render = function() { + this.addDefaultRenderRule = function(type, stroke, fill) { + this.cellRenderer.addDefaultRule(type, stroke, fill); + return this; } -} \ No newline at end of file + this.update = function(id_order) { + this.cellRenderer.update_order(this.d3_table, this.data, id_order); + } + this.render = function(d3_table, id_order) { + this.d3_table = d3_table; + var config = this.config; + var row = this.d3_table.append('tr') + .style('padding-bottom', config.track_padding) + .style('padding-top', config.track_padding); + // label segment + row.append('td').classed('track_label', true).append('p').text(this.getLabel()); + // cells segment + var cellArea = makeCellArea(row.append('td').classed('track_cells', true)); + this.cellRenderer.render(cellArea, this.data, id_order); + } +} +module.exports.Track = Track; \ No newline at end of file diff --git a/packages/oncoprintjs/test/data/gbm/tp53.json b/packages/oncoprintjs/test/data/gbm/tp53.json new file mode 100644 index 00000000000..6a2ef56d592 --- /dev/null +++ b/packages/oncoprintjs/test/data/gbm/tp53.json @@ -0,0 +1,398 @@ +[ +{ + "sample" : "TCGA-02-0001-01", + "mutation" : "P278R", + "gene" : "TP53" + }, + { + "sample" : "TCGA-02-0003-01", + "mutation" : "H178Q,R282W", + "gene" : "TP53" + }, + { + "gene" : "TP53", + "sample" : "TCGA-02-0006-01" + }, + { + "gene" : "TP53", + "sample" : "TCGA-02-0007-01" + }, + { + "sample" : "TCGA-02-0009-01", + "gene" : "TP53" + }, + { + "gene" : "TP53", + "mutation" : "R175H,R273C", + "sample" : "TCGA-02-0010-01" + }, + { + "sample" : "TCGA-02-0011-01", + "mutation" : "R175G", + "gene" : "TP53" + }, + { + "sample" : "TCGA-02-0014-01", + "mutation" : "N200fs", + "gene" : "TP53" + }, + { + "sample" : "TCGA-02-0021-01", + "gene" : "TP53" + }, + { + "mutation" : "R342*", + "gene" : "TP53", + "sample" : "TCGA-02-0024-01" + }, + { + "gene" : "TP53", + "sample" : "TCGA-02-0027-01" + }, + { + "gene" : "TP53", + "sample" : "TCGA-02-0028-01" + }, + { + "gene" : "TP53", + "mutation" : "R248Q", + "sample" : "TCGA-02-0033-01" + }, + { + "mutation" : "E258Q,C135Y", + "gene" : "TP53", + "sample" : "TCGA-02-0034-01" + }, + { + "mutation" : "R175H", + "gene" : "TP53", + "sample" : "TCGA-02-0037-01" + }, + { + "gene" : "TP53", + "sample" : "TCGA-02-0038-01" + }, + { + "gene" : "TP53", + "sample" : "TCGA-02-0043-01" + }, + { + "mutation" : "P177S,R282W", + "gene" : "TP53", + "sample" : "TCGA-02-0046-01" + }, + { + "gene" : "TP53", + "sample" : "TCGA-02-0047-01" + }, + { + "sample" : "TCGA-02-0052-01", + "gene" : "TP53" + }, + { + "sample" : "TCGA-02-0054-01", + "mutation" : "R213*", + "gene" : "TP53" + }, + { + "gene" : "TP53", + "mutation" : "V216M", + "sample" : "TCGA-02-0055-01" + }, + { + "sample" : "TCGA-02-0057-01", + "gene" : "TP53" + }, + { + "sample" : "TCGA-02-0058-01", + "mutation" : "V272M", + "gene" : "TP53" + }, + { + "sample" : "TCGA-02-0060-01", + "gene" : "TP53" + }, + { + "gene" : "TP53", + "sample" : "TCGA-02-0064-01" + }, + { + "sample" : "TCGA-02-0069-01", + "gene" : "TP53" + }, + { + "gene" : "TP53", + "sample" : "TCGA-02-0071-01" + }, + { + "sample" : "TCGA-02-0074-01", + "gene" : "TP53", + "mutation" : "N235D" + }, + { + "mutation" : "L130F", + "gene" : "TP53", + "sample" : "TCGA-02-0075-01" + }, + { + "mutation" : "R273C", + "gene" : "TP53", + "sample" : "TCGA-02-0080-01" + }, + { + "sample" : "TCGA-02-0083-01", + "gene" : "TP53", + "mutation" : "P278S" + }, + { + "gene" : "TP53", + "sample" : "TCGA-02-0085-01" + }, + { + "sample" : "TCGA-02-0086-01", + "gene" : "TP53" + }, + { + "mutation" : "H179Y", + "gene" : "TP53", + "sample" : "TCGA-02-0089-01" + }, + { + "gene" : "TP53", + "sample" : "TCGA-02-0099-01" + }, + { + "gene" : "TP53", + "sample" : "TCGA-02-0102-01" + }, + { + "sample" : "TCGA-02-0107-01", + "gene" : "TP53" + }, + { + "sample" : "TCGA-02-0113-01", + "gene" : "TP53" + }, + { + "gene" : "TP53", + "mutation" : "C176Y,A276V", + "sample" : "TCGA-02-0114-01" + }, + { + "gene" : "TP53", + "sample" : "TCGA-02-0115-01" + }, + { + "gene" : "TP53", + "sample" : "TCGA-02-0116-01" + }, + { + "gene" : "TP53", + "sample" : "TCGA-06-0122-01" + }, + { + "sample" : "TCGA-06-0124-01", + "gene" : "TP53" + }, + { + "gene" : "TP53", + "sample" : "TCGA-06-0125-01" + }, + { + "sample" : "TCGA-06-0126-01", + "gene" : "TP53" + }, + { + "sample" : "TCGA-06-0128-01", + "gene" : "TP53", + "mutation" : "T312fs" + }, + { + "gene" : "TP53", + "sample" : "TCGA-06-0129-01" + }, + { + "sample" : "TCGA-06-0130-01", + "mutation" : "R158H", + "gene" : "TP53" + }, + { + "sample" : "TCGA-06-0132-01", + "gene" : "TP53" + }, + { + "sample" : "TCGA-06-0133-01", + "gene" : "TP53" + }, + { + "sample" : "TCGA-06-0137-01", + "gene" : "TP53" + }, + { + "sample" : "TCGA-06-0138-01", + "gene" : "TP53" + }, + { + "gene" : "TP53", + "sample" : "TCGA-06-0139-01" + }, + { + "sample" : "TCGA-06-0141-01", + "gene" : "TP53" + }, + { + "gene" : "TP53", + "sample" : "TCGA-06-0143-01" + }, + { + "sample" : "TCGA-06-0145-01", + "gene" : "TP53" + }, + { + "gene" : "TP53", + "sample" : "TCGA-06-0147-01" + }, + { + "gene" : "TP53", + "sample" : "TCGA-06-0148-01" + }, + { + "sample" : "TCGA-06-0154-01", + "gene" : "TP53" + }, + { + "mutation" : "V97fs", + "gene" : "TP53", + "sample" : "TCGA-06-0156-01" + }, + { + "sample" : "TCGA-06-0157-01", + "gene" : "TP53" + }, + { + "sample" : "TCGA-06-0158-01", + "gene" : "TP53" + }, + { + "sample" : "TCGA-06-0166-01", + "gene" : "TP53" + }, + { + "gene" : "TP53", + "sample" : "TCGA-06-0168-01" + }, + { + "gene" : "TP53", + "sample" : "TCGA-06-0169-01" + }, + { + "sample" : "TCGA-06-0171-01", + "gene" : "TP53" + }, + { + "gene" : "TP53", + "sample" : "TCGA-06-0173-01" + }, + { + "sample" : "TCGA-06-0174-01", + "gene" : "TP53" + }, + { + "gene" : "TP53", + "sample" : "TCGA-06-0176-01" + }, + { + "gene" : "TP53", + "sample" : "TCGA-06-0178-01" + }, + { + "sample" : "TCGA-06-0184-01", + "gene" : "TP53", + "mutation" : "R158H" + }, + { + "gene" : "TP53", + "sample" : "TCGA-06-0185-01" + }, + { + "sample" : "TCGA-06-0187-01", + "gene" : "TP53" + }, + { + "sample" : "TCGA-06-0188-01", + "gene" : "TP53", + "mutation" : "A307_splice,A307V" + }, + { + "sample" : "TCGA-06-0189-01", + "gene" : "TP53" + }, + { + "sample" : "TCGA-06-0190-01", + "mutation" : "G245S", + "gene" : "TP53" + }, + { + "gene" : "TP53", + "mutation" : "G266R,D281A", + "sample" : "TCGA-06-0195-01" + }, + { + "gene" : "TP53", + "mutation" : "F270S", + "sample" : "TCGA-06-0197-01" + }, + { + "sample" : "TCGA-06-0201-01", + "gene" : "TP53" + }, + { + "gene" : "TP53", + "mutation" : "R273H", + "sample" : "TCGA-06-0206-01" + }, + { + "sample" : "TCGA-06-0208-01", + "gene" : "TP53" + }, + { + "sample" : "TCGA-06-0209-01", + "gene" : "TP53" + }, + { + "sample" : "TCGA-06-0210-01", + "gene" : "TP53" + }, + { + "gene" : "TP53", + "sample" : "TCGA-06-0211-01" + }, + { + "sample" : "TCGA-06-0213-01", + "gene" : "TP53", + "cna" : "HOMODELETED" + }, + { + "sample" : "TCGA-06-0214-01", + "gene" : "TP53" + }, + { + "sample" : "TCGA-06-0219-01", + "gene" : "TP53" + }, + { + "mutation" : "K139fs", + "gene" : "TP53", + "sample" : "TCGA-06-0221-01" + }, + { + "mutation" : "I195T", + "gene" : "TP53", + "sample" : "TCGA-06-0237-01" + }, + { + "mutation" : "R248Q", + "gene" : "TP53", + "sample" : "TCGA-06-0241-01" + } + ] \ No newline at end of file diff --git a/packages/oncoprintjs/test/index.html b/packages/oncoprintjs/test/index.html index 78bbf7ce8f5..6df6681e979 100644 --- a/packages/oncoprintjs/test/index.html +++ b/packages/oncoprintjs/test/index.html @@ -2,19 +2,18 @@ Oncoprint Test Page - - +

+ - Selected: + + + + + + + diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index f0c238069a1..c1c97c014cd 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -35,7 +35,14 @@ geneDataPromise.then(function(data) { geneData = data; }); $.when(geneDataPromise, genderDataPromise).then(function() { - var renderer = onc.addTrack('gender',genderData, {label:'Gender' }).renderer; + onc.addTrack('gender', genderData, {label: 'Gender'}) + .useRenderTemplate('categorical_color', { + color: {MALE: '#6699FF', FEMALE:'#FF00FF'}, + category: function(d) { + return d.attr_val; + } + }); + /*var renderer = onc.addTrack('gender',genderData, {label:'Gender' }).renderer; renderer.addRule({condition:function(d) { return d.attr_val === 'MALE';}, d3_shape: d3.select(document.createElementNS('http://www.w3.org/2000/svg', 'rect')), attrs: {'fill':'rgba(255,0,0,255)', 'width':'100%', 'height':'33.33%', 'y':'33.33%'}, From cf74cec4eecaa18ca799e049f54fb7569704b91c Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Thu, 7 May 2015 17:11:13 -0400 Subject: [PATCH 032/343] add bar chart template --- .../oncoprintjs/src/js/D3SVGCellRenderer.js | 26 ++++++++++++++++--- packages/oncoprintjs/test/js/test_page.js | 9 +++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/packages/oncoprintjs/src/js/D3SVGCellRenderer.js b/packages/oncoprintjs/src/js/D3SVGCellRenderer.js index 7549b1eb3bf..595bab06e5c 100644 --- a/packages/oncoprintjs/src/js/D3SVGCellRenderer.js +++ b/packages/oncoprintjs/src/js/D3SVGCellRenderer.js @@ -37,7 +37,7 @@ function D3SVGRuleset(track_config) { var applyRule = function(params, d3_g_selection, d3_data, d3_data_key) { d3_g_selection = d3_g_selection.data( - d3_data.filter(params.condition), + d3_data.filter(params.condition || function(d) { return true; }), d3_data_key ); var elts = d3_g_selection.select(function() { @@ -180,7 +180,6 @@ function D3SVGCellRenderer(data, track_config) { } }; self.addRule({ - condition: function(d) { return true; }, d3_shape: rect, attrs: attrs, }); @@ -197,7 +196,28 @@ function D3SVGCellRenderer(data, track_config) { // params: - data accessor // - endpoints of the value range // - color: string or function of datum - + var rect = utils.makeD3SVGElement('rect'); + var range = params.range.slice(); + var range_len = range[1] - range[0]; + var color = params.color; + var data = params.data; + var height_perc = function(d) { + return ((data(d) - range[0])/range_len)*100; + }; + var attrs = { + width: '100%', + height: function(d) { + return height_perc(d)+'%'; + }, + y: function(d) { + return (100 - height_perc(d))+ '%'; + }, + fill: color || '#000000' + }; + self.addRule({ + d3_shape: rect, + attrs: attrs + }); } else if (templName === 'genetic_alteration') { // any params? } diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index c1c97c014cd..30c8a169438 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -28,8 +28,10 @@ var genderData; var genderDataPromise = $.getJSON('./gbm/gender-gbm.json'); var geneData; var geneDataPromise = $.getJSON('./gbm/tp53.json'); +var mutationData; genderDataPromise.then(function(data) { genderData = data.data; + mutationData = genderData.map(function(x) { return $.extend({}, x, {attr_val: (x.attr_val === 'MALE' ? 70 + Math.random()*30 : Math.random()*30)}); }); }); geneDataPromise.then(function(data) { geneData = data; @@ -42,6 +44,13 @@ $.when(geneDataPromise, genderDataPromise).then(function() { return d.attr_val; } }); + onc.addTrack('mutations', mutationData, {label: 'Mutations'}) + .useRenderTemplate('bar_chart', { + data: function(d) { + return d.attr_val; + }, + range:[0,100] + }); /*var renderer = onc.addTrack('gender',genderData, {label:'Gender' }).renderer; renderer.addRule({condition:function(d) { return d.attr_val === 'MALE';}, d3_shape: d3.select(document.createElementNS('http://www.w3.org/2000/svg', 'rect')), From 5323f5d1ca4d8e13756bab2a02b8f9f871a6aa51 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Thu, 7 May 2015 18:44:54 -0400 Subject: [PATCH 033/343] minor changes - remove vestigial stuff and add semicolon --- packages/oncoprintjs/src/js/D3SVGCellRenderer.js | 3 --- packages/oncoprintjs/src/js/utils.js | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/oncoprintjs/src/js/D3SVGCellRenderer.js b/packages/oncoprintjs/src/js/D3SVGCellRenderer.js index 595bab06e5c..d0903c305e6 100644 --- a/packages/oncoprintjs/src/js/D3SVGCellRenderer.js +++ b/packages/oncoprintjs/src/js/D3SVGCellRenderer.js @@ -2,8 +2,6 @@ var utils = require('./utils'); var $ = require('jquery'); var _ = require('underscore'); -// TODO: handle accessing config properties cleaner - function D3SVGRuleset(track_config) { var self = this; self.rule_map = {}; @@ -88,7 +86,6 @@ function D3SVGCellRenderer(data, track_config) { self.cell_area; self.svg; self.g; - self.hits; self.parseRuleset = function(json_rules) { self.rule_set.fromJSON(json_rules); diff --git a/packages/oncoprintjs/src/js/utils.js b/packages/oncoprintjs/src/js/utils.js index e5b837cef1b..06a56fad135 100644 --- a/packages/oncoprintjs/src/js/utils.js +++ b/packages/oncoprintjs/src/js/utils.js @@ -35,7 +35,7 @@ exports.stableSort = function(arr, cmp) { }); // unzip return _.map(zipped, function(x) { return x[0];}); -} +}; exports.sort_row_by_rows = function(row, rows) { // TODO test this From 23c9b98d0731e3cd9f045e858717ee9b84985ec9 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Fri, 8 May 2015 15:22:45 -0400 Subject: [PATCH 034/343] moving events into enum in different file and starting to implement genetic_alteration template --- .../oncoprintjs/src/js/D3SVGCellRenderer.js | 94 ++++++++++++++++++- packages/oncoprintjs/src/js/events.js | 11 +++ packages/oncoprintjs/src/js/oncoprint.js | 19 ++-- packages/oncoprintjs/src/js/track.js | 5 +- packages/oncoprintjs/src/js/utils.js | 4 + packages/oncoprintjs/test/js/test_page.js | 10 ++ 6 files changed, 127 insertions(+), 16 deletions(-) create mode 100644 packages/oncoprintjs/src/js/events.js diff --git a/packages/oncoprintjs/src/js/D3SVGCellRenderer.js b/packages/oncoprintjs/src/js/D3SVGCellRenderer.js index d0903c305e6..689ec537116 100644 --- a/packages/oncoprintjs/src/js/D3SVGCellRenderer.js +++ b/packages/oncoprintjs/src/js/D3SVGCellRenderer.js @@ -1,6 +1,7 @@ var utils = require('./utils'); var $ = require('jquery'); var _ = require('underscore'); +var events = require('./events'); function D3SVGRuleset(track_config) { var self = this; @@ -193,11 +194,23 @@ function D3SVGCellRenderer(data, track_config) { // params: - data accessor // - endpoints of the value range // - color: string or function of datum + // - scale var rect = utils.makeD3SVGElement('rect'); var range = params.range.slice(); + var _data = params.data; + var data = params.data; + if (params.log_scale) { + if (range[0] <= 0 || range[1] <= 0) { + utils.warn("Using log scale with range that includes a number <= 0", "Bar chart template"); + } + range[0] = Math.log(range[0]); + range[1] = Math.log(range[1]); + data = function(d) { + return Math.log(_data(d)); + } + } var range_len = range[1] - range[0]; var color = params.color; - var data = params.data; var height_perc = function(d) { return ((data(d) - range[0])/range_len)*100; }; @@ -216,14 +229,85 @@ function D3SVGCellRenderer(data, track_config) { attrs: attrs }); } else if (templName === 'genetic_alteration') { - // any params? + params = $.extend({}, params); + var rect = utils.makeD3SVGElement('rect'); + // background (CNA) + var cna = params.cna_name || 'cna'; + self.addRule({ + d3_shape: rect, + attrs: { + width:'100%', + height: '100%', + fill: function(d) { + if (!d[cna]) { + return params.default_cell_color || '#D3D3D3'; + } else if (d[cna] === params.cna_amplified_name) { + return params.cna_amplified_color || '#FF0000'; + } else if (d[cna] === params.cna_homodeleted_name) { + return params.cna_homodeleted_color || '#0000FF'; + } + } + } + }); + // mutations + var mut = params.mut_name || 'mut'; + self.addRule({ + condition: function(d) { return !!d[mut]; }, + d3_shape: rect, + attrs: { + width: '100%', + height: '33.33%', + y: '33.33%', + fill: function(d) { + var m = d[mut]; + // TODO: look up defaults in real data + if (m === (params.mut_missense_name || 'MISSENSE')) { + return params.mut_missense_color || '#008000'; + } else if (m === (params.mut_trunc_name || 'TRUNC')) { + return params.mut_trunc_color || '#000000'; + } else if (m === (params.mut_inframe_name || 'INFRAME')) { + return params.mut_inframe_color || '#9F8170'; + } else if (m === (params.mut_frameshift_name || 'FRAMESHIFT')) { + return params.mut_frameshift_color || '#000000'; // TODO - is this default? + } else { + return params.mut_default_color || '#000000'; + } + } + } + }); + // mrna + var mrna = params.mrna_name || 'mrna'; + self.addRule({ + condition: function(d) { return !!d[mrna]; }, + d3_shape: rect, + attrs: { + width: '100%', + height: '100%', + fill: 'rgba(0,0,0,0)', + stroke-width: 2, + stroke: function(d) { + var m = d[mrna]; + // TODO: look up defaults in real data. or maybe just have no defaults here - put defaults in a different file + if (m === (params.mrna_upregulated_name || 'UPREGULATED')) { + return params.mrna_upregulated_color || '#FF9999'; + } else if (m === (params.mrna_downregulated_name || 'DOWNREGULATED')) { + return params.mrna_downregulated_color || '#6699CC'; + } + } + } + }); + // TODO: rppa + var triangle-up = utils.makeD3SVGElement('path').attr('d', 'triangle-up') } }; self.bindEvents = function(track) { - $(track).on('sort.oncoprint set_cell_width.oncoprint set_cell_padding.oncoprint', function() { + $(track).on(events.SORT, function() { + self.updateCells(); + }).on(events.SET_CELL_WIDTH, function() { + self.updateCells(); + self.updateCellArea(); + }).on(events.SET_CELL_PADDING, function() { self.updateCells(); - }); - $(track).on('set_cell_width.oncoprint set_cell_padding.oncoprint', function() { self.updateCellArea(); }); }; diff --git a/packages/oncoprintjs/src/js/events.js b/packages/oncoprintjs/src/js/events.js new file mode 100644 index 00000000000..f4b39baf526 --- /dev/null +++ b/packages/oncoprintjs/src/js/events.js @@ -0,0 +1,11 @@ +module.exports = { + ADD_TRACK: 'add_track.oncoprint', + REMOVE_TRACK: 'remove_track.oncoprint', + MOVE_TRACK: 'move_track.oncoprint', + SORT: 'sort.oncoprint', + SET_CELL_PADDING: 'set_cell_padding.oncoprint', + SET_CELL_WIDTH: 'set_cell_width.oncoprint', + CELL_CLICK: 'cell_click.oncoprint', + CELL_MOUSEENTER: 'cell_mouseenter.oncoprint', + CELL_MOUSELEAVE: 'cell_mouseleave.oncoprint', +}; \ No newline at end of file diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index e99964ccfcc..898b824ca21 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -3,6 +3,7 @@ var _ = require('underscore'); var d3 = require('d3'); var $ = require('jquery'); var ReadOnlyObject = require('./ReadOnlyObject'); +var events = require('./events'); // TODO: use self everywhere @@ -29,17 +30,17 @@ function Oncoprint(container_selector_string, config) { self.setCellWidth = function(w) { self.config.cell_width = w; - $(self).trigger('set_cell_width.oncoprint'); + $(self).trigger(events.SET_CELL_WIDTH); }; self.setCellPadding = function(p) { self.config.cell_padding = p; - $(self).trigger('set_cell_padding.oncoprint'); + $(self).trigger(events.SET_CELL_PADDING); }; self.sortOnTrack = function(trackName, dataCmp) { self.config.id_order = self.tracks[trackName].getDatumIds(dataCmp); - $(self).trigger('sort.oncoprint', {id_order: self.config.id_order}); + $(self).trigger(events.SORT, {id_order: self.config.id_order}); }; self.moveTrack = function(track_name, new_position) { @@ -50,7 +51,7 @@ function Oncoprint(container_selector_string, config) { self.track_order.splice(old_position, 1); self.track_order.splice(new_position, 0, track_name); - $(self).trigger('move_track.oncoprint', {track_name: track_name, tracks:self.tracks, track_order: self.track_order}); + $(self).trigger(events.MOVE_TRACK, {track_name: track_name, tracks:self.tracks, track_order: self.track_order}); }; self.addTrack = function(name, data, config) { @@ -64,7 +65,7 @@ function Oncoprint(container_selector_string, config) { // TODO: maybe this line shouldn't exist if we're not handling no data in oncoprint self.config.id_order = self.config.id_order.concat(_.difference(self.tracks[name].getDatumIds(), self.config.id_order)); - $(self).trigger('add_track.oncoprint', {track: self.tracks[name]}); + $(self).trigger(events.ADD_TRACK, {track: self.tracks[name]}); return self.tracks[name]; }; @@ -78,7 +79,7 @@ function Oncoprint(container_selector_string, config) { var oldPosition = self.track_order.indexOf(name); self.track_order.splice(oldPosition, 1); - $(self).trigger('remove_track.oncoprint', {track: name}); + $(self).trigger(events.REMOVE_TRACK, {track: name}); return true; }; } @@ -96,10 +97,10 @@ function OncoprintTableRenderer(container_selector_string) { })(self); self.bindEvents = function(oncoprint) { - $(oncoprint).on('add_track.oncoprint', function(e, data) { + $(oncoprint).on(events.ADD_TRACK, function(e, data) { data.track.renderer.init(self.table.append('tr')); }); - $(oncoprint).on('move_track.oncoprint', function(e, data) { + $(oncoprint).on(events.MOVE_TRACK, function(e, data) { var track_name = data.track_name; var new_position = data.track_order.indexOf(track_name); var track_order = data.track_order; @@ -111,7 +112,7 @@ function OncoprintTableRenderer(container_selector_string) { before_track.renderer.$row.after(track.renderer.$row); } }); - $(oncoprint).on('remove_track.oncoprint', function(e, data) { + $(oncoprint).on(events.REMOVE_TRACK, function(e, data) { var track = data.track; track.renderer.$row.remove(); }); diff --git a/packages/oncoprintjs/src/js/track.js b/packages/oncoprintjs/src/js/track.js index 8e279697ed4..197cd0c4d63 100644 --- a/packages/oncoprintjs/src/js/track.js +++ b/packages/oncoprintjs/src/js/track.js @@ -4,6 +4,7 @@ var $ = require('jquery'); var D3SVGCellRenderer = require('./D3SVGCellRenderer'); var ReadOnlyObject = require('./ReadOnlyObject'); var utils = require('./utils'); +var events = require('./events'); var defaultTrackConfig = { label: 'Gene', @@ -30,13 +31,13 @@ function Track(name, data, config, oncoprint_config) { self.renderer.bindEvents(self); self.bindEvents = function(oncoprint) { - var pass_down_from_oncoprint = ['sort.oncoprint', 'set_cell_width.oncoprint', 'set_cell_padding.oncoprint']; + var pass_down_from_oncoprint = [events.SORT, events.SET_CELL_WIDTH, events.SET_CELL_PADDING]; _.each(pass_down_from_oncoprint, function(evt) { $(oncoprint).on(evt, function(e, data) { $(self).trigger(evt, data); }) }); - var pass_up_from_cell_renderer = ['cell_click.oncoprint', 'cell_mouseenter.oncoprint', 'cell_mouseleave.oncoprint']; + var pass_up_from_cell_renderer = [events.CELL_CLICK, events.CELL_MOUSEENTER, events.CELL_MOUSELEAVE]; _.each(pass_up_from_cell_renderer, function(evt) { $(cell_renderer).on(evt, function(e, data) { $(oncoprint).trigger(evt, $.extend({}, data, {track: self})); diff --git a/packages/oncoprintjs/src/js/utils.js b/packages/oncoprintjs/src/js/utils.js index 06a56fad135..17368969cbd 100644 --- a/packages/oncoprintjs/src/js/utils.js +++ b/packages/oncoprintjs/src/js/utils.js @@ -20,6 +20,10 @@ exports.makeD3SVGElement = function(tag) { return d3.select(document.createElementNS('http://www.w3.org/2000/svg', tag)); }; +exports.warn = function(str, context) { + console.log("Oncoprint error in "+context+": "+str); +} + exports.stableSort = function(arr, cmp) { var zipped = []; _.each(arr, function(val, ind) { diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index 30c8a169438..e67877ae160 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -30,6 +30,8 @@ var geneData; var geneDataPromise = $.getJSON('./gbm/tp53.json'); var mutationData; genderDataPromise.then(function(data) { + console.log("received data"); + console.log(data); genderData = data.data; mutationData = genderData.map(function(x) { return $.extend({}, x, {attr_val: (x.attr_val === 'MALE' ? 70 + Math.random()*30 : Math.random()*30)}); }); }); @@ -51,6 +53,14 @@ $.when(geneDataPromise, genderDataPromise).then(function() { }, range:[0,100] }); + onc.addTrack('logmutations', mutationData, {label: 'Mutations (log scale)'}) + .useRenderTemplate('bar_chart', { + data: function(d) { + return d.attr_val; + }, + range:[0.1,100], + log_scale:true + }); /*var renderer = onc.addTrack('gender',genderData, {label:'Gender' }).renderer; renderer.addRule({condition:function(d) { return d.attr_val === 'MALE';}, d3_shape: d3.select(document.createElementNS('http://www.w3.org/2000/svg', 'rect')), From bf69c0be368b7aacd99c001811865e56a745b544 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Fri, 8 May 2015 15:23:03 -0400 Subject: [PATCH 035/343] add fake data --- .../oncoprintjs/test/data/gbm/gender-gbm.json | 2003 ++++++++++++++++- 1 file changed, 2002 insertions(+), 1 deletion(-) diff --git a/packages/oncoprintjs/test/data/gbm/gender-gbm.json b/packages/oncoprintjs/test/data/gbm/gender-gbm.json index a132465e2f9..6571af185da 100644 --- a/packages/oncoprintjs/test/data/gbm/gender-gbm.json +++ b/packages/oncoprintjs/test/data/gbm/gender-gbm.json @@ -462,6 +462,2007 @@ "sample" : "TCGA-06-0241-01", "attr_id" : "GENDER", "attr_val" : "FEMALE" - } + }, + { + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.770262901898" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.520124406767" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.651899674314" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.0638099630054" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.600571715831" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.143096970318" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.88410886898" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.942656629217" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.340373735274" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.512874527707" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.150155404028" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.0969411173853" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.517885403774" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.274946296721" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.210944670591" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.619680593683" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.226024081287" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.380274494243" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.684947760302" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.902547238037" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.702836956669" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.388446604958" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.163264888342" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.0908601090172" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.848728644147" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.843692707139" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.688399406352" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.343018695175" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.328718210652" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.740452923257" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.0963138731026" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.171752742518" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.230093865396" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.106178438276" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.753073584138" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.705221029746" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.325826181637" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.332034876429" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.758339898255" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.0109616955358" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.679469781695" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.225445759499" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.573496723753" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.994863492365" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.282237855494" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.351632578287" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.546424356277" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.184550220112" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.85923455184" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.124285209577" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.922456016278" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.210812364454" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.993495515474" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.527945135014" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.595518565182" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.375240856534" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.0193209272658" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.474131191117" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.0653620251556" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.24891869504" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.107562296591" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.266953184406" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.289651601806" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.338210417519" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.375455029471" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.98984912134" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.59706050626" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.104028078074" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.961529586529" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.771449246975" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.194151882616" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.384174867292" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.022200502542" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.679113369968" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.399942437816" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.920309722173" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.487483911123" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.597609967676" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.535540590129" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.228485974086" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.670259100101" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.124892042138" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.815438384658" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.589873853896" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.103353129255" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.895801468994" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.46318266516" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.141707642825" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.0215154027056" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.647273343247" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.517324555675" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.561800018513" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.168358553528" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.869620164219" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.916605542234" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.766971477202" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.0445787006869" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.17172871642" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.24152752823" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.439200362032" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.0638355607072" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.116222564764" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.182459539811" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.581966937046" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.418503865136" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.442356278072" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.461844967416" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.940642040877" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.12768439018" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.427821626576" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.938786523781" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.284433159301" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.494116963749" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.431196631093" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.823597448258" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.0271786881831" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.682697675215" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.132060328093" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.500486517053" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.945081457048" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.018070329293" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.323536227905" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.473737103281" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.78267685792" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.935492647978" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.32671639226" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.204218375735" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.466744759883" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.435924203411" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.650801212621" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.402046884233" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.000280079618779" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.120145761803" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.664335609887" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.291330030567" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.253167674289" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.0339870039002" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.0867200370256" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.321875458375" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.279494894941" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.0461653344499" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.181006894309" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.577678963926" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.271820735413" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.877246143308" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.393877767822" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.413178048882" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.639776435219" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.286913451185" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.673476947994" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.484440330358" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.286603560458" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.568020503536" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.19171310759" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.538289948218" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.313509762774" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.68801917902" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.846882925541" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.345270013147" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.478522625438" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.0952664009957" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.662161409493" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.170556927216" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.850648643903" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.0838800519814" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.505970339724" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.351361241794" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.0275914527167" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.448497850725" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.40186723082" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.120146698357" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.941767267748" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.148240543595" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.678986190981" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.291529990819" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.826997426622" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.468999270201" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.742557760059" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.382627134048" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.0953715339477" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.981305165392" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.163825769435" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.762375096175" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.0217640847108" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.647476413264" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.676977019976" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.142415459679" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.449804594041" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.582660447836" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.031678873259" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.923191493944" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.584329479779" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.660774599767" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.515578116045" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.293721419028" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.201346526462" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.0271198065819" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.364770709957" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.939888767865" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.294698385103" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.132462304488" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.318872503471" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.260491060753" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.879460756714" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.927874740761" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.752219865024" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.342312324864" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.695922924094" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.461599880474" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.0386928681972" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.0759497903047" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.534532438797" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.499098614568" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.946736115444" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.257012792556" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.095069016488" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.91375717086" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.0137005549424" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.202887172462" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.590444904437" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.317175203753" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.804058694857" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.0688563131785" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.439338968414" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.0574366520634" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.358412772742" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.109725013139" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.146312927054" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.911320530712" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.523794359465" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.893013807435" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.509787927401" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.0773048060078" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.642910734402" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.74680463297" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.922104985361" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.0381924927661" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.441737595547" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.967248808261" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.566653599922" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.593315695175" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.603384915372" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.877188145946" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.908903798441" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.4073917486" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.193121534073" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.175849068429" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.375245279124" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.222745785183" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.390340125452" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.711738693763" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.522471977689" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.0039334897952" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.702244842717" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.777744414507" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.000362828141133" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.105432155077" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.0348303652252" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.452008157341" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.117422021136" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.140528297078" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.802255600003" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.299643954147" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.583479891552" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.986114435266" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.966179400253" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.222905916843" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.73308798913" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.0731193771906" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.608797888276" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.177704425363" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.273236165018" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.161006765433" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.293954094695" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.988753414041" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.191329291933" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.820025606209" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.581125518947" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.392877041938" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.547050467257" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.404328434605" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.179624050609" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.00138469261595" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.270628444466" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.98904051205" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.376384887028" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.143126109392" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.111845879226" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.486191584316" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.354920699636" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.335957702573" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.215767166479" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.0135487627414" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.148622791169" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.404661024806" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.530937966811" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.586777128406" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.658852413973" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.729127280343" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.646228402375" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.165308806614" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.270863388884" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.970658098212" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.147616774065" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.227036712015" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.684914951863" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.230693493604" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.524209690189" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.85924031996" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.230323535571" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.947371720815" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.829289310586" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.622492050365" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.313618958576" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.973156429026" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.403753946339" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.862992314818" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.476912917638" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.465085412205" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.303926251493" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.454259508889" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.592964852492" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.968633927113" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.404665465512" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.50408042066" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.179737878317" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.291791126818" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.796765667122" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.301009308062" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.241677072217" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.73126816588" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.199230157886" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.294064887239" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.924224423886" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.0610932019434" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.102268819724" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.755847808743" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.194190796976" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.297734134497" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.345191739956" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.680797188655" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.124704157158" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.182599993787" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.0829043140527" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.0605147442325" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.917317038493" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.622358579292" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.715317485394" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.116771693553" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.54059955238" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.31514175746" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.294500441011" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.887873701403" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.84659575099" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.273776549173" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.887899094414" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.649106407189" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.503570093267" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.463211555447" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.98124512905" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.837421643615" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.218600168126" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.225492853467" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.316185442681" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.0582150858813" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.439247889897" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.925384049999" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.651808236866" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.688892000322" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.0771245008298" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.399548423632" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.899084833711" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.736767393457" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.159025547734" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.842631306041" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.673262622695" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.685605670926" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.292524130852" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.364477164324" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.714422947719" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.636804112486" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.222004757317" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.863778533887" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.32229847301" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.926697192094" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.890424047091" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.178927947656" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.178172763695" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.588407371577" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.796086936857" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.51973252756" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.820420145682" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.29656966996" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.685193906139" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.406417975677" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.638111494278" +}, +{ + "attr_id":"GENDER", + "attr_val":"MALE", + "sample":"0.555663072566" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.325785237528" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.324733795072" +}, +{ + "attr_id":"GENDER", + "attr_val":"FEMALE", + "sample":"0.447639363432" +} + ] } From 66031df2fb1144604230e08789b609e87507061c Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Fri, 8 May 2015 15:40:45 -0400 Subject: [PATCH 036/343] Small changes Fixed small naming violations Added hidden oncoprint configuration object Added in between cell area and label area of track - this is a good idea because it keeps the beginnings of the svgs synchronized in the table, so even if we only add some feature to one of the tracks theyll all stay syncd --- packages/oncoprintjs/src/js/D3SVGCellRenderer.js | 4 ++-- packages/oncoprintjs/src/js/oncoprint.js | 4 ++++ packages/oncoprintjs/src/js/track.js | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/oncoprintjs/src/js/D3SVGCellRenderer.js b/packages/oncoprintjs/src/js/D3SVGCellRenderer.js index 689ec537116..a3b4fd58b27 100644 --- a/packages/oncoprintjs/src/js/D3SVGCellRenderer.js +++ b/packages/oncoprintjs/src/js/D3SVGCellRenderer.js @@ -284,7 +284,7 @@ function D3SVGCellRenderer(data, track_config) { width: '100%', height: '100%', fill: 'rgba(0,0,0,0)', - stroke-width: 2, + 'stroke-width': 2, stroke: function(d) { var m = d[mrna]; // TODO: look up defaults in real data. or maybe just have no defaults here - put defaults in a different file @@ -297,7 +297,7 @@ function D3SVGCellRenderer(data, track_config) { } }); // TODO: rppa - var triangle-up = utils.makeD3SVGElement('path').attr('d', 'triangle-up') + var triangle_up = utils.makeD3SVGElement('path').attr('d', 'triangle-up') } }; self.bindEvents = function(track) { diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 898b824ca21..8621c9421ee 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -13,10 +13,14 @@ var defaultOncoprintConfig = { render: 'table', }; +var hiddenOncoprintConfig = { +}; + function Oncoprint(container_selector_string, config) { var self = this; self.table; self.config = $.extend({}, defaultOncoprintConfig, config || {}); + self.config = $.extend(self.config, hiddenOncoprintConfig); self.config.id_order = []; self.track_order = []; diff --git a/packages/oncoprintjs/src/js/track.js b/packages/oncoprintjs/src/js/track.js index 197cd0c4d63..db1642c3043 100644 --- a/packages/oncoprintjs/src/js/track.js +++ b/packages/oncoprintjs/src/js/track.js @@ -91,6 +91,7 @@ function TrackTableRenderer(cell_renderer) { self.row = row; self.$row = $(self.row.node()); var label_area = row.append('td').classed('track_label', true); + var between_area = row.append('td').classed('track_between', true); var cell_area = row.append('td').classed('track_cells', true); renderLabel(label_area); initCells(cell_area) From 6f1120548066c4a94feea8d5bdebc7975c4fb4a2 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Fri, 8 May 2015 18:07:29 -0400 Subject: [PATCH 037/343] Adding bar range markers (including signaling system) Adding bar range markers (like to denote what the range of the bar chart axis is), which required having a system to add padding to the beginning of tracks (to make space for the markers), which is an oncoprint-level change that must be initiated by a cell renderer (maybe should change to initiate by a track renderer...). So there's a signal system where signals get passed up to the oncoprint and then events get trickled down. --- .../oncoprintjs/src/js/D3SVGCellRenderer.js | 36 +++++++++++++++---- packages/oncoprintjs/src/js/events.js | 1 + packages/oncoprintjs/src/js/oncoprint.js | 9 +++++ packages/oncoprintjs/src/js/signals.js | 3 ++ packages/oncoprintjs/src/js/track.js | 25 +++++++------ packages/oncoprintjs/test/index.html | 1 + packages/oncoprintjs/test/js/test_page.js | 33 +++++++++-------- 7 files changed, 77 insertions(+), 31 deletions(-) create mode 100644 packages/oncoprintjs/src/js/signals.js diff --git a/packages/oncoprintjs/src/js/D3SVGCellRenderer.js b/packages/oncoprintjs/src/js/D3SVGCellRenderer.js index a3b4fd58b27..98ae76a8279 100644 --- a/packages/oncoprintjs/src/js/D3SVGCellRenderer.js +++ b/packages/oncoprintjs/src/js/D3SVGCellRenderer.js @@ -2,6 +2,7 @@ var utils = require('./utils'); var $ = require('jquery'); var _ = require('underscore'); var events = require('./events'); +var signals = require('./signals'); function D3SVGRuleset(track_config) { var self = this; @@ -115,7 +116,7 @@ function D3SVGCellRenderer(data, track_config) { }; self.updateCellArea = function() { - self.svg.attr('width', (self.track_config.get('cell_width') + self.track_config.get('cell_padding'))*self.data.length) + self.svg.attr('width', self.track_config.get('pre_track_padding') + (self.track_config.get('cell_width') + self.track_config.get('cell_padding'))*self.data.length) .attr('height', self.track_config.get('track_height')); }; @@ -123,7 +124,7 @@ function D3SVGCellRenderer(data, track_config) { var id_order = utils.invert_array(self.track_config.get('id_order')); self.g.transition() .attr('transform', function(d,i) { - return utils.translate(id_order[self.track_config.get('datum_id')(d)]*(self.track_config.get('cell_width') + self.track_config.get('cell_padding')), 0); + return utils.translate(self.track_config.get('pre_track_padding') + id_order[self.track_config.get('datum_id')(d)]*(self.track_config.get('cell_width') + self.track_config.get('cell_padding')), 0); }); self.drawCells(); @@ -197,22 +198,23 @@ function D3SVGCellRenderer(data, track_config) { // - scale var rect = utils.makeD3SVGElement('rect'); var range = params.range.slice(); + var effective_range = params.range.slice(); var _data = params.data; var data = params.data; if (params.log_scale) { if (range[0] <= 0 || range[1] <= 0) { utils.warn("Using log scale with range that includes a number <= 0", "Bar chart template"); } - range[0] = Math.log(range[0]); - range[1] = Math.log(range[1]); + effective_range[0] = Math.log(range[0]); + effective_range[1] = Math.log(range[1]); data = function(d) { return Math.log(_data(d)); } } - var range_len = range[1] - range[0]; + var range_len = effective_range[1] - effective_range[0]; var color = params.color; var height_perc = function(d) { - return ((data(d) - range[0])/range_len)*100; + return ((data(d) - effective_range[0])/range_len)*100; }; var attrs = { width: '100%', @@ -228,6 +230,22 @@ function D3SVGCellRenderer(data, track_config) { d3_shape: rect, attrs: attrs }); + // add range markers + self.svg.selectAll('text.bar_chart_range_marker').remove(); + var range_font_size = params.range_font_size || 10; + var range_label_width = range_font_size * Math.max(range[0].toString().length, range[1].toString().length) + 2; + $(self).trigger(signals.REQUEST_PRE_TRACK_PADDING, {pre_track_padding: range_label_width}); + var range_font_color = params.range_font_color || '#FF0000'; + self.svg.append('text').attr('font-size', range_font_size) + .attr('fill', range_font_color) + .attr('x', 0) + .attr('y', range_font_size) + .text(range[1]); + self.svg.append('text').attr('font-size', range_font_size) + .attr('fill', range_font_color) + .attr('x', 0) + .attr('y', track_config.get('track_height')) + .text(range[0]); } else if (templName === 'genetic_alteration') { params = $.extend({}, params); var rect = utils.makeD3SVGElement('rect'); @@ -309,6 +327,12 @@ function D3SVGCellRenderer(data, track_config) { }).on(events.SET_CELL_PADDING, function() { self.updateCells(); self.updateCellArea(); + }).on(events.SET_PRE_TRACK_PADDING, function(e,data) { + self.updateCells(); + self.updateCellArea(); + }); + $(self).on(signals.REQUEST_PRE_TRACK_PADDING, function(e, data) { + $(track).trigger(signals.REQUEST_PRE_TRACK_PADDING, data); }); }; }; diff --git a/packages/oncoprintjs/src/js/events.js b/packages/oncoprintjs/src/js/events.js index f4b39baf526..847b8430aa4 100644 --- a/packages/oncoprintjs/src/js/events.js +++ b/packages/oncoprintjs/src/js/events.js @@ -8,4 +8,5 @@ module.exports = { CELL_CLICK: 'cell_click.oncoprint', CELL_MOUSEENTER: 'cell_mouseenter.oncoprint', CELL_MOUSELEAVE: 'cell_mouseleave.oncoprint', + SET_PRE_TRACK_PADDING: 'set_pre_track_padding.oncoprint' }; \ No newline at end of file diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 8621c9421ee..c0f10ebae09 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -4,6 +4,7 @@ var d3 = require('d3'); var $ = require('jquery'); var ReadOnlyObject = require('./ReadOnlyObject'); var events = require('./events'); +var signals = require('./signals'); // TODO: use self everywhere @@ -14,6 +15,7 @@ var defaultOncoprintConfig = { }; var hiddenOncoprintConfig = { + pre_track_padding: 0, }; function Oncoprint(container_selector_string, config) { @@ -86,6 +88,13 @@ function Oncoprint(container_selector_string, config) { $(self).trigger(events.REMOVE_TRACK, {track: name}); return true; }; + + (function bindEvents(self) { + $(self).on(signals.REQUEST_PRE_TRACK_PADDING, function(e, data) { + self.config.pre_track_padding = Math.max(data.pre_track_padding, self.config.pre_track_padding); + $(self).trigger(events.SET_PRE_TRACK_PADDING, {pre_track_padding: self.config.pre_track_padding}); + }); + })(self); } function OncoprintTableRenderer(container_selector_string) { diff --git a/packages/oncoprintjs/src/js/signals.js b/packages/oncoprintjs/src/js/signals.js new file mode 100644 index 00000000000..ed8a1446766 --- /dev/null +++ b/packages/oncoprintjs/src/js/signals.js @@ -0,0 +1,3 @@ +module.exports = { + REQUEST_PRE_TRACK_PADDING: 'request_pre_track_padding.signal.oncoprint', +} \ No newline at end of file diff --git a/packages/oncoprintjs/src/js/track.js b/packages/oncoprintjs/src/js/track.js index db1642c3043..7ff083e82c9 100644 --- a/packages/oncoprintjs/src/js/track.js +++ b/packages/oncoprintjs/src/js/track.js @@ -5,13 +5,15 @@ var D3SVGCellRenderer = require('./D3SVGCellRenderer'); var ReadOnlyObject = require('./ReadOnlyObject'); var utils = require('./utils'); var events = require('./events'); +var signals = require('./signals'); var defaultTrackConfig = { label: 'Gene', datum_id: function(d) { return d['sample'];}, cell_height: 20, track_height: 20, - track_padding: 2.5, + track_padding: 20, + sort_cmp: undefined }; function Track(name, data, config, oncoprint_config) { @@ -26,18 +28,18 @@ function Track(name, data, config, oncoprint_config) { if (self.oncoprint_config.get('render') === 'table') { cell_renderer = new D3SVGCellRenderer(self.data, self.oncoprint_config.extend(self.config)); cell_renderer.bindEvents(self); - self.renderer = new TrackTableRenderer(cell_renderer); + self.renderer = new TrackTableRenderer(self.oncoprint_config.extend(self.config), cell_renderer); } self.renderer.bindEvents(self); self.bindEvents = function(oncoprint) { - var pass_down_from_oncoprint = [events.SORT, events.SET_CELL_WIDTH, events.SET_CELL_PADDING]; + var pass_down_from_oncoprint = [events.SORT, events.SET_CELL_WIDTH, events.SET_CELL_PADDING, events.SET_PRE_TRACK_PADDING]; _.each(pass_down_from_oncoprint, function(evt) { $(oncoprint).on(evt, function(e, data) { $(self).trigger(evt, data); }) }); - var pass_up_from_cell_renderer = [events.CELL_CLICK, events.CELL_MOUSEENTER, events.CELL_MOUSELEAVE]; + var pass_up_from_cell_renderer = [events.CELL_CLICK, events.CELL_MOUSEENTER, events.CELL_MOUSELEAVE, signals.REQUEST_PRE_TRACK_PADDING]; _.each(pass_up_from_cell_renderer, function(evt) { $(cell_renderer).on(evt, function(e, data) { $(oncoprint).trigger(evt, $.extend({}, data, {track: self})); @@ -63,13 +65,16 @@ function Track(name, data, config, oncoprint_config) { $(self).trigger('init.track.oncoprint', {label_text: self.getLabel()}); } -function TrackTableRenderer(cell_renderer) { +function TrackTableRenderer(track_config, cell_renderer) { // coupled with OncoprintTableRenderer var self = this; var cell_renderer = cell_renderer; self.row; self.$row; + self.label_area; + self.between_area; + self.cell_area; var label_text; self.bindEvents = function(track) { @@ -90,11 +95,11 @@ function TrackTableRenderer(cell_renderer) { self.init = function(row) { self.row = row; self.$row = $(self.row.node()); - var label_area = row.append('td').classed('track_label', true); - var between_area = row.append('td').classed('track_between', true); - var cell_area = row.append('td').classed('track_cells', true); - renderLabel(label_area); - initCells(cell_area) + self.label_area = row.append('td').classed('track_label', true); + self.between_area = row.append('td').classed('track_between', true).style('position', 'relative'); + self.cell_area = row.append('td').classed('track_cells', true); + renderLabel(self.label_area); + initCells(self.cell_area) }; self.addRule = function(params) { diff --git a/packages/oncoprintjs/test/index.html b/packages/oncoprintjs/test/index.html index 31c25e2e58a..9e8c5f57242 100644 --- a/packages/oncoprintjs/test/index.html +++ b/packages/oncoprintjs/test/index.html @@ -8,6 +8,7 @@ + diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index e67877ae160..f784f4aa631 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -20,6 +20,23 @@ $('#move_btn').click(function(){ onc.moveTrack('gender', newPos); console.log('moving gender to '+newPos); }); +$('#add_tracks_btn').click(function() { + onc.addTrack('mutations', mutationData, {label: 'Mutations'}) + .useRenderTemplate('bar_chart', { + data: function(d) { + return d.attr_val; + }, + range:[0,100] + }); + onc.addTrack('logmutations', mutationData, {label: 'Mutations (log scale)'}) + .useRenderTemplate('bar_chart', { + data: function(d) { + return d.attr_val; + }, + range:[0.1,100], + log_scale:true + }); + }); $('#remove_btn').click(function(){ onc.removeTrack('gender2'); console.log('removing Gender 2'); @@ -46,21 +63,7 @@ $.when(geneDataPromise, genderDataPromise).then(function() { return d.attr_val; } }); - onc.addTrack('mutations', mutationData, {label: 'Mutations'}) - .useRenderTemplate('bar_chart', { - data: function(d) { - return d.attr_val; - }, - range:[0,100] - }); - onc.addTrack('logmutations', mutationData, {label: 'Mutations (log scale)'}) - .useRenderTemplate('bar_chart', { - data: function(d) { - return d.attr_val; - }, - range:[0.1,100], - log_scale:true - }); + /*var renderer = onc.addTrack('gender',genderData, {label:'Gender' }).renderer; renderer.addRule({condition:function(d) { return d.attr_val === 'MALE';}, d3_shape: d3.select(document.createElementNS('http://www.w3.org/2000/svg', 'rect')), From c5b3e70d458961f5777e8d106d55ed6fb2dd1d9c Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Fri, 15 May 2015 17:28:48 -0400 Subject: [PATCH 038/343] Use track id instead of track name --- packages/oncoprintjs/src/js/oncoprint.js | 45 ++++++++++++----------- packages/oncoprintjs/src/js/track.js | 3 +- packages/oncoprintjs/test/js/test_page.js | 18 ++++++--- 3 files changed, 36 insertions(+), 30 deletions(-) diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index c0f10ebae09..387ad0d230c 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -20,6 +20,7 @@ var hiddenOncoprintConfig = { function Oncoprint(container_selector_string, config) { var self = this; + var track_id_counter = 0; self.table; self.config = $.extend({}, defaultOncoprintConfig, config || {}); self.config = $.extend(self.config, hiddenOncoprintConfig); @@ -44,48 +45,48 @@ function Oncoprint(container_selector_string, config) { $(self).trigger(events.SET_CELL_PADDING); }; - self.sortOnTrack = function(trackName, dataCmp) { - self.config.id_order = self.tracks[trackName].getDatumIds(dataCmp); + self.sortOnTrack = function(track_id, data_cmp) { + self.config.id_order = self.tracks[track_id].getDatumIds(data_cmp); $(self).trigger(events.SORT, {id_order: self.config.id_order}); }; - self.moveTrack = function(track_name, new_position) { + self.moveTrack = function(track_id, new_position) { new_position = Math.min(self.track_order.length-1, new_position); new_position = Math.max(0, new_position); - var old_position = self.track_order.indexOf(track_name); + var old_position = self.track_order.indexOf(track_id); self.track_order.splice(old_position, 1); - self.track_order.splice(new_position, 0, track_name); + self.track_order.splice(new_position, 0, track_id); - $(self).trigger(events.MOVE_TRACK, {track_name: track_name, tracks:self.tracks, track_order: self.track_order}); + $(self).trigger(events.MOVE_TRACK, {track_id: track_id, tracks:self.tracks, track_order: self.track_order}); }; - self.addTrack = function(name, data, config) { - if (name in self.tracks) { - return false; - } - self.tracks[name] = new Track(name, data, config, new ReadOnlyObject(self.config)); - self.tracks[name].bindEvents(self); - self.track_order.push(name); + self.addTrack = function(data, config) { + var track_id = track_id_counter; + track_id_counter += 1; + self.tracks[track_id] = new Track(data, config, new ReadOnlyObject(self.config)); + self.tracks[track_id].bindEvents(self); + self.track_order.push(track_id); // TODO: maybe this line shouldn't exist if we're not handling no data in oncoprint - self.config.id_order = self.config.id_order.concat(_.difference(self.tracks[name].getDatumIds(), self.config.id_order)); + self.config.id_order = self.config.id_order.concat(_.difference(self.tracks[track_id].getDatumIds(), self.config.id_order)); - $(self).trigger(events.ADD_TRACK, {track: self.tracks[name]}); - return self.tracks[name]; + $(self).trigger(events.ADD_TRACK, {track: self.tracks[track_id]}); + return track_id; }; - self.getTrack = function(name) { - return self.tracks[name]; + self.getTrack = function(track_id) { + return self.tracks[track_id]; }; - self.removeTrack = function(name) { - delete self.tracks[name]; + self.removeTrack = function(track_id) { + var track = self.tracks[track_id]; + delete self.tracks[track_id]; - var oldPosition = self.track_order.indexOf(name); + var oldPosition = self.track_order.indexOf(track_id); self.track_order.splice(oldPosition, 1); - $(self).trigger(events.REMOVE_TRACK, {track: name}); + $(self).trigger(events.REMOVE_TRACK, {track: track, track_id: track_id}); return true; }; diff --git a/packages/oncoprintjs/src/js/track.js b/packages/oncoprintjs/src/js/track.js index 7ff083e82c9..6a52b8c876d 100644 --- a/packages/oncoprintjs/src/js/track.js +++ b/packages/oncoprintjs/src/js/track.js @@ -16,9 +16,8 @@ var defaultTrackConfig = { sort_cmp: undefined }; -function Track(name, data, config, oncoprint_config) { +function Track(data, config, oncoprint_config) { var self = this; - self.name = name; self.config = $.extend({}, defaultTrackConfig, config || {}); // inherit from default self.oncoprint_config = oncoprint_config; diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index f784f4aa631..38da52089cf 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -3,8 +3,9 @@ var $ = require('jquery'); var Oncoprint = require('../../src/js/Oncoprint'); var onc = new Oncoprint('#onc'); +var genderTrack; $('#shuffle_btn').click(function() { - onc.sortOnTrack('gender', function(d1, d2) { + onc.sortOnTrack(genderTrack, function(d1, d2) { var map = {'MALE':0, 'FEMALE':1}; return map[d1.attr_val] - map[d2.attr_val]; }); @@ -21,14 +22,18 @@ $('#move_btn').click(function(){ console.log('moving gender to '+newPos); }); $('#add_tracks_btn').click(function() { - onc.addTrack('mutations', mutationData, {label: 'Mutations'}) + var mutationsTrack = + onc.addTrack(mutationData, {label: 'Mutations'}); + onc.getTrack(mutationsTrack) .useRenderTemplate('bar_chart', { data: function(d) { return d.attr_val; }, range:[0,100] }); - onc.addTrack('logmutations', mutationData, {label: 'Mutations (log scale)'}) + var logMutationsTrack = + onc.addTrack(mutationData, {label: 'Mutations (log scale)'}); + onc.getTrack(logMutationsTrack) .useRenderTemplate('bar_chart', { data: function(d) { return d.attr_val; @@ -38,8 +43,7 @@ $('#add_tracks_btn').click(function() { }); }); $('#remove_btn').click(function(){ - onc.removeTrack('gender2'); - console.log('removing Gender 2'); + onc.removeTrack(mutationsTrack); }); var genderData; var genderDataPromise = $.getJSON('./gbm/gender-gbm.json'); @@ -56,7 +60,9 @@ geneDataPromise.then(function(data) { geneData = data; }); $.when(geneDataPromise, genderDataPromise).then(function() { - onc.addTrack('gender', genderData, {label: 'Gender'}) + genderTrack = + onc.addTrack(genderData, {label: 'Gender'}); + onc.getTrack(genderTrack) .useRenderTemplate('categorical_color', { color: {MALE: '#6699FF', FEMALE:'#FF00FF'}, category: function(d) { From 264fc2685fe796c8c0530d1713c381d5c8258643 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Tue, 19 May 2015 12:40:27 -0400 Subject: [PATCH 039/343] pushing changes for ino --- packages/oncoprintjs/gulpfile.js | 2 +- packages/oncoprintjs/src/js/Toolbar.js | 16 +++ packages/oncoprintjs/src/js/oncoprint.js | 7 ++ packages/oncoprintjs/src/js/track.js | 24 +++- packages/oncoprintjs/src/js/utils.js | 146 ++++++++--------------- packages/oncoprintjs/src/js/utils2.js | 113 ++++++++++++++++++ 6 files changed, 204 insertions(+), 104 deletions(-) create mode 100644 packages/oncoprintjs/src/js/Toolbar.js create mode 100644 packages/oncoprintjs/src/js/utils2.js diff --git a/packages/oncoprintjs/gulpfile.js b/packages/oncoprintjs/gulpfile.js index 7ac7378ea66..5386a391202 100644 --- a/packages/oncoprintjs/gulpfile.js +++ b/packages/oncoprintjs/gulpfile.js @@ -34,7 +34,7 @@ gulp.task('test', function() { .pipe(notify("Done with building code for testing.")) // Unit tests - gulp.start('spec'); + /*gulp.start('spec');*/ // Copy over the HTML. gulp.src('test/index.html') diff --git a/packages/oncoprintjs/src/js/Toolbar.js b/packages/oncoprintjs/src/js/Toolbar.js new file mode 100644 index 00000000000..ae95d596e20 --- /dev/null +++ b/packages/oncoprintjs/src/js/Toolbar.js @@ -0,0 +1,16 @@ +var toolbar_events = { + +}; + +function Toolbar() { + var self = this; + + self.bindEvents = function(oncoprint) { + } +} + +function ToolbarRenderer() { + +} + +module.exports = Toolbar; \ No newline at end of file diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 387ad0d230c..21062f26c6c 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -3,6 +3,7 @@ var _ = require('underscore'); var d3 = require('d3'); var $ = require('jquery'); var ReadOnlyObject = require('./ReadOnlyObject'); +var Toolbar = require('./Toolbar') var events = require('./events'); var signals = require('./signals'); @@ -21,6 +22,7 @@ var hiddenOncoprintConfig = { function Oncoprint(container_selector_string, config) { var self = this; var track_id_counter = 0; + self.toolbar = new Toolbar(); self.table; self.config = $.extend({}, defaultOncoprintConfig, config || {}); self.config = $.extend(self.config, hiddenOncoprintConfig); @@ -34,6 +36,7 @@ function Oncoprint(container_selector_string, config) { self.renderer = new OncoprintTableRenderer(container_selector_string); } self.renderer.bindEvents(self); + self.toolbar.bindEvents(self); self.setCellWidth = function(w) { self.config.cell_width = w; @@ -50,6 +53,10 @@ function Oncoprint(container_selector_string, config) { $(self).trigger(events.SORT, {id_order: self.config.id_order}); }; + self.sortOnTracks = function(track_ids, data_cmps) { + self.config.id_order; + }; + self.moveTrack = function(track_id, new_position) { new_position = Math.min(self.track_order.length-1, new_position); new_position = Math.max(0, new_position); diff --git a/packages/oncoprintjs/src/js/track.js b/packages/oncoprintjs/src/js/track.js index 6a52b8c876d..c0bc0bcc6cb 100644 --- a/packages/oncoprintjs/src/js/track.js +++ b/packages/oncoprintjs/src/js/track.js @@ -23,6 +23,9 @@ function Track(data, config, oncoprint_config) { self.data = data; var cell_renderer; + var data_map = data.reduce(function(acc, next) { + acc[self.config.datum_id(next)] = next; + }); if (self.oncoprint_config.get('render') === 'table') { cell_renderer = new D3SVGCellRenderer(self.data, self.oncoprint_config.extend(self.config)); @@ -50,11 +53,24 @@ function Track(data, config, oncoprint_config) { // TODO: label decorations return self.config.label; }; - - self.getDatumIds = function(sort_cmp) { - return _.map((sort_cmp && utils.stableSort(self.data, sort_cmp)) || self.data, + + self.getDatumIds = function(sort_cmp, filter) { + filter = filter || function(d) { return true; }; + return _.map( + _.filter( + (sort_cmp && utils.stableSort(self.data, sort_cmp)) || self.data, + filter + ), self.config.datum_id - ); + ); + }; + + self.getSortedData = function(sort_cmp) { + return utils.stableSort(self.data, sort_cmp); + }; + + self.getDatum = function(datum_id) { + return data_map[datum_id]; }; self.useRenderTemplate = function(templName, params) { diff --git a/packages/oncoprintjs/src/js/utils.js b/packages/oncoprintjs/src/js/utils.js index 17368969cbd..45be4b4fccc 100644 --- a/packages/oncoprintjs/src/js/utils.js +++ b/packages/oncoprintjs/src/js/utils.js @@ -3,110 +3,58 @@ var _ = require("underscore"); var exports = module.exports = {}; exports.invert_array = function invert_array(arr) { - return arr.reduce(function(curr, next, index) { - curr[next] = index; - return curr; - }, {}); -}; - -exports.is_sample_genetically_altered = function is_sample_genetically_altered(datum) { - return datum.cna !== undefined - || datum.mutation !== undefined - || datum.rna !== undefined - || datum.protein !== undefined; + return arr.reduce(function(curr, next, index) { + curr[next] = index; + return curr; + }, {}); }; exports.makeD3SVGElement = function(tag) { - return d3.select(document.createElementNS('http://www.w3.org/2000/svg', tag)); + return d3.select(document.createElementNS('http://www.w3.org/2000/svg', tag)); }; exports.warn = function(str, context) { - console.log("Oncoprint error in "+context+": "+str); -} - -exports.stableSort = function(arr, cmp) { - var zipped = []; - _.each(arr, function(val, ind) { - zipped.push([val, ind]); - }); - zipped.sort(function(a,b) { - var cmp_res = cmp(a[0],b[0]); - if (cmp_res === 0) { - return a[1]-b[1]; - } else { - return cmp_res; - } - }); - // unzip - return _.map(zipped, function(x) { return x[0];}); -}; - -exports.sort_row_by_rows = function(row, rows) { - // TODO test this - var ordering = exports.invert_array( - rows[0].map(function(d) { - return d.sample || d.sample_id; - })); - - return _.sortBy(row, function(d) { - return ordering[d.sample || d.sample_id]; - }); + console.log("Oncoprint error in "+context+": "+str); }; -exports.translate = function translate(x,y) { - return "translate(" + x + "," + y + ")"; -}; - -exports.validate_rows = function(rows) { - // TODO -}; - -exports.validate_row_against_rows = function validate_row_against_rows(row, rows) { - if (rows.length === 0) { - throw "Rows are empty"; - } - - // make sure array lengths match - var lengths = rows.map(function(row) { - return row.length; - }); - - var matrix_width = lengths[0]; - - assert(matrix_width === row.length, - "Row lengths don't match: " + row.length + " and " + matrix_width); - - // TODO, jeese this is a lot of sorting and other computation - // just to validate the data. Is there a better way? - - // make sure sample_ids match - var matrix_sample_ids = rows[0].map(pluck_sample_id); - assert(matrix_sample_ids.length !== 0, "Cannot find sample identifier for rows."); - matrix_sample_ids = _.sortBy(matrix_sample_ids, _.identity); - - var row_sample_ids = row.map(pluck_sample_id); - assert(row_sample_ids.length !== 0, "Cannot find sample identifier for row."); - row_sample_ids = _.sortBy(row_sample_ids, _.identity); - - var intersection_stringified = JSON.stringify( - _.sortBy(_.intersection(matrix_sample_ids, row_sample_ids), - _.identity)); - - assert(JSON.stringify(row_sample_ids) === intersection_stringified, - "Sample ids do not match between new row and given rows.") - - assert(JSON.stringify(matrix_sample_ids) === intersection_stringified, - "Sample ids do not match between new row and given rows.") - - return true; -}; - -function assert(bool, msg) { - if (bool) return; - throw msg; -} -exports.assert = assert; - -function pluck_sample_id(datum) { - return datum.sample || datum.sample_id; -} +exports.stableSort = function(arr, cmp) { + // cmp returns something in [-1,0,1] + + cmp = [].concat(cmp); + var index_cmp = function(a,b) { + if (a[1] < b[1]) { + return -1; + } else if (a[1] > b[1]) { + return 1; + } else { + return 0; + } + }; + cmp = cmp.concat(index_cmp); // stable + + var ordered_cmp = function(a,b) { + var res = 0; + var cmp_ind = -1; + while (res === 0 && cmp_ind < cmp.length) { + cmp_ind += 1; + res = (cmp[cmp_ind])(a[0],b[0]); + } + return res; + }; + var zipped = []; + _.each(arr, function(val, ind) { + zipped.push([val, ind]); + }) + zipped.sort(ordered_cmp); + return _map(zipped, function(x) { return x[0];}); +}; + +exports.translate = function(x,y) { + return "translate(" + x + "," + y + ")"; +}; + +exports.assert = function(bool, msg) { + if (!bool) { + throw msg; + } +} \ No newline at end of file diff --git a/packages/oncoprintjs/src/js/utils2.js b/packages/oncoprintjs/src/js/utils2.js new file mode 100644 index 00000000000..e209a5a4529 --- /dev/null +++ b/packages/oncoprintjs/src/js/utils2.js @@ -0,0 +1,113 @@ +var _ = require("underscore"); + +var exports = module.exports = {}; + +exports.invert_array = function invert_array(arr) { + return arr.reduce(function(curr, next, index) { + curr[next] = index; + return curr; + }, {}); +}; + +exports.is_sample_genetically_altered = function is_sample_genetically_altered(datum) { + return datum.cna !== undefined + || datum.mutation !== undefined + || datum.rna !== undefined + || datum.protein !== undefined; +}; + +exports.makeD3SVGElement = function(tag) { + return d3.select(document.createElementNS('http://www.w3.org/2000/svg', tag)); +}; + +exports.warn = function(str, context) { + console.log("Oncoprint error in "+context+": "+str); +} + +exports.stableSort = function(arr, cmp) { + cmp = + var zipped = []; + _.each(arr, function(val, ind) { + zipped.push([val, ind]); + }); + zipped.sort(function(a,b) { + var cmp_res = cmp(a[0],b[0]); + if (cmp_res === 0) { + return a[1]-b[1]; + } else { + return cmp_res; + } + }); + // unzip + return _.map(zipped, function(x) { return x[0];}); +}; + +exports.sort_row_by_rows = function(row, rows) { + // TODO test this + var ordering = exports.invert_array( + rows[0].map(function(d) { + return d.sample || d.sample_id; + })); + + return _.sortBy(row, function(d) { + return ordering[d.sample || d.sample_id]; + }); +}; + +exports.translate = function translate(x,y) { + return "translate(" + x + "," + y + ")"; +}; + +exports.validate_rows = function(rows) { + // TODO +}; + +exports.validate_row_against_rows = function validate_row_against_rows(row, rows) { + if (rows.length === 0) { + throw "Rows are empty"; + } + + // make sure array lengths match + var lengths = rows.map(function(row) { + return row.length; + }); + + var matrix_width = lengths[0]; + + assert(matrix_width === row.length, + "Row lengths don't match: " + row.length + " and " + matrix_width); + + // TODO, jeese this is a lot of sorting and other computation + // just to validate the data. Is there a better way? + + // make sure sample_ids match + var matrix_sample_ids = rows[0].map(pluck_sample_id); + assert(matrix_sample_ids.length !== 0, "Cannot find sample identifier for rows."); + matrix_sample_ids = _.sortBy(matrix_sample_ids, _.identity); + + var row_sample_ids = row.map(pluck_sample_id); + assert(row_sample_ids.length !== 0, "Cannot find sample identifier for row."); + row_sample_ids = _.sortBy(row_sample_ids, _.identity); + + var intersection_stringified = JSON.stringify( + _.sortBy(_.intersection(matrix_sample_ids, row_sample_ids), + _.identity)); + + assert(JSON.stringify(row_sample_ids) === intersection_stringified, + "Sample ids do not match between new row and given rows.") + + assert(JSON.stringify(matrix_sample_ids) === intersection_stringified, + "Sample ids do not match between new row and given rows.") + + return true; +}; + +function assert(bool, msg) { + if (bool) return; + throw msg; +} +exports.assert = assert; + +function pluck_sample_id(datum) { + return datum.sample || datum.sample_id; +} From c321bea8a72ff99593a35c6cd2983d2525c908a6 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Tue, 19 May 2015 15:38:13 -0400 Subject: [PATCH 040/343] Fixing bugs, adding toggle whitespace to example page, starting toolbar in earnest --- .../oncoprintjs/src/js/OncoprintToolbar.js | 16 ++ packages/oncoprintjs/src/js/events.js | 2 + packages/oncoprintjs/src/js/oncoprint.js | 8 +- packages/oncoprintjs/src/js/track.js | 5 +- packages/oncoprintjs/src/js/utils.js | 6 +- packages/oncoprintjs/test/index.html | 1 + packages/oncoprintjs/test/js/test_page.js | 169 ++++-------------- packages/oncoprintjs/test/js/test_page_old.js | 143 +++++++++++++++ 8 files changed, 207 insertions(+), 143 deletions(-) create mode 100644 packages/oncoprintjs/src/js/OncoprintToolbar.js create mode 100644 packages/oncoprintjs/test/js/test_page_old.js diff --git a/packages/oncoprintjs/src/js/OncoprintToolbar.js b/packages/oncoprintjs/src/js/OncoprintToolbar.js new file mode 100644 index 00000000000..f17e2f37714 --- /dev/null +++ b/packages/oncoprintjs/src/js/OncoprintToolbar.js @@ -0,0 +1,16 @@ +var toolbar_events = { + +}; + +function OncoprintToolbar() { + var self = this; + + self.bindEvents = function(oncoprint) { + } +} + +function ToolbarRenderer() { + +} + +module.exports = OncoprintToolbar; \ No newline at end of file diff --git a/packages/oncoprintjs/src/js/events.js b/packages/oncoprintjs/src/js/events.js index 847b8430aa4..ee8ca3a87b1 100644 --- a/packages/oncoprintjs/src/js/events.js +++ b/packages/oncoprintjs/src/js/events.js @@ -8,5 +8,7 @@ module.exports = { CELL_CLICK: 'cell_click.oncoprint', CELL_MOUSEENTER: 'cell_mouseenter.oncoprint', CELL_MOUSELEAVE: 'cell_mouseleave.oncoprint', + ONCOPRINT_MOUSEENTER: 'oncoprint_mouseenter.oncoprint', + ONCOPRINT_MOUSELEAVE: 'oncoprint_mouseleave.oncoprint', SET_PRE_TRACK_PADDING: 'set_pre_track_padding.oncoprint' }; \ No newline at end of file diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 21062f26c6c..958bc6a1d27 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -22,7 +22,6 @@ var hiddenOncoprintConfig = { function Oncoprint(container_selector_string, config) { var self = this; var track_id_counter = 0; - self.toolbar = new Toolbar(); self.table; self.config = $.extend({}, defaultOncoprintConfig, config || {}); self.config = $.extend(self.config, hiddenOncoprintConfig); @@ -36,7 +35,6 @@ function Oncoprint(container_selector_string, config) { self.renderer = new OncoprintTableRenderer(container_selector_string); } self.renderer.bindEvents(self); - self.toolbar.bindEvents(self); self.setCellWidth = function(w) { self.config.cell_width = w; @@ -137,6 +135,12 @@ function OncoprintTableRenderer(container_selector_string) { var track = data.track; track.renderer.$row.remove(); }); + self.$table.mouseenter(function() { + $(oncoprint).trigger(events.ONCOPRINT_MOUSEENTER); + }); + self.$table.mouseleave(function() { + $(oncoprint).trigger(events.ONCOPRINT_MOUSELEAVE); + }); }; } diff --git a/packages/oncoprintjs/src/js/track.js b/packages/oncoprintjs/src/js/track.js index c0bc0bcc6cb..6ef03890c51 100644 --- a/packages/oncoprintjs/src/js/track.js +++ b/packages/oncoprintjs/src/js/track.js @@ -23,9 +23,10 @@ function Track(data, config, oncoprint_config) { self.data = data; var cell_renderer; - var data_map = data.reduce(function(acc, next) { + var data_map = _.reduce(data, function(acc, next) { acc[self.config.datum_id(next)] = next; - }); + return acc; + }, {}); if (self.oncoprint_config.get('render') === 'table') { cell_renderer = new D3SVGCellRenderer(self.data, self.oncoprint_config.extend(self.config)); diff --git a/packages/oncoprintjs/src/js/utils.js b/packages/oncoprintjs/src/js/utils.js index 45be4b4fccc..c04c27bf328 100644 --- a/packages/oncoprintjs/src/js/utils.js +++ b/packages/oncoprintjs/src/js/utils.js @@ -34,10 +34,10 @@ exports.stableSort = function(arr, cmp) { var ordered_cmp = function(a,b) { var res = 0; - var cmp_ind = -1; + var cmp_ind = 0; while (res === 0 && cmp_ind < cmp.length) { - cmp_ind += 1; res = (cmp[cmp_ind])(a[0],b[0]); + cmp_ind += 1; } return res; }; @@ -46,7 +46,7 @@ exports.stableSort = function(arr, cmp) { zipped.push([val, ind]); }) zipped.sort(ordered_cmp); - return _map(zipped, function(x) { return x[0];}); + return _.map(zipped, function(x) { return x[0];}); }; exports.translate = function(x,y) { diff --git a/packages/oncoprintjs/test/index.html b/packages/oncoprintjs/test/index.html index 9e8c5f57242..a0d5b068e14 100644 --- a/packages/oncoprintjs/test/index.html +++ b/packages/oncoprintjs/test/index.html @@ -9,6 +9,7 @@ + diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index 38da52089cf..faf4a3e76da 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -1,143 +1,40 @@ -var _ = require("underscore"); +var _ = require('underscore'); var $ = require('jquery'); -var Oncoprint = require('../../src/js/Oncoprint'); -var onc = new Oncoprint('#onc'); -var genderTrack; +var Oncoprint = require('../../src/js/Oncoprint'); +var cell_padding = 3; +var whitespace_on = true; + +var onc = new Oncoprint('#onc', {cell_padding: cell_padding}); $('#shuffle_btn').click(function() { - onc.sortOnTrack(genderTrack, function(d1, d2) { - var map = {'MALE':0, 'FEMALE':1}; - return map[d1.attr_val] - map[d2.attr_val]; - }); -}) -$('#padding_btn').click(function(){ - onc.setCellPadding(onc.config.cell_padding+1); -}); -$('#width_btn').click(function(){ - onc.setCellWidth(onc.config.cell_width+1); -}); -$('#move_btn').click(function(){ - var newPos = Math.floor(Math.random()*5); - onc.moveTrack('gender', newPos); - console.log('moving gender to '+newPos); -}); -$('#add_tracks_btn').click(function() { - var mutationsTrack = - onc.addTrack(mutationData, {label: 'Mutations'}); - onc.getTrack(mutationsTrack) - .useRenderTemplate('bar_chart', { - data: function(d) { - return d.attr_val; - }, - range:[0,100] - }); - var logMutationsTrack = - onc.addTrack(mutationData, {label: 'Mutations (log scale)'}); - onc.getTrack(logMutationsTrack) - .useRenderTemplate('bar_chart', { - data: function(d) { - return d.attr_val; - }, - range:[0.1,100], - log_scale:true - }); - }); -$('#remove_btn').click(function(){ - onc.removeTrack(mutationsTrack); -}); -var genderData; -var genderDataPromise = $.getJSON('./gbm/gender-gbm.json'); -var geneData; -var geneDataPromise = $.getJSON('./gbm/tp53.json'); -var mutationData; -genderDataPromise.then(function(data) { - console.log("received data"); - console.log(data); - genderData = data.data; - mutationData = genderData.map(function(x) { return $.extend({}, x, {attr_val: (x.attr_val === 'MALE' ? 70 + Math.random()*30 : Math.random()*30)}); }); -}); -geneDataPromise.then(function(data) { - geneData = data; -}); -$.when(geneDataPromise, genderDataPromise).then(function() { - genderTrack = - onc.addTrack(genderData, {label: 'Gender'}); - onc.getTrack(genderTrack) - .useRenderTemplate('categorical_color', { - color: {MALE: '#6699FF', FEMALE:'#FF00FF'}, - category: function(d) { - return d.attr_val; - } - }); - - /*var renderer = onc.addTrack('gender',genderData, {label:'Gender' }).renderer; - renderer.addRule({condition:function(d) { return d.attr_val === 'MALE';}, - d3_shape: d3.select(document.createElementNS('http://www.w3.org/2000/svg', 'rect')), - attrs: {'fill':'rgba(255,0,0,255)', 'width':'100%', 'height':'33.33%', 'y':'33.33%'}, - z_index: 1}); - renderer.addRule({condition:function(d) { return true;}, - d3_shape:d3.select(document.createElementNS('http://www.w3.org/2000/svg', 'rect')), - attrs:{'fill':'rgba(100,100,100,255)', 'width':'100%', 'height':function(d,i) { return 100*i/genderData.length + '%';}}, - z_index: 0}); - /*var renderer = onc.appendTrack('gender2',genderData, {label:'Gender 2' }).renderer.cellRenderer; - renderer.addRule(function(d) { return d.attr_val === 'MALE';}, d3.select(document.createElementNS('http://www.w3.org/2000/svg', 'rect')), {'fill':'rgba(255,0,0,255)', 'width':'100%', 'height':'33.33%', 'y':'33.33%'},1); - renderer.addRule(function(d) { return true;}, d3.select(document.createElementNS('http://www.w3.org/2000/svg', 'rect')), {'fill':'rgba(100,100,100,255)', 'width':'100%', 'height':'100%'}, 0);*/ + onc.sortOnTrack(gender_track, function(d1, d2) { + var map = {'MALE':0, 'FEMALE':1}; + return map[d1.attr_val] - map[d2.attr_val]; + }); }); -//global.Oncoprint = require('../../src/js/oncoprint'); -/* -var renderers = require("../../src/js/renderers"); -var utils = require("../../src/js/utils"); -var config = { rect_height: 20, - rect_padding: 3, - rect_width: 10, - row_height: 25, - mutation_fill: 'green', - width: 750, - cna_fills: { - null: 'grey', - undefined: 'grey', - AMPLIFIED: 'red', - HOMODELETED: 'blue' - } -}; - -module.exports = function test_script(filenames, div_selector_string) { - // filenames has length 2. - var genomic_file = filenames[0]; - var additional_file = filenames[1]; - - // genomic data - return d3.json(genomic_file, function(data) { - var oncoprint = OncoPrint(); - oncoprint(div_selector_string, data); - - // additional clinical data if it has been specified. - var is_inserted = false; - // TODO is_inserted is a hack. At some point, - // should add a remove row function too and test these together. - if (additional_file !== undefined) { - d3.json(additional_file, function(payload) { - d3.select("#insert-gender-data-gbm").on('click', function() { - if (!is_inserted) { - oncoprint.insert_row(div_selector_string, payload.data, renderers.gender_rule); - is_inserted = true; - } - }); - }); - } - - // shuffle order button - if (genomic_file.indexOf("gbm") !== -1) - d3.select('#shuffle-gbm').on('click', function() { - // get and shuffle order - var container = d3.select(div_selector_string); - var sampleids = container.datum()[0].map(function(d) { return d.sample_id || d.sample; }); - var shuffled_sampleids = d3.shuffle(sampleids); +$('#toggle_whitespace').click(function() { + whitespace_on = !whitespace_on; + if (whitespace_on) { + onc.setCellPadding(cell_padding); + } else { + onc.setCellPadding(0); + } +}); - var sampleid_to_array_index = utils.invert_array(shuffled_sampleids); - oncoprint.resort(div_selector_string, sampleid_to_array_index); - }); +var gender_data; +var gender_track_id; +var gender_data_promise = $.getJSON('./gbm/gender-gbm.json'); - }); -};*/ +gender_data_promise.then(function(data) { + gender_data = data.data; +}); +$.when(gender_data_promise).then(function() { + gender_track_id = onc.addTrack(gender_data, {label: 'Gender'}); + onc.getTrack(gender_track_id).useRenderTemplate('categorical_color', { + color: {MALE: '#6699FF', FEMALE: '#FF00FF'}, + category: function(d) { + return d.attr_val; + } + }); +}); \ No newline at end of file diff --git a/packages/oncoprintjs/test/js/test_page_old.js b/packages/oncoprintjs/test/js/test_page_old.js new file mode 100644 index 00000000000..38da52089cf --- /dev/null +++ b/packages/oncoprintjs/test/js/test_page_old.js @@ -0,0 +1,143 @@ +var _ = require("underscore"); +var $ = require('jquery'); + +var Oncoprint = require('../../src/js/Oncoprint'); +var onc = new Oncoprint('#onc'); +var genderTrack; +$('#shuffle_btn').click(function() { + onc.sortOnTrack(genderTrack, function(d1, d2) { + var map = {'MALE':0, 'FEMALE':1}; + return map[d1.attr_val] - map[d2.attr_val]; + }); +}) +$('#padding_btn').click(function(){ + onc.setCellPadding(onc.config.cell_padding+1); +}); +$('#width_btn').click(function(){ + onc.setCellWidth(onc.config.cell_width+1); +}); +$('#move_btn').click(function(){ + var newPos = Math.floor(Math.random()*5); + onc.moveTrack('gender', newPos); + console.log('moving gender to '+newPos); +}); +$('#add_tracks_btn').click(function() { + var mutationsTrack = + onc.addTrack(mutationData, {label: 'Mutations'}); + onc.getTrack(mutationsTrack) + .useRenderTemplate('bar_chart', { + data: function(d) { + return d.attr_val; + }, + range:[0,100] + }); + var logMutationsTrack = + onc.addTrack(mutationData, {label: 'Mutations (log scale)'}); + onc.getTrack(logMutationsTrack) + .useRenderTemplate('bar_chart', { + data: function(d) { + return d.attr_val; + }, + range:[0.1,100], + log_scale:true + }); + }); +$('#remove_btn').click(function(){ + onc.removeTrack(mutationsTrack); +}); +var genderData; +var genderDataPromise = $.getJSON('./gbm/gender-gbm.json'); +var geneData; +var geneDataPromise = $.getJSON('./gbm/tp53.json'); +var mutationData; +genderDataPromise.then(function(data) { + console.log("received data"); + console.log(data); + genderData = data.data; + mutationData = genderData.map(function(x) { return $.extend({}, x, {attr_val: (x.attr_val === 'MALE' ? 70 + Math.random()*30 : Math.random()*30)}); }); +}); +geneDataPromise.then(function(data) { + geneData = data; +}); +$.when(geneDataPromise, genderDataPromise).then(function() { + genderTrack = + onc.addTrack(genderData, {label: 'Gender'}); + onc.getTrack(genderTrack) + .useRenderTemplate('categorical_color', { + color: {MALE: '#6699FF', FEMALE:'#FF00FF'}, + category: function(d) { + return d.attr_val; + } + }); + + /*var renderer = onc.addTrack('gender',genderData, {label:'Gender' }).renderer; + renderer.addRule({condition:function(d) { return d.attr_val === 'MALE';}, + d3_shape: d3.select(document.createElementNS('http://www.w3.org/2000/svg', 'rect')), + attrs: {'fill':'rgba(255,0,0,255)', 'width':'100%', 'height':'33.33%', 'y':'33.33%'}, + z_index: 1}); + renderer.addRule({condition:function(d) { return true;}, + d3_shape:d3.select(document.createElementNS('http://www.w3.org/2000/svg', 'rect')), + attrs:{'fill':'rgba(100,100,100,255)', 'width':'100%', 'height':function(d,i) { return 100*i/genderData.length + '%';}}, + z_index: 0}); + /*var renderer = onc.appendTrack('gender2',genderData, {label:'Gender 2' }).renderer.cellRenderer; + renderer.addRule(function(d) { return d.attr_val === 'MALE';}, d3.select(document.createElementNS('http://www.w3.org/2000/svg', 'rect')), {'fill':'rgba(255,0,0,255)', 'width':'100%', 'height':'33.33%', 'y':'33.33%'},1); + renderer.addRule(function(d) { return true;}, d3.select(document.createElementNS('http://www.w3.org/2000/svg', 'rect')), {'fill':'rgba(100,100,100,255)', 'width':'100%', 'height':'100%'}, 0);*/ +}); +//global.Oncoprint = require('../../src/js/oncoprint'); +/* +var renderers = require("../../src/js/renderers"); +var utils = require("../../src/js/utils"); + +var config = { rect_height: 20, + rect_padding: 3, + rect_width: 10, + row_height: 25, + mutation_fill: 'green', + width: 750, + cna_fills: { + null: 'grey', + undefined: 'grey', + AMPLIFIED: 'red', + HOMODELETED: 'blue' + } +}; + +module.exports = function test_script(filenames, div_selector_string) { + // filenames has length 2. + var genomic_file = filenames[0]; + var additional_file = filenames[1]; + + // genomic data + return d3.json(genomic_file, function(data) { + var oncoprint = OncoPrint(); + oncoprint(div_selector_string, data); + + // additional clinical data if it has been specified. + var is_inserted = false; + // TODO is_inserted is a hack. At some point, + // should add a remove row function too and test these together. + if (additional_file !== undefined) { + d3.json(additional_file, function(payload) { + d3.select("#insert-gender-data-gbm").on('click', function() { + if (!is_inserted) { + oncoprint.insert_row(div_selector_string, payload.data, renderers.gender_rule); + is_inserted = true; + } + }); + }); + } + + // shuffle order button + if (genomic_file.indexOf("gbm") !== -1) + d3.select('#shuffle-gbm').on('click', function() { + // get and shuffle order + var container = d3.select(div_selector_string); + var sampleids = container.datum()[0].map(function(d) { return d.sample_id || d.sample; }); + var shuffled_sampleids = d3.shuffle(sampleids); + + var sampleid_to_array_index = utils.invert_array(shuffled_sampleids); + oncoprint.resort(div_selector_string, sampleid_to_array_index); + }); + + }); +};*/ From 20dba8a2ddf2e039f15f5e525e278e898355ebdd Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Wed, 20 May 2015 13:17:44 -0400 Subject: [PATCH 041/343] Adding GNU LGPL License --- packages/oncoprintjs/LICENSE | 165 ++++++++++++++++++ .../oncoprintjs/src/js/D3SVGCellRenderer.js | 37 +++- .../oncoprintjs/src/js/OncoprintToolbar.js | 31 +++- packages/oncoprintjs/src/js/ReadOnlyObject.js | 31 +++- packages/oncoprintjs/src/js/events.js | 34 +++- packages/oncoprintjs/src/js/oncoprint.js | 31 +++- packages/oncoprintjs/src/js/signals.js | 31 +++- packages/oncoprintjs/src/js/track.js | 42 ++++- packages/oncoprintjs/src/js/utils.js | 31 +++- 9 files changed, 422 insertions(+), 11 deletions(-) create mode 100644 packages/oncoprintjs/LICENSE diff --git a/packages/oncoprintjs/LICENSE b/packages/oncoprintjs/LICENSE new file mode 100644 index 00000000000..b2f02dd4696 --- /dev/null +++ b/packages/oncoprintjs/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/packages/oncoprintjs/src/js/D3SVGCellRenderer.js b/packages/oncoprintjs/src/js/D3SVGCellRenderer.js index 98ae76a8279..2f5b84f9cb3 100644 --- a/packages/oncoprintjs/src/js/D3SVGCellRenderer.js +++ b/packages/oncoprintjs/src/js/D3SVGCellRenderer.js @@ -1,3 +1,33 @@ +/* + * Copyright (c) 2015 Memorial Sloan-Kettering Cancer Center. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS + * FOR A PARTICULAR PURPOSE. The software and documentation provided hereunder + * is on an "as is" basis, and Memorial Sloan-Kettering Cancer Center has no + * obligations to provide maintenance, support, updates, enhancements or + * modifications. In no event shall Memorial Sloan-Kettering Cancer Center be + * liable to any party for direct, indirect, special, incidental or + * consequential damages, including lost profits, arising out of the use of this + * software and its documentation, even if Memorial Sloan-Kettering Cancer + * Center has been advised of the possibility of such damage. + */ + +/* + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + var utils = require('./utils'); var $ = require('jquery'); var _ = require('underscore'); @@ -121,6 +151,11 @@ function D3SVGCellRenderer(data, track_config) { }; self.updateCells = function() { + // enter/exit as necessary + var g_attached = self.svg.selectAll('g.cell').data(self.data, self.track_config.get('datum_id')); + g_attached.enter().append('g').classed('cell', true); + g_attached.exit().remove(); + self.g = self.svg.selectAll('g').data(self.data, self.track_config.get('datum_id')) var id_order = utils.invert_array(self.track_config.get('id_order')); self.g.transition() .attr('transform', function(d,i) { @@ -337,4 +372,4 @@ function D3SVGCellRenderer(data, track_config) { }; }; -module.exports = D3SVGCellRenderer; \ No newline at end of file +module.exports = D3SVGCellRenderer; diff --git a/packages/oncoprintjs/src/js/OncoprintToolbar.js b/packages/oncoprintjs/src/js/OncoprintToolbar.js index f17e2f37714..fbc3e2aebf7 100644 --- a/packages/oncoprintjs/src/js/OncoprintToolbar.js +++ b/packages/oncoprintjs/src/js/OncoprintToolbar.js @@ -1,3 +1,32 @@ +/* + * Copyright (c) 2015 Memorial Sloan-Kettering Cancer Center. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS + * FOR A PARTICULAR PURPOSE. The software and documentation provided hereunder + * is on an "as is" basis, and Memorial Sloan-Kettering Cancer Center has no + * obligations to provide maintenance, support, updates, enhancements or + * modifications. In no event shall Memorial Sloan-Kettering Cancer Center be + * liable to any party for direct, indirect, special, incidental or + * consequential damages, including lost profits, arising out of the use of this + * software and its documentation, even if Memorial Sloan-Kettering Cancer + * Center has been advised of the possibility of such damage. + */ + +/* + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ var toolbar_events = { }; @@ -13,4 +42,4 @@ function ToolbarRenderer() { } -module.exports = OncoprintToolbar; \ No newline at end of file +module.exports = OncoprintToolbar; diff --git a/packages/oncoprintjs/src/js/ReadOnlyObject.js b/packages/oncoprintjs/src/js/ReadOnlyObject.js index 7ceeac96714..962af464e1e 100644 --- a/packages/oncoprintjs/src/js/ReadOnlyObject.js +++ b/packages/oncoprintjs/src/js/ReadOnlyObject.js @@ -1,3 +1,32 @@ +/* + * Copyright (c) 2015 Memorial Sloan-Kettering Cancer Center. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS + * FOR A PARTICULAR PURPOSE. The software and documentation provided hereunder + * is on an "as is" basis, and Memorial Sloan-Kettering Cancer Center has no + * obligations to provide maintenance, support, updates, enhancements or + * modifications. In no event shall Memorial Sloan-Kettering Cancer Center be + * liable to any party for direct, indirect, special, incidental or + * consequential damages, including lost profits, arising out of the use of this + * software and its documentation, even if Memorial Sloan-Kettering Cancer + * Center has been advised of the possibility of such damage. + */ + +/* + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ var $ = require('jquery'); function ReadOnlyObject(obj) { var self = this; @@ -18,4 +47,4 @@ function ReadOnlyObject(obj) { } }; -module.exports = ReadOnlyObject; \ No newline at end of file +module.exports = ReadOnlyObject; diff --git a/packages/oncoprintjs/src/js/events.js b/packages/oncoprintjs/src/js/events.js index ee8ca3a87b1..8fbb39e0e01 100644 --- a/packages/oncoprintjs/src/js/events.js +++ b/packages/oncoprintjs/src/js/events.js @@ -1,3 +1,32 @@ +/* + * Copyright (c) 2015 Memorial Sloan-Kettering Cancer Center. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS + * FOR A PARTICULAR PURPOSE. The software and documentation provided hereunder + * is on an "as is" basis, and Memorial Sloan-Kettering Cancer Center has no + * obligations to provide maintenance, support, updates, enhancements or + * modifications. In no event shall Memorial Sloan-Kettering Cancer Center be + * liable to any party for direct, indirect, special, incidental or + * consequential damages, including lost profits, arising out of the use of this + * software and its documentation, even if Memorial Sloan-Kettering Cancer + * Center has been advised of the possibility of such damage. + */ + +/* + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ module.exports = { ADD_TRACK: 'add_track.oncoprint', REMOVE_TRACK: 'remove_track.oncoprint', @@ -10,5 +39,6 @@ module.exports = { CELL_MOUSELEAVE: 'cell_mouseleave.oncoprint', ONCOPRINT_MOUSEENTER: 'oncoprint_mouseenter.oncoprint', ONCOPRINT_MOUSELEAVE: 'oncoprint_mouseleave.oncoprint', - SET_PRE_TRACK_PADDING: 'set_pre_track_padding.oncoprint' -}; \ No newline at end of file + SET_PRE_TRACK_PADDING: 'set_pre_track_padding.oncoprint', + TRACK_INIT: 'init.track.oncoprint', +}; diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 958bc6a1d27..21e83bd27c6 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -1,3 +1,32 @@ +/* + * Copyright (c) 2015 Memorial Sloan-Kettering Cancer Center. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS + * FOR A PARTICULAR PURPOSE. The software and documentation provided hereunder + * is on an "as is" basis, and Memorial Sloan-Kettering Cancer Center has no + * obligations to provide maintenance, support, updates, enhancements or + * modifications. In no event shall Memorial Sloan-Kettering Cancer Center be + * liable to any party for direct, indirect, special, incidental or + * consequential damages, including lost profits, arising out of the use of this + * software and its documentation, even if Memorial Sloan-Kettering Cancer + * Center has been advised of the possibility of such damage. + */ + +/* + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ var Track = require('./Track'); var _ = require('underscore'); var d3 = require('d3'); @@ -144,4 +173,4 @@ function OncoprintTableRenderer(container_selector_string) { }; } -module.exports = Oncoprint; \ No newline at end of file +module.exports = Oncoprint; diff --git a/packages/oncoprintjs/src/js/signals.js b/packages/oncoprintjs/src/js/signals.js index ed8a1446766..8436eef5d37 100644 --- a/packages/oncoprintjs/src/js/signals.js +++ b/packages/oncoprintjs/src/js/signals.js @@ -1,3 +1,32 @@ +/* + * Copyright (c) 2015 Memorial Sloan-Kettering Cancer Center. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS + * FOR A PARTICULAR PURPOSE. The software and documentation provided hereunder + * is on an "as is" basis, and Memorial Sloan-Kettering Cancer Center has no + * obligations to provide maintenance, support, updates, enhancements or + * modifications. In no event shall Memorial Sloan-Kettering Cancer Center be + * liable to any party for direct, indirect, special, incidental or + * consequential damages, including lost profits, arising out of the use of this + * software and its documentation, even if Memorial Sloan-Kettering Cancer + * Center has been advised of the possibility of such damage. + */ + +/* + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ module.exports = { REQUEST_PRE_TRACK_PADDING: 'request_pre_track_padding.signal.oncoprint', -} \ No newline at end of file +} diff --git a/packages/oncoprintjs/src/js/track.js b/packages/oncoprintjs/src/js/track.js index 6ef03890c51..1326c3a2e72 100644 --- a/packages/oncoprintjs/src/js/track.js +++ b/packages/oncoprintjs/src/js/track.js @@ -1,3 +1,32 @@ +/* + * Copyright (c) 2015 Memorial Sloan-Kettering Cancer Center. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS + * FOR A PARTICULAR PURPOSE. The software and documentation provided hereunder + * is on an "as is" basis, and Memorial Sloan-Kettering Cancer Center has no + * obligations to provide maintenance, support, updates, enhancements or + * modifications. In no event shall Memorial Sloan-Kettering Cancer Center be + * liable to any party for direct, indirect, special, incidental or + * consequential damages, including lost profits, arising out of the use of this + * software and its documentation, even if Memorial Sloan-Kettering Cancer + * Center has been advised of the possibility of such damage. + */ + +/* + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ var _ = require('underscore'); var d3 = require('d3'); var $ = require('jquery'); @@ -22,6 +51,7 @@ function Track(data, config, oncoprint_config) { self.oncoprint_config = oncoprint_config; self.data = data; + self.filtered_data = data; var cell_renderer; var data_map = _.reduce(data, function(acc, next) { acc[self.config.datum_id(next)] = next; @@ -78,7 +108,13 @@ function Track(data, config, oncoprint_config) { self.renderer.useTemplate(templName, params); }; - $(self).trigger('init.track.oncoprint', {label_text: self.getLabel()}); + self.filterData = function(filter) { + self.filtered_data = self.data.filter() + + $(self).trigger(events.TRACK_FILTER_DATA, {filtered_data: self.filtered_data}); + }; + + $(self).trigger(events.TRACK_INIT, {label_text: self.getLabel()}); } function TrackTableRenderer(track_config, cell_renderer) { @@ -94,7 +130,7 @@ function TrackTableRenderer(track_config, cell_renderer) { var label_text; self.bindEvents = function(track) { - $(track).on('init.track.oncoprint', function(e, data) { + $(track).on(events.TRACK_INIT, function(e, data) { label_text = data.label_text; }); }; @@ -127,4 +163,4 @@ function TrackTableRenderer(track_config, cell_renderer) { }; } -module.exports = Track; \ No newline at end of file +module.exports = Track; diff --git a/packages/oncoprintjs/src/js/utils.js b/packages/oncoprintjs/src/js/utils.js index c04c27bf328..3e55c834ccd 100644 --- a/packages/oncoprintjs/src/js/utils.js +++ b/packages/oncoprintjs/src/js/utils.js @@ -1,3 +1,32 @@ +/* + * Copyright (c) 2015 Memorial Sloan-Kettering Cancer Center. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS + * FOR A PARTICULAR PURPOSE. The software and documentation provided hereunder + * is on an "as is" basis, and Memorial Sloan-Kettering Cancer Center has no + * obligations to provide maintenance, support, updates, enhancements or + * modifications. In no event shall Memorial Sloan-Kettering Cancer Center be + * liable to any party for direct, indirect, special, incidental or + * consequential damages, including lost profits, arising out of the use of this + * software and its documentation, even if Memorial Sloan-Kettering Cancer + * Center has been advised of the possibility of such damage. + */ + +/* + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ var _ = require("underscore"); var exports = module.exports = {}; @@ -57,4 +86,4 @@ exports.assert = function(bool, msg) { if (!bool) { throw msg; } -} \ No newline at end of file +} From 1a092943eb6bc2b8b1cbd66c103e706a7c3ba61b Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Wed, 20 May 2015 14:38:48 -0400 Subject: [PATCH 042/343] minor changes, making cell movement transitions optional/configurable --- packages/oncoprintjs/src/js/D3SVGCellRenderer.js | 9 +++++++-- packages/oncoprintjs/src/js/OncoprintToolbar.js | 2 +- packages/oncoprintjs/test/index.html | 4 ++-- packages/oncoprintjs/test/js/test_page.js | 2 +- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/packages/oncoprintjs/src/js/D3SVGCellRenderer.js b/packages/oncoprintjs/src/js/D3SVGCellRenderer.js index 2f5b84f9cb3..c934761205e 100644 --- a/packages/oncoprintjs/src/js/D3SVGCellRenderer.js +++ b/packages/oncoprintjs/src/js/D3SVGCellRenderer.js @@ -157,8 +157,13 @@ function D3SVGCellRenderer(data, track_config) { g_attached.exit().remove(); self.g = self.svg.selectAll('g').data(self.data, self.track_config.get('datum_id')) var id_order = utils.invert_array(self.track_config.get('id_order')); - self.g.transition() - .attr('transform', function(d,i) { + var g_target; + if (self.track_config.get('transition') > 0) { + g_target = self.g.transition().duration(self.track_config.get('transition')); + } else { + g_target = self.g; + } + g_target.attr('transform', function(d,i) { return utils.translate(self.track_config.get('pre_track_padding') + id_order[self.track_config.get('datum_id')(d)]*(self.track_config.get('cell_width') + self.track_config.get('cell_padding')), 0); }); diff --git a/packages/oncoprintjs/src/js/OncoprintToolbar.js b/packages/oncoprintjs/src/js/OncoprintToolbar.js index fbc3e2aebf7..f1dfcd15923 100644 --- a/packages/oncoprintjs/src/js/OncoprintToolbar.js +++ b/packages/oncoprintjs/src/js/OncoprintToolbar.js @@ -31,7 +31,7 @@ var toolbar_events = { }; -function OncoprintToolbar() { +function OncoprintToolbar(config) { var self = this; self.bindEvents = function(oncoprint) { diff --git a/packages/oncoprintjs/test/index.html b/packages/oncoprintjs/test/index.html index a0d5b068e14..fbc86364577 100644 --- a/packages/oncoprintjs/test/index.html +++ b/packages/oncoprintjs/test/index.html @@ -6,9 +6,9 @@
- + diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index faf4a3e76da..c4a6a537fd9 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -7,7 +7,7 @@ var whitespace_on = true; var onc = new Oncoprint('#onc', {cell_padding: cell_padding}); $('#shuffle_btn').click(function() { - onc.sortOnTrack(gender_track, function(d1, d2) { + onc.sortOnTrack(gender_track_id, function(d1, d2) { var map = {'MALE':0, 'FEMALE':1}; return map[d1.attr_val] - map[d2.attr_val]; }); From b100ef2e822d240125c415fd6e117ba665a318ee Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Tue, 26 May 2015 14:12:41 -0400 Subject: [PATCH 043/343] make a couple of renderer methods private --- .../oncoprintjs/src/js/D3SVGCellRenderer.js | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/packages/oncoprintjs/src/js/D3SVGCellRenderer.js b/packages/oncoprintjs/src/js/D3SVGCellRenderer.js index c934761205e..6d40f2c57ba 100644 --- a/packages/oncoprintjs/src/js/D3SVGCellRenderer.js +++ b/packages/oncoprintjs/src/js/D3SVGCellRenderer.js @@ -68,7 +68,7 @@ function D3SVGRuleset(track_config) { var applyRule = function(params, d3_g_selection, d3_data, d3_data_key) { d3_g_selection = d3_g_selection.data( d3_data.filter(params.condition || function(d) { return true; }), - d3_data_key + d3_data_key || function(d) { return d; } ); var elts = d3_g_selection.select(function() { return this.appendChild(params.shape.node().cloneNode(true)); @@ -119,19 +119,19 @@ function D3SVGCellRenderer(data, track_config) { self.svg; self.g; - self.parseRuleset = function(json_rules) { + self.setRuleset = function(json_rules) { self.rule_set.fromJSON(json_rules); }; self.addRule = function(params) { var ret = self.rule_set.addRule(params.condition, params.d3_shape, params.attrs, params.z_index); - self.updateCells(); + updateCells(); return ret; }; self.removeRule = function(rule_id) { self.rule_set.removeRule(rule_id); - self.updateCells(); + updateCells(); }; self.init = function(cell_area) { @@ -139,18 +139,18 @@ function D3SVGCellRenderer(data, track_config) { self.cell_area.selectAll('*').remove(); self.svg = self.cell_area.append('svg') - self.updateCellArea(); + updateCellArea(); self.g = self.svg.selectAll('g').data(self.data, self.track_config.get('datum_id')).enter().append('g').classed('cell', true); - self.updateCells(); + updateCells(); }; - self.updateCellArea = function() { + var updateCellArea = function() { self.svg.attr('width', self.track_config.get('pre_track_padding') + (self.track_config.get('cell_width') + self.track_config.get('cell_padding'))*self.data.length) .attr('height', self.track_config.get('track_height')); }; - self.updateCells = function() { + var updateCells = function() { // enter/exit as necessary var g_attached = self.svg.selectAll('g.cell').data(self.data, self.track_config.get('datum_id')); g_attached.enter().append('g').classed('cell', true); @@ -167,16 +167,16 @@ function D3SVGCellRenderer(data, track_config) { return utils.translate(self.track_config.get('pre_track_padding') + id_order[self.track_config.get('datum_id')(d)]*(self.track_config.get('cell_width') + self.track_config.get('cell_padding')), 0); }); - self.drawCells(); + drawCells(); }; - self.drawCells = function() { + var drawCells = function() { self.g.selectAll('*').remove(); self.rule_set.apply(self.g, self.data, self.track_config.get('datum_id')); - self.drawHitZones(); + drawHitZones(); }; - self.drawHitZones = function() { + var drawHitZones = function() { self.g.selectAll('rect.hit').remove(); var hits = self.g.append('rect').classed('hit', true) .attr('width', self.track_config.get('cell_width')) @@ -360,16 +360,16 @@ function D3SVGCellRenderer(data, track_config) { }; self.bindEvents = function(track) { $(track).on(events.SORT, function() { - self.updateCells(); + updateCells(); }).on(events.SET_CELL_WIDTH, function() { - self.updateCells(); - self.updateCellArea(); + updateCells(); + updateCellArea(); }).on(events.SET_CELL_PADDING, function() { - self.updateCells(); - self.updateCellArea(); + updateCells(); + updateCellArea(); }).on(events.SET_PRE_TRACK_PADDING, function(e,data) { - self.updateCells(); - self.updateCellArea(); + updateCells(); + updateCellArea(); }); $(self).on(signals.REQUEST_PRE_TRACK_PADDING, function(e, data) { $(track).trigger(signals.REQUEST_PRE_TRACK_PADDING, data); From 8f440ea49887e43496b7267748907963ceb64bc9 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Tue, 26 May 2015 17:17:25 -0400 Subject: [PATCH 044/343] fix label columns, only scroll cells --- packages/oncoprintjs/gulpfile.js | 6 ++++- packages/oncoprintjs/src/css/oncoprint.css | 11 +++++++++ packages/oncoprintjs/src/js/oncoprint.js | 26 +++++++++++++++------- packages/oncoprintjs/src/js/track.js | 24 ++++++++++++-------- packages/oncoprintjs/test/index.html | 1 + 5 files changed, 50 insertions(+), 18 deletions(-) create mode 100644 packages/oncoprintjs/src/css/oncoprint.css diff --git a/packages/oncoprintjs/gulpfile.js b/packages/oncoprintjs/gulpfile.js index 5386a391202..7219d96f8b3 100644 --- a/packages/oncoprintjs/gulpfile.js +++ b/packages/oncoprintjs/gulpfile.js @@ -43,6 +43,10 @@ gulp.task('test', function() { // Copy over the data. gulp.src('test/data/**') .pipe(gulp.dest('dist/test/')); + + // Copy over the css + gulp.src('src/css/**') + .pipe(gulp.dest('dist/test/')); }); gulp.task('prod', function() { @@ -74,5 +78,5 @@ gulp.task('default', ['clean'], function() { // Watch gulp.task('watch', function() { - gulp.watch(['src/js/**/*.js', 'test/*.html', 'test/js/**/*.js', 'test/spec/*.js'], ['test']); + gulp.watch(['src/js/**/*.js', 'src/css/**/*.css', 'test/*.html', 'test/js/**/*.js', 'test/spec/*.js'], ['test']); }); diff --git a/packages/oncoprintjs/src/css/oncoprint.css b/packages/oncoprintjs/src/css/oncoprint.css new file mode 100644 index 00000000000..3b4cac391fc --- /dev/null +++ b/packages/oncoprintjs/src/css/oncoprint.css @@ -0,0 +1,11 @@ +.oncoprint_container { + //position: relative; +} + +.fixed_oncoprint_table_container { + float:left; +} +.scrolling_oncoprint_table_container { + overflow: auto; + width: auto; +} \ No newline at end of file diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 21e83bd27c6..ab90c0c3594 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -134,19 +134,23 @@ function Oncoprint(container_selector_string, config) { function OncoprintTableRenderer(container_selector_string) { var self = this; - self.container = d3.select(container_selector_string); - self.table; - self.$table; + self.container = d3.select(container_selector_string).classed('oncoprint_container', true); + self.fixed_table; + self.$fixed_table; + self.scrolling_table; + self.$scrolling_table; (function initTable(self) { self.container.selectAll('*').remove(); - self.table = self.container.append('table'); - self.$table = $(self.table.node()); + self.fixed_table = self.container.append('div').classed('fixed_oncoprint_table_container', true).append('table') + self.$fixed_table = $(self.fixed_table.node()); + self.scrolling_table = self.container.append('div').classed('scrolling_oncoprint_table_container', true).append('table') + self.$scrolling_table = $(self.scrolling_table.node()); })(self); self.bindEvents = function(oncoprint) { $(oncoprint).on(events.ADD_TRACK, function(e, data) { - data.track.renderer.init(self.table.append('tr')); + data.track.renderer.init(self.fixed_table.append('tr'), self.scrolling_table.append('tr')); }); $(oncoprint).on(events.MOVE_TRACK, function(e, data) { var track_name = data.track_name; @@ -164,10 +168,16 @@ function OncoprintTableRenderer(container_selector_string) { var track = data.track; track.renderer.$row.remove(); }); - self.$table.mouseenter(function() { + self.$fixed_table.mouseenter(function() { + $(oncoprint).trigger(events.ONCOPRINT_MOUSEENTER); + }); + self.$fixed_table.mouseleave(function() { + $(oncoprint).trigger(events.ONCOPRINT_MOUSELEAVE); + }); + self.$scrolling_table.mouseenter(function() { $(oncoprint).trigger(events.ONCOPRINT_MOUSEENTER); }); - self.$table.mouseleave(function() { + self.$scrolling_table.mouseleave(function() { $(oncoprint).trigger(events.ONCOPRINT_MOUSELEAVE); }); }; diff --git a/packages/oncoprintjs/src/js/track.js b/packages/oncoprintjs/src/js/track.js index 1326c3a2e72..0b474adcfec 100644 --- a/packages/oncoprintjs/src/js/track.js +++ b/packages/oncoprintjs/src/js/track.js @@ -122,8 +122,10 @@ function TrackTableRenderer(track_config, cell_renderer) { var self = this; var cell_renderer = cell_renderer; - self.row; - self.$row; + self.fixed_row; + self.$fixed_row; + self.scrolling_row; + self.$scrolling_row; self.label_area; self.between_area; self.cell_area; @@ -137,19 +139,23 @@ function TrackTableRenderer(track_config, cell_renderer) { var renderLabel = function(label_area) { label_area.selectAll('*').remove(); - label_area.append('p').text(label_text); + label_area.append('p').text(label_text).style('display', 'inline'); }; var initCells = function(cell_area) { cell_renderer.init(cell_area); }; - self.init = function(row) { - self.row = row; - self.$row = $(self.row.node()); - self.label_area = row.append('td').classed('track_label', true); - self.between_area = row.append('td').classed('track_between', true).style('position', 'relative'); - self.cell_area = row.append('td').classed('track_cells', true); + self.init = function(fixed_row, scrolling_row) { + self.fixed_row = fixed_row; + self.$fixed_row = $(self.fixed_row.node()); + self.scrolling_row = scrolling_row; + self.$scrolling_row = $(self.scrolling_row.node()); + + self.label_area = self.fixed_row.append('td').classed('track_label', true); + self.between_area = self.fixed_row.append('td').classed('track_between', true).style('position', 'relative'); + self.between_area.append('p').style('display', 'inline').text('yoyoyo'); + self.cell_area = self.scrolling_row.append('td').classed('track_cells', true); renderLabel(self.label_area); initCells(self.cell_area) }; diff --git a/packages/oncoprintjs/test/index.html b/packages/oncoprintjs/test/index.html index fbc86364577..b17019ed3be 100644 --- a/packages/oncoprintjs/test/index.html +++ b/packages/oncoprintjs/test/index.html @@ -2,6 +2,7 @@ Oncoprint Test Page +
From 0462ea5c60ce85a8210a234935db6f3cdb2d3169 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Tue, 26 May 2015 18:03:11 -0400 Subject: [PATCH 045/343] start of legend system. but actually need to redo rules to have rule types now --- .../oncoprintjs/src/js/D3SVGCellRenderer.js | 39 +++++++++++++------ 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/packages/oncoprintjs/src/js/D3SVGCellRenderer.js b/packages/oncoprintjs/src/js/D3SVGCellRenderer.js index 6d40f2c57ba..8d526d8e9c2 100644 --- a/packages/oncoprintjs/src/js/D3SVGCellRenderer.js +++ b/packages/oncoprintjs/src/js/D3SVGCellRenderer.js @@ -44,7 +44,7 @@ function D3SVGRuleset(track_config) { if (z_index === undefined) { z_index = rule_id; } - self.rule_map[rule_id] = {condition: condition, shape: d3_shape, attrs: attrs, z_index: z_index}; + self.rule_map[rule_id] = {condition: condition || function(d) { return true; }, shape: d3_shape, attrs: attrs, z_index: z_index, id:rule_id}; return rule_id; }; @@ -52,6 +52,10 @@ function D3SVGRuleset(track_config) { delete self.rule_map[rule_id]; }; + self.getRule = function(rule_id) { + return self.rule_map[rule_id]; + } + var percentToPx = function(attr_val, attr_name) { // convert a percentage to a local pixel coordinate var width_like = ['width', 'x']; @@ -65,15 +69,19 @@ function D3SVGRuleset(track_config) { return attr_val+''; }; - var applyRule = function(params, d3_g_selection, d3_data, d3_data_key) { + var filterData = function(rule_params, d3_data) { + return d3_data.filter(rule_params.condition); + }; + + var applyRule = function(rule_params, d3_g_selection, d3_data) { d3_g_selection = d3_g_selection.data( - d3_data.filter(params.condition || function(d) { return true; }), - d3_data_key || function(d) { return d; } + filterData(rule_params, d3_data), + self.track_config.get('datum_id'), ); var elts = d3_g_selection.select(function() { - return this.appendChild(params.shape.node().cloneNode(true)); + return this.appendChild(rule_params.shape.node().cloneNode(true)); }); - _.each(params.attrs, function(val, key) { + _.each(rule_params.attrs, function(val, key) { elts.attr(key, function(d,i) { var curr_val = val; if (typeof curr_val === 'function') { @@ -95,10 +103,10 @@ function D3SVGRuleset(track_config) { ); }; - self.apply = function(d3_g_selection, d3_data, d3_data_key) { + self.apply = function(d3_g_selection, d3_data) { var rules = getOrderedRules(); _.each(rules, function(rule) { - applyRule(rule, d3_g_selection, d3_data, d3_data_key); + applyRule(rule, d3_g_selection, d3_data, self.track_config.get('datum_id')); }); }; @@ -108,6 +116,11 @@ function D3SVGRuleset(track_config) { self.addRule(rule.condition, rule.d3_shape, rule.attrs, rule.z_index); }); }; + + self.getLegendMap = function(only_active, d3_data) { + // returns map from rule_id to svg element + // if only_active is true, then only give back the rules that are used at least once + }; } function D3SVGCellRenderer(data, track_config) { @@ -119,14 +132,18 @@ function D3SVGCellRenderer(data, track_config) { self.svg; self.g; + self.getLegendMap = function(only_active) { + return self.rule_set.getLegendMap(only_active, self.data); + }; + self.setRuleset = function(json_rules) { self.rule_set.fromJSON(json_rules); }; self.addRule = function(params) { - var ret = self.rule_set.addRule(params.condition, params.d3_shape, params.attrs, params.z_index); + var rule_id = self.rule_set.addRule(params.condition, params.d3_shape, params.attrs, params.z_index); updateCells(); - return ret; + return rule_id; }; self.removeRule = function(rule_id) { @@ -172,7 +189,7 @@ function D3SVGCellRenderer(data, track_config) { var drawCells = function() { self.g.selectAll('*').remove(); - self.rule_set.apply(self.g, self.data, self.track_config.get('datum_id')); + self.rule_set.apply(self.g, self.data); drawHitZones(); }; From 0752af3a4dd12fb56507cbbc88f6866bcd431960 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Tue, 26 May 2015 18:52:01 -0400 Subject: [PATCH 046/343] beginning of linear range rule, system to be in place for better legend creation --- packages/oncoprintjs/src/js/D3SVGCellRenderer.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/oncoprintjs/src/js/D3SVGCellRenderer.js b/packages/oncoprintjs/src/js/D3SVGCellRenderer.js index 8d526d8e9c2..23c1cb4da3e 100644 --- a/packages/oncoprintjs/src/js/D3SVGCellRenderer.js +++ b/packages/oncoprintjs/src/js/D3SVGCellRenderer.js @@ -34,6 +34,14 @@ var _ = require('underscore'); var events = require('./events'); var signals = require('./signals'); +function D3SVGLinearRangeRule(condition, d3_shape, attrs, attr_bounds) { + +}; + +function D3SVGStaticRule(condition, d3_shape) { + +}; + function D3SVGRuleset(track_config) { var self = this; self.rule_map = {}; From b0b0a8582f86e2fefbb65f9af013b4f1b6e00f3d Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Mon, 8 Jun 2015 18:16:32 -0400 Subject: [PATCH 047/343] Cleaning up circular dependency and working on legend This is a mixed and not very well-defined commit. Work includes work to clean up circular dependency in signaling structure; doesn't make sense for track to be passing messages between oncoprint and cell renderer. Instead a parent object now will trigger/listen on children objects, but never the other way around. E.g. oncoprint should trigger/listen on tracks Work on legend includes verifying that this system will work using the *globals* dev hack, and sussing out who exactly should own the legend and how it should work. Finally, the static rule legend object has been implemented. --- .../oncoprintjs/src/js/D3SVGCellRenderer.js | 204 +++++++++++++----- packages/oncoprintjs/src/js/events.js | 1 + packages/oncoprintjs/src/js/globals.js | 1 + packages/oncoprintjs/src/js/oncoprint.js | 41 +++- packages/oncoprintjs/src/js/track.js | 15 +- packages/oncoprintjs/src/js/utils.js | 18 ++ 6 files changed, 218 insertions(+), 62 deletions(-) create mode 100644 packages/oncoprintjs/src/js/globals.js diff --git a/packages/oncoprintjs/src/js/D3SVGCellRenderer.js b/packages/oncoprintjs/src/js/D3SVGCellRenderer.js index 23c1cb4da3e..d28ab2a086e 100644 --- a/packages/oncoprintjs/src/js/D3SVGCellRenderer.js +++ b/packages/oncoprintjs/src/js/D3SVGCellRenderer.js @@ -33,26 +33,136 @@ var $ = require('jquery'); var _ = require('underscore'); var events = require('./events'); var signals = require('./signals'); +var globals = require('./globals'); -function D3SVGLinearRangeRule(condition, d3_shape, attrs, attr_bounds) { +function D3SVGRule() { + var percentToPx = function(attr_val, attr_name, cell_width, cell_height) { + // convert a percentage to a local pixel coordinate + var width_like = ['width', 'x']; + var height_like = ['height', 'y']; + attr_val = parseFloat(attr_val)/100; + if (width_like.indexOf(attr_name) > -1) { + attr_val = attr_val*cell_width; + } else if (height_like.indexOf(attr_name) > -1) { + attr_val = attr_val*cell_height; + } + return attr_val+''; + }; + + this.apply = function(d3_g_selection, cell_width, cell_height) { + var shape = this.shape; + var elts = d3_g_selection.select(function() { + return this.appendChild(shape.node().cloneNode(true)); + }); + _.each(this.attrs, function(val, key) { + elts.attr(key, function(d,i) { + var curr_val = val; + if (typeof curr_val === 'function') { + curr_val = curr_val(d,i); + } + if (typeof curr_val === 'string' && curr_val.indexOf('%') > -1) { + curr_val = percentToPx(curr_val, key, cell_width, cell_height); + } + return curr_val; + }); + }); + } + this.filterData = function(d3_data) { + return d3_data.filter(this.condition || function(d) { return true; }); + }; + +} + +function D3SVGLinearColorRangeRule(condition, d3_shape, data_function, data_range, color_range, z_index, rule_id) { + var self = this; + self.rule_id = rule_id; + self.condition = condition; + self.shape = d3_shape; + var fill_function = (function(_data_range, _color_range) { + return function(d) { + var datum = data_function(d); + distance = (datum - _data_range[0]) / (_data_range[1] - _data_range[0]); + _color_range = [d3.rgb(_color_range[0]).toString(), d3.rgb(_color_range[1]).toString()]; + return utils.lin_interp(distance, _color_range[0], _color_range[1]); + } + })(data_range, color_range); + self.attrs = { + fill: fill_function + }; + self.z_index = z_index; + (function makeLegendElement(self) { + // TODO + self.legend_g = utils.makeD3SVGElement('g'); + self.legend_g.append('text').attr('font-size', '12px').text(data_range[0]); + /* + var gradient = self.legend_g.append('linearGradient') + .attr('y1', 0).attr('y2', 0) + .attr('x1', ) + */ + })(self); }; +D3SVGLinearColorRangeRule.prototype = new D3SVGRule(); +D3SVGLinearColorRangeRule.prototype.constructor=D3SVGLinearColorRangeRule; -function D3SVGStaticRule(condition, d3_shape) { +function D3SVGLinearRangeRule(condition, d3_shape, data_function, data_range, attr_range, z_index, rule_id) { + var self = this; + self.rule_id = rule_id; + self.condition = condition; + self.shape = d3_shape; + self.attrs = {}; + _.each(attr_range, function(range, attr) { + self.attrs[attr] = (function(_data_range, _range) { + return function(d) { + var datum = data_function(d); + distance = (datum - _data_range[0]) / (_data_range[1] - _data_range[0]); + return utils.lin_interp(distance, _range[0], _range[1]); + }; + })(data_range, range); + }); + self.z_index = z_index; + self.legend_g = utils.makeD3SVGElement('g'); }; +D3SVGLinearRangeRule.prototype = new D3SVGRule(); +D3SVGLinearRangeRule.prototype.constructor=D3SVGLinearRangeRule; + +function D3SVGStaticRule(condition, d3_shape, attrs, z_index, rule_id, legend_label) { + var self = this; + self.rule_id = rule_id; + self.condition = condition; + self.shape = d3_shape; + self.attrs = attrs; + self.z_index = z_index; + self.legend_label = legend_label; + + self.getLegendGroup = function(cell_width, cell_height) { + var group = utils.makeD3SVGElement('g'); + if (self.legend_label) { + var text = group.append('text').text(self.legend_label); + } + var g = group.append('g'); + self.apply(g, cell_width, cell_height); + return group; + }; +}; +D3SVGStaticRule.prototype = new D3SVGRule(); +D3SVGStaticRule.prototype.constructor = D3SVGStaticRule; function D3SVGRuleset(track_config) { var self = this; self.rule_map = {}; self.track_config = track_config; - self.addRule = function(condition, d3_shape, attrs, z_index) { + self.addStaticRule = function(condition, d3_shape, attrs, z_index, legend_label) { var rule_id = Object.keys(self.rule_map).length; + attrs = attrs || {}; if (z_index === undefined) { z_index = rule_id; } - self.rule_map[rule_id] = {condition: condition || function(d) { return true; }, shape: d3_shape, attrs: attrs, z_index: z_index, id:rule_id}; + self.rule_map[rule_id] = new D3SVGStaticRule(condition, d3_shape, attrs, z_index, rule_id, legend_label); + globals.rulesvgs = globals.rulesvgs || []; + globals.rulesvgs.push(self.rule_map[rule_id].getLegendGroup(10, 20)); return rule_id; }; @@ -62,45 +172,10 @@ function D3SVGRuleset(track_config) { self.getRule = function(rule_id) { return self.rule_map[rule_id]; - } - - var percentToPx = function(attr_val, attr_name) { - // convert a percentage to a local pixel coordinate - var width_like = ['width', 'x']; - var height_like = ['height', 'y']; - attr_val = parseFloat(attr_val)/100; - if (width_like.indexOf(attr_name) > -1) { - attr_val = attr_val*self.track_config.get('cell_width'); - } else if (height_like.indexOf(attr_name) > -1) { - attr_val = attr_val*self.track_config.get('cell_height'); - } - return attr_val+''; }; - var filterData = function(rule_params, d3_data) { - return d3_data.filter(rule_params.condition); - }; - - var applyRule = function(rule_params, d3_g_selection, d3_data) { - d3_g_selection = d3_g_selection.data( - filterData(rule_params, d3_data), - self.track_config.get('datum_id'), - ); - var elts = d3_g_selection.select(function() { - return this.appendChild(rule_params.shape.node().cloneNode(true)); - }); - _.each(rule_params.attrs, function(val, key) { - elts.attr(key, function(d,i) { - var curr_val = val; - if (typeof curr_val === 'function') { - curr_val = curr_val(d,i); - } - if (typeof curr_val === 'string' && curr_val.indexOf('%') > -1) { - curr_val = percentToPx(curr_val, key); - } - return curr_val; - }); - }); + var filterG = function(rule, d3_g_selection, d3_data) { + return d3_g_selection.data(rule.filterData(d3_data), self.track_config.get('datum_id')); }; var getOrderedRules = function() { @@ -114,7 +189,7 @@ function D3SVGRuleset(track_config) { self.apply = function(d3_g_selection, d3_data) { var rules = getOrderedRules(); _.each(rules, function(rule) { - applyRule(rule, d3_g_selection, d3_data, self.track_config.get('datum_id')); + rule.apply(filterG(rule, d3_g_selection, d3_data), self.track_config.get('cell_width'), self.track_config.get('cell_height')); }); }; @@ -125,9 +200,16 @@ function D3SVGRuleset(track_config) { }); }; - self.getLegendMap = function(only_active, d3_data) { - // returns map from rule_id to svg element + self.getLegendMap = function(d3_data, only_active) { + // returns map from rule_id to g element // if only_active is true, then only give back the rules that are used at least once + var legend_map = {}; + _.each(getOrderedRules(), function(rule) { + if (!only_active || filterData(rule, d3_data).length > 0) { + legend_map[rule.rule_id] = rule.legend_g; + } + }); + return legend_map; }; } @@ -151,19 +233,28 @@ function D3SVGCellRenderer(data, track_config) { self.addRule = function(params) { var rule_id = self.rule_set.addRule(params.condition, params.d3_shape, params.attrs, params.z_index); updateCells(); + $(self).trigger(events.UPDATE_RENDER_RULES); + return rule_id; + }; + + self.addStaticRule = function(params) { + var rule_id = self.rule_set.addStaticRule(params.condition, params.d3_shape, params.attrs, params.z_index, params.legend_label); + updateCells(); + $(self).trigger(events.UPDATE_RENDER_RULES); return rule_id; }; self.removeRule = function(rule_id) { self.rule_set.removeRule(rule_id); updateCells(); + $(self).trigger(events.UPDATE_RENDER_RULES); }; self.init = function(cell_area) { self.cell_area = cell_area; self.cell_area.selectAll('*').remove(); - self.svg = self.cell_area.append('svg') + self.svg = self.cell_area.append('svg'); updateCellArea(); self.g = self.svg.selectAll('g').data(self.data, self.track_config.get('datum_id')).enter().append('g').classed('cell', true); @@ -233,6 +324,24 @@ function D3SVGCellRenderer(data, track_config) { if (templName === 'categorical_color') { // params: - map from category to color // - data accessor + _.each(params.color, function(color, category) { + var rect = utils.makeD3SVGElement('rect'); + rect.attr('fill', color); + var condition = (function(cat) { + return function(d) { + return d.attr_val === cat; + }; + })(category); + self.addStaticRule({ condition: condition, + d3_shape: rect, + attrs: { + width: '100%', + height: '100%' + }, + legend_label: category + }); + }); + /* var rect = utils.makeD3SVGElement('rect'); var color = $.extend({}, params.color); var category = params.category; @@ -246,7 +355,7 @@ function D3SVGCellRenderer(data, track_config) { self.addRule({ d3_shape: rect, attrs: attrs, - }); + });*/ } else if (templName === 'continuous_color') { // params: - data accessor // - endpoints of the value range @@ -396,9 +505,6 @@ function D3SVGCellRenderer(data, track_config) { updateCells(); updateCellArea(); }); - $(self).on(signals.REQUEST_PRE_TRACK_PADDING, function(e, data) { - $(track).trigger(signals.REQUEST_PRE_TRACK_PADDING, data); - }); }; }; diff --git a/packages/oncoprintjs/src/js/events.js b/packages/oncoprintjs/src/js/events.js index 8fbb39e0e01..9dc920599be 100644 --- a/packages/oncoprintjs/src/js/events.js +++ b/packages/oncoprintjs/src/js/events.js @@ -41,4 +41,5 @@ module.exports = { ONCOPRINT_MOUSELEAVE: 'oncoprint_mouseleave.oncoprint', SET_PRE_TRACK_PADDING: 'set_pre_track_padding.oncoprint', TRACK_INIT: 'init.track.oncoprint', + UPDATE_RENDER_RULES: 'update_render_rules.cell_renderer=.oncoprint', }; diff --git a/packages/oncoprintjs/src/js/globals.js b/packages/oncoprintjs/src/js/globals.js new file mode 100644 index 00000000000..a0995453769 --- /dev/null +++ b/packages/oncoprintjs/src/js/globals.js @@ -0,0 +1 @@ +module.exports = {}; \ No newline at end of file diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index ab90c0c3594..ad50873f75c 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -35,6 +35,8 @@ var ReadOnlyObject = require('./ReadOnlyObject'); var Toolbar = require('./Toolbar') var events = require('./events'); var signals = require('./signals'); +var globals = require('./globals'); +var utils = require('./utils'); // TODO: use self everywhere @@ -65,19 +67,33 @@ function Oncoprint(container_selector_string, config) { } self.renderer.bindEvents(self); + var track_events = [events.TRACK_INIT, events.TRACK_FILTER_DATA, events.UPDATE_RENDER_RULES, + events.CELL_CLICK, events.CELL_MOUSEENTER, events.CELL_MOUSELEAVE, + signals.REQUEST_PRE_TRACK_PADDING]; + + + var triggerTracks = function(evt, data) { + _.each(self.tracks, function(track) { + $(track).trigger(evt, data); + }); + }; + self.setCellWidth = function(w) { self.config.cell_width = w; $(self).trigger(events.SET_CELL_WIDTH); + triggerTracks(events.SET_CELL_WIDTH); }; self.setCellPadding = function(p) { self.config.cell_padding = p; $(self).trigger(events.SET_CELL_PADDING); + triggerTracks(events.SET_CELL_PADDING); }; self.sortOnTrack = function(track_id, data_cmp) { self.config.id_order = self.tracks[track_id].getDatumIds(data_cmp); $(self).trigger(events.SORT, {id_order: self.config.id_order}); + triggerTracks(events.SORT, {id_order: self.config.id_order}); }; self.sortOnTracks = function(track_ids, data_cmps) { @@ -99,7 +115,11 @@ function Oncoprint(container_selector_string, config) { var track_id = track_id_counter; track_id_counter += 1; self.tracks[track_id] = new Track(data, config, new ReadOnlyObject(self.config)); - self.tracks[track_id].bindEvents(self); + _.each(track_events, function(evt) { + $(self.tracks[track_id]).on(evt, function(e,data) { + $(self).trigger(evt, data); + }); + }); self.track_order.push(track_id); // TODO: maybe this line shouldn't exist if we're not handling no data in oncoprint @@ -139,18 +159,26 @@ function OncoprintTableRenderer(container_selector_string) { self.$fixed_table; self.scrolling_table; self.$scrolling_table; + self.legend_table; + self.$legend_table; (function initTable(self) { self.container.selectAll('*').remove(); self.fixed_table = self.container.append('div').classed('fixed_oncoprint_table_container', true).append('table') self.$fixed_table = $(self.fixed_table.node()); - self.scrolling_table = self.container.append('div').classed('scrolling_oncoprint_table_container', true).append('table') + self.scrolling_table = self.container.append('div').classed('scrolling_oncoprint_table_container', true).append('table'); self.$scrolling_table = $(self.scrolling_table.node()); + self.legend_table = self.container.append('div').classed('legend_oncoprint_table_container', true).append('table'); + self.$legend_table = $(self.legend_table.node()); + self.legend_table.append('tr').append('svg').attr('width', 100).attr('height', 100); })(self); self.bindEvents = function(oncoprint) { $(oncoprint).on(events.ADD_TRACK, function(e, data) { - data.track.renderer.init(self.fixed_table.append('tr'), self.scrolling_table.append('tr')); + var new_fixed_table_row = self.fixed_table.append('tr'); + var new_scrolling_table_row = self.scrolling_table.append('tr'); + var new_legend_table_row = self.legend_table.append('tr'); + data.track.renderer.init(new_fixed_table_row, new_scrolling_table_row); }); $(oncoprint).on(events.MOVE_TRACK, function(e, data) { var track_name = data.track_name; @@ -168,6 +196,13 @@ function OncoprintTableRenderer(container_selector_string) { var track = data.track; track.renderer.$row.remove(); }); + var legend_x = 0; + $(oncoprint).on(events.UPDATE_RENDER_RULES, function(e, data) { + self.legend_table.select('svg').select(function() { + return this.appendChild(globals.rulesvgs[globals.rulesvgs.length-1].node().cloneNode(true)); + }).attr('transform', utils.translate(legend_x, 0)); + legend_x += 30; + }); self.$fixed_table.mouseenter(function() { $(oncoprint).trigger(events.ONCOPRINT_MOUSEENTER); }); diff --git a/packages/oncoprintjs/src/js/track.js b/packages/oncoprintjs/src/js/track.js index 0b474adcfec..34b6b74e7b8 100644 --- a/packages/oncoprintjs/src/js/track.js +++ b/packages/oncoprintjs/src/js/track.js @@ -35,6 +35,7 @@ var ReadOnlyObject = require('./ReadOnlyObject'); var utils = require('./utils'); var events = require('./events'); var signals = require('./signals'); +var globals = require('./globals'); var defaultTrackConfig = { label: 'Gene', @@ -65,20 +66,14 @@ function Track(data, config, oncoprint_config) { } self.renderer.bindEvents(self); - self.bindEvents = function(oncoprint) { - var pass_down_from_oncoprint = [events.SORT, events.SET_CELL_WIDTH, events.SET_CELL_PADDING, events.SET_PRE_TRACK_PADDING]; - _.each(pass_down_from_oncoprint, function(evt) { - $(oncoprint).on(evt, function(e, data) { - $(self).trigger(evt, data); - }) - }); - var pass_up_from_cell_renderer = [events.CELL_CLICK, events.CELL_MOUSEENTER, events.CELL_MOUSELEAVE, signals.REQUEST_PRE_TRACK_PADDING]; + (function bindEvents() { + var pass_up_from_cell_renderer = [events.UPDATE_RENDER_RULES, events.CELL_CLICK, events.CELL_MOUSEENTER, events.CELL_MOUSELEAVE, signals.REQUEST_PRE_TRACK_PADDING]; _.each(pass_up_from_cell_renderer, function(evt) { $(cell_renderer).on(evt, function(e, data) { - $(oncoprint).trigger(evt, $.extend({}, data, {track: self})); + $(self).trigger(evt, $.extend({}, data, {track: self})); }) }); - }; + })(); self.getLabel = function() { // TODO: label decorations diff --git a/packages/oncoprintjs/src/js/utils.js b/packages/oncoprintjs/src/js/utils.js index 3e55c834ccd..12e59f86ca3 100644 --- a/packages/oncoprintjs/src/js/utils.js +++ b/packages/oncoprintjs/src/js/utils.js @@ -78,6 +78,24 @@ exports.stableSort = function(arr, cmp) { return _.map(zipped, function(x) { return x[0];}); }; +exports.lin_interp = function(t, a, b) { + if (a[0] === '#') { + var r = [parseInt(a.substring(1,3), 16), parseInt(b.substring(1,3), 16)]; + var g = [parseInt(a.substring(3,5), 16), parseInt(b.substring(3,5), 16)]; + var b = [parseInt(a.substring(5,7), 16), parseInt(b.substring(5,7), 16)]; + var R = r[0]*(1-t) + r[1]*t; + var G = g[0]*(1-t) + g[1]*t; + var B = b[0]*(1-t) + b[1]*t; + return '#' + R.toString(16) + G.toString(16) + B.toString(16); + } else if (isNaN(a) && a.indexOf('%') > -1) { + var A = parseFloat(a, 10); + var B = parseFloat(b, 10); + return (A*(1-t) + B*t)+'%'; + } else { + return a*(1-t) + b*t; + } +}; + exports.translate = function(x,y) { return "translate(" + x + "," + y + ")"; }; From 53c6ff1735fd8bd6d0dea6798bc173708e42a9c2 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Tue, 9 Jun 2015 16:57:34 -0400 Subject: [PATCH 048/343] static rule legend object further refined, utils added to deal with svgs --- .../oncoprintjs/src/js/D3SVGCellRenderer.js | 38 +++++++++++++++---- packages/oncoprintjs/src/js/oncoprint.js | 9 ++--- packages/oncoprintjs/src/js/utils.js | 25 ++++++++++++ 3 files changed, 59 insertions(+), 13 deletions(-) diff --git a/packages/oncoprintjs/src/js/D3SVGCellRenderer.js b/packages/oncoprintjs/src/js/D3SVGCellRenderer.js index d28ab2a086e..08c2b1f17c0 100644 --- a/packages/oncoprintjs/src/js/D3SVGCellRenderer.js +++ b/packages/oncoprintjs/src/js/D3SVGCellRenderer.js @@ -40,7 +40,7 @@ function D3SVGRule() { // convert a percentage to a local pixel coordinate var width_like = ['width', 'x']; var height_like = ['height', 'y']; - attr_val = parseFloat(attr_val)/100; + attr_val = parseFloat(attr_val, 10)/100; if (width_like.indexOf(attr_name) > -1) { attr_val = attr_val*cell_width; } else if (height_like.indexOf(attr_name) > -1) { @@ -51,9 +51,7 @@ function D3SVGRule() { this.apply = function(d3_g_selection, cell_width, cell_height) { var shape = this.shape; - var elts = d3_g_selection.select(function() { - return this.appendChild(shape.node().cloneNode(true)); - }); + var elts = utils.appendD3SVGElement(shape, d3_g_selection); _.each(this.attrs, function(val, key) { elts.attr(key, function(d,i) { var curr_val = val; @@ -105,16 +103,18 @@ function D3SVGLinearColorRangeRule(condition, d3_shape, data_function, data_rang D3SVGLinearColorRangeRule.prototype = new D3SVGRule(); D3SVGLinearColorRangeRule.prototype.constructor=D3SVGLinearColorRangeRule; -function D3SVGLinearRangeRule(condition, d3_shape, data_function, data_range, attr_range, z_index, rule_id) { +function D3SVGLinearRangeRule(condition, d3_shape, data_key, data_range, attr_range, z_index, rule_id, legend_label) { var self = this; self.rule_id = rule_id; self.condition = condition; self.shape = d3_shape; self.attrs = {}; + self.legend_label = legend_label; + _.each(attr_range, function(range, attr) { self.attrs[attr] = (function(_data_range, _range) { return function(d) { - var datum = data_function(d); + var datum = parseFloat(d[data_key], 10); distance = (datum - _data_range[0]) / (_data_range[1] - _data_range[0]); return utils.lin_interp(distance, _range[0], _range[1]); }; @@ -122,7 +122,28 @@ function D3SVGLinearRangeRule(condition, d3_shape, data_function, data_range, at }); self.z_index = z_index; - self.legend_g = utils.makeD3SVGElement('g'); + self.getLegendGroup = function(cell_width, cell_height) { + /*var ret = utils.makeD3SVGElement('g'); + var lower_group = ret.append('g'); + var upper_group = ret.append('g'); + + var lower_cell = (function(_data_range) { + return lower_group.append('g').data({data_key: _data_range[0]}); + })(data_range); + var upper_cell = (function(_data_range) { + return upper_group.append('g').data({data_key: _data_range[1]}); + })(data_range); + + self.apply(lower_cell, cell_width, cell_height); + self.apply(upper_cell, cell_width, cell_height); + + lower_group.append('text').text() + utils.appendD3SVGElement(lower_group); + utils.appendD3SVGElement(upper_group); + + return ret; */ + }; + }; D3SVGLinearRangeRule.prototype = new D3SVGRule(); D3SVGLinearRangeRule.prototype.constructor=D3SVGLinearRangeRule; @@ -139,7 +160,8 @@ function D3SVGStaticRule(condition, d3_shape, attrs, z_index, rule_id, legend_la self.getLegendGroup = function(cell_width, cell_height) { var group = utils.makeD3SVGElement('g'); if (self.legend_label) { - var text = group.append('text').text(self.legend_label); + group.append('text').text(self.legend_label) + .attr('alignment-baseline', 'hanging'); } var g = group.append('g'); self.apply(g, cell_width, cell_height); diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index ad50873f75c..e16932589d6 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -170,7 +170,7 @@ function OncoprintTableRenderer(container_selector_string) { self.$scrolling_table = $(self.scrolling_table.node()); self.legend_table = self.container.append('div').classed('legend_oncoprint_table_container', true).append('table'); self.$legend_table = $(self.legend_table.node()); - self.legend_table.append('tr').append('svg').attr('width', 100).attr('height', 100); + self.legend_table.append('tr').append('svg').attr('width', 600).attr('height', 100); })(self); self.bindEvents = function(oncoprint) { @@ -198,10 +198,9 @@ function OncoprintTableRenderer(container_selector_string) { }); var legend_x = 0; $(oncoprint).on(events.UPDATE_RENDER_RULES, function(e, data) { - self.legend_table.select('svg').select(function() { - return this.appendChild(globals.rulesvgs[globals.rulesvgs.length-1].node().cloneNode(true)); - }).attr('transform', utils.translate(legend_x, 0)); - legend_x += 30; + var group = utils.appendD3SVGElement(globals.rulesvgs[globals.rulesvgs.length-1], self.legend_table.select('svg')); + utils.spaceSVGElementsHorizontally(group, 10); + utils.spaceSVGElementsHorizontally(self.legend_table.select('svg'), 20); }); self.$fixed_table.mouseenter(function() { $(oncoprint).trigger(events.ONCOPRINT_MOUSEENTER); diff --git a/packages/oncoprintjs/src/js/utils.js b/packages/oncoprintjs/src/js/utils.js index 12e59f86ca3..d7316d9b868 100644 --- a/packages/oncoprintjs/src/js/utils.js +++ b/packages/oncoprintjs/src/js/utils.js @@ -42,6 +42,31 @@ exports.makeD3SVGElement = function(tag) { return d3.select(document.createElementNS('http://www.w3.org/2000/svg', tag)); }; +exports.appendD3SVGElement = function(elt, target) { + return target.select(function() { + return this.appendChild(elt.node().cloneNode(true)); + }); +}; + +exports.spaceSVGElementsHorizontally = function(group, padding) { + var x = 0; + var elts = exports.d3SelectChildren(group, '*').each(function() { + var transform = d3.select(this).attr('transform'); + var y = transform && transform.indexOf("translate") > -1 && parseFloat(transform.split(",")[1], 10); + y = y || 0; + d3.select(this).attr('transform', exports.translate(x, y)); + x += this.getBBox().width; + x += padding; + }); + return group; +}; + +exports.d3SelectChildren = function(parent, selector) { + return parent.selectAll(selector).filter(function() { + return this.parentNode === parent.node(); + }); +}; + exports.warn = function(str, context) { console.log("Oncoprint error in "+context+": "+str); }; From 4f0dd0b3db5ec5c9378b54a2b8c6ef2b6be4cf6a Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Tue, 9 Jun 2015 19:46:26 -0400 Subject: [PATCH 049/343] Linear color gradient rule, and demo of changing color scheme --- .../oncoprintjs/src/js/D3SVGCellRenderer.js | 182 +- packages/oncoprintjs/src/js/utils.js | 13 +- .../test/data/gbm/mutations-gbm.json | 2468 +++++++++++++++++ packages/oncoprintjs/test/index.html | 1 + packages/oncoprintjs/test/js/test_page.js | 25 + 5 files changed, 2610 insertions(+), 79 deletions(-) create mode 100644 packages/oncoprintjs/test/data/gbm/mutations-gbm.json diff --git a/packages/oncoprintjs/src/js/D3SVGCellRenderer.js b/packages/oncoprintjs/src/js/D3SVGCellRenderer.js index 08c2b1f17c0..bbddd0a0ff3 100644 --- a/packages/oncoprintjs/src/js/D3SVGCellRenderer.js +++ b/packages/oncoprintjs/src/js/D3SVGCellRenderer.js @@ -52,7 +52,10 @@ function D3SVGRule() { this.apply = function(d3_g_selection, cell_width, cell_height) { var shape = this.shape; var elts = utils.appendD3SVGElement(shape, d3_g_selection); - _.each(this.attrs, function(val, key) { + var attrs = this.attrs || {}; + attrs.width = attrs.width || '100%'; + attrs.height = attrs.height || '100%'; + _.each(attrs, function(val, key) { elts.attr(key, function(d,i) { var curr_val = val; if (typeof curr_val === 'function') { @@ -71,14 +74,15 @@ function D3SVGRule() { } -function D3SVGLinearColorRangeRule(condition, d3_shape, data_function, data_range, color_range, z_index, rule_id) { +function D3SVGLinearGradientRule(condition, d3_shape, data_key, data_range, color_range, z_index, rule_id) { var self = this; self.rule_id = rule_id; self.condition = condition; self.shape = d3_shape; + var fill_function = (function(_data_range, _color_range) { return function(d) { - var datum = data_function(d); + var datum = d[data_key]; distance = (datum - _data_range[0]) / (_data_range[1] - _data_range[0]); _color_range = [d3.rgb(_color_range[0]).toString(), d3.rgb(_color_range[1]).toString()]; return utils.lin_interp(distance, _color_range[0], _color_range[1]); @@ -89,64 +93,38 @@ function D3SVGLinearColorRangeRule(condition, d3_shape, data_function, data_rang }; self.z_index = z_index; - (function makeLegendElement(self) { - // TODO - self.legend_g = utils.makeD3SVGElement('g'); - self.legend_g.append('text').attr('font-size', '12px').text(data_range[0]); - /* - var gradient = self.legend_g.append('linearGradient') - .attr('y1', 0).attr('y2', 0) - .attr('x1', ) - */ - })(self); + self.getLegendGroup = (function(_data_range, _color_range) { + return function() { + var group = utils.makeD3SVGElement('g'); + var gradient_id = 'gradient'+self.rule_id; + + var gradient = group.append('svg:defs').append('svg:linearGradient') + .attr('id', gradient_id) + .attr('x1', '0%').attr('y1', '0%') + .attr('x2', '100%').attr('y2', '0%') + .attr('spreadMethod', 'pad'); + gradient.append('svg:stop') + .attr('offset', '0%') + .attr('stop-color', _color_range[0]) + .attr('stop-opacity', 1); + gradient.append('svg:stop') + .attr('offset', '100%') + .attr('stop-color', _color_range[1]) + .attr('stop-opacity', 1); + + group.append('text').text(_data_range[0]).attr('alignment-baseline', 'hanging'); + group.append('rect') + .attr('width', '100px').attr('height', '20px') + .style('fill', 'url(#'+gradient_id+')'); + group.append('text').text(_data_range[1]).attr('alignment-baseline', 'hanging'); + + return group; + }; + })(data_range, color_range); }; -D3SVGLinearColorRangeRule.prototype = new D3SVGRule(); -D3SVGLinearColorRangeRule.prototype.constructor=D3SVGLinearColorRangeRule; - -function D3SVGLinearRangeRule(condition, d3_shape, data_key, data_range, attr_range, z_index, rule_id, legend_label) { - var self = this; - self.rule_id = rule_id; - self.condition = condition; - self.shape = d3_shape; - self.attrs = {}; - self.legend_label = legend_label; - - _.each(attr_range, function(range, attr) { - self.attrs[attr] = (function(_data_range, _range) { - return function(d) { - var datum = parseFloat(d[data_key], 10); - distance = (datum - _data_range[0]) / (_data_range[1] - _data_range[0]); - return utils.lin_interp(distance, _range[0], _range[1]); - }; - })(data_range, range); - }); - self.z_index = z_index; +D3SVGLinearGradientRule.prototype = new D3SVGRule(); +D3SVGLinearGradientRule.prototype.constructor=D3SVGLinearGradientRule; - self.getLegendGroup = function(cell_width, cell_height) { - /*var ret = utils.makeD3SVGElement('g'); - var lower_group = ret.append('g'); - var upper_group = ret.append('g'); - - var lower_cell = (function(_data_range) { - return lower_group.append('g').data({data_key: _data_range[0]}); - })(data_range); - var upper_cell = (function(_data_range) { - return upper_group.append('g').data({data_key: _data_range[1]}); - })(data_range); - - self.apply(lower_cell, cell_width, cell_height); - self.apply(upper_cell, cell_width, cell_height); - - lower_group.append('text').text() - utils.appendD3SVGElement(lower_group); - utils.appendD3SVGElement(upper_group); - - return ret; */ - }; - -}; -D3SVGLinearRangeRule.prototype = new D3SVGRule(); -D3SVGLinearRangeRule.prototype.constructor=D3SVGLinearRangeRule; function D3SVGStaticRule(condition, d3_shape, attrs, z_index, rule_id, legend_label) { var self = this; @@ -188,6 +166,17 @@ function D3SVGRuleset(track_config) { return rule_id; }; + self.addLinearGradientRule = function(condition, d3_shape, data_key, data_range, color_range, z_index) { + var rule_id = Object.keys(self.rule_map).length; + if (z_index === undefined) { + z_index = rule_id; + } + self.rule_map[rule_id] = new D3SVGLinearGradientRule(condition, d3_shape, data_key, data_range, color_range, z_index, rule_id); + globals.rulesvgs = globals.rulesvgs || []; + globals.rulesvgs.push(self.rule_map[rule_id].getLegendGroup(10, 20)); + return rule_id; + }; + self.removeRule = function(rule_id) { delete self.rule_map[rule_id]; }; @@ -266,6 +255,13 @@ function D3SVGCellRenderer(data, track_config) { return rule_id; }; + self.addLinearGradientRule = function(params) { + var rule_id = self.rule_set.addLinearGradientRule(params.condition, params.d3_shape, params.data_key, params.data_range, params.color_range, params.z_index); + updateCells(); + $(self).trigger(events.UPDATE_RENDER_RULES); + return rule_id; + }; + self.removeRule = function(rule_id) { self.rule_set.removeRule(rule_id); updateCells(); @@ -363,26 +359,17 @@ function D3SVGCellRenderer(data, track_config) { legend_label: category }); }); - /* - var rect = utils.makeD3SVGElement('rect'); - var color = $.extend({}, params.color); - var category = params.category; - var attrs = { - width: '100%', - height: '100%', - fill: function(d) { - return color[category(d)]; - } - }; - self.addRule({ - d3_shape: rect, - attrs: attrs, - });*/ } else if (templName === 'continuous_color') { // params: - data accessor // - endpoints of the value range // - endpoints of the gradient (in same order) - + var rect = utils.makeD3SVGElement('rect'); + self.addLinearGradientRule({ + d3_shape: rect, + data_key: params.data_key, + data_range: params.data_range, + color_range: params.color_range + }); } else if (templName === 'heat_map') { // params: - data accessor // - endpoints of the value range @@ -531,3 +518,48 @@ function D3SVGCellRenderer(data, track_config) { }; module.exports = D3SVGCellRenderer; + +/* TODO!! +function D3SVGLinearRangeRule(condition, d3_shape, data_key, data_range, attr_range, z_index, rule_id, legend_label) { + var self = this; + self.rule_id = rule_id; + self.condition = condition; + self.shape = d3_shape; + self.attrs = {}; + self.legend_label = legend_label; + + _.each(attr_range, function(range, attr) { + self.attrs[attr] = (function(_data_range, _range) { + return function(d) { + var datum = parseFloat(d[data_key], 10); + distance = (datum - _data_range[0]) / (_data_range[1] - _data_range[0]); + return utils.lin_interp(distance, _range[0], _range[1]); + }; + })(data_range, range); + }); + self.z_index = z_index; + + self.getLegendGroup = function(cell_width, cell_height) { + var ret = utils.makeD3SVGElement('g'); + var lower_group = ret.append('g'); + var upper_group = ret.append('g'); + + var lower_cell = (function(_data_range) { + return lower_group.append('g').data({data_key: _data_range[0]}); + })(data_range); + var upper_cell = (function(_data_range) { + return upper_group.append('g').data({data_key: _data_range[1]}); + })(data_range); + + self.apply(lower_cell, cell_width, cell_height); + self.apply(upper_cell, cell_width, cell_height); + + lower_group.append('text').text() + utils.appendD3SVGElement(lower_group); + utils.appendD3SVGElement(upper_group); + + return ret; + }; +}; +D3SVGLinearRangeRule.prototype = new D3SVGRule(); +D3SVGLinearRangeRule.prototype.constructor=D3SVGLinearRangeRule;*/ diff --git a/packages/oncoprintjs/src/js/utils.js b/packages/oncoprintjs/src/js/utils.js index d7316d9b868..0f894bc75b3 100644 --- a/packages/oncoprintjs/src/js/utils.js +++ b/packages/oncoprintjs/src/js/utils.js @@ -108,10 +108,15 @@ exports.lin_interp = function(t, a, b) { var r = [parseInt(a.substring(1,3), 16), parseInt(b.substring(1,3), 16)]; var g = [parseInt(a.substring(3,5), 16), parseInt(b.substring(3,5), 16)]; var b = [parseInt(a.substring(5,7), 16), parseInt(b.substring(5,7), 16)]; - var R = r[0]*(1-t) + r[1]*t; - var G = g[0]*(1-t) + g[1]*t; - var B = b[0]*(1-t) + b[1]*t; - return '#' + R.toString(16) + G.toString(16) + B.toString(16); + var R = Math.round(r[0]*(1-t) + r[1]*t).toString(16); + var G = Math.round(g[0]*(1-t) + g[1]*t).toString(16); + var B = Math.round(b[0]*(1-t) + b[1]*t).toString(16); + + R = R.length < 2 ? '0'+R : R; + G = G.length < 2 ? '0'+G : G; + B = B.length < 2 ? '0'+B : B; + + return '#' + R + G + B; } else if (isNaN(a) && a.indexOf('%') > -1) { var A = parseFloat(a, 10); var B = parseFloat(b, 10); diff --git a/packages/oncoprintjs/test/data/gbm/mutations-gbm.json b/packages/oncoprintjs/test/data/gbm/mutations-gbm.json new file mode 100644 index 00000000000..b3b495f7abf --- /dev/null +++ b/packages/oncoprintjs/test/data/gbm/mutations-gbm.json @@ -0,0 +1,2468 @@ +{ + "attributes" : [ + { + "datatype" : "STRING", + "attr_id" : "GENDER", + "display_name" : "Person Gender", + "description" : "Patient gender." + } + ], + "data" : [ + { + "sample" : "TCGA-02-0001-01", + "attr_id" : "GENDER", + "attr_val" : "21.000000" + }, + { + "attr_val" : "37.000000", + "attr_id" : "GENDER", + "sample" : "TCGA-02-0003-01" + }, + { + "sample" : "TCGA-02-0006-01", + "attr_id" : "GENDER", + "attr_val" : "1.000000" + }, + { + "attr_id" : "GENDER", + "sample" : "TCGA-02-0007-01", + "attr_val" : "17.000000" + }, + { + "sample" : "TCGA-02-0009-01", + "attr_id" : "GENDER", + "attr_val" : "3.000000" + }, + { + "attr_val" : "24.000000", + "sample" : "TCGA-02-0010-01", + "attr_id" : "GENDER" + }, + { + "attr_id" : "GENDER", + "sample" : "TCGA-02-0011-01", + "attr_val" : "28.000000" + }, + { + "attr_id" : "GENDER", + "sample" : "TCGA-02-0014-01", + "attr_val" : "82.000000" + }, + { + "attr_val" : "18.000000", + "attr_id" : "GENDER", + "sample" : "TCGA-02-0021-01" + }, + { + "sample" : "TCGA-02-0024-01", + "attr_id" : "GENDER", + "attr_val" : "90.000000" + }, + { + "attr_val" : "2.000000", + "sample" : "TCGA-02-0027-01", + "attr_id" : "GENDER" + }, + { + "sample" : "TCGA-02-0028-01", + "attr_id" : "GENDER", + "attr_val" : "25.000000" + }, + { + "attr_id" : "GENDER", + "sample" : "TCGA-02-0033-01", + "attr_val" : "64.000000" + }, + { + "sample" : "TCGA-02-0034-01", + "attr_id" : "GENDER", + "attr_val" : "17.000000" + }, + { + "attr_val" : "7.000000", + "attr_id" : "GENDER", + "sample" : "TCGA-02-0037-01" + }, + { + "attr_val" : "22.000000", + "sample" : "TCGA-02-0038-01", + "attr_id" : "GENDER" + }, + { + "attr_val" : "29.000000", + "sample" : "TCGA-02-0043-01", + "attr_id" : "GENDER" + }, + { + "attr_val" : "78.000000", + "attr_id" : "GENDER", + "sample" : "TCGA-02-0046-01" + }, + { + "attr_val" : "45.000000", + "sample" : "TCGA-02-0047-01", + "attr_id" : "GENDER" + }, + { + "attr_val" : "70.000000", + "sample" : "TCGA-02-0052-01", + "attr_id" : "GENDER" + }, + { + "attr_val" : "11.000000", + "attr_id" : "GENDER", + "sample" : "TCGA-02-0054-01" + }, + { + "attr_val" : "25.000000", + "sample" : "TCGA-02-0055-01", + "attr_id" : "GENDER" + }, + { + "attr_val" : "0.000000", + "sample" : "TCGA-02-0057-01", + "attr_id" : "GENDER" + }, + { + "attr_val" : "22.000000", + "sample" : "TCGA-02-0058-01", + "attr_id" : "GENDER" + }, + { + "sample" : "TCGA-02-0060-01", + "attr_id" : "GENDER", + "attr_val" : "5.000000" + }, + { + "attr_val" : "71.000000", + "attr_id" : "GENDER", + "sample" : "TCGA-02-0064-01" + }, + { + "attr_id" : "GENDER", + "sample" : "TCGA-02-0069-01", + "attr_val" : "12.000000" + }, + { + "sample" : "TCGA-02-0071-01", + "attr_id" : "GENDER", + "attr_val" : "51.000000" + }, + { + "attr_val" : "20.000000", + "attr_id" : "GENDER", + "sample" : "TCGA-02-0074-01" + }, + { + "sample" : "TCGA-02-0075-01", + "attr_id" : "GENDER", + "attr_val" : "13.000000" + }, + { + "sample" : "TCGA-02-0080-01", + "attr_id" : "GENDER", + "attr_val" : "75.000000" + }, + { + "attr_id" : "GENDER", + "sample" : "TCGA-02-0083-01", + "attr_val" : "26.000000" + }, + { + "attr_val" : "21.000000", + "sample" : "TCGA-02-0085-01", + "attr_id" : "GENDER" + }, + { + "attr_id" : "GENDER", + "sample" : "TCGA-02-0086-01", + "attr_val" : "12.000000" + }, + { + "sample" : "TCGA-02-0089-01", + "attr_id" : "GENDER", + "attr_val" : "94.000000" + }, + { + "attr_val" : "35.000000", + "attr_id" : "GENDER", + "sample" : "TCGA-02-0099-01" + }, + { + "sample" : "TCGA-02-0102-01", + "attr_id" : "GENDER", + "attr_val" : "48.000000" + }, + { + "attr_id" : "GENDER", + "sample" : "TCGA-02-0107-01", + "attr_val" : "35.000000" + }, + { + "attr_val" : "24.000000", + "sample" : "TCGA-02-0113-01", + "attr_id" : "GENDER" + }, + { + "attr_id" : "GENDER", + "sample" : "TCGA-02-0114-01", + "attr_val" : "10.000000" + }, + { + "attr_val" : "59.000000", + "attr_id" : "GENDER", + "sample" : "TCGA-02-0115-01" + }, + { + "attr_val" : "80.000000", + "attr_id" : "GENDER", + "sample" : "TCGA-02-0116-01" + }, + { + "attr_val" : "28.000000", + "sample" : "TCGA-06-0122-01", + "attr_id" : "GENDER" + }, + { + "attr_val" : "17.000000", + "attr_id" : "GENDER", + "sample" : "TCGA-06-0124-01" + }, + { + "attr_id" : "GENDER", + "sample" : "TCGA-06-0125-01", + "attr_val" : "29.000000" + }, + { + "sample" : "TCGA-06-0126-01", + "attr_id" : "GENDER", + "attr_val" : "15.000000" + }, + { + "attr_id" : "GENDER", + "sample" : "TCGA-06-0128-01", + "attr_val" : "20.000000" + }, + { + "sample" : "TCGA-06-0129-01", + "attr_id" : "GENDER", + "attr_val" : "87.000000" + }, + { + "attr_val" : "55.000000", + "attr_id" : "GENDER", + "sample" : "TCGA-06-0130-01" + }, + { + "attr_id" : "GENDER", + "sample" : "TCGA-06-0132-01", + "attr_val" : "26.000000" + }, + { + "attr_val" : "48.000000", + "sample" : "TCGA-06-0133-01", + "attr_id" : "GENDER" + }, + { + "attr_id" : "GENDER", + "sample" : "TCGA-06-0137-01", + "attr_val" : "16.000000" + }, + { + "attr_val" : "67.000000", + "sample" : "TCGA-06-0138-01", + "attr_id" : "GENDER" + }, + { + "attr_val" : "83.000000", + "sample" : "TCGA-06-0139-01", + "attr_id" : "GENDER" + }, + { + "attr_id" : "GENDER", + "sample" : "TCGA-06-0141-01", + "attr_val" : "13.000000" + }, + { + "attr_id" : "GENDER", + "sample" : "TCGA-06-0143-01", + "attr_val" : "33.000000" + }, + { + "sample" : "TCGA-06-0145-01", + "attr_id" : "GENDER", + "attr_val" : "4.000000" + }, + { + "attr_id" : "GENDER", + "sample" : "TCGA-06-0147-01", + "attr_val" : "14.000000" + }, + { + "attr_val" : "85.000000", + "attr_id" : "GENDER", + "sample" : "TCGA-06-0148-01" + }, + { + "sample" : "TCGA-06-0154-01", + "attr_id" : "GENDER", + "attr_val" : "90.000000" + }, + { + "attr_id" : "GENDER", + "sample" : "TCGA-06-0156-01", + "attr_val" : "7.000000" + }, + { + "attr_val" : "19.000000", + "attr_id" : "GENDER", + "sample" : "TCGA-06-0157-01" + }, + { + "attr_id" : "GENDER", + "sample" : "TCGA-06-0158-01", + "attr_val" : "39.000000" + }, + { + "attr_id" : "GENDER", + "sample" : "TCGA-06-0166-01", + "attr_val" : "75.000000" + }, + { + "attr_val" : "2.000000", + "attr_id" : "GENDER", + "sample" : "TCGA-06-0168-01" + }, + { + "sample" : "TCGA-06-0169-01", + "attr_id" : "GENDER", + "attr_val" : "53.000000" + }, + { + "attr_val" : "99.000000", + "attr_id" : "GENDER", + "sample" : "TCGA-06-0171-01" + }, + { + "attr_id" : "GENDER", + "sample" : "TCGA-06-0173-01", + "attr_val" : "28.000000" + }, + { + "attr_val" : "46.000000", + "attr_id" : "GENDER", + "sample" : "TCGA-06-0174-01" + }, + { + "attr_id" : "GENDER", + "sample" : "TCGA-06-0176-01", + "attr_val" : "80.000000" + }, + { + "attr_val" : "33.000000", + "attr_id" : "GENDER", + "sample" : "TCGA-06-0178-01" + }, + { + "attr_id" : "GENDER", + "sample" : "TCGA-06-0184-01", + "attr_val" : "77.000000" + }, + { + "attr_val" : "71.000000", + "sample" : "TCGA-06-0185-01", + "attr_id" : "GENDER" + }, + { + "attr_val" : "43.000000", + "attr_id" : "GENDER", + "sample" : "TCGA-06-0187-01" + }, + { + "attr_id" : "GENDER", + "sample" : "TCGA-06-0188-01", + "attr_val" : "44.000000" + }, + { + "attr_id" : "GENDER", + "sample" : "TCGA-06-0189-01", + "attr_val" : "66.000000" + }, + { + "sample" : "TCGA-06-0190-01", + "attr_id" : "GENDER", + "attr_val" : "16.000000" + }, + { + "attr_val" : "63.000000", + "sample" : "TCGA-06-0195-01", + "attr_id" : "GENDER" + }, + { + "attr_val" : "6.000000", + "attr_id" : "GENDER", + "sample" : "TCGA-06-0197-01" + }, + { + "sample" : "TCGA-06-0201-01", + "attr_id" : "GENDER", + "attr_val" : "12.000000" + }, + { + "sample" : "TCGA-06-0206-01", + "attr_id" : "GENDER", + "attr_val" : "92.000000" + }, + { + "attr_id" : "GENDER", + "sample" : "TCGA-06-0208-01", + "attr_val" : "10.000000" + }, + { + "sample" : "TCGA-06-0209-01", + "attr_id" : "GENDER", + "attr_val" : "43.000000" + }, + { + "attr_val" : "17.000000", + "attr_id" : "GENDER", + "sample" : "TCGA-06-0210-01" + }, + { + "attr_val" : "77.000000", + "sample" : "TCGA-06-0211-01", + "attr_id" : "GENDER" + }, + { + "attr_id" : "GENDER", + "sample" : "TCGA-06-0213-01", + "attr_val" : "5.000000" + }, + { + "attr_val" : "96.000000", + "sample" : "TCGA-06-0214-01", + "attr_id" : "GENDER" + }, + { + "attr_val" : "59.000000", + "sample" : "TCGA-06-0219-01", + "attr_id" : "GENDER" + }, + { + "attr_id" : "GENDER", + "sample" : "TCGA-06-0221-01", + "attr_val" : "61.000000" + }, + { + "attr_val" : "9.000000", + "attr_id" : "GENDER", + "sample" : "TCGA-06-0237-01" + }, + { + "sample" : "TCGA-06-0241-01", + "attr_id" : "GENDER", + "attr_val" : "9.000000" + }, + { + "attr_id":"GENDER", + "attr_val":"44.000000", + "sample":"0.770262901898" +}, +{ + "attr_id":"GENDER", + "attr_val":"85.000000", + "sample":"0.520124406767" +}, +{ + "attr_id":"GENDER", + "attr_val":"24.000000", + "sample":"0.651899674314" +}, +{ + "attr_id":"GENDER", + "attr_val":"8.000000", + "sample":"0.0638099630054" +}, +{ + "attr_id":"GENDER", + "attr_val":"21.000000", + "sample":"0.600571715831" +}, +{ + "attr_id":"GENDER", + "attr_val":"37.000000", + "sample":"0.143096970318" +}, +{ + "attr_id":"GENDER", + "attr_val":"9.000000", + "sample":"0.88410886898" +}, +{ + "attr_id":"GENDER", + "attr_val":"3.000000", + "sample":"0.942656629217" +}, +{ + "attr_id":"GENDER", + "attr_val":"14.000000", + "sample":"0.340373735274" +}, +{ + "attr_id":"GENDER", + "attr_val":"2.000000", + "sample":"0.512874527707" +}, +{ + "attr_id":"GENDER", + "attr_val":"2.000000", + "sample":"0.150155404028" +}, +{ + "attr_id":"GENDER", + "attr_val":"73.000000", + "sample":"0.0969411173853" +}, +{ + "attr_id":"GENDER", + "attr_val":"17.000000", + "sample":"0.517885403774" +}, +{ + "attr_id":"GENDER", + "attr_val":"73.000000", + "sample":"0.274946296721" +}, +{ + "attr_id":"GENDER", + "attr_val":"73.000000", + "sample":"0.210944670591" +}, +{ + "attr_id":"GENDER", + "attr_val":"8.000000", + "sample":"0.619680593683" +}, +{ + "attr_id":"GENDER", + "attr_val":"8.000000", + "sample":"0.226024081287" +}, +{ + "attr_id":"GENDER", + "attr_val":"22.000000", + "sample":"0.380274494243" +}, +{ + "attr_id":"GENDER", + "attr_val":"21.000000", + "sample":"0.684947760302" +}, +{ + "attr_id":"GENDER", + "attr_val":"9.000000", + "sample":"0.902547238037" +}, +{ + "attr_id":"GENDER", + "attr_val":"34.000000", + "sample":"0.702836956669" +}, +{ + "attr_id":"GENDER", + "attr_val":"93.000000", + "sample":"0.388446604958" +}, +{ + "attr_id":"GENDER", + "attr_val":"25.000000", + "sample":"0.163264888342" +}, +{ + "attr_id":"GENDER", + "attr_val":"25.000000", + "sample":"0.0908601090172" +}, +{ + "attr_id":"GENDER", + "attr_val":"14.000000", + "sample":"0.848728644147" +}, +{ + "attr_id":"GENDER", + "attr_val":"15.000000", + "sample":"0.843692707139" +}, +{ + "attr_id":"GENDER", + "attr_val":"57.000000", + "sample":"0.688399406352" +}, +{ + "attr_id":"GENDER", + "attr_val":"34.000000", + "sample":"0.343018695175" +}, +{ + "attr_id":"GENDER", + "attr_val":"49.000000", + "sample":"0.328718210652" +}, +{ + "attr_id":"GENDER", + "attr_val":"66.000000", + "sample":"0.740452923257" +}, +{ + "attr_id":"GENDER", + "attr_val":"66.000000", + "sample":"0.0963138731026" +}, +{ + "attr_id":"GENDER", + "attr_val":"16.000000", + "sample":"0.171752742518" +}, +{ + "attr_id":"GENDER", + "attr_val":"0.000000", + "sample":"0.230093865396" +}, +{ + "attr_id":"GENDER", + "attr_val":"45.000000", + "sample":"0.106178438276" +}, +{ + "attr_id":"GENDER", + "attr_val":"19.000000", + "sample":"0.753073584138" +}, +{ + "attr_id":"GENDER", + "attr_val":"2.000000", + "sample":"0.705221029746" +}, +{ + "attr_id":"GENDER", + "attr_val":"50.000000", + "sample":"0.325826181637" +}, +{ + "attr_id":"GENDER", + "attr_val":"19.000000", + "sample":"0.332034876429" +}, +{ + "attr_id":"GENDER", + "attr_val":"77.000000", + "sample":"0.758339898255" +}, +{ + "attr_id":"GENDER", + "attr_val":"90.000000", + "sample":"0.0109616955358" +}, +{ + "attr_id":"GENDER", + "attr_val":"25.000000", + "sample":"0.679469781695" +}, +{ + "attr_id":"GENDER", + "attr_val":"22.000000", + "sample":"0.225445759499" +}, +{ + "attr_id":"GENDER", + "attr_val":"12.000000", + "sample":"0.573496723753" +}, +{ + "attr_id":"GENDER", + "attr_val":"22.000000", + "sample":"0.994863492365" +}, +{ + "attr_id":"GENDER", + "attr_val":"35.000000", + "sample":"0.282237855494" +}, +{ + "attr_id":"GENDER", + "attr_val":"29.000000", + "sample":"0.351632578287" +}, +{ + "attr_id":"GENDER", + "attr_val":"56.000000", + "sample":"0.546424356277" +}, +{ + "attr_id":"GENDER", + "attr_val":"45.000000", + "sample":"0.184550220112" +}, +{ + "attr_id":"GENDER", + "attr_val":"19.000000", + "sample":"0.85923455184" +}, +{ + "attr_id":"GENDER", + "attr_val":"1.000000", + "sample":"0.124285209577" +}, +{ + "attr_id":"GENDER", + "attr_val":"4.000000", + "sample":"0.922456016278" +}, +{ + "attr_id":"GENDER", + "attr_val":"36.000000", + "sample":"0.210812364454" +}, +{ + "attr_id":"GENDER", + "attr_val":"14.000000", + "sample":"0.993495515474" +}, +{ + "attr_id":"GENDER", + "attr_val":"23.000000", + "sample":"0.527945135014" +}, +{ + "attr_id":"GENDER", + "attr_val":"52.000000", + "sample":"0.595518565182" +}, +{ + "attr_id":"GENDER", + "attr_val":"24.000000", + "sample":"0.375240856534" +}, +{ + "attr_id":"GENDER", + "attr_val":"26.000000", + "sample":"0.0193209272658" +}, +{ + "attr_id":"GENDER", + "attr_val":"66.000000", + "sample":"0.474131191117" +}, +{ + "attr_id":"GENDER", + "attr_val":"8.000000", + "sample":"0.0653620251556" +}, +{ + "attr_id":"GENDER", + "attr_val":"78.000000", + "sample":"0.24891869504" +}, +{ + "attr_id":"GENDER", + "attr_val":"86.000000", + "sample":"0.107562296591" +}, +{ + "attr_id":"GENDER", + "attr_val":"19.000000", + "sample":"0.266953184406" +}, +{ + "attr_id":"GENDER", + "attr_val":"7.000000", + "sample":"0.289651601806" +}, +{ + "attr_id":"GENDER", + "attr_val":"99.000000", + "sample":"0.338210417519" +}, +{ + "attr_id":"GENDER", + "attr_val":"21.000000", + "sample":"0.375455029471" +}, +{ + "attr_id":"GENDER", + "attr_val":"14.000000", + "sample":"0.98984912134" +}, +{ + "attr_id":"GENDER", + "attr_val":"37.000000", + "sample":"0.59706050626" +}, +{ + "attr_id":"GENDER", + "attr_val":"10.000000", + "sample":"0.104028078074" +}, +{ + "attr_id":"GENDER", + "attr_val":"6.000000", + "sample":"0.961529586529" +}, +{ + "attr_id":"GENDER", + "attr_val":"70.000000", + "sample":"0.771449246975" +}, +{ + "attr_id":"GENDER", + "attr_val":"34.000000", + "sample":"0.194151882616" +}, +{ + "attr_id":"GENDER", + "attr_val":"37.000000", + "sample":"0.384174867292" +}, +{ + "attr_id":"GENDER", + "attr_val":"23.000000", + "sample":"0.022200502542" +}, +{ + "attr_id":"GENDER", + "attr_val":"25.000000", + "sample":"0.679113369968" +}, +{ + "attr_id":"GENDER", + "attr_val":"8.000000", + "sample":"0.399942437816" +}, +{ + "attr_id":"GENDER", + "attr_val":"23.000000", + "sample":"0.920309722173" +}, +{ + "attr_id":"GENDER", + "attr_val":"11.000000", + "sample":"0.487483911123" +}, +{ + "attr_id":"GENDER", + "attr_val":"80.000000", + "sample":"0.597609967676" +}, +{ + "attr_id":"GENDER", + "attr_val":"64.000000", + "sample":"0.535540590129" +}, +{ + "attr_id":"GENDER", + "attr_val":"19.000000", + "sample":"0.228485974086" +}, +{ + "attr_id":"GENDER", + "attr_val":"5.000000", + "sample":"0.670259100101" +}, +{ + "attr_id":"GENDER", + "attr_val":"67.000000", + "sample":"0.124892042138" +}, +{ + "attr_id":"GENDER", + "attr_val":"17.000000", + "sample":"0.815438384658" +}, +{ + "attr_id":"GENDER", + "attr_val":"5.000000", + "sample":"0.589873853896" +}, +{ + "attr_id":"GENDER", + "attr_val":"24.000000", + "sample":"0.103353129255" +}, +{ + "attr_id":"GENDER", + "attr_val":"21.000000", + "sample":"0.895801468994" +}, +{ + "attr_id":"GENDER", + "attr_val":"44.000000", + "sample":"0.46318266516" +}, +{ + "attr_id":"GENDER", + "attr_val":"0.000000", + "sample":"0.141707642825" +}, +{ + "attr_id":"GENDER", + "attr_val":"24.000000", + "sample":"0.0215154027056" +}, +{ + "attr_id":"GENDER", + "attr_val":"16.000000", + "sample":"0.647273343247" +}, +{ + "attr_id":"GENDER", + "attr_val":"2.000000", + "sample":"0.517324555675" +}, +{ + "attr_id":"GENDER", + "attr_val":"77.000000", + "sample":"0.561800018513" +}, +{ + "attr_id":"GENDER", + "attr_val":"14.000000", + "sample":"0.168358553528" +}, +{ + "attr_id":"GENDER", + "attr_val":"83.000000", + "sample":"0.869620164219" +}, +{ + "attr_id":"GENDER", + "attr_val":"7.000000", + "sample":"0.916605542234" +}, +{ + "attr_id":"GENDER", + "attr_val":"18.000000", + "sample":"0.766971477202" +}, +{ + "attr_id":"GENDER", + "attr_val":"5.000000", + "sample":"0.0445787006869" +}, +{ + "attr_id":"GENDER", + "attr_val":"8.000000", + "sample":"0.17172871642" +}, +{ + "attr_id":"GENDER", + "attr_val":"71.000000", + "sample":"0.24152752823" +}, +{ + "attr_id":"GENDER", + "attr_val":"34.000000", + "sample":"0.439200362032" +}, +{ + "attr_id":"GENDER", + "attr_val":"38.000000", + "sample":"0.0638355607072" +}, +{ + "attr_id":"GENDER", + "attr_val":"24.000000", + "sample":"0.116222564764" +}, +{ + "attr_id":"GENDER", + "attr_val":"21.000000", + "sample":"0.182459539811" +}, +{ + "attr_id":"GENDER", + "attr_val":"75.000000", + "sample":"0.581966937046" +}, +{ + "attr_id":"GENDER", + "attr_val":"25.000000", + "sample":"0.418503865136" +}, +{ + "attr_id":"GENDER", + "attr_val":"60.000000", + "sample":"0.442356278072" +}, +{ + "attr_id":"GENDER", + "attr_val":"0.000000", + "sample":"0.461844967416" +}, +{ + "attr_id":"GENDER", + "attr_val":"18.000000", + "sample":"0.940642040877" +}, +{ + "attr_id":"GENDER", + "attr_val":"83.000000", + "sample":"0.12768439018" +}, +{ + "attr_id":"GENDER", + "attr_val":"29.000000", + "sample":"0.427821626576" +}, +{ + "attr_id":"GENDER", + "attr_val":"16.000000", + "sample":"0.938786523781" +}, +{ + "attr_id":"GENDER", + "attr_val":"12.000000", + "sample":"0.284433159301" +}, +{ + "attr_id":"GENDER", + "attr_val":"79.000000", + "sample":"0.494116963749" +}, +{ + "attr_id":"GENDER", + "attr_val":"11.000000", + "sample":"0.431196631093" +}, +{ + "attr_id":"GENDER", + "attr_val":"5.000000", + "sample":"0.823597448258" +}, +{ + "attr_id":"GENDER", + "attr_val":"9.000000", + "sample":"0.0271786881831" +}, +{ + "attr_id":"GENDER", + "attr_val":"22.000000", + "sample":"0.682697675215" +}, +{ + "attr_id":"GENDER", + "attr_val":"85.000000", + "sample":"0.132060328093" +}, +{ + "attr_id":"GENDER", + "attr_val":"21.000000", + "sample":"0.500486517053" +}, +{ + "attr_id":"GENDER", + "attr_val":"48.000000", + "sample":"0.945081457048" +}, +{ + "attr_id":"GENDER", + "attr_val":"3.000000", + "sample":"0.018070329293" +}, +{ + "attr_id":"GENDER", + "attr_val":"83.000000", + "sample":"0.323536227905" +}, +{ + "attr_id":"GENDER", + "attr_val":"6.000000", + "sample":"0.473737103281" +}, +{ + "attr_id":"GENDER", + "attr_val":"39.000000", + "sample":"0.78267685792" +}, +{ + "attr_id":"GENDER", + "attr_val":"1.000000", + "sample":"0.935492647978" +}, +{ + "attr_id":"GENDER", + "attr_val":"83.000000", + "sample":"0.32671639226" +}, +{ + "attr_id":"GENDER", + "attr_val":"95.000000", + "sample":"0.204218375735" +}, +{ + "attr_id":"GENDER", + "attr_val":"48.000000", + "sample":"0.466744759883" +}, +{ + "attr_id":"GENDER", + "attr_val":"42.000000", + "sample":"0.435924203411" +}, +{ + "attr_id":"GENDER", + "attr_val":"14.000000", + "sample":"0.650801212621" +}, +{ + "attr_id":"GENDER", + "attr_val":"5.000000", + "sample":"0.402046884233" +}, +{ + "attr_id":"GENDER", + "attr_val":"91.000000", + "sample":"0.000280079618779" +}, +{ + "attr_id":"GENDER", + "attr_val":"59.000000", + "sample":"0.120145761803" +}, +{ + "attr_id":"GENDER", + "attr_val":"71.000000", + "sample":"0.664335609887" +}, +{ + "attr_id":"GENDER", + "attr_val":"47.000000", + "sample":"0.291330030567" +}, +{ + "attr_id":"GENDER", + "attr_val":"51.000000", + "sample":"0.253167674289" +}, +{ + "attr_id":"GENDER", + "attr_val":"96.000000", + "sample":"0.0339870039002" +}, +{ + "attr_id":"GENDER", + "attr_val":"26.000000", + "sample":"0.0867200370256" +}, +{ + "attr_id":"GENDER", + "attr_val":"36.000000", + "sample":"0.321875458375" +}, +{ + "attr_id":"GENDER", + "attr_val":"24.000000", + "sample":"0.279494894941" +}, +{ + "attr_id":"GENDER", + "attr_val":"23.000000", + "sample":"0.0461653344499" +}, +{ + "attr_id":"GENDER", + "attr_val":"51.000000", + "sample":"0.181006894309" +}, +{ + "attr_id":"GENDER", + "attr_val":"5.000000", + "sample":"0.577678963926" +}, +{ + "attr_id":"GENDER", + "attr_val":"20.000000", + "sample":"0.271820735413" +}, +{ + "attr_id":"GENDER", + "attr_val":"13.000000", + "sample":"0.877246143308" +}, +{ + "attr_id":"GENDER", + "attr_val":"13.000000", + "sample":"0.393877767822" +}, +{ + "attr_id":"GENDER", + "attr_val":"80.000000", + "sample":"0.413178048882" +}, +{ + "attr_id":"GENDER", + "attr_val":"6.000000", + "sample":"0.639776435219" +}, +{ + "attr_id":"GENDER", + "attr_val":"4.000000", + "sample":"0.286913451185" +}, +{ + "attr_id":"GENDER", + "attr_val":"1.000000", + "sample":"0.673476947994" +}, +{ + "attr_id":"GENDER", + "attr_val":"26.000000", + "sample":"0.484440330358" +}, +{ + "attr_id":"GENDER", + "attr_val":"1.000000", + "sample":"0.286603560458" +}, +{ + "attr_id":"GENDER", + "attr_val":"10.000000", + "sample":"0.568020503536" +}, +{ + "attr_id":"GENDER", + "attr_val":"62.000000", + "sample":"0.19171310759" +}, +{ + "attr_id":"GENDER", + "attr_val":"18.000000", + "sample":"0.538289948218" +}, +{ + "attr_id":"GENDER", + "attr_val":"95.000000", + "sample":"0.313509762774" +}, +{ + "attr_id":"GENDER", + "attr_val":"19.000000", + "sample":"0.68801917902" +}, +{ + "attr_id":"GENDER", + "attr_val":"50.000000", + "sample":"0.846882925541" +}, +{ + "attr_id":"GENDER", + "attr_val":"16.000000", + "sample":"0.345270013147" +}, +{ + "attr_id":"GENDER", + "attr_val":"19.000000", + "sample":"0.478522625438" +}, +{ + "attr_id":"GENDER", + "attr_val":"50.000000", + "sample":"0.0952664009957" +}, +{ + "attr_id":"GENDER", + "attr_val":"85.000000", + "sample":"0.662161409493" +}, +{ + "attr_id":"GENDER", + "attr_val":"29.000000", + "sample":"0.170556927216" +}, +{ + "attr_id":"GENDER", + "attr_val":"19.000000", + "sample":"0.850648643903" +}, +{ + "attr_id":"GENDER", + "attr_val":"10.000000", + "sample":"0.0838800519814" +}, +{ + "attr_id":"GENDER", + "attr_val":"67.000000", + "sample":"0.505970339724" +}, +{ + "attr_id":"GENDER", + "attr_val":"43.000000", + "sample":"0.351361241794" +}, +{ + "attr_id":"GENDER", + "attr_val":"25.000000", + "sample":"0.0275914527167" +}, +{ + "attr_id":"GENDER", + "attr_val":"11.000000", + "sample":"0.448497850725" +}, +{ + "attr_id":"GENDER", + "attr_val":"71.000000", + "sample":"0.40186723082" +}, +{ + "attr_id":"GENDER", + "attr_val":"13.000000", + "sample":"0.120146698357" +}, +{ + "attr_id":"GENDER", + "attr_val":"25.000000", + "sample":"0.941767267748" +}, +{ + "attr_id":"GENDER", + "attr_val":"22.000000", + "sample":"0.148240543595" +}, +{ + "attr_id":"GENDER", + "attr_val":"84.000000", + "sample":"0.678986190981" +}, +{ + "attr_id":"GENDER", + "attr_val":"26.000000", + "sample":"0.291529990819" +}, +{ + "attr_id":"GENDER", + "attr_val":"25.000000", + "sample":"0.826997426622" +}, +{ + "attr_id":"GENDER", + "attr_val":"3.000000", + "sample":"0.468999270201" +}, +{ + "attr_id":"GENDER", + "attr_val":"17.000000", + "sample":"0.742557760059" +}, +{ + "attr_id":"GENDER", + "attr_val":"41.000000", + "sample":"0.382627134048" +}, +{ + "attr_id":"GENDER", + "attr_val":"28.000000", + "sample":"0.0953715339477" +}, +{ + "attr_id":"GENDER", + "attr_val":"43.000000", + "sample":"0.981305165392" +}, +{ + "attr_id":"GENDER", + "attr_val":"29.000000", + "sample":"0.163825769435" +}, +{ + "attr_id":"GENDER", + "attr_val":"57.000000", + "sample":"0.762375096175" +}, +{ + "attr_id":"GENDER", + "attr_val":"78.000000", + "sample":"0.0217640847108" +}, +{ + "attr_id":"GENDER", + "attr_val":"11.000000", + "sample":"0.647476413264" +}, +{ + "attr_id":"GENDER", + "attr_val":"18.000000", + "sample":"0.676977019976" +}, +{ + "attr_id":"GENDER", + "attr_val":"26.000000", + "sample":"0.142415459679" +}, +{ + "attr_id":"GENDER", + "attr_val":"2.000000", + "sample":"0.449804594041" +}, +{ + "attr_id":"GENDER", + "attr_val":"18.000000", + "sample":"0.582660447836" +}, +{ + "attr_id":"GENDER", + "attr_val":"71.000000", + "sample":"0.031678873259" +}, +{ + "attr_id":"GENDER", + "attr_val":"48.000000", + "sample":"0.923191493944" +}, +{ + "attr_id":"GENDER", + "attr_val":"96.000000", + "sample":"0.584329479779" +}, +{ + "attr_id":"GENDER", + "attr_val":"21.000000", + "sample":"0.660774599767" +}, +{ + "attr_id":"GENDER", + "attr_val":"7.000000", + "sample":"0.515578116045" +}, +{ + "attr_id":"GENDER", + "attr_val":"54.000000", + "sample":"0.293721419028" +}, +{ + "attr_id":"GENDER", + "attr_val":"68.000000", + "sample":"0.201346526462" +}, +{ + "attr_id":"GENDER", + "attr_val":"27.000000", + "sample":"0.0271198065819" +}, +{ + "attr_id":"GENDER", + "attr_val":"20.000000", + "sample":"0.364770709957" +}, +{ + "attr_id":"GENDER", + "attr_val":"18.000000", + "sample":"0.939888767865" +}, +{ + "attr_id":"GENDER", + "attr_val":"1.000000", + "sample":"0.294698385103" +}, +{ + "attr_id":"GENDER", + "attr_val":"31.000000", + "sample":"0.132462304488" +}, +{ + "attr_id":"GENDER", + "attr_val":"30.000000", + "sample":"0.318872503471" +}, +{ + "attr_id":"GENDER", + "attr_val":"94.000000", + "sample":"0.260491060753" +}, +{ + "attr_id":"GENDER", + "attr_val":"19.000000", + "sample":"0.879460756714" +}, +{ + "attr_id":"GENDER", + "attr_val":"5.000000", + "sample":"0.927874740761" +}, +{ + "attr_id":"GENDER", + "attr_val":"14.000000", + "sample":"0.752219865024" +}, +{ + "attr_id":"GENDER", + "attr_val":"6.000000", + "sample":"0.342312324864" +}, +{ + "attr_id":"GENDER", + "attr_val":"20.000000", + "sample":"0.695922924094" +}, +{ + "attr_id":"GENDER", + "attr_val":"4.000000", + "sample":"0.461599880474" +}, +{ + "attr_id":"GENDER", + "attr_val":"82.000000", + "sample":"0.0386928681972" +}, +{ + "attr_id":"GENDER", + "attr_val":"87.000000", + "sample":"0.0759497903047" +}, +{ + "attr_id":"GENDER", + "attr_val":"67.000000", + "sample":"0.534532438797" +}, +{ + "attr_id":"GENDER", + "attr_val":"81.000000", + "sample":"0.499098614568" +}, +{ + "attr_id":"GENDER", + "attr_val":"26.000000", + "sample":"0.946736115444" +}, +{ + "attr_id":"GENDER", + "attr_val":"9.000000", + "sample":"0.257012792556" +}, +{ + "attr_id":"GENDER", + "attr_val":"56.000000", + "sample":"0.095069016488" +}, +{ + "attr_id":"GENDER", + "attr_val":"61.000000", + "sample":"0.91375717086" +}, +{ + "attr_id":"GENDER", + "attr_val":"3.000000", + "sample":"0.0137005549424" +}, +{ + "attr_id":"GENDER", + "attr_val":"14.000000", + "sample":"0.202887172462" +}, +{ + "attr_id":"GENDER", + "attr_val":"27.000000", + "sample":"0.590444904437" +}, +{ + "attr_id":"GENDER", + "attr_val":"20.000000", + "sample":"0.317175203753" +}, +{ + "attr_id":"GENDER", + "attr_val":"18.000000", + "sample":"0.804058694857" +}, +{ + "attr_id":"GENDER", + "attr_val":"47.000000", + "sample":"0.0688563131785" +}, +{ + "attr_id":"GENDER", + "attr_val":"24.000000", + "sample":"0.439338968414" +}, +{ + "attr_id":"GENDER", + "attr_val":"60.000000", + "sample":"0.0574366520634" +}, +{ + "attr_id":"GENDER", + "attr_val":"53.000000", + "sample":"0.358412772742" +}, +{ + "attr_id":"GENDER", + "attr_val":"83.000000", + "sample":"0.109725013139" +}, +{ + "attr_id":"GENDER", + "attr_val":"90.000000", + "sample":"0.146312927054" +}, +{ + "attr_id":"GENDER", + "attr_val":"41.000000", + "sample":"0.911320530712" +}, +{ + "attr_id":"GENDER", + "attr_val":"8.000000", + "sample":"0.523794359465" +}, +{ + "attr_id":"GENDER", + "attr_val":"0.000000", + "sample":"0.893013807435" +}, +{ + "attr_id":"GENDER", + "attr_val":"49.000000", + "sample":"0.509787927401" +}, +{ + "attr_id":"GENDER", + "attr_val":"15.000000", + "sample":"0.0773048060078" +}, +{ + "attr_id":"GENDER", + "attr_val":"81.000000", + "sample":"0.642910734402" +}, +{ + "attr_id":"GENDER", + "attr_val":"49.000000", + "sample":"0.74680463297" +}, +{ + "attr_id":"GENDER", + "attr_val":"14.000000", + "sample":"0.922104985361" +}, +{ + "attr_id":"GENDER", + "attr_val":"2.000000", + "sample":"0.0381924927661" +}, +{ + "attr_id":"GENDER", + "attr_val":"45.000000", + "sample":"0.441737595547" +}, +{ + "attr_id":"GENDER", + "attr_val":"6.000000", + "sample":"0.967248808261" +}, +{ + "attr_id":"GENDER", + "attr_val":"82.000000", + "sample":"0.566653599922" +}, +{ + "attr_id":"GENDER", + "attr_val":"85.000000", + "sample":"0.593315695175" +}, +{ + "attr_id":"GENDER", + "attr_val":"57.000000", + "sample":"0.603384915372" +}, +{ + "attr_id":"GENDER", + "attr_val":"93.000000", + "sample":"0.877188145946" +}, +{ + "attr_id":"GENDER", + "attr_val":"21.000000", + "sample":"0.908903798441" +}, +{ + "attr_id":"GENDER", + "attr_val":"13.000000", + "sample":"0.4073917486" +}, +{ + "attr_id":"GENDER", + "attr_val":"5.000000", + "sample":"0.193121534073" +}, +{ + "attr_id":"GENDER", + "attr_val":"11.000000", + "sample":"0.175849068429" +}, +{ + "attr_id":"GENDER", + "attr_val":"17.000000", + "sample":"0.375245279124" +}, +{ + "attr_id":"GENDER", + "attr_val":"11.000000", + "sample":"0.222745785183" +}, +{ + "attr_id":"GENDER", + "attr_val":"2.000000", + "sample":"0.390340125452" +}, +{ + "attr_id":"GENDER", + "attr_val":"10.000000", + "sample":"0.711738693763" +}, +{ + "attr_id":"GENDER", + "attr_val":"2.000000", + "sample":"0.522471977689" +}, +{ + "attr_id":"GENDER", + "attr_val":"20.000000", + "sample":"0.0039334897952" +}, +{ + "attr_id":"GENDER", + "attr_val":"47.000000", + "sample":"0.702244842717" +}, +{ + "attr_id":"GENDER", + "attr_val":"5.000000", + "sample":"0.777744414507" +}, +{ + "attr_id":"GENDER", + "attr_val":"25.000000", + "sample":"0.000362828141133" +}, +{ + "attr_id":"GENDER", + "attr_val":"63.000000", + "sample":"0.105432155077" +}, +{ + "attr_id":"GENDER", + "attr_val":"48.000000", + "sample":"0.0348303652252" +}, +{ + "attr_id":"GENDER", + "attr_val":"28.000000", + "sample":"0.452008157341" +}, +{ + "attr_id":"GENDER", + "attr_val":"22.000000", + "sample":"0.117422021136" +}, +{ + "attr_id":"GENDER", + "attr_val":"29.000000", + "sample":"0.140528297078" +}, +{ + "attr_id":"GENDER", + "attr_val":"4.000000", + "sample":"0.802255600003" +}, +{ + "attr_id":"GENDER", + "attr_val":"10.000000", + "sample":"0.299643954147" +}, +{ + "attr_id":"GENDER", + "attr_val":"11.000000", + "sample":"0.583479891552" +}, +{ + "attr_id":"GENDER", + "attr_val":"72.000000", + "sample":"0.986114435266" +}, +{ + "attr_id":"GENDER", + "attr_val":"23.000000", + "sample":"0.966179400253" +}, +{ + "attr_id":"GENDER", + "attr_val":"97.000000", + "sample":"0.222905916843" +}, +{ + "attr_id":"GENDER", + "attr_val":"2.000000", + "sample":"0.73308798913" +}, +{ + "attr_id":"GENDER", + "attr_val":"25.000000", + "sample":"0.0731193771906" +}, +{ + "attr_id":"GENDER", + "attr_val":"38.000000", + "sample":"0.608797888276" +}, +{ + "attr_id":"GENDER", + "attr_val":"22.000000", + "sample":"0.177704425363" +}, +{ + "attr_id":"GENDER", + "attr_val":"2.000000", + "sample":"0.273236165018" +}, +{ + "attr_id":"GENDER", + "attr_val":"33.000000", + "sample":"0.161006765433" +}, +{ + "attr_id":"GENDER", + "attr_val":"72.000000", + "sample":"0.293954094695" +}, +{ + "attr_id":"GENDER", + "attr_val":"27.000000", + "sample":"0.988753414041" +}, +{ + "attr_id":"GENDER", + "attr_val":"7.000000", + "sample":"0.191329291933" +}, +{ + "attr_id":"GENDER", + "attr_val":"17.000000", + "sample":"0.820025606209" +}, +{ + "attr_id":"GENDER", + "attr_val":"25.000000", + "sample":"0.581125518947" +}, +{ + "attr_id":"GENDER", + "attr_val":"2.000000", + "sample":"0.392877041938" +}, +{ + "attr_id":"GENDER", + "attr_val":"71.000000", + "sample":"0.547050467257" +}, +{ + "attr_id":"GENDER", + "attr_val":"55.000000", + "sample":"0.404328434605" +}, +{ + "attr_id":"GENDER", + "attr_val":"47.000000", + "sample":"0.179624050609" +}, +{ + "attr_id":"GENDER", + "attr_val":"52.000000", + "sample":"0.00138469261595" +}, +{ + "attr_id":"GENDER", + "attr_val":"53.000000", + "sample":"0.270628444466" +}, +{ + "attr_id":"GENDER", + "attr_val":"8.000000", + "sample":"0.98904051205" +}, +{ + "attr_id":"GENDER", + "attr_val":"6.000000", + "sample":"0.376384887028" +}, +{ + "attr_id":"GENDER", + "attr_val":"6.000000", + "sample":"0.143126109392" +}, +{ + "attr_id":"GENDER", + "attr_val":"97.000000", + "sample":"0.111845879226" +}, +{ + "attr_id":"GENDER", + "attr_val":"31.000000", + "sample":"0.486191584316" +}, +{ + "attr_id":"GENDER", + "attr_val":"88.000000", + "sample":"0.354920699636" +}, +{ + "attr_id":"GENDER", + "attr_val":"4.000000", + "sample":"0.335957702573" +}, +{ + "attr_id":"GENDER", + "attr_val":"1.000000", + "sample":"0.215767166479" +}, +{ + "attr_id":"GENDER", + "attr_val":"5.000000", + "sample":"0.0135487627414" +}, +{ + "attr_id":"GENDER", + "attr_val":"80.000000", + "sample":"0.148622791169" +}, +{ + "attr_id":"GENDER", + "attr_val":"52.000000", + "sample":"0.404661024806" +}, +{ + "attr_id":"GENDER", + "attr_val":"19.000000", + "sample":"0.530937966811" +}, +{ + "attr_id":"GENDER", + "attr_val":"93.000000", + "sample":"0.586777128406" +}, +{ + "attr_id":"GENDER", + "attr_val":"22.000000", + "sample":"0.658852413973" +}, +{ + "attr_id":"GENDER", + "attr_val":"26.000000", + "sample":"0.729127280343" +}, +{ + "attr_id":"GENDER", + "attr_val":"24.000000", + "sample":"0.646228402375" +}, +{ + "attr_id":"GENDER", + "attr_val":"21.000000", + "sample":"0.165308806614" +}, +{ + "attr_id":"GENDER", + "attr_val":"52.000000", + "sample":"0.270863388884" +}, +{ + "attr_id":"GENDER", + "attr_val":"10.000000", + "sample":"0.970658098212" +}, +{ + "attr_id":"GENDER", + "attr_val":"64.000000", + "sample":"0.147616774065" +}, +{ + "attr_id":"GENDER", + "attr_val":"22.000000", + "sample":"0.227036712015" +}, +{ + "attr_id":"GENDER", + "attr_val":"17.000000", + "sample":"0.684914951863" +}, +{ + "attr_id":"GENDER", + "attr_val":"25.000000", + "sample":"0.230693493604" +}, +{ + "attr_id":"GENDER", + "attr_val":"22.000000", + "sample":"0.524209690189" +}, +{ + "attr_id":"GENDER", + "attr_val":"63.000000", + "sample":"0.85924031996" +}, +{ + "attr_id":"GENDER", + "attr_val":"94.000000", + "sample":"0.230323535571" +}, +{ + "attr_id":"GENDER", + "attr_val":"24.000000", + "sample":"0.947371720815" +}, +{ + "attr_id":"GENDER", + "attr_val":"56.000000", + "sample":"0.829289310586" +}, +{ + "attr_id":"GENDER", + "attr_val":"48.000000", + "sample":"0.622492050365" +}, +{ + "attr_id":"GENDER", + "attr_val":"23.000000", + "sample":"0.313618958576" +}, +{ + "attr_id":"GENDER", + "attr_val":"0.000000", + "sample":"0.973156429026" +}, +{ + "attr_id":"GENDER", + "attr_val":"75.000000", + "sample":"0.403753946339" +}, +{ + "attr_id":"GENDER", + "attr_val":"24.000000", + "sample":"0.862992314818" +}, +{ + "attr_id":"GENDER", + "attr_val":"23.000000", + "sample":"0.476912917638" +}, +{ + "attr_id":"GENDER", + "attr_val":"19.000000", + "sample":"0.465085412205" +}, +{ + "attr_id":"GENDER", + "attr_val":"60.000000", + "sample":"0.303926251493" +}, +{ + "attr_id":"GENDER", + "attr_val":"29.000000", + "sample":"0.454259508889" +}, +{ + "attr_id":"GENDER", + "attr_val":"85.000000", + "sample":"0.592964852492" +}, +{ + "attr_id":"GENDER", + "attr_val":"89.000000", + "sample":"0.968633927113" +}, +{ + "attr_id":"GENDER", + "attr_val":"7.000000", + "sample":"0.404665465512" +}, +{ + "attr_id":"GENDER", + "attr_val":"12.000000", + "sample":"0.50408042066" +}, +{ + "attr_id":"GENDER", + "attr_val":"81.000000", + "sample":"0.179737878317" +}, +{ + "attr_id":"GENDER", + "attr_val":"15.000000", + "sample":"0.291791126818" +}, +{ + "attr_id":"GENDER", + "attr_val":"25.000000", + "sample":"0.796765667122" +}, +{ + "attr_id":"GENDER", + "attr_val":"11.000000", + "sample":"0.301009308062" +}, +{ + "attr_id":"GENDER", + "attr_val":"63.000000", + "sample":"0.241677072217" +}, +{ + "attr_id":"GENDER", + "attr_val":"23.000000", + "sample":"0.73126816588" +}, +{ + "attr_id":"GENDER", + "attr_val":"20.000000", + "sample":"0.199230157886" +}, +{ + "attr_id":"GENDER", + "attr_val":"7.000000", + "sample":"0.294064887239" +}, +{ + "attr_id":"GENDER", + "attr_val":"7.000000", + "sample":"0.924224423886" +}, +{ + "attr_id":"GENDER", + "attr_val":"24.000000", + "sample":"0.0610932019434" +}, +{ + "attr_id":"GENDER", + "attr_val":"11.000000", + "sample":"0.102268819724" +}, +{ + "attr_id":"GENDER", + "attr_val":"5.000000", + "sample":"0.755847808743" +}, +{ + "attr_id":"GENDER", + "attr_val":"15.000000", + "sample":"0.194190796976" +}, +{ + "attr_id":"GENDER", + "attr_val":"23.000000", + "sample":"0.297734134497" +}, +{ + "attr_id":"GENDER", + "attr_val":"2.000000", + "sample":"0.345191739956" +}, +{ + "attr_id":"GENDER", + "attr_val":"97.000000", + "sample":"0.680797188655" +}, +{ + "attr_id":"GENDER", + "attr_val":"60.000000", + "sample":"0.124704157158" +}, +{ + "attr_id":"GENDER", + "attr_val":"23.000000", + "sample":"0.182599993787" +}, +{ + "attr_id":"GENDER", + "attr_val":"16.000000", + "sample":"0.0829043140527" +}, +{ + "attr_id":"GENDER", + "attr_val":"9.000000", + "sample":"0.0605147442325" +}, +{ + "attr_id":"GENDER", + "attr_val":"28.000000", + "sample":"0.917317038493" +}, +{ + "attr_id":"GENDER", + "attr_val":"67.000000", + "sample":"0.622358579292" +}, +{ + "attr_id":"GENDER", + "attr_val":"84.000000", + "sample":"0.715317485394" +}, +{ + "attr_id":"GENDER", + "attr_val":"74.000000", + "sample":"0.116771693553" +}, +{ + "attr_id":"GENDER", + "attr_val":"24.000000", + "sample":"0.54059955238" +}, +{ + "attr_id":"GENDER", + "attr_val":"49.000000", + "sample":"0.31514175746" +}, +{ + "attr_id":"GENDER", + "attr_val":"25.000000", + "sample":"0.294500441011" +}, +{ + "attr_id":"GENDER", + "attr_val":"82.000000", + "sample":"0.887873701403" +}, +{ + "attr_id":"GENDER", + "attr_val":"37.000000", + "sample":"0.84659575099" +}, +{ + "attr_id":"GENDER", + "attr_val":"3.000000", + "sample":"0.273776549173" +}, +{ + "attr_id":"GENDER", + "attr_val":"36.000000", + "sample":"0.887899094414" +}, +{ + "attr_id":"GENDER", + "attr_val":"4.000000", + "sample":"0.649106407189" +}, +{ + "attr_id":"GENDER", + "attr_val":"83.000000", + "sample":"0.503570093267" +}, +{ + "attr_id":"GENDER", + "attr_val":"17.000000", + "sample":"0.463211555447" +}, +{ + "attr_id":"GENDER", + "attr_val":"22.000000", + "sample":"0.98124512905" +}, +{ + "attr_id":"GENDER", + "attr_val":"26.000000", + "sample":"0.837421643615" +}, +{ + "attr_id":"GENDER", + "attr_val":"16.000000", + "sample":"0.218600168126" +}, +{ + "attr_id":"GENDER", + "attr_val":"50.000000", + "sample":"0.225492853467" +}, +{ + "attr_id":"GENDER", + "attr_val":"3.000000", + "sample":"0.316185442681" +}, +{ + "attr_id":"GENDER", + "attr_val":"32.000000", + "sample":"0.0582150858813" +}, +{ + "attr_id":"GENDER", + "attr_val":"7.000000", + "sample":"0.439247889897" +}, +{ + "attr_id":"GENDER", + "attr_val":"1.000000", + "sample":"0.925384049999" +}, +{ + "attr_id":"GENDER", + "attr_val":"25.000000", + "sample":"0.651808236866" +}, +{ + "attr_id":"GENDER", + "attr_val":"43.000000", + "sample":"0.688892000322" +}, +{ + "attr_id":"GENDER", + "attr_val":"20.000000", + "sample":"0.0771245008298" +}, +{ + "attr_id":"GENDER", + "attr_val":"49.000000", + "sample":"0.399548423632" +}, +{ + "attr_id":"GENDER", + "attr_val":"90.000000", + "sample":"0.899084833711" +}, +{ + "attr_id":"GENDER", + "attr_val":"20.000000", + "sample":"0.736767393457" +}, +{ + "attr_id":"GENDER", + "attr_val":"27.000000", + "sample":"0.159025547734" +}, +{ + "attr_id":"GENDER", + "attr_val":"0.000000", + "sample":"0.842631306041" +}, +{ + "attr_id":"GENDER", + "attr_val":"31.000000", + "sample":"0.673262622695" +}, +{ + "attr_id":"GENDER", + "attr_val":"0.000000", + "sample":"0.685605670926" +}, +{ + "attr_id":"GENDER", + "attr_val":"71.000000", + "sample":"0.292524130852" +}, +{ + "attr_id":"GENDER", + "attr_val":"21.000000", + "sample":"0.364477164324" +}, +{ + "attr_id":"GENDER", + "attr_val":"89.000000", + "sample":"0.714422947719" +}, +{ + "attr_id":"GENDER", + "attr_val":"5.000000", + "sample":"0.636804112486" +}, +{ + "attr_id":"GENDER", + "attr_val":"33.000000", + "sample":"0.222004757317" +}, +{ + "attr_id":"GENDER", + "attr_val":"8.000000", + "sample":"0.863778533887" +}, +{ + "attr_id":"GENDER", + "attr_val":"29.000000", + "sample":"0.32229847301" +}, +{ + "attr_id":"GENDER", + "attr_val":"10.000000", + "sample":"0.926697192094" +}, +{ + "attr_id":"GENDER", + "attr_val":"23.000000", + "sample":"0.890424047091" +}, +{ + "attr_id":"GENDER", + "attr_val":"10.000000", + "sample":"0.178927947656" +}, +{ + "attr_id":"GENDER", + "attr_val":"47.000000", + "sample":"0.178172763695" +}, +{ + "attr_id":"GENDER", + "attr_val":"29.000000", + "sample":"0.588407371577" +}, +{ + "attr_id":"GENDER", + "attr_val":"14.000000", + "sample":"0.796086936857" +}, +{ + "attr_id":"GENDER", + "attr_val":"14.000000", + "sample":"0.51973252756" +}, +{ + "attr_id":"GENDER", + "attr_val":"23.000000", + "sample":"0.820420145682" +}, +{ + "attr_id":"GENDER", + "attr_val":"5.000000", + "sample":"0.29656966996" +}, +{ + "attr_id":"GENDER", + "attr_val":"11.000000", + "sample":"0.685193906139" +}, +{ + "attr_id":"GENDER", + "attr_val":"11.000000", + "sample":"0.406417975677" +}, +{ + "attr_id":"GENDER", + "attr_val":"89.000000", + "sample":"0.638111494278" +}, +{ + "attr_id":"GENDER", + "attr_val":"32.000000", + "sample":"0.555663072566" +}, +{ + "attr_id":"GENDER", + "attr_val":"29.000000", + "sample":"0.325785237528" +}, +{ + "attr_id":"GENDER", + "attr_val":"5.000000", + "sample":"0.324733795072" +}, +{ + "attr_id":"GENDER", + "attr_val":"9.000000", + "sample":"0.447639363432" +} + + ] +} diff --git a/packages/oncoprintjs/test/index.html b/packages/oncoprintjs/test/index.html index b17019ed3be..980f41b3e45 100644 --- a/packages/oncoprintjs/test/index.html +++ b/packages/oncoprintjs/test/index.html @@ -11,6 +11,7 @@ --> + diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index c4a6a537fd9..8dc0c9a140c 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -26,6 +26,10 @@ var gender_data; var gender_track_id; var gender_data_promise = $.getJSON('./gbm/gender-gbm.json'); +var mutation_data; +var mutation_track_id; +var mutation_data_promise = $.getJSON('./gbm/mutations-gbm.json'); + gender_data_promise.then(function(data) { gender_data = data.data; }); @@ -37,4 +41,25 @@ $.when(gender_data_promise).then(function() { return d.attr_val; } }); +}); + +mutation_data_promise.then(function(data) { + mutation_data = data.data; +}); +$.when(mutation_data_promise).then(function() { + mutation_track_id = onc.addTrack(mutation_data, {label: 'Mutations'}); + onc.getTrack(mutation_track_id).useRenderTemplate('continuous_color', { + data_key: 'attr_val', + data_range: [0,100], + color_range: ['#A9A9A9', '#FF0000'] + }); +}); + +$('#change_color_scheme').click(function() { + onc.getTrack(gender_track_id).useRenderTemplate('categorical_color', { + color: {MALE: '#0F0F0F', FEMALE: '#D6D6D6'}, + category: function(d) { + return d.attr_val; + } + }); }); \ No newline at end of file From 719fa92476faae26a6c80f47bd3c2915f7d819ed Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Wed, 10 Jun 2015 16:04:33 -0400 Subject: [PATCH 050/343] incomplete commit...still working on legend and on genetic alteration template --- .../oncoprintjs/src/js/D3SVGCellRenderer.js | 101 ++++++++++++++---- packages/oncoprintjs/src/js/oncoprint.js | 30 +++++- packages/oncoprintjs/src/js/utils.js | 16 +++ packages/oncoprintjs/test/js/test_page.js | 23 ++++ 4 files changed, 148 insertions(+), 22 deletions(-) diff --git a/packages/oncoprintjs/src/js/D3SVGCellRenderer.js b/packages/oncoprintjs/src/js/D3SVGCellRenderer.js index bbddd0a0ff3..192517a9001 100644 --- a/packages/oncoprintjs/src/js/D3SVGCellRenderer.js +++ b/packages/oncoprintjs/src/js/D3SVGCellRenderer.js @@ -36,6 +36,8 @@ var signals = require('./signals'); var globals = require('./globals'); function D3SVGRule() { + this.exclude_from_legend; + var percentToPx = function(attr_val, attr_name, cell_width, cell_height) { // convert a percentage to a local pixel coordinate var width_like = ['width', 'x']; @@ -74,11 +76,12 @@ function D3SVGRule() { } -function D3SVGLinearGradientRule(condition, d3_shape, data_key, data_range, color_range, z_index, rule_id) { +function D3SVGLinearGradientRule(condition, d3_shape, data_key, data_range, color_range, z_index, rule_id, exclude_from_legend) { var self = this; self.rule_id = rule_id; self.condition = condition; self.shape = d3_shape; + self.exclude_from_legend = exclude_from_legend; var fill_function = (function(_data_range, _color_range) { return function(d) { @@ -126,7 +129,7 @@ D3SVGLinearGradientRule.prototype = new D3SVGRule(); D3SVGLinearGradientRule.prototype.constructor=D3SVGLinearGradientRule; -function D3SVGStaticRule(condition, d3_shape, attrs, z_index, rule_id, legend_label) { +function D3SVGStaticRule(condition, d3_shape, attrs, z_index, rule_id, legend_label, exclude_from_legend) { var self = this; self.rule_id = rule_id; self.condition = condition; @@ -134,6 +137,7 @@ function D3SVGStaticRule(condition, d3_shape, attrs, z_index, rule_id, legend_la self.attrs = attrs; self.z_index = z_index; self.legend_label = legend_label; + self.exclude_from_legend = exclude_from_legend; self.getLegendGroup = function(cell_width, cell_height) { var group = utils.makeD3SVGElement('g'); @@ -154,24 +158,24 @@ function D3SVGRuleset(track_config) { self.rule_map = {}; self.track_config = track_config; - self.addStaticRule = function(condition, d3_shape, attrs, z_index, legend_label) { + self.addStaticRule = function(condition, d3_shape, attrs, z_index, legend_label, exclude_from_legend) { var rule_id = Object.keys(self.rule_map).length; attrs = attrs || {}; if (z_index === undefined) { z_index = rule_id; } - self.rule_map[rule_id] = new D3SVGStaticRule(condition, d3_shape, attrs, z_index, rule_id, legend_label); + self.rule_map[rule_id] = new D3SVGStaticRule(condition, d3_shape, attrs, z_index, rule_id, legend_label, exclude_from_legend); globals.rulesvgs = globals.rulesvgs || []; globals.rulesvgs.push(self.rule_map[rule_id].getLegendGroup(10, 20)); return rule_id; }; - self.addLinearGradientRule = function(condition, d3_shape, data_key, data_range, color_range, z_index) { + self.addLinearGradientRule = function(condition, d3_shape, data_key, data_range, color_range, z_index, exclude_from_legend) { var rule_id = Object.keys(self.rule_map).length; if (z_index === undefined) { z_index = rule_id; } - self.rule_map[rule_id] = new D3SVGLinearGradientRule(condition, d3_shape, data_key, data_range, color_range, z_index, rule_id); + self.rule_map[rule_id] = new D3SVGLinearGradientRule(condition, d3_shape, data_key, data_range, color_range, z_index, rule_id, exclude_from_legend); globals.rulesvgs = globals.rulesvgs || []; globals.rulesvgs.push(self.rule_map[rule_id].getLegendGroup(10, 20)); return rule_id; @@ -216,7 +220,7 @@ function D3SVGRuleset(track_config) { // if only_active is true, then only give back the rules that are used at least once var legend_map = {}; _.each(getOrderedRules(), function(rule) { - if (!only_active || filterData(rule, d3_data).length > 0) { + if ((!only_active || filterData(rule, d3_data).length > 0) && !rule.exclude_from_legend) { legend_map[rule.rule_id] = rule.legend_g; } }); @@ -239,6 +243,7 @@ function D3SVGCellRenderer(data, track_config) { self.setRuleset = function(json_rules) { self.rule_set.fromJSON(json_rules); + $(self).trigger(events.UPDATE_RENDER_RULES); }; self.addRule = function(params) { @@ -249,14 +254,14 @@ function D3SVGCellRenderer(data, track_config) { }; self.addStaticRule = function(params) { - var rule_id = self.rule_set.addStaticRule(params.condition, params.d3_shape, params.attrs, params.z_index, params.legend_label); + var rule_id = self.rule_set.addStaticRule(params.condition, params.d3_shape, params.attrs, params.z_index, params.legend_label, params.exclude_from_legend); updateCells(); $(self).trigger(events.UPDATE_RENDER_RULES); return rule_id; }; self.addLinearGradientRule = function(params) { - var rule_id = self.rule_set.addLinearGradientRule(params.condition, params.d3_shape, params.data_key, params.data_range, params.color_range, params.z_index); + var rule_id = self.rule_set.addLinearGradientRule(params.condition, params.d3_shape, params.data_key, params.data_range, params.color_range, params.z_index, params.exclude_from_legend); updateCells(); $(self).trigger(events.UPDATE_RENDER_RULES); return rule_id; @@ -280,7 +285,7 @@ function D3SVGCellRenderer(data, track_config) { }; var updateCellArea = function() { - self.svg.attr('width', self.track_config.get('pre_track_padding') + (self.track_config.get('cell_width') + self.track_config.get('cell_padding'))*self.data.length) + self.svg.attr('width', self.track_config.get('pre_track_padding') + (self.track_config.get('cell_width') + self.track_config.get('cell_padding'))*self.track_config.get('id_order').length) .attr('height', self.track_config.get('track_height')); }; @@ -298,6 +303,7 @@ function D3SVGCellRenderer(data, track_config) { g_target = self.g; } g_target.attr('transform', function(d,i) { + return utils.translate(self.track_config.get('pre_track_padding') + id_order[self.track_config.get('datum_id')(d)]*(self.track_config.get('cell_width') + self.track_config.get('cell_padding')), 0); }); @@ -352,10 +358,6 @@ function D3SVGCellRenderer(data, track_config) { })(category); self.addStaticRule({ condition: condition, d3_shape: rect, - attrs: { - width: '100%', - height: '100%' - }, legend_label: category }); }); @@ -379,6 +381,7 @@ function D3SVGCellRenderer(data, track_config) { // - endpoints of the value range // - color: string or function of datum // - scale + /* var rect = utils.makeD3SVGElement('rect'); var range = params.range.slice(); var effective_range = params.range.slice(); @@ -429,11 +432,64 @@ function D3SVGCellRenderer(data, track_config) { .attr('x', 0) .attr('y', track_config.get('track_height')) .text(range[0]); + */ } else if (templName === 'genetic_alteration') { params = $.extend({}, params); var rect = utils.makeD3SVGElement('rect'); // background (CNA) - var cna = params.cna_name || 'cna'; + var cna = params.cna_key || 'cna'; + self.addStaticRule({ + condition: function(d) { + return d[cna] === params.cna_amp_name; + }, + d3_shape: rect, + legend_label: 'Amplification', + attrs: { + fill: params.cna_amp_color + } + }); + self.addStaticRule({ + condition: function(d) { + return d[cna] === params.cna_homdel_name; + }, + d3_shape: rect, + legend_label: 'Homozygous Deletion', + attrs: { + fill: params.cna_homdel_color + } + }); + console.log(params); + self.addStaticRule({ + condition: function(d) { + return d[cna] === params.cna_gain_name; + }, + d3_shape: rect, + legend_label: 'Gain', + attrs: { + fill: params.cna_gain_color + } + }); + self.addStaticRule({ + condition: function(d) { + return d[cna] === params.cna_hetloss_name; + }, + d3_shape: rect, + legend_label: 'Hemizygous Deletion', + attrs: { + fill: params.cna_hetloss_color + } + }); + self.addStaticRule({ + condition: function(d) { + return !d[cna]; + }, + d3_shape: rect, + attrs: { + fill: params.default_cell_color + }, + exclude_from_legend: true + }); + /* self.addRule({ d3_shape: rect, attrs: { @@ -449,9 +505,13 @@ function D3SVGCellRenderer(data, track_config) { } } } - }); + });*/ // mutations - var mut = params.mut_name || 'mut'; + var mut = params.mut_key || 'mut'; + /* + self.addStaticRule({ + condition: function(d) {} + }); self.addRule({ condition: function(d) { return !!d[mut]; }, d3_shape: rect, @@ -475,9 +535,10 @@ function D3SVGCellRenderer(data, track_config) { } } } - }); + });*/ // mrna var mrna = params.mrna_name || 'mrna'; + /* self.addRule({ condition: function(d) { return !!d[mrna]; }, d3_shape: rect, @@ -496,9 +557,9 @@ function D3SVGCellRenderer(data, track_config) { } } } - }); + });*/ // TODO: rppa - var triangle_up = utils.makeD3SVGElement('path').attr('d', 'triangle-up') + var triangle_up = utils.makeD3SVGElement('path').attr('d', 'triangle-up'); } }; self.bindEvents = function(track) { diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index e16932589d6..232c7883cb2 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -91,7 +91,8 @@ function Oncoprint(container_selector_string, config) { }; self.sortOnTrack = function(track_id, data_cmp) { - self.config.id_order = self.tracks[track_id].getDatumIds(data_cmp); + var track_order = self.tracks[track_id].getDatumIds(data_cmp); + self.config.id_order = track_order.concat(_.difference(self.config.id_order, track_order)); $(self).trigger(events.SORT, {id_order: self.config.id_order}); triggerTracks(events.SORT, {id_order: self.config.id_order}); }; @@ -152,6 +153,31 @@ function Oncoprint(container_selector_string, config) { })(self); } +function OncoprintLegendRenderer(container_selector_string) { + var self = this; + self.container = d3.select(container_selector_string).classed('oncoprint_legend_container', true); + self.table; + self.$table; + + var rows = []; + + (function initTable(self) { + self.container.selectAll('*').remove(); + self.table = self.container.append('table'); + self.$table = $(self.table.node()); + })(self); + + var addRow = function(track_id) { + + } + + self.bindEvents = function(oncoprint) { + $(oncoprint).on(events.UPDATE_RENDER_RULES, function(e, data) { + + }); + }; +} + function OncoprintTableRenderer(container_selector_string) { var self = this; self.container = d3.select(container_selector_string).classed('oncoprint_container', true); @@ -170,7 +196,7 @@ function OncoprintTableRenderer(container_selector_string) { self.$scrolling_table = $(self.scrolling_table.node()); self.legend_table = self.container.append('div').classed('legend_oncoprint_table_container', true).append('table'); self.$legend_table = $(self.legend_table.node()); - self.legend_table.append('tr').append('svg').attr('width', 600).attr('height', 100); + self.legend_table.append('tr').append('svg').attr('width', 2000).attr('height', 100); })(self); self.bindEvents = function(oncoprint) { diff --git a/packages/oncoprintjs/src/js/utils.js b/packages/oncoprintjs/src/js/utils.js index 0f894bc75b3..2be48dc694a 100644 --- a/packages/oncoprintjs/src/js/utils.js +++ b/packages/oncoprintjs/src/js/utils.js @@ -61,6 +61,22 @@ exports.spaceSVGElementsHorizontally = function(group, padding) { return group; }; +exports.mutationType = function(mutation) { + var regex = { + 'NONSENSE': /^[A-Z]([0-9]+)[*]$/g, + 'FRAMESHIFT_DEL': /^[A-z*]([0-9]+)[A-z]{2}$/g, + 'INFRAME_DEL': /^([A-Z]+)([0-9]+)del$/g, + 'INFRAME_INS': /^([A-Z]+)([0-9]+)ins$/g, + 'SPLICE_SITE': /^[A-Z]([0-9]+)_splice$/g, + 'MISSENSE': /^[A-z]([0-9]+)[A-z]$/g + } + if (nonsense_regex.test(mutation)) { + + } else if (frame_shift_del_regex.test(mutation)) { + + } else if (frame_shift_ins) +}; + exports.d3SelectChildren = function(parent, selector) { return parent.selectAll(selector).filter(function() { return this.parentNode === parent.node(); diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index 8dc0c9a140c..920298bc232 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -30,6 +30,10 @@ var mutation_data; var mutation_track_id; var mutation_data_promise = $.getJSON('./gbm/mutations-gbm.json'); +var alteration_data; +var alteration_track_id; +var alteration_data_promise = $.getJSON('./gbm/tp53.json'); + gender_data_promise.then(function(data) { gender_data = data.data; }); @@ -55,6 +59,25 @@ $.when(mutation_data_promise).then(function() { }); }); +alteration_data_promise.then(function(data) { + alteration_data = data; +}); +$.when(alteration_data_promise).then(function() { + alteration_track_id = onc.addTrack(alteration_data, {label: 'TP53'}); + onc.getTrack(alteration_track_id).useRenderTemplate('genetic_alteration', { + cna_key: 'cna', + cna_amp_name: 'AMPLIFIED', + cna_homdel_name: 'HOMODELETED', + cna_gain_name: 'GAINED', + cna_hetloss_name: 'HETLOSS', + cna_amp_color: '#FF0000', + cna_gain_color: '#FFB6C1', + cna_hetloss_color: '#8FD8D8', + cna_homdel_color: '#0000FF', + default_cell_color: '#D3D3D3' + }); +}); + $('#change_color_scheme').click(function() { onc.getTrack(gender_track_id).useRenderTemplate('categorical_color', { color: {MALE: '#0F0F0F', FEMALE: '#D6D6D6'}, From 98fd7bf855cb6f1c4a99e250aae8e062bda4d175 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Wed, 10 Jun 2015 18:06:17 -0400 Subject: [PATCH 051/343] first commit, beginning to remove unnecessary oopness in tracks --- packages/oncoprintjs/src/css/oncoprint.css | 14 +- .../oncoprintjs/src/js/D3SVGCellRenderer.js | 6 + packages/oncoprintjs/src/js/oncoprint.js | 135 ++++++++++++------ packages/oncoprintjs/src/js/track.js | 18 +-- packages/oncoprintjs/src/js/utils.js | 6 +- packages/oncoprintjs/test/js/test_page.js | 5 +- 6 files changed, 126 insertions(+), 58 deletions(-) diff --git a/packages/oncoprintjs/src/css/oncoprint.css b/packages/oncoprintjs/src/css/oncoprint.css index 3b4cac391fc..a8ee755ffe8 100644 --- a/packages/oncoprintjs/src/css/oncoprint.css +++ b/packages/oncoprintjs/src/css/oncoprint.css @@ -2,10 +2,20 @@ //position: relative; } -.fixed_oncoprint_table_container { +.fixed_oncoprint_section_container { float:left; + overflow: auto; + width: auto; } -.scrolling_oncoprint_table_container { +.scrolling_oncoprint_section_container { overflow: auto; width: auto; +} + +.scrolling_oncoprint_table_container table td { + padding: 0; + margin: 0; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; } \ No newline at end of file diff --git a/packages/oncoprintjs/src/js/D3SVGCellRenderer.js b/packages/oncoprintjs/src/js/D3SVGCellRenderer.js index 192517a9001..eb108708467 100644 --- a/packages/oncoprintjs/src/js/D3SVGCellRenderer.js +++ b/packages/oncoprintjs/src/js/D3SVGCellRenderer.js @@ -508,6 +508,12 @@ function D3SVGCellRenderer(data, track_config) { });*/ // mutations var mut = params.mut_key || 'mut'; + self.addStaticRule({ + condition: function(d) { + return utils.mutationType(d[mut]) + } + + }); /* self.addStaticRule({ condition: function(d) {} diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 232c7883cb2..38bc685f848 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -43,30 +43,31 @@ var utils = require('./utils'); var defaultOncoprintConfig = { cell_width: 10, cell_padding: 3, - render: 'table', }; var hiddenOncoprintConfig = { pre_track_padding: 0, }; -function Oncoprint(container_selector_string, config) { +module.exports = function CreateOncoprint(container_selector_string, config) { + var oncoprint = new Oncoprint(config); + var renderer = new OncoprintSVGRenderer(container_selector_string, oncoprint); + return oncoprint; +}; + +function Oncoprint(config) { var self = this; var track_id_counter = 0; - self.table; self.config = $.extend({}, defaultOncoprintConfig, config || {}); self.config = $.extend(self.config, hiddenOncoprintConfig); - self.config.id_order = []; - self.track_order = []; + /*self.config.id_order = []; + self.config.track_order = []; */ + self.id_order = []; + self.track_order = []; self.tracks = {}; self.ids = {}; - if (self.config.render === 'table') { - self.renderer = new OncoprintTableRenderer(container_selector_string); - } - self.renderer.bindEvents(self); - var track_events = [events.TRACK_INIT, events.TRACK_FILTER_DATA, events.UPDATE_RENDER_RULES, events.CELL_CLICK, events.CELL_MOUSEENTER, events.CELL_MOUSELEAVE, signals.REQUEST_PRE_TRACK_PADDING]; @@ -81,50 +82,55 @@ function Oncoprint(container_selector_string, config) { self.setCellWidth = function(w) { self.config.cell_width = w; $(self).trigger(events.SET_CELL_WIDTH); - triggerTracks(events.SET_CELL_WIDTH); + //triggerTracks(events.SET_CELL_WIDTH); }; self.setCellPadding = function(p) { self.config.cell_padding = p; $(self).trigger(events.SET_CELL_PADDING); - triggerTracks(events.SET_CELL_PADDING); + //triggerTracks(events.SET_CELL_PADDING); }; self.sortOnTrack = function(track_id, data_cmp) { - var track_order = self.tracks[track_id].getDatumIds(data_cmp); - self.config.id_order = track_order.concat(_.difference(self.config.id_order, track_order)); + /*var track_id_order = self.tracks[track_id].getDatumIds(data_cmp); + self.config.id_order = track_id_order.concat(_.difference(self.config.id_order, track_id_order)); $(self).trigger(events.SORT, {id_order: self.config.id_order}); - triggerTracks(events.SORT, {id_order: self.config.id_order}); + triggerTracks(events.SORT, {id_order: self.config.id_order});*/ + throw "not implemented"; }; self.sortOnTracks = function(track_ids, data_cmps) { - self.config.id_order; + throw "not implemented"; }; self.moveTrack = function(track_id, new_position) { - new_position = Math.min(self.track_order.length-1, new_position); + new_position = Math.min(self.config.track_order.length-1, new_position); new_position = Math.max(0, new_position); - var old_position = self.track_order.indexOf(track_id); + var old_position = self.config.track_order.indexOf(track_id); - self.track_order.splice(old_position, 1); - self.track_order.splice(new_position, 0, track_id); + self.config.track_order.splice(old_position, 1); + self.config.track_order.splice(new_position, 0, track_id); - $(self).trigger(events.MOVE_TRACK, {track_id: track_id, tracks:self.tracks, track_order: self.track_order}); + $(self).trigger(events.MOVE_TRACK, {track_id: track_id, tracks:self.tracks, track_order: self.config.track_order}); }; self.addTrack = function(data, config) { var track_id = track_id_counter; track_id_counter += 1; - self.tracks[track_id] = new Track(data, config, new ReadOnlyObject(self.config)); - _.each(track_events, function(evt) { + self.tracks[track_id] ={id: track_id, data: data, config: config}; //new Track(data, config, new ReadOnlyObject(self.config)); + /*_.each(track_events, function(evt) { $(self.tracks[track_id]).on(evt, function(e,data) { $(self).trigger(evt, data); }); - }); + });*/ + //self.config.track_order.push(track_id); self.track_order.push(track_id); // TODO: maybe this line shouldn't exist if we're not handling no data in oncoprint - self.config.id_order = self.config.id_order.concat(_.difference(self.tracks[track_id].getDatumIds(), self.config.id_order)); + //self.config.id_order = self.config.id_order.concat(_.difference(self.tracks[track_id].getDatumIds(), self.config.id_order)); + self.id_order = self.id_order.concat(_.difference(self.id_order, _.map(data, function(d) { + return d[config.datum_id_key]; + }))); $(self).trigger(events.ADD_TRACK, {track: self.tracks[track_id]}); return track_id; @@ -138,8 +144,8 @@ function Oncoprint(container_selector_string, config) { var track = self.tracks[track_id]; delete self.tracks[track_id]; - var oldPosition = self.track_order.indexOf(track_id); - self.track_order.splice(oldPosition, 1); + var oldPosition = self.config.track_order.indexOf(track_id); + self.config.track_order.splice(oldPosition, 1); $(self).trigger(events.REMOVE_TRACK, {track: track, track_id: track_id}); return true; @@ -153,31 +159,55 @@ function Oncoprint(container_selector_string, config) { })(self); } -function OncoprintLegendRenderer(container_selector_string) { +function OncoprintSVGRenderer(container_selector_string, oncoprint) { var self = this; - self.container = d3.select(container_selector_string).classed('oncoprint_legend_container', true); - self.table; - self.$table; + self.container = d3.select(container_selector_string).classed('oncoprint_container', true); + self.fixed_svg; + self.$fixed_svg; + self.scrolling_svg; + self.$scrolling_svg; + + var track_configs = {}; + var trackY = function(track_id) { + var y = 0; + _.find(oncoprint.config.track_order, function(id) { + if (id === track_id) { + return true; + } else { + y += trackHeight(id); + return false; + } + }); + return y; + }; - var rows = []; + var trackHeight = function(track_id) { + var track_config = oncoprint.tracks[track_id].config; + return track_config.track_height + 2*track_config.track_padding; + } - (function initTable(self) { + (function init(self) { self.container.selectAll('*').remove(); - self.table = self.container.append('table'); - self.$table = $(self.table.node()); + self.fixed_svg = self.container.append('div').classed('fixed_oncoprint_section_container', true).append('svg'); + self.scrolling_svg = self.container.append('div').classed('scrolling_oncoprint_section_container', true).append('svg'); + self.$fixed_svg = $(self.fixed_svg.node()); + self.$scrolling_svg = $(self.scrolling_svg.node()); })(self); - var addRow = function(track_id) { - - } - self.bindEvents = function(oncoprint) { - $(oncoprint).on(events.UPDATE_RENDER_RULES, function(e, data) { + $(oncoprint).on(events.ADD_TRACK, function(e, data) { + data.track.renderer.init() + }); + $(oncoprint).on(events.MOVE_TRACK, function(e, data)) { + + }); + $(oncoprint).on(events.REMOVE_TRACK, function(e, data)) { }); }; } + function OncoprintTableRenderer(container_selector_string) { var self = this; self.container = d3.select(container_selector_string).classed('oncoprint_container', true); @@ -243,4 +273,27 @@ function OncoprintTableRenderer(container_selector_string) { }; } -module.exports = Oncoprint; +function OncoprintLegendRenderer(container_selector_string) { + var self = this; + self.container = d3.select(container_selector_string).classed('oncoprint_legend_container', true); + self.table; + self.$table; + + var rows = []; + + (function initTable(self) { + self.container.selectAll('*').remove(); + self.table = self.container.append('table'); + self.$table = $(self.table.node()); + })(self); + + var addRow = function(track_id) { + + } + + self.bindEvents = function(oncoprint) { + $(oncoprint).on(events.UPDATE_RENDER_RULES, function(e, data) { + + }); + }; +} diff --git a/packages/oncoprintjs/src/js/track.js b/packages/oncoprintjs/src/js/track.js index 34b6b74e7b8..b04c9f25222 100644 --- a/packages/oncoprintjs/src/js/track.js +++ b/packages/oncoprintjs/src/js/track.js @@ -42,7 +42,7 @@ var defaultTrackConfig = { datum_id: function(d) { return d['sample'];}, cell_height: 20, track_height: 20, - track_padding: 20, + track_padding: 5, sort_cmp: undefined }; @@ -59,11 +59,9 @@ function Track(data, config, oncoprint_config) { return acc; }, {}); - if (self.oncoprint_config.get('render') === 'table') { - cell_renderer = new D3SVGCellRenderer(self.data, self.oncoprint_config.extend(self.config)); - cell_renderer.bindEvents(self); - self.renderer = new TrackTableRenderer(self.oncoprint_config.extend(self.config), cell_renderer); - } + cell_renderer = new D3SVGCellRenderer(self.data, self.oncoprint_config.extend(self.config)); + cell_renderer.bindEvents(self); + self.renderer = new TrackSVGRenderer(self.oncoprint_config.extend(self.config), cell_renderer); self.renderer.bindEvents(self); (function bindEvents() { @@ -112,6 +110,7 @@ function Track(data, config, oncoprint_config) { $(self).trigger(events.TRACK_INIT, {label_text: self.getLabel()}); } +function TrackSVGRenderer() function TrackTableRenderer(track_config, cell_renderer) { // coupled with OncoprintTableRenderer @@ -146,11 +145,12 @@ function TrackTableRenderer(track_config, cell_renderer) { self.$fixed_row = $(self.fixed_row.node()); self.scrolling_row = scrolling_row; self.$scrolling_row = $(self.scrolling_row.node()); + self.scrolling_row.attr('height', track_config.get('track_height')); - self.label_area = self.fixed_row.append('td').classed('track_label', true); - self.between_area = self.fixed_row.append('td').classed('track_between', true).style('position', 'relative'); + self.label_area = self.fixed_row.append('td').classed('track_label', true).attr('height', track_config.get('track_height')+5); + self.between_area = self.fixed_row.append('td').classed('track_between', true).style('position', 'relative').attr('height', track_config.get('track_height')+5); self.between_area.append('p').style('display', 'inline').text('yoyoyo'); - self.cell_area = self.scrolling_row.append('td').classed('track_cells', true); + self.cell_area = self.scrolling_row.append('td').classed('track_cells', true).attr('height', track_config.get('track_height')+5); renderLabel(self.label_area); initCells(self.cell_area) }; diff --git a/packages/oncoprintjs/src/js/utils.js b/packages/oncoprintjs/src/js/utils.js index 2be48dc694a..01da935d148 100644 --- a/packages/oncoprintjs/src/js/utils.js +++ b/packages/oncoprintjs/src/js/utils.js @@ -70,11 +70,7 @@ exports.mutationType = function(mutation) { 'SPLICE_SITE': /^[A-Z]([0-9]+)_splice$/g, 'MISSENSE': /^[A-z]([0-9]+)[A-z]$/g } - if (nonsense_regex.test(mutation)) { - - } else if (frame_shift_del_regex.test(mutation)) { - - } else if (frame_shift_ins) + return ''; }; exports.d3SelectChildren = function(parent, selector) { diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index 920298bc232..e2ba4a1f90e 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -5,7 +5,8 @@ var Oncoprint = require('../../src/js/Oncoprint'); var cell_padding = 3; var whitespace_on = true; -var onc = new Oncoprint('#onc', {cell_padding: cell_padding}); +var onc = CreateOncoprint('#onc', {cell_padding: cell_padding}); + $('#shuffle_btn').click(function() { onc.sortOnTrack(gender_track_id, function(d1, d2) { var map = {'MALE':0, 'FEMALE':1}; @@ -51,12 +52,14 @@ mutation_data_promise.then(function(data) { mutation_data = data.data; }); $.when(mutation_data_promise).then(function() { + for (var i=0; i<10; i++) { mutation_track_id = onc.addTrack(mutation_data, {label: 'Mutations'}); onc.getTrack(mutation_track_id).useRenderTemplate('continuous_color', { data_key: 'attr_val', data_range: [0,100], color_range: ['#A9A9A9', '#FF0000'] }); +} }); alteration_data_promise.then(function(data) { From 349f2d3069a779b7303f4385d74674d82df28b6f Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Thu, 11 Jun 2015 12:35:18 -0400 Subject: [PATCH 052/343] stashing changes for demo --- packages/oncoprintjs/src/js/oncoprint.js | 204 ++++++++++++++--------- packages/oncoprintjs/src/js/track.js | 8 - 2 files changed, 125 insertions(+), 87 deletions(-) diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 38bc685f848..1d17d612aa0 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -49,10 +49,23 @@ var hiddenOncoprintConfig = { pre_track_padding: 0, }; +var defaultTrackConfig = { + label: 'Gene', + datum_id_key: 'sample', + cell_height: 20, + track_height: 20, + track_padding: 5, + sort_cmp: undefined +}; + module.exports = function CreateOncoprint(container_selector_string, config) { var oncoprint = new Oncoprint(config); var renderer = new OncoprintSVGRenderer(container_selector_string, oncoprint); - return oncoprint; + // TODO deal with rulesets + throw "not implemented"; + return { + method_to_expose: oncoprint.method + }; }; function Oncoprint(config) { @@ -61,48 +74,54 @@ function Oncoprint(config) { self.config = $.extend({}, defaultOncoprintConfig, config || {}); self.config = $.extend(self.config, hiddenOncoprintConfig); - /*self.config.id_order = []; - self.config.track_order = []; */ self.id_order = []; self.track_order = []; self.tracks = {}; self.ids = {}; - var track_events = [events.TRACK_INIT, events.TRACK_FILTER_DATA, events.UPDATE_RENDER_RULES, - events.CELL_CLICK, events.CELL_MOUSEENTER, events.CELL_MOUSELEAVE, - signals.REQUEST_PRE_TRACK_PADDING]; - - - var triggerTracks = function(evt, data) { - _.each(self.tracks, function(track) { - $(track).trigger(evt, data); - }); + self.getCellWidth = function() { + return self.config.cell_width; }; - - self.setCellWidth = function(w) { - self.config.cell_width = w; - $(self).trigger(events.SET_CELL_WIDTH); - //triggerTracks(events.SET_CELL_WIDTH); + self.getCellPadding = function() { + return self.config.cell_padding; }; - - self.setCellPadding = function(p) { - self.config.cell_padding = p; - $(self).trigger(events.SET_CELL_PADDING); - //triggerTracks(events.SET_CELL_PADDING); + self.getTrackHeight = function(track_id) { + return self.tracks[track_id].config.track_height; }; - - self.sortOnTrack = function(track_id, data_cmp) { - /*var track_id_order = self.tracks[track_id].getDatumIds(data_cmp); - self.config.id_order = track_id_order.concat(_.difference(self.config.id_order, track_id_order)); - $(self).trigger(events.SORT, {id_order: self.config.id_order}); - triggerTracks(events.SORT, {id_order: self.config.id_order});*/ - throw "not implemented"; + self.getTrackPadding = function(track_id) { + return self.tracks[track_id].config.track_padding; }; - - self.sortOnTracks = function(track_ids, data_cmps) { - throw "not implemented"; + self.getIdOrder = function() { + return self.id_order; }; + self.getTrackOrder = function() { + return self.track_order; + }; + self.getTrackLabel = function(track_id) { + return self.tracks[track_id].config.label; + }; + self.getTrackData = function(track_id) { + return self.tracks[track_id].data; + }; + self.getTrackDatumIdAccessor = function(track_id) { + return function(d) { + return d[self.tracks[track_id].config.datum_id_key]; + }; + }; + self.getTrackDatumIdKey = function(track_id) { + return self.tracks[track_id].config.datum_id_key; + }; + + self.removeTrack = function(track_id) { + var track = self.tracks[track_id]; + delete self.tracks[track_id]; + var oldPosition = self.config.track_order.indexOf(track_id); + self.config.track_order.splice(oldPosition, 1); + + $(self).trigger(events.REMOVE_TRACK, {track: track, track_id: track_id}); + return true; + }; self.moveTrack = function(track_id, new_position) { new_position = Math.min(self.config.track_order.length-1, new_position); new_position = Math.max(0, new_position); @@ -113,85 +132,108 @@ function Oncoprint(config) { $(self).trigger(events.MOVE_TRACK, {track_id: track_id, tracks:self.tracks, track_order: self.config.track_order}); }; - self.addTrack = function(data, config) { var track_id = track_id_counter; track_id_counter += 1; - self.tracks[track_id] ={id: track_id, data: data, config: config}; //new Track(data, config, new ReadOnlyObject(self.config)); - /*_.each(track_events, function(evt) { - $(self.tracks[track_id]).on(evt, function(e,data) { - $(self).trigger(evt, data); - }); - });*/ - //self.config.track_order.push(track_id); + self.tracks[track_id] ={id: track_id, data: data, config: $.extend({}, defaultTrackConfig, config)}; self.track_order.push(track_id); + self.id_order = self.id_order.concat(_.difference(self.id_order, _.map(data, self.getTrackDatumIdAccessor(track_id)))); - // TODO: maybe this line shouldn't exist if we're not handling no data in oncoprint - //self.config.id_order = self.config.id_order.concat(_.difference(self.tracks[track_id].getDatumIds(), self.config.id_order)); - self.id_order = self.id_order.concat(_.difference(self.id_order, _.map(data, function(d) { - return d[config.datum_id_key]; - }))); - - $(self).trigger(events.ADD_TRACK, {track: self.tracks[track_id]}); + $(self).trigger(events.ADD_TRACK, {track: track_id}); return track_id; }; - self.getTrack = function(track_id) { - return self.tracks[track_id]; + self.setCellWidth = function(w) { + self.config.cell_width = w; + $(self).trigger(events.SET_CELL_WIDTH); }; - - self.removeTrack = function(track_id) { - var track = self.tracks[track_id]; - delete self.tracks[track_id]; - - var oldPosition = self.config.track_order.indexOf(track_id); - self.config.track_order.splice(oldPosition, 1); - - $(self).trigger(events.REMOVE_TRACK, {track: track, track_id: track_id}); - return true; + self.setCellPadding = function(p) { + self.config.cell_padding = p; + $(self).trigger(events.SET_CELL_PADDING); }; - (function bindEvents(self) { - $(self).on(signals.REQUEST_PRE_TRACK_PADDING, function(e, data) { - self.config.pre_track_padding = Math.max(data.pre_track_padding, self.config.pre_track_padding); - $(self).trigger(events.SET_PRE_TRACK_PADDING, {pre_track_padding: self.config.pre_track_padding}); - }); - })(self); + self.sortOnTrack = function(track_id, data_cmp) { + throw "not implemented"; + }; + self.sortOnTracks = function(track_ids, data_cmps) { + throw "not implemented"; + }; } function OncoprintSVGRenderer(container_selector_string, oncoprint) { var self = this; self.container = d3.select(container_selector_string).classed('oncoprint_container', true); - self.fixed_svg; - self.$fixed_svg; - self.scrolling_svg; - self.$scrolling_svg; + self.label_svg; + self.$label_svg; + self.cell_svg; + self.$cell_svg; var track_configs = {}; var trackY = function(track_id) { var y = 0; - _.find(oncoprint.config.track_order, function(id) { + _.find(oncoprint.getTrackOrder(), function(id) { if (id === track_id) { return true; } else { - y += trackHeight(id); + y += renderedTrackHeight(id); return false; } }); return y; }; - var trackHeight = function(track_id) { - var track_config = oncoprint.tracks[track_id].config; - return track_config.track_height + 2*track_config.track_padding; - } + var renderedTrackHeight = function(track_id) { + return oncoprint.getTrackHeight(track_id) + 2*oncoprint.getTrackPadding(track_id); + }; + + var cellSvgWidth = function() { + return (oncoprint.getCellWidth() + oncoprint.getCellPadding())*oncoprint.getIdOrder().length; + }; + + var cellSvgHeight = function() { + return _.reduce(oncoprint.getTrackOrder(), function(memo, track_id) { + return memo + renderedTrackHeight(track_id); + }, 0); + }; + + var renderCells = function(track_id, rule_set) { + var data = oncoprint.getTrackData(track_id); + var id_accessor = oncoprint.getTrackDatumIdAccessor(track_id); + var track_y = trackY(track_id); + var id_order = utils.invert_array(oncoprint.getIdOrder()); + + (function updateSVG() { + self.cell_svg + .attr('width', cellSvgWidth()) + .attr('height', cellSvgHeight()); + })(); + var bound_g = (function createAndRemoveGroups() { + var cell_class = '.cell'+track_id; + + var bound_g = self.cell_svg.selectAll('g'+cell_class).data(data, id_accessor); + bound_g.enter().append('g').classed(cell_class, true); + bound_g.exit().remove(); + return bound_cells; + })(); + (function positionGroups() { + bound_g.attr('transform', function(d, i) { + return utils.translate(id_order[id_accessor(d)]*(oncoprint.getCellWidth() + oncoprint.getCellPadding()), track_y); + }); + })(); + (function cleanGroups() { + bound_g.selectAll('*').remove(); + })(); + (function renderCells() { + rule_set.apply(bound_g, data, id_accessor); + })(); + }; (function init(self) { self.container.selectAll('*').remove(); - self.fixed_svg = self.container.append('div').classed('fixed_oncoprint_section_container', true).append('svg'); - self.scrolling_svg = self.container.append('div').classed('scrolling_oncoprint_section_container', true).append('svg'); - self.$fixed_svg = $(self.fixed_svg.node()); - self.$scrolling_svg = $(self.scrolling_svg.node()); + self.label_svg = self.container.append('div').classed('fixed_oncoprint_section_container', true).append('svg'); + self.cell_svg = self.container.append('div').classed('scrolling_oncoprint_section_container', true).append('svg'); + self.$label_svg = $(self.label_svg.node()); + self.$cell_svg = $(self.cell_svg.node()); })(self); self.bindEvents = function(oncoprint) { @@ -205,6 +247,10 @@ function OncoprintSVGRenderer(container_selector_string, oncoprint) { }); }; + + self.renderCells = function(track_id, rule_set) { + + }; } diff --git a/packages/oncoprintjs/src/js/track.js b/packages/oncoprintjs/src/js/track.js index b04c9f25222..85a081f1879 100644 --- a/packages/oncoprintjs/src/js/track.js +++ b/packages/oncoprintjs/src/js/track.js @@ -54,10 +54,6 @@ function Track(data, config, oncoprint_config) { self.data = data; self.filtered_data = data; var cell_renderer; - var data_map = _.reduce(data, function(acc, next) { - acc[self.config.datum_id(next)] = next; - return acc; - }, {}); cell_renderer = new D3SVGCellRenderer(self.data, self.oncoprint_config.extend(self.config)); cell_renderer.bindEvents(self); @@ -93,9 +89,6 @@ function Track(data, config, oncoprint_config) { return utils.stableSort(self.data, sort_cmp); }; - self.getDatum = function(datum_id) { - return data_map[datum_id]; - }; self.useRenderTemplate = function(templName, params) { self.renderer.useTemplate(templName, params); @@ -110,7 +103,6 @@ function Track(data, config, oncoprint_config) { $(self).trigger(events.TRACK_INIT, {label_text: self.getLabel()}); } -function TrackSVGRenderer() function TrackTableRenderer(track_config, cell_renderer) { // coupled with OncoprintTableRenderer From ef352908f04559d68d0352e07203becbc5f9d360 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Thu, 11 Jun 2015 19:06:52 -0400 Subject: [PATCH 053/343] Refactor; removing Track class and changing way rulesets are used Changing the code to make RuleSets unstateful, so you render using a ruleset. All the rendering now takes place in the oncoprint renderer, no unnecessary delegation to a track renderer. Furthermore, realizing that the track doesn't actually do any computation, tracks are now simple objects that belong to the oncoprint and are handled by the oncoprint, there is no need to break them into a different class. This new paradigm is in progress, with gender track correctly rendering. Old code has not yet been excised but it will be soon. --- packages/oncoprintjs/src/js/RuleSet.js | 263 ++++++++++++++++++++++ packages/oncoprintjs/src/js/oncoprint.js | 145 ++++++++---- packages/oncoprintjs/test/js/test_page.js | 28 ++- 3 files changed, 380 insertions(+), 56 deletions(-) create mode 100644 packages/oncoprintjs/src/js/RuleSet.js diff --git a/packages/oncoprintjs/src/js/RuleSet.js b/packages/oncoprintjs/src/js/RuleSet.js new file mode 100644 index 00000000000..5d5f30c4cf9 --- /dev/null +++ b/packages/oncoprintjs/src/js/RuleSet.js @@ -0,0 +1,263 @@ +var _ = require('underscore'); +var utils = require('./utils'); + +var CATEGORICAL_COLOR = 0; +var GRADIENT_COLOR = 1; +var GENETIC_ALTERATION = 2; +module.exports = { + CATEGORICAL_COLOR: CATEGORICAL_COLOR, + GRADIENT_COLOR: GRADIENT_COLOR, + GENETIC_ALTERATION: GENETIC_ALTERATION, + makeRuleSet: function(type, params) { + if (type === CATEGORICAL_COLOR) { + return new D3SVGCategoricalColorRuleSet(params); + } else if (type === GRADIENT_COLOR) { + return new D3SVGGradientColorRuleSet(params); + } else if (type === GENETIC_ALTERATION) { + return new D3SVGGeneticAlterationRuleSet(params); + } else { + return new D3SVGRuleSet(); + } + } +}; + +function D3SVGRuleSet() { + var self = this; + var rule_counter = 0; + var getRuleId = function() { + rule_counter += 1; + return rule_counter; + }; + this.rule_map = {}; + + this.addRule = function(params) { + var rule_id = getRuleId(); + this.rule_map[rule_id] = new D3SVGRule(params, rule_id); + return rule_id; + } + this.addStaticRule = function(params) { + var rule_id = getRuleId(); + this.rule_map[rule_id] = new D3SVGStaticRule(params, rule_id); + return rule_id; + }; + this.addGradientRule = function(params) { + var rule_id = getRuleId(); + this.rule_map[rule_id] = new D3SVGGradientRule(params, rule_id); + return rule_id; + }; + this.removeRule = function(rule_id) { + delete this.rule_map[rule_id]; + }; + this.apply = function(g, data, datum_id_accessor, cell_width, cell_height) { + var rule_ids = Object.keys(this.rule_map); + var rules = _.map(rule_ids, function(id) { return self.rule_map[id]; }); + var sorted_rules = _.sortBy(rules, function(r) { return r.z_index; }).reverse(); + _.each(sorted_rules, function(rule) { + var affected_data = rule.filterData(data); + var affected_groups = g.data(affected_data, datum_id_accessor); + rule.apply(affected_groups, cell_width, cell_height); + }); + }; +} + +function D3SVGCategoricalColorRuleSet(params) { + D3SVGRuleSet.call(this, params); + var self = this; + _.each(params.color, function(color, category) { + var colored_rect = utils.makeD3SVGElement('rect').attr('fill', color); + var condition = (function(cat) { + return function(d) { + return params.getCategory(d) === cat; + }; + })(category); + self.addStaticRule({ + condition: condition, + shape: colored_rect, + legend_label: category + }); + }); +} +D3SVGCategoricalColorRuleSet.prototype = Object.create(D3SVGRuleSet.prototype); + +function D3SVGGradientColorRuleSet(params) { + D3SVGRuleSet.call(this, params); + this.addGradientRule({ + shape: utils.makeD3SVGElement('rect'), + data_key: params.data_key, + data_range: params.data_range, + color_range: params.color_range, + scale: params.scale + }); +} +D3SVGGradientColorRuleSet.prototype = Object.create(D3SVGRuleSet.prototype); + +function D3SVGGeneticAlterationRuleSet(params) { + D3SVGRuleSet.call(this, params); + var rect = utils.makeD3SVGElement('rect'); + var self = this; + var default_rule = this.addStaticRule({ + shape: rect, + exclude_from_legend: true, + attrs: { + fill: params.default_color, + width: '100%', + height: '100%' + }, + z_index: -1 + }); + _.each(params.cna.color, function(color, name) { + var new_cna_rule = self.addStaticRule({ + condition: (function(_name) { + return function(d) { + return d[params.cna_key] === _name; + }; + })(name), + shape: rect, + legend_label: params.cna.label[name], + attrs: { + fill: color, + width: '100%', + height: '100%' + }, + z_index: 0 + }); + }); + _.each(params.mut.color, function(color, name) { + var new_mut_rule = self.addStaticRule({ + condition: (function(_name) { + return function(d) { + return d[params.mut_key] === _name; // TODO: should be indexOf for multiple mutations? + } + })(name), + shape: rect, + legend_label: params.mut.label[name], + attrs: { + fill: color, + width: '100%', + height: '33.33%', + y: '33.33%' + }, + z_index: 1 + }); + }); + // TODO: mrna, rppa, other stuff? +} + +function D3SVGRule(params, rule_id) { + this.rule_id = rule_id; + this.condition = params.condition || function(d) { return true; }; + this.shape = params.shape || utils.makeD3SVGElement('rect'); + this.z_index = params.z_index || this.rule_id; + this.legend_label = params.legend_label; + this.exclude_from_legend = params.exclude_from_legend; + + this.attrs = params.attrs || {}; + this.attrs.width = this.attrs.width || '100%'; + this.attrs.height = this.attrs.height || '100%'; + + var percentToPx = function(attr_val, attr_name, cell_width, cell_height) { + // convert a percentage to a local pixel coordinate + var width_like = ['width', 'x']; + var height_like = ['height', 'y']; + attr_val = parseFloat(attr_val, 10)/100; + if (width_like.indexOf(attr_name) > -1) { + attr_val = attr_val*cell_width; + } else if (height_like.indexOf(attr_name) > -1) { + attr_val = attr_val*cell_height; + } + return attr_val+''; + }; + + this.apply = function(g, cell_width, cell_height) { + var shape = this.shape; + var elts = utils.appendD3SVGElement(shape, g); + var attrs = this.attrs || {}; + attrs.width = attrs.width || '100%'; + attrs.height = attrs.height || '100%'; + _.each(attrs, function(val, key) { + elts.attr(key, function(d,i) { + var curr_val = val; + if (typeof curr_val === 'function') { + curr_val = curr_val(d,i); + } + if (typeof curr_val === 'string' && curr_val.indexOf('%') > -1) { + curr_val = percentToPx(curr_val, key, cell_width, cell_height); + } + return curr_val; + }); + }); + } + this.filterData = function(data) { + return data.filter(this.condition); + }; +} + +function D3SVGGradientRule(params, rule_id) { + D3SVGRule.call(this, params, rule_id); + this.data_key = params.data_key; + this.data_range = params.data_range; + this.color_range = params.color_range; + + var scale = function(x) { + if (params.scale === 'log') { + return Math.log10(x); + } else { + return x; + } + }; + + this.scaled_data_range = _.map(this.data_range, scale); + + this.attrs.fill = function(d) { + var datum = d[this.data_key]; + var data_range = [this.scaled_data_range[0], this.scaled_data_range[1]] + var distance = (datum-this.scaled_data_range[0]) / (this.scaled_data_range[1]-this.scaled_data_range[0]); + color_range = [d3.rgb(this.color_range[0]).toString(), + d3.rgb(this.color_range[1]).toString()]; + return utils.lin_interp(distance, this.color_range[0], this.color_range[1]); + }; + + this.getLegendGroup = function() { + var group = utils.makeD3SVGElement('g'); + var gradient_id = 'gradient'+self.rule_id; + + var gradient = group.append('svg:defs').append('svg:linearGradient') + .attr('id', gradient_id) + .attr('x1', '0%').attr('y1', '0%') + .attr('x2', '100%').attr('y2', '0%') + .attr('spreadMethod', 'pad'); + gradient.append('svg:stop') + .attr('offset', '0%') + .attr('stop-color', this.color_range[0]) + .attr('stop-opacity', 1); + gradient.append('svg:stop') + .attr('offset', '100%') + .attr('stop-color', this.color_range[1]) + .attr('stop-opacity', 1); + + group.append('text').text(this.data_range[0]).attr('alignment-baseline', 'hanging'); + group.append('rect') + .attr('width', '100px').attr('height', '20px') + .style('fill', 'url(#'+gradient_id+')'); + group.append('text').text(this.data_range[1]).attr('alignment-baseline', 'hanging'); + + return group; + }; +} +D3SVGGradientRule.prototype = Object.create(D3SVGRule.prototype); + +function D3SVGStaticRule(params, rule_id) { + D3SVGRule.call(this, params, rule_id); + + this.getLegendGroup = function(cell_width, cell_height) { + var group = utils.makeD3SVGElement('g'); + if (this.legend_label) { + group.append('text').text(this.legend_label) + .attr('alignment-baseline', 'hanging'); + } + var g = group.append('g'); + this.apply(g, cell_width, cell_height); + return group; + }; +} +D3SVGStaticRule.prototype = Object.create(D3SVGRule.prototype); \ No newline at end of file diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 1d17d612aa0..d25c60eeb98 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -37,6 +37,7 @@ var events = require('./events'); var signals = require('./signals'); var globals = require('./globals'); var utils = require('./utils'); +var RuleSet = require('./RuleSet'); // TODO: use self everywhere @@ -61,10 +62,35 @@ var defaultTrackConfig = { module.exports = function CreateOncoprint(container_selector_string, config) { var oncoprint = new Oncoprint(config); var renderer = new OncoprintSVGRenderer(container_selector_string, oncoprint); - // TODO deal with rulesets - throw "not implemented"; return { - method_to_expose: oncoprint.method + CATEGORICAL_COLOR: RuleSet.CATEGORICAL_COLOR, + GRADIENT_COLOR: RuleSet.GRADIENT_COLOR, + GENETIC_ALTERATION: RuleSet.GENETIC_ALTERATION_TRACK, + addTrack: function(config) { + var track_id = oncoprint.addTrack(config); + return track_id; + }, + removeTrack: function(track_id) { + oncoprint.removeTrack(track_id); + }, + moveTrack: function(track_id, position) { + oncoprint.moveTrack(track_id, position); + }, + setTrackData: function(track_id, data) { + oncoprint.setTrackData(track_id, data); + // + renderer.renderTracks(); + // + }, + setRuleSet: function(track_id, type, params) { + renderer.setRuleSet(track_id, type, params); + // + renderer.renderTracks(); + // + }, + useSameRuleSet: function(target_track_id, source_track_id) { + renderer.useSameRuleSet(target_track_id, source_track_id); + } }; }; @@ -85,6 +111,9 @@ function Oncoprint(config) { self.getCellPadding = function() { return self.config.cell_padding; }; + self.getCellHeight = function(track_id) { + return self.tracks[track_id].config.cell_height; + }; self.getTrackHeight = function(track_id) { return self.tracks[track_id].config.track_height; }; @@ -103,6 +132,11 @@ function Oncoprint(config) { self.getTrackData = function(track_id) { return self.tracks[track_id].data; }; + self.setTrackData = function(track_id, data) { + self.tracks[track_id].data = data; + self.id_order = self.id_order.concat(_.difference(_.map(data, self.getTrackDatumIdAccessor(track_id)), self.id_order)); + }; + self.getTrackDatumIdAccessor = function(track_id) { return function(d) { return d[self.tracks[track_id].config.datum_id_key]; @@ -132,12 +166,11 @@ function Oncoprint(config) { $(self).trigger(events.MOVE_TRACK, {track_id: track_id, tracks:self.tracks, track_order: self.config.track_order}); }; - self.addTrack = function(data, config) { + self.addTrack = function(config) { var track_id = track_id_counter; track_id_counter += 1; - self.tracks[track_id] ={id: track_id, data: data, config: $.extend({}, defaultTrackConfig, config)}; + self.tracks[track_id] ={id: track_id, data: [], config: $.extend({}, defaultTrackConfig, config)}; self.track_order.push(track_id); - self.id_order = self.id_order.concat(_.difference(self.id_order, _.map(data, self.getTrackDatumIdAccessor(track_id)))); $(self).trigger(events.ADD_TRACK, {track: track_id}); return track_id; @@ -167,36 +200,47 @@ function OncoprintSVGRenderer(container_selector_string, oncoprint) { self.$label_svg; self.cell_svg; self.$cell_svg; + self.rule_set_map = {}; + self.rule_sets = []; - var track_configs = {}; - var trackY = function(track_id) { - var y = 0; - _.find(oncoprint.getTrackOrder(), function(id) { - if (id === track_id) { - return true; - } else { - y += renderedTrackHeight(id); - return false; - } - }); - return y; - }; + (function init() { + self.container.selectAll('*').remove(); + self.label_svg = self.container.append('div').classed('fixed_oncoprint_section_container', true).append('svg'); + self.cell_svg = self.container.append('div').classed('scrolling_oncoprint_section_container', true).append('svg'); + self.$label_svg = $(self.label_svg.node()); + self.$cell_svg = $(self.cell_svg.node()); + })(); - var renderedTrackHeight = function(track_id) { - return oncoprint.getTrackHeight(track_id) + 2*oncoprint.getTrackPadding(track_id); + self.setRuleSet = function(track_id, type, params) { + var new_rule_set = RuleSet.makeRuleSet(type, params); + self.rule_sets.push(new_rule_set); + self.rule_set_map[track_id] = self.rule_sets.length-1; + }; + self.useSameRuleSet = function(target_track_id, source_track_id) { + self.rule_set_map[target_track_id] = self.rule_set_map[source_track_id]; }; - var cellSvgWidth = function() { - return (oncoprint.getCellWidth() + oncoprint.getCellPadding())*oncoprint.getIdOrder().length; + var getRuleSet = function(track_id) { + var rule_set_index = self.rule_set_map[track_id]; + return self.rule_sets[rule_set_index]; }; - var cellSvgHeight = function() { - return _.reduce(oncoprint.getTrackOrder(), function(memo, track_id) { - return memo + renderedTrackHeight(track_id); - }, 0); + self.renderTracks = function() { + _.each(oncoprint.getTrackOrder(), function(track_id) { + renderTrackLabel(track_id); + renderTrackCells(track_id, getRuleSet(track_id)); + }); }; - var renderCells = function(track_id, rule_set) { + var renderTrackLabel = function(track_id) { + var label_class = 'label'+track_id; + self.label_svg.selectAll('.'+label_class).remove(); + self.label_svg.append('text').classed(label_class, true).text(oncoprint.getTrackLabel(track_id)) + .attr('transform', utils.translate(0, trackY(track_id))) + .attr('alignment-baseline', 'hanging'); + + }; + var renderTrackCells = function(track_id, rule_set) { var data = oncoprint.getTrackData(track_id); var id_accessor = oncoprint.getTrackDatumIdAccessor(track_id); var track_y = trackY(track_id); @@ -208,12 +252,12 @@ function OncoprintSVGRenderer(container_selector_string, oncoprint) { .attr('height', cellSvgHeight()); })(); var bound_g = (function createAndRemoveGroups() { - var cell_class = '.cell'+track_id; + var cell_class = 'cell'+track_id; - var bound_g = self.cell_svg.selectAll('g'+cell_class).data(data, id_accessor); + var bound_g = self.cell_svg.selectAll('g.'+cell_class).data(data, id_accessor); bound_g.enter().append('g').classed(cell_class, true); bound_g.exit().remove(); - return bound_cells; + return bound_g; })(); (function positionGroups() { bound_g.attr('transform', function(d, i) { @@ -224,32 +268,35 @@ function OncoprintSVGRenderer(container_selector_string, oncoprint) { bound_g.selectAll('*').remove(); })(); (function renderCells() { - rule_set.apply(bound_g, data, id_accessor); + rule_set.apply(bound_g, data, id_accessor, oncoprint.getCellWidth(), oncoprint.getCellHeight(track_id)); })(); }; - (function init(self) { - self.container.selectAll('*').remove(); - self.label_svg = self.container.append('div').classed('fixed_oncoprint_section_container', true).append('svg'); - self.cell_svg = self.container.append('div').classed('scrolling_oncoprint_section_container', true).append('svg'); - self.$label_svg = $(self.label_svg.node()); - self.$cell_svg = $(self.cell_svg.node()); - })(self); - - self.bindEvents = function(oncoprint) { - $(oncoprint).on(events.ADD_TRACK, function(e, data) { - data.track.renderer.init() - }); - $(oncoprint).on(events.MOVE_TRACK, function(e, data)) { - + var trackY = function(track_id) { + var y = 0; + _.find(oncoprint.getTrackOrder(), function(id) { + if (id === track_id) { + return true; + } else { + y += renderedTrackHeight(id); + return false; + } }); - $(oncoprint).on(events.REMOVE_TRACK, function(e, data)) { + return y; + }; - }); + var renderedTrackHeight = function(track_id) { + return oncoprint.getTrackHeight(track_id) + 2*oncoprint.getTrackPadding(track_id); }; - self.renderCells = function(track_id, rule_set) { + var cellSvgWidth = function() { + return (oncoprint.getCellWidth() + oncoprint.getCellPadding())*oncoprint.getIdOrder().length; + }; + var cellSvgHeight = function() { + return _.reduce(oncoprint.getTrackOrder(), function(memo, track_id) { + return memo + renderedTrackHeight(track_id); + }, 0); }; } diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index e2ba4a1f90e..18e89dc36ac 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -5,7 +5,7 @@ var Oncoprint = require('../../src/js/Oncoprint'); var cell_padding = 3; var whitespace_on = true; -var onc = CreateOncoprint('#onc', {cell_padding: cell_padding}); +var onc = Oncoprint('#onc', {cell_padding: cell_padding}); $('#shuffle_btn').click(function() { onc.sortOnTrack(gender_track_id, function(d1, d2) { @@ -39,15 +39,23 @@ gender_data_promise.then(function(data) { gender_data = data.data; }); $.when(gender_data_promise).then(function() { - gender_track_id = onc.addTrack(gender_data, {label: 'Gender'}); + gender_track_id = onc.addTrack({label: 'Gender'}); + onc.setRuleSet(gender_track_id, onc.CATEGORICAL_COLOR, { + color: {MALE: '#6699FF', FEMALE: '#FF00FF'}, + getCategory: function(d) { + return d.attr_val; + } + }); + onc.setTrackData(gender_track_id, gender_data); + /*gender_track_id = onc.addTrack(gender_data, {label: 'Gender'}); onc.getTrack(gender_track_id).useRenderTemplate('categorical_color', { color: {MALE: '#6699FF', FEMALE: '#FF00FF'}, category: function(d) { return d.attr_val; } - }); + });*/ }); - +/* mutation_data_promise.then(function(data) { mutation_data = data.data; }); @@ -80,12 +88,18 @@ $.when(alteration_data_promise).then(function() { default_cell_color: '#D3D3D3' }); }); - +*/ $('#change_color_scheme').click(function() { - onc.getTrack(gender_track_id).useRenderTemplate('categorical_color', { + onc.setRuleSet(gender_track_id, onc.CATEGORICAL_COLOR, { + color: {MALE: '#000000', FEMALE: '#999999'}, + getCategory: function(d) { + return d.attr_val; + } + }); + /*onc.getTrack(gender_track_id).useRenderTemplate('categorical_color', { color: {MALE: '#0F0F0F', FEMALE: '#D6D6D6'}, category: function(d) { return d.attr_val; } - }); + });*/ }); \ No newline at end of file From b0958bd5c9a934bd638100e542239269b5e77245 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Fri, 12 Jun 2015 14:45:04 -0400 Subject: [PATCH 054/343] Some legend work and some revision of API, and percent altered Changed genetic alteration rule set api according to discussion in meeting. Refined legend system so that now rules put their legend onto an svg. Minor changes to defaults to more closely match existing cbio Minor changes to test_page: adding mutation track, adding legend proof of concept. Added percent altered functionality, which automatically triggers iff a ruleset has a function called alteredData. --- packages/oncoprintjs/src/js/RuleSet.js | 101 +++++++---- packages/oncoprintjs/src/js/oncoprint.js | 210 +++++++++------------- packages/oncoprintjs/src/js/utils.js | 12 -- packages/oncoprintjs/test/js/test_page.js | 73 ++++---- 4 files changed, 191 insertions(+), 205 deletions(-) diff --git a/packages/oncoprintjs/src/js/RuleSet.js b/packages/oncoprintjs/src/js/RuleSet.js index 5d5f30c4cf9..ab4a4f15378 100644 --- a/packages/oncoprintjs/src/js/RuleSet.js +++ b/packages/oncoprintjs/src/js/RuleSet.js @@ -48,20 +48,27 @@ function D3SVGRuleSet() { this.removeRule = function(rule_id) { delete this.rule_map[rule_id]; }; - this.apply = function(g, data, datum_id_accessor, cell_width, cell_height) { + this.getRules = function() { var rule_ids = Object.keys(this.rule_map); var rules = _.map(rule_ids, function(id) { return self.rule_map[id]; }); - var sorted_rules = _.sortBy(rules, function(r) { return r.z_index; }).reverse(); - _.each(sorted_rules, function(rule) { + var sorted_rules = _.sortBy(rules, function(r) { return r.z_index; }); + return sorted_rules; + }; + this.apply = function(g, data, datum_id_accessor, cell_width, cell_height) { + _.each(this.getRules(), function(rule) { var affected_data = rule.filterData(data); var affected_groups = g.data(affected_data, datum_id_accessor); rule.apply(affected_groups, cell_width, cell_height); }); }; + this.getRule = function(rule_id) { + return this.rule_map[rule_id]; + }; } function D3SVGCategoricalColorRuleSet(params) { D3SVGRuleSet.call(this, params); + this.type = CATEGORICAL_COLOR; var self = this; _.each(params.color, function(color, category) { var colored_rect = utils.makeD3SVGElement('rect').attr('fill', color); @@ -76,35 +83,43 @@ function D3SVGCategoricalColorRuleSet(params) { legend_label: category }); }); + + self.putLegendGroup = function(svg, cell_width, cell_height) { + var group = svg.append('g'); + _.each(self.getRules(), function(rule) { + rule.putLegendGroup(group, cell_width, cell_height); + }) + utils.spaceSVGElementsHorizontally(group, 20); + } } D3SVGCategoricalColorRuleSet.prototype = Object.create(D3SVGRuleSet.prototype); function D3SVGGradientColorRuleSet(params) { D3SVGRuleSet.call(this, params); - this.addGradientRule({ + this.type = GRADIENT_COLOR; + var rule = this.addGradientRule({ shape: utils.makeD3SVGElement('rect'), data_key: params.data_key, data_range: params.data_range, color_range: params.color_range, scale: params.scale }); + this.putLegendGroup = function(svg) { + this.rule_map[rule].putLegendGroup(svg); + }; } D3SVGGradientColorRuleSet.prototype = Object.create(D3SVGRuleSet.prototype); function D3SVGGeneticAlterationRuleSet(params) { D3SVGRuleSet.call(this, params); - var rect = utils.makeD3SVGElement('rect'); var self = this; + self.type = GENETIC_ALTERATION; var default_rule = this.addStaticRule({ - shape: rect, + shape: utils.makeD3SVGElement('rect').attr('fill', params.default_color), exclude_from_legend: true, - attrs: { - fill: params.default_color, - width: '100%', - height: '100%' - }, z_index: -1 }); + var altered_rules = []; _.each(params.cna.color, function(color, name) { var new_cna_rule = self.addStaticRule({ condition: (function(_name) { @@ -112,7 +127,7 @@ function D3SVGGeneticAlterationRuleSet(params) { return d[params.cna_key] === _name; }; })(name), - shape: rect, + shape: utils.makeD3SVGElement('rect'), legend_label: params.cna.label[name], attrs: { fill: color, @@ -121,33 +136,48 @@ function D3SVGGeneticAlterationRuleSet(params) { }, z_index: 0 }); + altered_rules.push(new_cna_rule); }); _.each(params.mut.color, function(color, name) { var new_mut_rule = self.addStaticRule({ condition: (function(_name) { return function(d) { - return d[params.mut_key] === _name; // TODO: should be indexOf for multiple mutations? + return d[params.mut_type_key] === _name; // TODO: should be indexOf for multiple mutations? } })(name), - shape: rect, + shape: utils.makeD3SVGElement('rect').attr('fill', color), legend_label: params.mut.label[name], attrs: { - fill: color, width: '100%', height: '33.33%', y: '33.33%' }, z_index: 1 }); + altered_rules.push(new_mut_rule); }); // TODO: mrna, rppa, other stuff? + self.putLegendGroup = function(svg, cell_width, cell_height) { + var group = svg.append('g'); + _.each(self.getRules(), function(rule) { + rule.putLegendGroup(group, cell_width, cell_height); + }) + utils.spaceSVGElementsHorizontally(group, 20); + }; + self.alteredData = function(data) { + var altered_data = []; + _.each(altered_rules, function(rule_id) { + altered_data = altered_data.concat(self.getRule(rule_id).filterData(data)); + }); + return _.uniq(altered_data); + }; } function D3SVGRule(params, rule_id) { this.rule_id = rule_id; this.condition = params.condition || function(d) { return true; }; - this.shape = params.shape || utils.makeD3SVGElement('rect'); - this.z_index = params.z_index || this.rule_id; + this.shape = typeof params.shape === 'undefined' ? utils.makeD3SVGElement('rect') : params.shape; + this.z_index = typeof params.z_index === 'undefined' ? this.rule_id : params.z_index; this.legend_label = params.legend_label; this.exclude_from_legend = params.exclude_from_legend; @@ -190,6 +220,9 @@ function D3SVGRule(params, rule_id) { this.filterData = function(data) { return data.filter(this.condition); }; + this.isActive = function(data) { + return this.filterData(data).length > 0; + }; } function D3SVGGradientRule(params, rule_id) { @@ -206,19 +239,22 @@ function D3SVGGradientRule(params, rule_id) { } }; - this.scaled_data_range = _.map(this.data_range, scale); + var scaled_data_range = _.map(this.data_range, scale); this.attrs.fill = function(d) { - var datum = d[this.data_key]; - var data_range = [this.scaled_data_range[0], this.scaled_data_range[1]] - var distance = (datum-this.scaled_data_range[0]) / (this.scaled_data_range[1]-this.scaled_data_range[0]); - color_range = [d3.rgb(this.color_range[0]).toString(), - d3.rgb(this.color_range[1]).toString()]; - return utils.lin_interp(distance, this.color_range[0], this.color_range[1]); + var datum = d[params.data_key]; + var data_range = [scaled_data_range[0], scaled_data_range[1]]; + var distance = (datum-scaled_data_range[0]) / (scaled_data_range[1]-scaled_data_range[0]); + color_range = [d3.rgb(params.color_range[0]).toString(), + d3.rgb(params.color_range[1]).toString()]; + return utils.lin_interp(distance, params.color_range[0], params.color_range[1]); }; - this.getLegendGroup = function() { - var group = utils.makeD3SVGElement('g'); + this.putLegendGroup = function(svg) { + if (params.exclude_from_legend) { + return; + } + var group = svg.append('g'); var gradient_id = 'gradient'+self.rule_id; var gradient = group.append('svg:defs').append('svg:linearGradient') @@ -241,6 +277,7 @@ function D3SVGGradientRule(params, rule_id) { .style('fill', 'url(#'+gradient_id+')'); group.append('text').text(this.data_range[1]).attr('alignment-baseline', 'hanging'); + utils.spaceSVGElementsHorizontally(group, 10); return group; }; } @@ -249,14 +286,18 @@ D3SVGGradientRule.prototype = Object.create(D3SVGRule.prototype); function D3SVGStaticRule(params, rule_id) { D3SVGRule.call(this, params, rule_id); - this.getLegendGroup = function(cell_width, cell_height) { - var group = utils.makeD3SVGElement('g'); + this.putLegendGroup = function(svg, cell_width, cell_height) { + if (params.exclude_from_legend) { + return; + } + var group = svg.append('g'); + var g = group.append('g'); + this.apply(g, cell_width, cell_height); if (this.legend_label) { group.append('text').text(this.legend_label) .attr('alignment-baseline', 'hanging'); } - var g = group.append('g'); - this.apply(g, cell_width, cell_height); + utils.spaceSVGElementsHorizontally(group, 10); return group; }; } diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index d25c60eeb98..cdd5f22bc80 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -27,12 +27,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ -var Track = require('./Track'); var _ = require('underscore'); var d3 = require('d3'); var $ = require('jquery'); -var ReadOnlyObject = require('./ReadOnlyObject'); -var Toolbar = require('./Toolbar') var events = require('./events'); var signals = require('./signals'); var globals = require('./globals'); @@ -42,7 +39,7 @@ var RuleSet = require('./RuleSet'); // TODO: use self everywhere var defaultOncoprintConfig = { - cell_width: 10, + cell_width: 5.5, cell_padding: 3, }; @@ -53,45 +50,53 @@ var hiddenOncoprintConfig = { var defaultTrackConfig = { label: 'Gene', datum_id_key: 'sample', - cell_height: 20, + cell_height: 23, track_height: 20, track_padding: 5, sort_cmp: undefined }; -module.exports = function CreateOncoprint(container_selector_string, config) { - var oncoprint = new Oncoprint(config); - var renderer = new OncoprintSVGRenderer(container_selector_string, oncoprint); - return { - CATEGORICAL_COLOR: RuleSet.CATEGORICAL_COLOR, - GRADIENT_COLOR: RuleSet.GRADIENT_COLOR, - GENETIC_ALTERATION: RuleSet.GENETIC_ALTERATION_TRACK, - addTrack: function(config) { - var track_id = oncoprint.addTrack(config); - return track_id; - }, - removeTrack: function(track_id) { - oncoprint.removeTrack(track_id); - }, - moveTrack: function(track_id, position) { - oncoprint.moveTrack(track_id, position); - }, - setTrackData: function(track_id, data) { - oncoprint.setTrackData(track_id, data); - // - renderer.renderTracks(); - // - }, - setRuleSet: function(track_id, type, params) { - renderer.setRuleSet(track_id, type, params); - // - renderer.renderTracks(); - // - }, - useSameRuleSet: function(target_track_id, source_track_id) { - renderer.useSameRuleSet(target_track_id, source_track_id); - } - }; +module.exports = { + CATEGORICAL_COLOR: RuleSet.CATEGORICAL_COLOR, + GRADIENT_COLOR: RuleSet.GRADIENT_COLOR, + GENETIC_ALTERATION: RuleSet.GENETIC_ALTERATION, + create: function CreateOncoprint(container_selector_string, config) { + var oncoprint = new Oncoprint(config); + var renderer = new OncoprintSVGRenderer(container_selector_string, oncoprint); + return { + addTrack: function(config) { + var track_id = oncoprint.addTrack(config); + return track_id; + }, + removeTrack: function(track_id) { + oncoprint.removeTrack(track_id); + }, + moveTrack: function(track_id, position) { + oncoprint.moveTrack(track_id, position); + }, + setTrackData: function(track_id, data) { + oncoprint.setTrackData(track_id, data); + // + renderer.renderTracks(); + // + }, + setRuleSet: function(track_id, type, params) { + renderer.setRuleSet(track_id, type, params); + // + renderer.renderTracks(); + // + }, + useSameRuleSet: function(target_track_id, source_track_id) { + renderer.useSameRuleSet(target_track_id, source_track_id); + }, + setCellPadding: function(p) { + oncoprint.setCellPadding(p); + // + renderer.renderTracks(); + // + } + }; + } }; function Oncoprint(config) { @@ -111,6 +116,9 @@ function Oncoprint(config) { self.getCellPadding = function() { return self.config.cell_padding; }; + self.setCellPadding = function(p) { + self.config.cell_padding = p; + }; self.getCellHeight = function(track_id) { return self.tracks[track_id].config.cell_height; }; @@ -200,15 +208,20 @@ function OncoprintSVGRenderer(container_selector_string, oncoprint) { self.$label_svg; self.cell_svg; self.$cell_svg; + self.legend_table; + self.$legend_table; self.rule_set_map = {}; self.rule_sets = []; (function init() { self.container.selectAll('*').remove(); - self.label_svg = self.container.append('div').classed('fixed_oncoprint_section_container', true).append('svg'); + self.label_svg = self.container.append('div').classed('fixed_oncoprint_section_container', true).append('svg') + .attr('width', 100); self.cell_svg = self.container.append('div').classed('scrolling_oncoprint_section_container', true).append('svg'); self.$label_svg = $(self.label_svg.node()); self.$cell_svg = $(self.cell_svg.node()); + self.legend_table = self.container.append('table'); + self.$legend_table = $(self.legend_table.node()); })(); self.setRuleSet = function(track_id, type, params) { @@ -230,15 +243,38 @@ function OncoprintSVGRenderer(container_selector_string, oncoprint) { renderTrackLabel(track_id); renderTrackCells(track_id, getRuleSet(track_id)); }); + + (function renderLegend() { + self.legend_table.selectAll('*').remove(); + _.each(self.rule_sets, function(rule_set) { + var svg = self.legend_table.append('tr').append('svg').attr('width', 1000).attr('height', 50); + rule_set.putLegendGroup(svg, oncoprint.getCellWidth(), 20); // TODO: get actual cell height + }) + })(); }; var renderTrackLabel = function(track_id) { var label_class = 'label'+track_id; + var track_y = trackY(track_id); + self.label_svg + .attr('width', labelSvgWidth()) + .attr('height', labelSvgHeight()); self.label_svg.selectAll('.'+label_class).remove(); self.label_svg.append('text').classed(label_class, true).text(oncoprint.getTrackLabel(track_id)) - .attr('transform', utils.translate(0, trackY(track_id))) + .attr('transform', utils.translate(0, track_y)) .attr('alignment-baseline', 'hanging'); + var track_rule_set = getRuleSet(track_id); + var track_data = oncoprint.getTrackData(track_id); + if (track_rule_set.alteredData) { + var percent_altered = 100*(track_rule_set.alteredData(track_data).length / track_data.length); + self.label_svg.append('text').classed(label_class, true) + .attr('text-anchor', 'end') + .text(Math.floor(percent_altered)+'%') + .attr('alignment-baseline', 'hanging') + .attr('transform', utils.translate(labelSvgWidth(), track_y)); + } + }; var renderTrackCells = function(track_id, rule_set) { var data = oncoprint.getTrackData(track_id); @@ -250,6 +286,7 @@ function OncoprintSVGRenderer(container_selector_string, oncoprint) { self.cell_svg .attr('width', cellSvgWidth()) .attr('height', cellSvgHeight()); + })(); var bound_g = (function createAndRemoveGroups() { var cell_class = 'cell'+track_id; @@ -298,95 +335,10 @@ function OncoprintSVGRenderer(container_selector_string, oncoprint) { return memo + renderedTrackHeight(track_id); }, 0); }; -} - - -function OncoprintTableRenderer(container_selector_string) { - var self = this; - self.container = d3.select(container_selector_string).classed('oncoprint_container', true); - self.fixed_table; - self.$fixed_table; - self.scrolling_table; - self.$scrolling_table; - self.legend_table; - self.$legend_table; - - (function initTable(self) { - self.container.selectAll('*').remove(); - self.fixed_table = self.container.append('div').classed('fixed_oncoprint_table_container', true).append('table') - self.$fixed_table = $(self.fixed_table.node()); - self.scrolling_table = self.container.append('div').classed('scrolling_oncoprint_table_container', true).append('table'); - self.$scrolling_table = $(self.scrolling_table.node()); - self.legend_table = self.container.append('div').classed('legend_oncoprint_table_container', true).append('table'); - self.$legend_table = $(self.legend_table.node()); - self.legend_table.append('tr').append('svg').attr('width', 2000).attr('height', 100); - })(self); - - self.bindEvents = function(oncoprint) { - $(oncoprint).on(events.ADD_TRACK, function(e, data) { - var new_fixed_table_row = self.fixed_table.append('tr'); - var new_scrolling_table_row = self.scrolling_table.append('tr'); - var new_legend_table_row = self.legend_table.append('tr'); - data.track.renderer.init(new_fixed_table_row, new_scrolling_table_row); - }); - $(oncoprint).on(events.MOVE_TRACK, function(e, data) { - var track_name = data.track_name; - var new_position = data.track_order.indexOf(track_name); - var track_order = data.track_order; - var track = data.tracks[track_name]; - if (new_position === 0) { - self.$table.find('tr:first').before(track.renderer.$row); - } else { - var before_track = data.tracks[track_order[new_position-1]]; - before_track.renderer.$row.after(track.renderer.$row); - } - }); - $(oncoprint).on(events.REMOVE_TRACK, function(e, data) { - var track = data.track; - track.renderer.$row.remove(); - }); - var legend_x = 0; - $(oncoprint).on(events.UPDATE_RENDER_RULES, function(e, data) { - var group = utils.appendD3SVGElement(globals.rulesvgs[globals.rulesvgs.length-1], self.legend_table.select('svg')); - utils.spaceSVGElementsHorizontally(group, 10); - utils.spaceSVGElementsHorizontally(self.legend_table.select('svg'), 20); - }); - self.$fixed_table.mouseenter(function() { - $(oncoprint).trigger(events.ONCOPRINT_MOUSEENTER); - }); - self.$fixed_table.mouseleave(function() { - $(oncoprint).trigger(events.ONCOPRINT_MOUSELEAVE); - }); - self.$scrolling_table.mouseenter(function() { - $(oncoprint).trigger(events.ONCOPRINT_MOUSEENTER); - }); - self.$scrolling_table.mouseleave(function() { - $(oncoprint).trigger(events.ONCOPRINT_MOUSELEAVE); - }); + var labelSvgHeight = function() { + return cellSvgHeight(); }; -} - -function OncoprintLegendRenderer(container_selector_string) { - var self = this; - self.container = d3.select(container_selector_string).classed('oncoprint_legend_container', true); - self.table; - self.$table; - - var rows = []; - - (function initTable(self) { - self.container.selectAll('*').remove(); - self.table = self.container.append('table'); - self.$table = $(self.table.node()); - })(self); - - var addRow = function(track_id) { - - } - - self.bindEvents = function(oncoprint) { - $(oncoprint).on(events.UPDATE_RENDER_RULES, function(e, data) { - - }); + var labelSvgWidth = function() { + return 100; }; } diff --git a/packages/oncoprintjs/src/js/utils.js b/packages/oncoprintjs/src/js/utils.js index 01da935d148..0f894bc75b3 100644 --- a/packages/oncoprintjs/src/js/utils.js +++ b/packages/oncoprintjs/src/js/utils.js @@ -61,18 +61,6 @@ exports.spaceSVGElementsHorizontally = function(group, padding) { return group; }; -exports.mutationType = function(mutation) { - var regex = { - 'NONSENSE': /^[A-Z]([0-9]+)[*]$/g, - 'FRAMESHIFT_DEL': /^[A-z*]([0-9]+)[A-z]{2}$/g, - 'INFRAME_DEL': /^([A-Z]+)([0-9]+)del$/g, - 'INFRAME_INS': /^([A-Z]+)([0-9]+)ins$/g, - 'SPLICE_SITE': /^[A-Z]([0-9]+)_splice$/g, - 'MISSENSE': /^[A-z]([0-9]+)[A-z]$/g - } - return ''; -}; - exports.d3SelectChildren = function(parent, selector) { return parent.selectAll(selector).filter(function() { return this.parentNode === parent.node(); diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index 18e89dc36ac..080ab40dfe9 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -5,7 +5,7 @@ var Oncoprint = require('../../src/js/Oncoprint'); var cell_padding = 3; var whitespace_on = true; -var onc = Oncoprint('#onc', {cell_padding: cell_padding}); +var onc = Oncoprint.create('#onc', {cell_padding: cell_padding}); $('#shuffle_btn').click(function() { onc.sortOnTrack(gender_track_id, function(d1, d2) { @@ -40,55 +40,66 @@ gender_data_promise.then(function(data) { }); $.when(gender_data_promise).then(function() { gender_track_id = onc.addTrack({label: 'Gender'}); - onc.setRuleSet(gender_track_id, onc.CATEGORICAL_COLOR, { + onc.setRuleSet(gender_track_id, Oncoprint.CATEGORICAL_COLOR, { color: {MALE: '#6699FF', FEMALE: '#FF00FF'}, getCategory: function(d) { return d.attr_val; } }); onc.setTrackData(gender_track_id, gender_data); - /*gender_track_id = onc.addTrack(gender_data, {label: 'Gender'}); - onc.getTrack(gender_track_id).useRenderTemplate('categorical_color', { - color: {MALE: '#6699FF', FEMALE: '#FF00FF'}, - category: function(d) { - return d.attr_val; - } - });*/ }); -/* + mutation_data_promise.then(function(data) { mutation_data = data.data; }); $.when(mutation_data_promise).then(function() { - for (var i=0; i<10; i++) { - mutation_track_id = onc.addTrack(mutation_data, {label: 'Mutations'}); - onc.getTrack(mutation_track_id).useRenderTemplate('continuous_color', { + mutation_track_id = onc.addTrack({label: 'Mutations'}); + onc.setRuleSet(mutation_track_id, Oncoprint.GRADIENT_COLOR, { data_key: 'attr_val', data_range: [0,100], - color_range: ['#A9A9A9', '#FF0000'] + color_range: ['#A9A9A9', '#FF0000'], + scale: 'log' }); -} + onc.setTrackData(mutation_track_id, mutation_data); }); + alteration_data_promise.then(function(data) { - alteration_data = data; + alteration_data = _.map(data, function(x) { if (Math.random() < 0.3) { x.mut_type='MISSENSE'; } return x; }); }); $.when(alteration_data_promise).then(function() { - alteration_track_id = onc.addTrack(alteration_data, {label: 'TP53'}); - onc.getTrack(alteration_track_id).useRenderTemplate('genetic_alteration', { + alteration_track_id = onc.addTrack({label: 'TP53'}); + onc.setRuleSet(alteration_track_id, Oncoprint.GENETIC_ALTERATION, { + default_color: '#D3D3D3', cna_key: 'cna', - cna_amp_name: 'AMPLIFIED', - cna_homdel_name: 'HOMODELETED', - cna_gain_name: 'GAINED', - cna_hetloss_name: 'HETLOSS', - cna_amp_color: '#FF0000', - cna_gain_color: '#FFB6C1', - cna_hetloss_color: '#8FD8D8', - cna_homdel_color: '#0000FF', - default_cell_color: '#D3D3D3' + cna: { + color: { + AMPLIFIED: '#FF0000', + GAINED: '#FFB6C1', + HOMODELETED: '#8FD8D8', + HETLOSS: '#8FD8D8', + }, + label: { + AMPLIFIED: 'Amplification', + GAINED: 'Gain', + HOMODELETED: 'Homozygous Deletion', + HETLOSS: 'Heterozygous Deletion' + } + + }, + mut_type_key: 'mut_type', + mut: { + color: { + MISSENSE: 'green' + }, + label: { + MISSENSE: 'Missense Mutation' + } + } }); + onc.setTrackData(alteration_track_id, alteration_data); }); -*/ + $('#change_color_scheme').click(function() { onc.setRuleSet(gender_track_id, onc.CATEGORICAL_COLOR, { color: {MALE: '#000000', FEMALE: '#999999'}, @@ -96,10 +107,4 @@ $('#change_color_scheme').click(function() { return d.attr_val; } }); - /*onc.getTrack(gender_track_id).useRenderTemplate('categorical_color', { - color: {MALE: '#0F0F0F', FEMALE: '#D6D6D6'}, - category: function(d) { - return d.attr_val; - } - });*/ }); \ No newline at end of file From f9df843bdd6d88a6997286c2455192bc223dde40 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Fri, 12 Jun 2015 17:39:06 -0400 Subject: [PATCH 055/343] Prototype methods OOP for D3SVGRuleSet and auto color selection Adding automatic color selection (from d3 colors) if colors are not specified for every category in D3SVGCategoricalColorRuleSet. Changing D3SVGRuleSet definition to use prototype because it plays better with calling super method. --- packages/oncoprintjs/src/js/RuleSet.js | 48 +++++++++++++++++------ packages/oncoprintjs/src/js/oncoprint.js | 18 ++++----- packages/oncoprintjs/test/js/test_page.js | 5 +-- 3 files changed, 46 insertions(+), 25 deletions(-) diff --git a/packages/oncoprintjs/src/js/RuleSet.js b/packages/oncoprintjs/src/js/RuleSet.js index ab4a4f15378..5e80d4780ed 100644 --- a/packages/oncoprintjs/src/js/RuleSet.js +++ b/packages/oncoprintjs/src/js/RuleSet.js @@ -21,56 +21,61 @@ module.exports = { } }; -function D3SVGRuleSet() { - var self = this; +var D3SVGRuleSet = (function() { + function D3SVGRuleSet() { + this.rule_map = {}; + }; var rule_counter = 0; var getRuleId = function() { rule_counter += 1; return rule_counter; }; - this.rule_map = {}; - - this.addRule = function(params) { + D3SVGRuleSet.prototype.addRule = function(params) { var rule_id = getRuleId(); this.rule_map[rule_id] = new D3SVGRule(params, rule_id); return rule_id; } - this.addStaticRule = function(params) { + D3SVGRuleSet.prototype.addStaticRule = function(params) { var rule_id = getRuleId(); this.rule_map[rule_id] = new D3SVGStaticRule(params, rule_id); return rule_id; }; - this.addGradientRule = function(params) { + D3SVGRuleSet.prototype.addGradientRule = function(params) { var rule_id = getRuleId(); this.rule_map[rule_id] = new D3SVGGradientRule(params, rule_id); return rule_id; }; - this.removeRule = function(rule_id) { + D3SVGRuleSet.prototype.removeRule = function(rule_id) { delete this.rule_map[rule_id]; }; - this.getRules = function() { + D3SVGRuleSet.prototype.getRules = function() { + var self = this; var rule_ids = Object.keys(this.rule_map); var rules = _.map(rule_ids, function(id) { return self.rule_map[id]; }); var sorted_rules = _.sortBy(rules, function(r) { return r.z_index; }); return sorted_rules; }; - this.apply = function(g, data, datum_id_accessor, cell_width, cell_height) { + D3SVGRuleSet.prototype.apply = function(g, data, datum_id_accessor, cell_width, cell_height) { _.each(this.getRules(), function(rule) { var affected_data = rule.filterData(data); var affected_groups = g.data(affected_data, datum_id_accessor); rule.apply(affected_groups, cell_width, cell_height); }); }; - this.getRule = function(rule_id) { + D3SVGRuleSet.prototype.getRule = function(rule_id) { return this.rule_map[rule_id]; }; -} + return D3SVGRuleSet; +})(); function D3SVGCategoricalColorRuleSet(params) { D3SVGRuleSet.call(this, params); this.type = CATEGORICAL_COLOR; var self = this; - _.each(params.color, function(color, category) { + var d3_colors = _.shuffle(d3.scale.category20().range() + .concat(d3.scale.category20b().range()) + .concat(d3.scale.category20c().range())); + var addColorRule = function(color, category) { var colored_rect = utils.makeD3SVGElement('rect').attr('fill', color); var condition = (function(cat) { return function(d) { @@ -82,8 +87,24 @@ function D3SVGCategoricalColorRuleSet(params) { shape: colored_rect, legend_label: category }); + }; + _.each(params.color, function(color, category) { + addColorRule(color, category); }); + self.apply = function(g, data, datum_id_accessor, cell_width, cell_height) { + var missing_categories = []; + _.each(data, function(datum) { + var category = params.getCategory(datum); + if (!params.color.hasOwnProperty(category)) { + var new_color = d3_colors.pop(); + params.color[category] = new_color; + addColorRule(new_color, category); + } + }); + D3SVGRuleSet.prototype.apply.call(this, g, data, datum_id_accessor, cell_width, cell_height); + }; + self.putLegendGroup = function(svg, cell_width, cell_height) { var group = svg.append('g'); _.each(self.getRules(), function(rule) { @@ -172,6 +193,7 @@ function D3SVGGeneticAlterationRuleSet(params) { return _.uniq(altered_data); }; } +D3SVGGeneticAlterationRuleSet.prototype = Object.create(D3SVGRuleSet.prototype); function D3SVGRule(params, rule_id) { this.rule_id = rule_id; diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index cdd5f22bc80..3e27b24b605 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -158,21 +158,21 @@ function Oncoprint(config) { var track = self.tracks[track_id]; delete self.tracks[track_id]; - var oldPosition = self.config.track_order.indexOf(track_id); - self.config.track_order.splice(oldPosition, 1); + var oldPosition = self.track_order.indexOf(track_id); + self.track_order.splice(oldPosition, 1); $(self).trigger(events.REMOVE_TRACK, {track: track, track_id: track_id}); return true; }; self.moveTrack = function(track_id, new_position) { - new_position = Math.min(self.config.track_order.length-1, new_position); + new_position = Math.min(self.track_order.length-1, new_position); new_position = Math.max(0, new_position); - var old_position = self.config.track_order.indexOf(track_id); + var old_position = self.track_order.indexOf(track_id); - self.config.track_order.splice(old_position, 1); - self.config.track_order.splice(new_position, 0, track_id); + self.track_order.splice(old_position, 1); + self.track_order.splice(new_position, 0, track_id); - $(self).trigger(events.MOVE_TRACK, {track_id: track_id, tracks:self.tracks, track_order: self.config.track_order}); + $(self).trigger(events.MOVE_TRACK, {track_id: track_id, tracks:self.tracks, track_order: self.track_order}); }; self.addTrack = function(config) { var track_id = track_id_counter; @@ -239,7 +239,7 @@ function OncoprintSVGRenderer(container_selector_string, oncoprint) { }; self.renderTracks = function() { - _.each(oncoprint.getTrackOrder(), function(track_id) { + _.each(oncoprint.getTrackOrder(), function(track_id, ind) { renderTrackLabel(track_id); renderTrackCells(track_id, getRuleSet(track_id)); }); @@ -297,7 +297,7 @@ function OncoprintSVGRenderer(container_selector_string, oncoprint) { return bound_g; })(); (function positionGroups() { - bound_g.attr('transform', function(d, i) { + bound_g.transition().attr('transform', function(d, i) { return utils.translate(id_order[id_accessor(d)]*(oncoprint.getCellWidth() + oncoprint.getCellPadding()), track_y); }); })(); diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index 080ab40dfe9..3b9e86a0d4d 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -41,7 +41,7 @@ gender_data_promise.then(function(data) { $.when(gender_data_promise).then(function() { gender_track_id = onc.addTrack({label: 'Gender'}); onc.setRuleSet(gender_track_id, Oncoprint.CATEGORICAL_COLOR, { - color: {MALE: '#6699FF', FEMALE: '#FF00FF'}, + color: {}, getCategory: function(d) { return d.attr_val; } @@ -57,8 +57,7 @@ $.when(mutation_data_promise).then(function() { onc.setRuleSet(mutation_track_id, Oncoprint.GRADIENT_COLOR, { data_key: 'attr_val', data_range: [0,100], - color_range: ['#A9A9A9', '#FF0000'], - scale: 'log' + color_range: ['#A9A9A9', '#FF0000'] }); onc.setTrackData(mutation_track_id, mutation_data); }); From 82a1445f990eaeeae3d0daa8d0fa9d14246472a0 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Fri, 12 Jun 2015 18:27:54 -0400 Subject: [PATCH 056/343] Log gradient scale...probably perceptually useless tbh --- packages/oncoprintjs/src/js/RuleSet.js | 68 ++++++++++++++++------- packages/oncoprintjs/src/js/utils.js | 4 ++ packages/oncoprintjs/test/js/test_page.js | 9 +++ 3 files changed, 62 insertions(+), 19 deletions(-) diff --git a/packages/oncoprintjs/src/js/RuleSet.js b/packages/oncoprintjs/src/js/RuleSet.js index 5e80d4780ed..2b34390b6e0 100644 --- a/packages/oncoprintjs/src/js/RuleSet.js +++ b/packages/oncoprintjs/src/js/RuleSet.js @@ -72,9 +72,7 @@ function D3SVGCategoricalColorRuleSet(params) { D3SVGRuleSet.call(this, params); this.type = CATEGORICAL_COLOR; var self = this; - var d3_colors = _.shuffle(d3.scale.category20().range() - .concat(d3.scale.category20b().range()) - .concat(d3.scale.category20c().range())); + var d3_colors = _.shuffle(d3.scale.category20().range()); var addColorRule = function(color, category) { var colored_rect = utils.makeD3SVGElement('rect').attr('fill', color); var condition = (function(cat) { @@ -253,32 +251,39 @@ function D3SVGGradientRule(params, rule_id) { this.data_range = params.data_range; this.color_range = params.color_range; + var getGradientId = (function() { + var gradient_counter = 0; + return function() { + gradient_counter += 1; + return 'gradient'+'_'+rule_id+'_'+gradient_counter; + } + })(); var scale = function(x) { if (params.scale === 'log') { - return Math.log10(x); + return Math.log10(Math.max(x, 0.1)); } else { return x; } }; var scaled_data_range = _.map(this.data_range, scale); - - this.attrs.fill = function(d) { - var datum = d[params.data_key]; + var fill_function = function(d) { + var datum = scale(d[params.data_key]); var data_range = [scaled_data_range[0], scaled_data_range[1]]; var distance = (datum-scaled_data_range[0]) / (scaled_data_range[1]-scaled_data_range[0]); color_range = [d3.rgb(params.color_range[0]).toString(), d3.rgb(params.color_range[1]).toString()]; return utils.lin_interp(distance, params.color_range[0], params.color_range[1]); }; + this.attrs.fill = fill_function; - this.putLegendGroup = function(svg) { - if (params.exclude_from_legend) { - return; - } - var group = svg.append('g'); - var gradient_id = 'gradient'+self.rule_id; - + var makeDatum = function(x) { + var ret = {}; + ret[params.data_key] = x; + return ret; + }; + var putLinearGradient = function(group, color_range, width, height) { + var gradient_id = getGradientId(); var gradient = group.append('svg:defs').append('svg:linearGradient') .attr('id', gradient_id) .attr('x1', '0%').attr('y1', '0%') @@ -286,17 +291,42 @@ function D3SVGGradientRule(params, rule_id) { .attr('spreadMethod', 'pad'); gradient.append('svg:stop') .attr('offset', '0%') - .attr('stop-color', this.color_range[0]) + .attr('stop-color', color_range[0]) .attr('stop-opacity', 1); gradient.append('svg:stop') .attr('offset', '100%') - .attr('stop-color', this.color_range[1]) + .attr('stop-color', color_range[1]) .attr('stop-opacity', 1); - - group.append('text').text(this.data_range[0]).attr('alignment-baseline', 'hanging'); group.append('rect') - .attr('width', '100px').attr('height', '20px') + .attr('width',width).attr('height', height) .style('fill', 'url(#'+gradient_id+')'); + }; + + var putLogGradient = function(group, color_range, width, height) { + // TODO: I think this is perceptually useless....but could still leave it I guess + var gradient_group = group.append('g'); + var t, datum; + for (var i=0; i -1 && parseFloat(transform.split(",")[1], 10); y = y || 0; diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index 3b9e86a0d4d..b3f675583af 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -60,6 +60,15 @@ $.when(mutation_data_promise).then(function() { color_range: ['#A9A9A9', '#FF0000'] }); onc.setTrackData(mutation_track_id, mutation_data); + + var log_mut_track_id = onc.addTrack({label: 'Log Mutations'}); + onc.setRuleSet(log_mut_track_id, Oncoprint.GRADIENT_COLOR, { + data_key: 'attr_val', + data_range: [0,100], + color_range: ['#A9A9A9', '#FF0000'], + scale: 'log' + }); + onc.setTrackData(log_mut_track_id, mutation_data); }); From dc22507a12ed27c1d56a8ccf062051de19115ef0 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Mon, 15 Jun 2015 17:00:23 -0400 Subject: [PATCH 057/343] save as SVG, and added bar chart, and added sorting API --- packages/oncoprintjs/src/js/RuleSet.js | 80 ++++++++++++++++++++++ packages/oncoprintjs/src/js/oncoprint.js | 81 +++++++++++++++++++++-- packages/oncoprintjs/test/index.html | 11 +-- packages/oncoprintjs/test/js/test_page.js | 9 ++- 4 files changed, 166 insertions(+), 15 deletions(-) diff --git a/packages/oncoprintjs/src/js/RuleSet.js b/packages/oncoprintjs/src/js/RuleSet.js index 2b34390b6e0..0c9c4ddf250 100644 --- a/packages/oncoprintjs/src/js/RuleSet.js +++ b/packages/oncoprintjs/src/js/RuleSet.js @@ -4,10 +4,12 @@ var utils = require('./utils'); var CATEGORICAL_COLOR = 0; var GRADIENT_COLOR = 1; var GENETIC_ALTERATION = 2; +var BAR_CHART = 3; module.exports = { CATEGORICAL_COLOR: CATEGORICAL_COLOR, GRADIENT_COLOR: GRADIENT_COLOR, GENETIC_ALTERATION: GENETIC_ALTERATION, + BAR_CHART: BAR_CHART, makeRuleSet: function(type, params) { if (type === CATEGORICAL_COLOR) { return new D3SVGCategoricalColorRuleSet(params); @@ -15,6 +17,8 @@ module.exports = { return new D3SVGGradientColorRuleSet(params); } else if (type === GENETIC_ALTERATION) { return new D3SVGGeneticAlterationRuleSet(params); + } else if (type === BAR_CHART) { + return new D3SVGBarChartRuleSet(params); } else { return new D3SVGRuleSet(); } @@ -45,6 +49,11 @@ var D3SVGRuleSet = (function() { this.rule_map[rule_id] = new D3SVGGradientRule(params, rule_id); return rule_id; }; + D3SVGRuleSet.prototype.addBarChartRule = function(params) { + var rule_id = getRuleId(); + this.rule_map[rule_id] = new D3SVGBarChartRule(params, rule_id); + return rule_id; + }; D3SVGRuleSet.prototype.removeRule = function(rule_id) { delete this.rule_map[rule_id]; }; @@ -129,6 +138,21 @@ function D3SVGGradientColorRuleSet(params) { } D3SVGGradientColorRuleSet.prototype = Object.create(D3SVGRuleSet.prototype); +function D3SVGBarChartRuleSet(params) { + D3SVGRuleSet.call(this, params); + var self = this; + self.type = BAR_CHART; + var rule = this.addBarChartRule({ + data_key: params.data_key, + data_range: params.data_range, + scale: params.scale + }); + this.putLegendGroup = function(svg, cell_width, cell_height) { + this.rule_map[rule].putLegendGroup(svg, cell_width, cell_height); + }; +} +D3SVGBarChartRuleSet.prototype = Object.create(D3SVGRuleSet.prototype); + function D3SVGGeneticAlterationRuleSet(params) { D3SVGRuleSet.call(this, params); var self = this; @@ -245,6 +269,62 @@ function D3SVGRule(params, rule_id) { }; } +function D3SVGBarChartRule(params, rule_id) { + D3SVGRule.call(this, params, rule_id); + this.data_key = params.data_key; + this.data_range = params.data_range; + + var scale = function(x) { + if (params.scale === 'log') { + return Math.log10(Math.max(x, 0.1)); + } else { + return x; + } + }; + + var scaled_data_range = _.map(this.data_range, scale); + var height_helper = function(d) { + var datum = scale(d[params.data_key]); + var data_range = [scaled_data_range[0], scaled_data_range[1]]; + var distance = (datum-scaled_data_range[0]) / (scaled_data_range[1]-scaled_data_range[0]); + return distance * 100; + }; + var y_function = function(d) { + return (100 - height_helper(d)) + '%'; + }; + var height_function = function(d) { + return height_helper(d) + '%'; + }; + this.attrs.height = height_function; + this.attrs.y = y_function; + + + this.putLegendGroup = function(svg, cell_width, cell_height) { + if (params.exclude_from_legend) { + return; + } + var rect =utils.makeD3SVGElement('rect'); + var group = svg.append('g'); + var bottom_end = group.append('g'); + utils.appendD3SVGElement(rect, bottom_end) + .attr('width', cell_width) + .attr('height', 1).attr('y', cell_height-1); + bottom_end.append('text').text(this.data_range[0]).attr('alignment-baseline', 'hanging');; + utils.spaceSVGElementsHorizontally(bottom_end, 5); + + var top_end = group.append('g'); + utils.appendD3SVGElement(rect, top_end) + .attr('width', cell_width) + .attr('height', cell_height); + top_end.append('text').text(this.data_range[1]).attr('alignment-baseline', 'hanging');; + utils.spaceSVGElementsHorizontally(top_end, 5); + + utils.spaceSVGElementsHorizontally(group, 10); + return group; + }; +} +D3SVGBarChartRule.prototype = Object.create(D3SVGRule.prototype); + function D3SVGGradientRule(params, rule_id) { D3SVGRule.call(this, params, rule_id); this.data_key = params.data_key; diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 3e27b24b605..e3acbcf5ad7 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -60,6 +60,7 @@ module.exports = { CATEGORICAL_COLOR: RuleSet.CATEGORICAL_COLOR, GRADIENT_COLOR: RuleSet.GRADIENT_COLOR, GENETIC_ALTERATION: RuleSet.GENETIC_ALTERATION, + BAR_CHART: RuleSet.BAR_CHART, create: function CreateOncoprint(container_selector_string, config) { var oncoprint = new Oncoprint(config); var renderer = new OncoprintSVGRenderer(container_selector_string, oncoprint); @@ -94,6 +95,9 @@ module.exports = { // renderer.renderTracks(); // + }, + toSVG: function(ctr) { + return renderer.toSVG(ctr); } }; } @@ -131,6 +135,9 @@ function Oncoprint(config) { self.getIdOrder = function() { return self.id_order; }; + self.setIdOrder = function(id_order) { + self.id_order = id_order; + }; self.getTrackOrder = function() { return self.track_order; }; @@ -141,8 +148,19 @@ function Oncoprint(config) { return self.tracks[track_id].data; }; self.setTrackData = function(track_id, data) { + var id_accessor = self.getTrackDatumIdAccessor(track_id); + self.tracks[track_id].data = data; - self.id_order = self.id_order.concat(_.difference(_.map(data, self.getTrackDatumIdAccessor(track_id)), self.id_order)); + self.id_order = self.id_order.concat(_.difference(_.map(data, id_accessor), self.id_order)); + + self.tracks[track_id].id_data_map = {}; + var id_data_map = self.tracks[track_id].id_data_map; + _.each(self.tracks[track_id].data, function(datum) { + id_data_map[id_accessor(datum)] = datum; + }); + }; + self.getTrackDatum = function(track_id, datum_id) { + return self.tracks[track_id].id_data_map[datum_id]; }; self.getTrackDatumIdAccessor = function(track_id) { @@ -193,6 +211,21 @@ function Oncoprint(config) { $(self).trigger(events.SET_CELL_PADDING); }; + self.sort = function(track_id_list, cmp_list) { + var lexicographically_ordered_cmp = function(id1,id2) { + var cmp_result; + for (var i=0, _len = track_id_list.length; i<_len; i++) { + cmp_result = cmp_list[i](self.getTrackDatum(id1),self.getTrackDatum(id2)); + if (cmp_result !== 0) { + break; + } + } + return cmp_result; + }; + self.getIdOrder().sort(lexicographically_ordered_cmp); + $(self).trigger(events.SORT, {id_order: self.id_order}); + }; + self.sortOnTrack = function(track_id, data_cmp) { throw "not implemented"; }; @@ -205,11 +238,9 @@ function OncoprintSVGRenderer(container_selector_string, oncoprint) { var self = this; self.container = d3.select(container_selector_string).classed('oncoprint_container', true); self.label_svg; - self.$label_svg; self.cell_svg; - self.$cell_svg; self.legend_table; - self.$legend_table; + self.legend_svg; self.rule_set_map = {}; self.rule_sets = []; @@ -218,12 +249,14 @@ function OncoprintSVGRenderer(container_selector_string, oncoprint) { self.label_svg = self.container.append('div').classed('fixed_oncoprint_section_container', true).append('svg') .attr('width', 100); self.cell_svg = self.container.append('div').classed('scrolling_oncoprint_section_container', true).append('svg'); - self.$label_svg = $(self.label_svg.node()); - self.$cell_svg = $(self.cell_svg.node()); self.legend_table = self.container.append('table'); - self.$legend_table = $(self.legend_table.node()); })(); + var render_events = [events.ADD_TRACK, events.REMOVE_TRACK, events.MOVE_TRACK, events.SORT, events.SET_CELL_PADDING, events.SET_CELL_WIDTH]; + $(oncoprint).on(render_events.join(" "), function() { + self.renderTracks(); + }); + self.setRuleSet = function(track_id, type, params) { var new_rule_set = RuleSet.makeRuleSet(type, params); self.rule_sets.push(new_rule_set); @@ -238,9 +271,35 @@ function OncoprintSVGRenderer(container_selector_string, oncoprint) { return self.rule_sets[rule_set_index]; }; + self.toSVG = function(ctr) { + var svg = ctr.append('svg'); + //var svg = utils.makeD3SVGElement('svg'); + var vertical_padding = 5; + utils.appendD3SVGElement(self.label_svg, svg); + utils.appendD3SVGElement(self.cell_svg, svg).attr('x',+self.label_svg.attr('width')); + var legend_row_y = +self.label_svg.attr('height') + vertical_padding; + self.legend_table.selectAll('tr').selectAll('svg').each(function() { + var d3_elt = d3.select(this); + utils.appendD3SVGElement(d3_elt, svg).attr('y', legend_row_y); + console.log(legend_row_y); + legend_row_y += +d3_elt.attr('height'); + }); + //utils.appendD3SVGelement(self.label_svg, svg); + console.log(svg); + if (ctr) { + utils.appendD3SVGElement(svg, ctr); + } + return svg; + }; + self.renderTracks = function() { _.each(oncoprint.getTrackOrder(), function(track_id, ind) { renderTrackLabel(track_id); + var rule_set = getRuleSet(track_id); + if (!rule_set) { + console.log("No rule set found for track id "+track_id); + return; + } renderTrackCells(track_id, getRuleSet(track_id)); }); @@ -265,6 +324,10 @@ function OncoprintSVGRenderer(container_selector_string, oncoprint) { .attr('alignment-baseline', 'hanging'); var track_rule_set = getRuleSet(track_id); + if (!track_rule_set) { + console.log("No rule set found for track id "+track_id); + return; + } var track_data = oncoprint.getTrackData(track_id); if (track_rule_set.alteredData) { var percent_altered = 100*(track_rule_set.alteredData(track_data).length / track_data.length); @@ -279,6 +342,10 @@ function OncoprintSVGRenderer(container_selector_string, oncoprint) { var renderTrackCells = function(track_id, rule_set) { var data = oncoprint.getTrackData(track_id); var id_accessor = oncoprint.getTrackDatumIdAccessor(track_id); + if (!id_accessor) { + console.log("No id accessor found for track id "+track_id); + return; + } var track_y = trackY(track_id); var id_order = utils.invert_array(oncoprint.getIdOrder()); diff --git a/packages/oncoprintjs/test/index.html b/packages/oncoprintjs/test/index.html index 980f41b3e45..eda848a6bb5 100644 --- a/packages/oncoprintjs/test/index.html +++ b/packages/oncoprintjs/test/index.html @@ -7,17 +7,18 @@
- - + + + + + + diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index b3f675583af..8f08ed7bd70 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -1,5 +1,6 @@ var _ = require('underscore'); var $ = require('jquery'); +var d3 = require('d3'); var Oncoprint = require('../../src/js/Oncoprint'); var cell_padding = 3; @@ -22,6 +23,9 @@ $('#toggle_whitespace').click(function() { onc.setCellPadding(0); } }); + $('#to_svg_btn').click(function() { + onc.toSVG(d3.select('#svg_container')); + }); var gender_data; var gender_track_id; @@ -62,10 +66,9 @@ $.when(mutation_data_promise).then(function() { onc.setTrackData(mutation_track_id, mutation_data); var log_mut_track_id = onc.addTrack({label: 'Log Mutations'}); - onc.setRuleSet(log_mut_track_id, Oncoprint.GRADIENT_COLOR, { + onc.setRuleSet(log_mut_track_id, Oncoprint.BAR_CHART, { data_key: 'attr_val', data_range: [0,100], - color_range: ['#A9A9A9', '#FF0000'], scale: 'log' }); onc.setTrackData(log_mut_track_id, mutation_data); @@ -109,7 +112,7 @@ $.when(alteration_data_promise).then(function() { }); $('#change_color_scheme').click(function() { - onc.setRuleSet(gender_track_id, onc.CATEGORICAL_COLOR, { + onc.setRuleSet(gender_track_id, Oncoprint.CATEGORICAL_COLOR, { color: {MALE: '#000000', FEMALE: '#999999'}, getCategory: function(d) { return d.attr_val; From 7294609f67c1c995fcfb6eb2ebba1c4b6aa4a3f6 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Mon, 15 Jun 2015 17:27:33 -0400 Subject: [PATCH 058/343] handling legend of multiple tracks with same ruleset --- packages/oncoprintjs/src/js/RuleSet.js | 9 ++++++ packages/oncoprintjs/src/js/oncoprint.js | 35 ++++++++++++++++------- packages/oncoprintjs/test/js/test_page.js | 4 +++ 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/packages/oncoprintjs/src/js/RuleSet.js b/packages/oncoprintjs/src/js/RuleSet.js index 0c9c4ddf250..8c61c00969b 100644 --- a/packages/oncoprintjs/src/js/RuleSet.js +++ b/packages/oncoprintjs/src/js/RuleSet.js @@ -74,6 +74,15 @@ var D3SVGRuleSet = (function() { D3SVGRuleSet.prototype.getRule = function(rule_id) { return this.rule_map[rule_id]; }; + D3SVGRuleSet.prototype.markLegendRendered = function() { + this.legend_rendered = true; + }; + D3SVGRuleSet.prototype.unmarkLegendRendered = function() { + this.legend_rendered = false; + }; + D3SVGRuleSet.prototype.isLegendRendered = function() { + return this.legend_rendered; + }; return D3SVGRuleSet; })(); diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index e3acbcf5ad7..0374e4b21dd 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -241,8 +241,7 @@ function OncoprintSVGRenderer(container_selector_string, oncoprint) { self.cell_svg; self.legend_table; self.legend_svg; - self.rule_set_map = {}; - self.rule_sets = []; + self.rule_sets = {}; (function init() { self.container.selectAll('*').remove(); @@ -259,19 +258,19 @@ function OncoprintSVGRenderer(container_selector_string, oncoprint) { self.setRuleSet = function(track_id, type, params) { var new_rule_set = RuleSet.makeRuleSet(type, params); - self.rule_sets.push(new_rule_set); - self.rule_set_map[track_id] = self.rule_sets.length-1; + self.rule_sets[track_id] = new_rule_set; }; self.useSameRuleSet = function(target_track_id, source_track_id) { - self.rule_set_map[target_track_id] = self.rule_set_map[source_track_id]; + self.rule_sets[target_track_id] = self.rule_sets[source_track_id]; }; var getRuleSet = function(track_id) { - var rule_set_index = self.rule_set_map[track_id]; - return self.rule_sets[rule_set_index]; + return self.rule_sets[track_id]; }; self.toSVG = function(ctr) { + ctr.attr('width', 2000); + ctr.attr('height', 1000); var svg = ctr.append('svg'); //var svg = utils.makeD3SVGElement('svg'); var vertical_padding = 5; @@ -305,10 +304,24 @@ function OncoprintSVGRenderer(container_selector_string, oncoprint) { (function renderLegend() { self.legend_table.selectAll('*').remove(); - _.each(self.rule_sets, function(rule_set) { - var svg = self.legend_table.append('tr').append('svg').attr('width', 1000).attr('height', 50); - rule_set.putLegendGroup(svg, oncoprint.getCellWidth(), 20); // TODO: get actual cell height - }) + _.each(oncoprint.getTrackOrder(), function(track_id) { + var rule_set = getRuleSet(track_id); + if (!rule_set) { + console.log("No rule set found for track id "+track_id); + return; + } + if (!rule_set.isLegendRendered()) { + var svg = self.legend_table.append('tr').append('svg').attr('width', 1000).attr('height', 50); + rule_set.putLegendGroup(svg, oncoprint.getCellWidth(), 20); // TODO: get actual cell height + rule_set.markLegendRendered(); + } + }); + _.each(oncoprint.getTrackOrder(), function(track_id) { + var rule_set = getRuleSet(track_id); + if (rule_set) { + rule_set.unmarkLegendRendered(); + } + }); })(); }; diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index 8f08ed7bd70..cf78110cba6 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -109,6 +109,10 @@ $.when(alteration_data_promise).then(function() { } }); onc.setTrackData(alteration_track_id, alteration_data); + + var second_alt_track = onc.addTrack({Label: 'TP53 duplicate'}); + onc.useSameRuleSet(second_alt_track, alteration_track_id); + onc.setTrackData(second_alt_track, alteration_data); }); $('#change_color_scheme').click(function() { From 72e5c3f3b4fe771b022856c2922f9e400e09138f Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Tue, 16 Jun 2015 12:12:00 -0400 Subject: [PATCH 059/343] first commit, canvas exploration --- packages/oncoprintjs/src/js/RuleSet.js | 12 ++++++------ packages/oncoprintjs/src/js/oncoprint.js | 21 ++++++++++++++++----- packages/oncoprintjs/src/js/utils.js | 2 +- packages/oncoprintjs/test/index.html | 3 ++- packages/oncoprintjs/test/js/test_page.js | 16 +++++++++++++++- 5 files changed, 40 insertions(+), 14 deletions(-) diff --git a/packages/oncoprintjs/src/js/RuleSet.js b/packages/oncoprintjs/src/js/RuleSet.js index 8c61c00969b..250c88033ea 100644 --- a/packages/oncoprintjs/src/js/RuleSet.js +++ b/packages/oncoprintjs/src/js/RuleSet.js @@ -64,11 +64,11 @@ var D3SVGRuleSet = (function() { var sorted_rules = _.sortBy(rules, function(r) { return r.z_index; }); return sorted_rules; }; - D3SVGRuleSet.prototype.apply = function(g, data, datum_id_accessor, cell_width, cell_height) { + D3SVGRuleSet.prototype.apply = function(svg, g, data, datum_id_accessor, cell_width, cell_height) { _.each(this.getRules(), function(rule) { var affected_data = rule.filterData(data); var affected_groups = g.data(affected_data, datum_id_accessor); - rule.apply(affected_groups, cell_width, cell_height); + rule.apply(svg, affected_groups, cell_width, cell_height); }); }; D3SVGRuleSet.prototype.getRule = function(rule_id) { @@ -108,7 +108,7 @@ function D3SVGCategoricalColorRuleSet(params) { addColorRule(color, category); }); - self.apply = function(g, data, datum_id_accessor, cell_width, cell_height) { + self.apply = function(svg, g, data, datum_id_accessor, cell_width, cell_height) { var missing_categories = []; _.each(data, function(datum) { var category = params.getCategory(datum); @@ -118,7 +118,7 @@ function D3SVGCategoricalColorRuleSet(params) { addColorRule(new_color, category); } }); - D3SVGRuleSet.prototype.apply.call(this, g, data, datum_id_accessor, cell_width, cell_height); + D3SVGRuleSet.prototype.apply.call(this, svg, g, data, datum_id_accessor, cell_width, cell_height); }; self.putLegendGroup = function(svg, cell_width, cell_height) { @@ -251,7 +251,7 @@ function D3SVGRule(params, rule_id) { return attr_val+''; }; - this.apply = function(g, cell_width, cell_height) { + this.apply = function(svg, g, cell_width, cell_height) { var shape = this.shape; var elts = utils.appendD3SVGElement(shape, g); var attrs = this.attrs || {}; @@ -433,7 +433,7 @@ function D3SVGStaticRule(params, rule_id) { } var group = svg.append('g'); var g = group.append('g'); - this.apply(g, cell_width, cell_height); + this.apply(svg, g, cell_width, cell_height); if (this.legend_label) { group.append('text').text(this.legend_label) .attr('alignment-baseline', 'hanging'); diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 0374e4b21dd..1d64808212d 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -246,8 +246,9 @@ function OncoprintSVGRenderer(container_selector_string, oncoprint) { (function init() { self.container.selectAll('*').remove(); self.label_svg = self.container.append('div').classed('fixed_oncoprint_section_container', true).append('svg') - .attr('width', 100); - self.cell_svg = self.container.append('div').classed('scrolling_oncoprint_section_container', true).append('svg'); + .attr('width', 100).attr('xmlns', "http://www.w3.org/2000/svg"); + //self.cell_canvas = self.container.append('div').classed('scrolling_oncoprint_section_container', true).append('canvas').attr('id', 'cell_canvas').attr('width', 1000).attr('height', 400); + self.cell_svg = self.container.append('div').classed('scrolling_oncoprint_section_container', true).append('svg').attr('xmlns', "http://www.w3.org/2000/svg"); self.legend_table = self.container.append('table'); })(); @@ -271,7 +272,7 @@ function OncoprintSVGRenderer(container_selector_string, oncoprint) { self.toSVG = function(ctr) { ctr.attr('width', 2000); ctr.attr('height', 1000); - var svg = ctr.append('svg'); + var svg = ctr.append('svg').attr('xmlns', "http://www.w3.org/2000/svg"); //var svg = utils.makeD3SVGElement('svg'); var vertical_padding = 5; utils.appendD3SVGElement(self.label_svg, svg); @@ -311,7 +312,7 @@ function OncoprintSVGRenderer(container_selector_string, oncoprint) { return; } if (!rule_set.isLegendRendered()) { - var svg = self.legend_table.append('tr').append('svg').attr('width', 1000).attr('height', 50); + var svg = self.legend_table.append('tr').append('svg').attr('width', 1000).attr('height', 50).attr('xmlns', "http://www.w3.org/2000/svg"); rule_set.putLegendGroup(svg, oncoprint.getCellWidth(), 20); // TODO: get actual cell height rule_set.markLegendRendered(); } @@ -352,6 +353,15 @@ function OncoprintSVGRenderer(container_selector_string, oncoprint) { } }; + /*var renderCellsToCanvas = function() { + var ctx = self.cell_canvas.node().getContext('2d'); + for (var child = self.cell_svg.node().firstChild; child; child = child.nextSibling) { + var d3_child = d3.select(child); + //ctx.rect(d3_child.attr('x'), d3_child.attr('y'), d3_child.attr('width'), d3_child.attr('height')); + ctx.rect(Math.random()*1000, Math.random()*200, 1, 1); + } + ctx.stroke(); + }*/ var renderTrackCells = function(track_id, rule_set) { var data = oncoprint.getTrackData(track_id); var id_accessor = oncoprint.getTrackDatumIdAccessor(track_id); @@ -385,8 +395,9 @@ function OncoprintSVGRenderer(container_selector_string, oncoprint) { bound_g.selectAll('*').remove(); })(); (function renderCells() { - rule_set.apply(bound_g, data, id_accessor, oncoprint.getCellWidth(), oncoprint.getCellHeight(track_id)); + rule_set.apply(self.cell_svg, bound_g, data, id_accessor, oncoprint.getCellWidth(), oncoprint.getCellHeight(track_id)); })(); + //renderCellsToCanvas(); }; var trackY = function(track_id) { diff --git a/packages/oncoprintjs/src/js/utils.js b/packages/oncoprintjs/src/js/utils.js index 6d85b0f95da..b2745b4c7de 100644 --- a/packages/oncoprintjs/src/js/utils.js +++ b/packages/oncoprintjs/src/js/utils.js @@ -42,7 +42,7 @@ exports.makeD3SVGElement = function(tag) { return d3.select(document.createElementNS('http://www.w3.org/2000/svg', tag)); }; -exports.appendD3SVGElement = function(elt, target) { +exports.appendD3SVGElement = function(elt, target, svg) { return target.select(function() { return this.appendChild(elt.node().cloneNode(true)); }); diff --git a/packages/oncoprintjs/test/index.html b/packages/oncoprintjs/test/index.html index eda848a6bb5..badc1d209f0 100644 --- a/packages/oncoprintjs/test/index.html +++ b/packages/oncoprintjs/test/index.html @@ -12,10 +12,11 @@ + - + diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index cf78110cba6..cc1bbca5ae8 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -25,6 +25,20 @@ $('#toggle_whitespace').click(function() { }); $('#to_svg_btn').click(function() { onc.toSVG(d3.select('#svg_container')); + var DOMURL = window.URL || window.webkitURL || window; + var ctx = $('#canvas')[0].getContext('2d'); + var img = new Image(); + var svg; + var data = $('#svg_container')[0].outerHTML; + console.log(data); + svg = new Blob([data], {type: 'image/svg+xml;charset=utf-8'}); + console.log(svg); + var url = DOMURL.createObjectURL(svg); + img.src = url; + img.onload = function() { + ctx.drawImage(img, 0, 0); + DOMURL.revokeObjectURL(url); + }; }); var gender_data; @@ -122,4 +136,4 @@ $('#change_color_scheme').click(function() { return d.attr_val; } }); -}); \ No newline at end of file +}); From aba1d53fc6718a2f79420af2fc00fc3e380063d9 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Tue, 16 Jun 2015 14:00:21 -0400 Subject: [PATCH 060/343] Merge branch 'master' into canvas-rendering-exploration --- packages/oncoprintjs/.gitignore | 3 +++ packages/oncoprintjs/src/js/events.js | 1 + packages/oncoprintjs/src/js/oncoprint.js | 20 +++++++------------- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/packages/oncoprintjs/.gitignore b/packages/oncoprintjs/.gitignore index 9c65dc24b53..bab8de9a0e6 100644 --- a/packages/oncoprintjs/.gitignore +++ b/packages/oncoprintjs/.gitignore @@ -38,5 +38,8 @@ Session.vim .netrwhist *~ +### emacs ### +\#*\# + # Compiled assets dist diff --git a/packages/oncoprintjs/src/js/events.js b/packages/oncoprintjs/src/js/events.js index 9dc920599be..7175a69135a 100644 --- a/packages/oncoprintjs/src/js/events.js +++ b/packages/oncoprintjs/src/js/events.js @@ -34,6 +34,7 @@ module.exports = { SORT: 'sort.oncoprint', SET_CELL_PADDING: 'set_cell_padding.oncoprint', SET_CELL_WIDTH: 'set_cell_width.oncoprint', + SET_TRACK_DATA: 'set_track_data.oncoprint', CELL_CLICK: 'cell_click.oncoprint', CELL_MOUSEENTER: 'cell_mouseenter.oncoprint', CELL_MOUSELEAVE: 'cell_mouseleave.oncoprint', diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 1d64808212d..2a764a6a5de 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -77,24 +77,15 @@ module.exports = { }, setTrackData: function(track_id, data) { oncoprint.setTrackData(track_id, data); - // - renderer.renderTracks(); - // }, setRuleSet: function(track_id, type, params) { renderer.setRuleSet(track_id, type, params); - // - renderer.renderTracks(); - // }, useSameRuleSet: function(target_track_id, source_track_id) { renderer.useSameRuleSet(target_track_id, source_track_id); }, setCellPadding: function(p) { oncoprint.setCellPadding(p); - // - renderer.renderTracks(); - // }, toSVG: function(ctr) { return renderer.toSVG(ctr); @@ -120,9 +111,6 @@ function Oncoprint(config) { self.getCellPadding = function() { return self.config.cell_padding; }; - self.setCellPadding = function(p) { - self.config.cell_padding = p; - }; self.getCellHeight = function(track_id) { return self.tracks[track_id].config.cell_height; }; @@ -137,6 +125,7 @@ function Oncoprint(config) { }; self.setIdOrder = function(id_order) { self.id_order = id_order; + $(self).trigger(events.SET_ID_ORDER); }; self.getTrackOrder = function() { return self.track_order; @@ -158,6 +147,7 @@ function Oncoprint(config) { _.each(self.tracks[track_id].data, function(datum) { id_data_map[id_accessor(datum)] = datum; }); + $(self).trigger(events.SET_TRACK_DATA); }; self.getTrackDatum = function(track_id, datum_id) { return self.tracks[track_id].id_data_map[datum_id]; @@ -212,6 +202,8 @@ function Oncoprint(config) { }; self.sort = function(track_id_list, cmp_list) { + track_id_list = [].concat(track_id_list); + cmp_list = [].concat(cmp_list); var lexicographically_ordered_cmp = function(id1,id2) { var cmp_result; for (var i=0, _len = track_id_list.length; i<_len; i++) { @@ -252,7 +244,8 @@ function OncoprintSVGRenderer(container_selector_string, oncoprint) { self.legend_table = self.container.append('table'); })(); - var render_events = [events.ADD_TRACK, events.REMOVE_TRACK, events.MOVE_TRACK, events.SORT, events.SET_CELL_PADDING, events.SET_CELL_WIDTH]; + var render_events = [events.ADD_TRACK, events.REMOVE_TRACK, events.MOVE_TRACK, events.SORT, events.SET_CELL_PADDING, + events.SET_CELL_WIDTH, events.SET_TRACK_DATA]; $(oncoprint).on(render_events.join(" "), function() { self.renderTracks(); }); @@ -260,6 +253,7 @@ function OncoprintSVGRenderer(container_selector_string, oncoprint) { self.setRuleSet = function(track_id, type, params) { var new_rule_set = RuleSet.makeRuleSet(type, params); self.rule_sets[track_id] = new_rule_set; + self.renderTracks(); }; self.useSameRuleSet = function(target_track_id, source_track_id) { self.rule_sets[target_track_id] = self.rule_sets[source_track_id]; From 0fd962f5aa62268e6700923d28554737a0347eb8 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Tue, 16 Jun 2015 19:14:43 -0400 Subject: [PATCH 061/343] Successful canvas rendering Canvas rendering implemented (for rectangles only) Cell mouseenter events in the canvas implemented, demo in showing red rectangle on the cell. --- packages/oncoprintjs/gulpfile.js | 7 +- packages/oncoprintjs/src/js/RuleSet.js | 35 ++++-- packages/oncoprintjs/src/js/events.js | 3 +- packages/oncoprintjs/src/js/oncoprint.js | 128 +++++++++++++++++++--- packages/oncoprintjs/test/index.html | 1 + packages/oncoprintjs/test/js/test_page.js | 8 ++ 6 files changed, 156 insertions(+), 26 deletions(-) diff --git a/packages/oncoprintjs/gulpfile.js b/packages/oncoprintjs/gulpfile.js index 7219d96f8b3..35e7793f6ad 100644 --- a/packages/oncoprintjs/gulpfile.js +++ b/packages/oncoprintjs/gulpfile.js @@ -76,7 +76,12 @@ gulp.task('default', ['clean'], function() { gulp.start('prod'); }); + +function handleError(err) { + console.log(err.toString()); +} // Watch gulp.task('watch', function() { - gulp.watch(['src/js/**/*.js', 'src/css/**/*.css', 'test/*.html', 'test/js/**/*.js', 'test/spec/*.js'], ['test']); + gulp.watch(['src/js/**/*.js', 'src/css/**/*.css', 'test/*.html', 'test/js/**/*.js', 'test/spec/*.js'], ['test']) + .on('error', handleError); }); diff --git a/packages/oncoprintjs/src/js/RuleSet.js b/packages/oncoprintjs/src/js/RuleSet.js index 250c88033ea..055600c1952 100644 --- a/packages/oncoprintjs/src/js/RuleSet.js +++ b/packages/oncoprintjs/src/js/RuleSet.js @@ -154,7 +154,8 @@ function D3SVGBarChartRuleSet(params) { var rule = this.addBarChartRule({ data_key: params.data_key, data_range: params.data_range, - scale: params.scale + scale: params.scale, + fill: params.fill, }); this.putLegendGroup = function(svg, cell_width, cell_height) { this.rule_map[rule].putLegendGroup(svg, cell_width, cell_height); @@ -251,24 +252,39 @@ function D3SVGRule(params, rule_id) { return attr_val+''; }; + var convertAttr = function(d, i, attr_val, attr_name, cell_width, cell_height) { + var ret = attr_val; + if (typeof ret === 'function') { + ret = ret(d,i); + } + if (typeof ret === 'string' && ret.indexOf('%') > -1) { + ret = percentToPx(ret, attr_name, cell_width, cell_height); + } + return ret; + }; + this.apply = function(svg, g, cell_width, cell_height) { var shape = this.shape; var elts = utils.appendD3SVGElement(shape, g); var attrs = this.attrs || {}; attrs.width = attrs.width || '100%'; attrs.height = attrs.height || '100%'; + attrs.x = attrs.x || 0; + attrs.y = attrs.y || 0; _.each(attrs, function(val, key) { elts.attr(key, function(d,i) { - var curr_val = val; - if (typeof curr_val === 'function') { - curr_val = curr_val(d,i); + if (key === 'x' || key === 'y') { + return; } - if (typeof curr_val === 'string' && curr_val.indexOf('%') > -1) { - curr_val = percentToPx(curr_val, key, cell_width, cell_height); - } - return curr_val; + return convertAttr(d, i, val, key, cell_width, cell_height); }); }); + + elts.attr('transform', function(d,i) { + var x_val = convertAttr(d, i, attrs.x, 'x', cell_width, cell_height); + var y_val = convertAttr(d, i, attrs.y, 'y', cell_width, cell_height); + return utils.translate(x_val, y_val); + }); } this.filterData = function(data) { return data.filter(this.condition); @@ -306,13 +322,16 @@ function D3SVGBarChartRule(params, rule_id) { }; this.attrs.height = height_function; this.attrs.y = y_function; + this.attrs.fill = params.fill || '#000000'; this.putLegendGroup = function(svg, cell_width, cell_height) { + // TODO: triangle legend piece if (params.exclude_from_legend) { return; } var rect =utils.makeD3SVGElement('rect'); + rect.attr('fill', this.attrs.fill); var group = svg.append('g'); var bottom_end = group.append('g'); utils.appendD3SVGElement(rect, bottom_end) diff --git a/packages/oncoprintjs/src/js/events.js b/packages/oncoprintjs/src/js/events.js index 7175a69135a..e1d12ebd873 100644 --- a/packages/oncoprintjs/src/js/events.js +++ b/packages/oncoprintjs/src/js/events.js @@ -42,5 +42,6 @@ module.exports = { ONCOPRINT_MOUSELEAVE: 'oncoprint_mouseleave.oncoprint', SET_PRE_TRACK_PADDING: 'set_pre_track_padding.oncoprint', TRACK_INIT: 'init.track.oncoprint', - UPDATE_RENDER_RULES: 'update_render_rules.cell_renderer=.oncoprint', + UPDATE_RENDER_RULES: 'update_render_rules.cell_renderer.oncoprint', + FINISHED_RENDERING: 'finished_rendering.renderer.oncoprint' }; diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 2a764a6a5de..ab13f157c0b 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -64,7 +64,7 @@ module.exports = { create: function CreateOncoprint(container_selector_string, config) { var oncoprint = new Oncoprint(config); var renderer = new OncoprintSVGRenderer(container_selector_string, oncoprint); - return { + var ret = { addTrack: function(config) { var track_id = oncoprint.addTrack(config); return track_id; @@ -89,8 +89,12 @@ module.exports = { }, toSVG: function(ctr) { return renderer.toSVG(ctr); + }, + setClipping: function(c) { + renderer.setClipping(c); } }; + return ret; } }; @@ -234,13 +238,29 @@ function OncoprintSVGRenderer(container_selector_string, oncoprint) { self.legend_table; self.legend_svg; self.rule_sets = {}; + self.clipping = true; (function init() { self.container.selectAll('*').remove(); self.label_svg = self.container.append('div').classed('fixed_oncoprint_section_container', true).append('svg') .attr('width', 100).attr('xmlns', "http://www.w3.org/2000/svg"); - //self.cell_canvas = self.container.append('div').classed('scrolling_oncoprint_section_container', true).append('canvas').attr('id', 'cell_canvas').attr('width', 1000).attr('height', 400); - self.cell_svg = self.container.append('div').classed('scrolling_oncoprint_section_container', true).append('svg').attr('xmlns', "http://www.w3.org/2000/svg"); + self.cell_canvas = self.container.append('div').classed('scrolling_oncoprint_section_container', true).append('canvas').attr('id', 'cell_canvas').attr('width', 1000).attr('height', 200); + self.cell_canvas.node().addEventListener('mousemove', function(evt) { + $(self).off(events.FINISHED_RENDERING); + var canvas_rect = self.cell_canvas.node().getBoundingClientRect(); + var x = evt.clientX - canvas_rect.left; + var y = evt.clientY - canvas_rect.top; + var cell = self.mousePosToCell(x,y); + if (cell) { + $(self).on(events.FINISHED_RENDERING, function() { + self.cell_canvas_ctx.fillStyle = '#ff0000'; + self.cell_canvas_ctx.fillRect(cell.x, cell.y, 2, 2); + console.log(cell.datum); + }); + } + }); + self.cell_canvas_ctx = self.cell_canvas.node().getContext('2d'); + self.cell_svg = utils.makeD3SVGElement('svg');//self.container.append('div').classed('scrolling_oncoprint_section_container', true).append('svg').attr('xmlns', "http://www.w3.org/2000/svg"); self.legend_table = self.container.append('table'); })(); @@ -278,8 +298,6 @@ function OncoprintSVGRenderer(container_selector_string, oncoprint) { console.log(legend_row_y); legend_row_y += +d3_elt.attr('height'); }); - //utils.appendD3SVGelement(self.label_svg, svg); - console.log(svg); if (ctr) { utils.appendD3SVGElement(svg, ctr); } @@ -318,17 +336,21 @@ function OncoprintSVGRenderer(container_selector_string, oncoprint) { } }); })(); + }; var renderTrackLabel = function(track_id) { var label_class = 'label'+track_id; var track_y = trackY(track_id); + var label_y = track_y + oncoprint.getTrackPadding(track_id); // TODO: centralize it self.label_svg .attr('width', labelSvgWidth()) .attr('height', labelSvgHeight()); self.label_svg.selectAll('.'+label_class).remove(); self.label_svg.append('text').classed(label_class, true).text(oncoprint.getTrackLabel(track_id)) - .attr('transform', utils.translate(0, track_y)) + //.attr('x', 0) + //.attr('y', track_y) + .attr('transform', utils.translate(0, label_y)) .attr('alignment-baseline', 'hanging'); var track_rule_set = getRuleSet(track_id); @@ -343,19 +365,52 @@ function OncoprintSVGRenderer(container_selector_string, oncoprint) { .attr('text-anchor', 'end') .text(Math.floor(percent_altered)+'%') .attr('alignment-baseline', 'hanging') - .attr('transform', utils.translate(labelSvgWidth(), track_y)); + //.attr('x', labelSvgWidth()) + //.attr('y', track_y); + .attr('transform', utils.translate(labelSvgWidth(), label_y)); } }; - /*var renderCellsToCanvas = function() { - var ctx = self.cell_canvas.node().getContext('2d'); + var renderCellsToCanvas = function() { + var ctx = self.cell_canvas_ctx; + ctx.clearRect(0,0, self.cell_canvas.node().width, self.cell_canvas.node().height); + ctx.beginPath(); + var toprint = 5; for (var child = self.cell_svg.node().firstChild; child; child = child.nextSibling) { - var d3_child = d3.select(child); - //ctx.rect(d3_child.attr('x'), d3_child.attr('y'), d3_child.attr('width'), d3_child.attr('height')); - ctx.rect(Math.random()*1000, Math.random()*200, 1, 1); + renderCellToCanvas(child); } ctx.stroke(); - }*/ + ctx.closePath(); + $(self).trigger(events.FINISHED_RENDERING); + }; + var pointInCanvas = function(x, y) { + var canvas_node = self.cell_canvas.node(); + var x_lim = self.cell_canvas.attr('width'); + var y_lim = self.cell_canvas.attr('height'); + return x <= x_lim && x >= 0 && y <= y_lim && y >= 0; + }; + var renderCellToCanvas = function(node) { + var ctx = self.cell_canvas_ctx; + var rectpt = self.cell_svg.node().createSVGPoint(); + var d3_node = d3.select(node); + rectpt = rectpt.matrixTransform(node.getCTM()); + if (self.clipping && !pointInCanvas(rectpt.x, rectpt.y)) { + return; + } + switch (node.tagName) { + case 'g': + for (var child=node.firstChild; child; child = child.nextSibling) { + renderCellToCanvas(child); + } + break; + case 'rect': + ctx.fillStyle = d3_node.attr('fill'); + ctx.strokeStyle = d3_node.attr('stroke') || ctx.fillStyle; + ctx.fillRect(rectpt.x /*+ d3_node.attr('x')*/, rectpt.y/* + d3_node.attr('y')*/, d3_node.attr('width'), d3_node.attr('height')); + + } + }; + setInterval(renderCellsToCanvas, 1000/60); var renderTrackCells = function(track_id, rule_set) { var data = oncoprint.getTrackData(track_id); var id_accessor = oncoprint.getTrackDatumIdAccessor(track_id); @@ -365,6 +420,8 @@ function OncoprintSVGRenderer(container_selector_string, oncoprint) { } var track_y = trackY(track_id); var id_order = utils.invert_array(oncoprint.getIdOrder()); + var cell_width = oncoprint.getCellWidth(); + var cell_height = oncoprint.getCellHeight(track_id); (function updateSVG() { self.cell_svg @@ -382,16 +439,17 @@ function OncoprintSVGRenderer(container_selector_string, oncoprint) { })(); (function positionGroups() { bound_g.transition().attr('transform', function(d, i) { - return utils.translate(id_order[id_accessor(d)]*(oncoprint.getCellWidth() + oncoprint.getCellPadding()), track_y); + var x = id_order[id_accessor(d)]*(oncoprint.getCellWidth() + oncoprint.getCellPadding()); + var y = track_y + oncoprint.getTrackPadding(track_id); + return utils.translate(x, y); }); })(); (function cleanGroups() { bound_g.selectAll('*').remove(); })(); (function renderCells() { - rule_set.apply(self.cell_svg, bound_g, data, id_accessor, oncoprint.getCellWidth(), oncoprint.getCellHeight(track_id)); + rule_set.apply(self.cell_svg, bound_g, data, id_accessor, cell_width, cell_height); })(); - //renderCellsToCanvas(); }; var trackY = function(track_id) { @@ -407,6 +465,40 @@ function OncoprintSVGRenderer(container_selector_string, oncoprint) { return y; }; + self.mousePosToCell = function(x,y) { + // TODO: centralize all of these coordinate stuff...shouldn't be calculating them twice in two different functions D: + var yInTrack = function(track_id, y) { + var track_y = trackY(track_id); + var track_padding = oncoprint.getTrackPadding(track_id); + var track_height = oncoprint.getTrackHeight(track_id); + return y >= track_y + track_padding && y <= track_y + track_padding + track_height; + }; + var track_id = false; + _.find(oncoprint.getTrackOrder(), function(id) { + if (yInTrack(id, y)) { + track_id = id; + return true; + } + return false; + }); + if (track_id === false) { + return undefined; + } else { + var cell_width = oncoprint.getCellWidth(); + var cell_padding = oncoprint.getCellPadding(); + var cell_index = Math.floor(x / (cell_padding +cell_width)); + var in_cell = (x % (cell_padding + cell_width)) < cell_width; + if (in_cell) { + var datum_id = oncoprint.getIdOrder()[cell_index]; + var datum = oncoprint.getTrackDatum(track_id, datum_id); + var cell_x = cell_index*(cell_padding + cell_width); // TODO: centralize it + var cell_y = trackY(track_id)+oncoprint.getTrackPadding(track_id);// TODO: centralize it + return {datum: datum, x: cell_x, y: cell_y}; + } else { + return undefined; + } + } + }; var renderedTrackHeight = function(track_id) { return oncoprint.getTrackHeight(track_id) + 2*oncoprint.getTrackPadding(track_id); }; @@ -426,4 +518,8 @@ function OncoprintSVGRenderer(container_selector_string, oncoprint) { var labelSvgWidth = function() { return 100; }; + + self.setClipping = function(c) { + self.clipping = c; + }; } diff --git a/packages/oncoprintjs/test/index.html b/packages/oncoprintjs/test/index.html index badc1d209f0..b53fd97697e 100644 --- a/packages/oncoprintjs/test/index.html +++ b/packages/oncoprintjs/test/index.html @@ -10,6 +10,7 @@ + diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index cc1bbca5ae8..9660184ae81 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -2,6 +2,7 @@ var _ = require('underscore'); var $ = require('jquery'); var d3 = require('d3'); +var globals = require('../../src/js/globals'); var Oncoprint = require('../../src/js/Oncoprint'); var cell_padding = 3; var whitespace_on = true; @@ -40,6 +41,12 @@ $('#toggle_whitespace').click(function() { DOMURL.revokeObjectURL(url); }; }); + var clipping = true; + $('#clipping_btn').click(function() { + clipping = !clipping; + onc.setClipping(clipping); + console.log("Clipping is "+clipping); + }); var gender_data; var gender_track_id; @@ -83,6 +90,7 @@ $.when(mutation_data_promise).then(function() { onc.setRuleSet(log_mut_track_id, Oncoprint.BAR_CHART, { data_key: 'attr_val', data_range: [0,100], + fill: '#ff0000', scale: 'log' }); onc.setTrackData(log_mut_track_id, mutation_data); From 2189191e13681cb98fd72f5929072100bd22f7e0 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Wed, 17 Jun 2015 13:59:09 -0400 Subject: [PATCH 062/343] adding duplicate gender track for stress-testing --- packages/oncoprintjs/test/js/test_page.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index 9660184ae81..6f39156976d 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -72,6 +72,11 @@ $.when(gender_data_promise).then(function() { } }); onc.setTrackData(gender_track_id, gender_data); + //for (var i=0; i<3; i++) { + var dup_gender_track_id = onc.addTrack({label: 'Gender'}); + onc.useSameRuleSet(dup_gender_track_id, gender_track_id); + onc.setTrackData(dup_gender_track_id, gender_data); + //} }); mutation_data_promise.then(function(data) { From 42683844d833f4499c59b88d5ce8a38be46024bb Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Wed, 17 Jun 2015 19:04:52 -0400 Subject: [PATCH 063/343] canvas and svg rendering in, and better calculations in. seems like svg is way to go actually --- packages/oncoprintjs/src/js/oncoprint.js | 768 ++++++++++++++-------- packages/oncoprintjs/src/js/utils.js | 17 + packages/oncoprintjs/test/index.html | 2 +- packages/oncoprintjs/test/js/test_page.js | 4 +- 4 files changed, 530 insertions(+), 261 deletions(-) diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index ab13f157c0b..c7f4cf70077 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -63,7 +63,12 @@ module.exports = { BAR_CHART: RuleSet.BAR_CHART, create: function CreateOncoprint(container_selector_string, config) { var oncoprint = new Oncoprint(config); - var renderer = new OncoprintSVGRenderer(container_selector_string, oncoprint); + var renderer = new OncoprintSVGRenderer(oncoprint, {label_font: '12px Arial'}); + renderer.attachLabelSVG(container_selector_string); + renderer.attachCellSVG(container_selector_string); + /*var canvas = d3.select(container_selector_string).append('div').classed('scrolling_oncoprint_section_container', true) + .append('canvas'); + var canvas_drawer = new CanvasSVGDrawer(canvas, renderer.getCellSVG());*/ var ret = { addTrack: function(config) { var track_id = oncoprint.addTrack(config); @@ -90,9 +95,6 @@ module.exports = { toSVG: function(ctr) { return renderer.toSVG(ctr); }, - setClipping: function(c) { - renderer.setClipping(c); - } }; return ret; } @@ -230,296 +232,546 @@ function Oncoprint(config) { }; } -function OncoprintSVGRenderer(container_selector_string, oncoprint) { - var self = this; - self.container = d3.select(container_selector_string).classed('oncoprint_container', true); - self.label_svg; - self.cell_svg; - self.legend_table; - self.legend_svg; - self.rule_sets = {}; - self.clipping = true; - - (function init() { - self.container.selectAll('*').remove(); - self.label_svg = self.container.append('div').classed('fixed_oncoprint_section_container', true).append('svg') - .attr('width', 100).attr('xmlns', "http://www.w3.org/2000/svg"); - self.cell_canvas = self.container.append('div').classed('scrolling_oncoprint_section_container', true).append('canvas').attr('id', 'cell_canvas').attr('width', 1000).attr('height', 200); - self.cell_canvas.node().addEventListener('mousemove', function(evt) { - $(self).off(events.FINISHED_RENDERING); - var canvas_rect = self.cell_canvas.node().getBoundingClientRect(); - var x = evt.clientX - canvas_rect.left; - var y = evt.clientY - canvas_rect.top; - var cell = self.mousePosToCell(x,y); - if (cell) { - $(self).on(events.FINISHED_RENDERING, function() { - self.cell_canvas_ctx.fillStyle = '#ff0000'; - self.cell_canvas_ctx.fillRect(cell.x, cell.y, 2, 2); - console.log(cell.datum); - }); - } - }); - self.cell_canvas_ctx = self.cell_canvas.node().getContext('2d'); - self.cell_svg = utils.makeD3SVGElement('svg');//self.container.append('div').classed('scrolling_oncoprint_section_container', true).append('svg').attr('xmlns', "http://www.w3.org/2000/svg"); - self.legend_table = self.container.append('table'); - })(); - - var render_events = [events.ADD_TRACK, events.REMOVE_TRACK, events.MOVE_TRACK, events.SORT, events.SET_CELL_PADDING, - events.SET_CELL_WIDTH, events.SET_TRACK_DATA]; - $(oncoprint).on(render_events.join(" "), function() { - self.renderTracks(); - }); - - self.setRuleSet = function(track_id, type, params) { - var new_rule_set = RuleSet.makeRuleSet(type, params); - self.rule_sets[track_id] = new_rule_set; - self.renderTracks(); - }; - self.useSameRuleSet = function(target_track_id, source_track_id) { - self.rule_sets[target_track_id] = self.rule_sets[source_track_id]; - }; - - var getRuleSet = function(track_id) { - return self.rule_sets[track_id]; - }; - - self.toSVG = function(ctr) { - ctr.attr('width', 2000); - ctr.attr('height', 1000); - var svg = ctr.append('svg').attr('xmlns', "http://www.w3.org/2000/svg"); - //var svg = utils.makeD3SVGElement('svg'); - var vertical_padding = 5; - utils.appendD3SVGElement(self.label_svg, svg); - utils.appendD3SVGElement(self.cell_svg, svg).attr('x',+self.label_svg.attr('width')); - var legend_row_y = +self.label_svg.attr('height') + vertical_padding; - self.legend_table.selectAll('tr').selectAll('svg').each(function() { - var d3_elt = d3.select(this); - utils.appendD3SVGElement(d3_elt, svg).attr('y', legend_row_y); - console.log(legend_row_y); - legend_row_y += +d3_elt.attr('height'); - }); - if (ctr) { - utils.appendD3SVGElement(svg, ctr); - } - return svg; +var OncoprintRenderer = (function() { + function OncoprintRenderer(oncoprint, config) { + this.rule_sets = {}; + this.clipping = true; + this.oncoprint = oncoprint; + this.config = config; }; - self.renderTracks = function() { - _.each(oncoprint.getTrackOrder(), function(track_id, ind) { - renderTrackLabel(track_id); - var rule_set = getRuleSet(track_id); - if (!rule_set) { - console.log("No rule set found for track id "+track_id); - return; + OncoprintRenderer.prototype.getLabelFont = function() { + return this.config.label_font; + }; + OncoprintRenderer.prototype.setRuleSet = function(track_id, type, params) { + var new_rule_set = RuleSet.makeRuleSet(type, params); + this.rule_sets[track_id] = new_rule_set; + }; + OncoprintRenderer.prototype.useSameRuleSet = function(target_track_id, source_track_id) { + this.rule_sets[target_track_id] = this.rule_sets[source_track_id]; + }; + OncoprintRenderer.prototype.getRuleSet = function(track_id) { + return this.rule_sets[track_id]; + }; + OncoprintRenderer.prototype.getTrackTop = function(track_id) { + var y = 0; + var self =this; + _.find(this.oncoprint.getTrackOrder(), function(id) { + if (id === track_id) { + return true; + } else { + y += self.getTrackRenderHeight(id); + return false; } - renderTrackCells(track_id, getRuleSet(track_id)); }); - - (function renderLegend() { - self.legend_table.selectAll('*').remove(); - _.each(oncoprint.getTrackOrder(), function(track_id) { - var rule_set = getRuleSet(track_id); - if (!rule_set) { - console.log("No rule set found for track id "+track_id); - return; - } - if (!rule_set.isLegendRendered()) { - var svg = self.legend_table.append('tr').append('svg').attr('width', 1000).attr('height', 50).attr('xmlns', "http://www.w3.org/2000/svg"); - rule_set.putLegendGroup(svg, oncoprint.getCellWidth(), 20); // TODO: get actual cell height - rule_set.markLegendRendered(); - } - }); - _.each(oncoprint.getTrackOrder(), function(track_id) { - var rule_set = getRuleSet(track_id); - if (rule_set) { - rule_set.unmarkLegendRendered(); - } - }); - })(); - + return y; }; - - var renderTrackLabel = function(track_id) { - var label_class = 'label'+track_id; - var track_y = trackY(track_id); - var label_y = track_y + oncoprint.getTrackPadding(track_id); // TODO: centralize it - self.label_svg - .attr('width', labelSvgWidth()) - .attr('height', labelSvgHeight()); - self.label_svg.selectAll('.'+label_class).remove(); - self.label_svg.append('text').classed(label_class, true).text(oncoprint.getTrackLabel(track_id)) - //.attr('x', 0) - //.attr('y', track_y) - .attr('transform', utils.translate(0, label_y)) - .attr('alignment-baseline', 'hanging'); - - var track_rule_set = getRuleSet(track_id); - if (!track_rule_set) { - console.log("No rule set found for track id "+track_id); - return; - } - var track_data = oncoprint.getTrackData(track_id); - if (track_rule_set.alteredData) { - var percent_altered = 100*(track_rule_set.alteredData(track_data).length / track_data.length); - self.label_svg.append('text').classed(label_class, true) - .attr('text-anchor', 'end') - .text(Math.floor(percent_altered)+'%') - .attr('alignment-baseline', 'hanging') - //.attr('x', labelSvgWidth()) - //.attr('y', track_y); - .attr('transform', utils.translate(labelSvgWidth(), label_y)); - } - + OncoprintRenderer.prototype.getTrackRenderTop = function(track_id) { + return this.getTrackTop(track_id) + this.oncoprint.getTrackPadding(track_id); }; - var renderCellsToCanvas = function() { - var ctx = self.cell_canvas_ctx; - ctx.clearRect(0,0, self.cell_canvas.node().width, self.cell_canvas.node().height); - ctx.beginPath(); - var toprint = 5; - for (var child = self.cell_svg.node().firstChild; child; child = child.nextSibling) { - renderCellToCanvas(child); + OncoprintRenderer.prototype.getTrackRenderHeight = function(track_id) { + return this.oncoprint.getTrackHeight(track_id) + 2*this.oncoprint.getTrackPadding(track_id); + }; + OncoprintRenderer.prototype.getCellPos = function(track_id, datum_id) { + var index = this.oncoprint.getIdOrder().indexOf(datum_id); + if (index > -1) { + return {x: index*(this.oncoprint.getCellWidth()+this.oncoprint.getCellPadding()), + y: this.getTrackRenderTop(track_id)}; + } else { + return [-1,-1]; } - ctx.stroke(); - ctx.closePath(); - $(self).trigger(events.FINISHED_RENDERING); }; - var pointInCanvas = function(x, y) { - var canvas_node = self.cell_canvas.node(); - var x_lim = self.cell_canvas.attr('width'); - var y_lim = self.cell_canvas.attr('height'); - return x <= x_lim && x >= 0 && y <= y_lim && y >= 0; + OncoprintRenderer.prototype.getCellAreaWidth = function() { + return this.oncoprint.getIdOrder().length*(this.oncoprint.getCellWidth() + this.oncoprint.getCellPadding()); }; - var renderCellToCanvas = function(node) { - var ctx = self.cell_canvas_ctx; - var rectpt = self.cell_svg.node().createSVGPoint(); - var d3_node = d3.select(node); - rectpt = rectpt.matrixTransform(node.getCTM()); - if (self.clipping && !pointInCanvas(rectpt.x, rectpt.y)) { - return; - } - switch (node.tagName) { - case 'g': - for (var child=node.firstChild; child; child = child.nextSibling) { - renderCellToCanvas(child); - } - break; - case 'rect': - ctx.fillStyle = d3_node.attr('fill'); - ctx.strokeStyle = d3_node.attr('stroke') || ctx.fillStyle; - ctx.fillRect(rectpt.x /*+ d3_node.attr('x')*/, rectpt.y/* + d3_node.attr('y')*/, d3_node.attr('width'), d3_node.attr('height')); + OncoprintRenderer.prototype.getCellAreaHeight = function() { + var height = 0; + var self = this; + _.each(this.oncoprint.getTrackOrder(), function(track_id) { + height += self.getTrackRenderHeight(track_id); + }); + return height; + }; + OncoprintRenderer.prototype.getLabelAreaWidth = function() { + var label_font = this.getLabelFont(); + var labels = _.map(this.oncoprint.getTrackOrder(), this.oncoprint.getTrackLabel); + var label_widths = _.map(labels, function(label) { + return utils.textWidth(label, label_font); + }); + var max_label_width = Math.max(_.max(label_widths), 0); + var max_percent_altered_width = utils.textWidth('100%', label_font); + var buffer_width = 20; + return max_label_width + buffer_width + max_percent_altered_width ; + }; + OncoprintRenderer.prototype.getLabelAreaHeight = function() { + return this.getCellAreaHeight(); + }; + OncoprintRenderer.prototype.render = function() { + throw "not implemented in abstract class"; + } + return OncoprintRenderer; +})(); + +var OncoprintSVGRenderer = (function() { + function OncoprintSVGRenderer(oncoprint, config) { + OncoprintRenderer.call(this, oncoprint, config); + this.label_svg = utils.makeD3SVGElement('svg'); + this.cell_svg = utils.makeD3SVGElement('svg'); + this.label_container; + this.cell_container; + + var render_events = [events.ADD_TRACK, events.REMOVE_TRACK, events.MOVE_TRACK, events.SORT, events.SET_CELL_PADDING, + events.SET_CELL_WIDTH, events.SET_TRACK_DATA]; + var self = this; + $(oncoprint).on(render_events.join(" "), function() { + self.render(); + }); + } + utils.extends(OncoprintSVGRenderer, OncoprintRenderer); + OncoprintSVGRenderer.prototype.getParentViewRect = function() { + var parent = this.cell_svg.node().parentNode; + var parentRect = parent.getBoundingClientRect(); + return {x: parent.scrollLeft, y: parent.scrollTop, width: parentRect.right - parentRect.left, height: parentRect.bottom - parentRect.top}; + }; + OncoprintSVGRenderer.prototype.setRuleSet = function(track_id, type, params) { + OncoprintRenderer.prototype.setRuleSet.call(this, track_id, type, params); + this.render(); + }; + OncoprintSVGRenderer.prototype.useSameRuleSet = function(target_track_id, source_track_id) { + OncoprintRenderer.prototype.useSameRuleSet.call(this, target_track_id, source_track_id); + this.render(); + } + OncoprintSVGRenderer.prototype.getLabelSVG = function() { + return this.label_svg; + }; + OncoprintSVGRenderer.prototype.getCellSVG = function() { + return this.cell_svg; + }; + OncoprintSVGRenderer.prototype.attachLabelSVG = function(container_selector_string) { + this.label_container = d3.select(container_selector_string).append('div').classed('fixed_oncoprint_section_container', true); + var label_svg = this.getLabelSVG(); + this.label_container.select(function () { + return this.appendChild(label_svg.node()); + }); + }; + OncoprintSVGRenderer.prototype.attachCellSVG = function(container_selector_string) { + this.cell_container = d3.select(container_selector_string).append('div').classed('scrolling_oncoprint_section_container', true); + var cell_svg = this.getCellSVG(); + this.cell_container.select(function() { + return this.appendChild(cell_svg.node()); + }); + var self = this; + $(this.cell_container.node()).on('scroll', function() { + self.render(); + }); + }; + OncoprintSVGRenderer.prototype.renderTrackLabel = function(oncoprint, track_id, rule_set, svg) { + var label_class = 'label'+track_id; + var label_y = this.getTrackRenderTop(track_id); + svg.selectAll('.'+label_class).remove(); + svg.append('text').classed(label_class, true).text(oncoprint.getTrackLabel(track_id)) + .attr('alignment-baseline', 'hanging') + .attr('transform', utils.translate(0, label_y)); + if (rule_set.alteredData && typeof rule_set.alteredData === 'function') { + var data = oncoprint.getTrackData(track_id); + var num_altered = rule_set.alteredData(data).length; + var percent_altered = Math.floor(100*num_altered/data.length); + svg.append('text').classed(label_class, true) + .text(percent_altered+'%') + .attr('text-anchor', 'end') + .attr('alignment-baseline', 'hanging') + .attr('transform', utils.translate(this.getLabelAreaWidth(), label_y)); } }; - setInterval(renderCellsToCanvas, 1000/60); - var renderTrackCells = function(track_id, rule_set) { + OncoprintSVGRenderer.prototype.renderTrackCells = function(oncoprint, track_id, rule_set, svg) { + // TODO: have option to only alter position of cells that already exist, for scrolling, and only render enter()'ing cells + // TODO: transition new cells from position to left or right of screen, not from origin var data = oncoprint.getTrackData(track_id); var id_accessor = oncoprint.getTrackDatumIdAccessor(track_id); if (!id_accessor) { - console.log("No id accessor found for track id "+track_id); - return; + return false; } - var track_y = trackY(track_id); var id_order = utils.invert_array(oncoprint.getIdOrder()); + var view_rect = this.getParentViewRect(); var cell_width = oncoprint.getCellWidth(); - var cell_height = oncoprint.getCellHeight(track_id); - - (function updateSVG() { - self.cell_svg - .attr('width', cellSvgWidth()) - .attr('height', cellSvgHeight()); + var cell_padding = oncoprint.getCellPadding(); - })(); var bound_g = (function createAndRemoveGroups() { var cell_class = 'cell'+track_id; - - var bound_g = self.cell_svg.selectAll('g.'+cell_class).data(data, id_accessor); + data = data.filter(function(d,i) { + var position = id_order[id_accessor(d)]*(cell_width + cell_padding); + var xlim = [view_rect.x, view_rect.x + view_rect.width]; + return position >= xlim[0] && position < xlim[1]; + }); + var bound_g = svg.selectAll('g.'+cell_class).data(data, id_accessor); bound_g.enter().append('g').classed(cell_class, true); bound_g.exit().remove(); return bound_g; })(); + var self = this; (function positionGroups() { - bound_g.transition().attr('transform', function(d, i) { - var x = id_order[id_accessor(d)]*(oncoprint.getCellWidth() + oncoprint.getCellPadding()); - var y = track_y + oncoprint.getTrackPadding(track_id); - return utils.translate(x, y); + bound_g.transition().attr('transform', function(d,i) { + var pos = self.getCellPos(track_id, id_accessor(d)); + return utils.translate(pos.x, pos.y); }); })(); - (function cleanGroups() { - bound_g.selectAll('*').remove(); - })(); (function renderCells() { - rule_set.apply(self.cell_svg, bound_g, data, id_accessor, cell_width, cell_height); + bound_g.selectAll('*').remove(); + rule_set.apply(svg, bound_g, data, id_accessor, oncoprint.getCellWidth(), oncoprint.getCellHeight(track_id)); })(); }; - - var trackY = function(track_id) { - var y = 0; - _.find(oncoprint.getTrackOrder(), function(id) { - if (id === track_id) { - return true; - } else { - y += renderedTrackHeight(id); - return false; + OncoprintSVGRenderer.prototype.render = function() { + var self = this; + this.cell_svg.attr('width', this.getCellAreaWidth()) + .attr('height', this.getCellAreaHeight()); + this.label_svg.attr('width', this.getLabelAreaWidth()) + .attr('height', this.getLabelAreaHeight()); + _.each(this.oncoprint.getTrackOrder(), function(track_id) { + var rule_set = self.getRuleSet(track_id); + if (rule_set) { + self.renderTrackLabel(self.oncoprint, track_id, rule_set, self.getLabelSVG()); + self.renderTrackCells(self.oncoprint, track_id, rule_set, self.getCellSVG()); + //renderTrackLegend(self.oncoprint, track_id, rule_set, self.getLegendSVG()); } }); - return y; }; - - self.mousePosToCell = function(x,y) { - // TODO: centralize all of these coordinate stuff...shouldn't be calculating them twice in two different functions D: - var yInTrack = function(track_id, y) { - var track_y = trackY(track_id); - var track_padding = oncoprint.getTrackPadding(track_id); - var track_height = oncoprint.getTrackHeight(track_id); - return y >= track_y + track_padding && y <= track_y + track_padding + track_height; - }; - var track_id = false; - _.find(oncoprint.getTrackOrder(), function(id) { - if (yInTrack(id, y)) { - track_id = id; - return true; - } - return false; + return OncoprintSVGRenderer; +})(); + +var CanvasSVGDrawer = (function() { + function CanvasSVGDrawer(canvas, svg) { + this.canvas = canvas; + this.ctx = this.canvas.node().getContext('2d'); + this.svg = svg; + this.clipping_buffer = 300; + setInterval($.proxy(this.draw, this), 1000/60); + var self = this; + $(this.canvas.node().parentNode).on('scroll', function() { + self.draw(); }); - if (track_id === false) { - return undefined; - } else { - var cell_width = oncoprint.getCellWidth(); - var cell_padding = oncoprint.getCellPadding(); - var cell_index = Math.floor(x / (cell_padding +cell_width)); - var in_cell = (x % (cell_padding + cell_width)) < cell_width; - if (in_cell) { - var datum_id = oncoprint.getIdOrder()[cell_index]; - var datum = oncoprint.getTrackDatum(track_id, datum_id); - var cell_x = cell_index*(cell_padding + cell_width); // TODO: centralize it - var cell_y = trackY(track_id)+oncoprint.getTrackPadding(track_id);// TODO: centralize it - return {datum: datum, x: cell_x, y: cell_y}; - } else { - return undefined; - } + } + var containsPoint = function(x, y, rect) { + return x >= rect.x && y >= rect.y && x < rect.x + rect.width && y < rect.y + rect.height; + }; + CanvasSVGDrawer.prototype.draw = function() { + this.canvas.attr('width', this.svg.attr('width')) + .attr('height', this.svg.attr('height')); + var canvas_node = this.canvas.node(); + var ctx = this.ctx; + ctx.clearRect(0, 0, canvas_node.width, canvas_node.height); + ctx.beginPath(); + var view_rect = this.getParentViewRect(); + for (var child = this.svg.node().firstChild; child; child = child.nextSibling) { + this.drawNode(child, view_rect); } + ctx.stroke(); + ctx.closePath(); }; - var renderedTrackHeight = function(track_id) { - return oncoprint.getTrackHeight(track_id) + 2*oncoprint.getTrackPadding(track_id); - }; - - var cellSvgWidth = function() { - return (oncoprint.getCellWidth() + oncoprint.getCellPadding())*oncoprint.getIdOrder().length; - }; - - var cellSvgHeight = function() { - return _.reduce(oncoprint.getTrackOrder(), function(memo, track_id) { - return memo + renderedTrackHeight(track_id); - }, 0); - }; - var labelSvgHeight = function() { - return cellSvgHeight(); - }; - var labelSvgWidth = function() { - return 100; + CanvasSVGDrawer.prototype.getParentViewRect = function() { + var parent = this.canvas.node().parentNode; + var parentRect = parent.getBoundingClientRect(); + return {x: parent.scrollLeft, y: parent.scrollTop, width: parentRect.right - parentRect.left, height: parentRect.bottom - parentRect.top}; }; - - self.setClipping = function(c) { - self.clipping = c; + CanvasSVGDrawer.prototype.drawNode = function(node, view_rect) { + var d3_node = d3.select(node); + var ctx = this.ctx; + var pos = this.svg.node().createSVGPoint(); + pos = pos.matrixTransform(node.getCTM()); + if (containsPoint(pos.x, pos.y, view_rect)) { + switch (node.tagName) { + case 'g': + for (var child = node.firstChild; child; child = child.nextSibling) { + this.drawNode(child, view_rect); + } + case 'rect': + ctx.fillStyle = d3_node.attr('fill'); + ctx.strokeStyle = d3_node.attr('stroke') || ctx.fillStyle; + ctx.fillRect(pos.x, pos.y, d3_node.attr('width'), d3_node.attr('height')); + } + } }; -} + return CanvasSVGDrawer; +})(); +// function OncoprintSVGRenderer(container_selector_string, oncoprint) { +// OncoprintRenderer.call(this, oncoprint); +// var self = this; +// self.container = d3.select(container_selector_string).classed('oncoprint_container', true); +// self.label_svg; +// self.cell_svg; +// self.legend_table; +// self.legend_svg; +// self.rule_sets = {}; +// self.clipping = true; + +// (function init() { +// self.container.selectAll('*').remove(); +// self.label_svg = self.container.append('div').classed('fixed_oncoprint_section_container', true).append('svg') +// .attr('width', 100).attr('xmlns', "http://www.w3.org/2000/svg"); +// self.cell_canvas = self.container.append('div').classed('scrolling_oncoprint_section_container', true).append('canvas').attr('id', 'cell_canvas').attr('width', 1000).attr('height', 200); +// self.cell_canvas.node().addEventListener('mousemove', function(evt) { +// $(self).off(events.FINISHED_RENDERING); +// var canvas_rect = self.cell_canvas.node().getBoundingClientRect(); +// var x = evt.clientX - canvas_rect.left; +// var y = evt.clientY - canvas_rect.top; +// var cell = self.mousePosToCell(x,y); +// if (cell) { +// $(self).on(events.FINISHED_RENDERING, function() { +// self.cell_canvas_ctx.fillStyle = '#ff0000'; +// self.cell_canvas_ctx.fillRect(cell.x, cell.y, 2, 2); +// console.log(cell.datum); +// }); +// } +// }); +// self.cell_canvas_ctx = self.cell_canvas.node().getContext('2d'); +// self.cell_svg = utils.makeD3SVGElement('svg');//self.container.append('div').classed('scrolling_oncoprint_section_container', true).append('svg').attr('xmlns', "http://www.w3.org/2000/svg"); +// self.legend_table = self.container.append('table'); +// })(); + +// var render_events = [events.ADD_TRACK, events.REMOVE_TRACK, events.MOVE_TRACK, events.SORT, events.SET_CELL_PADDING, +// events.SET_CELL_WIDTH, events.SET_TRACK_DATA]; +// $(oncoprint).on(render_events.join(" "), function() { +// self.renderTracks(); +// }); + +// self.toSVG = function(ctr) { +// ctr.attr('width', 2000); +// ctr.attr('height', 1000); +// var svg = ctr.append('svg').attr('xmlns', "http://www.w3.org/2000/svg"); +// //var svg = utils.makeD3SVGElement('svg'); +// var vertical_padding = 5; +// utils.appendD3SVGElement(self.label_svg, svg); +// utils.appendD3SVGElement(self.cell_svg, svg).attr('x',+self.label_svg.attr('width')); +// var legend_row_y = +self.label_svg.attr('height') + vertical_padding; +// self.legend_table.selectAll('tr').selectAll('svg').each(function() { +// var d3_elt = d3.select(this); +// utils.appendD3SVGElement(d3_elt, svg).attr('y', legend_row_y); +// console.log(legend_row_y); +// legend_row_y += +d3_elt.attr('height'); +// }); +// if (ctr) { +// utils.appendD3SVGElement(svg, ctr); +// } +// return svg; +// }; + +// self.renderTracks = function() { +// _.each(oncoprint.getTrackOrder(), function(track_id, ind) { +// renderTrackLabel(track_id); +// var rule_set = self.getRuleSet(track_id); +// if (!rule_set) { +// console.log("No rule set found for track id "+track_id); +// return; +// } +// renderTrackCells(track_id, self.getRuleSet(track_id)); +// }); + +// (function renderLegend() { +// self.legend_table.selectAll('*').remove(); +// _.each(oncoprint.getTrackOrder(), function(track_id) { +// var rule_set = self.getRuleSet(track_id); +// if (!rule_set) { +// console.log("No rule set found for track id "+track_id); +// return; +// } +// if (!rule_set.isLegendRendered()) { +// var svg = self.legend_table.append('tr').append('svg').attr('width', 1000).attr('height', 50).attr('xmlns', "http://www.w3.org/2000/svg"); +// rule_set.putLegendGroup(svg, oncoprint.getCellWidth(), 20); // TODO: get actual cell height +// rule_set.markLegendRendered(); +// } +// }); +// _.each(oncoprint.getTrackOrder(), function(track_id) { +// var rule_set = self.getRuleSet(track_id); +// if (rule_set) { +// rule_set.unmarkLegendRendered(); +// } +// }); +// })(); + +// }; + +// var renderTrackLabel = function(track_id) { +// var label_class = 'label'+track_id; +// var track_y = trackY(track_id); +// var label_y = track_y + oncoprint.getTrackPadding(track_id); // TODO: centralize it +// self.label_svg +// .attr('width', labelSvgWidth()) +// .attr('height', labelSvgHeight()); +// self.label_svg.selectAll('.'+label_class).remove(); +// self.label_svg.append('text').classed(label_class, true).text(oncoprint.getTrackLabel(track_id)) +// //.attr('x', 0) +// //.attr('y', track_y) +// .attr('transform', utils.translate(0, label_y)) +// .attr('alignment-baseline', 'hanging'); + +// var track_rule_set = self.getRuleSet(track_id); +// if (!track_rule_set) { +// console.log("No rule set found for track id "+track_id); +// return; +// } +// var track_data = oncoprint.getTrackData(track_id); +// if (track_rule_set.alteredData) { +// var percent_altered = 100*(track_rule_set.alteredData(track_data).length / track_data.length); +// self.label_svg.append('text').classed(label_class, true) +// .attr('text-anchor', 'end') +// .text(Math.floor(percent_altered)+'%') +// .attr('alignment-baseline', 'hanging') +// //.attr('x', labelSvgWidth()) +// //.attr('y', track_y); +// .attr('transform', utils.translate(labelSvgWidth(), label_y)); +// } + +// }; +// var renderCellsToCanvas = function() { +// var ctx = self.cell_canvas_ctx; +// ctx.clearRect(0,0, self.cell_canvas.node().width, self.cell_canvas.node().height); +// ctx.beginPath(); +// var toprint = 5; +// for (var child = self.cell_svg.node().firstChild; child; child = child.nextSibling) { +// renderCellToCanvas(child); +// } +// ctx.stroke(); +// ctx.closePath(); +// $(self).trigger(events.FINISHED_RENDERING); +// }; +// var pointInCanvas = function(x, y) { +// var canvas_node = self.cell_canvas.node(); +// var x_lim = self.cell_canvas.attr('width'); +// var y_lim = self.cell_canvas.attr('height'); +// return x <= x_lim && x >= 0 && y <= y_lim && y >= 0; +// }; +// var renderCellToCanvas = function(node) { +// var ctx = self.cell_canvas_ctx; +// var rectpt = self.cell_svg.node().createSVGPoint(); +// var d3_node = d3.select(node); +// rectpt = rectpt.matrixTransform(node.getCTM()); +// if (self.clipping && !pointInCanvas(rectpt.x, rectpt.y)) { +// return; +// } +// switch (node.tagName) { +// case 'g': +// for (var child=node.firstChild; child; child = child.nextSibling) { +// renderCellToCanvas(child); +// } +// break; +// case 'rect': +// ctx.fillStyle = d3_node.attr('fill'); +// ctx.strokeStyle = d3_node.attr('stroke') || ctx.fillStyle; +// ctx.fillRect(rectpt.x /*+ d3_node.attr('x')*/, rectpt.y/* + d3_node.attr('y')*/, d3_node.attr('width'), d3_node.attr('height')); + +// } +// }; +// setInterval(renderCellsToCanvas, 1000/60); +// var renderTrackCells = function(track_id, rule_set) { +// var data = oncoprint.getTrackData(track_id); +// var id_accessor = oncoprint.getTrackDatumIdAccessor(track_id); +// if (!id_accessor) { +// console.log("No id accessor found for track id "+track_id); +// return; +// } +// var track_y = trackY(track_id); +// var id_order = utils.invert_array(oncoprint.getIdOrder()); +// var cell_width = oncoprint.getCellWidth(); +// var cell_height = oncoprint.getCellHeight(track_id); + +// (function updateSVG() { +// self.cell_svg +// .attr('width', cellSvgWidth()) +// .attr('height', cellSvgHeight()); + +// })(); +// var bound_g = (function createAndRemoveGroups() { +// var cell_class = 'cell'+track_id; + +// var bound_g = self.cell_svg.selectAll('g.'+cell_class).data(data, id_accessor); +// bound_g.enter().append('g').classed(cell_class, true); +// bound_g.exit().remove(); +// return bound_g; +// })(); +// (function positionGroups() { +// bound_g.transition().attr('transform', function(d, i) { +// var x = id_order[id_accessor(d)]*(oncoprint.getCellWidth() + oncoprint.getCellPadding()); +// var y = track_y + oncoprint.getTrackPadding(track_id); +// return utils.translate(x, y); +// }); +// })(); +// (function cleanGroups() { +// bound_g.selectAll('*').remove(); +// })(); +// (function renderCells() { +// rule_set.apply(self.cell_svg, bound_g, data, id_accessor, cell_width, cell_height); +// })(); +// }; + +// var trackY = function(track_id) { +// var y = 0; +// _.find(oncoprint.getTrackOrder(), function(id) { +// if (id === track_id) { +// return true; +// } else { +// y += renderedTrackHeight(id); +// return false; +// } +// }); +// return y; +// }; + +// self.mousePosToCell = function(x,y) { +// // TODO: centralize all of these coordinate stuff...shouldn't be calculating them twice in two different functions D: +// var yInTrack = function(track_id, y) { +// var track_y = trackY(track_id); +// var track_padding = oncoprint.getTrackPadding(track_id); +// var track_height = oncoprint.getTrackHeight(track_id); +// return y >= track_y + track_padding && y <= track_y + track_padding + track_height; +// }; +// var track_id = false; +// _.find(oncoprint.getTrackOrder(), function(id) { +// if (yInTrack(id, y)) { +// track_id = id; +// return true; +// } +// return false; +// }); +// if (track_id === false) { +// return undefined; +// } else { +// var cell_width = oncoprint.getCellWidth(); +// var cell_padding = oncoprint.getCellPadding(); +// var cell_index = Math.floor(x / (cell_padding +cell_width)); +// var in_cell = (x % (cell_padding + cell_width)) < cell_width; +// if (in_cell) { +// var datum_id = oncoprint.getIdOrder()[cell_index]; +// var datum = oncoprint.getTrackDatum(track_id, datum_id); +// var cell_x = cell_index*(cell_padding + cell_width); // TODO: centralize it +// var cell_y = trackY(track_id)+oncoprint.getTrackPadding(track_id);// TODO: centralize it +// return {datum: datum, x: cell_x, y: cell_y}; +// } else { +// return undefined; +// } +// } +// }; +// var renderedTrackHeight = function(track_id) { +// return oncoprint.getTrackHeight(track_id) + 2*oncoprint.getTrackPadding(track_id); +// }; + +// var cellSvgWidth = function() { +// return (oncoprint.getCellWidth() + oncoprint.getCellPadding())*oncoprint.getIdOrder().length; +// }; + +// var cellSvgHeight = function() { +// return _.reduce(oncoprint.getTrackOrder(), function(memo, track_id) { +// return memo + renderedTrackHeight(track_id); +// }, 0); +// }; +// var labelSvgHeight = function() { +// return cellSvgHeight(); +// }; +// var labelSvgWidth = function() { +// return 100; +// }; + +// self.setClipping = function(c) { +// self.clipping = c; +// }; +// } +// OncoprintSVGRenderer.prototype = Object.create(OncoprintRenderer.prototype); diff --git a/packages/oncoprintjs/src/js/utils.js b/packages/oncoprintjs/src/js/utils.js index b2745b4c7de..710108c424a 100644 --- a/packages/oncoprintjs/src/js/utils.js +++ b/packages/oncoprintjs/src/js/utils.js @@ -28,6 +28,7 @@ * along with this program. If not, see . */ var _ = require("underscore"); +var $ = require("jquery"); var exports = module.exports = {}; @@ -38,6 +39,11 @@ exports.invert_array = function invert_array(arr) { }, {}); }; +exports.extends = function(child_class, parent_class) { + child_class.prototype = Object.create(parent_class.prototype); + child_class.prototype.constructor = child_class; +}; + exports.makeD3SVGElement = function(tag) { return d3.select(document.createElementNS('http://www.w3.org/2000/svg', tag)); }; @@ -65,6 +71,17 @@ exports.spaceSVGElementsHorizontally = function(group, padding) { return group; }; +exports.textWidth = function(string, font) { + var obj = $('
'+string+'
') + .css({position: 'absolute', float: 'left', + 'white-space':'nowrap', visibility: 'hidden', + font: font}) + .appendTo($('body')); + var width = obj.width(); + obj.remove(); + return width; +}; + exports.d3SelectChildren = function(parent, selector) { return parent.selectAll(selector).filter(function() { return this.parentNode === parent.node(); diff --git a/packages/oncoprintjs/test/index.html b/packages/oncoprintjs/test/index.html index b53fd97697e..0a61f722f96 100644 --- a/packages/oncoprintjs/test/index.html +++ b/packages/oncoprintjs/test/index.html @@ -13,7 +13,7 @@ - + diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index 6f39156976d..a6e3d4f50ae 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -72,11 +72,11 @@ $.when(gender_data_promise).then(function() { } }); onc.setTrackData(gender_track_id, gender_data); - //for (var i=0; i<3; i++) { + for (var i=0; i<10; i++) { var dup_gender_track_id = onc.addTrack({label: 'Gender'}); onc.useSameRuleSet(dup_gender_track_id, gender_track_id); onc.setTrackData(dup_gender_track_id, gender_data); - //} + } }); mutation_data_promise.then(function(data) { From b53ab02f761b738421ed6a3ac8cc600a001f332a Mon Sep 17 00:00:00 2001 From: Adam Abeshouse Date: Fri, 19 Jun 2015 17:39:08 -0400 Subject: [PATCH 064/343] Adding DS_Store to gitignore, and new SVG rendering strategy (every element gets an SVG) and new clipping strategy (display none or initial) --- packages/oncoprintjs/.gitignore | 5 ++ packages/oncoprintjs/src/css/oncoprint.css | 9 ++ packages/oncoprintjs/src/js/oncoprint.js | 97 ++++++++++++++++++---- packages/oncoprintjs/test/js/test_page.js | 4 +- 4 files changed, 99 insertions(+), 16 deletions(-) diff --git a/packages/oncoprintjs/.gitignore b/packages/oncoprintjs/.gitignore index bab8de9a0e6..84e9efa8139 100644 --- a/packages/oncoprintjs/.gitignore +++ b/packages/oncoprintjs/.gitignore @@ -43,3 +43,8 @@ Session.vim # Compiled assets dist + +# DS_Store +.DS_Store +src/.DS_Store +test/.DS_Store diff --git a/packages/oncoprintjs/src/css/oncoprint.css b/packages/oncoprintjs/src/css/oncoprint.css index a8ee755ffe8..a1acb173979 100644 --- a/packages/oncoprintjs/src/css/oncoprint.css +++ b/packages/oncoprintjs/src/css/oncoprint.css @@ -12,6 +12,15 @@ width: auto; } +.cell_div { + background-color: #EEE; + position: relative; +} + +.cell { + position: absolute; +} + .scrolling_oncoprint_table_container table td { padding: 0; margin: 0; diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index c7f4cf70077..776d8b4e6d7 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -272,6 +272,9 @@ var OncoprintRenderer = (function() { OncoprintRenderer.prototype.getTrackRenderHeight = function(track_id) { return this.oncoprint.getTrackHeight(track_id) + 2*this.oncoprint.getTrackPadding(track_id); }; + OncoprintRenderer.prototype.getCellX = function(index) { + return index*(this.oncoprint.getCellWidth()+this.oncoprint.getCellPadding()); + }; OncoprintRenderer.prototype.getCellPos = function(track_id, datum_id) { var index = this.oncoprint.getIdOrder().indexOf(datum_id); if (index > -1) { @@ -319,13 +322,17 @@ var OncoprintSVGRenderer = (function() { this.cell_svg = utils.makeD3SVGElement('svg'); this.label_container; this.cell_container; + this.cell_div; - var render_events = [events.ADD_TRACK, events.REMOVE_TRACK, events.MOVE_TRACK, events.SORT, events.SET_CELL_PADDING, - events.SET_CELL_WIDTH, events.SET_TRACK_DATA]; + var render_events = [events.ADD_TRACK, events.REMOVE_TRACK, events.SET_TRACK_DATA]; + var reposition_events = [events.MOVE_TRACK, events.SORT, events.SET_CELL_PADDING, events.SET_CELL_WIDTH]; var self = this; $(oncoprint).on(render_events.join(" "), function() { self.render(); }); + $(oncoprint).on(reposition_events.join(" "), function() { + self.repositionCells(); + }); } utils.extends(OncoprintSVGRenderer, OncoprintRenderer); @@ -357,14 +364,21 @@ var OncoprintSVGRenderer = (function() { }; OncoprintSVGRenderer.prototype.attachCellSVG = function(container_selector_string) { this.cell_container = d3.select(container_selector_string).append('div').classed('scrolling_oncoprint_section_container', true); + this.cell_div = this.cell_container.append('div').classed('cell_div', true); var cell_svg = this.getCellSVG(); this.cell_container.select(function() { return this.appendChild(cell_svg.node()); }); var self = this; + $(this.cell_container.node()).on('scroll', function() { - self.render(); + //TODO: need to do this more smartly --- dont do it on + // every scroll, just when you need it + // TODO: separate into repositioning and reclipping + self.repositionCells(); }); + // TODO: delete this if you want it back + cell_svg.style('display', 'none'); }; OncoprintSVGRenderer.prototype.renderTrackLabel = function(oncoprint, track_id, rule_set, svg) { var label_class = 'label'+track_id; @@ -396,35 +410,90 @@ var OncoprintSVGRenderer = (function() { var view_rect = this.getParentViewRect(); var cell_width = oncoprint.getCellWidth(); var cell_padding = oncoprint.getCellPadding(); - - var bound_g = (function createAndRemoveGroups() { - var cell_class = 'cell'+track_id; + var cell_div = this.cell_div; + var cell_height = oncoprint.getCellHeight(track_id); + + //var bound_g + var bound_svg = (function createAndRemoveGroups() { + var cell_class = 'cell'; + var track_cell_class = 'cell'+track_id; + + /* CLIPPING data = data.filter(function(d,i) { var position = id_order[id_accessor(d)]*(cell_width + cell_padding); var xlim = [view_rect.x, view_rect.x + view_rect.width]; return position >= xlim[0] && position < xlim[1]; - }); - var bound_g = svg.selectAll('g.'+cell_class).data(data, id_accessor); - bound_g.enter().append('g').classed(cell_class, true); - bound_g.exit().remove(); - return bound_g; + });*/ + var bound_svg = cell_div.selectAll('svg.'+track_cell_class).data(data, id_accessor); + bound_svg.enter().append('svg').classed(track_cell_class, true).classed(cell_class, true) + .style('width', cell_width).style('height', cell_height); + //var bound_g = svg.selectAll('g.'+cell_class).data(data, id_accessor); + //bound_g.enter().append('g').classed(cell_class, true); + //bound_g.exit().remove(); + //return bound_g; + return bound_svg; })(); var self = this; (function positionGroups() { - bound_g.transition().attr('transform', function(d,i) { + /*bound_g.transition().attr('transform', function(d,i) { var pos = self.getCellPos(track_id, id_accessor(d)); return utils.translate(pos.x, pos.y); + });*/ + bound_svg.transition().style('left', function(d,i) { + var pos = self.getCellPos(track_id, id_accessor(d)); + return pos.x; + }).style('top', function(d,i) { + var pos = self.getCellPos(track_id, id_accessor(d)); + return pos.y; }); })(); (function renderCells() { - bound_g.selectAll('*').remove(); - rule_set.apply(svg, bound_g, data, id_accessor, oncoprint.getCellWidth(), oncoprint.getCellHeight(track_id)); + //bound_g.selectAll('*').remove(); + //rule_set.apply(svg, bound_g, data, id_accessor, oncoprint.getCellWidth(), oncoprint.getCellHeight(track_id)); + bound_svg.selectAll('*').remove(); + rule_set.apply(svg, bound_svg, data, id_accessor, oncoprint.getCellWidth(), oncoprint.getCellHeight(track_id)); })(); }; + OncoprintSVGRenderer.prototype.repositionTrackCells = function(oncoprint, track_id) { + var data = oncoprint.getTrackData(track_id); + var id_accessor = oncoprint.getTrackDatumIdAccessor(track_id); + var view_rect = this.getParentViewRect(); + var self = this; + if (!id_accessor) { + return false; + } + var cell_div = this.cell_div; + + var track_cell_class = 'cell'+track_id; + var bound_svg = cell_div.selectAll('svg.'+track_cell_class).data(data, id_accessor); + + // TODO: find x as a set function of i, that will make this faster, and clipping faster + bound_svg.transition().style('left', function(d,i) { + return self.getCellX(i); + }).style('top', function(d,i) { + return self.getTrackRenderTop(track_id); + }).style('display', function(d,i) { + var position = self.getCellX(i); + var xlim = [view_rect.x, view_rect.x + view_rect.width]; + var ret = (position >= xlim[0] && position < xlim[1]) ? 'initial' : 'none'; + return ret; + }); + }; + OncoprintSVGRenderer.prototype.repositionCells = function() { + var oncoprint = this.oncoprint; + var self = this; + _.each(oncoprint.getTrackOrder(), function(track_id) { + self.repositionTrackCells(oncoprint, track_id); + }); + } OncoprintSVGRenderer.prototype.render = function() { var self = this; this.cell_svg.attr('width', this.getCellAreaWidth()) .attr('height', this.getCellAreaHeight()); + this.cell_div.style('min-width', this.getCellAreaWidth()+'px') + .style('min-height', this.getCellAreaHeight()+'px'); + //this.cell_div.append('svg').classed('cell', true).style('left', '10').style('top','5').style('width','10').style('height','10') + //.append('rect').attr('width', '10').attr('height','10').attr('fill', '#ff0000'); this.label_svg.attr('width', this.getLabelAreaWidth()) .attr('height', this.getLabelAreaHeight()); _.each(this.oncoprint.getTrackOrder(), function(track_id) { diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index a6e3d4f50ae..f821c11c0e2 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -72,11 +72,11 @@ $.when(gender_data_promise).then(function() { } }); onc.setTrackData(gender_track_id, gender_data); - for (var i=0; i<10; i++) { + //for (var i=0; i<10; i++) { var dup_gender_track_id = onc.addTrack({label: 'Gender'}); onc.useSameRuleSet(dup_gender_track_id, gender_track_id); onc.setTrackData(dup_gender_track_id, gender_data); - } + //} }); mutation_data_promise.then(function(data) { From 0dc892b3551df491d22a76b92f45f4fc2269f0c9 Mon Sep 17 00:00:00 2001 From: Adam Abeshouse Date: Mon, 22 Jun 2015 10:49:18 -0400 Subject: [PATCH 065/343] beginning of clever positioning separation --- packages/oncoprintjs/src/js/oncoprint.js | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 776d8b4e6d7..bf72781840a 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -375,7 +375,8 @@ var OncoprintSVGRenderer = (function() { //TODO: need to do this more smartly --- dont do it on // every scroll, just when you need it // TODO: separate into repositioning and reclipping - self.repositionCells(); + //self.repositionCells(); + self.clipCells(); }); // TODO: delete this if you want it back cell_svg.style('display', 'none'); @@ -454,6 +455,26 @@ var OncoprintSVGRenderer = (function() { rule_set.apply(svg, bound_svg, data, id_accessor, oncoprint.getCellWidth(), oncoprint.getCellHeight(track_id)); })(); }; + OncoprintSVGRenderer.prototype.clipCells = function() { + var self = this; + var view_rect = this.getParentViewRect(); + _.each(this.oncoprint.getTrackOrder(), function(track_id) { + var data = self.oncoprint.getTrackData(track_id); + var id_accessor = self.oncoprint.getTrackDatumIdAccessor(track_id); + if (!id_accessor) { + return false; + } + + var track_cell_class = 'cell'+track_id; + var bound_svg = self.cell_div.selectAll('svg.'+track_cell_class).data(data, id_accessor); + bound_svg.style('display', function(d,i) { + var position = self.getCellX(i); + var xlim = [view_rect.x, view_rect.x + view_rect.width]; + var ret = (position >= xlim[0] && position < xlim[1]) ? 'initial' : 'none'; + return ret; + }); + }); + }; OncoprintSVGRenderer.prototype.repositionTrackCells = function(oncoprint, track_id) { var data = oncoprint.getTrackData(track_id); var id_accessor = oncoprint.getTrackDatumIdAccessor(track_id); From 801dcb6482461f2a0b904e3026fb6a270cb635c7 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Mon, 22 Jun 2015 16:35:57 -0400 Subject: [PATCH 066/343] Fast clipping and code cleanup Code cleanup: * Fixed utils.stableSort so it actually stably sorts * Deleted legacy SVGRenderer code that was commented out * Took svg argument out of apply functions in RuleSet.js Fast clipping * VisibleIndexBounds strategy * Note that only 2 VisibleIndexBounds objects are ever created - this is for speed Finally, implemented gender track sort in test page for demoing --- packages/oncoprintjs/src/js/RuleSet.js | 12 +- packages/oncoprintjs/src/js/events.js | 4 +- packages/oncoprintjs/src/js/oncoprint.js | 630 +++++++--------------- packages/oncoprintjs/src/js/utils.js | 36 +- packages/oncoprintjs/test/js/test_page.js | 2 +- 5 files changed, 212 insertions(+), 472 deletions(-) diff --git a/packages/oncoprintjs/src/js/RuleSet.js b/packages/oncoprintjs/src/js/RuleSet.js index 055600c1952..bcbae7f9b81 100644 --- a/packages/oncoprintjs/src/js/RuleSet.js +++ b/packages/oncoprintjs/src/js/RuleSet.js @@ -64,11 +64,11 @@ var D3SVGRuleSet = (function() { var sorted_rules = _.sortBy(rules, function(r) { return r.z_index; }); return sorted_rules; }; - D3SVGRuleSet.prototype.apply = function(svg, g, data, datum_id_accessor, cell_width, cell_height) { + D3SVGRuleSet.prototype.apply = function(g, data, datum_id_accessor, cell_width, cell_height) { _.each(this.getRules(), function(rule) { var affected_data = rule.filterData(data); var affected_groups = g.data(affected_data, datum_id_accessor); - rule.apply(svg, affected_groups, cell_width, cell_height); + rule.apply(affected_groups, cell_width, cell_height); }); }; D3SVGRuleSet.prototype.getRule = function(rule_id) { @@ -108,7 +108,7 @@ function D3SVGCategoricalColorRuleSet(params) { addColorRule(color, category); }); - self.apply = function(svg, g, data, datum_id_accessor, cell_width, cell_height) { + self.apply = function(g, data, datum_id_accessor, cell_width, cell_height) { var missing_categories = []; _.each(data, function(datum) { var category = params.getCategory(datum); @@ -118,7 +118,7 @@ function D3SVGCategoricalColorRuleSet(params) { addColorRule(new_color, category); } }); - D3SVGRuleSet.prototype.apply.call(this, svg, g, data, datum_id_accessor, cell_width, cell_height); + D3SVGRuleSet.prototype.apply.call(this, g, data, datum_id_accessor, cell_width, cell_height); }; self.putLegendGroup = function(svg, cell_width, cell_height) { @@ -263,7 +263,7 @@ function D3SVGRule(params, rule_id) { return ret; }; - this.apply = function(svg, g, cell_width, cell_height) { + this.apply = function(g, cell_width, cell_height) { var shape = this.shape; var elts = utils.appendD3SVGElement(shape, g); var attrs = this.attrs || {}; @@ -452,7 +452,7 @@ function D3SVGStaticRule(params, rule_id) { } var group = svg.append('g'); var g = group.append('g'); - this.apply(svg, g, cell_width, cell_height); + this.apply(g, cell_width, cell_height); if (this.legend_label) { group.append('text').text(this.legend_label) .attr('alignment-baseline', 'hanging'); diff --git a/packages/oncoprintjs/src/js/events.js b/packages/oncoprintjs/src/js/events.js index e1d12ebd873..7ba7472ae67 100644 --- a/packages/oncoprintjs/src/js/events.js +++ b/packages/oncoprintjs/src/js/events.js @@ -35,6 +35,7 @@ module.exports = { SET_CELL_PADDING: 'set_cell_padding.oncoprint', SET_CELL_WIDTH: 'set_cell_width.oncoprint', SET_TRACK_DATA: 'set_track_data.oncoprint', + SET_ID_ORDER: 'set_id_order.oncoprint', CELL_CLICK: 'cell_click.oncoprint', CELL_MOUSEENTER: 'cell_mouseenter.oncoprint', CELL_MOUSELEAVE: 'cell_mouseleave.oncoprint', @@ -43,5 +44,6 @@ module.exports = { SET_PRE_TRACK_PADDING: 'set_pre_track_padding.oncoprint', TRACK_INIT: 'init.track.oncoprint', UPDATE_RENDER_RULES: 'update_render_rules.cell_renderer.oncoprint', - FINISHED_RENDERING: 'finished_rendering.renderer.oncoprint' + FINISHED_RENDERING: 'finished_rendering.renderer.oncoprint', + FINISHED_POSITIONING: 'finished_positioning.renderer.oncoprint' }; diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index bf72781840a..91286133dae 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -39,7 +39,7 @@ var RuleSet = require('./RuleSet'); // TODO: use self everywhere var defaultOncoprintConfig = { - cell_width: 5.5, + cell_width: 6, cell_padding: 3, }; @@ -95,6 +95,9 @@ module.exports = { toSVG: function(ctr) { return renderer.toSVG(ctr); }, + sort: function(track_id_list, cmp_list) { + oncoprint.sort(track_id_list, cmp_list); + } }; return ret; } @@ -213,14 +216,14 @@ function Oncoprint(config) { var lexicographically_ordered_cmp = function(id1,id2) { var cmp_result; for (var i=0, _len = track_id_list.length; i<_len; i++) { - cmp_result = cmp_list[i](self.getTrackDatum(id1),self.getTrackDatum(id2)); + cmp_result = cmp_list[i](self.getTrackDatum(track_id_list[i], id1),self.getTrackDatum(track_id_list[i], id2)); if (cmp_result !== 0) { break; } } return cmp_result; }; - self.getIdOrder().sort(lexicographically_ordered_cmp); + self.setIdOrder(utils.stableSort(self.getIdOrder(), lexicographically_ordered_cmp)); $(self).trigger(events.SORT, {id_order: self.id_order}); }; @@ -239,7 +242,12 @@ var OncoprintRenderer = (function() { this.oncoprint = oncoprint; this.config = config; }; - + OncoprintRenderer.prototype.getCellCSSClass = function() { + return 'cell'; + }; + OncoprintRenderer.prototype.getTrackCellCSSClass = function(track_id) { + return 'cell'+track_id; + }; OncoprintRenderer.prototype.getLabelFont = function() { return this.config.label_font; }; @@ -316,28 +324,95 @@ var OncoprintRenderer = (function() { })(); var OncoprintSVGRenderer = (function() { + function VisibleIndexBounds(first, last) { + this.first = first; + this.last = last; + this.toShow = function(new_bounds) { + var ret = []; + var i; + if (new_bounds.first < this.first) { + for (i=new_bounds.first; i < this.first; i++) { + ret.push(i); + } + } + if (new_bounds.last > this.last) { + for (i=this.last + 1; i <= new_bounds.last; i++) { + ret.push(i); + } + } + return ret; + }; + this.toHide = function(new_bounds) { + var ret = []; + var i; + if (new_bounds.first > this.first) { + for (i=this.first; i < new_bounds.first; i++) { + ret.push(i); + } + } + if (new_bounds.last < this.last) { + for (i=new_bounds.last+1; i <= this.last; i++) { + ret.push(i); + } + } + return ret; + }; + this.set = function(first, last) { + this.first = first; + this.last = last; + return this; + }; + } function OncoprintSVGRenderer(oncoprint, config) { OncoprintRenderer.call(this, oncoprint, config); this.label_svg = utils.makeD3SVGElement('svg'); this.cell_svg = utils.makeD3SVGElement('svg'); this.label_container; this.cell_container; + this.cell_container_node; this.cell_div; + this.cells = {}; + this.curr_clip_bounds = new VisibleIndexBounds(-1, -2); + this.prev_clip_bounds = new VisibleIndexBounds(-1, -2); var render_events = [events.ADD_TRACK, events.REMOVE_TRACK, events.SET_TRACK_DATA]; - var reposition_events = [events.MOVE_TRACK, events.SORT, events.SET_CELL_PADDING, events.SET_CELL_WIDTH]; + var reposition_events = [events.MOVE_TRACK, events.SET_CELL_PADDING, events.SET_CELL_WIDTH]; + var reclip_events = [events.SET_CELL_PADDING, events.SET_CELL_WIDTH]; + var reposition_then_reclip_events = [events.SET_ID_ORDER]; var self = this; $(oncoprint).on(render_events.join(" "), function() { self.render(); }); $(oncoprint).on(reposition_events.join(" "), function() { - self.repositionCells(); + self.positionCells(); + }); + $(oncoprint).on(reclip_events.join(" "), function() { + self.clipCells(); + }); + $(oncoprint).on(reposition_then_reclip_events.join(" "), function() { + self.positionCells(); + self.clipCells(true); }); } utils.extends(OncoprintSVGRenderer, OncoprintRenderer); - OncoprintSVGRenderer.prototype.getParentViewRect = function() { - var parent = this.cell_svg.node().parentNode; + OncoprintSVGRenderer.prototype.getClipBounds = function() { + var parent = this.cell_container_node; + var parentRect = parent.getBoundingClientRect(); + var x = parent.scrollLeft; + var width = parentRect.right-parentRect.left; + var cell_unit = this.oncoprint.getCellWidth() + this.oncoprint.getCellPadding(); + + var first_visible = Math.floor(x / cell_unit); + var last_visible = Math.ceil((x + width) / cell_unit); + + return this.curr_clip_bounds.set(first_visible, last_visible); + }; + OncoprintSVGRenderer.prototype.getPreviousClipBounds = function() { + return this.prev_clip_bounds; + }; + OncoprintSVGRenderer.prototype.getScrollRect = function() { + var parent = this.cell_div.node().parentNode; var parentRect = parent.getBoundingClientRect(); return {x: parent.scrollLeft, y: parent.scrollTop, width: parentRect.right - parentRect.left, height: parentRect.bottom - parentRect.top}; }; @@ -364,22 +439,13 @@ var OncoprintSVGRenderer = (function() { }; OncoprintSVGRenderer.prototype.attachCellSVG = function(container_selector_string) { this.cell_container = d3.select(container_selector_string).append('div').classed('scrolling_oncoprint_section_container', true); + this.cell_container_node = this.cell_container.node(); this.cell_div = this.cell_container.append('div').classed('cell_div', true); - var cell_svg = this.getCellSVG(); - this.cell_container.select(function() { - return this.appendChild(cell_svg.node()); - }); var self = this; $(this.cell_container.node()).on('scroll', function() { - //TODO: need to do this more smartly --- dont do it on - // every scroll, just when you need it - // TODO: separate into repositioning and reclipping - //self.repositionCells(); self.clipCells(); }); - // TODO: delete this if you want it back - cell_svg.style('display', 'none'); }; OncoprintSVGRenderer.prototype.renderTrackLabel = function(oncoprint, track_id, rule_set, svg) { var label_class = 'label'+track_id; @@ -399,469 +465,151 @@ var OncoprintSVGRenderer = (function() { .attr('transform', utils.translate(this.getLabelAreaWidth(), label_y)); } }; - OncoprintSVGRenderer.prototype.renderTrackCells = function(oncoprint, track_id, rule_set, svg) { - // TODO: have option to only alter position of cells that already exist, for scrolling, and only render enter()'ing cells - // TODO: transition new cells from position to left or right of screen, not from origin + OncoprintSVGRenderer.prototype.drawTrackCells = function(track_id, rule_set) { + var oncoprint = this.oncoprint; var data = oncoprint.getTrackData(track_id); + var id_key = oncoprint.getTrackDatumIdKey(track_id); var id_accessor = oncoprint.getTrackDatumIdAccessor(track_id); - if (!id_accessor) { - return false; - } - var id_order = utils.invert_array(oncoprint.getIdOrder()); - var view_rect = this.getParentViewRect(); - var cell_width = oncoprint.getCellWidth(); - var cell_padding = oncoprint.getCellPadding(); - var cell_div = this.cell_div; - var cell_height = oncoprint.getCellHeight(track_id); - - //var bound_g - var bound_svg = (function createAndRemoveGroups() { - var cell_class = 'cell'; - var track_cell_class = 'cell'+track_id; - - /* CLIPPING - data = data.filter(function(d,i) { - var position = id_order[id_accessor(d)]*(cell_width + cell_padding); - var xlim = [view_rect.x, view_rect.x + view_rect.width]; - return position >= xlim[0] && position < xlim[1]; - });*/ - var bound_svg = cell_div.selectAll('svg.'+track_cell_class).data(data, id_accessor); - bound_svg.enter().append('svg').classed(track_cell_class, true).classed(cell_class, true) - .style('width', cell_width).style('height', cell_height); - //var bound_g = svg.selectAll('g.'+cell_class).data(data, id_accessor); - //bound_g.enter().append('g').classed(cell_class, true); - //bound_g.exit().remove(); - //return bound_g; - return bound_svg; - })(); - var self = this; - (function positionGroups() { - /*bound_g.transition().attr('transform', function(d,i) { - var pos = self.getCellPos(track_id, id_accessor(d)); - return utils.translate(pos.x, pos.y); - });*/ - bound_svg.transition().style('left', function(d,i) { - var pos = self.getCellPos(track_id, id_accessor(d)); - return pos.x; - }).style('top', function(d,i) { - var pos = self.getCellPos(track_id, id_accessor(d)); - return pos.y; - }); - })(); - (function renderCells() { - //bound_g.selectAll('*').remove(); - //rule_set.apply(svg, bound_g, data, id_accessor, oncoprint.getCellWidth(), oncoprint.getCellHeight(track_id)); - bound_svg.selectAll('*').remove(); - rule_set.apply(svg, bound_svg, data, id_accessor, oncoprint.getCellWidth(), oncoprint.getCellHeight(track_id)); - })(); - }; - OncoprintSVGRenderer.prototype.clipCells = function() { var self = this; - var view_rect = this.getParentViewRect(); - _.each(this.oncoprint.getTrackOrder(), function(track_id) { - var data = self.oncoprint.getTrackData(track_id); - var id_accessor = self.oncoprint.getTrackDatumIdAccessor(track_id); - if (!id_accessor) { - return false; - } + this.cells[track_id] = this.cells[track_id] || {}; - var track_cell_class = 'cell'+track_id; - var bound_svg = self.cell_div.selectAll('svg.'+track_cell_class).data(data, id_accessor); - bound_svg.style('display', function(d,i) { - var position = self.getCellX(i); - var xlim = [view_rect.x, view_rect.x + view_rect.width]; - var ret = (position >= xlim[0] && position < xlim[1]) ? 'initial' : 'none'; - return ret; - }); + var cell_class = this.getCellCSSClass(); + var track_cell_class = this.getTrackCellCSSClass(track_id); + + var bound_svg = this.cell_div.selectAll('svg.'+track_cell_class).data(data, id_accessor); + bound_svg.enter().append('svg').classed(track_cell_class, true).classed(cell_class, true); + bound_svg.style('width', oncoprint.getCellWidth()).style('height', oncoprint.getCellHeight(track_id)); + bound_svg.each(function(d,i) { + self.cells[track_id][d[id_key]] = this; }); + bound_svg.selectAll('*').remove(); + rule_set.apply(bound_svg, data, id_accessor, oncoprint.getCellWidth(), oncoprint.getCellHeight(track_id)); }; - OncoprintSVGRenderer.prototype.repositionTrackCells = function(oncoprint, track_id) { - var data = oncoprint.getTrackData(track_id); - var id_accessor = oncoprint.getTrackDatumIdAccessor(track_id); - var view_rect = this.getParentViewRect(); + OncoprintSVGRenderer.prototype.positionCells = function() { var self = this; - if (!id_accessor) { - return false; - } - var cell_div = this.cell_div; - - var track_cell_class = 'cell'+track_id; - var bound_svg = cell_div.selectAll('svg.'+track_cell_class).data(data, id_accessor); - - // TODO: find x as a set function of i, that will make this faster, and clipping faster - bound_svg.transition().style('left', function(d,i) { - return self.getCellX(i); - }).style('top', function(d,i) { - return self.getTrackRenderTop(track_id); - }).style('display', function(d,i) { - var position = self.getCellX(i); - var xlim = [view_rect.x, view_rect.x + view_rect.width]; - var ret = (position >= xlim[0] && position < xlim[1]) ? 'initial' : 'none'; - return ret; + _.each(this.oncoprint.getTrackOrder(), function(track_id) { + self.positionTrackCells(track_id); }); }; - OncoprintSVGRenderer.prototype.repositionCells = function() { + OncoprintSVGRenderer.prototype.positionTrackCells = function(track_id, bound_svg) { var oncoprint = this.oncoprint; + if (!bound_svg) { + bound_svg = this.cell_div.selectAll('svg.'+this.getTrackCellCSSClass(track_id)) + .data(oncoprint.getTrackData(track_id), oncoprint.getTrackDatumIdAccessor(track_id)); + } var self = this; - _.each(oncoprint.getTrackOrder(), function(track_id) { - self.repositionTrackCells(oncoprint, track_id); + var id_key = oncoprint.getTrackDatumIdKey(track_id); + var id_order = oncoprint.getIdOrder(); + var y = this.getTrackRenderTop(track_id); + bound_svg.transition().style('left', function(d,i) { + return self.getCellX(id_order.indexOf(d[id_key])); + }).style('top', y).each("end", function() { + $(self).trigger(events.FINISHED_POSITIONING); }); - } + }; + OncoprintSVGRenderer.prototype.clipCells = function(force) { + var self = this; + var oncoprint = this.oncoprint; + + var id_order = oncoprint.getIdOrder(); + var visible_bounds = this.getClipBounds(); + var prev_bounds = force ? this.prev_clip_bounds.set(id_order.length, -1) : this.getPreviousClipBounds(); + var to_show = prev_bounds.toShow(visible_bounds); + var to_hide = prev_bounds.toHide(visible_bounds); + var i, len; + for (i=0, len = to_show.length; i < len; i++) { + var datum_id = id_order[to_show[i]]; + _.each(self.cells, function(cell_map) { + var cell = cell_map[datum_id]; + if (cell) { + cell.style.display = 'initial'; + } + }); + } + for (i=0, len = to_hide.length; i < len; i++) { + var datum_id = id_order[to_hide[i]]; + _.each(self.cells, function(cell_map) { + var cell = cell_map[datum_id]; + if (cell) { + cell.style.display = 'none'; + } + }); + } + this.prev_clip_bounds.set(visible_bounds.first, visible_bounds.last); + }; OncoprintSVGRenderer.prototype.render = function() { var self = this; this.cell_svg.attr('width', this.getCellAreaWidth()) .attr('height', this.getCellAreaHeight()); this.cell_div.style('min-width', this.getCellAreaWidth()+'px') .style('min-height', this.getCellAreaHeight()+'px'); - //this.cell_div.append('svg').classed('cell', true).style('left', '10').style('top','5').style('width','10').style('height','10') - //.append('rect').attr('width', '10').attr('height','10').attr('fill', '#ff0000'); this.label_svg.attr('width', this.getLabelAreaWidth()) .attr('height', this.getLabelAreaHeight()); _.each(this.oncoprint.getTrackOrder(), function(track_id) { var rule_set = self.getRuleSet(track_id); if (rule_set) { + self.drawTrackCells(track_id, rule_set); + self.positionTrackCells(track_id); self.renderTrackLabel(self.oncoprint, track_id, rule_set, self.getLabelSVG()); - self.renderTrackCells(self.oncoprint, track_id, rule_set, self.getCellSVG()); //renderTrackLegend(self.oncoprint, track_id, rule_set, self.getLegendSVG()); } }); + self.clipCells(); }; return OncoprintSVGRenderer; })(); -var CanvasSVGDrawer = (function() { - function CanvasSVGDrawer(canvas, svg) { - this.canvas = canvas; - this.ctx = this.canvas.node().getContext('2d'); - this.svg = svg; - this.clipping_buffer = 300; - setInterval($.proxy(this.draw, this), 1000/60); - var self = this; - $(this.canvas.node().parentNode).on('scroll', function() { - self.draw(); - }); - } - var containsPoint = function(x, y, rect) { - return x >= rect.x && y >= rect.y && x < rect.x + rect.width && y < rect.y + rect.height; - }; - CanvasSVGDrawer.prototype.draw = function() { - this.canvas.attr('width', this.svg.attr('width')) - .attr('height', this.svg.attr('height')); - var canvas_node = this.canvas.node(); - var ctx = this.ctx; - ctx.clearRect(0, 0, canvas_node.width, canvas_node.height); - ctx.beginPath(); - var view_rect = this.getParentViewRect(); - for (var child = this.svg.node().firstChild; child; child = child.nextSibling) { - this.drawNode(child, view_rect); - } - ctx.stroke(); - ctx.closePath(); - }; - CanvasSVGDrawer.prototype.getParentViewRect = function() { - var parent = this.canvas.node().parentNode; - var parentRect = parent.getBoundingClientRect(); - return {x: parent.scrollLeft, y: parent.scrollTop, width: parentRect.right - parentRect.left, height: parentRect.bottom - parentRect.top}; - }; - CanvasSVGDrawer.prototype.drawNode = function(node, view_rect) { - var d3_node = d3.select(node); - var ctx = this.ctx; - var pos = this.svg.node().createSVGPoint(); - pos = pos.matrixTransform(node.getCTM()); - if (containsPoint(pos.x, pos.y, view_rect)) { - switch (node.tagName) { - case 'g': - for (var child = node.firstChild; child; child = child.nextSibling) { - this.drawNode(child, view_rect); - } - case 'rect': - ctx.fillStyle = d3_node.attr('fill'); - ctx.strokeStyle = d3_node.attr('stroke') || ctx.fillStyle; - ctx.fillRect(pos.x, pos.y, d3_node.attr('width'), d3_node.attr('height')); - } - } - }; - return CanvasSVGDrawer; -})(); -// function OncoprintSVGRenderer(container_selector_string, oncoprint) { -// OncoprintRenderer.call(this, oncoprint); -// var self = this; -// self.container = d3.select(container_selector_string).classed('oncoprint_container', true); -// self.label_svg; -// self.cell_svg; -// self.legend_table; -// self.legend_svg; -// self.rule_sets = {}; -// self.clipping = true; - -// (function init() { -// self.container.selectAll('*').remove(); -// self.label_svg = self.container.append('div').classed('fixed_oncoprint_section_container', true).append('svg') -// .attr('width', 100).attr('xmlns', "http://www.w3.org/2000/svg"); -// self.cell_canvas = self.container.append('div').classed('scrolling_oncoprint_section_container', true).append('canvas').attr('id', 'cell_canvas').attr('width', 1000).attr('height', 200); -// self.cell_canvas.node().addEventListener('mousemove', function(evt) { -// $(self).off(events.FINISHED_RENDERING); -// var canvas_rect = self.cell_canvas.node().getBoundingClientRect(); -// var x = evt.clientX - canvas_rect.left; -// var y = evt.clientY - canvas_rect.top; -// var cell = self.mousePosToCell(x,y); -// if (cell) { -// $(self).on(events.FINISHED_RENDERING, function() { -// self.cell_canvas_ctx.fillStyle = '#ff0000'; -// self.cell_canvas_ctx.fillRect(cell.x, cell.y, 2, 2); -// console.log(cell.datum); -// }); -// } -// }); -// self.cell_canvas_ctx = self.cell_canvas.node().getContext('2d'); -// self.cell_svg = utils.makeD3SVGElement('svg');//self.container.append('div').classed('scrolling_oncoprint_section_container', true).append('svg').attr('xmlns', "http://www.w3.org/2000/svg"); -// self.legend_table = self.container.append('table'); -// })(); - -// var render_events = [events.ADD_TRACK, events.REMOVE_TRACK, events.MOVE_TRACK, events.SORT, events.SET_CELL_PADDING, -// events.SET_CELL_WIDTH, events.SET_TRACK_DATA]; -// $(oncoprint).on(render_events.join(" "), function() { -// self.renderTracks(); -// }); - -// self.toSVG = function(ctr) { -// ctr.attr('width', 2000); -// ctr.attr('height', 1000); -// var svg = ctr.append('svg').attr('xmlns', "http://www.w3.org/2000/svg"); -// //var svg = utils.makeD3SVGElement('svg'); -// var vertical_padding = 5; -// utils.appendD3SVGElement(self.label_svg, svg); -// utils.appendD3SVGElement(self.cell_svg, svg).attr('x',+self.label_svg.attr('width')); -// var legend_row_y = +self.label_svg.attr('height') + vertical_padding; -// self.legend_table.selectAll('tr').selectAll('svg').each(function() { -// var d3_elt = d3.select(this); -// utils.appendD3SVGElement(d3_elt, svg).attr('y', legend_row_y); -// console.log(legend_row_y); -// legend_row_y += +d3_elt.attr('height'); -// }); -// if (ctr) { -// utils.appendD3SVGElement(svg, ctr); -// } -// return svg; -// }; - -// self.renderTracks = function() { -// _.each(oncoprint.getTrackOrder(), function(track_id, ind) { -// renderTrackLabel(track_id); -// var rule_set = self.getRuleSet(track_id); -// if (!rule_set) { -// console.log("No rule set found for track id "+track_id); -// return; -// } -// renderTrackCells(track_id, self.getRuleSet(track_id)); +// var CanvasSVGDrawer = (function() { +// function CanvasSVGDrawer(canvas, svg) { +// this.canvas = canvas; +// this.ctx = this.canvas.node().getContext('2d'); +// this.svg = svg; +// this.clipping_buffer = 300; +// setInterval($.proxy(this.draw, this), 1000/60); +// var self = this; +// $(this.canvas.node().parentNode).on('scroll', function() { +// self.draw(); // }); - -// (function renderLegend() { -// self.legend_table.selectAll('*').remove(); -// _.each(oncoprint.getTrackOrder(), function(track_id) { -// var rule_set = self.getRuleSet(track_id); -// if (!rule_set) { -// console.log("No rule set found for track id "+track_id); -// return; -// } -// if (!rule_set.isLegendRendered()) { -// var svg = self.legend_table.append('tr').append('svg').attr('width', 1000).attr('height', 50).attr('xmlns', "http://www.w3.org/2000/svg"); -// rule_set.putLegendGroup(svg, oncoprint.getCellWidth(), 20); // TODO: get actual cell height -// rule_set.markLegendRendered(); -// } -// }); -// _.each(oncoprint.getTrackOrder(), function(track_id) { -// var rule_set = self.getRuleSet(track_id); -// if (rule_set) { -// rule_set.unmarkLegendRendered(); -// } -// }); -// })(); - +// } +// var containsPoint = function(x, y, rect) { +// return x >= rect.x && y >= rect.y && x < rect.x + rect.width && y < rect.y + rect.height; // }; - -// var renderTrackLabel = function(track_id) { -// var label_class = 'label'+track_id; -// var track_y = trackY(track_id); -// var label_y = track_y + oncoprint.getTrackPadding(track_id); // TODO: centralize it -// self.label_svg -// .attr('width', labelSvgWidth()) -// .attr('height', labelSvgHeight()); -// self.label_svg.selectAll('.'+label_class).remove(); -// self.label_svg.append('text').classed(label_class, true).text(oncoprint.getTrackLabel(track_id)) -// //.attr('x', 0) -// //.attr('y', track_y) -// .attr('transform', utils.translate(0, label_y)) -// .attr('alignment-baseline', 'hanging'); - -// var track_rule_set = self.getRuleSet(track_id); -// if (!track_rule_set) { -// console.log("No rule set found for track id "+track_id); -// return; -// } -// var track_data = oncoprint.getTrackData(track_id); -// if (track_rule_set.alteredData) { -// var percent_altered = 100*(track_rule_set.alteredData(track_data).length / track_data.length); -// self.label_svg.append('text').classed(label_class, true) -// .attr('text-anchor', 'end') -// .text(Math.floor(percent_altered)+'%') -// .attr('alignment-baseline', 'hanging') -// //.attr('x', labelSvgWidth()) -// //.attr('y', track_y); -// .attr('transform', utils.translate(labelSvgWidth(), label_y)); -// } - -// }; -// var renderCellsToCanvas = function() { -// var ctx = self.cell_canvas_ctx; -// ctx.clearRect(0,0, self.cell_canvas.node().width, self.cell_canvas.node().height); +// CanvasSVGDrawer.prototype.draw = function() { +// this.canvas.attr('width', this.svg.attr('width')) +// .attr('height', this.svg.attr('height')); +// var canvas_node = this.canvas.node(); +// var ctx = this.ctx; +// ctx.clearRect(0, 0, canvas_node.width, canvas_node.height); // ctx.beginPath(); -// var toprint = 5; -// for (var child = self.cell_svg.node().firstChild; child; child = child.nextSibling) { -// renderCellToCanvas(child); +// var view_rect = this.getParentViewRect(); +// for (var child = this.svg.node().firstChild; child; child = child.nextSibling) { +// this.drawNode(child, view_rect); // } // ctx.stroke(); // ctx.closePath(); -// $(self).trigger(events.FINISHED_RENDERING); // }; -// var pointInCanvas = function(x, y) { -// var canvas_node = self.cell_canvas.node(); -// var x_lim = self.cell_canvas.attr('width'); -// var y_lim = self.cell_canvas.attr('height'); -// return x <= x_lim && x >= 0 && y <= y_lim && y >= 0; +// CanvasSVGDrawer.prototype.getParentViewRect = function() { +// var parent = this.canvas.node().parentNode; +// var parentRect = parent.getBoundingClientRect(); +// return {x: parent.scrollLeft, y: parent.scrollTop, width: parentRect.right - parentRect.left, height: parentRect.bottom - parentRect.top}; // }; -// var renderCellToCanvas = function(node) { -// var ctx = self.cell_canvas_ctx; -// var rectpt = self.cell_svg.node().createSVGPoint(); +// CanvasSVGDrawer.prototype.drawNode = function(node, view_rect) { // var d3_node = d3.select(node); -// rectpt = rectpt.matrixTransform(node.getCTM()); -// if (self.clipping && !pointInCanvas(rectpt.x, rectpt.y)) { -// return; -// } -// switch (node.tagName) { -// case 'g': -// for (var child=node.firstChild; child; child = child.nextSibling) { -// renderCellToCanvas(child); -// } -// break; -// case 'rect': -// ctx.fillStyle = d3_node.attr('fill'); -// ctx.strokeStyle = d3_node.attr('stroke') || ctx.fillStyle; -// ctx.fillRect(rectpt.x /*+ d3_node.attr('x')*/, rectpt.y/* + d3_node.attr('y')*/, d3_node.attr('width'), d3_node.attr('height')); - -// } -// }; -// setInterval(renderCellsToCanvas, 1000/60); -// var renderTrackCells = function(track_id, rule_set) { -// var data = oncoprint.getTrackData(track_id); -// var id_accessor = oncoprint.getTrackDatumIdAccessor(track_id); -// if (!id_accessor) { -// console.log("No id accessor found for track id "+track_id); -// return; -// } -// var track_y = trackY(track_id); -// var id_order = utils.invert_array(oncoprint.getIdOrder()); -// var cell_width = oncoprint.getCellWidth(); -// var cell_height = oncoprint.getCellHeight(track_id); - -// (function updateSVG() { -// self.cell_svg -// .attr('width', cellSvgWidth()) -// .attr('height', cellSvgHeight()); - -// })(); -// var bound_g = (function createAndRemoveGroups() { -// var cell_class = 'cell'+track_id; - -// var bound_g = self.cell_svg.selectAll('g.'+cell_class).data(data, id_accessor); -// bound_g.enter().append('g').classed(cell_class, true); -// bound_g.exit().remove(); -// return bound_g; -// })(); -// (function positionGroups() { -// bound_g.transition().attr('transform', function(d, i) { -// var x = id_order[id_accessor(d)]*(oncoprint.getCellWidth() + oncoprint.getCellPadding()); -// var y = track_y + oncoprint.getTrackPadding(track_id); -// return utils.translate(x, y); -// }); -// })(); -// (function cleanGroups() { -// bound_g.selectAll('*').remove(); -// })(); -// (function renderCells() { -// rule_set.apply(self.cell_svg, bound_g, data, id_accessor, cell_width, cell_height); -// })(); -// }; - -// var trackY = function(track_id) { -// var y = 0; -// _.find(oncoprint.getTrackOrder(), function(id) { -// if (id === track_id) { -// return true; -// } else { -// y += renderedTrackHeight(id); -// return false; -// } -// }); -// return y; -// }; - -// self.mousePosToCell = function(x,y) { -// // TODO: centralize all of these coordinate stuff...shouldn't be calculating them twice in two different functions D: -// var yInTrack = function(track_id, y) { -// var track_y = trackY(track_id); -// var track_padding = oncoprint.getTrackPadding(track_id); -// var track_height = oncoprint.getTrackHeight(track_id); -// return y >= track_y + track_padding && y <= track_y + track_padding + track_height; -// }; -// var track_id = false; -// _.find(oncoprint.getTrackOrder(), function(id) { -// if (yInTrack(id, y)) { -// track_id = id; -// return true; -// } -// return false; -// }); -// if (track_id === false) { -// return undefined; -// } else { -// var cell_width = oncoprint.getCellWidth(); -// var cell_padding = oncoprint.getCellPadding(); -// var cell_index = Math.floor(x / (cell_padding +cell_width)); -// var in_cell = (x % (cell_padding + cell_width)) < cell_width; -// if (in_cell) { -// var datum_id = oncoprint.getIdOrder()[cell_index]; -// var datum = oncoprint.getTrackDatum(track_id, datum_id); -// var cell_x = cell_index*(cell_padding + cell_width); // TODO: centralize it -// var cell_y = trackY(track_id)+oncoprint.getTrackPadding(track_id);// TODO: centralize it -// return {datum: datum, x: cell_x, y: cell_y}; -// } else { -// return undefined; +// var ctx = this.ctx; +// var pos = this.svg.node().createSVGPoint(); +// pos = pos.matrixTransform(node.getCTM()); +// if (containsPoint(pos.x, pos.y, view_rect)) { +// switch (node.tagName) { +// case 'g': +// for (var child = node.firstChild; child; child = child.nextSibling) { +// this.drawNode(child, view_rect); +// } +// case 'rect': +// ctx.fillStyle = d3_node.attr('fill'); +// ctx.strokeStyle = d3_node.attr('stroke') || ctx.fillStyle; +// ctx.fillRect(pos.x, pos.y, d3_node.attr('width'), d3_node.attr('height')); // } // } // }; -// var renderedTrackHeight = function(track_id) { -// return oncoprint.getTrackHeight(track_id) + 2*oncoprint.getTrackPadding(track_id); -// }; - -// var cellSvgWidth = function() { -// return (oncoprint.getCellWidth() + oncoprint.getCellPadding())*oncoprint.getIdOrder().length; -// }; - -// var cellSvgHeight = function() { -// return _.reduce(oncoprint.getTrackOrder(), function(memo, track_id) { -// return memo + renderedTrackHeight(track_id); -// }, 0); -// }; -// var labelSvgHeight = function() { -// return cellSvgHeight(); -// }; -// var labelSvgWidth = function() { -// return 100; -// }; - -// self.setClipping = function(c) { -// self.clipping = c; -// }; -// } -// OncoprintSVGRenderer.prototype = Object.create(OncoprintRenderer.prototype); +// return CanvasSVGDrawer; +// })(); diff --git a/packages/oncoprintjs/src/js/utils.js b/packages/oncoprintjs/src/js/utils.js index 710108c424a..08d51f94e96 100644 --- a/packages/oncoprintjs/src/js/utils.js +++ b/packages/oncoprintjs/src/js/utils.js @@ -95,32 +95,22 @@ exports.warn = function(str, context) { exports.stableSort = function(arr, cmp) { // cmp returns something in [-1,0,1] - cmp = [].concat(cmp); - var index_cmp = function(a,b) { - if (a[1] < b[1]) { - return -1; - } else if (a[1] > b[1]) { - return 1; - } else { - return 0; - } - }; - cmp = cmp.concat(index_cmp); // stable - - var ordered_cmp = function(a,b) { - var res = 0; - var cmp_ind = 0; - while (res === 0 && cmp_ind < cmp.length) { - res = (cmp[cmp_ind])(a[0],b[0]); - cmp_ind += 1; - } - return res; - }; var zipped = []; _.each(arr, function(val, ind) { zipped.push([val, ind]); - }) - zipped.sort(ordered_cmp); + }); + var stable_cmp = function(a,b) { + var res = cmp(a[0], b[0]); + if (res === 0) { + if (a[1] < b[1]) { + res = -1; + } else if (a[1] > b[1]) { + res = 1; + } + } + return res; + }; + zipped.sort(stable_cmp); return _.map(zipped, function(x) { return x[0];}); }; diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index f821c11c0e2..5bc485a037d 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -10,7 +10,7 @@ var whitespace_on = true; var onc = Oncoprint.create('#onc', {cell_padding: cell_padding}); $('#shuffle_btn').click(function() { - onc.sortOnTrack(gender_track_id, function(d1, d2) { + onc.sort(gender_track_id, function(d1, d2) { var map = {'MALE':0, 'FEMALE':1}; return map[d1.attr_val] - map[d2.attr_val]; }); From 2d4bd15a28f5c280dcb90d629c7e3007d958d320 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Mon, 22 Jun 2015 17:57:01 -0400 Subject: [PATCH 067/343] Isolating rendering tasks so things arent rerendered --- packages/oncoprintjs/src/js/oncoprint.js | 51 ++++++++++++++++------- packages/oncoprintjs/test/js/test_page.js | 4 +- 2 files changed, 38 insertions(+), 17 deletions(-) diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 91286133dae..bb9a6c6c314 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -66,9 +66,6 @@ module.exports = { var renderer = new OncoprintSVGRenderer(oncoprint, {label_font: '12px Arial'}); renderer.attachLabelSVG(container_selector_string); renderer.attachCellSVG(container_selector_string); - /*var canvas = d3.select(container_selector_string).append('div').classed('scrolling_oncoprint_section_container', true) - .append('canvas'); - var canvas_drawer = new CanvasSVGDrawer(canvas, renderer.getCellSVG());*/ var ret = { addTrack: function(config) { var track_id = oncoprint.addTrack(config); @@ -156,7 +153,7 @@ function Oncoprint(config) { _.each(self.tracks[track_id].data, function(datum) { id_data_map[id_accessor(datum)] = datum; }); - $(self).trigger(events.SET_TRACK_DATA); + $(self).trigger(events.SET_TRACK_DATA, {track_id: track_id}); }; self.getTrackDatum = function(track_id, datum_id) { return self.tracks[track_id].id_data_map[datum_id]; @@ -197,7 +194,7 @@ function Oncoprint(config) { self.tracks[track_id] ={id: track_id, data: [], config: $.extend({}, defaultTrackConfig, config)}; self.track_order.push(track_id); - $(self).trigger(events.ADD_TRACK, {track: track_id}); + $(self).trigger(events.ADD_TRACK, {track_id: track_id}); return track_id; }; @@ -375,14 +372,22 @@ var OncoprintSVGRenderer = (function() { this.curr_clip_bounds = new VisibleIndexBounds(-1, -2); this.prev_clip_bounds = new VisibleIndexBounds(-1, -2); - var render_events = [events.ADD_TRACK, events.REMOVE_TRACK, events.SET_TRACK_DATA]; + var render_all_events = [events.MOVE_TRACK, events.REMOVE_TRACK]; + var render_track_events = [events.ADD_TRACK, events.SET_TRACK_DATA]; var reposition_events = [events.MOVE_TRACK, events.SET_CELL_PADDING, events.SET_CELL_WIDTH]; + var resize_cell_div_events = [events.SET_CELL_PADDING, events.SET_CELL_WIDTH]; var reclip_events = [events.SET_CELL_PADDING, events.SET_CELL_WIDTH]; var reposition_then_reclip_events = [events.SET_ID_ORDER]; var self = this; - $(oncoprint).on(render_events.join(" "), function() { + $(oncoprint).on(resize_cell_div_events.join(" "), function() { + self.resizeCellDiv(); + }); + $(oncoprint).on(render_all_events.join(" "), function() { self.render(); }); + $(oncoprint).on(render_track_events.join(" "), function(e, d) { + self.render(d.track_id); + }); $(oncoprint).on(reposition_events.join(" "), function() { self.positionCells(); }); @@ -444,7 +449,9 @@ var OncoprintSVGRenderer = (function() { var self = this; $(this.cell_container.node()).on('scroll', function() { - self.clipCells(); + _.debounce(function() { + self.clipCells(); + }, 60)(); }); }; OncoprintSVGRenderer.prototype.renderTrackLabel = function(oncoprint, track_id, rule_set, svg) { @@ -537,23 +544,37 @@ var OncoprintSVGRenderer = (function() { } this.prev_clip_bounds.set(visible_bounds.first, visible_bounds.last); }; - OncoprintSVGRenderer.prototype.render = function() { + OncoprintSVGRenderer.prototype.resizeCellDiv = function() { + this.cell_div.style('min-width', this.getCellAreaWidth()+'px') + .style('min-height', this.getCellAreaHeight()+'px'); + }; + OncoprintSVGRenderer.prototype.isTrackRenderable = function(track_id) { + return this.getRuleSet(track_id) && this.oncoprint.getTrackData(track_id).length > 0; + }; + OncoprintSVGRenderer.prototype.render = function(track_id) { var self = this; this.cell_svg.attr('width', this.getCellAreaWidth()) .attr('height', this.getCellAreaHeight()); - this.cell_div.style('min-width', this.getCellAreaWidth()+'px') - .style('min-height', this.getCellAreaHeight()+'px'); this.label_svg.attr('width', this.getLabelAreaWidth()) .attr('height', this.getLabelAreaHeight()); - _.each(this.oncoprint.getTrackOrder(), function(track_id) { - var rule_set = self.getRuleSet(track_id); - if (rule_set) { + this.resizeCellDiv(); + + var renderTrack = function(track_id) { + if (self.isTrackRenderable(track_id)) { + var rule_set = self.getRuleSet(track_id); self.drawTrackCells(track_id, rule_set); self.positionTrackCells(track_id); self.renderTrackLabel(self.oncoprint, track_id, rule_set, self.getLabelSVG()); //renderTrackLegend(self.oncoprint, track_id, rule_set, self.getLegendSVG()); } - }); + }; + if (typeof track_id !== "undefined") { + renderTrack(track_id); + } else { + _.each(this.oncoprint.getTrackOrder(), function(track_id) { + renderTrack(track_id); + }); + } self.clipCells(); }; return OncoprintSVGRenderer; diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index 5bc485a037d..5fd0a144b6c 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -72,11 +72,11 @@ $.when(gender_data_promise).then(function() { } }); onc.setTrackData(gender_track_id, gender_data); - //for (var i=0; i<10; i++) { + for (var i=0; i<10; i++) { var dup_gender_track_id = onc.addTrack({label: 'Gender'}); onc.useSameRuleSet(dup_gender_track_id, gender_track_id); onc.setTrackData(dup_gender_track_id, gender_data); - //} + } }); mutation_data_promise.then(function(data) { From 698cc990c2ac695fec4bd570a9f7b517203b6889 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Mon, 22 Jun 2015 18:11:37 -0400 Subject: [PATCH 068/343] Get rid of cell_svg --- packages/oncoprintjs/src/css/oncoprint.css | 4 +- packages/oncoprintjs/src/js/oncoprint.js | 50 +++++++++------------- 2 files changed, 22 insertions(+), 32 deletions(-) diff --git a/packages/oncoprintjs/src/css/oncoprint.css b/packages/oncoprintjs/src/css/oncoprint.css index a1acb173979..dd9b4a949ac 100644 --- a/packages/oncoprintjs/src/css/oncoprint.css +++ b/packages/oncoprintjs/src/css/oncoprint.css @@ -13,8 +13,8 @@ } .cell_div { - background-color: #EEE; - position: relative; + background-color: #fff; + position: relative; } .cell { diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index bb9a6c6c314..94022fbbb15 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -63,9 +63,7 @@ module.exports = { BAR_CHART: RuleSet.BAR_CHART, create: function CreateOncoprint(container_selector_string, config) { var oncoprint = new Oncoprint(config); - var renderer = new OncoprintSVGRenderer(oncoprint, {label_font: '12px Arial'}); - renderer.attachLabelSVG(container_selector_string); - renderer.attachCellSVG(container_selector_string); + var renderer = new OncoprintSVGRenderer(container_selector_string, oncoprint, {label_font: '12px Arial'}); var ret = { addTrack: function(config) { var track_id = oncoprint.addTrack(config); @@ -360,10 +358,10 @@ var OncoprintSVGRenderer = (function() { return this; }; } - function OncoprintSVGRenderer(oncoprint, config) { + function OncoprintSVGRenderer(container_selector_string, oncoprint, config) { OncoprintRenderer.call(this, oncoprint, config); + var self = this; this.label_svg = utils.makeD3SVGElement('svg'); - this.cell_svg = utils.makeD3SVGElement('svg'); this.label_container; this.cell_container; this.cell_container_node; @@ -372,13 +370,29 @@ var OncoprintSVGRenderer = (function() { this.curr_clip_bounds = new VisibleIndexBounds(-1, -2); this.prev_clip_bounds = new VisibleIndexBounds(-1, -2); + (function initLabelContainer() { + self.label_container = d3.select(container_selector_string).append('div').classed('fixed_oncoprint_section_container', true); + var label_svg = self.getLabelSVG(); + self.label_container.select(function () { + return this.appendChild(label_svg.node()); + }); + })(); + (function initCellContainer() { + self.cell_container = d3.select(container_selector_string).append('div').classed('scrolling_oncoprint_section_container', true); + self.cell_container_node = self.cell_container.node(); + self.cell_div = self.cell_container.append('div').classed('cell_div', true); + + $(self.cell_container.node()).on('scroll', function() { + self.clipCells(); + }); + })(); + var render_all_events = [events.MOVE_TRACK, events.REMOVE_TRACK]; var render_track_events = [events.ADD_TRACK, events.SET_TRACK_DATA]; var reposition_events = [events.MOVE_TRACK, events.SET_CELL_PADDING, events.SET_CELL_WIDTH]; var resize_cell_div_events = [events.SET_CELL_PADDING, events.SET_CELL_WIDTH]; var reclip_events = [events.SET_CELL_PADDING, events.SET_CELL_WIDTH]; var reposition_then_reclip_events = [events.SET_ID_ORDER]; - var self = this; $(oncoprint).on(resize_cell_div_events.join(" "), function() { self.resizeCellDiv(); }); @@ -432,28 +446,6 @@ var OncoprintSVGRenderer = (function() { OncoprintSVGRenderer.prototype.getLabelSVG = function() { return this.label_svg; }; - OncoprintSVGRenderer.prototype.getCellSVG = function() { - return this.cell_svg; - }; - OncoprintSVGRenderer.prototype.attachLabelSVG = function(container_selector_string) { - this.label_container = d3.select(container_selector_string).append('div').classed('fixed_oncoprint_section_container', true); - var label_svg = this.getLabelSVG(); - this.label_container.select(function () { - return this.appendChild(label_svg.node()); - }); - }; - OncoprintSVGRenderer.prototype.attachCellSVG = function(container_selector_string) { - this.cell_container = d3.select(container_selector_string).append('div').classed('scrolling_oncoprint_section_container', true); - this.cell_container_node = this.cell_container.node(); - this.cell_div = this.cell_container.append('div').classed('cell_div', true); - var self = this; - - $(this.cell_container.node()).on('scroll', function() { - _.debounce(function() { - self.clipCells(); - }, 60)(); - }); - }; OncoprintSVGRenderer.prototype.renderTrackLabel = function(oncoprint, track_id, rule_set, svg) { var label_class = 'label'+track_id; var label_y = this.getTrackRenderTop(track_id); @@ -553,8 +545,6 @@ var OncoprintSVGRenderer = (function() { }; OncoprintSVGRenderer.prototype.render = function(track_id) { var self = this; - this.cell_svg.attr('width', this.getCellAreaWidth()) - .attr('height', this.getCellAreaHeight()); this.label_svg.attr('width', this.getLabelAreaWidth()) .attr('height', this.getLabelAreaHeight()); this.resizeCellDiv(); From cd7e00bcce749d441fb0b2b99e02a81b7895fe5f Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Mon, 22 Jun 2015 18:51:18 -0400 Subject: [PATCH 069/343] Bringing back the legend --- packages/oncoprintjs/src/css/oncoprint.css | 9 +++ packages/oncoprintjs/src/js/RuleSet.js | 34 +++++------ packages/oncoprintjs/src/js/oncoprint.js | 71 ++++++++++++++++++---- packages/oncoprintjs/src/js/utils.js | 8 +++ packages/oncoprintjs/test/js/test_page.js | 17 ++++-- 5 files changed, 103 insertions(+), 36 deletions(-) diff --git a/packages/oncoprintjs/src/css/oncoprint.css b/packages/oncoprintjs/src/css/oncoprint.css index dd9b4a949ac..a34442cc1cc 100644 --- a/packages/oncoprintjs/src/css/oncoprint.css +++ b/packages/oncoprintjs/src/css/oncoprint.css @@ -27,4 +27,13 @@ -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; +} + +.ruleset_legend_label { + font-family: Arial; + font-weight: bold; + fill: gray; + text-anchor: start; + alignment-baseline: hanging; + font-size: 12px; } \ No newline at end of file diff --git a/packages/oncoprintjs/src/js/RuleSet.js b/packages/oncoprintjs/src/js/RuleSet.js index bcbae7f9b81..61002d205f7 100644 --- a/packages/oncoprintjs/src/js/RuleSet.js +++ b/packages/oncoprintjs/src/js/RuleSet.js @@ -25,14 +25,21 @@ module.exports = { } }; +var getRuleSetId = utils.makeIdCounter(); + var D3SVGRuleSet = (function() { - function D3SVGRuleSet() { + function D3SVGRuleSet(params) { this.rule_map = {}; + this.rule_set_id = getRuleSetId(); + this.legend_label = params.legend_label; + }; + var getRuleId = utils.makeIdCounter(); + + D3SVGRuleSet.prototype.getLegendLabel = function() { + return this.legend_label; }; - var rule_counter = 0; - var getRuleId = function() { - rule_counter += 1; - return rule_counter; + D3SVGRuleSet.prototype.getRuleSetId = function() { + return this.rule_set_id; }; D3SVGRuleSet.prototype.addRule = function(params) { var rule_id = getRuleId(); @@ -74,15 +81,6 @@ var D3SVGRuleSet = (function() { D3SVGRuleSet.prototype.getRule = function(rule_id) { return this.rule_map[rule_id]; }; - D3SVGRuleSet.prototype.markLegendRendered = function() { - this.legend_rendered = true; - }; - D3SVGRuleSet.prototype.unmarkLegendRendered = function() { - this.legend_rendered = false; - }; - D3SVGRuleSet.prototype.isLegendRendered = function() { - return this.legend_rendered; - }; return D3SVGRuleSet; })(); @@ -127,7 +125,8 @@ function D3SVGCategoricalColorRuleSet(params) { rule.putLegendGroup(group, cell_width, cell_height); }) utils.spaceSVGElementsHorizontally(group, 20); - } + return group; + }; } D3SVGCategoricalColorRuleSet.prototype = Object.create(D3SVGRuleSet.prototype); @@ -142,7 +141,7 @@ function D3SVGGradientColorRuleSet(params) { scale: params.scale }); this.putLegendGroup = function(svg) { - this.rule_map[rule].putLegendGroup(svg); + return this.rule_map[rule].putLegendGroup(svg); }; } D3SVGGradientColorRuleSet.prototype = Object.create(D3SVGRuleSet.prototype); @@ -158,7 +157,7 @@ function D3SVGBarChartRuleSet(params) { fill: params.fill, }); this.putLegendGroup = function(svg, cell_width, cell_height) { - this.rule_map[rule].putLegendGroup(svg, cell_width, cell_height); + return this.rule_map[rule].putLegendGroup(svg, cell_width, cell_height); }; } D3SVGBarChartRuleSet.prototype = Object.create(D3SVGRuleSet.prototype); @@ -216,6 +215,7 @@ function D3SVGGeneticAlterationRuleSet(params) { rule.putLegendGroup(group, cell_width, cell_height); }) utils.spaceSVGElementsHorizontally(group, 20); + return group; }; self.alteredData = function(data) { var altered_data = []; diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 94022fbbb15..d9a1d33a13f 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -41,6 +41,7 @@ var RuleSet = require('./RuleSet'); var defaultOncoprintConfig = { cell_width: 6, cell_padding: 3, + legend: true }; var hiddenOncoprintConfig = { @@ -62,8 +63,10 @@ module.exports = { GENETIC_ALTERATION: RuleSet.GENETIC_ALTERATION, BAR_CHART: RuleSet.BAR_CHART, create: function CreateOncoprint(container_selector_string, config) { + config = $.extend({}, defaultOncoprintConfig, config || {}); + config = $.extend(config, hiddenOncoprintConfig); var oncoprint = new Oncoprint(config); - var renderer = new OncoprintSVGRenderer(container_selector_string, oncoprint, {label_font: '12px Arial'}); + var renderer = new OncoprintSVGRenderer(container_selector_string, oncoprint, {label_font: '12px Arial', legend:config.legend}); var ret = { addTrack: function(config) { var track_id = oncoprint.addTrack(config); @@ -101,9 +104,7 @@ module.exports = { function Oncoprint(config) { var self = this; var track_id_counter = 0; - self.config = $.extend({}, defaultOncoprintConfig, config || {}); - self.config = $.extend(self.config, hiddenOncoprintConfig); - + self.config = config; self.id_order = []; self.track_order = []; self.tracks = {}; @@ -361,21 +362,19 @@ var OncoprintSVGRenderer = (function() { function OncoprintSVGRenderer(container_selector_string, oncoprint, config) { OncoprintRenderer.call(this, oncoprint, config); var self = this; - this.label_svg = utils.makeD3SVGElement('svg'); + this.label_svg; this.label_container; this.cell_container; this.cell_container_node; this.cell_div; + this.legend_svg; this.cells = {}; this.curr_clip_bounds = new VisibleIndexBounds(-1, -2); this.prev_clip_bounds = new VisibleIndexBounds(-1, -2); (function initLabelContainer() { self.label_container = d3.select(container_selector_string).append('div').classed('fixed_oncoprint_section_container', true); - var label_svg = self.getLabelSVG(); - self.label_container.select(function () { - return this.appendChild(label_svg.node()); - }); + self.label_svg = self.label_container.append('svg'); })(); (function initCellContainer() { self.cell_container = d3.select(container_selector_string).append('div').classed('scrolling_oncoprint_section_container', true); @@ -386,6 +385,11 @@ var OncoprintSVGRenderer = (function() { self.clipCells(); }); })(); + (function initLegend() { + if (config.legend) { + self.legend_svg = d3.select(container_selector_string).append('svg'); + } + })(); var render_all_events = [events.MOVE_TRACK, events.REMOVE_TRACK]; var render_track_events = [events.ADD_TRACK, events.SET_TRACK_DATA]; @@ -540,13 +544,31 @@ var OncoprintSVGRenderer = (function() { this.cell_div.style('min-width', this.getCellAreaWidth()+'px') .style('min-height', this.getCellAreaHeight()+'px'); }; + OncoprintSVGRenderer.prototype.resizeLabelSVG = function() { + this.getLabelSVG().attr('width', this.getLabelAreaWidth()) + .attr('height', this.getLabelAreaHeight()); + }; + OncoprintSVGRenderer.prototype.resizeLegendSVG = function() { + var new_height = 0; + var new_width = 0; + var point = this.legend_svg.node().createSVGPoint(); + utils.d3SelectChildren(this.legend_svg, 'g').each(function() { + point.x = 0; + point.y = 0; + point = point.matrixTransform(this.getCTM()); + var bbox = this.getBBox(); + new_height = Math.max(new_height, point.y+bbox.height); + new_width = Math.max(new_width, point.x + bbox.width); + console.log(bbox); + }); + this.legend_svg.attr('width', new_width).attr('height', new_height); + }; OncoprintSVGRenderer.prototype.isTrackRenderable = function(track_id) { return this.getRuleSet(track_id) && this.oncoprint.getTrackData(track_id).length > 0; }; OncoprintSVGRenderer.prototype.render = function(track_id) { var self = this; - this.label_svg.attr('width', this.getLabelAreaWidth()) - .attr('height', this.getLabelAreaHeight()); + this.resizeLabelSVG(); this.resizeCellDiv(); var renderTrack = function(track_id) { @@ -555,7 +577,6 @@ var OncoprintSVGRenderer = (function() { self.drawTrackCells(track_id, rule_set); self.positionTrackCells(track_id); self.renderTrackLabel(self.oncoprint, track_id, rule_set, self.getLabelSVG()); - //renderTrackLegend(self.oncoprint, track_id, rule_set, self.getLegendSVG()); } }; if (typeof track_id !== "undefined") { @@ -566,7 +587,31 @@ var OncoprintSVGRenderer = (function() { }); } self.clipCells(); - }; + this.renderLegend(); + }; + OncoprintSVGRenderer.prototype.renderLegend = function() { + var svg = this.legend_svg; + svg.selectAll('*').remove(); + var padding = 25; + var y = padding; + var rendered = {}; + var cell_width = this.oncoprint.getCellWidth(); + var self = this; + _.each(this.rule_sets, function(rule_set, track_id) { + var rule_set_id = rule_set.getRuleSetId(); + if (!rendered.hasOwnProperty(rule_set_id)) { + var text = svg.append('text').classed('ruleset_legend_label', true).text(rule_set.getLegendLabel()) + .attr('transform', utils.translate(0,y)); + var group = rule_set.putLegendGroup(svg, cell_width, self.oncoprint.getCellHeight(track_id)); + rendered[rule_set_id] = true; + group.attr('transform', utils.translate(200,y)); + var bounding_box = group.node().getBBox(); + y += bounding_box.height; + y += padding; + } + }); + this.resizeLegendSVG(); + } return OncoprintSVGRenderer; })(); diff --git a/packages/oncoprintjs/src/js/utils.js b/packages/oncoprintjs/src/js/utils.js index 08d51f94e96..d985270754a 100644 --- a/packages/oncoprintjs/src/js/utils.js +++ b/packages/oncoprintjs/src/js/utils.js @@ -44,6 +44,14 @@ exports.extends = function(child_class, parent_class) { child_class.prototype.constructor = child_class; }; +exports.makeIdCounter = function() { + var counter = 0; + return function() { + counter += 1; + return counter; + }; +}; + exports.makeD3SVGElement = function(tag) { return d3.select(document.createElementNS('http://www.w3.org/2000/svg', tag)); }; diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index 5fd0a144b6c..308cc0df449 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -69,10 +69,11 @@ $.when(gender_data_promise).then(function() { color: {}, getCategory: function(d) { return d.attr_val; - } + }, + legend_label: 'Gender' }); onc.setTrackData(gender_track_id, gender_data); - for (var i=0; i<10; i++) { + for (var i=0; i<2; i++) { var dup_gender_track_id = onc.addTrack({label: 'Gender'}); onc.useSameRuleSet(dup_gender_track_id, gender_track_id); onc.setTrackData(dup_gender_track_id, gender_data); @@ -87,7 +88,8 @@ $.when(mutation_data_promise).then(function() { onc.setRuleSet(mutation_track_id, Oncoprint.GRADIENT_COLOR, { data_key: 'attr_val', data_range: [0,100], - color_range: ['#A9A9A9', '#FF0000'] + color_range: ['#A9A9A9', '#FF0000'], + legend_label: 'Mutations' }); onc.setTrackData(mutation_track_id, mutation_data); @@ -96,7 +98,8 @@ $.when(mutation_data_promise).then(function() { data_key: 'attr_val', data_range: [0,100], fill: '#ff0000', - scale: 'log' + scale: 'log', + legend_label: 'Mutations (Log scale)' }); onc.setTrackData(log_mut_track_id, mutation_data); }); @@ -133,7 +136,8 @@ $.when(alteration_data_promise).then(function() { label: { MISSENSE: 'Missense Mutation' } - } + }, + legend_label: 'Genetic Alteration' }); onc.setTrackData(alteration_track_id, alteration_data); @@ -147,6 +151,7 @@ $('#change_color_scheme').click(function() { color: {MALE: '#000000', FEMALE: '#999999'}, getCategory: function(d) { return d.attr_val; - } + }, + legend_label: 'Gender (modified color)' }); }); From 741994e57f7e0077ed674c125b04323ca21fdf9b Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Mon, 22 Jun 2015 19:16:49 -0400 Subject: [PATCH 070/343] new legend element for bar charts, showing scale --- packages/oncoprintjs/src/js/RuleSet.js | 39 +++++++++++++---------- packages/oncoprintjs/src/js/oncoprint.js | 1 - packages/oncoprintjs/test/js/test_page.js | 9 ++++++ 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/packages/oncoprintjs/src/js/RuleSet.js b/packages/oncoprintjs/src/js/RuleSet.js index 61002d205f7..4e38bc03521 100644 --- a/packages/oncoprintjs/src/js/RuleSet.js +++ b/packages/oncoprintjs/src/js/RuleSet.js @@ -307,6 +307,11 @@ function D3SVGBarChartRule(params, rule_id) { } }; + var makeDatum = function(x) { + var ret = {}; + ret[params.data_key] = x; + return ret; + }; var scaled_data_range = _.map(this.data_range, scale); var height_helper = function(d) { var datum = scale(d[params.data_key]); @@ -324,30 +329,30 @@ function D3SVGBarChartRule(params, rule_id) { this.attrs.y = y_function; this.attrs.fill = params.fill || '#000000'; - this.putLegendGroup = function(svg, cell_width, cell_height) { // TODO: triangle legend piece if (params.exclude_from_legend) { return; } - var rect =utils.makeD3SVGElement('rect'); - rect.attr('fill', this.attrs.fill); var group = svg.append('g'); - var bottom_end = group.append('g'); - utils.appendD3SVGElement(rect, bottom_end) - .attr('width', cell_width) - .attr('height', 1).attr('y', cell_height-1); - bottom_end.append('text').text(this.data_range[0]).attr('alignment-baseline', 'hanging');; - utils.spaceSVGElementsHorizontally(bottom_end, 5); - - var top_end = group.append('g'); - utils.appendD3SVGElement(rect, top_end) - .attr('width', cell_width) - .attr('height', cell_height); - top_end.append('text').text(this.data_range[1]).attr('alignment-baseline', 'hanging');; - utils.spaceSVGElementsHorizontally(top_end, 5); - + group.append('text').text(this.data_range[0]).attr('alignment-baseline', 'hanging'); + var rect_group = group.append('g'); + var mesh = 50; + for (var i=0; i<=mesh; i++) { + var t = i/mesh; + var d = (1-t)*this.data_range[0] + t*this.data_range[1]; + var datum = makeDatum(d); + var height = cell_height*height_helper(datum)/100; + rect_group.append('rect') + .attr('width', 1) + .attr('height', height) + .attr('y', cell_height-height) + .attr('fill', this.attrs.fill); + } + utils.spaceSVGElementsHorizontally(rect_group, 0); + group.append('text').text(this.data_range[1]).attr('alignment-baseline', 'hanging'); utils.spaceSVGElementsHorizontally(group, 10); + return group; }; } diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index d9a1d33a13f..40081630a6a 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -559,7 +559,6 @@ var OncoprintSVGRenderer = (function() { var bbox = this.getBBox(); new_height = Math.max(new_height, point.y+bbox.height); new_width = Math.max(new_width, point.x + bbox.width); - console.log(bbox); }); this.legend_svg.attr('width', new_width).attr('height', new_height); }; diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index 308cc0df449..a45646a49bc 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -102,6 +102,15 @@ $.when(mutation_data_promise).then(function() { legend_label: 'Mutations (Log scale)' }); onc.setTrackData(log_mut_track_id, mutation_data); + + var dup_mut_track_id = onc.addTrack({label: 'Mutations'}); + onc.setRuleSet(dup_mut_track_id, Oncoprint.BAR_CHART, { + data_key: 'attr_val', + data_range: [0,100], + fill: '#ff0000', + legend_label: 'Mutations (Linear scale)' + }); + onc.setTrackData(dup_mut_track_id, mutation_data); }); From c8e7bff9cddf8f4b81406f5ba80647ed71f69758 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Tue, 23 Jun 2015 14:52:36 -0400 Subject: [PATCH 071/343] clever clipping done --- packages/oncoprintjs/src/js/oncoprint.js | 90 +++++++++++++++++++----- 1 file changed, 71 insertions(+), 19 deletions(-) diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 40081630a6a..91c57205bfa 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -358,6 +358,11 @@ var OncoprintSVGRenderer = (function() { this.last = last; return this; }; + this.fromViewInterval = function(interval, cell_unit) { + this.first = Math.floor(interval[0]/cell_unit); + this.last = Math.ceil(interval[1]/cell_unit); + return this; + }; } function OncoprintSVGRenderer(container_selector_string, oncoprint, config) { OncoprintRenderer.call(this, oncoprint, config); @@ -372,6 +377,8 @@ var OncoprintSVGRenderer = (function() { this.curr_clip_bounds = new VisibleIndexBounds(-1, -2); this.prev_clip_bounds = new VisibleIndexBounds(-1, -2); + this.clip_zone_start = 0; + (function initLabelContainer() { self.label_container = d3.select(container_selector_string).append('div').classed('fixed_oncoprint_section_container', true); self.label_svg = self.label_container.append('svg'); @@ -419,6 +426,11 @@ var OncoprintSVGRenderer = (function() { } utils.extends(OncoprintSVGRenderer, OncoprintRenderer); + OncoprintSVGRenderer.prototype.getViewInterval = function() { + var parent = this.cell_container_node; + var parentRect = parent.getBoundingClientRect(); + return {x: parent.scrollLeft, width: parentRect.right - parentRect.left}; + }; OncoprintSVGRenderer.prototype.getClipBounds = function() { var parent = this.cell_container_node; var parentRect = parent.getBoundingClientRect(); @@ -510,33 +522,73 @@ var OncoprintSVGRenderer = (function() { $(self).trigger(events.FINISHED_POSITIONING); }); }; + OncoprintSVGRenderer.prototype.getClipViewInterval = function() { + var self = this; + var view = this.getViewInterval(); + var x = view.x; + var width = view.width; + var clip_buffer = Math.floor(0.05*width); + var clip_zone_size = 3*width; + + var variant=1; + if (variant === 0) { + var section0 = this.clip_zone_start; + var section2 = this.clip_zone_start + 2*width; + + if (x > section2) { + this.clip_zone_start += width; + } else if (x < section0) { + this.clip_zone_start -= width; + } + + return [this.clip_zone_start, this.clip_zone_start + clip_zone_size]; + } else if (variant === 1) { + var section1 = this.clip_zone_start + width; + + while (x > section1 + clip_buffer) { + this.clip_zone_start += clip_buffer; + section1 = this.clip_zone_start + width; + } + while (x < section1 - clip_buffer) { + this.clip_zone_start -= clip_buffer; + section1 = this.clip_zone_start + width; + } + return [this.clip_zone_start, this.clip_zone_start + clip_zone_size]; + } + }; OncoprintSVGRenderer.prototype.clipCells = function(force) { var self = this; var oncoprint = this.oncoprint; var id_order = oncoprint.getIdOrder(); - var visible_bounds = this.getClipBounds(); + var visible_bounds = this.curr_clip_bounds.fromViewInterval(this.getClipViewInterval(), this.oncoprint.getCellWidth() + this.oncoprint.getCellPadding()); + visible_bounds.first = Math.max(0, visible_bounds.first); + visible_bounds.last = Math.min(id_order.length-1, visible_bounds.last); var prev_bounds = force ? this.prev_clip_bounds.set(id_order.length, -1) : this.getPreviousClipBounds(); var to_show = prev_bounds.toShow(visible_bounds); var to_hide = prev_bounds.toHide(visible_bounds); - var i, len; - for (i=0, len = to_show.length; i < len; i++) { - var datum_id = id_order[to_show[i]]; - _.each(self.cells, function(cell_map) { - var cell = cell_map[datum_id]; - if (cell) { - cell.style.display = 'initial'; - } - }); - } - for (i=0, len = to_hide.length; i < len; i++) { - var datum_id = id_order[to_hide[i]]; - _.each(self.cells, function(cell_map) { - var cell = cell_map[datum_id]; - if (cell) { - cell.style.display = 'none'; - } - }); + if (to_show.length > 0 || to_hide.length > 0) { + //this.cell_div.node().style.display = 'none'; + var i, len; + for (i=0, len = to_show.length; i < len; i++) { + var datum_id = id_order[to_show[i]]; + _.each(self.cells, function(cell_map) { + var cell = cell_map[datum_id]; + if (cell) { + cell.style.display = 'inherit'; + } + }); + } + for (i=0, len = to_hide.length; i < len; i++) { + var datum_id = id_order[to_hide[i]]; + _.each(self.cells, function(cell_map) { + var cell = cell_map[datum_id]; + if (cell) { + cell.style.display = 'none'; + } + }); + } + //this.cell_div.node().style.display = 'inherit'; } this.prev_clip_bounds.set(visible_bounds.first, visible_bounds.last); }; From 9871139ee4c5f86a793298fab36d4cf4a9939b3a Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Tue, 23 Jun 2015 16:51:10 -0400 Subject: [PATCH 072/343] adding tooltips and cell rollover effects --- packages/oncoprintjs/src/css/oncoprint.css | 4 +++ packages/oncoprintjs/src/js/oncoprint.js | 38 +++++++++++++++++++--- packages/oncoprintjs/test/index.html | 3 ++ packages/oncoprintjs/test/js/test_page.js | 9 +++-- 4 files changed, 47 insertions(+), 7 deletions(-) diff --git a/packages/oncoprintjs/src/css/oncoprint.css b/packages/oncoprintjs/src/css/oncoprint.css index a34442cc1cc..f76c58bd638 100644 --- a/packages/oncoprintjs/src/css/oncoprint.css +++ b/packages/oncoprintjs/src/css/oncoprint.css @@ -21,6 +21,10 @@ position: absolute; } +.cell_rollover { + outline: 1px solid DarkGray; +} + .scrolling_oncoprint_table_container table td { padding: 0; margin: 0; diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 91c57205bfa..6cb7b5e4091 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -29,7 +29,6 @@ */ var _ = require('underscore'); var d3 = require('d3'); -var $ = require('jquery'); var events = require('./events'); var signals = require('./signals'); var globals = require('./globals'); @@ -41,7 +40,7 @@ var RuleSet = require('./RuleSet'); var defaultOncoprintConfig = { cell_width: 6, cell_padding: 3, - legend: true + legend: true, }; var hiddenOncoprintConfig = { @@ -54,7 +53,10 @@ var defaultTrackConfig = { cell_height: 23, track_height: 20, track_padding: 5, - sort_cmp: undefined + sort_cmp: undefined, + tooltip: function(d) { + return d['sample']; + } }; module.exports = { @@ -141,6 +143,9 @@ function Oncoprint(config) { self.getTrackData = function(track_id) { return self.tracks[track_id].data; }; + self.getTrackTooltip = function(track_id) { + return self.tracks[track_id].config.tooltip; + }; self.setTrackData = function(track_id, data) { var id_accessor = self.getTrackDatumIdAccessor(track_id); @@ -491,11 +496,36 @@ var OncoprintSVGRenderer = (function() { var cell_class = this.getCellCSSClass(); var track_cell_class = this.getTrackCellCSSClass(track_id); + var bound_svg = this.cell_div.selectAll('svg.'+track_cell_class).data(data, id_accessor); bound_svg.enter().append('svg').classed(track_cell_class, true).classed(cell_class, true); bound_svg.style('width', oncoprint.getCellWidth()).style('height', oncoprint.getCellHeight(track_id)); + var tooltip = this.oncoprint.getTrackTooltip(track_id); bound_svg.each(function(d,i) { - self.cells[track_id][d[id_key]] = this; + var dom_cell = this; + var id = id_accessor(d); + var tooltip_html = tooltip(d); + if (tooltip) { + $(dom_cell).one("mouseover", function() { + $(dom_cell).qtip({ + content: { + text: tooltip_html + }, + position: {my:'left bottom', at:'top middle', viewport: $(window)}, + style: { classes: 'qtip-rounded qtip-shadow qtip-lightyellow' }, + show: {event: "mouseover"}, + hide: {fixed: true, delay: 100, event: "mouseout"} + }); + $(dom_cell).trigger("mouseover"); + }); + } + $(dom_cell).on("mouseover", function() { + d3.select(dom_cell).classed("cell_rollover", true); + }); + $(dom_cell).on("mouseout", function() { + d3.select(dom_cell).classed("cell_rollover", false); + }); + self.cells[track_id][id] = this; }); bound_svg.selectAll('*').remove(); rule_set.apply(bound_svg, data, id_accessor, oncoprint.getCellWidth(), oncoprint.getCellHeight(track_id)); diff --git a/packages/oncoprintjs/test/index.html b/packages/oncoprintjs/test/index.html index 0a61f722f96..5b332c379bb 100644 --- a/packages/oncoprintjs/test/index.html +++ b/packages/oncoprintjs/test/index.html @@ -3,6 +3,7 @@ Oncoprint Test Page +
@@ -12,6 +13,8 @@ + + diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index a45646a49bc..c6f4f783ae7 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -1,5 +1,4 @@ var _ = require('underscore'); -var $ = require('jquery'); var d3 = require('d3'); var globals = require('../../src/js/globals'); @@ -84,12 +83,16 @@ mutation_data_promise.then(function(data) { mutation_data = data.data; }); $.when(mutation_data_promise).then(function() { - mutation_track_id = onc.addTrack({label: 'Mutations'}); + mutation_track_id = onc.addTrack({label: 'Mutations', + tooltip: function(d) { + return '

'+d.sample+': '+d.attr_val+'

'; + } + }); onc.setRuleSet(mutation_track_id, Oncoprint.GRADIENT_COLOR, { data_key: 'attr_val', data_range: [0,100], color_range: ['#A9A9A9', '#FF0000'], - legend_label: 'Mutations' + legend_label: 'Mutations', }); onc.setTrackData(mutation_track_id, mutation_data); From 913a0249785c45592f7baee36e4b36c953ffb0a0 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Tue, 23 Jun 2015 18:06:15 -0400 Subject: [PATCH 073/343] delete legacy code --- packages/oncoprintjs/src/js/oncoprint.js | 77 ------------------------ 1 file changed, 77 deletions(-) diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 6cb7b5e4091..cb079c69bbf 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -379,8 +379,6 @@ var OncoprintSVGRenderer = (function() { this.cell_div; this.legend_svg; this.cells = {}; - this.curr_clip_bounds = new VisibleIndexBounds(-1, -2); - this.prev_clip_bounds = new VisibleIndexBounds(-1, -2); this.clip_zone_start = 0; @@ -436,26 +434,6 @@ var OncoprintSVGRenderer = (function() { var parentRect = parent.getBoundingClientRect(); return {x: parent.scrollLeft, width: parentRect.right - parentRect.left}; }; - OncoprintSVGRenderer.prototype.getClipBounds = function() { - var parent = this.cell_container_node; - var parentRect = parent.getBoundingClientRect(); - var x = parent.scrollLeft; - var width = parentRect.right-parentRect.left; - var cell_unit = this.oncoprint.getCellWidth() + this.oncoprint.getCellPadding(); - - var first_visible = Math.floor(x / cell_unit); - var last_visible = Math.ceil((x + width) / cell_unit); - - return this.curr_clip_bounds.set(first_visible, last_visible); - }; - OncoprintSVGRenderer.prototype.getPreviousClipBounds = function() { - return this.prev_clip_bounds; - }; - OncoprintSVGRenderer.prototype.getScrollRect = function() { - var parent = this.cell_div.node().parentNode; - var parentRect = parent.getBoundingClientRect(); - return {x: parent.scrollLeft, y: parent.scrollTop, width: parentRect.right - parentRect.left, height: parentRect.bottom - parentRect.top}; - }; OncoprintSVGRenderer.prototype.setRuleSet = function(track_id, type, params) { OncoprintRenderer.prototype.setRuleSet.call(this, track_id, type, params); this.render(); @@ -695,58 +673,3 @@ var OncoprintSVGRenderer = (function() { } return OncoprintSVGRenderer; })(); - -// var CanvasSVGDrawer = (function() { -// function CanvasSVGDrawer(canvas, svg) { -// this.canvas = canvas; -// this.ctx = this.canvas.node().getContext('2d'); -// this.svg = svg; -// this.clipping_buffer = 300; -// setInterval($.proxy(this.draw, this), 1000/60); -// var self = this; -// $(this.canvas.node().parentNode).on('scroll', function() { -// self.draw(); -// }); -// } -// var containsPoint = function(x, y, rect) { -// return x >= rect.x && y >= rect.y && x < rect.x + rect.width && y < rect.y + rect.height; -// }; -// CanvasSVGDrawer.prototype.draw = function() { -// this.canvas.attr('width', this.svg.attr('width')) -// .attr('height', this.svg.attr('height')); -// var canvas_node = this.canvas.node(); -// var ctx = this.ctx; -// ctx.clearRect(0, 0, canvas_node.width, canvas_node.height); -// ctx.beginPath(); -// var view_rect = this.getParentViewRect(); -// for (var child = this.svg.node().firstChild; child; child = child.nextSibling) { -// this.drawNode(child, view_rect); -// } -// ctx.stroke(); -// ctx.closePath(); -// }; -// CanvasSVGDrawer.prototype.getParentViewRect = function() { -// var parent = this.canvas.node().parentNode; -// var parentRect = parent.getBoundingClientRect(); -// return {x: parent.scrollLeft, y: parent.scrollTop, width: parentRect.right - parentRect.left, height: parentRect.bottom - parentRect.top}; -// }; -// CanvasSVGDrawer.prototype.drawNode = function(node, view_rect) { -// var d3_node = d3.select(node); -// var ctx = this.ctx; -// var pos = this.svg.node().createSVGPoint(); -// pos = pos.matrixTransform(node.getCTM()); -// if (containsPoint(pos.x, pos.y, view_rect)) { -// switch (node.tagName) { -// case 'g': -// for (var child = node.firstChild; child; child = child.nextSibling) { -// this.drawNode(child, view_rect); -// } -// case 'rect': -// ctx.fillStyle = d3_node.attr('fill'); -// ctx.strokeStyle = d3_node.attr('stroke') || ctx.fillStyle; -// ctx.fillRect(pos.x, pos.y, d3_node.attr('width'), d3_node.attr('height')); -// } -// } -// }; -// return CanvasSVGDrawer; -// })(); From 70e517ed739f21d43d28eb87579ed9969d15eeb9 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Tue, 23 Jun 2015 19:53:01 -0400 Subject: [PATCH 074/343] First pass at dragging. A lot of technical debt and optimization left --- packages/oncoprintjs/src/css/oncoprint.css | 13 +++ packages/oncoprintjs/src/js/oncoprint.js | 111 +++++++++++++++++++-- packages/oncoprintjs/test/index.html | 3 +- packages/oncoprintjs/test/js/test_page.js | 15 ++- 4 files changed, 123 insertions(+), 19 deletions(-) diff --git a/packages/oncoprintjs/src/css/oncoprint.css b/packages/oncoprintjs/src/css/oncoprint.css index f76c58bd638..606957836c0 100644 --- a/packages/oncoprintjs/src/css/oncoprint.css +++ b/packages/oncoprintjs/src/css/oncoprint.css @@ -40,4 +40,17 @@ text-anchor: start; alignment-baseline: hanging; font-size: 12px; +} + +.dragging_label { + font-weight: bold; +} + +.noselect { + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; } \ No newline at end of file diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index cb079c69bbf..511b886d2c2 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -135,7 +135,7 @@ function Oncoprint(config) { $(self).trigger(events.SET_ID_ORDER); }; self.getTrackOrder = function() { - return self.track_order; + return self.track_order.slice(); }; self.getTrackLabel = function(track_id) { return self.tracks[track_id].config.label; @@ -237,6 +237,7 @@ function Oncoprint(config) { } var OncoprintRenderer = (function() { + // TODO: technical debt in naming, separation of track y and label y, stuff like that function OncoprintRenderer(oncoprint, config) { this.rule_sets = {}; this.clipping = true; @@ -262,6 +263,16 @@ var OncoprintRenderer = (function() { OncoprintRenderer.prototype.getRuleSet = function(track_id) { return this.rule_sets[track_id]; }; + OncoprintRenderer.prototype.getTrackTops = function() { + var ret = {}; + var y = 0; + var self = this; + _.each(this.oncoprint.getTrackOrder(), function(id) { + ret[id] = y; + y += self.getTrackRenderHeight(id); + }); + return ret; + }; OncoprintRenderer.prototype.getTrackTop = function(track_id) { var y = 0; var self =this; @@ -275,6 +286,14 @@ var OncoprintRenderer = (function() { }); return y; }; + OncoprintRenderer.prototype.getTrackRenderTops = function() { + var tops = this.getTrackTops(); + var self = this; + _.each(tops, function(top, id) { + tops[id] = top + self.oncoprint.getTrackPadding(id); + }); + return tops; + }; OncoprintRenderer.prototype.getTrackRenderTop = function(track_id) { return this.getTrackTop(track_id) + this.oncoprint.getTrackPadding(track_id); }; @@ -379,12 +398,28 @@ var OncoprintSVGRenderer = (function() { this.cell_div; this.legend_svg; this.cells = {}; + this.curr_clip_bounds = new VisibleIndexBounds(-1, -2); + this.prev_clip_bounds = new VisibleIndexBounds(-1, -2); this.clip_zone_start = 0; (function initLabelContainer() { self.label_container = d3.select(container_selector_string).append('div').classed('fixed_oncoprint_section_container', true); self.label_svg = self.label_container.append('svg'); + $(self.label_svg.node()).on("mousedown", function(evt) { + var in_track = -1; + var track_tops = self.getTrackRenderTops(); + var mouse_y = evt.clientY; + _.find(self.oncoprint.getTrackOrder(), function(id) { + if (mouse_y >= track_tops[id] && mouse_y <= track_tops[id] + self.getTrackRenderHeight(id)) { + in_track = id; + return true; + } + }); + if (in_track > -1) { + self.dragLabel(in_track); + } + }); })(); (function initCellContainer() { self.cell_container = d3.select(container_selector_string).append('div').classed('scrolling_oncoprint_section_container', true); @@ -401,7 +436,7 @@ var OncoprintSVGRenderer = (function() { } })(); - var render_all_events = [events.MOVE_TRACK, events.REMOVE_TRACK]; + var render_all_events = [events.REMOVE_TRACK]; var render_track_events = [events.ADD_TRACK, events.SET_TRACK_DATA]; var reposition_events = [events.MOVE_TRACK, events.SET_CELL_PADDING, events.SET_CELL_WIDTH]; var resize_cell_div_events = [events.SET_CELL_PADDING, events.SET_CELL_WIDTH]; @@ -426,6 +461,11 @@ var OncoprintSVGRenderer = (function() { self.positionCells(); self.clipCells(true); }); + $(oncoprint).on(events.MOVE_TRACK, function() { + // TODO: only reposition tracks that have been moved as a result - this is a fairly slow op so necessary opt + self.positionCells(); + self.renderLabels(); + }) } utils.extends(OncoprintSVGRenderer, OncoprintRenderer); @@ -434,6 +474,9 @@ var OncoprintSVGRenderer = (function() { var parentRect = parent.getBoundingClientRect(); return {x: parent.scrollLeft, width: parentRect.right - parentRect.left}; }; + OncoprintSVGRenderer.prototype.getPreviousClipBounds = function() { + return this.prev_clip_bounds; + }; OncoprintSVGRenderer.prototype.setRuleSet = function(track_id, type, params) { OncoprintRenderer.prototype.setRuleSet.call(this, track_id, type, params); this.render(); @@ -445,23 +488,32 @@ var OncoprintSVGRenderer = (function() { OncoprintSVGRenderer.prototype.getLabelSVG = function() { return this.label_svg; }; - OncoprintSVGRenderer.prototype.renderTrackLabel = function(oncoprint, track_id, rule_set, svg) { + OncoprintSVGRenderer.prototype.renderTrackLabel = function(oncoprint, track_id, rule_set, svg, label_y) { var label_class = 'label'+track_id; - var label_y = this.getTrackRenderTop(track_id); + label_y = typeof label_y === "undefined" ? this.getTrackRenderTop(track_id) : label_y; svg.selectAll('.'+label_class).remove(); svg.append('text').classed(label_class, true).text(oncoprint.getTrackLabel(track_id)) .attr('alignment-baseline', 'hanging') + .classed('noselect', true) .attr('transform', utils.translate(0, label_y)); - if (rule_set.alteredData && typeof rule_set.alteredData === 'function') { + + var altered_data_label_class = 'altered_data_label'+track_id; + if (rule_set && rule_set.alteredData && typeof rule_set.alteredData === 'function') { + svg.selectAll('.'+altered_data_label_class).remove(); var data = oncoprint.getTrackData(track_id); var num_altered = rule_set.alteredData(data).length; var percent_altered = Math.floor(100*num_altered/data.length); - svg.append('text').classed(label_class, true) + svg.append('text').classed(altered_data_label_class, true) .text(percent_altered+'%') .attr('text-anchor', 'end') .attr('alignment-baseline', 'hanging') + .classed('noselect', true) + .attr('transform', utils.translate(this.getLabelAreaWidth(), label_y)); + } else { + svg.selectAll('.'+altered_data_label_class) .attr('transform', utils.translate(this.getLabelAreaWidth(), label_y)); } + return svg.selectAll('.'+label_class+',.'+altered_data_label_class); }; OncoprintSVGRenderer.prototype.drawTrackCells = function(track_id, rule_set) { var oncoprint = this.oncoprint; @@ -576,7 +628,6 @@ var OncoprintSVGRenderer = (function() { var to_show = prev_bounds.toShow(visible_bounds); var to_hide = prev_bounds.toHide(visible_bounds); if (to_show.length > 0 || to_hide.length > 0) { - //this.cell_div.node().style.display = 'none'; var i, len; for (i=0, len = to_show.length; i < len; i++) { var datum_id = id_order[to_show[i]]; @@ -596,7 +647,6 @@ var OncoprintSVGRenderer = (function() { } }); } - //this.cell_div.node().style.display = 'inherit'; } this.prev_clip_bounds.set(visible_bounds.first, visible_bounds.last); }; @@ -625,6 +675,13 @@ var OncoprintSVGRenderer = (function() { OncoprintSVGRenderer.prototype.isTrackRenderable = function(track_id) { return this.getRuleSet(track_id) && this.oncoprint.getTrackData(track_id).length > 0; }; + OncoprintSVGRenderer.prototype.renderLabels = function() { + var self = this; + _.each(this.oncoprint.getTrackOrder(), function(track_id) { + var rule_set = self.getRuleSet(track_id); + self.renderTrackLabel(self.oncoprint, track_id, rule_set, self.getLabelSVG()); + }); + }; OncoprintSVGRenderer.prototype.render = function(track_id) { var self = this; this.resizeLabelSVG(); @@ -671,5 +728,43 @@ var OncoprintSVGRenderer = (function() { }); this.resizeLegendSVG(); } + OncoprintSVGRenderer.prototype.dragLabel = function(track_id) { + // TODO: everything about this method is technical debt + var self = this; + var other_tracks = this.oncoprint.getTrackOrder(); + var curr_pos = other_tracks.indexOf(track_id); + other_tracks.splice(curr_pos, 1); + + var new_track_pos; + var track_tops_true = this.getTrackRenderTops(); + delete track_tops_true[track_id]; + var handler = function(evt) { + var track_tops = $.extend({},{},track_tops_true); + var mouse_y = evt.clientY; + var render_y = Math.max(0, mouse_y); + render_y = Math.min(render_y, self.getLabelAreaHeight()); + self.renderTrackLabel(self.oncoprint, track_id, false, self.label_svg, mouse_y).classed('dragging_label', true); + + var first_below = 0; + while (track_tops[other_tracks[first_below]] < render_y && first_below < other_tracks.length) { + first_below += 1; + } + if (first_below > 0) { + track_tops[other_tracks[first_below-1]] -= 3; + } + if (first_below < other_tracks.length) { + track_tops[other_tracks[first_below]] += 3; + } + new_track_pos = first_below; + _.each(track_tops, function(top, id) { + self.renderTrackLabel(self.oncoprint, id, false, self.label_svg, top); + }); + }; + $(self.label_svg.node()).on("mousemove", handler); + $(self.label_svg.node()).on("mouseup", function(evt) { + $(self.label_svg.node()).off("mousemove", handler); + self.oncoprint.moveTrack(track_id, new_track_pos); + }); + }; return OncoprintSVGRenderer; })(); diff --git a/packages/oncoprintjs/test/index.html b/packages/oncoprintjs/test/index.html index 5b332c379bb..b1e3df2756b 100644 --- a/packages/oncoprintjs/test/index.html +++ b/packages/oncoprintjs/test/index.html @@ -10,8 +10,7 @@ - - + diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index c6f4f783ae7..fbf2aed9858 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -23,7 +23,7 @@ $('#toggle_whitespace').click(function() { onc.setCellPadding(0); } }); - $('#to_svg_btn').click(function() { + /*$('#to_svg_btn').click(function() { onc.toSVG(d3.select('#svg_container')); var DOMURL = window.URL || window.webkitURL || window; var ctx = $('#canvas')[0].getContext('2d'); @@ -39,17 +39,14 @@ $('#toggle_whitespace').click(function() { ctx.drawImage(img, 0, 0); DOMURL.revokeObjectURL(url); }; - }); - var clipping = true; - $('#clipping_btn').click(function() { - clipping = !clipping; - onc.setClipping(clipping); - console.log("Clipping is "+clipping); - }); + });*/ var gender_data; var gender_track_id; var gender_data_promise = $.getJSON('./gbm/gender-gbm.json'); +$('#move_btn').click(function() { + onc.moveTrack(gender_track_id, 4); +}) var mutation_data; var mutation_track_id; @@ -72,7 +69,7 @@ $.when(gender_data_promise).then(function() { legend_label: 'Gender' }); onc.setTrackData(gender_track_id, gender_data); - for (var i=0; i<2; i++) { + for (var i=0; i<0; i++) { var dup_gender_track_id = onc.addTrack({label: 'Gender'}); onc.useSameRuleSet(dup_gender_track_id, gender_track_id); onc.setTrackData(dup_gender_track_id, gender_data); From d399525e7b111cd351be04f12806c3d3c494e2db Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Wed, 24 Jun 2015 14:46:56 -0400 Subject: [PATCH 075/343] fixing bugs --- packages/oncoprintjs/src/js/oncoprint.js | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 511b886d2c2..2efd60a61be 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -187,10 +187,25 @@ function Oncoprint(config) { new_position = Math.max(0, new_position); var old_position = self.track_order.indexOf(track_id); - self.track_order.splice(old_position, 1); - self.track_order.splice(new_position, 0, track_id); - - $(self).trigger(events.MOVE_TRACK, {track_id: track_id, tracks:self.tracks, track_order: self.track_order}); + var new_order = self.track_order.slice(); + var i; + var moved_tracks = []; + if (old_position > new_position) { + for (i=new_position+1; i<=old_position; i++) { + new_order[i] = self.track_order[i-1]; + moved_tracks.push(self.track_order[i-1]); + } + moved_tracks.push(track_id); + } else if (old_position < new_position) { + for (i=old_position; i Date: Wed, 24 Jun 2015 19:03:46 -0400 Subject: [PATCH 076/343] cleaning up dragging and adding test data --- packages/oncoprintjs/makefakedata.py | 14 + packages/oncoprintjs/src/css/oncoprint.css | 8 + packages/oncoprintjs/src/js/oncoprint.js | 162 +- packages/oncoprintjs/src/js/utils.js | 4 + .../oncoprintjs/test/data/gbm/gender-gbm.json | 20001 +++++++++++++++- packages/oncoprintjs/test/index.html | 1 - packages/oncoprintjs/test/js/test_page.js | 3 - 7 files changed, 20098 insertions(+), 95 deletions(-) create mode 100644 packages/oncoprintjs/makefakedata.py diff --git a/packages/oncoprintjs/makefakedata.py b/packages/oncoprintjs/makefakedata.py new file mode 100644 index 00000000000..4c955e14d7b --- /dev/null +++ b/packages/oncoprintjs/makefakedata.py @@ -0,0 +1,14 @@ +from random import random +def samplename(): + return str(random())+str(random())+str(random())+str(random()); + +f = open("fakedata.txt","w") +for i in xrange(4000): + f.write("{\n") + f.write('"attr_id":"GENDER",\n') + f.write('"attr_val":"%s",\n'%("MALE" if random() < 0.5 else "FEMALE")) + f.write('"sample":"%s"\n'%samplename()) + f.write("},\n") +f.close() + + diff --git a/packages/oncoprintjs/src/css/oncoprint.css b/packages/oncoprintjs/src/css/oncoprint.css index 606957836c0..feb3cf17063 100644 --- a/packages/oncoprintjs/src/css/oncoprint.css +++ b/packages/oncoprintjs/src/css/oncoprint.css @@ -53,4 +53,12 @@ -moz-user-select: none; -ms-user-select: none; user-select: none; +} + +.cell-qtip { + background-color: rgba(0,0,0,0) !important; + border: none !important; +} +.cell-qtip .qtip-content { + background-color: rgba(255,255,255,0.9) !important; } \ No newline at end of file diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 2efd60a61be..67d66a454ec 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -35,8 +35,6 @@ var globals = require('./globals'); var utils = require('./utils'); var RuleSet = require('./RuleSet'); -// TODO: use self everywhere - var defaultOncoprintConfig = { cell_width: 6, cell_padding: 3, @@ -164,8 +162,9 @@ function Oncoprint(config) { }; self.getTrackDatumIdAccessor = function(track_id) { + var key = self.getTrackDatumIdKey(track_id); return function(d) { - return d[self.tracks[track_id].config.datum_id_key]; + return d[key]; }; }; self.getTrackDatumIdKey = function(track_id) { @@ -242,17 +241,9 @@ function Oncoprint(config) { self.setIdOrder(utils.stableSort(self.getIdOrder(), lexicographically_ordered_cmp)); $(self).trigger(events.SORT, {id_order: self.id_order}); }; - - self.sortOnTrack = function(track_id, data_cmp) { - throw "not implemented"; - }; - self.sortOnTracks = function(track_ids, data_cmps) { - throw "not implemented"; - }; } var OncoprintRenderer = (function() { - // TODO: technical debt in naming, separation of track y and label y, stuff like that function OncoprintRenderer(oncoprint, config) { this.rule_sets = {}; this.clipping = true; @@ -284,24 +275,11 @@ var OncoprintRenderer = (function() { var self = this; _.each(this.oncoprint.getTrackOrder(), function(id) { ret[id] = y; - y += self.getTrackRenderHeight(id); + y += self.getRenderedTrackHeight(id); }); return ret; }; - OncoprintRenderer.prototype.getTrackTop = function(track_id) { - var y = 0; - var self =this; - _.find(this.oncoprint.getTrackOrder(), function(id) { - if (id === track_id) { - return true; - } else { - y += self.getTrackRenderHeight(id); - return false; - } - }); - return y; - }; - OncoprintRenderer.prototype.getTrackRenderTops = function() { + OncoprintRenderer.prototype.getCellTops = function() { var tops = this.getTrackTops(); var self = this; _.each(tops, function(top, id) { @@ -309,34 +287,23 @@ var OncoprintRenderer = (function() { }); return tops; }; - OncoprintRenderer.prototype.getTrackRenderTop = function(track_id) { - return this.getTrackTop(track_id) + this.oncoprint.getTrackPadding(track_id); + OncoprintRenderer.prototype.getLabelTops = function() { + return this.getCellTops(); }; - OncoprintRenderer.prototype.getTrackRenderHeight = function(track_id) { + OncoprintRenderer.prototype.getRenderedTrackHeight = function(track_id) { return this.oncoprint.getTrackHeight(track_id) + 2*this.oncoprint.getTrackPadding(track_id); }; OncoprintRenderer.prototype.getCellX = function(index) { return index*(this.oncoprint.getCellWidth()+this.oncoprint.getCellPadding()); }; - OncoprintRenderer.prototype.getCellPos = function(track_id, datum_id) { - var index = this.oncoprint.getIdOrder().indexOf(datum_id); - if (index > -1) { - return {x: index*(this.oncoprint.getCellWidth()+this.oncoprint.getCellPadding()), - y: this.getTrackRenderTop(track_id)}; - } else { - return [-1,-1]; - } - }; OncoprintRenderer.prototype.getCellAreaWidth = function() { return this.oncoprint.getIdOrder().length*(this.oncoprint.getCellWidth() + this.oncoprint.getCellPadding()); }; OncoprintRenderer.prototype.getCellAreaHeight = function() { - var height = 0; - var self = this; - _.each(this.oncoprint.getTrackOrder(), function(track_id) { - height += self.getTrackRenderHeight(track_id); - }); - return height; + var track_tops = this.getTrackTops(); + var track_order = this.oncoprint.getTrackOrder(); + var last_track = track_order[track_order.length-1]; + return track_tops[last_track] + this.getRenderedTrackHeight(last_track); }; OncoprintRenderer.prototype.getLabelAreaWidth = function() { var label_font = this.getLabelFont(); @@ -422,11 +389,12 @@ var OncoprintSVGRenderer = (function() { self.label_container = d3.select(container_selector_string).append('div').classed('fixed_oncoprint_section_container', true); self.label_svg = self.label_container.append('svg'); $(self.label_svg.node()).on("mousedown", function(evt) { + // TODO: fix this shit UP var in_track = -1; - var track_tops = self.getTrackRenderTops(); + var track_tops = self.getTrackTops(); var mouse_y = evt.clientY; _.find(self.oncoprint.getTrackOrder(), function(id) { - if (mouse_y >= track_tops[id] && mouse_y <= track_tops[id] + self.getTrackRenderHeight(id)) { + if (mouse_y >= track_tops[id] && mouse_y <= track_tops[id] + self.getRenderedTrackHeight(id)) { in_track = id; return true; } @@ -476,22 +444,15 @@ var OncoprintSVGRenderer = (function() { self.positionCells(); self.clipCells(true); }); - $(oncoprint).on(events.MOVE_TRACK, function() { + $(oncoprint).on(events.MOVE_TRACK, function(evt, data) { // TODO: only reposition tracks that have been moved as a result - this is a fairly slow op so necessary opt - self.positionCells(); + self.positionCells(data.moved_tracks); self.renderLabels(); }) } utils.extends(OncoprintSVGRenderer, OncoprintRenderer); - OncoprintSVGRenderer.prototype.getViewInterval = function() { - var parent = this.cell_container_node; - var parentRect = parent.getBoundingClientRect(); - return {x: parent.scrollLeft, width: parentRect.right - parentRect.left}; - }; - OncoprintSVGRenderer.prototype.getPreviousClipBounds = function() { - return this.prev_clip_bounds; - }; + // Rule sets OncoprintSVGRenderer.prototype.setRuleSet = function(track_id, type, params) { OncoprintRenderer.prototype.setRuleSet.call(this, track_id, type, params); this.render(); @@ -500,14 +461,41 @@ var OncoprintSVGRenderer = (function() { OncoprintRenderer.prototype.useSameRuleSet.call(this, target_track_id, source_track_id); this.render(); } + + // Containers OncoprintSVGRenderer.prototype.getLabelSVG = function() { return this.label_svg; }; + OncoprintSVGRenderer.prototype.resizeCellDiv = function() { + this.cell_div.style('min-width', this.getCellAreaWidth()+'px') + .style('min-height', this.getCellAreaHeight()+'px'); + }; + OncoprintSVGRenderer.prototype.resizeLabelSVG = function() { + this.getLabelSVG().attr('width', this.getLabelAreaWidth()) + .attr('height', this.getLabelAreaHeight()); + }; + OncoprintSVGRenderer.prototype.resizeLegendSVG = function() { + var new_height = 0; + var new_width = 0; + var point = this.legend_svg.node().createSVGPoint(); + utils.d3SelectChildren(this.legend_svg, 'g').each(function() { + point.x = 0; + point.y = 0; + point = point.matrixTransform(this.getCTM()); + var bbox = this.getBBox(); + new_height = Math.max(new_height, point.y+bbox.height); + new_width = Math.max(new_width, point.x + bbox.width); + }); + this.legend_svg.attr('width', new_width).attr('height', new_height); + }; + + // Labels OncoprintSVGRenderer.prototype.renderTrackLabel = function(oncoprint, track_id, rule_set, svg, label_y) { var label_class = 'label'+track_id; - label_y = typeof label_y === "undefined" ? this.getTrackRenderTop(track_id) : label_y; + label_y = typeof label_y === "undefined" ? this.getLabelTops()[track_id] : label_y; svg.selectAll('.'+label_class).remove(); svg.append('text').classed(label_class, true).text(oncoprint.getTrackLabel(track_id)) + .attr('font', this.getLabelFont()) .attr('alignment-baseline', 'hanging') .classed('noselect', true) .attr('transform', utils.translate(0, label_y)); @@ -520,6 +508,7 @@ var OncoprintSVGRenderer = (function() { var percent_altered = Math.floor(100*num_altered/data.length); svg.append('text').classed(altered_data_label_class, true) .text(percent_altered+'%') + .attr('font', this.getLabelFont()) .attr('text-anchor', 'end') .attr('alignment-baseline', 'hanging') .classed('noselect', true) @@ -530,6 +519,8 @@ var OncoprintSVGRenderer = (function() { } return svg.selectAll('.'+label_class+',.'+altered_data_label_class); }; + + // Cells OncoprintSVGRenderer.prototype.drawTrackCells = function(track_id, rule_set) { var oncoprint = this.oncoprint; var data = oncoprint.getTrackData(track_id); @@ -557,7 +548,7 @@ var OncoprintSVGRenderer = (function() { text: tooltip_html }, position: {my:'left bottom', at:'top middle', viewport: $(window)}, - style: { classes: 'qtip-rounded qtip-shadow qtip-lightyellow' }, + style: { classes: 'cell-qtip', border: 'none'}, show: {event: "mouseover"}, hide: {fixed: true, delay: 100, event: "mouseout"} }); @@ -575,12 +566,8 @@ var OncoprintSVGRenderer = (function() { bound_svg.selectAll('*').remove(); rule_set.apply(bound_svg, data, id_accessor, oncoprint.getCellWidth(), oncoprint.getCellHeight(track_id)); }; - OncoprintSVGRenderer.prototype.positionCells = function() { - var self = this; - _.each(this.oncoprint.getTrackOrder(), function(track_id) { - self.positionTrackCells(track_id); - }); - }; + + // Positioning OncoprintSVGRenderer.prototype.positionTrackCells = function(track_id, bound_svg) { var oncoprint = this.oncoprint; if (!bound_svg) { @@ -590,13 +577,30 @@ var OncoprintSVGRenderer = (function() { var self = this; var id_key = oncoprint.getTrackDatumIdKey(track_id); var id_order = oncoprint.getIdOrder(); - var y = this.getTrackRenderTop(track_id); + var y = this.getCellTops()[track_id]; bound_svg.transition().style('left', function(d,i) { return self.getCellX(id_order.indexOf(d[id_key])); }).style('top', y).each("end", function() { $(self).trigger(events.FINISHED_POSITIONING); }); }; + OncoprintSVGRenderer.prototype.positionCells = function(track_ids) { + track_ids = track_ids || this.oncoprint.getTrackOrder(); + var self = this; + _.each(track_ids, function(track_id) { + self.positionTrackCells(track_id); + }); + }; + + // Clipping + OncoprintSVGRenderer.prototype.getViewInterval = function() { + var parent = this.cell_container_node; + var parentRect = parent.getBoundingClientRect(); + return {x: parent.scrollLeft, width: parentRect.right - parentRect.left}; + }; + OncoprintSVGRenderer.prototype.getPreviousClipBounds = function() { + return this.prev_clip_bounds; + }; OncoprintSVGRenderer.prototype.getClipViewInterval = function() { var self = this; var view = this.getViewInterval(); @@ -665,28 +669,7 @@ var OncoprintSVGRenderer = (function() { } this.prev_clip_bounds.set(visible_bounds.first, visible_bounds.last); }; - OncoprintSVGRenderer.prototype.resizeCellDiv = function() { - this.cell_div.style('min-width', this.getCellAreaWidth()+'px') - .style('min-height', this.getCellAreaHeight()+'px'); - }; - OncoprintSVGRenderer.prototype.resizeLabelSVG = function() { - this.getLabelSVG().attr('width', this.getLabelAreaWidth()) - .attr('height', this.getLabelAreaHeight()); - }; - OncoprintSVGRenderer.prototype.resizeLegendSVG = function() { - var new_height = 0; - var new_width = 0; - var point = this.legend_svg.node().createSVGPoint(); - utils.d3SelectChildren(this.legend_svg, 'g').each(function() { - point.x = 0; - point.y = 0; - point = point.matrixTransform(this.getCTM()); - var bbox = this.getBBox(); - new_height = Math.max(new_height, point.y+bbox.height); - new_width = Math.max(new_width, point.x + bbox.width); - }); - this.legend_svg.attr('width', new_width).attr('height', new_height); - }; + OncoprintSVGRenderer.prototype.isTrackRenderable = function(track_id) { return this.getRuleSet(track_id) && this.oncoprint.getTrackData(track_id).length > 0; }; @@ -751,13 +734,12 @@ var OncoprintSVGRenderer = (function() { other_tracks.splice(curr_pos, 1); var new_track_pos; - var track_tops_true = this.getTrackRenderTops(); + var track_tops_true = this.getLabelTops(); delete track_tops_true[track_id]; var handler = function(evt) { var track_tops = $.extend({},{},track_tops_true); var mouse_y = evt.clientY; - var render_y = Math.max(0, mouse_y); - render_y = Math.min(render_y, self.getLabelAreaHeight()); + var render_y = utils.minMax(mouse_y, 0, self.getLabelAreaHeight()); self.renderTrackLabel(self.oncoprint, track_id, false, self.label_svg, mouse_y).classed('dragging_label', true); var first_below = 0; diff --git a/packages/oncoprintjs/src/js/utils.js b/packages/oncoprintjs/src/js/utils.js index d985270754a..93af3917109 100644 --- a/packages/oncoprintjs/src/js/utils.js +++ b/packages/oncoprintjs/src/js/utils.js @@ -52,6 +52,10 @@ exports.makeIdCounter = function() { }; }; +exports.minMax = function(t, a, b) { + return Math.max(Math.min(b,t), a); +}; + exports.makeD3SVGElement = function(tag) { return d3.select(document.createElementNS('http://www.w3.org/2000/svg', tag)); }; diff --git a/packages/oncoprintjs/test/data/gbm/gender-gbm.json b/packages/oncoprintjs/test/data/gbm/gender-gbm.json index 6571af185da..750e8f2fc14 100644 --- a/packages/oncoprintjs/test/data/gbm/gender-gbm.json +++ b/packages/oncoprintjs/test/data/gbm/gender-gbm.json @@ -2462,7 +2462,20006 @@ "attr_id":"GENDER", "attr_val":"FEMALE", "sample":"0.447639363432" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.03846505573240.3293854992340.8842338674610.30958931548" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1187029759330.3535329912360.4035310650850.997857170882" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.02956719933020.9365843637870.1989423476070.817240179141" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3146420401190.6840434010070.4351505106050.920204335201" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5205048588490.159283022040.7889885377890.425761637236" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7406116031840.7008555078890.7367821444830.0967619384483" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4428189993630.1188570384680.6335274462120.453988611935" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4686686478620.9475883280750.09855858000460.646224633602" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5614751831070.3176461907890.4522687837980.869773430783" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6139664941890.7171846724770.9930413942530.812173366829" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1073529317580.5297510598110.55361130420.782283429974" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2406084670470.8619683607710.9522989568170.364443546614" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2704541731190.4176393032020.1970370328070.837031734705" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.270232995340.4347801366350.6901510216680.909444369328" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1557623493430.1536655273330.8389106850030.170285436526" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4044099056780.6734210720280.1086615070530.881126334145" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4383560370930.6683838902660.5058242369770.672636541731" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.06438193312150.849118870150.2308894608110.743240454384" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.09160299082880.9434261736160.1783078560390.53668575773" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.329910041840.0668793796210.8939432537790.985985482868" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.006112559574850.1006825832330.6906522096110.312755662556" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7164875057570.257723722260.3200551857040.608883744514" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9444052923040.7112348791690.6111664820150.203363880494" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5109600822880.9489854697710.3728244590840.21090242127" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7647522671140.5872788873080.4428461843270.935120108916" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4076152788170.3548989053580.1582161343940.801448985944" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5927416928250.6413621119520.7090359563470.597652744865" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1628954593440.5340536563830.185897524310.00134749084412" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5665128444820.6716117756960.928692993630.0825027747901" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7896569337920.8193941034230.4908224446060.346061197855" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.07505875858420.6986252261480.1745356565720.687888065509" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2522130218530.8199706573440.2526364832870.540680799519" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9156815476090.6733626955070.1618846606540.298972790151" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7367014393470.2300951865430.6877505431920.706638285673" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9276468989960.271505451270.3992064993010.807492373774" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.002618301765420.6083033427720.3922558228350.904361293137" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9418846407990.5060098568140.6661752781670.29616190203" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6139532697920.6696298515140.7392545808710.630691035806" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6876698536040.1111225742660.6340675859380.54793987888" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5220891149440.5631224208850.303145255510.127861485215" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5750830138650.2009516508180.4851681146410.399368610071" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2176514508020.9483822281150.9792027951450.391864911069" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7050989095250.772472715850.6125712376420.542514355206" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.922271140270.8216335646910.5025314075290.217811189316" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7863443803170.6225935620010.09059919698690.676702644494" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6373955976050.4943789573540.05706623610540.931803697737" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2326874085430.1187078664860.470691097810.496434054751" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7138496653290.4210754508660.3022677918020.107399890359" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9966761411550.04422135095060.6438331865050.741306962502" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7717611978030.4130822941630.5217972871740.0316114714942" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7741179724080.9317595270720.3388218190880.439975202139" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.0998102706870.3631821326140.6448342220660.502033729949" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8762464946330.8179315869640.8325635425850.2917671241" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9379741751090.8563986769230.5591725284190.68049084796" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.03099488722240.06231929217250.5029523946660.617895742155" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6515872443640.926355255050.65208450420.879888276936" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2167742021340.1801707149420.1534363729450.735037865858" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3106576697370.8493626808320.08274052232180.251955341657" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5988532518030.1392153756010.9692167381630.600861252264" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1672226862690.2142262542350.7031585017680.000314861864972" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.05847290195050.3402606095190.3566753184680.76380614952" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7319750095870.05737186508090.07323110803180.113745436509" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4297106110380.6869972068970.5999560550420.474128694946" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7840679983870.1872403935970.9230908848360.0160058158135" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1525985558420.1547358110660.4389783073720.0898766637915" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7275844563360.6657557026770.2693276352450.102659328164" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2805448999560.3191682421310.2786063706190.870913067154" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6793511932720.5196320002850.4752962958330.225531539728" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6083712843330.855152345830.5097752563180.956953009459" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8098648127340.0001818240414460.8413540321680.52497892581" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.248268871450.0521289300290.7763244767190.432951158338" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.053879192870.6209050509420.00269887502840.401283969016" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1984177458640.7220318707150.610190054260.370642187436" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1903185887050.3468335738030.7938257768020.842000667912" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3638110473870.9820806968310.4432707236860.0990892321762" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1173089161840.3892145357770.6903892793530.406141131659" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.07497050370050.4083735326230.4925075509780.750068539214" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5593038978610.6175164394030.9017285738880.255921714966" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2401505007980.5600867744650.8155845290970.195001944113" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.08857643594860.6582343373220.7570140142410.0871635412431" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5981323441750.9644210606220.8277487651660.690688485618" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.05745633638720.8807189752950.6922148110190.62756219218" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3242844305430.8367404640620.5541407530020.835424016171" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9218059029890.1944198069840.5722092867750.249460224969" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9664241877580.06256925851430.6763819160310.394835472017" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.32126639940.02447466125260.0177280077740.725184102375" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3555785376190.5940794606230.2097729920460.389300159433" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2025165370530.4125664129970.7782658758140.474143319115" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4423112255010.1022141420420.06867712752280.626246644638" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.02742189088110.9118866812750.7614910087150.117521770133" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8110242870810.8917723770810.5534234635710.283400138888" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.198715699620.5643114779910.5997838692550.810316219224" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1593107545290.8348290935110.2703647558830.965584800317" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7537385587280.7702112643170.6752023509230.0636705059026" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4564689194280.6880549510520.3103528907690.258131267969" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.20304478240.494120019790.04208919515150.843809337717" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8950766877110.836866179050.1776368444010.798354359728" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.818136067070.3218687482020.01212597274410.984316081388" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6187870459560.1945054236530.8069510648440.326631002819" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3981585948210.03571140745420.01664412420460.638609993073" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7441160742750.6806742591640.9348011127190.869250108168" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9656669362570.4678950763840.7930636735580.293980824442" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1650146330720.3163288087280.4726020852970.792753799549" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8727757937080.9017627985740.08713836952480.0625563564655" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8211168483040.02967978964740.1051082282540.49724047613" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8836109984380.5045291117260.4077260781870.503213019961" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2438667722120.0144897657280.1062913290470.656193960769" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3147839584420.9073364233080.2553958463360.69469686172" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.894551213150.2321029396390.4647794639420.406066989598" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3975227765030.1657369186560.2707886819040.478219138058" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.563858812660.8282180549640.9064380862460.0543244481611" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9198463007760.2277410860510.2818017971660.368130834054" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2772120486290.4261862632960.6334183798010.117995246983" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.09316296972710.5778629260930.6810164724610.0187019466944" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7694615915370.6767902132880.6237130199370.95089732143" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.03873664605580.3728149172650.5408815043730.546955307107" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2108792661740.1511471630730.2946129673780.498462013224" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8628130405190.9382336746550.9679270477530.381025147639" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.810193713720.7198779590650.1046231145170.317004678627" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4444506811090.2151021220840.3137701242040.0193715791985" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3895649403940.7252670579440.323576114210.401887813466" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8728734677780.9745866871980.06921263088240.601715487296" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.04988485543830.6839478691130.8682468998240.331261258748" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2229769930590.7140794551780.6593032373120.269689517127" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4269011497940.3699409367310.6666267720460.57557661918" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9950933726070.295260564820.5530031884710.683072602449" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3302404639990.6394751840910.2603451470090.8324491072" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.006326879172190.9508064107870.1604682995870.531041480683" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.22693611690.5293911029920.6460843308110.966048619456" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3441039353610.3357256694170.002109322086940.247687010385" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7831024392280.2800258075580.9760576355140.87353723298" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3794041234390.6926580952210.8599571451270.136651423037" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4300673979860.5290602636640.3672830968460.815085888546" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2076454741260.5973391712970.5891190240450.737818965955" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6564994614740.471415181240.1714809669750.110218455663" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6057170895170.1103994662410.08984185455990.388971779353" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.52306565480.7766221175280.953294820460.839286700107" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.03582819856210.5185603421690.08364747514840.790145254021" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7820919358320.6039118842420.295657616970.019858988542" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8376183512120.6002573725880.6353206223160.118788234202" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6847312030940.07192107938870.9243931171970.259645793245" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6337295585020.06220930856230.09453204393310.233547041198" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.02789932171440.9848576444740.4110444620430.182901822942" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2098420681980.1174889848710.4692760547770.191069949152" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7912897704250.8207021376230.524889549060.17775354473" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7003442254140.4043495492730.6662338997620.102502836227" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7424273681520.3775430399380.2836013421340.631655529059" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.0328501059470.2019611230030.891495328650.866432943393" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5698845949710.9796771773790.4372943957610.684110384074" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2192825511040.4436585924530.2121221637360.940539081691" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.0782711425390.7844914609770.4068992464940.16641720419" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2598784207550.04573135877160.7199790955690.207670375234" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.64768037350.8878816424530.1157109428070.280225778089" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7933972385890.9301223886790.2980025764370.111491834701" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.07129223399620.4639776571010.1359845321730.81539159192" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9557211519720.06799770898720.3027228087580.712461367846" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.04110840803040.5683118238280.9929034410180.305116928047" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8442528570740.7794641664610.5494067229030.94180107697" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2575866731250.1749224952520.06148496693730.717584504697" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7673381599510.04098837857930.4192269360280.429687800712" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5402432570990.3158636056860.6110298619440.518865588807" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4518457402440.2645226388040.169145531390.599661834988" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.04460641558240.8124585316890.39340780390.605735965525" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5145096424440.2568992080850.3893167270110.434088963628" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.0181812042190.5579434027820.4665154706740.0697764989395" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5143359841910.9266449766230.2204232516830.953770152913" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.03224667125980.6130837045240.4310065244210.390464255683" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5643329350680.8619163984870.6611012425040.592121805458" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3409647047530.5549794906350.04741136779960.247480849633" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7961482501690.3458378690050.4918434530280.27564864308" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9704482025910.7261039167340.2280621431360.176843975113" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5002835783850.5943796712340.9362756014360.558057747617" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3929331611120.9768203051910.2608552188680.114359937228" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7711198550820.2017299133330.001816098418980.559993556793" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7598628839070.2797914998920.8584337036930.177252864688" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.172530331990.7629778220440.1949316986130.5788341557" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5025453749850.7608052170880.5433869326530.00112603318909" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1616352655430.2434714354450.7450275716980.414048139364" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7480020883710.161006567830.5235950627820.74568886961" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3279122546440.5345872629340.09914796905240.0737447113097" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5983651641050.5526157319140.6715235084730.706195608754" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.49313380260.06796170736050.08236215656140.0654082163038" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8692227550640.04125979061940.5245122489120.319536742227" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.04527931619290.83840599970.5496727993430.192303940454" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7253619021570.2309710320120.302576473670.583907323507" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6055002756730.1080733959330.6831121121220.0164648231913" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3643517554540.2898190702180.4398749067630.268190334444" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8702883784810.4904003424570.01668215990620.536019866384" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5764751770540.2095845100670.6793473023630.441760061118" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.217054188670.4474747127850.4702792990130.539431000338" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.369681410960.2288892850820.6379473088760.494058091749" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.09459353284380.1806805572220.7517814680940.834381011654" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9005833628330.8746323397360.2362191555570.813954382677" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.05145269636680.4394942334710.7221776977080.087827724766" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2527254867510.1244523627890.978161978820.225852585712" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8824407500380.6896080980860.5040854053090.335485264814" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5664679667620.5198269276310.5582986592230.199184127445" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2313444671950.06446184898560.9039010773550.177401098571" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.09059444066420.5189416686160.8338839029710.180081624398" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.763376035520.8707732997070.5570160919880.711615065099" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5614705405510.4836286035930.507354994230.875091036684" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7576359770340.6883543706930.04995334945280.0180126065191" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.0648091193040.7178384733590.275728578940.562252369811" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8590222782170.7799174100460.5061946014020.78869230454" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2166671807950.3433858515890.6368610290620.696836331107" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7152853865220.3724076438570.9314560264530.592682790926" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.814061412140.9438272770030.09668320768170.140057446806" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2502878039820.2114772969080.2511680222050.0432043114179" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.93642098450.2024828652420.4278737681840.673846096818" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1750826937590.6746786324340.02849926991130.97158203792" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.326457299910.5417061063640.2735875423780.306594425094" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4017186596940.1525981731620.7076760028310.726670810303" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3746701082290.2521107932350.4740828554970.794914616081" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.02186358951890.4882754151050.7539162263120.728972081183" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4639655728770.8545530523780.5859765500530.780631572855" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2112994538260.7357673718310.5235038814910.763411380773" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4699238018320.8617173864890.8620670845120.462972260762" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9773473349010.7343186764430.8612838443190.349477466507" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.01581429965640.7643955604770.918453120450.484993859173" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1284917545220.6036772329010.8556532739070.713702446934" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4884566853730.7049538303920.4750689805530.820448287927" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1749143975440.5991087639670.007410453202660.270608536815" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4955668523520.2996547676830.9075780923940.256927852618" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9349057538860.173018342010.40131670580.992293361651" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9617279232430.9395011685640.5769611326960.192265113101" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.139116820880.1552849144690.7432141798490.924358762988" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7371626130150.07426582158910.746197691260.308699609559" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3760838804820.6116214141760.9034655315520.961458911252" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4945847870440.491782734050.8647337881560.185960225232" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2170960034650.6746892258250.6696158852520.493494872112" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1615939405430.2245260031470.4454495798180.903955190415" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9599240945110.231698572610.01473070117560.867070995711" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2225515841390.2104866828240.4569671924220.721593213275" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9497056258290.8990117353170.9415214800620.70630562152" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1197459261090.3669352934780.3187325912840.839062033889" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9622258258580.5724652162460.8335853924060.775658996412" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8113057183980.1862692300530.03218877808030.845313662734" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.03173386927640.3480922895560.7429725326690.764558275513" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4663959632010.5573295125520.5526438499420.980846753041" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.09158036305360.7136615652260.4612906756240.358859098556" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.995603261370.6997719951330.2803508645660.630842689351" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.09664798577370.1220691505610.07081573337890.948095660543" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2662717119890.7182921455990.795216412710.436469483467" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4693160054430.01132484018090.9900191153390.268147118792" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9939064750480.0786387942670.8980383534070.361673355969" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7463506930660.01380600118980.07029491653260.149761097383" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5482956252010.591745029330.02191970586740.0537390204142" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2491451459810.06620466554880.5810023002230.95923645117" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.186522265360.4491610932610.4396945135550.162831099495" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5442465287380.8158857840260.8984527439720.490771096024" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5010481542450.7805046876190.7378264037770.348390650799" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3618440683140.1496578471270.8537621068260.403757479259" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4994563242990.7024930987850.9755783037390.657715683609" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6939606883780.7409099861970.3586581246430.866307602443" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.08533997777170.6893355952570.4347154102240.905394351286" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6861371847210.5341115738310.30693738150.815474836972" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6461772725340.4275570684760.3629741030130.942610855198" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.07403812365650.06165852884060.5878809371430.059207973755" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6973212444110.4294175457770.4225237121030.607512313553" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9558320422990.648453045770.5473959711270.28535602185" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3294713992670.8314281090510.0004515133199660.0805826691801" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6282982377370.5571727011930.04840315505780.684419613337" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1120976706360.2404415329720.9162158358320.8577657462" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.03395497046450.3528416341890.4265113058130.632983346694" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4690634397510.003096465159860.9576272699310.257348143442" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4803318903540.5530545146190.1803815305460.818372356721" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1481569469740.5120048265980.97373614190.910620541817" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8574252761680.3817078656750.384015123320.242385704946" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2360510759590.8150799291650.05713199176870.00665797282162" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.279759524430.6883966245810.6949531079060.465639721945" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9710991672250.8578372542460.2040969646240.632118691982" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2679127469010.2694653180390.3617294617280.263189445255" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7410204193260.7665717141810.3319243859390.899578531573" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.04893861107730.2594084414670.8778389112250.329216477758" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.09268476288730.1739233906340.7605243511880.579216387427" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6006880640760.2509435408940.02464681541090.189992197172" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4790968698430.889724075560.7332205637760.473763821545" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5334490991690.6010380449630.7927774240590.506409707994" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4999809614220.3718257526940.05589983476330.838409159036" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7343387132050.1576819223460.2936078219440.021197077673" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.01320618424980.226857345190.7093282650550.992622836217" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5181528240960.2380964795720.09560920750460.14394751784" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3677811830640.4787048741080.5412824942540.509470923104" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1134095708530.8530564705920.4017181405330.757628585539" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.329408811290.7548783874360.9008086625980.785820674185" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7370837712530.4814989217740.468044999560.565792790563" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1039710197850.6627545027160.2087665608310.070323296661" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.0589693806560.7880325923590.8069052375040.423740107438" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2222134572860.1571427761930.1675165779210.559370766418" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4303656928620.6315290180920.3924469683640.106724848786" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2654311916710.3679648808410.6916850414880.149654424522" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.08808010565390.8049464788660.3158964805160.895344838478" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.209635271110.9467904938810.04803597791560.34524543557" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5677110013320.7572456167720.6199436579190.488860506057" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5261075668940.4347303027380.7567775214230.950800616059" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3125402835120.5692200597210.963933270070.45027037463" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2280788014170.5973585792470.6007929854740.945111843984" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.976105824860.8021542862530.5228596146150.00144942710256" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9520699134210.7165085597340.005139391843750.0353418513729" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2359845935880.1349141258990.198630693080.337902599053" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6487717519140.2864970332350.3396574232590.676599890557" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1284676154490.2296316833640.2768889227690.02371218191" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.04449856577530.4456707582560.9439115721190.744958945294" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5247177451370.02677370662870.4887556317310.0968667558151" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.0337750109540.5488656721820.6984058748470.597551265998" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7085539615590.3344412746160.9712494318930.583624743855" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9218652513970.8347108890420.4126270516130.576633487373" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.939523118750.8470330046490.5447977783110.964266639992" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9212089224070.3506959968280.8413484014580.552969979183" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3214474407330.8563771452320.5383249676010.765339467264" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6226794821190.1289053435460.4490949969130.524962851114" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.04250114915930.1431155617160.2377469985770.937907716139" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5576121053660.6588519711590.1851090813470.280593579174" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3237318395670.1371350232060.2453039351340.449490126118" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1353958171280.9540554792070.253840033570.151740827881" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8178740579710.906262478540.5541358455090.25153130225" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4342661245790.9315044992020.8792077412690.598903448605" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7669522372690.9280392741450.07166311180220.515508374652" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4356508982650.1474272872720.6434736819710.206217314365" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7029246604210.6872569192660.08059216108790.446499091665" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2790047110250.5204193063710.4415965239130.562227457092" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4247540928960.4888165176870.9762061727560.722212780906" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9095990740890.3799960751140.3221078432510.364091064846" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6850333019730.107431044470.07048404190090.842761148576" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6407184193390.8341960678770.6642177986070.49196159651" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2421715401340.4827467619570.7479395074750.546152582303" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1450480301350.7209730001720.4291757841880.639717820045" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.136194753030.2953417459950.5595847406230.339322428747" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4990111421520.9727498209220.4795444120140.204260508298" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4320570083770.370735393770.7610836027280.159594841269" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4749266805690.2509372175770.899608781230.235966031092" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3516725981860.6099156476940.7812089951960.0943421967001" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3715288327380.4081633119140.2851920975070.661812287148" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8682496387020.4519062593610.7180465166740.423559049339" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1697793586760.5446028373080.3373830111450.0734351524282" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5757294749370.4510452773290.02105289580040.0319019254747" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6841259108290.8223795703570.4655534431890.538863465099" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5609592464950.3752675663870.2353818184090.623386241279" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6725463958590.6082644358370.2597863408560.579628836711" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6015840554510.9372573082410.1445237850490.995477753053" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.07199932080470.738444539280.3830344426410.721551827094" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3518551885280.07152276335230.3887658791780.47406066074" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.05030372935580.8974001372350.7634368144320.895114176272" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4931043702890.8160230547770.1291905645570.543147763797" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1856642926280.8924856591390.3080679688120.613312982" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1583535015990.7187759988620.3204052479610.864459684107" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9275302422720.3685008875160.7463322347920.885003177312" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.08112129567980.373369082910.04195939114590.552089327745" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8013581183410.3982948956280.3531897589750.733270779456" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.05913370546550.8986257121090.0775348145170.409092454012" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4544975726430.1010698330.4528867253370.947466608232" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4352862787380.7805959669130.3668830933750.397383511163" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3141109189280.1963917586860.7285882904580.688850319846" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2408987595930.5122932141350.6743662948370.151410378802" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2178831427980.6946289847580.2634349051690.244804295996" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.983615835510.6322337143350.4682878712740.827167508846" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1625677591150.7286209797150.2942784344280.717542208791" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2614017793680.140991663830.55287677540.455670075501" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1189324803680.02791256037690.03125714175220.701623841977" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5543932783870.9183671600110.4445823056720.0733846154186" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8978331055980.4298653642250.7243840179320.6832760165" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8194597104340.6270524202370.1536539131310.988589444055" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5042027759560.09457750955910.9714300739840.536690313134" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5804576671170.4506371743760.1159799676240.75040941295" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9738479091640.202117630230.02704753201170.568697955914" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.804575054970.9125991690360.640038319160.128413800486" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1459958479840.2797712517060.8866135164650.345916802463" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8245771293940.7197721915860.4724019790980.525861067836" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1227246764330.4586650929450.7850072870480.243163078466" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7415713860470.9350702345390.3758361763590.634645961208" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9959715544370.7185928117710.7441172992020.273468643622" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6423042225460.02950967341810.1763702563740.857960843595" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1465622932720.2425115490290.2177482314390.368758733016" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6589935449680.1502560733170.1420432367670.210894403587" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.09426181903350.2816707657440.9458171670760.213310102705" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9697543181970.6782634778050.4512951376780.263116536043" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8554872298430.4083188033550.2791996903290.408551693598" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9133739688130.9017452487220.4794894264390.932335013553" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4976141375870.7965806868320.9708780890090.113889049309" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7012227640250.7741765874060.07308342087820.393250720363" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1169955511960.8231031201840.8982164402570.955602934976" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7912402523240.1542606418080.4961765676330.512718129433" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3164793707570.2009934259890.06025126679110.190074881663" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2606488134430.3433766558170.07007020334480.699144681646" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2647358837170.786053258620.03756548249990.787564849249" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5294273207580.7361555591070.4293395453180.730261532728" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1506148709130.5886906352990.137930336350.521594649761" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9064703048110.8652334095920.7384004271470.821579489964" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4973029181020.01133913580160.2819201785410.789131887833" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2317886357720.7185393115830.9188863834480.0836878052866" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.03786820930660.4339133930130.09056153254910.0280258225288" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8877498130640.7967513359580.7911638731580.730455735529" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.86686147610.2868234978130.03991949483420.228094577197" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8519931421590.4976334771310.2054154391410.386603263896" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9357624551680.2851221926210.6777944759680.227776530775" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8058102700610.6514197544060.3465259475840.38206625555" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5762708645310.09931075955650.4581174033980.365493346475" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3767830398020.4238138196660.1706449188970.797150182879" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4390343596310.6759592610180.7017163612110.427071209733" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4489511578230.5198933736080.4431431639190.306208868947" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3609371247150.2906452559510.5598729958890.522606832241" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7480504284140.3634670554430.6325265274850.262093306002" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.803796506310.9549526175860.5179952859210.117540929789" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1451005079820.6930643044560.2716429572630.0208739418195" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.500903448930.9519968355580.5747022763220.495409341543" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9452936820910.6558860925630.1281811728160.650198219941" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3416975985050.1295133368770.5620787169830.523126728114" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1643160585040.3320154019250.6285175193610.511323739043" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.0900180113210.2684541093620.9759313345190.953476001461" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6824876982630.3975504020190.01950154308280.297229940661" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7333610963640.5232565196680.6454208424040.625372379998" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6475484335730.261382539080.6807609267860.209280242317" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7089688358550.3318391199590.9743555383360.0827162408735" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.003160585501880.02146581913260.06288634811940.423844995657" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1478342453490.08561085659590.3078219379830.327540811708" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.02652443945660.09548326505890.9672927189990.550479092895" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4065724582180.05480133043110.6125158846780.396966317045" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3664349020630.9582289766140.4296218325270.131589053624" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9392455770140.9477505642440.8195224411290.624224565545" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.560624919180.9919552147990.9484930591760.205637022581" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7404871271310.4266257942650.0477012042320.325809897174" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3117366267340.8689350982020.3683140835120.565047599027" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3114018674780.1142255787550.05159422815860.490532122489" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5112173152860.9324944789340.9434450146180.137382967727" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.149044420690.680066641630.8075901277440.612587501948" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2545470296950.7783365055690.9584985223390.818895719661" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9542073487840.1812166089230.6778683625580.368375831694" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2092416773030.6424809437690.5534654038320.528122290876" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5686347925630.9870689449920.5958737229590.4101540865" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.623005884070.7087887881830.1509242682550.251748261348" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6947626370220.7951656931210.8678497914770.936205614295" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6926817513150.1524375363810.2289525898320.960667612852" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2090560064840.6348209789940.6109974043570.49385885164" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8964444555520.800794926460.3117452257670.112091620652" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1187452451790.5553075399260.6138508686510.5622154857" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.08776412773390.9819206128820.5520564050350.181502968003" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7334362474880.7541842834030.6730669813080.000545046505192" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1643032401640.4011052201870.9729281488360.12376948363" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.000978873530270.7290423102460.7571063012450.599106569624" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8004027900530.6836571512620.5668623084970.697837288803" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9568754571110.7202171161330.04505755802250.0716754044907" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9573640259190.06386864553680.7615826190090.839129908935" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.09252230498310.5935741239860.7540175834960.605992815538" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.02131756538250.5211916048170.9581240426890.0626063290172" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4019653249460.6759445389010.5813638570020.915258340711" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4509029449610.1952224990560.3972227918320.445317937477" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1942182747290.6115844517560.02003668755550.0964541046737" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9462752403040.7178469634220.3467105385920.420889552279" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5698287197230.2189179363120.6423676772450.274254653038" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5874152586180.8374010807180.2835057693740.600374373036" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2670337534720.004547963513430.3384842853850.0662562185323" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5205305762920.01630059071860.4737888397160.889080547577" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6101549622550.7075325034480.3449686397390.723035758709" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.908715959130.08320598186410.5374612682140.593324518969" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.04592746454980.1492790199380.0251866104640.379179567783" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9001771185910.5221113033980.07941499792340.794118974147" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9284548893180.9198221604650.1698761315980.190110778349" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9136744972160.587551847820.2310161188470.602770933603" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9102005740650.2432622241530.4016168247960.566415655226" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.259759959980.1340342295870.8038769727630.573302387619" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.909774538850.4840906801290.7417626017380.144023702319" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2948060772950.8104077974120.4656843345520.864300057983" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2575373519370.9749956929410.9441639837430.0578509755176" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3110207230140.06567384841250.1206678676510.367220603838" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1296177039760.428799660570.2742862026310.0875897615499" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3264815174760.27730693570.710718869560.435981979442" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7743557128070.726751493170.5551593296690.286538496319" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9611110640650.4499248200750.6383673342690.0596152900342" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5081380021630.879249817860.2925359024660.394914678818" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.932396609460.3810789853720.7745128502810.0887307077766" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.01634926868750.331808224590.144351727170.513579151878" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1491986386490.0009129149860370.9864623892830.802255001616" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.0701763446210.6949840811580.9968439566730.15833206498" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9456898479220.5990040601770.7219502152940.804992902617" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7555838937530.2224847041450.2089517813560.352938583355" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.06358028449970.1899238944980.05009687599940.176063362258" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7795892064950.1189343258390.2690279033670.348943164989" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7787512600820.9878389379960.5429232994330.559570673242" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1166655238210.5283621322580.4017473372070.466508147085" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5655019779440.9363623101820.8401736031590.42074285732" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1905104472250.8867122184830.5892492580250.969248816252" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6505113836220.8247722924060.01015623202520.522943679558" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7849159622490.3559928230020.6675657634950.707540707057" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2518736833180.2454492683770.143518272790.36623529942" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1826465705030.9167530457890.148363976610.227344525593" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8473509118790.9132881622460.7632686623150.970239983351" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1896084791430.4306624058120.4972336621150.504731398397" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.09292167614830.2380172820730.7205263835960.610845433419" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8633032250760.57924701950.1077023224510.140952964195" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.477853764550.3138582896590.4944531306270.670528522775" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6915204291130.8089311546350.4833295258650.192572700137" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4889685269230.833370704830.04606174674470.682087547821" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8939108733110.8901206171730.2012008322130.818904071276" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1712299281940.8626032314350.1912648642970.403941625574" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2721554925730.590621081740.990763534860.359675387925" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5775167195370.6224629301050.8784832576240.0949021011575" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2798325864730.6446330904870.6647685774070.651624930514" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5614710540740.9477377753390.7606942570680.336338934807" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8962498024790.8364679429740.4482572236760.295197545963" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9362574182690.8876881667380.4009260152790.778994769691" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4999118011550.9986077326850.7844934290650.993519480177" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6258863450420.02848080037130.7407800159290.00319553668613" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.02441276620890.5003963352830.07881984473270.30639051471" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1461494919260.3539316380220.8909494189950.945957336955" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.02151558120420.9803046405790.2980032808830.413609759936" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7446954177480.7915325826850.6442971512990.0730616799755" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9294514257990.562471846980.04742866858930.2405875413" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3038601625370.5066160583660.2571533947950.175176072591" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9050441239250.8657424468050.7345537560880.939869544677" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3768854719490.352735786750.9585825832050.221758824689" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7311957379850.8432366056120.6790567726920.55416911526" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9244951159740.1447584761510.3230674657480.373468300365" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9062964878840.4692275278180.743454325930.0272448475853" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4199638733960.8336066331280.2355801037950.513262961962" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6325239840270.4692839870690.8937792747340.667262514305" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6716062989050.1782808392080.996822721860.22004437386" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9267973658850.09922898241120.6275986447750.307195777459" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8614364816610.2521158362180.6297443528550.105840595627" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9517097518630.1845892462250.1135687633470.49045926663" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4422852366490.3969402522130.2269448067680.10046321668" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.616485050050.4331974439730.1357301359290.89496056261" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6040697465740.81818919640.1159062529290.779547844688" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4903040873410.9689063688090.9468474031890.890147570056" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3839663746260.4576061821830.0674944674940.884062085766" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.009646607402140.7589571746630.3463233517110.743490959752" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5783006437390.2987528642540.154135555850.617157959125" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7110130885180.220762890880.2458270193840.481232680625" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6672008506960.562425425950.1441269026320.0665142972875" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.802207695540.9669326253160.3978589283010.341885216784" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5154921512910.04153917409840.3845813465540.904696989568" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2223032412980.857502162570.535136197850.203471732897" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.872122121070.1171301125370.6476708984740.118837130218" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8775250025640.5809643688050.5714521471340.383688977629" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1417363211050.2357192873070.6423565452330.112016415237" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.03291511663190.2068722078940.03957806228680.214870202215" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8989987047420.9214068450410.6996392916910.399674555678" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.146284514180.7700814629570.652081495540.222435597708" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7182428215050.4331517526460.2447463669760.724246642919" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8069722177120.9703404545520.8810959135890.56974136758" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5205619179390.9604829905850.836333582120.96225561014" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8545384705250.2344123633640.803744483750.73402238626" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4685324469290.7621008885950.06939958401140.701893130905" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1131891431930.3295069085430.9951776514640.652063745825" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.06871479823450.07590975178470.5214660461020.96299047875" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4505195768480.1981148513370.1150522415850.127559797613" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3881998727610.6928959518680.1173544054120.689806564069" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4837884389190.1279948359030.1747385399040.898188908289" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.07645728753290.7100814654150.634537135250.654166422098" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7720493368140.5230738504160.115492235010.104536263214" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4415538708850.4772707779130.2784441532530.768166006204" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3804594185580.01739052737540.5899985430850.782528049223" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9340244894890.6624934698580.4202015128990.614281510567" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8471662427180.4804356503130.5680139650570.881752530321" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6195025689280.7785519376230.1467358112760.910745547302" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2046779075940.1956915148910.2277611243780.421856883841" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.154224178170.1061804790230.1796731549880.687594329157" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6560923761550.9611807159620.6390613064040.627249876961" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2105599731280.3511323880080.7290930247930.122303282407" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4462495466630.7145583427440.3674553563770.68994414813" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6392812832850.6460165946530.4790338225480.75873796154" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1029117712210.9669523306550.1138430331820.324814048544" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3348474889480.3177014239180.8731566208690.764275453676" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3922809343670.6453209174760.7564125077870.126998449325" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.02411547219790.5915689678710.4138744167030.313789117587" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2306878805810.3308292913480.003448413709940.76532305037" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8017571063740.8961865957220.9377144096130.854751638965" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7402635794580.05445314325940.3211050671740.731127683745" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1641514346970.924665748480.2483637044640.179269518315" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.03632609984160.8565230862670.4608495729250.134044528482" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2192426719620.3893560219290.6081131672410.617405735088" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5769646728640.7067017545080.9649346825960.181827571973" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1840350731320.01596775762980.7121323080630.568925779802" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.04865715226440.7611558948340.3909682845210.558855051567" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9526314750710.1181055387630.6846653168390.0172721427654" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3947179183690.9609676756850.2087862803050.570723427989" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8281065382510.9072089656280.233867790590.605524000554" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.356825877760.9013977244890.6013117890070.183501409106" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6307718248170.2736389571180.9120002935530.0914577766793" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9371339144280.3685090505580.2063428938780.71243059873" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7964863451320.6281411060820.9948602900650.367173352148" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4572689116850.8615842302590.8329099081610.70372571582" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8451607119720.02243886444760.8467956387610.823721163478" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.72807877850.1483333521570.7842128345050.225466842511" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7618970517810.1373169048140.4572832503680.325144251465" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6001043926570.7969483611140.5147977518190.248228888852" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8562125703010.3325553930280.1309439131980.991260986172" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.835039597890.7961484454010.5173077673780.0987325808191" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.402822302750.5059330241030.9533220146750.313767741472" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.04773393548710.8263234116260.2637065104140.440238461793" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.890822292830.2069524300520.3486459997650.689881417518" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9592530248030.5942203260920.4292039777730.565538636377" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6022319045970.7986464698880.7739147305530.281064097831" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8940033591440.5246936106940.1724811269320.436913296857" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1837187959660.5127226867710.2932423570620.686116887713" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.04210987662240.1145833070490.06618087953940.730606509235" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4860641400860.3594913286770.4901470443960.605357344503" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8183472789450.08407412762650.5148507830530.439079812327" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7361502865820.2610655750830.8058996465290.958462542544" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3418930473580.7934508420570.9389863923850.581226634195" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2672011956530.9936167233290.9039510392640.0683424218977" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.02994309016160.1867357353090.2035960626170.445628029654" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7313109151530.2417993531430.5859094440470.443867818649" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6177158780210.0860692007070.8551244105690.838792019016" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3261892828280.7157959243210.5447874306420.910952851303" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4163155934060.7821253118270.2447585718280.400156065978" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1283469084010.5226824597190.9262219329340.890896840655" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.214899617710.1848036814610.1380537322650.440268912529" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9909471491550.4983529047920.9094452428690.893889520534" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8310117705430.3568928527160.2101195361190.878951499725" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.07038077065210.4659084327890.2649975677850.516108141016" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2138782302580.8844531187060.472507394840.364415794881" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.07807999868460.3452434158160.9277029829620.196786116035" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.549312434630.4460920006070.3585053500330.133346888108" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8813157943780.2334835476660.9525344415780.918914177802" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9940611449550.1533478868570.4601340210360.464261845214" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2628360772240.7161425584360.06116895380120.0917174371016" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4564091880990.4497186503040.5741539369610.0418007660432" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9647209350130.05560758502620.704218232060.131004719233" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.08101356884970.2816656182750.2764243154180.207600481097" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7000676957220.4131586122830.5406915013320.907575886374" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3647244213740.1587160665650.242286418770.801671155189" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5298967005430.9140637893540.9717188657420.205243520019" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8409000556750.07505445297030.1313555991530.275310752297" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5415231152820.6619727955850.438835226450.0588293135249" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7417005625720.4425970965270.2970279290980.150172994105" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6111153676720.6648470048020.5733193717770.576115499965" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6684720069060.2715069350110.6749153301250.939435862185" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.319201726270.6998712850860.7739019745720.661995749602" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8702783037820.1657218224660.04825641661570.752183692232" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9736704483730.7653513933970.1594898167710.175598008698" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.09055003131840.8989926656350.02992053323790.601643631314" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.00190204329490.5758943512240.2339728974540.0955159320021" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4719186217550.9925165240840.8883358739780.793646487157" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9548156448090.9595908137660.809693315740.7646195945" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6943417912640.3816917472230.7612686671450.940667357872" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.729822467740.2662944826170.3306231265720.662783182311" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9030739106240.7190467807830.9181608631560.0415938331616" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1117186570610.3581429067480.1801015032680.257886482845" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8246698380020.2904641494090.02536570456190.437229075118" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3432901730140.4252739585230.8677313658420.27272062195" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9291257188690.7922325846320.7221677510780.428007934683" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6168235021380.09794197253510.84286865540.617439839767" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4991031575110.5200179902290.4437278790010.905877901093" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9682111133650.5401638774780.789606295580.532378355781" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3294285070490.07739194924180.5553192241850.939519121266" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8535082902480.5347993864960.7870564104290.988282748432" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8096326174210.1013832315750.5815521686840.545693997711" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6593750573830.01350542305480.7519089996440.0513773954251" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.06715590834710.8956423870810.9634969246780.880157392487" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3632413291880.4804615827940.5787011869130.14652008791" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7671248722980.8005635741080.6160287702240.621805316527" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1838469589690.05988735533040.8551198206340.0908461641184" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4793335182040.947162368650.5248197606050.933387283591" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5960408523610.1037314196140.1240082166340.0941508234509" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4209662314840.8286598019190.7000246889720.506715291062" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.05931709995960.06998115847750.9028088960770.186797534266" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3960673960710.4254306815650.7377487354760.399121524921" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9023948640190.1902167057670.7317236410650.12007733458" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7610535497150.9246140068650.2245266027440.555774251474" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5517558953940.8224051256210.3232498764240.0261402482535" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.845505233940.04335723419370.7618566750860.36555897176" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5570074702810.5523671335250.573491677640.644512942716" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.189916044590.3006063597140.5528749818790.304537470238" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9722255374550.09216307895220.7823929343060.278424754691" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8735202991130.2317337918850.1279343408830.0983255754953" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.01808270286950.5394234108610.4880808415130.888828670936" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9619231656660.4735374397190.7004518402530.632129177153" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8712086518290.8635067409660.7161763176050.690355602523" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5386211585920.6812643798480.2786077692560.553403808671" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7009597350520.6259658696540.5545345939630.973178120743" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9509921587050.389971291670.6145003503420.00876995319087" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4557659085420.7179849133680.7894656466690.684447100461" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6989076460730.9148923961690.08776036552740.986797855931" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7794361570440.4440989643330.4346224309110.942911836474" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7761125783060.8734786070690.01468223624050.744371489936" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.09995566226170.004569567224830.5822658177380.540229466237" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4058899855230.1914244607980.4261752695650.253650719223" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6501240414940.4796406482450.6597543661070.973512850762" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1285255968230.8682992242180.03558735301360.868390316915" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3971603942390.4844759767270.2014705830120.370541966376" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2507613150460.8450462456070.7428773143640.279079522872" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2846172443360.4273403113440.3362905248140.512167196176" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5142549299610.2784265380370.1316651022290.886852146226" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3878240032150.4410852598710.8058403924670.582337224735" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.734142175850.8299500094250.1596931658420.260414423239" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9501804219240.5085100359310.3536891957120.631738871195" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.02454703663930.8310823700340.813287978950.543521265265" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7444461220840.5116321442750.7191360189050.388589087031" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1636938612290.7913993124250.7377699238090.427135486111" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.07305378356380.6453145946520.1478117162790.723619496904" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9751758118990.8722805461180.8276836822290.0805718950616" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.008104125616570.6441398685010.3478372350260.188883529853" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4702468103140.237940276350.9424951279410.932774098464" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5708578864470.0193444545610.8502344094150.176822638822" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4899419587640.03990615253220.6747648965520.849538399123" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4106590890420.7666713957970.7399970142090.962715441045" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3776713357320.7116753931730.6935273702590.882526410428" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6744555273880.1816824565160.3874493889010.0743966302051" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9509496406190.4022690811270.5007194081420.834580580637" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6536164995470.5859867749990.4571997975010.640250554547" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9609746496610.2939997444470.1024594013910.985138521888" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7788345854550.5616529594970.2064412261940.357758729395" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7723051663660.4168957487350.9538810157610.415333383483" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8832350137880.04747221569670.06297407590870.938513229793" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2469652838030.8262776960120.3111205196860.0033567401333" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7325966112890.997862904590.4398706173210.939694685683" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8017100954980.6317815634220.8110739857420.816153195009" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6903307329420.9839113522880.4031456627740.685482061008" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6837491503050.9616902335810.3717913341690.0697581583172" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6967828354380.7962522612730.6818856431250.486095611443" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.05555620648070.6769807901780.710752314330.168824328887" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1025259272450.1778849282540.959553991360.65827699012" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9635674343920.9777754823220.9438688950680.853084621014" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.532890225050.3986850903870.4497333972830.197414777524" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.343026841140.4618368252520.7269422012650.253922508305" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7849983128450.5496496064960.3539197490670.206380465539" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1286797550740.2453013576910.4366758077690.706509394316" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5692723510820.2791692039010.244451651780.372784970691" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.686892373240.194527724880.2603097460550.967566820013" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1494718799010.6239808387240.6720140842340.597857647069" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4512155951350.6343762864170.8935559548580.834152297231" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7858426934550.425097883780.8405393044410.747777560225" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5303264333840.96807570080.3744305492770.753796885004" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9408864178130.2764176161130.06491149576380.837200933132" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.671219603750.6595777588070.2711096059120.663696128506" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3697918389550.9147619541610.4195112921750.660273304707" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4858563799080.9070202372760.7057588006030.957907079991" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5734886838120.1197707750480.03468837077660.444900161847" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2250901221640.3070470054980.7412194445070.468095150025" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2405503316960.5155693962530.176559597040.242987518964" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.694196416510.9733633581190.8279327724530.949649986812" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8800070114840.4782408742170.7738086996120.355599461379" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.002575789908840.2225190014750.7475360885570.336352343074" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2958488737110.3814780512530.776343412010.41097670204" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9414109218940.6095897382850.7166774885030.837588022887" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1866355129960.4610533239430.5890731205290.406124057072" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.08003547251780.1153525389720.1638786600910.500670679234" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7077651131330.4616623035120.9142714090970.211148296483" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9804590446190.476026231720.8592612919570.708737062151" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5005664558840.1329134674420.5966293408130.238233179584" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.08403899011960.5027415441130.03662252007380.0786128276279" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3644989627780.4994309206340.9147506616570.253613943535" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8300509170740.5303087535380.9666894252830.0357918663329" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.03261624765980.2397297393090.07249126627560.97543370343" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.257981578350.6349860067450.01943506374310.935516597449" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.285194970960.7135949173960.8481510170190.656928945019" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7325807320410.4362254476240.9415199937020.338638857274" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5962724292630.01326043550240.04033498220970.934072071857" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2403063950750.4080150936820.2896854076590.763276941416" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.187393999680.4163679015120.141097531550.903814976141" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5282530898990.3625548520030.569993277650.741932651725" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1788323341750.9432149344070.6058702021970.511065767462" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.662463262280.1797982017140.128191105870.782333356527" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8877403362550.235616523410.9773699967310.415903811318" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2076137130840.02109149116260.5176356893730.261273955688" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8154810441030.02608225900870.1618568837240.60505118444" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.903654017070.5598418936780.6014946158810.78411904997" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.05151442238140.1391232828860.02460073253560.29231959452" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7538153699990.8915252415560.5970232087110.407652167572" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3104166432730.08729621378590.5719493553720.932894997093" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.22014844990.3603651534920.8747902902560.597376405751" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5961430829920.4137342719880.04212719328550.56763561751" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4710874444470.1313957643150.6815344523240.92629583315" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4549184577320.4838039194290.7342211840740.2521514241" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1428449924480.07274496924840.02316988638150.594459373531" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2638801183620.7360804302390.7858279237110.492150570927" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6648145254640.4266497500840.3299075381080.150157437625" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1909283525950.9370813633490.4623444914640.805918810354" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6462439501110.03812895849420.2972103162070.471092482943" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.50327361190.2174071092810.4467776476630.750030150065" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3339457369260.6912467543440.6676324767060.970733656947" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.06345674505630.8498761115840.4369828348780.80338845471" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.677779528520.9457270409250.3830306095420.593688097977" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9588871442090.04069424705020.9792124187540.342345880692" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2487586388610.8006375987930.8807697352950.907269708821" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5309001512940.4685194250680.8608292879080.778067743175" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7142349458510.03855584929490.6136511874180.0325788529203" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2796357688680.4913902935630.6478513763520.946405866711" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.03194325889980.1196007946580.0570980403440.230037969856" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.06330366789620.6807251532390.4342468649270.617840303024" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4675564537240.4666089358480.003546227200170.884258539353" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7463549590230.9018484805330.7046769585690.895656797277" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5794823662390.7047110709580.5719772341830.952802832412" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7999118543880.8994090740810.9813039215420.3696042015" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7821413523820.2488736886670.4631087415940.651903658748" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.01370374513220.2718625553030.4592072914470.248214857504" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6327594423590.1890760298290.1960362349820.786282995211" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4414565332180.6513760423040.4825347291940.0876902009536" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9097387949420.3335724576620.4299006193740.782087181663" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.177862248840.02177612184560.8630123714730.156703984375" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3220559499050.4217290772230.1584420642450.774885139392" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.05938274854820.7551186074350.9373003725720.912150742722" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5157122854620.9663849987670.6997888450980.894266514057" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9606846402020.1442341756350.639672070750.437029094105" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4494365606550.4278354052140.6560203089960.385147116108" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6504649263270.3459759246320.2669096936890.0410827086765" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5145774611570.03754352996310.676866743410.543757400233" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4042490028340.9018176847460.0655989800820.49419468357" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8989516814220.962378865270.5689215164730.501699986546" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6876695090380.5575197566740.1745547444460.991952087546" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3828666257390.9515507523130.1386134573270.513003189585" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5418370991270.3623249160160.5435540395070.839624898821" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.161778487360.8385808631810.2686316507460.309049120653" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4900726719080.2858446845260.2333837986790.374882276873" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8736980217240.1971613296570.7597133220950.862489465834" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7186880951480.842295116870.3273152819510.198234883608" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2541598572630.689302019690.3165367334250.436529899132" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2754606538680.7124548347420.8366345754630.909306268599" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2732035172490.7331881259520.1996904410260.955490274761" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.04559117343970.4936742741650.06932458996410.854461904576" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8362033075370.8184450913280.5535330375980.811697066697" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7292648777490.1067849713180.1291390035820.739975899342" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4409975082420.5397726290050.8445112878430.275457091719" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3673264137030.3010027714090.4357354170640.532719561346" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5203220402960.8523527892410.933896634170.674935493982" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8514969556690.7263293386590.1509768609320.945562096488" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3725638635380.2254253917560.8932257113130.443269495222" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8830390715410.9589016217150.6482014075830.459850059387" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5027713448230.4180128156370.6176852328760.969644859286" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2305746734780.9429798159930.3760134132720.222286645668" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1255177064870.287149153140.8136034801310.00891110388189" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6322581803330.4696077979230.2773410665570.614890389959" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6986854007640.8903603162180.2115097059770.766630993769" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.568968809310.8669779470040.5449918020610.305179241886" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.06745330106820.09763820375720.4148834029540.396082531865" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1202445220210.5664666751270.1163270017550.388428307437" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5443628170080.7741963015840.7764918021130.285259665743" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2601680266960.1479821997020.5081308165070.0228906735664" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.05163723803720.0663867559990.1964011327630.0796524773194" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9401500180630.1876424799060.8978643285350.456514898784" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4462294590430.02905875607110.5375005343710.517541605331" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6469087718850.1386220282610.4125269862160.611954047016" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.02555473459360.126127416810.1829985237580.477057291937" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7955789304230.2787904274840.7462781831960.030076857254" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5008815437040.1757104501350.4875124169830.158378026059" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1913521294230.08802963548620.8919734188110.107832092627" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2973046417010.9686461261340.1651418314190.0107168754418" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2464104153450.2775129627950.2120197800390.525499224456" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1318037784680.9085986259210.5216744345870.657063056427" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3965052787050.8080467055550.8194608966920.545613184252" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2936717572080.4722081549060.2084364017060.0634187678211" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5171278198340.08366758810960.6830947876490.381565212956" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5857490199980.4177055742420.4935158481740.535603629984" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8152010320910.1253762758480.2556576698440.980770386877" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.953936939430.2420234834940.2567584862160.529609067174" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4850789163070.6978157247890.7302783048590.765083279085" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.03973997327880.4785716575050.5051806065020.656470916372" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7021159131710.1223059526430.7469384605080.0493811645367" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.09959423318820.9279944273960.4360905092950.56349916326" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2155195316690.914541070240.2651649661790.731658196679" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8780406118420.8396306863950.75527605170.85408277903" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.952640619370.9058239749040.1256383792460.355169451889" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1716102272810.8050295379740.08638360626730.150324733832" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1782001756980.347485802570.4625159604810.686166673652" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5535358944080.989941627140.3690776351920.482395479834" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8085865441610.8061621273730.5974151029730.0996467531213" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.09829091646380.7748584149990.4358010400250.423827805749" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8312339379130.2393152473360.08092735622520.821143371152" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.04882595183320.9213086664570.01632031124720.458657256046" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1136154435260.4002334835870.452032088980.434112543864" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6173214574820.5842474815630.08766216052160.139854968441" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5575978713280.5966266207570.6571280367970.258742395915" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5020459008620.4929398293430.01865798501640.539025169689" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9938883918340.1937377706630.3442469989540.784390635366" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3232864339140.8734986956240.7407869275110.64747348156" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6681655369510.4041952059410.293569286480.295535049242" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.02090391999420.9122998537820.002437204840290.455302857189" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9521602731060.114334138540.5280030652050.528393055322" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.0832010482460.391875984570.1847382514530.21312575076" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9332800066240.8421344506820.09736535571010.887879025608" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2489001249490.6218913557670.6534413268120.947183968807" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7308987199730.4900199675670.007885852975780.87626894613" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4967540728660.8041843754490.5255416112380.621588302799" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6163927660220.9221653105590.7289884311540.876039423872" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3608532688330.1903265842660.5412221643620.253523570611" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5764891354240.3867904631020.440928831890.989143344228" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3663361873840.2152183003860.108603579350.433943649099" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6609466436070.9403238966470.1818619902420.471868501127" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2613496537830.986365526850.5324605292620.14574911784" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6879657283140.7409476689610.8627855468260.742099403" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1979059740960.4211891568940.298710621780.505834215614" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8649966941260.7609702029410.285120515550.668142711278" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.05481345449160.530162147710.5590830179180.221981338689" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1408620769940.09283568828280.4767480347260.136247131767" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1687721687390.6406316186420.6784768086780.370775703989" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9003763920690.9337259441670.09568682111440.299030913474" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2232454788640.1364143680950.5789000047520.743187778369" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5395645607080.936873603070.4404486863130.586591014442" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4554216906580.08329576959270.7924126711750.851849160556" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6576553499520.3538545720270.453195677990.902932536185" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9898146588410.725735962850.7270241043690.623676550612" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9062167291030.4608818754080.8026702760790.879004284998" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6589813186810.2764889297240.4448330860960.71698857766" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8969458656730.6793556822490.5896887044620.741056452666" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7360758437220.7688159071910.103842002660.513901904739" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.04627209269940.6366576097470.2056130871750.524238351812" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1775643302110.9317192008680.1197727275420.228633926867" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3592861210340.2250695233970.1376003432160.560296115338" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5448794082190.6387556887040.5943395141280.0218694733178" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6170323463610.0403533952470.8568200629820.293138902422" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6184728073520.1476946738470.5171294826670.15445752381" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.004347417727880.6893951558520.4171337053620.733429254678" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5056249909560.6939278742480.07687257179020.576167296769" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2045156414690.3558850362340.3346211146430.536000264215" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5480313632170.1258345833970.7737111415960.274624091267" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8082016407720.3030809339070.9287361192630.354745206895" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3910018443290.6376427261470.4729544483920.00755083777585" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.646729000570.142815727370.4050746218820.88740463981" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6913371330440.6388113909020.6242089913650.478191747068" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2839835609330.7315279984850.7658617808130.831147523216" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4598659239650.1812032962460.8583218455660.384963209572" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1845583420820.4549353969580.347413931210.798235179328" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.63650227230.4761994668250.1352119329840.455081222911" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3004539988040.2856569953120.3475184640130.891674588595" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2568760486830.1222274971030.5870046799890.766857914023" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.007116026321380.785351136120.684247828550.861262577683" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1640158147690.1508618894370.5288261079580.615573302108" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8395145300290.9219753617550.6479147102750.75659620825" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1052267235520.8152685706860.3684841318840.151641294927" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6192239414180.9395435189020.8293867994230.800716396782" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2874400826140.6337239339920.8793411283230.429591117428" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2833751267880.558143125120.9615872327730.368113846773" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8414515608610.8798948826250.7232990885160.577808286314" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7852180267520.1598537502040.3211335809830.950213080226" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5544285947270.4316193565080.8133399505060.971001166901" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4303149128280.2432098791010.4005396753340.775085498184" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.698650919380.3639980663390.778680652170.547495638713" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5141359106590.7239312274080.03364129758150.79733165869" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.04700904830190.1961978957540.420004820830.61062338653" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6757715732890.6004674416890.4809830230160.439719881445" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5193665661930.01159421811590.1866596231720.00763947516236" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3884840601710.4027300696660.9394703812350.722482517429" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3191812891330.9856810069620.8019488849270.227824938929" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3511622740860.3444892359620.9312021201110.17830151498" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5845952575910.5450818216450.975864608660.79965256072" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1628869842750.396657181860.4399468753040.65949435826" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2293074436680.04182156371330.2505907869110.317101977805" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1457052592750.7346685277940.3613841585670.247351838895" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6902199316140.5383861462850.2202634773370.757696691867" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8186989224890.01690547311910.1890133508850.22428676711" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5510029730680.1649232900010.04134543858180.273270424379" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9105486959620.01036297144350.4563083846680.745075888215" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9852806264790.1953840364560.3385791015810.688983132091" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.07454769613540.1013626914380.3945938422520.855668215149" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9445780125860.6675649721370.2709061814840.062221249986" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2217472586480.370629422120.1760241699170.634041944767" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7149865858730.1249764761310.5443014709580.663874827621" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2536289770730.4903596620290.1328731883880.100351265709" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6745426089340.08769597774450.5107121261070.45413131057" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.03781297019230.2629073621620.9103749989740.778629729161" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6721475222270.4879832963240.5781838996160.873298749677" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.02707862630410.3990784691130.4288030214760.0311529153162" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.257041552930.7773235248790.4745451421090.60380441967" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.320244269170.2223890887180.2515540458680.136701775404" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.312665641890.7252002562160.07738023286150.322019510547" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1534937720740.6744032599580.2557064139790.187334317299" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6851736525990.0525599198240.1804444906920.862802774819" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2123247202940.2985199914040.1822530376950.0691020912899" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7640614828230.4502315015140.8473702978760.225240774588" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7123412514840.3738445276430.3426601789310.827754347329" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.03983143952870.372018130720.2993479497990.714200511234" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3866380620910.7815964051420.1217960731980.165963188374" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4456730983940.6167830364950.9991125682370.759471575318" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2166245998580.8194064933550.5688015104670.124814953437" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5649008714730.8945125757920.801887400430.864180114202" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.07559298641680.9432294401490.7281025587060.56169891146" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7143414349430.4521166636470.467244118850.465755272043" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.06106165180190.9284894598210.06285849282020.60589981654" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2309105949750.8037999437410.4556380947890.0104202396995" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9489632899560.2833072701760.9893440996510.383644214894" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9918132643360.7054506108630.2859962138460.294922984589" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.885279434910.9098875266850.08863268845670.483353559602" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.0006413617453750.9267503972090.4422218619480.46583418482" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5958686594830.6212971292180.5834677634430.606429825289" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6824108821030.1597227960480.3610315179490.661707893313" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2897119753340.3049490563830.702029648130.228967620088" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5146700388190.9042214866140.4566607958970.944839888257" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3442175694990.1139107252620.3513831384920.467748294676" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9082759065380.666793393870.8944234320130.899963097712" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4633156373810.5603424018270.6961997022040.0018192043209" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4560687847370.1335236096120.7144525359040.834584651036" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.07409021705660.7072944467070.9411840680560.0472448391388" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1032735190790.445504665290.7222761349780.551742957723" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2105218664390.4036951663640.9351144232990.994386517977" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"3.85189890879e-050.5940519338270.6496866641940.869219208037" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9802047061840.9054809889480.7596730283460.579120115913" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4266156908210.4461652049950.2284865510550.144709969225" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.966063102020.7569977894750.08490190992410.509712414313" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7164773994880.478901689010.3407114573660.0753792271643" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9311493100820.636978219070.8237451518670.249856089614" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8683749899420.149036873410.2683470037940.445681605306" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4012694785710.3569083925170.1578720245140.705851531418" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.01862121486470.7604780771970.02213895626970.906824305783" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8574247213040.4748517191210.758175794260.446466610304" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.01523676186430.06067812150450.4442177387750.22572067087" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9410318575040.621398758240.7652657300980.119551905963" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4358501124810.6364002036530.5366915915470.50416129081" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5818502691460.8604120401470.9432896621380.0848380912583" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5051508465410.1382219474580.9739905233750.341722164809" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.0981805848960.9649126293980.8939245078690.228099453166" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.637482876890.836865543090.6317442488780.139545649823" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1200215487150.781550797090.6596590717930.600243883588" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3369850223390.2512192160280.4778906191380.623223963873" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5427106377460.2661971089330.1158036664410.792199474823" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.02329260350680.8715672124080.334191852350.828484311233" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.08891033475860.3389710207070.4902417393180.821378774422" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.08486282126160.4593112624530.7068800847710.648650864707" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2809530780020.666111238330.9581362671930.482206887318" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.09584572149280.5706303816460.6567294354480.455471476044" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1408867762580.8445484042120.3443059877130.104330654652" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9842156517480.6718461174550.9252017040660.0335952035448" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.572602199720.6206013715490.5811809594770.61394845537" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4822916615940.05237011474110.7074295633990.601212565536" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1121178559810.1619929349040.08871441186680.805443646806" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.150192469650.2262265653370.6571145201450.460465922773" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.09566603037860.7575009095870.9187210377660.523603313941" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4932919231260.2424468616920.2022147688920.773549194808" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2242079492770.8354784293190.1626816607750.74621659812" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4128886441750.2677438211450.4144156243260.721039952965" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2493179049530.5941777755750.7203682384820.0677492737143" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6707901449310.8830958552650.7495078220020.746321460509" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7614696383410.5129952588420.8783076728270.680783331129" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9550244624480.4086370773780.7803527928060.357051933043" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7675602916060.2476213717820.01652078551990.766785195434" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5763670497630.1112498081240.941676330510.469145765797" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9909311136860.1906910562570.9461858808890.701809582262" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5189415935330.6948120195220.07900888822560.589787765887" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6111317959650.8667049133440.6850941945430.184221231484" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1210864392570.2485089939850.124977108340.994336664391" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3252423797340.163042241930.9566550173120.602879302454" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2240467141420.9386170579770.8790078538570.778288825561" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.444682235540.2565426596740.3940813775860.844136896957" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.48584490490.4687770613310.8729004321120.410673676778" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1797674031650.2820750781770.2057333389590.719035920691" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.683045307550.3247715146460.5279521901830.0792900816611" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3017145428270.9252976043290.6446630053580.525751279707" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3421078450910.08950281611590.7962674570220.544018175571" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7273860063180.9747672972370.09218201195880.810315644429" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7078526186850.4596853449430.8139760168720.321236631946" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8429106939510.9490689786630.2965420511630.726756400341" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2639153241320.5805616951960.4823487732510.424990762743" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9223445740980.1734517175180.3366673806740.517220859716" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4731442779230.8521107447440.6047268958270.235082571446" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6157937327280.834163995380.4319105447890.919715090161" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.01198013092180.921506698630.3798040273960.596094163961" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2095884694560.0575662202910.7594775626020.303405290206" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6225398065410.2700217923010.8202343615110.523024016327" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2798801056460.1191946098160.08733368693270.162997977729" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7490950004560.1110455213930.9091068193040.208251752943" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2776941344470.9302537701110.5413486172370.525270281214" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.09613917525710.2593821393310.8195537516280.343221687465" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1940299866720.4698806882550.4517500611490.982648653044" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9609796370550.2000323808050.3502037484690.996104192534" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.502358004170.5329397122050.8760845454330.707082100145" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5498012731030.1391414869040.08532623626720.649033210933" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9224843792280.1393778894490.4529735289460.449521246259" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2404575396310.9752277583080.3956818203730.845066086794" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9991391907580.5658872703430.006445006378950.654261058375" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8878185534580.6920387517260.8623388488320.266285792784" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2527451312550.1116327923680.666407580610.918325731589" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7458446860090.3574641572640.2205491512570.550475988022" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2555515584830.4123517245190.4644471889070.0368361827156" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4931454233420.6240118464650.494342643140.902326952682" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7337198903810.9866083279640.6006161723810.829688520632" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9982736651320.9453673077720.6790926718120.864063095381" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4229644688820.8932614789820.8862453312180.539892874933" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7643787113920.9356066078020.8462351894030.0512636814826" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7316369719610.2685720331890.9347435638750.767130689967" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4621321486530.5499039465850.4971110866270.374826770248" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7976612943680.04399282678340.5750263603160.2014174316" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9583202056040.7927181814510.8460264987880.994915025076" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5117532293750.9576443977040.8540386732770.804321568813" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.723952626130.09950568213990.1146898288180.797751030341" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.699611499110.7337068777070.5196996827320.303536548425" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9024894191920.1475639064810.5769498295140.854186515467" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8710361611140.7878239196770.7785594563940.489211129022" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.0950702041480.9558055844370.01233169267670.167877708548" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9003251774230.9839042198880.2916210337120.78146001672" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6612765150990.3244753623310.8605924116260.40449511154" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3981607784070.3975059926970.7578230903710.66854198241" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8896769559860.836645568730.6390238827580.280347332197" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1525220519320.6238180299530.985415516640.500781448157" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8680204009750.7056208917360.1292121879350.414665726173" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5473183778050.885641256150.3111802054340.829585598449" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8812883623570.1453516622350.9365563791960.803737134604" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3841674352070.4334651681650.3144875961120.444517889686" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3890144287350.3940430800250.471449729960.162371956509" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3774162927610.3509548801090.8018952337850.675626736843" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2384667227670.3983951825650.2265977909940.891159199778" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9774766115910.9752532078840.2697645188740.690362104885" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6307187248020.863590253430.1011733215480.429430404369" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2653796720370.3543082191630.3521581476390.537432639816" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4159828113860.5731972160130.7428504294650.889874179771" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1621711524340.8955044194110.4369501744530.539372217285" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3910066759280.9346575050990.7619555358480.963119831257" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4641670243670.980013172650.4200613309430.327589305009" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.333268291130.7565139059320.4614428776280.973746875467" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2401989776130.09831106372970.3450674817820.897359439521" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1789322638250.3202549597930.1983441699910.879443389089" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8215281224760.4287293886290.1140945435770.336707397711" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1045368940710.2799245802150.6401061403010.0516553858057" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8524294027980.3204486400970.7594609290530.190902937159" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9039356587240.5251155530840.3270106492460.107717741839" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2060999493840.4089701327730.8517213579190.770752431745" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4185030887180.6347128069880.0430985557280.313362300333" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8319165229260.3511508089660.7953604435070.618284129163" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1801623037540.06191333117590.233366359580.343123557472" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8223225303990.2437522979430.5555814438380.264036332017" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2081022189760.8027158161710.8764571380.840545232299" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5673422456920.65769046450.9279523842010.284229513641" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9783264193780.8229425582320.1368423195040.891546665715" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.09875515531280.7852674755050.1002240050110.487151057636" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2014008561790.213492856920.1047409779930.405146127744" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5950741127970.9556934198470.9669003341530.146641639744" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5559827914930.08973288924550.9163355508190.169645474532" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8648979618560.5419165993550.6151373384040.252972958203" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.07703241266050.2299613644170.2431074301020.364197829031" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.883296614740.4223978651370.2831960397680.578701389325" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.947498369750.1131744465420.5609814492460.881947473184" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5562581559460.8672229133340.8023921083140.5488284398" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2664115846820.68943297960.06748403643790.652056430128" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.565184828670.2655940303570.5636303795450.722151300547" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7258107034570.9391652363140.2831710172170.174831294643" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8369333407620.1179052249220.4644631209370.849672568025" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8861509380670.2022312899360.6145552116890.14540442123" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4019828806140.8839409340180.6078326958220.229388320087" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7541649038650.3291261101730.7779312632780.65088961809" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2804882539190.3543592618810.5513549984230.498836039356" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8856648886510.9213094605020.6603410069340.201540145616" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7644354740950.7435609103640.08321881606540.0500371418072" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7929601280950.8148923652610.6825068462130.99609026921" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9337825352740.9980752310580.7583981511270.395208800209" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5964024874090.2487359698610.9715309158920.819051141123" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7247362995750.2563525834570.7286121169140.2253785865" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.08311784660060.5013306654740.9027461698990.195543346334" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5535976083630.7046362069160.3447012971690.059873986951" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5160043818870.3609149559240.9882457424590.450618415785" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.602546608210.09460222797020.9901486044920.66116705398" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7047520154930.1803028840320.2459573664260.610741532171" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8009173370740.9806793739240.1516273424560.805175820354" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.08729747364390.5027427082140.2666604156990.103943688277" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.735618938580.517949215590.02588358960330.974259469389" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.02542486310860.6895247221880.2144119167240.712930181737" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2938185118620.4576071040770.5272645036650.277446370801" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8812092629380.4446035889890.4991651939860.487264184301" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5103758649970.2559560078580.6886415360410.878451987984" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7355664403080.8095979630830.2280029003610.697998705011" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9150411823970.2855193300150.3953085546470.566007560772" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4375350920440.4461757683850.7118761320970.243998038534" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3523154199740.5637168151980.6672942695160.59760104967" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3302931508550.7004351320950.4691214116620.611972775416" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9676158125240.3334832213690.9740513427740.571822380031" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2201112754170.7174492584410.08224084531120.750345068749" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.502755706340.2276071716720.5499576870310.895589474775" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1291971481260.791162111560.6030280614560.0518131248353" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.281542233890.8667380046430.06414103615840.0101060742658" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3553195755160.6278127300730.4157799855080.903046684061" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6571701472930.4479024118170.7987852208710.826146553493" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3361132034840.8058810505150.9423195606710.542515948939" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9164628088530.7646761543830.8787779773370.801377404556" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7357588839070.1606393613780.02179818839930.874509262015" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9543037185340.5641649229170.7531515907840.417642592509" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6805821818850.05338264182020.464505269710.112139770953" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2870489051930.9579499905030.8648960356190.0816605066223" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7250916232880.5197875202510.2462684965720.901275445651" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.199938769610.6427050115990.3924388555210.199385041229" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6825182054290.4328559841680.9659771044950.698461929488" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2870990975940.5028491404980.459153059620.0104140250005" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.769190639460.5363613630080.5609130201910.98293197606" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1732328907170.5127947619990.2210369019240.119339224453" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7512903942350.4749233274910.04576527910490.433413158579" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.03930692032690.02050016774540.2283700023630.746984953629" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.01724961076530.7774621108030.02793367156520.675804734285" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3841278116850.2124882406190.6138747168880.585602435263" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9084281391190.5376197890140.7210152879010.0994187043545" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.852230529820.8663915553770.1033715510380.806940392513" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5417275209770.4543293544670.4551683809030.402874015471" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.057476510290.3278554086420.5762801424910.54105712271" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2399123383120.2061315690140.9849359024630.976407894035" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9439874783160.949420128160.9624985899810.24116873551" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3048773355350.1308704855330.5880063353260.171060389891" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3584779889330.7519446881210.9978540590520.928656072872" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1957235585530.3273923125720.975195003330.884055799413" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.01266219998830.6062770820890.9677575606010.867684852035" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.01897527184840.5786305103560.6195085362140.296673695882" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.591517717660.7023778175720.5663717290090.305853891462" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4142163007490.7362588118010.510565298450.378049728207" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7497870165550.541723992160.1214650335120.347749665427" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7873747873380.04481072082840.226529653690.756147823515" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5307441130240.9395919403120.5449990102060.591879769612" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.08570392643310.3696808343210.5473646427160.352005294729" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4843023737450.1598168233690.8985939840790.554053243646" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1850391342510.5662030930040.1697667649810.411790224518" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8327855519890.1291168921310.1430307629370.0209460046341" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.04129486063540.00998116828330.2087142220750.115254988195" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3258493169620.4245441262470.9048435002280.977339241154" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7350577883450.1467592635560.668822432860.635805946476" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.08071954647180.9295633391870.962667328990.808310295513" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9064182480280.3953714636260.04797142936150.425262452661" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6723580910740.178157343940.84359203920.63372433562" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.02384085835910.0290926585430.7202234552690.182727862245" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8545291387630.6031532901560.3826670812120.513740482438" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.575400765770.7459743019220.6371787471920.633160281135" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5047759071980.1283182573360.1281894377640.24401751652" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7732228239150.943706155560.7331396342640.272434570774" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6387598535850.2198171485330.4820176760540.114388886594" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4361829384950.2593953538340.07834747493240.795871190962" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4231266693440.02690716877060.3978524100540.0415174328454" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.04382040309850.8406933020870.9596273773250.708215785598" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2345763863160.6552209827610.3686025950840.807955640545" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6506648817390.9380804364980.5899146250680.163204084005" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7863571611280.2642048460570.1738159357860.55119448676" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1431930963240.6597065857460.7366794272050.901381543481" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6449448948320.1350861503970.4740540585520.825794945965" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3879755197840.4566048235490.5925921820610.920599632392" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2020149766670.32274532690.1655711566650.0903469383828" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5225380566310.09941994906710.3158749112010.485623112261" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.342645460530.5774729357550.1578530188370.61577367565" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8184268556880.5911560845020.4155896553820.0522909603513" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3051113601920.5426110261950.5369829767040.390163289246" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1119729016430.1693161702440.01695815239940.95014231377" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3476765251290.6815917621510.3195545415640.332329606431" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2347002382430.06604511125150.2942032485210.956799001946" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1683027266160.1660695213310.4349523185250.154853692473" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5064058769940.08793355096880.290380400970.0423313042605" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7476834588640.3016163647880.9136425573930.0748025644498" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.127744634520.7303290422190.2957221899640.063158031633" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7114344582930.5158239290580.7507641368090.662703386306" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5362728182830.9898828967930.4034860745750.521751017851" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.0149856697740.3335922763550.09902016010270.0192122554527" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.557611412540.7960203526550.9944349304720.106662524641" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3259209672250.3826382775640.4126626503570.0327055134362" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5660185029580.2519721337750.8889090664250.637820624451" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8995663513440.6897304157110.2808020083410.764844256094" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4877219813730.3214617700420.8083591683970.227742671614" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2187966422460.3899333107340.4386899175940.616444382848" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7913535827040.701719103490.5361952052040.756418391817" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4033782384250.03925737755370.9436410320760.141483039599" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7075847975820.6535030004920.6083724690620.558056024949" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7458518540920.05062653420420.1556349073950.394650468523" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.573541085130.7189951221640.2403788064730.509225287358" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8008150673080.001946212835620.5614918741380.0994181754776" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.001063738985740.9335218105040.892541094560.750900907475" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.459816075360.9149984621050.6835225173580.573079976932" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5461812435930.1721591765690.6826283413870.908515589423" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4806560424430.5241353917080.7656614545730.89595060267" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.06477494462440.209691137730.7735611123540.0635646999138" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1234954570610.5679444373710.5961543252440.192113407126" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.102786717040.4434653128790.924206338670.150044624381" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5025802333860.4689331967110.1768554867550.56102342377" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8408068558690.7390138640210.5393956623130.54348289049" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1735566923590.448796254780.4886894423250.700790872147" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.08260662559840.8690927537150.06960219894220.125301768814" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3471065880840.6499042981430.007056155920340.924323029053" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1554245152610.3922311456050.7874855216390.300474936839" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1567861750190.3338185405180.5328922086880.523782030727" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8225342150760.6817379716240.8320866966040.29182987116" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7528702759940.01965529462650.01189985448860.0122214539946" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5973769108260.7077021770220.1946604838610.604559029599" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8718033998980.05758292584870.2159880247020.0515326794917" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.506602740730.3355619400760.594535177270.884173260732" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.324193439060.8579629576740.2994421070250.675666380796" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5907961738390.1220961671980.01270007878960.892585229966" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6625213674180.69897647180.7867972322980.564356773749" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5684585988640.2150415131650.006283036220790.416133017461" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6849670050130.5008379338480.2664751001750.323131089687" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3417396073220.9421849598870.7410899863940.348402697511" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6393175245370.5922550371420.2335514953460.682018714995" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4760748331220.7918850672070.4563237074250.833249601953" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9875354406150.05874865950420.4233258815750.982782350081" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7806764270530.7853026411580.6897714417930.419130695612" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.861921578260.7390090563660.6738847092450.0987542110382" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2287286315650.696447271330.4012874057220.187137193925" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9465036755020.7713856924120.006620233080280.390015444321" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7997479835090.08519683494670.2612409353660.510440132648" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.04696330098570.05804644580320.1939558233880.363693195657" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8223472654130.0988050614290.6257089123750.230847889635" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3029802681210.8219903690610.04436627757220.340489372155" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1275600934120.08442670438910.4094870604670.24646873448" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4838011585010.2212239001990.3594679638920.315159406596" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7737857848060.9303369749430.9716784987250.909684002319" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.0309564878610.4207174181450.4442226401540.760049548543" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.172191735080.8852277230220.335730488110.916604650765" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1003878050690.610900813140.2322098416940.0790676905299" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.143436624130.8393018760570.04204645436860.420054407386" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9974184606310.6513835179290.748478636090.271300292301" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4074410919250.1494389314390.3111223981160.993484894243" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9927849553690.5800092240150.8137967913450.846970917796" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.471810035580.01688916253260.453847820810.206994605523" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6008492972670.6565719846150.4367844456280.945094815978" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.06578658256750.4253584755090.3239144005410.0268314735525" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6462369604730.628969724470.5291769109240.459011566607" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7833394162910.7183609392770.2801749002890.856318314371" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3557340250550.1819387359270.2316430727160.428265580857" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1744905379010.5467824496060.5202971382910.235918799679" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9324599317370.6578086077180.1994478509960.96913201701" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.04392327751940.7820483007960.6759557517720.793319620174" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4207664589720.4191577550680.7775731721990.708855990594" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4214156841080.6426262972120.5178052584520.65888233403" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3956528481380.3930194466910.9463754279660.298293616815" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8329073557850.2692066922930.8996036716630.844474108011" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.667944055980.1398131701180.9143765012370.795471692318" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9095606769080.8299518564660.639376787830.322255583818" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4843874787690.75435500070.581280209230.857585185148" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.501067441090.8318928104760.6858053407290.717950992712" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7545644617140.1042077575150.610122762180.641759453853" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1325579755130.5560122990710.7206628024940.660031624489" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9485913153810.2939118527910.5364785255690.128662659849" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6316071215110.144587199450.8424570370680.464124443252" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4141779849340.5910143697560.793783998660.123947169701" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4491517550980.05790925031580.7241643021990.783125805544" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4501064879120.55154587220.2049935178370.681587422384" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3330673008960.7060066176510.4342898413540.271762129594" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.02583461614230.8530470511080.8643001911240.312994016932" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3337249238590.7408719696310.6011813123660.618789422316" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.02115524585590.06889032401280.3923263200090.862290831697" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3916424851050.515504156290.5869826833150.281665545463" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2361498424870.3083354072450.9098139134010.63057781588" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8131212564350.006480453799210.790651583480.54463224512" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.29099359330.5546315664720.6790438368710.668863631115" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3200830801490.7290013003210.1282513170670.67975203246" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5735848615930.04007988655580.4489984044990.174917178447" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3276189211860.1210509094540.2516841985940.518546559592" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9503216127920.8105139171090.8882613789530.793403455802" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7424737263920.9103634708260.3399021175210.906796986561" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7791862543140.1646421680240.4525936613140.0568763321372" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1916459780860.2456394097830.3496849736020.472917952613" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2948939947890.7723084647840.1145859172020.929781518952" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.250590027940.7028140577490.8707288063010.157938698366" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.865611273170.5609270744690.905719793110.961956963785" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9231368736470.04378850241090.1238555030870.562888340747" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4466601806790.5082741745260.9030050133340.878713193584" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.868197883810.5393376918340.4137046568930.702885562396" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4031969932580.01511820498470.6166479251070.17525167529" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1662403728810.1256453241690.04973082381510.190334781359" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.21816854530.9686710638620.7636661903560.983639519717" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8461320085120.9660849001980.5173419774830.920919761387" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3569760150980.4504573605960.468199145690.0416936205596" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9397702417450.05205649724660.256064169070.251397751846" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5926701340570.6758906007750.6375854117220.333817229785" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8424983607620.9473762718720.2773427300280.615253192158" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4400129324710.07551664157270.8547084671390.203206745496" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5328434384880.8892178463180.4496116240030.670022187771" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2440594318430.04246336509390.2618005527860.413027728193" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7503073646070.4863488380.7462545976370.246302818861" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9768104677520.837260849710.9783398579750.46248234216" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.03275021376760.3165763397130.8367433467160.573004342913" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2485590457370.004862701600030.7459437445590.310378216554" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9620190051580.5146786946410.9698750213890.464304829706" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4060263113970.96537288860.5861689815660.90538220996" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.01348178008260.2330515150750.482070065360.982366513979" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9009378880630.3583074478460.5594745594790.282411309815" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1057153967250.1791759727930.1119494415930.827197929645" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.962948026050.9867520334460.664094490870.792447388437" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4506396356680.1191973238080.9752199830180.252086230558" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7872452129810.8738504726610.9874712384460.807440972506" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2218600242970.6131642039060.04536892509980.781603085527" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8181266144620.2926502805380.5997574941070.623343421228" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8899188259950.9766040605770.06503803578170.141253638959" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9369262903030.1123419632280.4893216734630.371352236225" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8882121702140.3696261781480.4057353706480.753512938784" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9944541452010.2283315837730.3094507706820.117111753429" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.552784915860.776563153210.704134121870.255462898353" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3508515751590.5763116993520.5494330293140.931389278856" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4567191979430.7435663230920.0414224959820.349561232805" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9204557455610.1202346212570.8484519217320.962071491968" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4484885433230.2961396427510.9591400793180.105699471718" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1639095160710.7137377520560.8883671849830.342648131483" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2439313723120.0770849971640.08195206134110.974947240815" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.628400458050.8510697097530.2220026638540.480810594801" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.06064564627510.9459014534370.09516178348450.0508567241419" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.247081087980.1025198669720.3533475286380.0661657546779" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7323942014640.7014904533320.9657571157410.288740929249" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6726772268610.26996111930.3991480725780.799298802396" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.715523657690.7738356012860.9385662113340.12440986086" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6978375269840.4700142255060.6927805268010.364982136385" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7993131441670.6966769574670.6211867329050.906486066039" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9384582248890.841506303430.9666298059360.910070886609" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9875231366460.06661969186180.5168898399960.963994457811" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9379935303570.968825546540.860246223780.578074409322" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2010676324630.4758017943320.7234047896660.304364653736" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8283762803330.07674332674180.6966821401330.876911401604" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1618074493780.7768733310190.3319313641330.966165845131" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3140437216570.188531156830.6394159906210.959969936709" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3315959408860.06502361582080.711279514170.922471077057" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9962954546990.929060933780.6824551385130.395144378354" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.639045582730.8972211211850.2981385965740.975120414623" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8385031421450.03161632856210.1059276666770.518048946756" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7926509076010.5685940452560.7385115073480.363740175814" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5743656180730.03871320555020.5659278203560.877575559266" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9631842043080.0296816891310.7379489266840.892959486045" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4470359281730.617267043590.8492960143420.515897035946" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9177990346370.725614676760.6407620088250.273505710355" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1379858252090.317361296390.8301409418820.244637564376" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9396746148320.4820053630220.855858517510.329598155956" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2984498157290.823232600470.2804556412310.991707133424" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8202849117360.4849002989270.02916870383380.128810141338" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3911200125130.402773858290.8261485364770.61690176316" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9788024997060.05988179088870.7268401988190.751977680661" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.512919112420.7269408604210.8009035416990.674372380651" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8294045730390.03446320280060.9469231411620.959154995476" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2572642224430.8454483290930.4853173504250.758431364838" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2086826985820.09275372284250.4740537412680.572382273596" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.0537916253720.02234603095130.2054745650560.520511067189" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3784707015040.4811996200550.5678302929570.428749143572" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6366597180330.6537079734730.3596550078240.163931493162" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6922694207580.7752762454670.03155522263060.544036357285" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9543362315120.1381269554120.1417533094410.431598525867" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1444183817130.2182804013590.4798992057610.909708364748" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8227066211050.4905101713090.1530597753540.578458968736" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5034059967070.277595597370.5299496486450.366060591793" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7559693915680.2672685480230.6373472209020.306535589716" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7345127504080.6721441505430.4398329319440.450417006907" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9276676479090.2492867629840.05107100559740.688814692355" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9136564167020.2653538762580.388696470350.271614499664" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3857825465660.3524052105940.9398394667990.845836471588" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9342813956790.1852455185250.3362995275880.646045612208" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4225394364450.3163061554220.3190134997360.238777764889" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.08412649760780.3824973446640.2352031055520.254623060365" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.54092167870.1142692562880.563194321080.367447891971" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5286554568860.5247439753750.1672059836480.997521376081" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.495029036510.4146732826770.2623160572260.453148252278" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2040483150260.1341955762060.8228385305370.954467835659" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5830698428120.475325510310.7298372082310.564342179363" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4616977065750.5386930929970.5971363230840.677778526617" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8098749501620.609218892310.408062256340.775467824164" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4669493544540.3927970983770.8896438963040.869326144477" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.08470368202920.5842794829050.621092576260.965196185684" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2813720666990.3606830126290.2822609528090.640782956585" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.882234936010.4892644531810.9502636853130.427496077349" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2823172814870.1565853026820.07837653917120.603705161258" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2797083753320.6229230051150.2151206485760.186508910614" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7271781514240.8829090980580.2009217964440.401162220905" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5193765376780.5480332540020.019072538880.834848311113" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2226299125770.2993921737250.5058750986250.0710448628066" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6318045943380.4686079942880.5406980820820.550080081367" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7478708929550.5791857576240.8822492498840.241404999687" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8419247687390.1308401151410.4483817592540.501884367325" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9377242542710.2534899389250.3492071604370.986988537652" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.04853560454950.5585989136240.5528307260610.848778788543" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7285307793580.9881906879290.303084470940.944995373707" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9761629320870.1655200336510.6735873476720.507076545389" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6418335854650.08810665562780.913056445090.58083933279" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.03976275836030.2812923872660.928151615270.0671275679972" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3683522678340.4286920342230.1877870195440.962500819654" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7667957964910.9984751338890.159930069390.170842342126" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4934036309250.6097368914470.5003625554350.101582825468" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3779405823720.09229851938090.8718179112010.970596678057" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9800977533860.3386642792860.001130072603680.997885849421" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5546345197870.72207333560.4252383152550.577441570007" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9842553162070.5923681240390.5532729549530.739040536212" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4802645187730.7290444612380.6056109174430.951912044899" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9206173700210.802675124690.5563014240890.34013199099" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7174538943220.4051722209820.4340182979610.489021558614" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9949896767120.3257826929620.2936583629270.271837504965" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.106181271490.7882080225170.8889484422540.234753554528" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1557454129440.2023919959150.6401030208320.786956789697" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3884466076410.3633086278070.001227741692860.941673566128" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1309850446390.3279089620040.9373348277260.567693692674" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9722358664880.4580039226190.1104028893610.456337797828" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5698892630580.8405712594330.4708103044440.557012367061" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8256425212810.2919658860680.8717976870250.919114127712" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.0465841574560.02869466926420.2037573207280.0664259534352" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8817021344630.6112572990270.3498170973150.219034032477" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4686097293680.7564971652620.7272418537860.0120128151036" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2051266459650.09376175361480.2357891926930.755114224225" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.168227228820.09942889568320.8195113784960.43507541157" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6362244734040.6026844526310.09043352570890.408638500525" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4072865184360.4751232378260.5408837029730.529656267634" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8785171659130.4191705743420.2183078304760.02938811566" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9945440018140.9398713341640.6895335086850.480758513556" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.881938538640.9342799466980.7515813690150.378885770926" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2623185429910.2457995106720.2437173501010.856925752627" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1364495690530.8798925629340.9321636166280.0319395546881" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8568007312610.4223039328550.9475420609750.2630402226" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.02130599963750.2544495535230.6957284155950.315208764101" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.418058929870.3036979857890.6376493871180.157585719538" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6285950140260.5910532076370.7945560295150.962355435128" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9295881053730.8944650747680.06834177494910.00441151819289" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6408517335680.2704008256210.2160324339850.697168669635" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9608832957970.5598825089560.1685272080110.156586810018" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1805531302450.7665324065540.8279576323070.286966251972" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1642120027710.01233242688070.356645194680.994978490483" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2491690720290.8048083255540.347641503970.291690228571" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3060492091550.9209256617620.8625562475340.547777237474" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3724134723830.6426688564560.8088419854310.144985908686" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9705907259510.8789680069250.2462830228580.443474383301" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.292182457050.04598716108970.799133536140.838525667253" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1908376456660.247063531550.3965767790550.521210670529" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.220577186010.6392532756380.8023930765170.452109102836" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7028309924230.670573302220.9955762945280.858520684066" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3215383373360.1863678763610.04949803488560.32456917605" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1520700341340.7721215331050.2722208465740.378290225281" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8021992171970.8162628560870.136488948310.694419184512" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9875138526790.4244396979270.498192371490.388683135665" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.08726779635120.3108202719980.2555679629290.539124615096" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2520716530840.9677374703980.9111556007020.932592741439" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5961477535710.817975810050.005906845830030.182671052821" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7870469570830.06206854240230.2490218294160.0640662576597" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5111672867080.7283117182530.05835926305210.937928042266" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.308352622970.8586870116310.06361359330970.774744521226" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.748000804380.3472280433330.5851256686860.027611872391" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7852265527120.4811442518530.9354466721660.404310060058" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5053657376520.5630018515470.7041517979790.0579149107683" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2581704146120.3742158208390.7448102054640.933404188684" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.219571587540.7620695976280.1307116414170.427506538286" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7790739203640.4933315395770.8626240591320.995253158258" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7089136693010.6527399082730.2505972315950.545156795639" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8013203132870.870757863710.4202004751760.679551383747" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5591221049270.05154047901750.2052125016410.636101580396" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.08747545981180.04380372059470.5209319841550.411125339989" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7569788847930.8190494408970.2644661164160.996872337501" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7029562891760.4282940495090.2753089472230.908758034998" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2795422250150.4502184153610.9661837625750.980498842776" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1042452710150.5346511994740.09650952452730.925214925995" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7128391140140.5002918833130.2066031039540.252165474157" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9513170686660.2214884148250.6225630699720.728952023705" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2288339399360.8110775722270.310062631490.049253659466" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1756686522910.3915787048660.5899910751210.728722442444" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4583298116170.8524920572260.237317259850.44541988943" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7103701177390.7859096213010.2319241926940.0200633514931" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5450489688220.1994289219450.6209947243440.962204426118" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7336614281020.7273682125710.01075154852710.828925626044" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.09651980125070.08784115579870.1452611630560.886606191578" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8971900119920.9796494867750.7723278952820.776351846811" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2464967800430.1542259192450.1299078523060.582144925485" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9161912339740.01247981479660.5265859378130.0241130108686" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6956379576770.2867969098950.4404807205560.956559757758" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3308255674920.4535104161950.8157189615890.595046154908" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8092364176380.7168497086960.1251578614140.701760319434" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9551614733580.8069022730690.6235627597120.890484726007" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.07418489918080.1743310246210.9334551915510.44137563967" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5313959843720.5175210464810.1926821739120.23862426352" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.01057239134870.09528770538410.4242646663940.679048425138" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3261424514440.5844382107430.05236702566880.175688246544" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3415684395790.06813670834030.5852386678750.0510032537404" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8872120889280.2963371899750.549043650350.00720678104468" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2028620466960.6852662595760.4390933778030.738390180288" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6879830530090.9749506382420.5130844063270.225935008444" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4097095255470.5889073239410.4771106684540.497515700026" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4351340496650.7392656105130.5063777488360.229014508611" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5891180980750.06247799720920.6491085416790.474065793562" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1310645901160.8550078251740.002623078784650.568505927362" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6484444249310.07691353894460.5453776885740.845806085565" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.0331661511890.5817336335750.8352101998320.436393247607" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8460202741430.442374037980.2354622230790.749602971261" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6797141082830.4666060015840.5531299415720.12001075494" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.0282705209350.9148469210380.6502790371870.0601052481004" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9316487958890.9583300030890.2431908112840.636066866964" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3371428749260.6101949347270.9536938576950.484443091073" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5150130229740.02709585781080.3234396891610.273643462015" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7615320304790.7787653916550.7783212720160.47224422612" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1903342699460.2240951977230.1663323638270.112800188622" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1148498417090.1053666275030.7162456730880.155316553679" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3526982128070.4690296593220.1739314918320.735603542531" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.06623443764230.2569254115050.7541349501830.489090367009" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1149141065910.690481012880.3574182505710.62888240478" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8658814202940.7325060413670.8046662609980.532287519899" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7050731350460.4405673474720.3072526637890.113859076715" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7837969442080.3573775774270.6009318052740.528702794057" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1397981066290.6859848074780.5258257679630.359368178477" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6007424121360.9219455859210.06219278206120.97011290686" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8138351963810.9636540011770.4286438823390.863051812633" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6476203317740.04128184345230.6471608970230.687108949137" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.419119848140.5001613539470.8294517278760.490916091665" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3098511506790.7075738773960.1739229514050.233132920724" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5643028810910.8016187169440.05876703673970.580042185096" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6929523841410.5070230893580.9647477076360.980899036577" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.09328633095750.1418607015570.8197611128410.783895910626" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.0918352800930.9558608447740.9999977192510.959356519038" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7622631135820.9436521910690.1348877000940.215829954095" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6876915321970.476678720320.4658060281250.754397750249" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4053321783660.5477484451050.538301239980.789478421772" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.05114597133880.170232546230.8736424806180.759743741658" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8860853762720.6598372333150.6652110152550.772928466728" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1732324039610.5195358640060.5081487620420.165221952904" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5116250715230.01398800890610.5578555582820.391788563756" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1366353985450.8066362660920.8175344541460.549626549513" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8822859277150.287450057790.6513944266560.936352785842" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6875882076130.2881831803790.1512641882650.459381000753" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1933122571380.5217157802580.03659720228570.671339323362" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4561994128160.50013322470.1461631927760.152275333736" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4534775137490.853071068410.6163330714720.0729201221065" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3398944365650.8650421525250.7884602751820.808826596929" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8254217211840.07425345965170.3501885561150.898035753154" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.05978382726990.6404443263510.8908861678330.258208830349" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1807427260430.7278031063930.07551955824050.673234496256" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5524803132790.7379030532460.2786870282860.252085490496" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.197865638030.0001021710836050.8428105330690.166395972941" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6782115855760.3975755147430.5939133559530.470296375788" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5436589906850.01878837554040.5830884465290.66382052472" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5944699816270.4904936747270.1075329241050.450830268778" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4929607779040.964384071910.1835491312260.0582309901497" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1491775620370.5048435232350.3475717957860.125725039382" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2002659594940.3114576837110.8326601638190.304779766625" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8035485128450.1490115627820.5227200378110.34592029008" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9887196133590.5518613936870.8194592172760.0751691228785" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8127260403760.641669367770.4347380626190.597246817066" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2919149887570.6416957343130.701018849540.762873040033" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7354545527540.9268233466390.5963384971760.827379918752" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.01260650154880.4102181839650.5265541989770.189780429011" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6025548655760.7968421457260.4566855330650.664710731577" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4661682199650.460356977080.7654395310850.542476676729" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8924541009850.255737801140.6627183207650.00179290566383" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8857626285610.04293379390150.9083608453570.772315007965" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1028918026330.08222789618740.2851799659440.652303472308" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7244755144240.05934335242340.6867678102270.771668690054" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9817286610480.9185409691180.4995854928330.360299038899" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3882337525350.1690362741960.7011349547530.571514369993" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3431611215370.4794243875070.2411306830630.694557542131" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4051776267240.6738624709910.4543200235370.583837553909" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4696399934010.925054474720.8460297087730.438188138153" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2383768160960.7696820798130.4180065123320.0281037963257" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3900463524740.923797924270.1813463701390.243978313046" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1685621469870.5975459068030.7345118537120.801557680028" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8952402003420.6954973859990.2172011666630.77960852191" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6949751192890.1528761102010.1097010823540.789942945591" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3108140360380.0190068945260.9694618418860.715878081761" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9282298070730.3763949910940.4862435353990.529762501411" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5579363080350.9074470677030.3021807857230.699132479421" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9831445444080.005120919877150.8089592405740.224084344447" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.02511131776870.9152322307670.08081677831810.958875565002" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5438834553120.8497441280460.4042397309940.0579710849559" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4136515652810.8030409615520.5143990604670.301233397267" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6364319198740.4767618997820.555994837210.458954094398" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1221550836810.5431852827720.740504660370.594758514421" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.830704972410.9080196740520.7866423938010.922192085074" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7371845589380.8699353643850.3405044752170.748568287117" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7961131789460.06896506296770.4484426962660.919242058024" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7487889897840.7547067058490.5953741082250.447588640855" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5878064440710.1495251535870.262098538310.93576003515" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1832932772370.4977286315330.4421635813020.683292605602" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7686967542540.7750446634560.3524741413890.990901182573" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2496672890080.498060669170.8449136066780.438104255491" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4519473592610.3411133106910.7477477547330.0728603073565" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1582940826630.6969528322750.5942125531840.299652158569" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7978351117310.6392582683360.5750326424490.417172418975" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7346550822180.1168729002790.4986861639270.965531021764" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.08794901606580.6384535325620.6827905652630.606595905718" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9206556377920.9268405946560.7518059073930.741716620239" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1997249117250.8614947052650.1098825436220.0451427540228" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2678556562320.3849858716090.9782476191480.717401965609" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4273872482750.9465564361070.7090432656190.900654978325" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2005494197780.2148837347480.009748634614390.348861760602" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.63861114280.03275098586740.3117755164250.554632339045" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.02195033906760.2009558731630.4377798431330.157830566638" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7848648508160.9855865919630.4256625941360.548429241478" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8826804456670.7760692147940.7273417675610.0732247348332" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6183228938620.5671083487220.5794320475030.441518682085" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.06972375895610.3071416374470.2211617208010.988511827016" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.08322438867140.9406725202130.8588912329480.763555542839" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8857052127560.3315186874280.6286122321440.671880112" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1173384702850.5743711245810.9723285714880.92288412438" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6634147932320.4749042544570.4530573906560.117788636652" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1306182953350.7866065427360.88289282910.741758358485" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3931621343040.7442608579990.02622375959480.792235121113" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.675708969920.6531026008690.7843490436330.817560807857" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5301949128220.04241170652150.6746863805610.879982543316" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7854925831470.5509877798250.07613932812110.609250728101" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1554630961450.6632412695390.4732792494070.936784555676" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4783108177680.4731551762450.6793625923780.541225960489" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2621085663010.8304904285820.2643013614110.460846468083" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6323086737270.5316364006910.9833496315530.353913161737" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7982027069920.04820597855010.8077764671260.525984140851" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2835935620410.5925488427940.9349256589760.435793401107" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7325052764220.1673009276210.5894202605740.353311071455" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1366181823830.1154526682430.006429647599540.506487017933" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7185246671780.7780953756970.7664504825350.0904716427405" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1953902806120.05546997438080.8210607459080.96294661607" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1820102781430.8608379769910.5362259764750.360552489284" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3327378289440.1910993479980.1827254388470.44824980076" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1186997994750.2376065317170.90802772470.708051161122" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6477756674490.7348800518660.05707355773510.912676578859" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1334521291720.8946591306170.8325436482350.424694810132" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8736363722460.8846581160110.240306098390.210265451158" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7468029598970.1856233247130.7457677318780.51960045868" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8310382288990.2326202280550.8763215953460.141136469074" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4502314306080.04530384550890.1372553567120.501033006126" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6959278319320.7444136745450.8831927830930.177264454159" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9602131324690.9510000903690.5687296240770.659273208475" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.518138642780.3786083192230.07924870265370.644559700211" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8655783566280.7083492192810.8277035702480.928206181279" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6317026234390.6290610124120.04456807059920.858256466383" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5526059231420.1542752358040.5868687352430.509632182268" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.005307592089380.5961101707420.359482640750.318003840959" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2199195827010.3419480826790.04975811920790.412304409412" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6415364769280.4889732566760.1784732809270.77255154772" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.06927063370460.2258786964440.8709363443150.0515461775962" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6934669852390.9688448701640.1293638134220.343582640468" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8082745108980.8165875100910.704819744710.690806777538" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.931201223770.4339558681850.3457365777960.298996213661" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1147729392720.4628506115670.7629133785690.731230183625" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6904659855790.5254610472530.2351997660050.730831261436" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1512700304860.5775863009250.6842963745920.592787572361" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9838949961560.9452771836660.2255421060890.0440045449489" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7849856138150.7296126163050.8735951878030.774381318843" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.01865834339030.4321860973920.01192505246530.9984077852" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4272573166110.7675968481920.1226431478650.887794653716" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6482608051280.5176367895830.5658660711990.0823791774306" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5085080539920.6188999926990.01510206775630.599155161547" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6709393722870.3842632350780.2372289506790.137030545601" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.09254070405890.4590829888490.03869645742750.807885109504" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7753954000180.7880501754140.9143989229470.931833011278" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.683832905310.3859847473810.7086419428020.212986690805" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4816189497580.6639308708540.7860100213890.692028285652" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3040029113690.5903702090170.06698046637740.536469757132" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3912889595450.1973327123090.02619047643820.272681509433" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6665372281180.4475943051720.815824514240.60448123673" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.490001543290.8640974896640.1818658094390.00361015275344" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5231250674990.8745626303710.6117127882770.293687452983" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4707372810960.5953996859610.680642137580.799431464639" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3524362873050.9302377014580.1627779124380.872275991905" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1122636498060.6734673436810.4017089205190.626527494724" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1339934485660.5521237948450.7207245386570.223839120885" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3046147097450.7828750180930.9536575599090.197441425969" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6621750516450.05063691795810.1920483599660.719525699369" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7415279719820.5632062722790.8263370030390.276225359671" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.07464120492790.02793209294970.8162067159180.745000857529" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5702158450550.6015485872960.1657805555640.20255959714" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5307995862070.7297766434970.9277295051610.253750328171" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.861022278930.5464029149210.07387205923780.733648123488" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1751090137750.4671002330930.5345430768810.257164721395" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3683911554110.1294963110260.9969871030180.631932673087" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6618630180570.6213743191780.768301034520.753692951901" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1764388605370.9769065011080.3973998137840.869219519933" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4558227798960.3829756925480.2951617823330.660939601826" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4657662415250.2593950116390.9082460114060.862089486433" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.893136817960.9152920899670.8346041609320.113433679581" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2063848088960.05808585225610.6976932261650.150468161675" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.07234450685010.3998363354890.7163239104210.62340148314" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4315086062680.9388638041120.7644327317150.173357265874" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.11169860910.3025011582120.6557020694230.0884422880426" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8371753432810.3809505974630.8845479025330.84496698555" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8692737641350.4724779512380.4345102973380.191865335275" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4285834860780.1175634526090.8684135589170.0223947882069" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3534747157820.4467223219140.4285880683660.669618200521" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4748234058730.1639931562120.6936781202740.262266789636" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3288936299990.4463248072280.001658718708690.191855446267" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7674623680250.3286935790720.2217616608390.780941491878" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.843827472220.2205466042020.2399218849220.759939432582" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3718628054190.8231609864860.3996657757890.388774528216" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3686738466740.5954947984380.573587761250.138573730883" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2237578332740.5600829190010.6540296522880.487373867919" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2926742445140.8104853392830.5327839911950.571834166147" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7876898286820.6909101909050.5462970008990.991908591297" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7781459424910.09737103652570.4660922366770.609605281889" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4618213553780.6132033177660.04564783141480.798312651892" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6682027499340.9830202968970.6167048920880.0725611493317" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6341959649680.4909695636170.3904931197570.59586439322" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7347529549910.03206799837980.4684244127250.192464132265" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7551534420810.797515482220.8444237292030.943527587625" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1494584593210.6596668059150.01142180623120.713796414914" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.736755622750.4873156859990.7478672462820.634554034629" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1878314898540.7001626025670.2046453597160.12930253353" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8179539917940.4207087767340.1096162443110.193845806557" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1512328343830.2710550001990.8606699796920.495990270661" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.01571056363780.5610074936140.05750491039370.0129654266808" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4996104664750.7918705768350.7369759359870.884908449105" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3557683951170.8785074172830.475310069640.998639298501" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6087835668610.1868467128920.003208745263010.138248008841" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3365455278350.04447933652490.04618828495510.0998911987173" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4934231116030.2334650012630.3705131828520.26636474365" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.06992524625830.3130737671770.7033943774970.261232982868" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7634954014770.4808567275190.09080020893130.72034537885" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.593693501460.1877642466220.03311718614880.114388591171" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8836085486130.4184269465480.2511287735470.112384553862" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5958039452370.1480388444390.7776600183540.182413852123" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1340162845290.4331652168610.5889715209030.601257690866" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9990063314690.3673589984730.2798236898550.752363463429" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9346694590470.1706750816650.3303250900520.166497465005" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.04971816338340.8358335581780.1692934740710.672850535002" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9516429441540.1426298405150.03764709053010.144523062649" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3826183168150.03910984561830.06441572758720.809967224147" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1226046838580.4096620833730.3254530066180.471003585203" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5983231951410.7520402092490.4604784047520.259528367854" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2515000220890.01938211314390.4856276783470.0201778528888" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3903429529140.1730213457490.07072062768330.934844841991" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8556011592390.9458184003130.3314474418520.935198153793" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6457168542520.4332031414030.6911940822130.29450275488" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2580530241570.7892309060160.8936897837280.866387437051" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1119732172460.333359060950.2606338570570.136385919803" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1941669898670.03150565245260.8201239654270.881373356216" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2526915010290.4443079143330.6087846171020.691451967606" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.85272597140.2202972353160.5216952352770.25523938315" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5886607562090.003547842336140.2107919620030.21266665211" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7781533117480.02972565823060.6718506317590.026735949154" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5211488924040.7782022122430.7206080372330.0597201717026" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8621218229210.8824664079270.9948048605630.820621691936" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7035024295330.07185653694830.9838995042490.254633031599" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.009664249133240.3458439884720.5490803821830.46848828605" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2180802782890.4107085042720.4786140683580.944900261039" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7106939522450.0854478907250.7391408990610.683306612365" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4802404959630.06828279453580.6131416502030.96339674451" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4032976395370.9652250510840.6042647919610.392562708514" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7654234873510.9587832291540.253377524640.699919583635" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2428561717950.1457534442430.4206094357190.0183159785812" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7717473641530.618145059070.03093155026560.393515748557" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1947707547040.2976178624380.2524842637710.383824569463" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8585785004410.3005426836340.2977177301710.638991705453" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.04042844008590.03446871886990.6416325669340.943681885201" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1674082555170.4667301488420.8320716378810.517262824357" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5430172227570.4576579253120.1709759043230.179791226745" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9354248862760.3938317075820.6775518444570.199050814303" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2523842284250.7795699932910.8995213219430.573977796321" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9583406805280.1318408669440.9963936812970.0941777454656" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.885309316660.313640778830.5253499440890.979605508964" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8680278803570.7967887900260.6651352148510.420358642626" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8397547493990.3617335424570.9707692653630.694851796936" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8945891463920.4450093286570.9698633291790.718490537827" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9737861566680.6341409935350.9089178370430.0980782066507" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7522025192940.3608565473780.6421809712990.146868868113" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7588845964690.1694906612620.7184315347950.860618614919" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9823036181830.1473246643920.5098831919860.775979160966" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5661501242660.4962199853660.2030490883770.201118153026" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5074192949540.9825978512080.6509230348120.253381574715" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2760169945340.7869553133290.6421393138670.857438630881" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7932533625660.1297052920550.8138728254140.0306162773506" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5364230623220.07970833118950.5426625116630.742516173194" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.101135103630.01627456651890.7656747328880.521759368888" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9657720445590.9451413209250.2321048256810.712518498892" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5977503812540.1686935351610.6996893691680.21279199973" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4649096357740.6215209218880.2005262922350.747980913387" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.106700229340.251627047460.9237489985710.678762544952" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2059478655410.2824851832930.5795628744280.900645324263" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3525683952320.2236187673470.1185701714420.2884990869" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8679903034490.830759422110.5361972991550.144384530066" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4318995114940.08728012390760.07296307444130.182531317433" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9452729049890.6271723205260.07486023669590.0894213170503" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.600797584990.5876494987010.4545953230360.216521820277" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7364398487730.5381855676370.4106987401960.0640726355077" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.123767045290.01768321546080.08709525068860.669808823211" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4021567829580.6951373437520.2299753879330.00656428424839" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.04645029732930.863296392180.9074438623060.446178949872" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1543694251530.4236590677180.04761469684590.295170673526" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3840313307950.6154680147590.233651260330.134440775642" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4637670110530.7833054572380.1071113545870.407318029394" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2422926584130.9810853517780.6119639802010.547577211119" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1091738024590.1950621596030.9419961117830.839963761881" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5948431254880.3780407280410.3175899380060.0124974492502" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6873544378860.4890635490460.07157928412410.0535643097062" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7866575078690.4936473980150.53546242170.975514336919" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7738079016940.7898008622650.2483928129020.777419270805" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1192642559510.677698394860.3340131041140.508043705664" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.006198507200740.07229881584350.7011071177030.221527860365" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1969707059250.5009314046360.219059294050.606220125353" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9210887043640.7281376420480.5774137692750.620387835033" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3744136647360.8835567505750.9258695189420.230795218279" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5083475686070.9540869156190.6927728750740.854480030268" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8871478875640.7603773579170.8721847706630.540441800409" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8293398673740.3360530336690.9653748963440.124148543638" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9192712905560.7960541124350.160685901390.507303274332" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.846611094930.7350865471790.4173616541380.327145423222" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4158428876430.6191702924080.5563742143230.222826952826" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8722461887050.0008835660110550.04361443296630.653969908897" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9041654832290.1438796246080.8547720265990.503457912609" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5889685186330.1176981849010.1528823958940.99854161522" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1319419575840.5813218271790.3219819290510.169665897926" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2407812738420.02610640093840.8699431784880.589319215798" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8226211394460.375571862740.6739017103320.543770586759" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3101537918980.6254993047420.5905496501420.425909545594" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3567036059860.6646298696170.8957041857970.435646645584" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8415358877710.05080619771350.1772052932540.964277396103" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3227470840710.6868017984350.8154892419180.513693382911" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8866555832610.2137468942540.8491565896830.555109257379" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2110111168690.52494719640.6292661598080.789067205487" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7979813309660.9344147302830.1245382632310.396883503839" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6965525533410.6574955132760.7913665247070.104179812279" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.08490544980820.2169093951270.04190497087330.387394128334" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8024387286580.01840897955640.5361728468840.541318221479" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.477592514110.1500680134970.3009107895250.330207087631" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.589806535730.2980941343560.09944669443290.0858458934929" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2984477013380.5579199526780.2122879221770.924249422907" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3109634723770.8679021048760.4086388311620.179429057975" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7011322263380.9278693694570.3662878078070.186801826986" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7345640623820.5777408338930.9401572932270.59393969034" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.07619999912440.2793327831610.9234286504260.504741320534" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5786393018630.07340402042960.5123534413930.738667861468" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8448639772020.1256869863540.8002360717730.0320611964141" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3480452002810.2140733126030.1019269651690.133865056212" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.03039744722350.06037254412340.8026637666110.600476172986" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2449199153670.9626970473490.7154791348660.416632454843" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9205929143230.7308804163530.8913170978940.157051381045" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.008380071320970.7731645014160.8008439337820.572358904814" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3128067338870.7233669714460.077363079450.493050805596" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3586273519470.5673503084050.8473196354690.941594598804" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9828522323440.7467011313870.2518730055620.654773463763" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7711178496520.7064609992720.06541291665880.439659047462" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5281233778860.236257052090.2160469095710.0550055659268" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3917913035190.9903495224670.8629756684340.41434091182" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5816298041640.671539583270.585201017220.487167125279" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8098265820210.9701310244160.9850775402640.287699396277" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2302379627110.4464660108170.7675540604950.996944024554" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1854518683850.917839798370.1343914248170.996370940903" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6958585942820.5708843865690.1079766599630.384153464699" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7155638564550.4434126634630.9923112709380.177609731633" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1988494679060.1009143053640.7459667325640.5477181203" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8463010455540.03158344936710.669105416340.230885288679" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8564848965230.9765078696780.3616338848910.262505664823" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7035255348340.421741198850.07699993702220.0140395872411" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7547016500920.4489054055780.9949192473970.237493160632" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3768919192430.009788095216630.04206942203260.389729764666" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1641179085980.964835727250.2985011431670.585322020061" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6655373201520.542400599920.5842692958740.658400770789" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.0186719687320.2974464198220.7587106107470.6547990536" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4041450911410.6641530520150.82535417910.91680866581" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7487507673120.4100817507630.924497071810.758302269987" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9719015507770.5554199616770.950330231530.324103561741" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6608806711070.1379693571080.5002536166720.641341962713" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1644592103880.7320204414950.1484252868710.959937776351" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8407883034250.8350439355590.119486302840.0317005027167" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5104154542610.3269775668260.3855842001460.794827366968" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6106676112160.6691654324840.55892145070.523634978116" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2358265706380.641375689010.3697820095820.400694032148" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.04309795414760.194051962640.5807858111110.841305407459" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4997879735610.475111493920.2440248781790.318299998022" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6898463141190.395895141360.5000272223070.0768887095372" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2540888385250.3534011627110.7845140339840.833908784409" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7120511249070.2154688445010.7817676622080.329125159833" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.903232175410.4523455869860.894206514160.687235730871" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.05799741034330.4273768405740.3109516633760.543670046221" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.333599996280.8147677094160.3750956638350.414312301613" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2069492029340.2520329922930.4815945187950.543463648786" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2404976261170.5403205367820.05160667422590.868425153963" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5158288030540.5451083583680.2425337231210.317523360244" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5731409611390.498859493860.4919302528820.57271552373" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.317956646890.6194737145690.8316817527770.299381078209" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3217874379260.6973791345550.3790692613430.347244348486" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6296871970110.2106437603190.5617568631380.68454823999" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.02420737165660.1894065397750.8304871462580.774947607811" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6882907615360.6288488955870.6216370619140.774038529147" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5709545058630.1814837713130.4256865719180.778794896155" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8838575378430.6786151653790.3907119944530.416535040836" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9016991675420.4292974497670.1490311768970.180804836258" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2287017186130.2705849963170.422496898690.388814980687" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8511301355350.2839586317750.8959439886770.195773109337" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5854060589520.2691857927630.9200583228550.250006801704" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1948350055340.3603481446470.3610547983610.716559089642" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4465201874180.4281221623470.4253822407760.123419684772" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8564866011780.5487866686440.03940307758170.497852637579" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8360517482560.4484797942360.5745690668560.353847456474" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.002781050339720.5719355279630.92822532780.504513167108" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7196761755030.1401468787760.7649120423930.890200129767" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4700267645290.1207362855650.6337619227580.0971562690192" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.09619219666970.2966999176150.5442707683820.40028471755" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7564337910480.05615029786310.2958363559890.994472669711" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4454068295160.9093875919360.4031440331630.67745671869" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.793145945350.9634416930770.04444539707050.81076439147" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2485193095810.6209884488240.3824921416580.908727765713" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4347079164990.1068739641940.8342154783950.332452611507" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5525486092030.2727685454080.9982315767080.984028918959" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4049045496970.3280399806640.6846740802590.648712204245" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8937593273010.5828779703670.9857156446890.960744043792" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2531772112880.7001413109060.7567014848310.0209847813553" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1390838864830.6672533488230.2703831487650.207658183387" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1698479680530.9313648642570.4170585014710.495076595467" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.0635288026560.9994418325270.6872806180110.839844366858" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2104237970350.8274736714560.1301776252980.294934433388" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3207287983990.9744509833440.1290140984130.244089621723" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.04901749686660.06608202480160.9332802263680.205541610649" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.23718406120.8715428721580.1076976595370.00148001508775" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5262507095520.2984479096260.5537198574170.734651710511" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2770323959670.01097370867590.9270236013010.869967639145" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.469547725380.4558572025640.8315880555810.455608717976" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3144829077270.6183288676280.2672726015440.885526255332" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4062086003250.2400956013770.8794599614410.5194993366" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2189461004860.4620899359720.5249056192950.478243395297" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5958750759980.3659472172280.2966547927780.319937962019" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.04933397229080.9754242769320.7101498089530.252091382812" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.849208603620.3487731253030.1049334603630.285071304558" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6725403221240.5349493920920.5777609878770.414647769766" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7251074433950.8662457185880.1348624517790.15957773615" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6256130761620.0909891803740.6968187820780.676146757166" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6864629033620.1130279222910.4748633571250.104227489717" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7357900360640.07907281943520.2098419617290.797927644535" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7189232696850.1064724491540.5348256752640.838904692299" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4888744640470.8993785328140.05004343845570.561935353823" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6170780554970.8420785384090.8868344520710.239935470421" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8009077279950.255020994190.5215077774460.33673588113" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5716261341750.02792687386610.5111185009660.913257412461" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5842062121790.7319122893820.9378921820710.135302478223" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3124461348780.2151193740940.6500152263110.327024990357" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6910525062250.8221924551890.3239710887290.842243829762" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.69561153250.6387759772170.2166330152940.957639481984" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5706734048030.4287881720190.8741682225310.520569631447" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7140039858570.1342566379560.6776189968110.347937218051" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8851997271230.474543430410.2925679597220.83944672082" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5179491049230.3660924346370.9147549655030.496529548714" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.81255010310.9162304520830.07043518273310.603399214123" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8572474230010.8327091780120.2851259245920.128681102521" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8504651575180.3233255020170.167264007360.126707158887" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.008972479001020.6291015378920.7394528182280.819389032602" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.502707941790.5798643449940.888487557880.751175282638" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8781751799230.1640345033550.237412319680.509914087942" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.690762465260.1709262939970.7929302067940.0285794439899" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4889604691440.3444969759080.584579722840.181882476964" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7512376446690.9043871992860.2524200391820.373157053772" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6791644473340.9459461813920.2248293796470.359952537392" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6931023414560.6111922159330.2069098541220.558576847503" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.01407283744310.2707238580310.7822657502810.321608057634" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8767647176910.1887793484650.4121710620840.881180581884" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.07904315488130.1968749677740.6343212223810.963586111809" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2943742105440.4063955096080.583214762710.649758776493" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9736469528810.8530097978630.3551385204480.556992961385" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5398276171860.6496723749940.9354047799540.0109788229605" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.07605888221770.6800277632630.5716233007870.645349601991" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9879680787150.3318763314140.06221353860840.733262715719" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8953731476070.08316114958140.5261618130680.442454554944" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3823255907990.4927579002130.3932768145980.0302631694876" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6868320091730.7989923135050.4754670637660.0396442871038" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7103790578490.6255827079470.3771598694910.237463416037" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5902254975910.4039221058730.3976043766760.615226935166" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5859137213160.854090855890.4354474735970.120844966134" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.002339237666170.2026558078420.8917689533790.446583991893" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5167748033210.1061031496760.1596844472370.324955205511" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5893578862840.308640663520.7906475170110.172138061295" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1601350347840.08937469681960.2005870602880.916389421515" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.0255251624610.6992510980580.3480385887590.0218611084583" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7591536558640.06400188155990.0433526100310.0624474928406" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.09830394603910.1225401664910.4728639864240.329331037011" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3445972771780.8587654028690.2703297709060.659066444196" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.757608221170.8923610274930.7687015363810.291626585764" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1321297554790.8779991237620.8271910086690.793473524405" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.05871893646370.3035305678730.2978110758780.100781186899" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3788351407610.0239276045570.1273093746350.760872651334" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6585676486490.5808407867280.864796985230.695659154964" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.588902826290.208720717210.8889766862090.74015429984" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4627807964590.5459997066230.6379183223470.138858621834" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.891383939470.006012000747560.374997985560.9014649561" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5278569912530.7951557882350.7025805556330.461125139212" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7498665204650.2512108772640.140740882290.567551800893" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.778216283610.8040037772930.5427816085950.316211964248" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5266621814860.315909081920.4337497524920.0547924737313" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.243125862140.3319590197110.9176807895790.655942971197" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1763037292720.8410708140730.631416032210.404933444459" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.377220494780.4291180748510.7548776724680.132485616076" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.0106372395310.9899076236620.7967071901350.700342360838" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.04235971999210.1938628067230.7416919138560.944498940407" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2873871468840.04515415748530.2913908964370.618137429252" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8046710678610.9502307755150.4828658202510.853861777216" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9965404351570.4210228598440.3846670598840.375966330663" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7141130201450.1826893004650.6710111717210.463034923867" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7165673717470.9453035538720.6481796459840.472378142823" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2863310487510.9528772032630.8862160476510.584348213686" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6957404476630.1370943057310.9578438650790.998526785508" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7058780933930.3047159209240.3954774423580.149283137497" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1949151787380.7075939242040.4529477501870.0647207996716" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5706590808390.4841587119660.6018397700530.324836752275" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.426802570240.5614089809310.4595448913970.632069913863" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.48794669360.5307291095420.8343999433990.772390917283" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2907224691210.9825826418350.09900011201420.553214119319" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.969339638920.2151651966540.3761783502880.851696030528" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7476229283180.2051738244830.3073936602820.734125597665" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7849246469280.07842902798640.0378837417920.147269473964" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1306405039390.6957503031480.9395777295630.369411484368" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6655374447730.0792204443950.1459837043270.484665625383" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8676336326230.12263589780.7192877269140.215632695993" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6094270869640.505410964580.6833298634630.339986935207" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2484895613960.2657176917510.4510052200690.132145576801" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.06046584006510.5822151895320.4476196650920.645794044024" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3466136057730.1594381833380.2862953986340.279602404254" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8305114471410.1482841767940.9323657286750.999707048051" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1081823795140.940071576290.3750045789940.718213188251" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6447219402910.4892996923430.5676912351640.646681737561" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2675875575890.6022730046720.2204193427390.357849812562" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8689822085440.3088996368210.5305433323790.948419205921" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8688325571570.8816655179990.2427975482370.95372239257" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3919719431630.4294785953720.1659770361010.0332987076391" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.01958060101250.5863042789910.6194241218130.932950741883" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2640731379530.09753701943970.4155621650230.594227314842" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3548634624480.9544391456310.6544189083630.54715968164" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7793692297960.992619361880.5514383268180.212498256617" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1924685346540.6341501113690.5401007755670.625615722148" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1757881916160.2625537673050.1278110734780.887115346282" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3625691262360.1030526189920.8307804351330.0366296603313" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5756021313970.3072386900390.3694705023790.192702583321" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4287163308470.5900110322630.9934552661190.428807209545" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.005900444401560.7846231766860.03053857907630.538703977009" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5376335404620.6795546802410.6464858045350.762801659793" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9182326845550.2332713905680.4230159236170.377052333678" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6240508660110.4289333631910.4776137933820.50734804259" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.05137585203990.4464598460460.6778916253470.540928126221" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.193749301670.1895145321620.2223733714770.791970078857" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4519656750960.7109036960360.541721310550.760190751497" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1133448560030.9575179569460.3099835013110.773247958321" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1929325348270.620638119390.6007251746880.0283082230011" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3609271932450.421833016220.3950851413070.194313588501" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1488446110720.529142750910.1694907753930.458013516551" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1803696532590.8614769039530.8648045882980.0211836294641" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.05420107730630.05225074917220.3554265435390.213432316803" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6015918515850.4434655346440.4156818256050.915013154383" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9992652492570.5570795347130.8004388271140.167465547053" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5238915689240.2981546644060.9245753756120.441986231736" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6173435160.9523062704860.0976544876770.769227279765" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.05564941876140.8273953658550.9699048314010.840823398173" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7616103988380.6781752330230.1646162003550.808581387419" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8837419424650.979295590090.1601111702290.137780040214" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3219607813720.7166093051840.7201135779340.440407194048" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8031059072850.08111054036960.9379398289250.568419572974" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9488268167580.4504827807060.2786039111440.208070164314" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4054225508490.4245134075290.1171762479270.54254256749" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5035870004640.4205261746470.3623524216530.228145439518" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2603035142090.08263081331530.5291707340070.109285826633" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.579817580930.2702894976010.1077166638060.466846991882" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9495790070820.9184897047810.2389101243570.637397592418" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.08801354441230.917279986040.06703502285230.23527461063" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9871204782880.1410172487910.8721746963380.953845709653" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4843992164380.650851083490.4981101955470.41622241659" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8554319007090.2550494847820.4057405478650.210740585183" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5975111968630.6028003449630.6368183925350.787584686667" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7107801758140.1575269588110.147926437280.239342006351" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5280452136320.2916402674180.9607661943460.384752833909" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8822857498410.3045492975620.3312836399430.690664609697" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6065246223660.2470172039790.1682858406490.616453949361" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2841031827090.2064930419510.6934258018880.64378081073" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.05825940615040.02549088879280.8331002418760.233947480732" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3386420218070.4541972299880.8692681186110.16880391262" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1231494075570.6329034108180.6841198265650.65665398059" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4765849073690.445407869640.7483763638660.203302288022" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.09218668852380.3646645221350.7781329037820.0467623542924" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7868220925520.1693085025410.2602677151250.442187305414" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6263487713790.5157927424990.4856944171690.295970893195" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4705561148610.5137596280960.398574371330.0416249830653" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6063746907080.1495909741370.04528224797510.63793564084" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7259763891640.542370098850.2129958203890.465260799941" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9849046504750.6229021392910.2786218959090.605112316454" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3478747789530.2693889850120.7082965478190.12604397919" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.472442777070.8526903715860.4144335462140.529165040474" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.565075502850.9486197886530.6892914604450.555866726422" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4989939264010.02889018771260.2007928405710.0125031332862" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.06290996664140.8139307960730.1557961446530.703153732225" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1405623457920.1280682284670.04201777757020.926933381543" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6798075353920.6509706868810.9110599225170.176025566701" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6381808146870.8698059271770.8495478154690.888818648834" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8133730869820.2931298603180.8229567402220.972110086851" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1153621835610.351662200950.8439750600340.653450664277" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1637410455420.3423164336130.9218712288920.809815078473" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3837178751230.2689116072270.125684867070.263902152683" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9792459241110.06096437997630.7146092967150.570032009391" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5475476262920.7149673833950.1270647540720.233952359392" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8169636690490.100914819270.5193873782470.076003736153" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3201430818010.06328631384520.4740063732520.843486442702" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2464078378820.3823512883110.3945892520920.65573599062" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.595305430190.3833983446590.9867536782090.891614764798" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4171894739040.4503956559420.7039228560560.22956432652" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8519194895680.5188086542020.3203684214380.141910867056" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7680572446030.7663223171930.5135032294940.41386136078" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.06291753740060.2128802431190.06292620463950.183972079957" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7111744080950.8943755133930.948806332750.19017272967" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2324616621430.09723514650280.007902040477460.228343592756" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.06396965141270.6612713485480.09624973178560.197375095964" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8364142894220.7025283401950.694027339640.0580639998883" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6394050431490.5261058695240.4999270720490.20705279969" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1907066831650.2051187744670.9054905782920.881420102995" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4128535598750.7566933333460.4210292785270.602177104433" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.49379559560.5496173789440.3003371759060.668850911105" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7394677455070.2081321172570.4045393163090.485061486449" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1828840543780.9623135351480.8210545216250.0471113435496" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.07791244075330.2789658585970.4824547911880.900244440161" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6711826382770.1046186542150.5996500732530.408082920443" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5162574931720.2072747965440.9614860333590.0532044024227" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7113732940010.5714594645080.7927672533420.891650968972" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4398734124470.8135800080410.5237966938190.346959027057" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.415289346280.785519043310.1437261460.229431102843" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4602440353220.6954162417680.2784687603380.616917788147" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7734042405320.4170915471980.1405834511480.26949328812" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8572435325160.8228924009160.6864696738770.799881711214" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5871013304730.09402024350830.9883723235940.453126711322" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7685705246850.8167328631880.9934248853070.970304926441" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6496860930550.5488449354670.4234948025560.706413270364" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9856134069770.6084344197620.4223939572930.647299222221" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7047487485760.1324086696710.5904254568960.587886634843" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5604985342650.7489292413240.5595543446650.998383645181" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2021767816490.3755076307930.3974046917670.0610284112544" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.77578490520.8577055308070.5093888463870.888375976312" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1642753757650.6415851983980.2152423279840.898138136856" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5815031124290.4452223660730.5440923842960.937804230937" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8226094612460.3469151345130.04476138316480.60637559593" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1949798105290.6158503577120.5217005240340.296266132071" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5125543348440.7441600578520.2458502223750.493494856621" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1871732893680.5240300390450.6986434483460.270009565975" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.05527758489850.4995571275290.4003926735760.383003666689" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4727746926030.9642700287310.3672337990240.937320797727" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9830087498580.6929113637960.09442845071490.454160873982" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.540733872990.5446179255120.1627251796720.196434966173" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9083875973980.2329381521030.7647417891820.089425708844" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.001953696996720.1165535422060.1791623624980.301428909274" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3855324062690.8498697409970.6474582644550.910216055337" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4524720724740.1793944501680.08139171250130.109704445362" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.18887710470.8037290941960.2172213043610.976560966895" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5915966659160.6644866029620.262297184530.476264045313" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8090288554760.9649887967660.9158355283490.602122397016" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5510132892760.4326105793580.8823830531550.103654340745" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5534706541990.009295443086590.9832763329610.31422404705" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8111572567080.5851360044490.1090174544330.190424290745" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2703251112470.7473108234680.4620637776670.40321086749" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2190208323550.2047173277510.9317622235960.860665583947" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2765939542160.2469844365110.8542114367390.887524951369" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7995122213220.09469681549990.9318653037130.392745421037" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6378010216960.708801681620.6652532820080.777200649498" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6632421919960.6715606313280.07317803128370.730624928109" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2451248040180.430546149550.03002491746270.983695018107" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1311744481910.8148342135040.8953924928520.621153229234" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.987660672330.1820394422870.8591109257260.822669304187" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9683922444820.5307471412450.3656818511140.548455517264" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7699488164070.09370631238040.163709732050.829199259916" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6139830489190.10081493410.03400014427640.493765221233" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.03877014664690.4823324024540.4257356644280.43574405352" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3379485267230.1459041241460.8120517604420.185542094007" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7279322881380.1293159354590.4121193210510.607988257768" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6124589796840.1992727844470.5766834468260.356986066269" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6081569196270.99252363530.8657296354870.192220418243" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7642893958790.02200644017780.4379291228080.176330688292" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1305640401970.7944831438320.2283309889370.832430734038" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.518855283770.7473332735530.6175611126560.450508865747" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7890020263240.4839600194070.1169169293950.00848105142066" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7286734447030.4436860619180.1037498529630.0684806419537" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.179175357540.4819716260530.1057574736380.737568622492" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7179256230280.2765215438750.8358023077720.514727812257" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.002894118254270.1970996884870.1234345929870.798586170338" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4007260959030.2973940344990.9915122636820.842880711964" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.466683016850.6838995925470.7870714611180.405651295385" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2524809930230.09031367193370.2869050054770.986515422656" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2290145912240.632079324010.3188681977210.316592595245" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.001139350405640.9164087609890.5964374489740.0836576458764" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7580043024610.9753645726260.4291385698530.4010005705" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7128893123760.3394391417580.596687743740.549435035294" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.386355867630.3371364737490.6290355814880.449008450522" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5615132087120.07072342499680.06006756774980.840740851095" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5253301988270.4339797220470.5216339636430.86534192196" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.01183783070070.3264136426970.7434937480830.527962511366" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9565750224290.2321219464560.1662777032790.777336031657" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2560760497570.6440554481480.3586102912970.454693895784" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.58111960130.2264432840740.05372039528430.319184998483" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8096012228090.7744716208730.9035451289160.800218225804" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7999513917060.0830237837410.4741736943340.795278866977" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.736643544990.8176147452510.2763007673690.76415310581" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2132380776610.5973762477180.4244272229710.998503151999" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.07540116105840.01072490777160.3711669431330.591498053494" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6594599498370.8754062284130.640312730120.626955291871" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5828567754630.89946795820.773113811780.50632271849" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6811666338620.755200697980.5859838111540.93967888363" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8197106643640.07981233577880.7659748534360.671072836073" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1663522942580.4093161846150.4724497494920.619110929307" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4915941692310.5433956335840.7605457301280.0702727307358" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.02860315417280.7041276008510.3674708880010.733718830962" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3765110540530.2625942900660.2369307246810.738798978929" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9307410209550.3655628539960.8170245865270.447223833938" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2181382734890.3207492965040.3303211736630.224782854939" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.04632530856010.7756754082910.4210770060450.509001071421" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3664207576620.8824451398650.8860295569940.992367207808" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.07403408119210.05555389126370.4547468480990.330575446601" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3729156090950.4766605491890.1764972012120.398643795698" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.06992295795420.9399547638620.271462233530.987298330757" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8358892091060.4432872605060.415389131530.577977874079" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.954785950610.8187630295160.846854416180.668668452272" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6598852122680.6096167111510.7393123976750.00765258367517" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9896155295570.3439706294340.3982616147260.561186494518" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2072857400360.4360254303430.4630333648560.742724691221" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7083726233420.91234223560.2226005896890.278947090824" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8206258529030.9795040266180.4875568496310.328334575455" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1729761358230.7029087698870.8975466331470.493940576453" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2985232814790.384022275730.1617983566410.908174589831" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.05666734846760.6632006403370.1890484505480.740210504812" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5474305706880.6023554364640.6949856607070.421627073018" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4114635359180.2349696062880.79349022350.812227035154" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.531258092420.7072159685340.3678903379610.28726592492" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9192094547420.9958461098240.08418536408420.555702785887" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1968696302790.2192451819620.5024645121440.761071528679" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9089134782170.2986910785110.5923000623610.241572733523" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8839428025280.01094691897640.3902783292120.194489452646" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3445473994580.121738086330.8382177240210.743467037379" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.16364825520.9680480632090.6688519740930.105052080709" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6160347296580.5354732186860.9322656521140.946918486442" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.316384500750.6907026548170.2212422210740.91635876122" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.826648399640.6764843016390.6134898363530.173265258804" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9741704710910.1179086348240.09864840753620.00512114179859" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8535105950410.7297579873780.2098171638930.886685015451" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3652932294040.4799150041110.3091299499460.421127383231" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.02250101782690.5749608688340.829100504810.973195338976" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8820744381530.794138046390.7117973233920.801991517629" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8758142335070.05812812171060.6658590095810.950171023632" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5998059927350.9350144707340.7449786134210.824701907252" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.262662050460.07488686776940.4898557452130.185885713659" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2191333999830.5389416637580.9419307755340.248587212873" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2622029192480.9387766495410.1181870908190.184228807206" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7486973273360.04586964847670.8749113710560.316419208729" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8766924657510.3336011084620.7817603241040.987822084345" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5304441672370.2431952088650.9129754865270.948767824152" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7793794106970.3678918217260.4221943680960.652084718397" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6131326775220.1494357016320.5044207820170.738753441318" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2616627559720.5514502203960.4941578737580.430784860136" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6426476840730.0390938348270.1794074426610.0970169104689" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9739684634580.7713506349480.7705545803480.145631532846" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4729589770870.3243493326480.3211219808160.462635240591" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6046930649770.02537520884750.09616828700570.950114160525" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4230687378410.8727455986040.9897958872820.722095589704" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8849578311260.322148070680.4452306325560.084630291152" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8497997292310.2837795098840.2209665701920.452871433018" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.06128671831490.3660246387960.5617869532210.571386005886" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.58012427060.9886931550140.4775231575160.187874355482" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9127414117770.5920281245750.8778542884320.181881749661" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2098516982680.411110141690.175273754430.96689030195" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3198138416230.6878706078520.4495020052590.719002941278" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.446450962480.02908224481010.195549012530.50258561213" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3458184193080.9252092269210.0689060440070.799951679192" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.001945331332820.6334957117660.1442382838510.571298949293" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7594049008330.1599320246830.2121275066880.858995597317" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9725482584990.6303286748250.3408267800150.321621642506" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1744924164370.01854154333210.1898497993660.349774336746" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4357726546890.757366958580.598429507360.564409176659" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6348033791450.8199814422250.6089586410360.576088294046" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9901675842270.594190356530.9412141943890.662853247646" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3978685556670.6259156832910.0244701270330.386290627759" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5309394643280.4464935420090.08816207469980.476930786303" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7211713971370.5203243939710.996613702990.279004000496" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7171512461150.04534302595060.7242673206680.606098696267" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8935298920260.5676245158960.9852540788250.868231584272" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3814707686110.06063101352460.6078984533540.255603168209" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8825624547610.8927083741330.7577003671280.871214229118" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8105941990610.5506660797730.6324692004020.0511483390529" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5449350067160.0562283272390.8549443749290.304278454445" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6155071139240.9549601861030.2953327893390.822019551326" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4386718421580.02225801270210.8449628587950.499105409795" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.01584725817350.2776344924510.5169728415870.274936931378" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5602666650830.3772578248630.9237225721240.112622355439" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.276707788140.3245764854260.1793084514310.503909548205" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3234690621760.3685998436650.4923268239730.767389295442" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.491744611750.559652611730.3503757608440.848275723583" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6712536116940.175394335650.7004517644630.800618367828" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8685859129210.9500422201060.7073349261770.679298827312" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4047371681890.3530723181180.659193643340.420341369041" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3063786713990.7422798483750.5655044034230.197992075101" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.649243240830.7253531837480.1239848658550.0763850914741" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8972933046440.9288508634090.7327125749880.0982241344079" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1529396098190.5960003913890.1474792325210.745400803404" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8558064024870.6831878425110.10514800450.506811120463" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1270619064030.3306125917740.007173618105710.208078781423" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6192673886720.9020055917880.8604228570370.443668081019" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4876558124970.3152202330310.150762456240.0608075994016" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9562059500550.5092723545360.3494187345240.550260083321" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1897704271570.7589989809770.01006642422070.382983630206" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.02893087075270.115092114560.5913285790530.376665421461" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8209058037850.06854525544530.7311689043660.517403808013" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4678607103680.7960022292610.7496197231790.997711202059" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5156235068930.8643361516020.864899812110.120344359786" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1419659736530.2199309345180.5189683199740.36464284258" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5332457552650.7121470677290.06677687225680.499746885816" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.379894058210.3023673733540.7425475897870.136693175062" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5584848461110.5272152940090.501885919260.577953082412" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8313481129280.9352720706710.6962481478350.996264401887" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2983726755380.01508420149640.04296282440320.410220260758" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.654273914890.751599313560.2725103196140.368900413725" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7039378825860.1775259688170.2925636722130.842585399438" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4922596198270.5339782240760.8324312255250.268044995099" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1028137071770.2170473809510.9679449320740.913800594366" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3253885181660.373219168310.001153580393370.998348418456" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7572854719710.08959031410350.2679324717490.36760692827" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5220333383890.2887694092430.5552556673380.830842812785" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.509554616820.3154325818420.09282691800320.343380507135" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2847626439360.4184904363480.2580703405170.878438593147" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.0603202683220.3920802902290.09764521928720.922993400515" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3322328809450.4505968795370.5088011464130.410052119961" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2582652418870.6838756211640.7158359223550.769567520119" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7452610612340.04338231059150.4512754078480.342818486492" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7655887272610.9110620787430.5449283581170.373106891551" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.01137231276750.6316194178580.536684911540.0296975059276" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3837669210570.8909217482460.1150209802530.131934440468" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2796818276930.3142739948210.683645587940.423277940896" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.07104644221530.724881880730.992407391070.932747954889" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5211120446320.6908038594690.333203450060.351858387388" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3323446600940.1198311432710.3968452075120.801044507623" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6010020929820.174544613510.0004106333544730.284288897789" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.02580070028050.3901681266490.5054600728580.29696109035" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6450329315920.09079487832110.5036052244590.844951341776" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2629442312810.2417205519490.06586826852240.536778072295" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7155129239460.8925174754150.7586739399610.0257222088263" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.864829214220.9338697237670.835222421140.585263619395" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3941909547080.0238278370090.6249039916860.184421084526" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7058447251180.07215643110320.6869174833550.86304272038" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2217136515890.4669599499190.5135978956350.116856214648" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6232793650430.04070932539080.6098038205320.378590196839" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9048732392760.4732363635050.2737455400820.989972226451" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9646351211860.08552818613380.5655653210760.341794903624" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.000582777501730.1127964003250.8640926883760.703438279908" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2777577242410.2299580543180.5270034308720.0705541773906" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5454443199890.5050456654870.09718368367850.915410528285" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9260208153620.6376086950270.2358493968240.524440423302" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3280293293890.8281341882470.6355592676690.0831177100162" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1575788512420.6579644254790.6758380779420.473976187161" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6734438086950.4721393804490.6811453113050.786369533525" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6725169483590.1540977324160.568039304480.86321409462" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4739362669470.6748102274490.241273663640.108462121196" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.490799704230.1311105497490.6096653529690.334622144731" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4956198409080.9477115498790.2215655338420.188696379801" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1329575823280.9081370759250.3312659851110.145848723384" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.975301967410.4928951435770.9491053935970.504434311349" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1539385952360.4294475676310.8878462129750.72108222001" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1006962497110.05093445710050.5183370868820.345980023955" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8710164757710.1349824295850.9817588420980.16446297143" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2141836109880.0630356154970.7003173328040.999890100391" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7271692115660.8624205607840.2143509950640.10126023584" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5978150257410.356121757820.9212249539450.603866323709" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.07046779030630.2738005444280.9985579072180.313739539529" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1101006746560.06223326638860.8487330369220.0441483647282" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8191866546840.5401150541090.4990726151530.758962002045" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6557269802860.9099235705740.2852260041090.293125074435" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5522381161740.2597333562460.04822593049780.6474502702" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.571653215530.02325575111850.7866613298280.096711263124" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8864851501730.7164193900160.2715167157630.539918801028" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3198515158590.8768301405720.03545434217740.547075952425" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3826021876520.1679636634520.5493504667380.201691335192" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.424812877530.7208583037080.2732310003950.503897534923" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4569948859740.2579660587510.483795245070.480618199427" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.579939849820.06908334727020.4278732608420.107698788" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6440377750120.1086147650860.2485463250830.881237892611" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4021021813560.4174997670780.5457826789460.0487577528233" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.06324874703640.9830160763840.9672905924670.133789775424" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6608315085810.6529418293050.4054731401890.765587628384" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.851724579160.1955609501570.2997903468250.625478826219" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9223695106980.0334350915490.9615422796760.149107584021" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2435872246720.5497514298090.4318786872640.655516192979" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.0817346604260.6451289701650.4751988525010.917965532081" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.06405110034860.3466591361130.3497966982390.387531920648" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4959748695310.5861819849160.1895413485530.464165384476" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8265600366760.1823060209620.1086310473720.748762006628" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1141248077290.5410171895920.4747299837140.015725733256" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9063342259190.06047032601610.1886238316630.584936290797" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.006893184193950.3595929453570.8315235465230.515605696028" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7234885005030.9998691732390.5512356851320.93674425207" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3265002557440.626116979730.9314092530610.0190553021703" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4573389477350.8784164658580.4301591391460.704694789719" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7088736915190.4719830019140.1837626560670.661379856004" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1694197373970.8334397677010.6305854853740.0312356368933" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2064901538370.09866051942430.209645866010.300884445311" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2778545552160.4552804335140.2910538924030.390472191686" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9869458986070.5035046269070.3715415948910.0295077511885" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.976303754890.3276321114990.9197263795460.530513640881" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5352357478360.06580311243990.4359481473640.805887314046" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6804290285870.7262210837040.1335388110880.12568659413" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1336127855440.9628548121830.04515362270720.142449064858" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.901497522530.4599133678750.8061400016340.0229784165954" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.02921321882970.0195267731990.1837447024140.0561873538905" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1499355273510.1761597486250.647795526080.0635246044561" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.08518772673710.4788266648310.244812624210.755418716305" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.06155959761330.3630417636550.5081447548590.982376432739" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7929482066370.5849939006480.2558385291330.455880639802" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1341752011510.06688200906390.6106588384490.353381978508" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7506674862840.4593088516780.4570790012930.332399837213" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2694803807750.2198357061630.4507957202110.868892949106" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1417963174780.1624238815830.2871458254920.844244258977" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.03189355184210.3358747955810.9592305713420.0241473304796" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6338848923640.08382323058480.1066566324190.975382628461" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9883084308770.238139961080.868569055840.43848487125" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9321187684830.6904504925530.5263292188950.0471514434483" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5376079836360.002450412784580.1265957037580.958611515514" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9148713859060.1320546800740.2203961297330.410493025879" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.401915321950.5408245358510.7346688647710.255872753052" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5046870435990.5731067172690.5841777854710.103126967575" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5206632959850.4111571915050.8169181200390.0717314545466" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.786141464470.679799202510.907106538530.671396018898" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.05532125309760.03503267940070.299777639710.509432515641" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2629186640570.8743573056190.8969817702770.734583094835" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.09609945309390.3088896447750.2019855868740.243375003629" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7479716339070.03798388069360.20186017680.795395017977" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5813603755530.6911242583260.6086166015340.324239248575" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2570346358840.6528604786660.772775857090.286570220991" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8465152299530.577261819990.01762987750440.556141188691" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4342512992170.651702157390.4369564248130.917025900179" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1755570367370.8153742115540.8680211356370.971793160111" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5581653010950.4723732817370.1120543698010.432119795228" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7848710196660.8865269979660.08550074781590.468877670283" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5241308739420.5147056298460.2822046467570.0957847773885" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7487876378540.9699252270350.5494463040740.254675440466" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5085580800060.7206529950050.3461083678430.286422756133" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.05902169359550.003519447261580.5892325570510.736262314794" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3214751091570.1925876615890.141193728470.213589056929" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1604583637140.8345175660410.2586170417530.771970286557" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.214097315230.8743924484340.2378804673070.891616794489" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6246856667430.1323131721410.02784446906370.414301223014" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6422978611810.684397020180.8803638242730.880871947286" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5125295638150.9406916233850.06436220564740.534064474917" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2420515490730.8891935758640.2962587412740.906130510686" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2627161399840.8830323018140.501770012510.0440544452776" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8353663686690.4785156512480.1406260595750.214644333443" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2116641480150.2181959491950.1760993874320.529719114046" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.960810539850.6046916667460.8845323709440.596733792039" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.903816207390.5869809750770.2462206869220.717135192483" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4601905317510.5747660774210.4101443323980.869701130233" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1783198382270.8937821524850.9894865346610.650961631765" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9458481852460.4458624557120.3884317412710.665492369099" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2638647642640.7405438157380.375247224380.269797113697" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4189966704370.5943277693670.6815600934980.06216637767" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.829583105520.163796981880.6384981676040.758242760118" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3480056536220.9798475803180.5677728029180.108555634847" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2582178298350.6447090187260.9987900568890.144198208094" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8164980837630.8157209320790.3461263092710.917137295884" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.378608696220.2344238027150.5425841900020.42253238692" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7169165568440.1455305621020.3511847688970.885245214413" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6678490774450.8238125983810.7641997338280.861580102298" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6485290605460.6603901576010.4403298075850.0469428426511" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1014927635610.2148899671950.2454009918230.762335235543" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4813662328930.5565759046620.6742133450350.976284068508" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3544854292550.09265166653690.9057633472070.29825875244" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.431404050240.7170977859380.8018592418140.111099610988" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.399192331130.7714649659350.9662349237780.547035612742" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6212976783910.8038231150490.2982056908780.306268577948" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6791864030040.7231675899620.2845778997290.719829247958" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3426574974860.3417467198810.8689201938850.851994905941" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5267697145510.9534621933930.4327522751450.656730648358" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2426644738440.7894764913610.05901662515640.502645151968" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1854112646010.7147116330620.794715208270.78292449998" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.06922536005080.07221703066810.3082694792410.914028402013" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9126517779890.4952379764180.9881324295970.0139049470255" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1819295587490.3046430979640.6964857746770.464863505905" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4420926581590.9653947520950.569884570240.383478553888" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7225312151050.2982942284450.8583940264570.288035225238" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3686381891460.7216459642260.5449155720760.582517870317" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.388585708190.0259421156350.7914487648850.761237276209" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4930349696390.4076455209640.52233499330.174425644986" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1601015493740.2322973511130.229964935880.0382217136016" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1391316391030.2511429845340.237505507450.660177877253" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8988398646770.1817916392250.8915708242510.991614937078" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2212532628810.367440602470.3152054273290.0965650373538" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7862908519050.9269471372190.21346928090.322413868938" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4731408621630.05173729184140.7719552725980.561036719407" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5512725021170.5653591604910.8839015634560.19027129495" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2712720263640.8085911200820.0945867554530.140649639118" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9644037962650.1162055024140.3338093538560.199259153133" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5411626081150.7559507226110.7577668899570.574214411049" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7805151436820.3152059934520.6616466035820.635954181865" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9820723744770.7255564547790.8208824526010.0145795000509" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9879600081280.6323502667460.4321858846590.817555311345" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9651349106390.05305858145980.455663061490.645770086483" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.09912663432330.5009297184040.8872710583680.0217283295215" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1514654659950.454732527790.5581655515920.0781067789606" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1117136722970.7736875912880.5056024638410.667707857531" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3600678360790.767730923380.6534952942880.98766963885" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5908447814630.9727493270170.496107883840.100153903755" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8862560777250.26833121920.9299877489910.391564269636" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5099455458330.09086369880230.8730689682080.185211854219" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5195778112620.4747421237990.6454377534680.68187788851" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9374405949290.7871683198850.3717949222790.268747515634" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6863925293950.6112638559560.08815898319510.126958437752" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3439112279880.5808306900640.2056935701130.931872154931" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.368813257810.9694251467390.4293963868330.686969237549" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6843335418470.6746995262560.6317551359120.296808714198" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9841243318820.3546844634720.1375804109370.113206576684" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7182316276560.378677830450.0819231615190.551725788528" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.498758135210.3958182908180.506914220140.226113453492" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3888747749530.9753916682480.54718329560.140130913949" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1387182459460.6848554411860.9937272421350.948295124109" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.782025847630.1396466985170.8803461362680.693141039163" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4471883926120.3474359338160.4573806348490.404858747813" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6446289997960.5734276764080.4923398544280.891363867185" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.07841786610740.7151556432910.1458027948180.380250886594" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6342234893140.9548084054370.03544296560490.369621655752" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1363884621150.01405405058990.3049270400430.976394375324" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2572665813440.8660495976610.6864439359670.305016105872" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.05197952639740.2404064784780.4568957927370.596980659481" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4284845459770.102366039710.03337759557620.267343537936" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.07526638621940.2555930257770.08384002863090.848697456202" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5595861848450.9097930469510.1676470982480.525426488934" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8713829619920.01557088904640.8978716220650.305175860651" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.02664564218550.0736236903050.35622964980.349688558012" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4064669915980.789988766720.102396141260.247630607868" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9992112988690.957257699490.6742949983380.846101808842" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6166769478060.8531531323270.4964536839320.101836805922" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2472684055770.8842328017110.8325369738070.815568265375" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.0165660558810.8046432509910.3134904214640.235905161664" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8404003204290.1380091760790.8406447957560.571852822279" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6030409710610.3447148136250.4080529778280.983805217286" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9912446243850.5420046239390.1130114641680.762438414703" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7743642650420.02798345104240.8164851896460.395080771646" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2683395639550.3935333957930.4796220528970.184146590827" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4626583517530.4246554785070.6602689359020.513922347726" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7221716712870.6536552565840.1983537894610.324659614729" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8716649894120.01821605525330.2172613781080.518826249773" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9240832278980.5497529656870.2604071417190.104201179518" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3038738523080.06579415310660.4450847026440.298361462199" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4766394656710.02875145077020.0933669404890.71617033695" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.08071078780710.3545492011760.2003679708060.441980729008" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3375054412060.125645318540.9493753493950.881454924211" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3809499721640.04091239208030.02093475364530.784779581705" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6428559305140.8590043069090.8138381985320.757259676142" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3986514476430.09852668933280.976981473470.817911046212" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6212610533120.361336892350.917986188160.320798560528" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7893752382750.629441581940.160158332370.599200616027" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3116939592950.750758628610.9175614291290.736428027916" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1563076449890.6968971670720.457004683710.966169036626" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7582311797210.6628727233970.9961962392480.531427764127" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.443363169190.533246507830.2816482233050.64428484931" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9420298669420.981661953360.03221729059290.103509695495" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9815550784840.1434478693240.683577715090.494556868525" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7043350172180.7558212925230.160193421350.195250526484" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6665082488730.06016491856260.5774012521910.373526333083" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4052670530.8330640272880.1593924223920.99255649312" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.05270253865850.4950610425980.4632008754330.540342435067" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.07451548562240.2238896802750.2153526843670.420183569774" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.729755507910.03929781002050.9763099091880.552649481436" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9352814411840.4573778455620.1873264216060.0361954434704" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.01921582452630.4744293904850.768410912890.728758194354" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5917978859190.8163556741430.06546841260840.348599282517" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9259470613410.2357647944240.5884828985750.118489126327" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.811977683210.5120342266720.3338231020420.0181757926193" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.01802804909290.1274435067210.9527187365040.771066501214" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.09774751163890.2429947201360.0121033806530.806841020306" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8964505667420.06442109119970.4250563821670.285847384119" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.227967503790.460539943550.7443228209240.21232772841" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9218587315710.08172361074390.766336356340.759404313481" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6295147004830.7007671649590.946385659540.814881110772" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.737695729980.5207646887350.01850093426680.619855041826" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6724418252790.09196092225940.5415686981630.840476549553" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1472961804270.9822832906440.1249469980630.804131213039" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7829502194180.2171716526850.9070988445140.798390517697" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8248052644930.1395851358930.01614063751090.0159947627842" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4343228582030.6227583618020.8976836227010.1056347535" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8570331563240.4256239737220.9196270271060.774772286668" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.976671072770.3235032793630.6730657338930.74202606227" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9084490418190.7279497613250.2461395721680.69232332308" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7244669862390.3697588733180.05546955628370.0442945837883" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4299030875570.07537605342480.2167875337790.99391025763" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4394390724510.2014884695540.8634751928310.448702042432" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3667869420370.3821716380530.8520869265960.831898819654" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8842731494620.2683436241950.329794626120.516721139388" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2201328304640.4987927739620.3951550350980.352220044" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2278698880360.2890026738740.803543058760.441094436942" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4365989920970.9796579380140.4646190427340.308508123723" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.08817145598030.6575477890230.9782465328150.0691746475428" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8318846451350.5816030206120.1916577394950.353936819644" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8114104317460.3260835555630.3361731053490.58994994812" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.412479094420.9501178519560.1717831847080.207611591513" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5906817593460.5437155071260.6911802128850.553670217868" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.07842987298010.2736841430820.9712102469890.0527801945604" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7008689811820.3306553753080.04033109270130.528573794559" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9642367181120.6608737496660.9518430644270.514904454078" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9416872615760.4881895765020.8724770016370.73731078715" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8756856707430.973973198970.7717337972240.977818079679" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1297092420960.2264725513950.1237818169970.199223954907" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.09913844105870.5505608255870.3082868114720.174705590893" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2950033813910.9004492418980.9656668689670.438680628832" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2307212261320.04402428101380.3480437805490.0104192629555" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.934060877060.6246058304790.1630294842360.393846328786" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6806524772070.1005273650480.3611630709930.179438483027" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6606246562810.9412040276260.7497843501120.00294862776623" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4411802112310.1990877507060.2198466012760.768167459176" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2525680973650.6870292799080.720517902670.590599827346" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8913551294230.06225442094730.866881272210.951860750973" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8470103876220.3492466648420.5744275123770.823424209077" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4214501737820.3472265280550.8842151756640.166399679335" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2366191018150.4115545047260.4378043572980.665912989201" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8242048908190.7576431970760.244940257720.757379125595" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3540064600020.2276750936480.5333027550010.000644547098763" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.165713736820.06917277050930.2444928735020.263887308051" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9447003894220.6911396921320.273151046750.268123149901" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7030521327330.1994103085550.1797676869860.0898297620976" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.07756671418210.4410576740410.1880485778830.652426509335" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7332938906130.571385282960.9435469118030.719291215399" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2818832932770.4741275818880.008193208219820.988767871926" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1556838982820.07664938546480.7499668759470.827318905767" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8923741729380.06396115215240.4184919989960.598769238029" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.06878660106930.626301971780.2013925968230.131835877293" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6265609885550.3603816135140.8357370109750.481702266495" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7700556852370.06232955926610.7271177166340.660446668322" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1561683474650.6889991918540.7411915469720.020605582525" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3652404425270.01020302331870.4848972236560.743666510246" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5963983326080.389170114880.08983118153540.47916853558" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2594146495390.008547483312120.8274997989530.414512275325" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3038836021050.5475923938510.9122289591720.0700788519974" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7164583490550.07700887250510.4422674410920.956977285936" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8910292121870.1380784655020.2852290483980.910287626399" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.417414285050.5207539040990.2234222578980.281474152119" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2827390927540.3149753053570.06756433910950.231791237134" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8461555821330.2573832706650.5892712835960.528251561028" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.281016729950.6828044693840.4739329019620.331806868955" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8122593760480.1789943505010.4615778293110.398139374932" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1278942623170.03774277852230.4431448500880.127719246724" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.07600412816390.7776052737040.5838571476880.856518929043" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5577614289950.8761485311610.1609649219620.384978321797" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1034796432980.9942449964990.5592714153480.533526950126" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.257864888860.7120470856440.5036862528730.348989552021" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9238505725180.1600377168070.1525970598720.302303253886" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2039053225460.9078022163240.6914928378020.223464574176" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2083601941880.0146407072050.3754306158820.436362049182" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4377598307620.7475138534580.7937821499470.162322157154" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4429285498550.3384370607280.6141004525360.049721385042" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.02123197763740.126441631970.1847389598940.274831804752" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.414065949750.1811118180060.8923647051760.275774801468" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.153848660520.3542117134010.06905535557660.113585570247" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2188226246420.5476034602750.2294182483770.975366820091" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.09561223827330.09539442874760.8720546137350.805431766366" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9142488552140.5453346532520.01228261638960.215121330097" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7032000396920.0476901464490.4604779280760.460195465014" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.856424811010.948262676790.6063468461430.617232910336" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2178178753520.8976343162470.1662905404250.0538983863189" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.03981389832250.3658057929750.1455497949750.716814388384" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4077557557450.3502220447810.2651614444750.385941377634" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.09053253485670.88080956240.5888182924240.822734648895" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1708259528570.6758877331190.156596734740.102549593664" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9579632813810.3971234283290.176323681640.992723514678" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1135050711040.2178563790680.03084163492770.246801301784" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.314519844420.9357828136910.1547220158610.528435471992" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6879071170220.4202711696980.9945526199280.013538910926" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.01010920195560.4671245245470.163907357670.612979332982" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9963097097780.8184790140130.02403716911480.434845291982" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4110819836060.8823444482670.4026363854130.580541922005" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.225880993330.7290194436180.3324297817460.3014148351" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5035640898560.399375384740.7310628892560.347909892642" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3991262304680.4300059969610.2047653654560.553831596676" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5715125407240.4006464108410.5537667270410.128694613399" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5165481661010.5576621646050.4978762253460.118260778645" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1048254658360.9892876146680.482257052290.466036371858" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3874026004250.1351410595540.6077512434210.398810112467" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2304978263770.08937212432520.4095355097870.35002966721" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.02465789825040.6585570823320.6155274739730.0731394704649" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.158053748640.5892023353840.4904872714310.713908350259" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8042959528390.9988477051630.5027850182730.643204364117" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8013578905630.9803226127410.8741094654290.0742098620413" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5407663282040.3032952500930.5071978983230.280072278378" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5609465080430.2020240269420.9008085733980.188512861349" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.06286587214910.3050680378370.04645898021870.649361813242" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3028616183550.0670024235720.966445880270.488773337889" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.008925316549810.05328322974660.2209427233390.157381010773" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7612663171520.3490294597040.5343176181040.103609908959" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.128356217790.6946166454280.337609419880.301533908113" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4128096425890.3421285544830.5414712892130.148091496951" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8650016076560.733250668740.2632498685610.66258708305" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2592517095730.8384022213550.7651257427410.307383232457" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4502387191910.7010892231080.4777267170610.453858606468" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3755293162120.7050225270910.2494644143850.633907604697" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2676772320740.4326234792190.5038283897350.942113533253" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5773578994450.08493671509150.945263403120.352436287767" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7515234881190.469915531010.6148837312380.889098164386" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1009801346530.7561291170150.5872290954190.788987671451" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.02304264607110.04765647937590.3119559521520.959534348149" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8411521903210.08178343312780.8801967022150.130907552055" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4680172642890.926745940530.1262022694910.593614557924" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6159403941830.9386263246280.4705904094040.493705502495" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5734454605140.6751634080240.3280762365650.357223360436" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7770243793910.7622359419850.04111433110330.258463605618" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1482722723070.7908828863760.6082358121520.578132270749" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7877214976890.7776211757550.3220040621770.393315480662" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.01115779560040.03173178611930.5225448599690.319447829827" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5579789298470.2249681115730.4860088789330.387917780148" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2671278772290.3159478902470.4010791646030.105705864503" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1641559457930.7743515822730.5279337237660.401626211964" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1916469124150.3998545592510.2352187639970.112263360339" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5546792087280.1621111354140.3885600751990.239556823388" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.77680480340.359185627970.5355110895690.382534277019" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5750335618740.547585439150.2192767545270.520411568466" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6551258407580.1063221747330.188662898640.616411526733" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5192918832780.07685526308550.06654281805590.533583714245" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3242735607920.5868846689110.8963120027010.915258158689" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4496587416170.01538683089820.9530109266510.91871880609" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2834632013720.8458500509350.6714868480160.187086292261" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.007147343859220.3099517201210.5836887143980.945052327627" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4576706460580.2705360029780.4945991334860.634248170554" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5145416467510.7647253787290.3976110483250.827996526664" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3229662392550.3648731259380.1911383151430.226791800773" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5748784602290.482778680050.07962886819830.171108775178" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.817501773270.5738202109920.4972260839230.996863820894" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3218453128880.8189214903870.1387905756030.0562844793847" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1174567106440.3903677441070.469236094750.236377262482" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5068292643020.7632961646310.7435894998620.0922245897414" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6308030495260.3780943672260.990415508820.657236874283" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3684016775310.8167148925730.8046761457970.0271112431391" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5653318927850.03164607079680.09887159959410.430407594982" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4090415223040.3793174937080.5970495360330.977025295827" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8212299614810.7049128278640.834284055160.910365374872" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6432104348860.006487717585610.3007539677590.0608653884093" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4714048675340.8041858902610.3148394254690.0634034074584" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6680618149690.288079438850.01692978724220.00340761753006" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.990663669840.5285509762560.08709962838260.56606509556" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8405488729040.2292353255380.6222207202910.703199100915" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9099166585730.5752289878440.1142113752590.518170578767" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2876781586120.6925099321060.1075796825090.219778836073" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6572508431070.02613147566140.2716669369880.549050789438" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2553852902520.6453820457160.883585845060.527855977774" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3800296587110.9678145913410.4454505407710.78399036941" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6662883037860.4783296397620.3675952656250.653144981051" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2142014742320.1178351452770.03039280635540.9999614388" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3914048707610.6547092987180.3853878976370.0233818201748" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3750253037760.9308661691580.1779706927630.353330527431" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6902721785830.9513481556360.5720123566430.700026015026" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5156010567250.5750503005890.4730828255340.335777084999" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7636571305160.7644269993630.7391705504270.812594740442" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7107575384030.2011587112250.5796691245760.804632243426" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6798172588510.1964724016310.906860196120.233178213534" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2314660936410.8469669654460.00205140538730.265471286541" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5543596779990.178380205340.9502991806390.480722577444" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2509969045320.1910760673630.7921046403430.135107147033" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6887934244530.3796905512030.4394276699330.227088185015" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7164639819670.06582114088020.6727681509540.231553256864" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.008605460741560.09310265504870.6119949894440.28177220467" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.007240927752870.3416635946090.2600601384390.123438001545" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1622616635090.03563315499360.07509555730830.773437074752" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3795979096130.1253937922650.1695556754720.560421220795" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5331965513270.6360236034850.48087300760.945106058022" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8120661637780.3989493856870.5613302801390.370713223388" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5085990129840.4096449904620.4148880602450.251813806959" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2179949940770.2912905429950.6036488402980.986513229699" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8708052688490.1796862724990.3009851678050.393715360419" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2198873071620.1496390326640.8544782409460.223016877086" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1229401957040.9031286394990.2024272798620.287779294637" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1696604166570.9897057349330.671225876520.433874214456" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4175626937040.9845264151380.4959110207690.00571521759209" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3560398760770.1588418355490.4283845631150.826623892424" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3535044255940.05980786927670.7962035820910.836681634362" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5393846552850.2026331249880.671867821420.299882858227" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2198922974070.9247495399580.2950127796810.848759674196" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3057666519130.1486405114480.4749640593860.640547569574" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1780019585280.8327800392890.6323205600430.511032542288" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7386829074970.3398668151580.76322558540.160057760925" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2834937405520.6296788903620.8272862065670.0656240205911" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4774978997390.5397231727340.09501716518220.225961718533" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1471549204610.2393329178060.4167799614390.715784322567" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1792910616450.455585205970.5310480460080.0916401160885" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4872194255550.4155842627820.3547757684850.668737984269" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4114448278320.2413990281790.04833250729540.497880644218" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7462513922170.6516701342120.963764114520.144126919059" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2578092487130.1849492536470.0657550157760.951117564034" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3717933787670.1736252080590.4450314834620.889903825418" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.542651087340.4896803784150.0543566179190.592430923324" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.08329092158610.5107371583390.403368049840.180753417842" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3992022719730.605121904850.2850831381640.178175546381" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1651906646150.09960002450320.8529625174640.290725127063" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2660733865780.8636609540220.02377594237490.88385212222" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1867797355770.5743244107660.6856941738030.00182183198751" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2036003217040.2330933555140.0615995648720.107139525788" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7374684000010.8525232952380.68834511990.292819970443" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7918404776820.7801849383550.3630584686520.0778560020586" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.03443236165140.14984528650.5482223981240.902618097765" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7276125797930.03030026762820.007194087053850.772346343258" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5863610308880.8141591507910.4979317275890.492044064803" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.772879980630.9777447573960.9776681949520.938848093556" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5803322338310.4265741483510.3023728816650.000514860073733" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9683702309780.5085268048050.3793410459130.0412803113861" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3083054823040.7812536309420.8715754900910.624807597443" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4915292795190.05094549000850.4002661138990.602597176213" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8463684653970.9202949214670.2021071859510.421299102193" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.01992133538890.9050336314830.8340180611290.768270954463" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1680514510130.4913822824470.4319044910060.0313365761427" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1499878105170.6533560029580.6394153037570.126437370271" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1719040638130.6951778612690.5387669792580.901911436309" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.03656085122360.0638374857610.01509514613280.730891223702" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2777359435880.8745812969020.4282527375250.19152515516" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4597155870090.1982085165660.5310510062120.877647022773" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9108522523250.3520946196260.4472230870160.451681770559" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.02744912269130.6497641354210.5738428844180.734803812505" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4844142308980.004285398338750.2490841497920.314920035349" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.08014812466290.9555782439890.9468450080610.422579289993" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.03392822488030.4540661132970.9928916067320.279595580141" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.07420572241670.01109057660010.7092907550760.485297379297" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4085372839520.5818276183260.03415235070740.368029131994" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4345035743320.2253794852220.9987710761490.0781725545264" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8304322490720.3321434202680.6431664796420.0234472399798" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8069017433250.2601881905010.7738322971170.503309534767" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6729220471650.5615458718690.8415079743580.279513054155" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6018083689170.2613942599270.4265083990820.0277802112031" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9778253073890.8503536814960.6723266685770.876803338464" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9977934012290.1149456503430.5964002440770.26230414168" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3165607814210.2597286557440.6550968842270.0112670498486" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8976198781780.1239221411440.6562642547520.02319973325" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2717131180870.839685538870.0508614063270.688788949805" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.356916064810.2003939681730.9691268580940.509796944865" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1233676455750.6825721673390.992607454360.95776278849" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8061994682710.7353242544290.2691688548840.425127096777" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5329283076010.2504315533940.8807362722030.523529011248" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9506455009560.5056491968310.546235128320.579089849084" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.390145307360.4746712326590.8213761460250.630498202659" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2511678894280.2736789084290.04286813991430.649286799877" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8002325094960.6522667685040.8857237812860.99010834277" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7141426646460.0049228033590.9176923127440.1732709853" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2930393716320.6192244905980.1823638005540.915068817888" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.444993882480.3582971766940.2772091355730.495793305575" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4452005624610.6985209514760.04244417959480.711759947787" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7888541327970.5347768512970.8024778885660.570694830472" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.433443941340.6248115583460.3592560968110.345232346262" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.007365135723090.894822211510.764207603540.516691716272" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9422154152640.1146883967720.253643384460.453099597692" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5072008791410.577119738580.4608412518960.977182406486" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6803783177050.1488992432810.3277103010820.639259706289" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7896361516190.3525332986460.922284825920.414153969345" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.638945818550.9378370512660.2932150060890.872138813971" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3823145784680.3618416735060.3547393366810.749299553189" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2092340053780.7269035277150.6634246730920.087340786651" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9166614592980.5845173048660.8115944190360.346290825283" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1697212828940.08977523332950.2584406603880.981742472517" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5584066769220.2163353914840.275065932280.992037437474" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3565469155060.02593584006170.2645221827170.32900772582" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.878470018090.9193189821730.1435377485980.0534371630469" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7071007695530.05152952061360.9581385684450.0516992400179" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5710138390830.4590526108550.01925307176820.49419205485" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.04761992835180.1532234878090.5654786139970.309560603761" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9784706362610.1850535827620.007982688035680.319990199591" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6104387992080.9867069422880.3445981075380.0311992356013" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8323313668610.614958670570.03731969430140.428073198264" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8846612470190.2369350024320.9854375635590.426782327838" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3349888242560.8606481886260.2469355318550.442098244858" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4511965833870.8547237339190.1647523269470.282076267627" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6746088360890.2576430278060.6753119032920.159811454819" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5661492505480.0492072071080.4736032295830.181672449295" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1886020383250.386960680080.9862618690440.0329622615055" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.04605085850280.01683263122270.452987874740.28909856348" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.0639439603620.9568931783550.2586846393090.111364010208" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6492427005640.3425854720120.6207489959440.821495015239" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6745126998490.7389554152880.8204898694070.60964751488" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1173234021130.9916854376860.7194955864510.50659576707" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1763864570390.9773317954640.7645749340530.243770934512" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5434190638260.741562489820.2746710412090.589976274005" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6386418316450.9651815509040.3159284946590.750805993643" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9644146800230.3353976524390.7258372629730.0771358638961" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9623331626390.498689290560.3320624493140.98613969657" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7719939288790.4597071620410.7804912528350.497203140353" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6899537389390.3139222234090.1561726613430.9422172501" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8180469791410.2708144757540.1514670701340.0686187012661" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8515037338220.7307619248810.9242236280140.803548978096" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.03113016381120.9923298134060.1364260859620.49055339784" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.01767521218260.06474641687710.4608127680980.77584874768" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4298025234870.7557991122720.9077142821430.88324366846" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8284068854790.9150528282870.8982024353080.568859653986" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2773717739750.3442334653470.03143210929190.28273769861" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4014803969550.7253001787460.5263941540150.461143084422" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5194672053690.6110001565110.7975669881850.914781716256" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8600639275480.7475024713350.7924165323370.708857718185" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3339711728360.3685945770470.01041141076820.977723803981" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6733854820530.3695035742290.08387902148360.155417697359" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5453928048430.8054451670410.2989236976330.43491433443" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.70756678650.9006181112610.6058406479690.519285817947" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9924807551890.907839019160.2149527043170.793877102004" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.0301157800150.4966792288820.1919565483610.112166879592" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3891643883260.1901674625570.2726858956380.497025402334" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6356317216830.5897146419130.8460046509850.30244569086" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9534129855620.3058929846880.1867870534990.925518081252" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2855517257540.05841773310440.7545153688080.876516942559" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7958965953770.7163291710260.6869726709440.026172521647" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3441037917060.8029504040210.6465163868210.223339097301" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4808504868050.9277942583390.9674379423910.156680289199" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2931177986530.8705428704190.5148984149930.719163541754" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3564202377050.5764449501690.9699109116270.704355673485" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.129259477310.4935648433980.7143616031330.174056228014" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1606562460870.2014024886810.5550264207420.653983291537" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7691901306830.7055968549590.9020478548010.00295313630346" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5343286305230.4127271963440.1834510532380.112949852856" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6917532780930.4150827974270.8579359385260.568127336711" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.673237282840.3959162527910.3084880030120.637522964574" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.04718096007490.2207822902280.2414553110360.912357031869" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2862411703140.9435554544810.2213152104280.578828632667" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6778149639770.3523851750210.8060997555530.55618315553" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1627480537180.6152661611380.01918468139250.645600382881" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1513059396990.2017683081180.7853130091810.655034596783" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.964442001450.936163322910.07718097526720.884514209166" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3934449282150.9925304845330.0901872457370.55930831222" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.02905699403350.2249669036570.07157650073850.270357134607" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1401691354080.08533619450090.1526746732170.886827412127" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.435334146530.7420187721410.8617471960340.190227471526" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4795976838140.8799122086980.3122430890880.851842476055" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6240480840720.04935966107810.01746366731580.946717726289" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9574584471110.5964651599060.6460876159770.182579080162" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8906595486120.04825468609590.6642021649810.217659742279" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3066944171470.5793773128240.3137441896760.56886527871" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4848124664520.7463356365460.1691294499370.183033317555" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.07816912126660.7363471051290.2522760483540.0374610335158" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9239379785530.9262762628990.7028484734140.94322808582" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1974243106740.1534143388590.2883293933380.442147431768" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3604222422950.6035311281150.9935099395750.258796807955" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5712290104440.9762758186150.7707443162860.408273515874" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2391782217070.9170031488440.155646111210.0610056773996" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7337645265490.5438475880970.9602553134280.997143559111" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8178315054840.4551601351140.4713711251350.491062658556" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6757317293840.3487422720270.705739450440.181271351555" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3876589105730.8574393112560.4671442230550.110468430992" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5105934629930.3218761889860.9158750182930.620587464366" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6239386073110.9455468507680.3157877207060.77724585711" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7834179050880.3957682235620.3027645880820.885163048651" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.888660532690.8341888303840.0003912022667430.0318523339217" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6735316943260.2453507530730.02145022296290.340570142779" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3076248240130.9533666370810.9247010987660.567542650931" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.07513586846110.09935533261480.3163495942430.497492018598" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7465633323590.3783638620260.03657568103850.551644745298" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.08047319218090.7972634371310.9595984736940.764233841077" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6201966347030.5726147289350.9109380848480.648441735828" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4461798152850.8123050165730.06731701432260.0769816098703" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.06674766081510.0597890678980.2446097003970.785378067084" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9680696695120.1458299386820.7059102031030.554087587535" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5552069964260.4135827993960.5680857349710.132905600489" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4508659844320.1537565777730.5816536454820.958449306868" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.07124527635520.4453263898540.7241699143360.198477878597" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6230033945220.6589311881820.2216116841610.167630241531" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2409916804320.8682890716270.1260585125390.378231627974" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1098370512610.03615464592890.4294999687480.917582941168" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9965928208630.4322277286230.03547847676210.78865743983" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2616486259240.1202663176980.5928606857140.21462275864" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3020346788970.4462909843080.5688965640610.388088552716" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9938121799480.1210485001130.6752615033630.964239499114" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5423559062860.5619459053290.8375121837390.463921167924" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5060456624050.2773684667550.5585279082210.651907960673" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5126798685110.6316815254630.9881086191540.779313022781" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8819021003940.452305857990.6896985610090.600122406967" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9544877877920.5825250149160.4022803254970.640766235633" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.343499509540.3316643791910.826395369210.457044158577" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3371999797120.2016562070750.4250782876720.809623513402" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5439395474160.9358985282360.1091408676330.731476101318" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7267160383790.9812454682960.8638916604370.141061640728" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1858624405520.6166552403490.94559802090.444084652836" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4842397429460.207442151160.3308309570810.773144355613" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4282174166740.9806014157710.4395150195750.319630800352" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.0441011146150.3623412260680.5766248381850.373408470609" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2273424369030.01427031795430.813356870.430959362009" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.710441427170.9399136178790.6927803173980.673017447128" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8054340573520.7702884214920.95461685040.837432912644" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3002837376020.1930291389450.7925994893840.0325606944092" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4697427543730.4849661824950.9351161636510.455549280565" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1605528065870.934003325490.7667593697030.620111752741" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6399037859960.5327725808860.753867041510.608087904136" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5056612109190.8215330737920.2726290126120.66792947318" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9863093718630.4140799919470.2733665801510.917539554453" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.09312961927190.2966572423350.4405839373720.164528555845" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5806977061930.8124125053950.2286156170940.355522064397" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6041571185860.6998836789840.3061100283580.174894939161" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5615832633550.001787264816250.8089064381180.907585290766" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1365089664430.1702070359330.3296596410430.924667313189" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3037472925090.1627032140030.6386472774870.520537857473" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.465188868580.7324553817420.8853458738730.480879630162" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5877089641690.4704144372370.9887405231040.297361702877" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8246592858520.367189237530.9162880022830.438003610174" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4727885499310.8758392629880.4253014048380.276074920723" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2847800156160.8874483919970.824594770850.774937309242" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2864265700830.7033563619580.2245898675880.656273686335" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5500427270820.9991304620650.3536858360770.412864445564" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4679223656590.5297105937090.09656765249770.148653067604" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9246185501660.288898833420.637703961770.302939282932" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2316963827860.693037943660.6618460738640.644945951517" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6688325646030.4607414777560.7300139170.479727183817" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.04744594530730.9656093094190.1962582108930.408997961981" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4573134860010.08072917765360.3222299843420.664511016951" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3016856874950.6308249953430.7617681392540.835155765288" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9743612211180.4255319996750.1958126555780.40218047539" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3900005832720.4424854635660.7325315504260.842161834536" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2647687762650.6504867789650.7328223940220.875531628898" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9516986998960.1581575448030.6874662046340.0122305038106" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3300549747340.2062200369190.1826638476740.182998681343" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9137345782150.7033886323170.04822238338520.842058422013" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4593136300340.2175985546020.6557113324880.386089168487" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5579795658830.1186493426110.4141206582570.987102512444" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2403008226350.6208986441760.2720186306110.03222370467" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.08808851899560.2470185624180.7559647073690.874467439171" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1241670895690.8355161192630.2761316863920.551596804107" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6221099326260.01746658895050.5991311991480.0998650211365" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.607528850020.6676153988250.5502052248320.00157500365737" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1766940540610.3025297357830.6987321605350.57874284159" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1451169195760.65015244280.6944691557330.732019392934" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8032870094360.4105158964530.3273072845360.0928105598492" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3503985510110.9208600202690.1322985496910.115885327421" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7207286238620.6490087976040.4340241677690.280631196708" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.631370635530.5812475109820.745571333340.289947291721" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8810373790090.2117528746590.3887284216650.855152131909" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5612902453150.31322060370.1374553479130.272115579697" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5826918796560.8895992007620.7281925120360.991130374176" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6708849306210.05218632161230.7487335373420.631266839767" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5010587234620.00108776122230.4680906484760.105911487857" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7814473490970.1496850007860.7678503607840.117487311525" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3189002657140.7664631061430.8971626185020.573435675623" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6033513508090.8898258422170.5449216226480.985596526056" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1447322059220.1990798706570.5019970271550.26111144872" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7150045836860.8483476514980.8498015000470.301644801232" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2087393648320.8730036469950.889103300050.187581968276" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4003597916790.6286538846480.7533969187290.578601076552" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2509176596020.7793296082110.3682184322240.894634249847" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1963659522090.5629465627330.3822110309470.00901075344371" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.75362192130.4073084957050.2732331258330.23265498274" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.05282380387810.5242768807430.8706844923070.22085905489" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8004531140130.09783394367520.04992113918790.783218528041" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2105530280090.1477457230880.5205113556530.944652689574" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.09391697682340.7511543716890.1275614320680.321374427918" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1102632202510.08362245528350.6047195038910.179062212663" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3156223329440.2018021011640.5882670707370.0819998337942" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6647142147710.1119997298590.1997708716290.216542983594" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4073217655890.4764455516990.6693933881620.0598364812954" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8760825654590.8499290320330.369794201510.557505647547" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.811441615120.3428876512380.09146662491210.0720419040527" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.006038703448590.3631522868490.6899227419680.5627909573" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.675129957560.6481650308720.6110408901110.971488017965" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5871509404860.4502294522270.4553233201730.393899976801" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2681554793240.1001265187690.008283805980560.715495603572" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4305044743980.865057136290.9953314878980.142848475384" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3477187509750.4315463721290.8428911028460.319835126925" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.07813790252770.7345677263210.3273002290350.0015129877713" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4281681064990.8944006449660.6701600913640.740637099488" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1918460731640.7549842869580.3932554096190.242156041949" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5736185159460.7392182775210.5168138020960.186853016838" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6510452459430.3352862713040.822581938550.872723210277" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4332943354430.08879780829330.9291174913390.128187705283" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4506084237940.618702412180.6058672013930.27570987868" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.299891333060.09725384987070.7916238175210.930015532984" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.02629759609710.9876301752110.05716122706810.751842730015" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.795053357410.8133811610510.6790852689870.570212231942" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.09804715528160.4059923010390.9289787006120.785498565006" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.06511952852590.02155444545470.09072835739830.900432584868" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5925508756190.447056376290.4761849416940.00137519038837" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6478482712660.8420041554750.5530510693790.398260435721" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6880556172680.7703326541260.4442096601770.936697998361" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6367776461440.6941217542150.04209450354630.338884737066" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.01569870096370.0762674966380.2586246625520.236206408014" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.04489566968710.1151549480070.5602744587970.352876747913" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8095036684590.4349315992480.1549528789060.43441150502" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.03637045060110.2006303764230.5868362839990.954613149155" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9489196232350.5359284621260.7179311664180.106023308163" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2426974516920.9828984548830.2717452456430.397381676579" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8722604758530.04741720236030.2326110436170.834351466269" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1303124507960.4043996278020.9542580044310.296766096972" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4140032227140.4655146434410.748069338870.374432455628" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.09984238740320.02655379374640.4286828626060.403477311589" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9504282570990.8515973448050.1454744793740.941804194888" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.718640582880.3518746242660.8160816094920.405628004634" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2301097062030.2484962043670.6831571456410.690132474495" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2728817353860.2457072391580.5827143140720.902718580094" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5853609418580.6611473359760.1341172777980.878243920947" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.458468081810.1242687270670.1519215817370.0478362831922" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4803468258550.1797514248260.1309472710310.0162537023175" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.479282864090.2491968554220.8769982872830.460641596744" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8403677544430.7895105354240.4313422574810.126544757153" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.03304345682030.6602798387320.8071480024410.724021098462" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9431211668820.02749844970730.726278331520.612467071189" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7399513925630.7249348554590.770763363140.176657925952" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5782718516110.2782232715390.612169442540.298646007571" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1960101271930.3361474976410.4519027529290.688705552796" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.03680834534410.3177133502860.5588554365060.646314695843" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.08700592370260.03621296385170.2644073025940.990385083479" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7197261449320.04292646291260.7188514465260.240968892999" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2548879999710.2967032172260.4692230323640.438346635305" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1628562444850.9162471534520.5140310444870.818572205195" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5419613122290.8765073844810.2806078097740.827345377933" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5398841044090.02401806437040.355541715050.0738312872059" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2920271721460.4589411541770.2414315130720.965069477169" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1123432195610.7203596151730.8248337172530.432061045326" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.08060377466040.3662864643750.1439649500170.494626911451" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7604253732080.8922602961590.1770822539750.896158364287" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6646669687620.441675178880.5307280269310.960048067854" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4655986613930.4614965814460.0161768509030.470939498203" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6838977086480.5013041464740.2243905775740.987539789643" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4790175773430.7476458694340.1072687599770.205362164792" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9119026123860.143450230440.0819850353890.997361159658" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5539442731990.6089485117980.133448909770.482766865073" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9875777046850.03007070174860.7425026488630.399752670234" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4637875162490.8923893355720.9931590263620.214259226946" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9224381201820.2337696700170.6111160198880.0144225725513" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.085033664720.4597612024460.3554672043930.474402188777" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2432161648780.2424619867090.9980948631710.631490982723" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7674856419590.9585822987050.3041119572560.102862051736" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5470180890910.6062524766390.5952945396740.38802806835" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5431688618420.2939459783780.07301241413410.734075858528" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.717524530680.5562146550220.1387028794450.436846008698" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8888986517290.2232915914440.7470883771360.402877605048" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8235652856780.7870586921110.1143819196890.425272307325" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7068442828610.5322053395550.8060854218940.394025026309" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7743765127140.9247699340560.9877005782560.768799445547" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5188808349360.07989683309020.98030753010.288742690562" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3217253559050.8566660482150.9679191133310.622624287759" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.763694559550.3175172902390.01884784262670.882190937531" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6177250772060.900527919950.8703722933690.614662277628" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3815026373860.5655459023310.5894803073240.807517665918" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2804462647060.4763936005180.1837709293640.384297887312" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5893757974380.7947301974460.1723442096580.513767837178" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9992070542640.6660631253180.3803375689880.803030589294" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4249617223330.3399666341120.5143818525310.96385534067" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1966330469450.008482197949580.1125878334690.969375508785" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5734832919960.8016056344830.1553625922610.977785948536" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.689815680780.3690009226530.6615581961670.0826043179013" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.01417804071090.9491678232290.9884930627030.129233390474" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.570847221030.9413855078150.6329041601150.994383527874" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1958912935390.4636254592020.7121375533360.31035301039" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4129845181380.2342521463190.5047067102880.877906704608" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6536446876280.135258280810.1922443895860.627821699938" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7827364847420.06292032256860.636645226780.150387100783" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6409918953540.09422752655580.9376783529590.68277402544" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3749507304880.2049543336180.3120745259630.395013235437" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4816944052180.434544206940.394976241940.918261488662" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9567897495540.03641667044380.2942616239020.938729323336" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5151279152470.8064208818040.9465941426470.728002853803" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1226969285740.04622185055240.94434619770.619064659471" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1563207918690.396897758030.2226477831240.510638513792" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3875545125950.0005409763029370.7100767147140.214009519242" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3171499174990.5496595351280.2581656098220.686385263323" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.03723238402290.4761002956640.1468265115690.379999214985" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3123972630660.4723863609660.1959731673820.817369163424" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6937155024030.4924723675040.08929533761070.675833267707" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9024309960130.1839626196250.4696566982680.0145377078799" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3425184601750.2300514166320.7121677100350.172347817127" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4625727849250.9123760050710.9543901623630.886444029526" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6458422213870.166174289080.47927398790.810886387231" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7066354418870.4556430819250.2735228205040.550294510131" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4180120526910.4172830388620.7424850489110.228152094888" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.04290907881620.3797463716310.7404889096060.459810317301" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1492865807410.5984372753960.2033901231430.129536734883" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2455644572020.7932319523150.9768303041070.412915071578" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6573026132550.3471173052110.8024665973770.265454582098" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1337622827730.8527993546360.7986333457130.241613202623" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.721479242310.5210684984530.6229642790720.131169197866" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2968915258960.7779631055620.5633984393870.768209653707" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7139378929570.4542537260720.2292923798080.435918356719" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.02859404240030.8095153682720.2630011719030.00877365279974" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6669700314430.9859977256830.8572686476710.588541982373" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9173804389850.9694490527150.2740570684780.625065645747" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4810643919960.9399502100520.8397445052650.534425470392" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.753719622950.9900970926640.7843361979170.793289605615" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3768664865750.2426332682270.6392562161610.483996631209" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7604015937640.7506430720130.2459743386220.263326544054" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6178004219410.6578748368020.6686060656680.0688968790322" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4620821378780.4498061597630.4134962312840.823913358705" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5145752490950.3040257758460.1777889867110.587107187554" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.08719467849510.154625776180.5769156040580.973516915005" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2329541067680.3054718340160.7886842676870.975617219618" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4195276058140.02098866964440.3140824560260.295158265797" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2310341220170.5590160502450.5866384844060.534707385054" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2537614372370.106809956040.9469674097440.0545755412572" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3003600253440.4597752359070.9598693284430.744595184051" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7616814868470.359101197970.8211013272030.28781062983" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.873663442540.2220663542760.121760342540.16588518166" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.02963238299570.3782437431480.5851208512920.982604308603" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9677221486070.1459395556390.05614900822980.49264484572" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7721973042430.4913535331460.08106098684490.102173034254" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4802308716420.7971211216210.5817752333110.0856082363556" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8075912765620.6938240424150.9383617255780.865000481587" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5624253534960.067997457950.1873877045050.591748259469" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6319937609650.4514022787250.5633164158790.143839173295" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2895596220170.6830941804430.7000005571280.560543331828" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9641581356580.03075222812540.8460595790120.12942604897" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7939058223960.8336380921520.5402750590180.428219598994" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7717046822720.665638536450.1296875001490.511001254867" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2001055588130.6149772038820.6750554155850.18559860899" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8196013079460.5477994809560.3869817712380.655102768826" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7293433002490.3144580711310.4193871589550.476683400285" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8894889174530.110525536690.172867635980.224261230777" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8988851276030.3294536200690.4476653209830.185554563763" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6866256564590.9250950711030.1697395731520.353850843863" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.268519761120.9630089070090.1140668414040.287076431527" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.01225112194490.2731469692770.5686097621130.285794205623" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8858815519420.1995682179640.346011502350.272158614945" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5149413519030.9340773986780.679878841180.484536931918" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.886488601250.4714836903450.3416650684120.488764821463" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4551569418760.6348648221150.142163401950.266191682566" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7043272093590.1294237153830.9875105989660.350114999984" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7476919317780.3122377649570.9120004611180.211866136099" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.246875071550.6926847967790.1287382635120.150828896897" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7326126385460.9925139204180.928722536530.678971654171" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.01605230896020.2588712155640.7265633324840.860330014122" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3662752119530.1396452188030.3153675487480.162070622695" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1267939134410.3869891095160.294726042310.793355220744" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.06144129511370.575574934840.2400361957320.551218240143" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.631375343860.3096070464990.9268831403430.783243340121" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3953875693630.687115084110.6960813067980.446303953574" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6099486915670.3035991168970.1603716179120.876082779591" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9462917905680.7959378929920.4460286828010.902974479522" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9269133404070.9623011914950.9335947668350.129031070345" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.05536008830930.4616640180730.09449183007620.503805962248" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4782970036570.3290846966010.8957840479030.962880046939" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3442304922250.5882512682780.3803356351070.214908904047" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.03522605060050.04950199063010.7262547480260.0837862611016" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5205127950220.9765773651640.6951917816350.940583442777" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2310357309270.1129602386520.06832256985940.822941951407" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7060537877910.6671289236010.8641618957390.668127858668" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2534051050250.5775680690390.5291209312110.850471621052" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.03879268739150.811031749450.19521737080.692382339673" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8233034834180.8820711356230.3700467883870.778362310062" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8567631510350.1563698741410.6943053984490.84498390733" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3957112438320.2026297342610.6936188876710.898584509045" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6750418658260.5678203024630.9378257857820.7105398352" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.420140724440.250190552920.9896638842510.203802431777" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6582649633930.01381616800410.9064700819110.390521630774" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7337644379170.4479726418230.1978203990270.835445709589" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7994543710620.4823475206640.1994639408740.336018550713" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6441671899530.1590626066870.2973941972560.721017328269" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6555354611410.7344825827860.9966161590830.329339260254" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3374673247030.1637088375450.2727216100270.306208972551" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.599673833880.2939746807690.1417462177690.984599846496" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9201161225440.9650773104020.66871440280.652613570322" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9615191859450.2720975178820.9116014367040.783364601449" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4144119576340.02541369060930.05785086374650.966157498983" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7232290478960.4497368970520.2495419587820.0743660883313" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.08183199007250.5243061874120.1837064031610.0506992174026" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5982279766270.176437939890.6096803156210.521663855778" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9548639475420.3700040617670.9513973002410.745925028163" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2715054954490.9817932525230.2360940711690.281444409161" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.920807986720.8230047779530.6472189925620.253963722884" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.604595826670.4994399381840.1146505864630.578530405093" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4018970333120.2117772557480.7999461875630.819053688168" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8315113310070.9259425135480.6231956242780.569024780682" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6713749680070.8481391791140.4943194772070.400913392691" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3036262633170.603536202580.5899695412270.57873313442" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4805491131360.5510125405740.3920382293760.0809402012614" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8170157474280.9819748147260.6807017038920.0360773531288" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8457676812740.2566568336920.02811558782380.122934832174" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5391079043960.2973435199960.50091915940.647285517557" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1759303844970.8081043522530.06515851384450.689425270124" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.560241508950.6126767243020.6366280806980.577258955353" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6387795891450.8073567354010.1251270401770.986169189615" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4787961370910.8984018123490.8578540286490.300918494113" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5242552649350.08818620302870.2795669001360.726574453906" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6336490077610.557409586980.5125778740230.8476299662" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.04949983408750.00381262783050.7445230063780.888299336518" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9388602135750.7959192150160.2267592148880.773632497218" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6320470859190.4534747142130.7593694924230.751589390175" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1181640396280.6289416750220.4878332247180.977605755089" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4979891034240.3882941080280.633884962330.750292839263" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1081063242070.08377535946970.778320140310.128515166055" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6409877533340.7521029778630.3905262008040.938568955455" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3755210161150.5155332831520.8885560981130.486550557623" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.09462266863510.5711704908770.737937224730.495426156556" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6271348978620.3742673433390.1170107793810.439453158263" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4027004424350.1426855719720.5923208356530.499951046042" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3145591426470.05298874063150.5870317091540.249300999305" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6110217719340.5528480557690.918815648620.588355080203" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5416308726090.8202712286010.1785201071760.316733229879" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1444856641460.9231861609070.7141847343260.897773724603" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3877002353820.4675804516820.4159836972870.723762205267" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9707865212440.3539304900130.9894618946360.109913431391" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9486512612060.205892879720.8311952754560.556445285508" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9775610457950.7628657250580.8526865162870.0684128539126" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6694745194330.3138565503320.6493869285510.476388312921" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2424334308950.4099665835060.8018758206210.731429135583" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.240914375940.6834233341080.9546495484550.348162692053" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3153894230960.7890028347610.1230304131130.112405753113" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9631915298070.6126005903770.3958257972780.329625413288" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5857764454250.6007811650610.4836407744830.0853539061297" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.585703952240.8356010848330.7684064210260.397780619921" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7036206000210.2433907440740.6565942195610.286437568204" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5613669034570.7788381523640.4848445253350.581893611042" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9444936034530.7957715810950.3493196568690.156035542652" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5877129991250.4324232804310.8706941618740.766037981734" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3277628841410.7114784081790.136294032160.575465636023" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8854347134730.3012123035910.9007191990860.176359651314" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.0440932207310.3933437802240.8826604495380.109412893328" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8452490551280.9956310550280.2520809422940.821681018193" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3138846130520.02677548570870.6515444462510.130755007721" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8290540416460.4302105897820.5747291516780.148805267359" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3894123750340.6736372879250.4252148040150.781721020708" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7404821545750.100839969180.8547987974080.709051586471" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7759469232710.4113163777260.6662494410550.391432891203" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.002388250828940.5358738432880.2999423322430.943517559218" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4926034012210.5661803292050.03067963514020.0471243096911" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4126193509580.5610444019950.09640601896510.416675423674" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2158626208690.6906073111680.6670549765220.746226538364" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6267124839010.5116800802520.9803102263060.896379316914" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8696649498790.7737929285510.1582947528660.333255069802" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6131462973530.9440872371590.4646511782060.297072815166" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7846270494720.5548293648690.2659372319330.0374472554123" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1140157135840.7937175454720.4071963190810.760075964743" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1927310719960.6119124679170.4663427447940.151946080853" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6310109807940.9810611352590.4771520861030.567242206555" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6095368296290.8617820186410.2178487089370.37888955981" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1857811503880.5507410719350.8853547452320.579075078131" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.04197922877560.8637764582530.3202457311010.459294964209" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9156376623590.169249596550.07007037175220.238419331984" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3742284358380.8327583435660.4248349960670.101386175596" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2217522811060.3479689928320.190492303310.69448361353" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9829586865190.5431553517830.9737917500850.969959807708" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6462421452180.7965460995250.6257421708310.38930734379" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1102800543360.54846416060.3107337087790.291335545937" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2050920481120.8980292380.3380858345780.618714192174" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3656964656080.4778390263010.8571531618160.308230408553" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4530386960660.2127451893720.6698845922450.00346060811125" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3662355801220.8315634986740.5354909191720.473548099004" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5339573940620.1576133987650.3771537433110.434941404983" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9126472604010.09944894492070.173626156410.737612082652" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9120896794330.4421183547310.02222519678480.0859200149767" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6246808612780.5330529649820.9575718337170.838182930282" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5303280635270.774628817650.8919353266440.0712321217703" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4325632582820.8221193337380.546799169360.211224943178" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.329032774470.6338984860660.3250496849890.143795087854" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3804577018720.279675685950.551522232870.575773318989" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6179852085790.9525323013720.6449159668640.27338154666" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9552559537070.4546342041170.6832273612590.843830835933" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2533264616580.3831604774360.7568301889090.706257017311" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.09453501810870.4067834484140.05742310575840.790272057067" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2893779104270.2870136928230.1491003299930.683208201078" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6920044828810.444897802350.1649532675450.530818486245" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.797738427590.3127263384820.1523182011760.244990584651" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2711268740740.2339334041840.2382474034140.788486231974" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.253021474840.5864034909810.3303430001430.237831731767" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3235399916640.4380908359630.6654038443130.400610534808" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7686376926910.3159097919660.4844894831230.424141665683" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1453823896050.5570593982860.5072283271510.625648055109" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8802619818640.3595154895280.475544351030.823690456282" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8980113459030.9831547666460.5531691860380.563718776147" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.475361769130.6157070582840.6248933554630.617058281154" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4187070524390.8930427061870.08607331606380.895409507946" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1535938372780.2115691452990.2189611303510.746035258061" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6233637712210.3785135776160.0198581275860.258679562402" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5828022310430.9963963802290.9997867008020.400226798961" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7352588530060.5408158318540.6187715610430.124222594812" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.956577703970.3872390632380.6783730498210.173104209914" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4190466603550.249875420580.5296412176810.294566422982" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6244230789690.6994413079930.5892228449560.950696619077" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9008574634060.09744915414270.250857283280.838927663504" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8995179848140.996787519320.5606296022030.8872375242" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2288355277920.7179635240580.7120140786070.324047057988" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7480934280430.05598064505630.6833206792160.200394496917" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7308799277220.5814378444140.3274337973050.210547748964" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.533332207170.402941114580.6657030230730.801757050022" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7305151848770.549018201450.1920839960990.898817772" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4878864331250.6532456691770.06363516019610.371490728291" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7409726098210.9049801012260.1771792428070.34463861136" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7725083276420.5144660434870.2131089167770.942013113657" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.02272943273270.9401429504520.2422406323150.176059681279" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6629043955350.2429392732050.0152316518050.730928114759" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3812736845020.3088172673750.2033014095150.411034759962" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5620971119850.1436904160950.4103793170380.119470621683" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7171272163520.3264246723370.9405641312720.445641173003" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5542746156510.5669666080560.2187223564580.409108437517" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9295635154750.1639538160760.3060270451050.947077709104" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.0515111972770.5820609406910.4625241129860.162372250026" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1831359068960.0356804441110.7900543310850.823514215901" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1386864934680.3757753434320.7563643547240.57114376953" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.06581250927110.6245034234830.2004402041860.83581367402" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.788792596720.2026585761790.4851018901450.124205840529" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7951497991060.6788065022330.5015982712480.493690887083" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7679689229630.8274695535070.8900980222680.53840970262" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4294152800430.8301380258670.3451979369520.408018358768" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5166031040050.9547998642390.9652413772240.273682788815" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6505905457420.2819704560760.914240054830.678894654098" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2884065448480.8146556757260.787844380230.664733218932" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9333668881740.1277459578080.9054678305720.115158008331" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4587068597490.05607867614170.06686965601910.408007091808" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8803900946310.1326971136250.1129947764740.85406589492" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.410062093880.9315356042430.2986822229260.690250525031" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3177316312880.4547505552540.2644158489050.585109353138" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3581710202740.6062032689050.2286052016720.248325078696" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9223868305360.1739231080050.9838862043510.261786706486" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7816797782650.1181191502120.385850012920.0162781113474" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.002155730540990.3145773459370.8723087404590.818142238632" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1158043607810.346769836830.4579180193120.713409296992" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7807891182890.5704411496620.3221136753440.0182023419539" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2408679404340.6839167103340.1516877501010.710422696428" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8808048152910.9887085173440.1965293835430.405054244515" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.02488502668190.4869286960020.8755566754360.698830092735" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7587717827880.9446024462940.1869595547730.214422915519" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7433705361680.6632863498830.1798447379610.609137321003" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5964518629020.003655354561680.207676652960.266062416255" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3349161364370.2970733384570.06349467191110.577129995039" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5228777165970.3012872978020.7649796502290.503641175621" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.112512186570.1181781748570.4785407083520.527218530927" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4016828874150.995732174340.368177723940.264067663749" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4628933171740.5722774622640.8738424855240.362767824752" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2906412748450.1977195663330.5207684383210.468685009835" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.06555479922950.503086257950.3995853431120.256045584363" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.07861638767750.7955427197890.7869065669830.891098173504" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5969635562230.3894713380770.6511649921130.976626683735" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4864703323890.9327171451340.3437520530540.435656207582" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7868075512560.4104218450890.507175598410.445449099552" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2163846757510.06928427162850.7428041230660.940768570628" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1724777283620.8127099689920.2912113698790.707453464944" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.893440611870.1712525097140.1962655097110.920578196273" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.943574718490.1825045207660.9589985811130.655235125845" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4548559955280.1781076841890.4739392632820.510389710337" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3163382382620.3177242634730.3122120790750.284814493231" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5113268337520.2142665984530.3714861677070.0496782739968" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.376368704010.6351968971210.5731327905850.806395665985" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9087498883710.6225891506380.8838314854730.713295869963" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.03833366739440.8251666838190.1578596002370.753960393971" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2853628227060.8276678938710.4634674113460.15426779891" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4257562001020.7518316516560.136058432760.459173801166" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2335642780630.02848072542360.4476203240360.267996975416" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5357268153140.4949120543770.3012069421010.589418262113" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6515889708220.9778015141430.6775346460010.674812324316" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1361081544690.2361771356220.09189637380480.891778243836" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.08680547211030.09357991603590.9987191615090.372659346717" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1447105485630.2638913531160.9587964856970.210204834016" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4962235918180.6969247838850.8979799167670.510350840949" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5312212178940.9586785962320.1961047698420.810776001373" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4396419927750.7038950445080.4435591468150.44896656276" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7852297288650.2239763783140.7107781540310.751590238635" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3549958432570.4581679229150.2251205839210.107988468247" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7539001431560.4316921550040.3345666903340.863612108208" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.509146076220.4699132091490.8950650789940.429652969918" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7108398612470.3010082314410.1404928449430.176996388895" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7259320096140.4978912262270.7030807602970.447692789487" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9565557449770.4263465173180.5858163439610.992776873343" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3990359464030.6574968256110.9636697691510.17558368087" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2027641208350.8667666534640.3947968134860.234567023156" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.01613529444520.387207147350.05076103436640.272159990102" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3417966629710.06845886029950.5096467075270.385441890814" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.04118455384120.3911914670350.4846411106530.654250780571" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4290123208560.3477951230140.3926403923640.606540924571" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5110265695660.8194500700630.7708555221060.790623097142" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1389564685750.6252260146960.4600384041870.758947129872" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7262628954570.9996884101690.3585177099080.00278587514525" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1321949662860.8630683745080.9277131739990.758709954764" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4631029232790.6295943464090.8036083206130.0140940098816" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6322493242930.3872346448370.3301909604970.186482985293" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3221055797450.04677882933880.2327132034040.000167370132178" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6507923775410.8788209494950.7829695352440.143400800697" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.709223021280.8423203247610.04281590704270.970158419055" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1719859060650.4723264322120.1446813130570.181761308135" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8476962975050.4244810556740.8593549173710.526237483066" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7913850465350.1926754228130.5483943464930.986524983861" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4311346302390.08685315068670.8457963486890.952571491312" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6233038076490.4318915721890.3352139823580.406539579924" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.621768559750.5000816305280.412975332340.793943612889" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3004801097240.4416949077690.6401248746750.389755629071" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4476413027760.08635120799880.8869664862940.170317918212" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1285951088780.5940493187270.8145032585370.772776369723" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3436307710310.9739184403770.2822022572040.132794853568" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6076826923710.7473668332370.9271053852440.417636526196" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.01206048757990.1252619097330.6342828689740.624515349145" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.0967455773860.05439812499740.2389090925530.611126187961" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2032300730150.2558306917290.4370728315890.661536677639" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.06956263659820.8091473544430.2700862951670.558342818503" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5355046678670.09464717356210.1455509018590.208908260376" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6199041657580.9385937193680.3299784096280.347325954307" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6287694841720.4489982281120.07283588791490.131790257089" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3750967515050.9070961979060.3587941484650.931901087543" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1223473664020.07184091730710.2468493923380.286496050296" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2209696515060.3630543167360.840047560690.528896430312" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7035722240110.8100312558560.6007673869130.330512374752" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9465390925450.2924546107230.4651048319710.659438377951" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4698580138730.8908761734910.07660707788240.21366808876" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4340277326290.2260803719840.1051082228610.4920483911" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2907583071910.2672711578120.6924441680550.0497938712937" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.263882154360.237809019370.9517698797280.00901502774487" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8781510174190.4393774133330.9025453902360.488766875066" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7377461743990.9118119560830.9981121742160.836366830371" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9359932466470.5945546010430.2053485487830.364253141291" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2949969226970.685696903850.03831056571310.241035965947" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3438181704350.5197782884710.6098488548280.421292738479" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8882057845420.7472061678270.1284776906470.681880105019" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6476249171680.9389678310170.5757520080120.504248163824" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9737223899640.7468437677350.8438603849170.070526555006" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3612366879340.9213536975860.1962213127990.190238029877" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6490060754770.7270501534420.5746277110880.589174782149" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.593666365280.7613705714230.8133203538620.594387279312" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4667676809310.5199357275750.3684048420210.141075865909" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3165688979720.1282170774940.1632947984050.930799531561" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4414071715470.2781503408380.2670667705130.172673919308" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6742868272970.07365943492850.6265881851380.999260637155" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.02554539442420.4012817962250.006009742824150.297360353222" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8349293095950.001456905949680.7410635274510.711040628964" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5770045228020.4169071687210.808635076190.475720786278" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7632640677740.7767052215790.3123214747540.684289029644" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4070950076710.08262738265910.3720407298170.103516655134" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9664001586130.9063658473260.1793840516430.701530153345" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9137439522250.1536471870470.5478905130970.778317491757" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9604827994650.6082650175030.5735964054550.6847932285" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9809448942670.06974722139350.6056014114530.252157491471" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7393289023410.8855937550690.5749605139260.294692981987" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1621347674190.4821542206460.9009946088950.823992789045" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1470144366830.4499663196410.7633681581220.097804689241" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1520786710940.5399029943190.1490880185050.589654697707" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3313076203210.2223612059440.5534646898810.738310008859" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5958034392990.9803757125470.321618379330.541380310891" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7984731422310.9477191900440.2979591283390.220763791381" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7484228262670.8384983680760.05491552646980.768360021145" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8492519383920.653550836420.6210617714490.977305122787" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1538857174630.7792791365590.5798789684030.589599420257" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4286126306070.3541841996630.7469416528320.939037102974" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.417093827730.7348124841130.9973306769570.859603137958" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.08897528230330.2273621166060.6972322257880.05501159142" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7095695894890.3459712169370.0801951412740.343769159503" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4869054526930.5139868450860.3671778596860.471869532446" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1497478364760.06254795979370.2792300012850.762345303363" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1347916873690.7273382126990.8003287665770.368631654532" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.223250296270.2498866573070.3822078469820.814058280664" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2278551799970.7480339227520.9378610369750.791535955462" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1231763760740.70814779990.07219708029350.549183242005" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.367074370940.4663291983610.713844581850.199525107141" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2419925978490.6313104961590.9712143271820.516372982596" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8594209466990.2361244787820.1537344232630.833609594963" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9392669846440.133100875380.3062634935920.531020911484" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7294745931840.7242215803340.8908549046340.324153764711" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6840436106640.3576019260460.6433274951970.157910227351" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3923497706420.953419833780.04346024069910.708471661081" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.288164525930.5181553627380.4152980398820.421335978375" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.06692114653180.3290137169170.9656809687240.720785955566" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7332104943580.2141329123990.2059175992560.0699033837971" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8487170357720.7526605712250.6889741301410.297965650949" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1805434607780.8663221209520.8874693489970.572709556642" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7398980608910.7276136788630.3151545690980.192067115291" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4418989204910.9648210796470.5406741520330.643785796431" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5113996230840.04563563390050.9291444795970.193889626747" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6744938818640.1243997031480.05697588203570.500908119522" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2409351756470.4718930187350.8173807961420.368799961246" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.0904029251520.6180379868090.06609879267340.162980376876" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.06985603909250.5841156226140.05137607023140.645826731192" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1761687567370.001563411256880.7458298253940.855994024071" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3699378713530.423255749830.9349859400330.834687411163" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4231142483550.939096569780.4004076379740.126681785192" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6450812203380.6493839588270.1953628323020.909449582956" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7143641757290.5907028304010.03845710601990.88483190829" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9850181920390.2015172097650.4442915366350.124817928683" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9360839330510.9504157730120.8198632627640.417267685051" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9911383368490.709617919280.6496660137320.710330843072" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.04080609551580.4166787697910.6325964666980.358682435132" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2997874583570.9561088859020.5598663381190.0978674610556" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7864618336660.2449444820170.8702000656910.555184400452" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2071297727950.1196731263350.5666509577560.751746780681" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2286883212150.411891219410.3642513210260.349703419355" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6359660869960.09105330609680.3649688689330.158103443827" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1329916019540.7966301750720.4363599748990.375024544641" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3011606645730.8191282344890.1710335055980.776691967347" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8116052051320.2934946835180.1124869895910.682969063394" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4056142494680.6299662743790.6731765424120.0282280145268" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9794535213150.3422596437550.8258285376050.522895746193" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7043153367450.8735759826640.03719089569290.725188793254" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9644125444430.6205286901940.8684710189320.183589974583" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8902211476570.1590966433450.5471565297560.458159932796" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.07287701391480.09949948224430.6987010938240.427116459518" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4316273504530.1389811181820.6539563305860.994474932074" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7069109888950.6561945432550.561029077020.351599283658" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8980105728590.2761978001780.6623793791340.277480237447" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8099390348350.8228334225580.7684161739820.609608403445" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"9.20767368766e-050.6535286341230.7567196650190.864609128569" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1340844879690.2569436355480.1631808587250.991361827614" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2547935883320.6033589432570.3284468349050.603877492169" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2970775609380.5159269834330.303131565720.645161669069" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2995405448660.39886116060.2178704922130.790570936203" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9788996829720.025478444190.8495953081220.462157160404" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2988530606050.3919102755810.7462225339650.00520028955458" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3209246735380.2391978845340.789027476590.864053479265" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6786937596920.4055532327910.8280345488460.34172149432" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8078946611990.01714378743610.4730861373830.817018232234" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5087584391440.4034558641320.9473972941150.407132593849" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9553735911950.6260880913560.1355029007170.169936891542" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8141762378940.944760185020.1243732631190.850186279184" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8648614029090.5246509704040.7076311180710.786689323968" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8982924138340.1827956095680.543469360290.391945667068" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2987731024680.8952857164960.6068529054480.480396934014" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2073478377990.3352198826540.05125656076380.957755104181" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.652969161110.1635021093440.9274200129120.292441185182" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7530404273280.3715221028610.3132459357590.645210934223" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.265744733410.9044480869640.8212545360240.514381997392" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6031849063180.9015974370090.5178544981410.685211203111" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8322997822880.9445213353460.734681400190.121101656697" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4099167784340.3106067627530.125139050350.0901118020798" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8328622819560.4322201822770.4915857520030.101774267316" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1204355916870.4409232762860.08542357507370.329361484188" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2785382462810.716724690250.5320944618310.615472608938" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2871168810290.4880189223540.07947074007490.753690388411" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6143150422660.8833156041350.0940827983830.189451396796" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9971997290070.1477420997080.7509820445230.703469710671" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3752786169330.8170311922430.07486337012880.825344953456" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2779099117350.6347216603920.8728767629220.857547679915" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3438281820290.08629404261520.4844077190250.865869727291" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.04336476174290.933127355320.9962672251770.954935822469" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1642121397730.6631467900260.09177766167610.796089490517" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3317282099940.05076947449630.9649542455650.568908339034" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6346676615940.1046138698560.1149856228840.0124845048952" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6565556232910.5732219959150.2501752064290.708688969904" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2120880805550.2116369850550.1631827115550.0431658113467" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6226276751260.3995399350030.6033326579490.819885152338" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9846597907820.1227441261180.9625640483390.795149939507" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8276122469320.3972728683870.617617802410.359420341811" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4163413443950.4795092743740.9930799369980.702816746368" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3293152118530.968012578250.85105584590.211363108816" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8513595333830.326332704530.4060424676760.849961165666" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4479608927620.6355462458470.4362241519430.842011670458" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8101039569150.5264260999770.777908691640.82754465193" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5539957060810.6128050578310.07390728368650.163432829953" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6324095494490.3785596071390.2192607601070.550982243257" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3833316082270.2223856375850.9420017591220.164985579104" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.934124049890.2249438749380.1710491011650.347661020621" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5297033966520.04200566482850.08047760201140.333602626295" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7885868298780.873273893170.7921137756080.553733479842" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.07988966767770.02243136722890.7974672002470.678190891035" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.001093124640570.4283108099170.6553182113250.0359653500677" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8463706285130.2947882519920.7272695641490.821266011478" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1273526159070.1130665007790.9865999682160.349727406629" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.04869006047330.917542566040.5254350372940.00177995143031" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.07025391383430.7864097945740.8823211744510.998352053197" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2502991672740.8702364913460.9189265572330.953001944206" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3792562523440.2969823246540.5823166669730.497013306255" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7041254723730.8335206556860.4184001423960.310950797144" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9304716627980.3182288661170.9150269163260.622958129779" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4646023995190.9919377328550.006332915545630.3819843603" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4410991398010.6636908656660.2975957794320.168425502154" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8365319126040.8887108287660.4959978442080.773667322014" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3572988101780.7780958631490.3472675847140.609567937815" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.02580804419990.4715011613930.9559815770240.920715899051" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.604180110270.2035664594550.4135179185550.43969624156" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.957394096970.4768528162890.3240458769340.685995956188" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4560715749420.5882514154760.5126162919160.24273146736" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7632662229950.7324951985210.683130078160.920056113883" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1298923165610.02016802358990.6736332443950.0850725479215" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8821697411610.07942457226080.4419614025680.933628368665" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4628858152360.07072988188030.6768496593170.0723999548603" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5106107526540.9341881629790.209907294010.994221093483" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6219869158410.4240462827330.1260327347190.594027593182" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.367635332180.5628422456260.7621386564940.284052876933" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3631696783920.6766312216970.830035331670.952928767196" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8992510681920.7179695513890.4675206964870.147424954026" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9213133136420.6557486416450.5762816014060.951988540174" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6168939427110.1814330270610.520373353420.0700940612978" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8781030995930.1536303685780.5771584246340.74734238559" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7473400528360.419327683940.1735503156540.76705570139" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.05109298315550.5231229841630.1068099637850.273868146899" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7276406880880.03962747295850.5479518229140.796201199106" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.866300751610.1320478429780.1918718200180.804921250774" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8854085311720.1603787174250.2305728698270.364493816769" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1419688124730.4832628392930.1623441737860.995274383162" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.773941793650.7162243773470.5817250108830.0540419367319" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8129612959760.3468773567930.3294866923060.628963044446" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6598251670150.2900546691030.9231256974230.884641920399" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.365781923560.7685554250740.668209428170.387786337751" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.415371317990.09682588315360.7169572043930.787516380584" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.228990185260.2598619773630.6711098231080.00169326234586" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.396074236840.3199601029250.2436792224960.0322424363238" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6060921089190.6908075468820.1125738615040.950610427735" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1023492966420.9356578847960.5051282726120.101582927752" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1061169802090.3265749229980.363909291070.284082343484" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4073777755050.7761102407290.1705390793890.452558284933" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.707900172820.3193123127210.4035577599150.521870717855" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9459414074750.6722213162050.8277580329130.425899073001" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2414578158760.5114636684150.2521427794390.952272451413" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2165628293120.4905438988420.5409723433650.938407575967" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3584999077010.6320338264530.317958272930.466849633756" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6320891776620.009985246357410.1153650235520.1731463714" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5218398799080.2838166089840.5099917679640.475393684882" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4554721351880.8732711018660.362703234720.597895194284" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.08260796611630.5173141457050.6725812333870.315322498128" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5718355571220.6702637224780.5103667196410.972526011181" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.814624808540.6393566937550.5054701100620.367876246749" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3877619670120.3873198331770.2263600506540.0559364695368" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4977249144710.07975184553370.9465535319670.23053976391" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8487185374320.6743149231050.6222189246020.263565870277" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5041757268810.1786786910250.5197841389250.207039736717" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9714855484710.1740098572130.07333739071160.543730090559" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1643811137240.9808834928820.719828138420.691063512502" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8663944154440.5012635740680.09062262971240.111552712464" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7711661243750.9018937134680.2577242884310.248490672409" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4656592953380.5131336724340.6177360580350.691914617053" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1623575187450.7202905781430.8901188810860.02419713726" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.706124924110.6338202391210.01147102859680.441925403184" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4859535100180.9305513457860.4909762571010.570528242947" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9310513723370.398462535860.9923075300170.922968592652" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2602912590520.8635709190850.2657078366280.711174360403" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6361605671880.9247557705640.2771325307860.559790469008" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8564467257940.274664375940.9428883394660.143897207495" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.414546870310.4618792192810.748463874430.502367915113" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.07196302019590.4142641874410.4044891861250.214074629696" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1793592753670.07124190040740.09324200149890.401552831909" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5625047512320.647630098860.4177997836330.479385735388" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9309857129440.7173572671730.6647568975490.530939394739" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2085758212690.9606346124420.4265172804810.404274569076" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6035414028490.3188731962470.5817032752930.493368171214" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3593781915680.006311579208530.05787884381790.216768205858" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9563904953520.06781242934430.7344021209620.635122634899" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8727996735450.8606601992320.4562956187370.984264204479" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8766166841450.3703639668170.5077982582630.117451546065" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1343095841430.2491157962160.2541245715610.164134337968" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1827143780480.4530644329360.6449574413720.0710450861133" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.09231215429490.7905887257220.6105261524520.31003444535" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3628217293760.5838130634560.4663105275330.917894526464" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4758153432120.6553897913580.1074358589370.11553961885" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3729364948110.445159725540.7984071457820.77826418466" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2409844619310.5180884849190.815940937990.756978454321" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.06118324305190.6007921792030.817374426460.236356900984" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.543690190490.5129695989750.159613548010.19389219114" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2375561529620.2277278364080.8866023605270.0949200813133" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2885837130770.1645314854730.9520769920510.980399723444" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2077469836170.9684533942490.8873976435120.658494238755" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8966464595860.01013031018320.2897279324020.138339668539" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5589972617870.2245721295050.9349723229110.910339925348" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6234899710850.6821303116190.909960390970.686105466693" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4712328980020.09508923287710.8329405447960.645964073716" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1641908003210.1699374833930.3781213095780.0672306986249" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9881243917260.1153775672590.4864930469390.111686764713" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5881644036030.6198526635130.4097069457320.827730155911" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4672168582520.2989613849370.7910627827010.796151744029" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.889294064260.8180150990890.8324471494310.423457289591" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.02375783258010.5269547768450.7310784626850.534580591617" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4477511711430.3310258425530.9661687671130.452687451945" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2155600206710.02609984177050.58000590340.569227852949" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3287531772970.5579406995690.2873570581150.0157803316394" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7017260011120.06309197701160.9615820759910.450372278576" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7540125527520.7574930602830.5856669135160.737598737771" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2683923946960.7002384293150.9343244978470.95457442946" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3308114693130.1578940516190.5007782978390.0803395783532" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.09570403992530.1319281787350.3004795647550.525949084453" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1747322055730.5070838704860.5973448449260.093274731305" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6742622134740.01599093568210.5788968077020.623204948841" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2175897580970.8825652999760.528864002130.56943380864" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7649794484780.7138313837170.5049731125420.484554533684" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8812544623470.3839786972180.759299109930.939196863688" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8586715463480.4813913458540.8491297349280.460025359632" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8458157156170.1349976485410.8293356371550.95225871075" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5854488588820.02761263450220.5754712630560.752576981024" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.722579859510.6572276114220.7562591750160.308508499199" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.05216545671960.03201772692450.1683464039730.517702064337" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3711352028330.9912848813790.8011400676740.936010754711" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.913501361460.2889441769110.09968872269760.244733129975" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2356124667850.3646565526540.1714580624210.43058702788" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4025454483530.869885065680.03006119811260.452189955884" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.946530188490.06119378181350.5729455424880.545299770421" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7714556339090.8107166803530.3913084854210.32209745136" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2307598753030.3284490562750.8827183650250.0979194412415" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.469917182490.3614900113820.8134507664310.74558191837" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2738860639330.7410460184340.9912373462660.430492023972" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.265951091860.3389707291240.9506901838850.921053454277" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.365601022490.7683204548790.535356921650.599379471576" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9235355491950.2248885032990.8085575005290.919281815344" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4841273150780.2160200410360.9116544647060.0544133030768" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5936007286270.1492001100510.9351587509930.883788808195" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7673760196880.2923867886970.07972615202150.169597595895" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6736001445340.9475684725480.7501690717260.824565006794" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.06501661110860.5976350838840.0760987257370.286734973231" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1795429775610.1008615596920.612078145210.538635578132" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5559068557980.5107285788810.6005663985620.725289469249" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5293236444030.4122020039220.8672177678130.472046720467" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7903994671490.982776725320.6088476616370.966183374541" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1059203962920.04134799600260.6076364097120.88803787357" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4953666055730.5101336013140.5587144105960.488778326491" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1568129887540.1216518775090.9853870876120.777689920458" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2927757697020.4543306158530.8267233998240.871233217861" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2484085244390.292689017930.5414624710040.638617305531" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1269630236110.8122055975060.7106335041970.546600389007" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4709547765980.9316053940540.5949853820260.0952282555402" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9768978127720.0005474695176170.8916202439110.587766596301" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1962082935160.52943387170.5035337892460.0769990246062" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6597043452770.4089372493780.5596526311910.647582810214" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6093485339990.1688785701690.7291476811270.198537297418" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4290552732990.6021017227780.03288591023280.793261975476" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1141726401620.6726873576360.5783738067530.287906384682" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6821095216980.2319648438780.5973856898250.762492174585" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3435702830150.2312082510380.5759801601880.270012133023" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9315003282490.4336954549480.0510228344860.399381640509" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8796393098560.3296599713390.360549012480.601091328653" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.0681492211020.7941980353150.6508217399090.0692316343951" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6624696773490.1462557051170.1571420050890.189701298308" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7402999972030.2926345373110.2126525112420.926278731455" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7548811607730.3071319423540.8499978055410.536831538248" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2098981172530.1762620126360.15374510530.990936725471" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7545401332240.534881339870.5210202532680.206944652646" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8523838753260.4366123481120.4395558811110.663420738889" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.06496394720230.7894853268230.1013732815040.0500845889715" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3348783996340.7028099737680.03265191885660.474121784789" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8573885897420.0211401072250.0196583945230.971725955269" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8095427118310.4039302035240.3841317674780.487917311292" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1717667718650.4739958105910.6580579020410.417747526223" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2180047506790.6098149734430.8527932418160.34582447806" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3396651624050.2471343778420.4126295416010.00607746855941" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1771640292310.2506413595710.6257959439150.891536598462" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1786572875360.6309630414590.2178239355760.554294674626" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5196426360560.07104117992430.2553444312480.56599219554" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6357679170760.4213815012310.9041737308270.610478236835" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.04912194060490.2813862350920.008156150696380.779399646353" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5531282351680.6167449873420.9867615080670.6675260063" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9014911379820.1301828894540.01023286808490.138802594974" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8040190837750.4399496635170.6684619092360.869026555484" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2626655977370.2598159204770.2397627258890.815714961182" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.03024289568670.7193327581450.5912988867990.154569781813" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9656068159390.7705573121230.7457524931860.235385444242" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1058745822150.9171815399160.1208939500110.866246811513" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.961288588460.1804603301590.3635019772830.656948076334" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7977714384610.2267620319540.5003330533020.555871228771" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1608587934550.3257255183370.6212851666480.260563962829" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1648243216260.1532598603230.8060326257570.963427944427" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7471431888910.01197972515850.8933235118110.410931501999" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9009265416050.3638881114110.6577068085930.621206413609" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5689436686320.6207364610490.9142464442040.546613261718" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5188894219180.5809901428160.1303402720150.00492619832346" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.526805955420.05254396973710.6684298849710.747907775874" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1504538912950.793648769570.03655628494350.130325517454" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9123884683190.06798793147920.9255083865970.659514723203" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.07373552448640.6096300405360.3455621109630.10455511114" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2764158573290.1297607952790.6019034585890.911481741587" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1290316732210.8251310598370.3727958014720.547738059472" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9761591571470.4856042879140.100252023980.129250732105" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3246061620860.1498587818450.2667039983860.545441726566" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.171070093330.9480448882770.1432647303420.727029061823" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8836392778720.9894069310630.6194862544180.996525690032" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.02639134599670.001680076863470.7944865595620.936013307534" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7419772292690.3737206434090.3893428492850.876302821913" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.00272260488890.7244715748370.3755635941760.879885642388" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9476690795310.02536485601640.8861134386260.94883280843" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5128886561320.5164772112850.4407446280990.996104172603" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6375283225530.2793443699330.2892045854640.186355537545" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4187527935850.2334989377620.7756745213280.47313002112" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6618195146440.9058377884190.1556375357350.75619520308" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3248598565790.9601722060350.3984148488880.0958370088329" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8912926694090.6126681301570.9671307040470.588083949847" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9799915343950.8126980773640.1747192722820.965565975455" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4807357446540.8532309464090.1348745843060.667183793024" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3635897683370.6055687436580.1354731981720.715400877467" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.740290514150.9626432516350.4532135057050.4534462124" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3156085250160.2230894707450.4678058316880.139207809757" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.04178103682880.5841462651130.181071370520.607672205611" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1224716593310.1146413615280.918191823670.95674974357" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5379412355020.2687012437780.7041833720860.547989795244" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3278215421220.9805292145910.9998829990830.879658435374" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3534515661440.8022027320350.3699065942680.0501141378062" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2486339771080.5110337997270.9332910471710.841228707316" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6180995956180.820899688040.9007306813780.525923097813" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6359552488450.6568808568010.9798986902110.906296911164" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5206650439150.683963937040.2168594754710.0929325772145" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2851513870040.924057004670.5898592310570.0463403188393" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6348679968610.2207687426670.8579760839720.683122052187" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8658345289390.860875400440.2246242389680.00682508366432" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3024739824570.8863577149920.7708509170680.531919468154" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9492566925780.8133998488750.7284909169750.831936637173" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5952225833240.5085873157710.2474793423960.168254724142" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8252654026020.6123560045350.3158379165520.492211191327" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1780903335050.8135108725820.2065483851940.0218368023504" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7676603274250.4125360019780.2790203271580.217499113462" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3811975213950.3033455847110.3456150026260.249434608948" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2281964090310.4276966567460.2887001423960.651393198477" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6000448576230.9141640172960.9614781450780.69483228629" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.308463483940.6100810088840.2452644389350.43256913281" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1282705342840.148005256950.4035320877250.603376957576" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.193472705130.04734000962870.2992246367660.535571119313" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5110050902760.06517617349130.8033686533810.398815152386" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2007964790370.01429326542450.9277103064560.299290570884" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4246159796540.5870575609740.7566465833680.507375395113" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2865293600750.5434426908890.3041401489850.283707459012" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6124560946380.3079036795880.5559064560020.0413465009007" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3504966048260.277943130170.9737151660630.00842165668578" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8491239536860.08812671616080.5537574676970.346196964953" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6839720772360.1411417751370.4048969636330.258873413539" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4723122565260.7316308024840.7322879586570.795287454904" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5692217721020.7959425332870.7233966612260.972526262409" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.03322844194890.7264907560110.6477935145970.339373841694" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1556059479450.4268081395530.4222141502410.954238234797" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5987230104830.8475820008680.8061968827710.667212842521" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.09072966760710.6204670423540.7980626924430.741003108866" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6923261972840.52144635850.8455918708590.296979505219" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1269259919880.8629674974080.9921466378860.691733967808" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5252875943310.81700894760.1896883371210.813093710335" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4220978019770.4337213904920.6481948250690.180374180447" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4422736107650.9293881545170.7726429844730.642658697966" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2947719546520.2228511198250.02351419983510.795841203562" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8727136179880.274889806720.8878978152010.0150684782834" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2027526775370.1563823952980.5862295000810.72453236063" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2715183745690.07006859825660.4631969222360.183549615976" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5379589754260.9409973701010.6001581777980.839994116658" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4408095912510.6077342673440.6254847168520.696666765834" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.08132475760250.7256073884150.7279235380840.219404036712" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5969395096310.7225458683650.473859513730.928707023383" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6823855842290.8680022991350.2100013845060.0773278939616" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.98328002580.1841030453240.9072187688840.039052920202" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2373655143170.9015362807650.2909928424350.737463203001" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9371165313430.4107617019060.7490573064340.751543866249" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9139473818290.4436350820330.4857107635930.976922617091" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2510792599460.5493368822980.4897423794350.524061989458" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.09684564663810.4683056800980.8081316383650.436148169855" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5809711673090.7863973814960.2764776309950.402599499123" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.5966316694550.1674651028910.8793737085580.0610255124226" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1407609533320.7816343117450.09697995613570.028132300014" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9219766128710.4131729039810.8261236342450.548493752996" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1724222429240.1275827342530.07136017598150.783193302009" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8041105933680.5157133717490.3145124163450.437522676938" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9070236189570.9374817450250.1610723073010.00142856818103" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3282382475110.4246300280050.3161547622250.747729443726" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2272720196010.7533854924870.4990809225140.740489715055" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5863795893170.1659625751950.1709141762280.738515632653" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9543563595090.2901921154760.1021051885930.915946305024" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2328506194820.4481642527150.6903586531460.831827627109" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2284594579240.1667368526780.9751033725670.704715068538" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2062744145250.3251812400130.006985082960360.269701319583" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.9772869913390.163394186190.3173500089980.368080040758" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4416756579260.1316273449520.4450647278590.779049946295" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1965065886260.57030929420.3107686359270.892112823102" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6401495677890.3279471690080.9328918362770.910027562562" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.0004976766207120.2124438689260.1300041304290.211867254177" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.882677845910.8725800323140.8464630759240.266278023688" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.2968012210470.8661421196730.6684996752510.132169013059" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7733531493280.3225244472560.8718818152970.784388363294" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.003267402453290.1722749954110.7914370346480.98815472191" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1984941755480.9640991329960.6138690894950.982332502278" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.27384660950.04604293387480.9345123964780.664281404257" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2453378311910.05978255776850.4704140362020.192188124636" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6902057787270.2740624533580.8933443968180.831338100586" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.02151885454110.0672942050990.5840030210180.45592156714" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.1887166309020.499960456680.5303863478640.805899396612" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.709117566790.5283438138720.9929206197430.984603196657" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.09871877381270.4023102774960.9137127462450.631692126527" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.909594061530.5814546923410.5385047835320.397036236828" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.3095226968750.6492261799960.8505763433220.133188090529" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6890841292780.8200010352140.8474938213740.326198676514" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.3498539692060.7413044487810.6152045148610.405004891792" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6363175881360.8545489286980.9125948024870.864752794226" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5770024118950.6033130761810.1884054978810.651959860847" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5877974665370.2575817889930.1154773775040.608149425417" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1269374920050.4866756880140.2643463809940.00761486178551" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.883291796270.7898154884290.5835448619740.195644283602" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.1208088725830.7919158620680.5321429810430.232309136781" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.4028688796880.2769207571360.5950817322750.828513144827" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9622101971630.6658229489170.06512564583570.145577684983" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5604547349120.698892865050.01684532118660.875526928083" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.4451346829140.9065444729960.7328796252730.0831332972522" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6361192261590.06891029633590.1045013779850.310042492895" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7941775011440.3190006979240.5413726408260.844379546558" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.6213542541740.01572318511730.2058176074310.567306781843" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8417708304240.0735350276090.3554317048170.44040143507" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.2806525507870.3493241551020.3593596210320.345395910729" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.8778642578090.3488847481170.7418212085140.360869181461" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6018363186350.01524903989910.9587707250430.188599760023" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7664056833680.5512410235160.3974532873630.946642005209" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.9797645406660.7306377649710.7447701351540.671612102358" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.6793061599770.9170533574020.1589105875280.606089830878" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.7682275436810.01397013974260.554695602380.861012803749" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.7678609918080.7482244439010.3605971377080.540853400739" +}, +{ +"attr_id":"GENDER", +"attr_val":"MALE", +"sample":"0.8207307331970.6361634450080.612689301550.77014436595" +}, +{ +"attr_id":"GENDER", +"attr_val":"FEMALE", +"sample":"0.5860374187460.1888603096250.5223646582250.277077826618" } - ] } diff --git a/packages/oncoprintjs/test/index.html b/packages/oncoprintjs/test/index.html index b1e3df2756b..8c507d43b31 100644 --- a/packages/oncoprintjs/test/index.html +++ b/packages/oncoprintjs/test/index.html @@ -10,7 +10,6 @@ - diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index fbf2aed9858..180744fa240 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -44,9 +44,6 @@ $('#toggle_whitespace').click(function() { var gender_data; var gender_track_id; var gender_data_promise = $.getJSON('./gbm/gender-gbm.json'); -$('#move_btn').click(function() { - onc.moveTrack(gender_track_id, 4); -}) var mutation_data; var mutation_track_id; From a2b4dc18d7a5dcb154ac18f87774a3c92f25cddd Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Wed, 24 Jun 2015 19:04:30 -0400 Subject: [PATCH 077/343] first commit, adding hongxins toolbar --- packages/oncoprintjs/src/js/toolbar.html | 52 ++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 packages/oncoprintjs/src/js/toolbar.html diff --git a/packages/oncoprintjs/src/js/toolbar.html b/packages/oncoprintjs/src/js/toolbar.html new file mode 100644 index 00000000000..2e3108d368f --- /dev/null +++ b/packages/oncoprintjs/src/js/toolbar.html @@ -0,0 +1,52 @@ + + + + + + + + + + JS Bin + + +
+
+ + +
+
+ + +
+ + + + + +
+ + + +
+
+ + \ No newline at end of file From d536f3078dd779d015f040328f56f8ff1c8f128e Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Thu, 25 Jun 2015 10:45:27 -0400 Subject: [PATCH 078/343] display toolbar (bad layout still) --- packages/oncoprintjs/gulpfile.js | 3 +++ packages/oncoprintjs/src/css/oncoprint.css | 11 ++++++++--- packages/oncoprintjs/src/{js => html}/toolbar.html | 7 +++---- packages/oncoprintjs/src/js/oncoprint.js | 12 ++++++++++-- 4 files changed, 24 insertions(+), 9 deletions(-) rename packages/oncoprintjs/src/{js => html}/toolbar.html (81%) diff --git a/packages/oncoprintjs/gulpfile.js b/packages/oncoprintjs/gulpfile.js index 35e7793f6ad..1da6caf3a62 100644 --- a/packages/oncoprintjs/gulpfile.js +++ b/packages/oncoprintjs/gulpfile.js @@ -40,6 +40,9 @@ gulp.task('test', function() { gulp.src('test/index.html') .pipe(gulp.dest('dist/test/')); + gulp.src('src/html/toolbar.html') + .pipe(gulp.dest('dist/test/')); + // Copy over the data. gulp.src('test/data/**') .pipe(gulp.dest('dist/test/')); diff --git a/packages/oncoprintjs/src/css/oncoprint.css b/packages/oncoprintjs/src/css/oncoprint.css index feb3cf17063..d10fdf97934 100644 --- a/packages/oncoprintjs/src/css/oncoprint.css +++ b/packages/oncoprintjs/src/css/oncoprint.css @@ -1,7 +1,12 @@ -.oncoprint_container { - //position: relative; +.toolbar_container { + display: inline-block; + vertical-align: top; + float: right; +} +.content_container { + display: inline-block; + vertical-align: bottom; } - .fixed_oncoprint_section_container { float:left; overflow: auto; diff --git a/packages/oncoprintjs/src/js/toolbar.html b/packages/oncoprintjs/src/html/toolbar.html similarity index 81% rename from packages/oncoprintjs/src/js/toolbar.html rename to packages/oncoprintjs/src/html/toolbar.html index 2e3108d368f..74bf57e627c 100644 --- a/packages/oncoprintjs/src/js/toolbar.html +++ b/packages/oncoprintjs/src/html/toolbar.html @@ -2,10 +2,6 @@ - - - - JS Bin @@ -47,6 +43,9 @@ + + + \ No newline at end of file diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 67d66a454ec..1552c725403 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -373,6 +373,7 @@ var OncoprintSVGRenderer = (function() { function OncoprintSVGRenderer(container_selector_string, oncoprint, config) { OncoprintRenderer.call(this, oncoprint, config); var self = this; + this.toolbar_container; this.label_svg; this.label_container; this.cell_container; @@ -385,6 +386,13 @@ var OncoprintSVGRenderer = (function() { this.clip_zone_start = 0; + (function initToolbarContainer() { + self.toolbar_container = d3.select(container_selector_string).append('div').classed('toolbar_container', true); + d3.select(container_selector_string).append('br'); + $.ajax({url: "toolbar.html", context: document.body, success: function(response) { + $(self.toolbar_container.node()).html(response); + }}); + })(); (function initLabelContainer() { self.label_container = d3.select(container_selector_string).append('div').classed('fixed_oncoprint_section_container', true); self.label_svg = self.label_container.append('svg'); @@ -392,7 +400,7 @@ var OncoprintSVGRenderer = (function() { // TODO: fix this shit UP var in_track = -1; var track_tops = self.getTrackTops(); - var mouse_y = evt.clientY; + var mouse_y = evt.clientY - self.label_svg.node().offsetTop; _.find(self.oncoprint.getTrackOrder(), function(id) { if (mouse_y >= track_tops[id] && mouse_y <= track_tops[id] + self.getRenderedTrackHeight(id)) { in_track = id; @@ -738,7 +746,7 @@ var OncoprintSVGRenderer = (function() { delete track_tops_true[track_id]; var handler = function(evt) { var track_tops = $.extend({},{},track_tops_true); - var mouse_y = evt.clientY; + var mouse_y = evt.clientY - self.label_svg.node().offsetTop; var render_y = utils.minMax(mouse_y, 0, self.getLabelAreaHeight()); self.renderTrackLabel(self.oncoprint, track_id, false, self.label_svg, mouse_y).classed('dragging_label', true); From 9829ba2f99550bf0b7c61fa1a04b771cc2150841 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Thu, 25 Jun 2015 15:29:48 -0400 Subject: [PATCH 079/343] some performance and other tweaks --- packages/oncoprintjs/src/js/oncoprint.js | 48 +++++++++++++---------- packages/oncoprintjs/src/js/utils.js | 2 +- packages/oncoprintjs/test/js/test_page.js | 2 +- 3 files changed, 30 insertions(+), 22 deletions(-) diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 1552c725403..d2e18a45827 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -389,9 +389,9 @@ var OncoprintSVGRenderer = (function() { (function initToolbarContainer() { self.toolbar_container = d3.select(container_selector_string).append('div').classed('toolbar_container', true); d3.select(container_selector_string).append('br'); - $.ajax({url: "toolbar.html", context: document.body, success: function(response) { + /*$.ajax({url: "toolbar.html", context: document.body, success: function(response) { $(self.toolbar_container.node()).html(response); - }}); + }});*/ })(); (function initLabelContainer() { self.label_container = d3.select(container_selector_string).append('div').classed('fixed_oncoprint_section_container', true); @@ -414,6 +414,7 @@ var OncoprintSVGRenderer = (function() { })(); (function initCellContainer() { self.cell_container = d3.select(container_selector_string).append('div').classed('scrolling_oncoprint_section_container', true); + //self.cell_container.style('display', 'none'); self.cell_container_node = self.cell_container.node(); self.cell_div = self.cell_container.append('div').classed('cell_div', true); @@ -463,11 +464,11 @@ var OncoprintSVGRenderer = (function() { // Rule sets OncoprintSVGRenderer.prototype.setRuleSet = function(track_id, type, params) { OncoprintRenderer.prototype.setRuleSet.call(this, track_id, type, params); - this.render(); + this.render(track_id); }; OncoprintSVGRenderer.prototype.useSameRuleSet = function(target_track_id, source_track_id) { OncoprintRenderer.prototype.useSameRuleSet.call(this, target_track_id, source_track_id); - this.render(); + this.render(target_track_id); } // Containers @@ -501,12 +502,18 @@ var OncoprintSVGRenderer = (function() { OncoprintSVGRenderer.prototype.renderTrackLabel = function(oncoprint, track_id, rule_set, svg, label_y) { var label_class = 'label'+track_id; label_y = typeof label_y === "undefined" ? this.getLabelTops()[track_id] : label_y; - svg.selectAll('.'+label_class).remove(); - svg.append('text').classed(label_class, true).text(oncoprint.getTrackLabel(track_id)) - .attr('font', this.getLabelFont()) - .attr('alignment-baseline', 'hanging') - .classed('noselect', true) - .attr('transform', utils.translate(0, label_y)); + + var to_reposition; + if (rule_set) { + svg.selectAll('.'+label_class).remove(); + to_reposition = svg.append('text').classed(label_class, true).text(oncoprint.getTrackLabel(track_id)) + .attr('font', this.getLabelFont()) + .attr('alignment-baseline', 'hanging') + .classed('noselect', true) + } else { + to_reposition = svg.selectAll('.' + label_class); + } + to_reposition.attr('transform', utils.translate(0, label_y)); var altered_data_label_class = 'altered_data_label'+track_id; if (rule_set && rule_set.alteredData && typeof rule_set.alteredData === 'function') { @@ -514,17 +521,16 @@ var OncoprintSVGRenderer = (function() { var data = oncoprint.getTrackData(track_id); var num_altered = rule_set.alteredData(data).length; var percent_altered = Math.floor(100*num_altered/data.length); - svg.append('text').classed(altered_data_label_class, true) + to_reposition = svg.append('text').classed(altered_data_label_class, true) .text(percent_altered+'%') .attr('font', this.getLabelFont()) .attr('text-anchor', 'end') .attr('alignment-baseline', 'hanging') .classed('noselect', true) - .attr('transform', utils.translate(this.getLabelAreaWidth(), label_y)); } else { - svg.selectAll('.'+altered_data_label_class) - .attr('transform', utils.translate(this.getLabelAreaWidth(), label_y)); + to_reposition = svg.selectAll('.'+altered_data_label_class) } + to_reposition.attr('transform', utils.translate(this.getLabelAreaWidth(), label_y)); return svg.selectAll('.'+label_class+',.'+altered_data_label_class); }; @@ -548,8 +554,8 @@ var OncoprintSVGRenderer = (function() { bound_svg.each(function(d,i) { var dom_cell = this; var id = id_accessor(d); - var tooltip_html = tooltip(d); if (tooltip) { + var tooltip_html = tooltip(d); $(dom_cell).one("mouseover", function() { $(dom_cell).qtip({ content: { @@ -586,11 +592,9 @@ var OncoprintSVGRenderer = (function() { var id_key = oncoprint.getTrackDatumIdKey(track_id); var id_order = oncoprint.getIdOrder(); var y = this.getCellTops()[track_id]; - bound_svg.transition().style('left', function(d,i) { + bound_svg.style('left', function(d,i) { return self.getCellX(id_order.indexOf(d[id_key])); - }).style('top', y).each("end", function() { - $(self).trigger(events.FINISHED_POSITIONING); - }); + }).style('top', y); }; OncoprintSVGRenderer.prototype.positionCells = function(track_ids) { track_ids = track_ids || this.oncoprint.getTrackOrder(); @@ -675,6 +679,7 @@ var OncoprintSVGRenderer = (function() { }); } } + this.prev_clip_bounds.set(visible_bounds.first, visible_bounds.last); }; @@ -693,6 +698,7 @@ var OncoprintSVGRenderer = (function() { this.resizeLabelSVG(); this.resizeCellDiv(); + this.cell_div.style('display', 'none'); var renderTrack = function(track_id) { if (self.isTrackRenderable(track_id)) { var rule_set = self.getRuleSet(track_id); @@ -709,6 +715,7 @@ var OncoprintSVGRenderer = (function() { }); } self.clipCells(); + this.cell_div.style('display', 'inherit'); this.renderLegend(); }; OncoprintSVGRenderer.prototype.renderLegend = function() { @@ -744,10 +751,11 @@ var OncoprintSVGRenderer = (function() { var new_track_pos; var track_tops_true = this.getLabelTops(); delete track_tops_true[track_id]; + var label_area_height = self.getLabelAreaHeight(); var handler = function(evt) { var track_tops = $.extend({},{},track_tops_true); var mouse_y = evt.clientY - self.label_svg.node().offsetTop; - var render_y = utils.minMax(mouse_y, 0, self.getLabelAreaHeight()); + var render_y = utils.clamp(mouse_y, 0, label_area_height); self.renderTrackLabel(self.oncoprint, track_id, false, self.label_svg, mouse_y).classed('dragging_label', true); var first_below = 0; diff --git a/packages/oncoprintjs/src/js/utils.js b/packages/oncoprintjs/src/js/utils.js index 93af3917109..e274404c262 100644 --- a/packages/oncoprintjs/src/js/utils.js +++ b/packages/oncoprintjs/src/js/utils.js @@ -52,7 +52,7 @@ exports.makeIdCounter = function() { }; }; -exports.minMax = function(t, a, b) { +exports.clamp = function(t, a, b) { return Math.max(Math.min(b,t), a); }; diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index 180744fa240..6d6cfa9afb6 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -66,7 +66,7 @@ $.when(gender_data_promise).then(function() { legend_label: 'Gender' }); onc.setTrackData(gender_track_id, gender_data); - for (var i=0; i<0; i++) { + for (var i=0; i<8; i++) { var dup_gender_track_id = onc.addTrack({label: 'Gender'}); onc.useSameRuleSet(dup_gender_track_id, gender_track_id); onc.setTrackData(dup_gender_track_id, gender_data); From c3a44612a0c5d404dfebd815039ca603c8698c42 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Mon, 29 Jun 2015 15:21:21 -0400 Subject: [PATCH 080/343] new folder for production code --- .../src/{js => .js}/D3SVGCellRenderer.js | 0 .../src/{js => .js}/OncoprintToolbar.js | 0 .../src/{js => .js}/ReadOnlyObject.js | 0 packages/oncoprintjs/src/.js/RuleSet.js | 469 +++++++++++ .../oncoprintjs/src/{js => .js}/Toolbar.js | 0 packages/oncoprintjs/src/.js/events.js | 49 ++ .../oncoprintjs/src/{js => .js}/globals.js | 0 packages/oncoprintjs/src/{js => .js}/main.js | 0 packages/oncoprintjs/src/.js/oncoprint.js | 783 ++++++++++++++++++ .../oncoprintjs/src/{js => .js}/renderers.js | 0 .../src/{js => .js}/rendering_engine.js | 0 .../oncoprintjs/src/{js => .js}/signals.js | 0 .../oncoprintjs/src/{js => .js}/sorting.js | 0 packages/oncoprintjs/src/{js => .js}/track.js | 0 packages/oncoprintjs/src/.js/utils.js | 160 ++++ .../oncoprintjs/src/{js => .js}/utils2.js | 0 16 files changed, 1461 insertions(+) rename packages/oncoprintjs/src/{js => .js}/D3SVGCellRenderer.js (100%) rename packages/oncoprintjs/src/{js => .js}/OncoprintToolbar.js (100%) rename packages/oncoprintjs/src/{js => .js}/ReadOnlyObject.js (100%) create mode 100644 packages/oncoprintjs/src/.js/RuleSet.js rename packages/oncoprintjs/src/{js => .js}/Toolbar.js (100%) create mode 100644 packages/oncoprintjs/src/.js/events.js rename packages/oncoprintjs/src/{js => .js}/globals.js (100%) rename packages/oncoprintjs/src/{js => .js}/main.js (100%) create mode 100644 packages/oncoprintjs/src/.js/oncoprint.js rename packages/oncoprintjs/src/{js => .js}/renderers.js (100%) rename packages/oncoprintjs/src/{js => .js}/rendering_engine.js (100%) rename packages/oncoprintjs/src/{js => .js}/signals.js (100%) rename packages/oncoprintjs/src/{js => .js}/sorting.js (100%) rename packages/oncoprintjs/src/{js => .js}/track.js (100%) create mode 100644 packages/oncoprintjs/src/.js/utils.js rename packages/oncoprintjs/src/{js => .js}/utils2.js (100%) diff --git a/packages/oncoprintjs/src/js/D3SVGCellRenderer.js b/packages/oncoprintjs/src/.js/D3SVGCellRenderer.js similarity index 100% rename from packages/oncoprintjs/src/js/D3SVGCellRenderer.js rename to packages/oncoprintjs/src/.js/D3SVGCellRenderer.js diff --git a/packages/oncoprintjs/src/js/OncoprintToolbar.js b/packages/oncoprintjs/src/.js/OncoprintToolbar.js similarity index 100% rename from packages/oncoprintjs/src/js/OncoprintToolbar.js rename to packages/oncoprintjs/src/.js/OncoprintToolbar.js diff --git a/packages/oncoprintjs/src/js/ReadOnlyObject.js b/packages/oncoprintjs/src/.js/ReadOnlyObject.js similarity index 100% rename from packages/oncoprintjs/src/js/ReadOnlyObject.js rename to packages/oncoprintjs/src/.js/ReadOnlyObject.js diff --git a/packages/oncoprintjs/src/.js/RuleSet.js b/packages/oncoprintjs/src/.js/RuleSet.js new file mode 100644 index 00000000000..4e38bc03521 --- /dev/null +++ b/packages/oncoprintjs/src/.js/RuleSet.js @@ -0,0 +1,469 @@ +var _ = require('underscore'); +var utils = require('./utils'); + +var CATEGORICAL_COLOR = 0; +var GRADIENT_COLOR = 1; +var GENETIC_ALTERATION = 2; +var BAR_CHART = 3; +module.exports = { + CATEGORICAL_COLOR: CATEGORICAL_COLOR, + GRADIENT_COLOR: GRADIENT_COLOR, + GENETIC_ALTERATION: GENETIC_ALTERATION, + BAR_CHART: BAR_CHART, + makeRuleSet: function(type, params) { + if (type === CATEGORICAL_COLOR) { + return new D3SVGCategoricalColorRuleSet(params); + } else if (type === GRADIENT_COLOR) { + return new D3SVGGradientColorRuleSet(params); + } else if (type === GENETIC_ALTERATION) { + return new D3SVGGeneticAlterationRuleSet(params); + } else if (type === BAR_CHART) { + return new D3SVGBarChartRuleSet(params); + } else { + return new D3SVGRuleSet(); + } + } +}; + +var getRuleSetId = utils.makeIdCounter(); + +var D3SVGRuleSet = (function() { + function D3SVGRuleSet(params) { + this.rule_map = {}; + this.rule_set_id = getRuleSetId(); + this.legend_label = params.legend_label; + }; + var getRuleId = utils.makeIdCounter(); + + D3SVGRuleSet.prototype.getLegendLabel = function() { + return this.legend_label; + }; + D3SVGRuleSet.prototype.getRuleSetId = function() { + return this.rule_set_id; + }; + D3SVGRuleSet.prototype.addRule = function(params) { + var rule_id = getRuleId(); + this.rule_map[rule_id] = new D3SVGRule(params, rule_id); + return rule_id; + } + D3SVGRuleSet.prototype.addStaticRule = function(params) { + var rule_id = getRuleId(); + this.rule_map[rule_id] = new D3SVGStaticRule(params, rule_id); + return rule_id; + }; + D3SVGRuleSet.prototype.addGradientRule = function(params) { + var rule_id = getRuleId(); + this.rule_map[rule_id] = new D3SVGGradientRule(params, rule_id); + return rule_id; + }; + D3SVGRuleSet.prototype.addBarChartRule = function(params) { + var rule_id = getRuleId(); + this.rule_map[rule_id] = new D3SVGBarChartRule(params, rule_id); + return rule_id; + }; + D3SVGRuleSet.prototype.removeRule = function(rule_id) { + delete this.rule_map[rule_id]; + }; + D3SVGRuleSet.prototype.getRules = function() { + var self = this; + var rule_ids = Object.keys(this.rule_map); + var rules = _.map(rule_ids, function(id) { return self.rule_map[id]; }); + var sorted_rules = _.sortBy(rules, function(r) { return r.z_index; }); + return sorted_rules; + }; + D3SVGRuleSet.prototype.apply = function(g, data, datum_id_accessor, cell_width, cell_height) { + _.each(this.getRules(), function(rule) { + var affected_data = rule.filterData(data); + var affected_groups = g.data(affected_data, datum_id_accessor); + rule.apply(affected_groups, cell_width, cell_height); + }); + }; + D3SVGRuleSet.prototype.getRule = function(rule_id) { + return this.rule_map[rule_id]; + }; + return D3SVGRuleSet; +})(); + +function D3SVGCategoricalColorRuleSet(params) { + D3SVGRuleSet.call(this, params); + this.type = CATEGORICAL_COLOR; + var self = this; + var d3_colors = _.shuffle(d3.scale.category20().range()); + var addColorRule = function(color, category) { + var colored_rect = utils.makeD3SVGElement('rect').attr('fill', color); + var condition = (function(cat) { + return function(d) { + return params.getCategory(d) === cat; + }; + })(category); + self.addStaticRule({ + condition: condition, + shape: colored_rect, + legend_label: category + }); + }; + _.each(params.color, function(color, category) { + addColorRule(color, category); + }); + + self.apply = function(g, data, datum_id_accessor, cell_width, cell_height) { + var missing_categories = []; + _.each(data, function(datum) { + var category = params.getCategory(datum); + if (!params.color.hasOwnProperty(category)) { + var new_color = d3_colors.pop(); + params.color[category] = new_color; + addColorRule(new_color, category); + } + }); + D3SVGRuleSet.prototype.apply.call(this, g, data, datum_id_accessor, cell_width, cell_height); + }; + + self.putLegendGroup = function(svg, cell_width, cell_height) { + var group = svg.append('g'); + _.each(self.getRules(), function(rule) { + rule.putLegendGroup(group, cell_width, cell_height); + }) + utils.spaceSVGElementsHorizontally(group, 20); + return group; + }; +} +D3SVGCategoricalColorRuleSet.prototype = Object.create(D3SVGRuleSet.prototype); + +function D3SVGGradientColorRuleSet(params) { + D3SVGRuleSet.call(this, params); + this.type = GRADIENT_COLOR; + var rule = this.addGradientRule({ + shape: utils.makeD3SVGElement('rect'), + data_key: params.data_key, + data_range: params.data_range, + color_range: params.color_range, + scale: params.scale + }); + this.putLegendGroup = function(svg) { + return this.rule_map[rule].putLegendGroup(svg); + }; +} +D3SVGGradientColorRuleSet.prototype = Object.create(D3SVGRuleSet.prototype); + +function D3SVGBarChartRuleSet(params) { + D3SVGRuleSet.call(this, params); + var self = this; + self.type = BAR_CHART; + var rule = this.addBarChartRule({ + data_key: params.data_key, + data_range: params.data_range, + scale: params.scale, + fill: params.fill, + }); + this.putLegendGroup = function(svg, cell_width, cell_height) { + return this.rule_map[rule].putLegendGroup(svg, cell_width, cell_height); + }; +} +D3SVGBarChartRuleSet.prototype = Object.create(D3SVGRuleSet.prototype); + +function D3SVGGeneticAlterationRuleSet(params) { + D3SVGRuleSet.call(this, params); + var self = this; + self.type = GENETIC_ALTERATION; + var default_rule = this.addStaticRule({ + shape: utils.makeD3SVGElement('rect').attr('fill', params.default_color), + exclude_from_legend: true, + z_index: -1 + }); + var altered_rules = []; + _.each(params.cna.color, function(color, name) { + var new_cna_rule = self.addStaticRule({ + condition: (function(_name) { + return function(d) { + return d[params.cna_key] === _name; + }; + })(name), + shape: utils.makeD3SVGElement('rect'), + legend_label: params.cna.label[name], + attrs: { + fill: color, + width: '100%', + height: '100%' + }, + z_index: 0 + }); + altered_rules.push(new_cna_rule); + }); + _.each(params.mut.color, function(color, name) { + var new_mut_rule = self.addStaticRule({ + condition: (function(_name) { + return function(d) { + return d[params.mut_type_key] === _name; // TODO: should be indexOf for multiple mutations? + } + })(name), + shape: utils.makeD3SVGElement('rect').attr('fill', color), + legend_label: params.mut.label[name], + attrs: { + width: '100%', + height: '33.33%', + y: '33.33%' + }, + z_index: 1 + }); + altered_rules.push(new_mut_rule); + }); + // TODO: mrna, rppa, other stuff? + self.putLegendGroup = function(svg, cell_width, cell_height) { + var group = svg.append('g'); + _.each(self.getRules(), function(rule) { + rule.putLegendGroup(group, cell_width, cell_height); + }) + utils.spaceSVGElementsHorizontally(group, 20); + return group; + }; + self.alteredData = function(data) { + var altered_data = []; + _.each(altered_rules, function(rule_id) { + altered_data = altered_data.concat(self.getRule(rule_id).filterData(data)); + }); + return _.uniq(altered_data); + }; +} +D3SVGGeneticAlterationRuleSet.prototype = Object.create(D3SVGRuleSet.prototype); + +function D3SVGRule(params, rule_id) { + this.rule_id = rule_id; + this.condition = params.condition || function(d) { return true; }; + this.shape = typeof params.shape === 'undefined' ? utils.makeD3SVGElement('rect') : params.shape; + this.z_index = typeof params.z_index === 'undefined' ? this.rule_id : params.z_index; + this.legend_label = params.legend_label; + this.exclude_from_legend = params.exclude_from_legend; + + this.attrs = params.attrs || {}; + this.attrs.width = this.attrs.width || '100%'; + this.attrs.height = this.attrs.height || '100%'; + + var percentToPx = function(attr_val, attr_name, cell_width, cell_height) { + // convert a percentage to a local pixel coordinate + var width_like = ['width', 'x']; + var height_like = ['height', 'y']; + attr_val = parseFloat(attr_val, 10)/100; + if (width_like.indexOf(attr_name) > -1) { + attr_val = attr_val*cell_width; + } else if (height_like.indexOf(attr_name) > -1) { + attr_val = attr_val*cell_height; + } + return attr_val+''; + }; + + var convertAttr = function(d, i, attr_val, attr_name, cell_width, cell_height) { + var ret = attr_val; + if (typeof ret === 'function') { + ret = ret(d,i); + } + if (typeof ret === 'string' && ret.indexOf('%') > -1) { + ret = percentToPx(ret, attr_name, cell_width, cell_height); + } + return ret; + }; + + this.apply = function(g, cell_width, cell_height) { + var shape = this.shape; + var elts = utils.appendD3SVGElement(shape, g); + var attrs = this.attrs || {}; + attrs.width = attrs.width || '100%'; + attrs.height = attrs.height || '100%'; + attrs.x = attrs.x || 0; + attrs.y = attrs.y || 0; + _.each(attrs, function(val, key) { + elts.attr(key, function(d,i) { + if (key === 'x' || key === 'y') { + return; + } + return convertAttr(d, i, val, key, cell_width, cell_height); + }); + }); + + elts.attr('transform', function(d,i) { + var x_val = convertAttr(d, i, attrs.x, 'x', cell_width, cell_height); + var y_val = convertAttr(d, i, attrs.y, 'y', cell_width, cell_height); + return utils.translate(x_val, y_val); + }); + } + this.filterData = function(data) { + return data.filter(this.condition); + }; + this.isActive = function(data) { + return this.filterData(data).length > 0; + }; +} + +function D3SVGBarChartRule(params, rule_id) { + D3SVGRule.call(this, params, rule_id); + this.data_key = params.data_key; + this.data_range = params.data_range; + + var scale = function(x) { + if (params.scale === 'log') { + return Math.log10(Math.max(x, 0.1)); + } else { + return x; + } + }; + + var makeDatum = function(x) { + var ret = {}; + ret[params.data_key] = x; + return ret; + }; + var scaled_data_range = _.map(this.data_range, scale); + var height_helper = function(d) { + var datum = scale(d[params.data_key]); + var data_range = [scaled_data_range[0], scaled_data_range[1]]; + var distance = (datum-scaled_data_range[0]) / (scaled_data_range[1]-scaled_data_range[0]); + return distance * 100; + }; + var y_function = function(d) { + return (100 - height_helper(d)) + '%'; + }; + var height_function = function(d) { + return height_helper(d) + '%'; + }; + this.attrs.height = height_function; + this.attrs.y = y_function; + this.attrs.fill = params.fill || '#000000'; + + this.putLegendGroup = function(svg, cell_width, cell_height) { + // TODO: triangle legend piece + if (params.exclude_from_legend) { + return; + } + var group = svg.append('g'); + group.append('text').text(this.data_range[0]).attr('alignment-baseline', 'hanging'); + var rect_group = group.append('g'); + var mesh = 50; + for (var i=0; i<=mesh; i++) { + var t = i/mesh; + var d = (1-t)*this.data_range[0] + t*this.data_range[1]; + var datum = makeDatum(d); + var height = cell_height*height_helper(datum)/100; + rect_group.append('rect') + .attr('width', 1) + .attr('height', height) + .attr('y', cell_height-height) + .attr('fill', this.attrs.fill); + } + utils.spaceSVGElementsHorizontally(rect_group, 0); + group.append('text').text(this.data_range[1]).attr('alignment-baseline', 'hanging'); + utils.spaceSVGElementsHorizontally(group, 10); + + return group; + }; +} +D3SVGBarChartRule.prototype = Object.create(D3SVGRule.prototype); + +function D3SVGGradientRule(params, rule_id) { + D3SVGRule.call(this, params, rule_id); + this.data_key = params.data_key; + this.data_range = params.data_range; + this.color_range = params.color_range; + + var getGradientId = (function() { + var gradient_counter = 0; + return function() { + gradient_counter += 1; + return 'gradient'+'_'+rule_id+'_'+gradient_counter; + } + })(); + var scale = function(x) { + if (params.scale === 'log') { + return Math.log10(Math.max(x, 0.1)); + } else { + return x; + } + }; + + var scaled_data_range = _.map(this.data_range, scale); + var fill_function = function(d) { + var datum = scale(d[params.data_key]); + var data_range = [scaled_data_range[0], scaled_data_range[1]]; + var distance = (datum-scaled_data_range[0]) / (scaled_data_range[1]-scaled_data_range[0]); + color_range = [d3.rgb(params.color_range[0]).toString(), + d3.rgb(params.color_range[1]).toString()]; + return utils.lin_interp(distance, params.color_range[0], params.color_range[1]); + }; + this.attrs.fill = fill_function; + + var makeDatum = function(x) { + var ret = {}; + ret[params.data_key] = x; + return ret; + }; + var putLinearGradient = function(group, color_range, width, height) { + var gradient_id = getGradientId(); + var gradient = group.append('svg:defs').append('svg:linearGradient') + .attr('id', gradient_id) + .attr('x1', '0%').attr('y1', '0%') + .attr('x2', '100%').attr('y2', '0%') + .attr('spreadMethod', 'pad'); + gradient.append('svg:stop') + .attr('offset', '0%') + .attr('stop-color', color_range[0]) + .attr('stop-opacity', 1); + gradient.append('svg:stop') + .attr('offset', '100%') + .attr('stop-color', color_range[1]) + .attr('stop-opacity', 1); + group.append('rect') + .attr('width',width).attr('height', height) + .style('fill', 'url(#'+gradient_id+')'); + }; + + var putLogGradient = function(group, color_range, width, height) { + // TODO: I think this is perceptually useless....but could still leave it I guess + var gradient_group = group.append('g'); + var t, datum; + for (var i=0; i. + */ +module.exports = { + ADD_TRACK: 'add_track.oncoprint', + REMOVE_TRACK: 'remove_track.oncoprint', + MOVE_TRACK: 'move_track.oncoprint', + SORT: 'sort.oncoprint', + SET_CELL_PADDING: 'set_cell_padding.oncoprint', + SET_CELL_WIDTH: 'set_cell_width.oncoprint', + SET_TRACK_DATA: 'set_track_data.oncoprint', + SET_ID_ORDER: 'set_id_order.oncoprint', + CELL_CLICK: 'cell_click.oncoprint', + CELL_MOUSEENTER: 'cell_mouseenter.oncoprint', + CELL_MOUSELEAVE: 'cell_mouseleave.oncoprint', + ONCOPRINT_MOUSEENTER: 'oncoprint_mouseenter.oncoprint', + ONCOPRINT_MOUSELEAVE: 'oncoprint_mouseleave.oncoprint', + SET_PRE_TRACK_PADDING: 'set_pre_track_padding.oncoprint', + TRACK_INIT: 'init.track.oncoprint', + UPDATE_RENDER_RULES: 'update_render_rules.cell_renderer.oncoprint', + FINISHED_RENDERING: 'finished_rendering.renderer.oncoprint', + FINISHED_POSITIONING: 'finished_positioning.renderer.oncoprint' +}; diff --git a/packages/oncoprintjs/src/js/globals.js b/packages/oncoprintjs/src/.js/globals.js similarity index 100% rename from packages/oncoprintjs/src/js/globals.js rename to packages/oncoprintjs/src/.js/globals.js diff --git a/packages/oncoprintjs/src/js/main.js b/packages/oncoprintjs/src/.js/main.js similarity index 100% rename from packages/oncoprintjs/src/js/main.js rename to packages/oncoprintjs/src/.js/main.js diff --git a/packages/oncoprintjs/src/.js/oncoprint.js b/packages/oncoprintjs/src/.js/oncoprint.js new file mode 100644 index 00000000000..d2e18a45827 --- /dev/null +++ b/packages/oncoprintjs/src/.js/oncoprint.js @@ -0,0 +1,783 @@ +/* + * Copyright (c) 2015 Memorial Sloan-Kettering Cancer Center. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS + * FOR A PARTICULAR PURPOSE. The software and documentation provided hereunder + * is on an "as is" basis, and Memorial Sloan-Kettering Cancer Center has no + * obligations to provide maintenance, support, updates, enhancements or + * modifications. In no event shall Memorial Sloan-Kettering Cancer Center be + * liable to any party for direct, indirect, special, incidental or + * consequential damages, including lost profits, arising out of the use of this + * software and its documentation, even if Memorial Sloan-Kettering Cancer + * Center has been advised of the possibility of such damage. + */ + +/* + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ +var _ = require('underscore'); +var d3 = require('d3'); +var events = require('./events'); +var signals = require('./signals'); +var globals = require('./globals'); +var utils = require('./utils'); +var RuleSet = require('./RuleSet'); + +var defaultOncoprintConfig = { + cell_width: 6, + cell_padding: 3, + legend: true, +}; + +var hiddenOncoprintConfig = { + pre_track_padding: 0, +}; + +var defaultTrackConfig = { + label: 'Gene', + datum_id_key: 'sample', + cell_height: 23, + track_height: 20, + track_padding: 5, + sort_cmp: undefined, + tooltip: function(d) { + return d['sample']; + } +}; + +module.exports = { + CATEGORICAL_COLOR: RuleSet.CATEGORICAL_COLOR, + GRADIENT_COLOR: RuleSet.GRADIENT_COLOR, + GENETIC_ALTERATION: RuleSet.GENETIC_ALTERATION, + BAR_CHART: RuleSet.BAR_CHART, + create: function CreateOncoprint(container_selector_string, config) { + config = $.extend({}, defaultOncoprintConfig, config || {}); + config = $.extend(config, hiddenOncoprintConfig); + var oncoprint = new Oncoprint(config); + var renderer = new OncoprintSVGRenderer(container_selector_string, oncoprint, {label_font: '12px Arial', legend:config.legend}); + var ret = { + addTrack: function(config) { + var track_id = oncoprint.addTrack(config); + return track_id; + }, + removeTrack: function(track_id) { + oncoprint.removeTrack(track_id); + }, + moveTrack: function(track_id, position) { + oncoprint.moveTrack(track_id, position); + }, + setTrackData: function(track_id, data) { + oncoprint.setTrackData(track_id, data); + }, + setRuleSet: function(track_id, type, params) { + renderer.setRuleSet(track_id, type, params); + }, + useSameRuleSet: function(target_track_id, source_track_id) { + renderer.useSameRuleSet(target_track_id, source_track_id); + }, + setCellPadding: function(p) { + oncoprint.setCellPadding(p); + }, + toSVG: function(ctr) { + return renderer.toSVG(ctr); + }, + sort: function(track_id_list, cmp_list) { + oncoprint.sort(track_id_list, cmp_list); + } + }; + return ret; + } +}; + +function Oncoprint(config) { + var self = this; + var track_id_counter = 0; + self.config = config; + self.id_order = []; + self.track_order = []; + self.tracks = {}; + self.ids = {}; + + self.getCellWidth = function() { + return self.config.cell_width; + }; + self.getCellPadding = function() { + return self.config.cell_padding; + }; + self.getCellHeight = function(track_id) { + return self.tracks[track_id].config.cell_height; + }; + self.getTrackHeight = function(track_id) { + return self.tracks[track_id].config.track_height; + }; + self.getTrackPadding = function(track_id) { + return self.tracks[track_id].config.track_padding; + }; + self.getIdOrder = function() { + return self.id_order; + }; + self.setIdOrder = function(id_order) { + self.id_order = id_order; + $(self).trigger(events.SET_ID_ORDER); + }; + self.getTrackOrder = function() { + return self.track_order.slice(); + }; + self.getTrackLabel = function(track_id) { + return self.tracks[track_id].config.label; + }; + self.getTrackData = function(track_id) { + return self.tracks[track_id].data; + }; + self.getTrackTooltip = function(track_id) { + return self.tracks[track_id].config.tooltip; + }; + self.setTrackData = function(track_id, data) { + var id_accessor = self.getTrackDatumIdAccessor(track_id); + + self.tracks[track_id].data = data; + self.id_order = self.id_order.concat(_.difference(_.map(data, id_accessor), self.id_order)); + + self.tracks[track_id].id_data_map = {}; + var id_data_map = self.tracks[track_id].id_data_map; + _.each(self.tracks[track_id].data, function(datum) { + id_data_map[id_accessor(datum)] = datum; + }); + $(self).trigger(events.SET_TRACK_DATA, {track_id: track_id}); + }; + self.getTrackDatum = function(track_id, datum_id) { + return self.tracks[track_id].id_data_map[datum_id]; + }; + + self.getTrackDatumIdAccessor = function(track_id) { + var key = self.getTrackDatumIdKey(track_id); + return function(d) { + return d[key]; + }; + }; + self.getTrackDatumIdKey = function(track_id) { + return self.tracks[track_id].config.datum_id_key; + }; + + self.removeTrack = function(track_id) { + var track = self.tracks[track_id]; + delete self.tracks[track_id]; + + var oldPosition = self.track_order.indexOf(track_id); + self.track_order.splice(oldPosition, 1); + + $(self).trigger(events.REMOVE_TRACK, {track: track, track_id: track_id}); + return true; + }; + self.moveTrack = function(track_id, new_position) { + new_position = Math.min(self.track_order.length-1, new_position); + new_position = Math.max(0, new_position); + var old_position = self.track_order.indexOf(track_id); + + var new_order = self.track_order.slice(); + var i; + var moved_tracks = []; + if (old_position > new_position) { + for (i=new_position+1; i<=old_position; i++) { + new_order[i] = self.track_order[i-1]; + moved_tracks.push(self.track_order[i-1]); + } + moved_tracks.push(track_id); + } else if (old_position < new_position) { + for (i=old_position; i this.last) { + for (i=this.last + 1; i <= new_bounds.last; i++) { + ret.push(i); + } + } + return ret; + }; + this.toHide = function(new_bounds) { + var ret = []; + var i; + if (new_bounds.first > this.first) { + for (i=this.first; i < new_bounds.first; i++) { + ret.push(i); + } + } + if (new_bounds.last < this.last) { + for (i=new_bounds.last+1; i <= this.last; i++) { + ret.push(i); + } + } + return ret; + }; + this.set = function(first, last) { + this.first = first; + this.last = last; + return this; + }; + this.fromViewInterval = function(interval, cell_unit) { + this.first = Math.floor(interval[0]/cell_unit); + this.last = Math.ceil(interval[1]/cell_unit); + return this; + }; + } + function OncoprintSVGRenderer(container_selector_string, oncoprint, config) { + OncoprintRenderer.call(this, oncoprint, config); + var self = this; + this.toolbar_container; + this.label_svg; + this.label_container; + this.cell_container; + this.cell_container_node; + this.cell_div; + this.legend_svg; + this.cells = {}; + this.curr_clip_bounds = new VisibleIndexBounds(-1, -2); + this.prev_clip_bounds = new VisibleIndexBounds(-1, -2); + + this.clip_zone_start = 0; + + (function initToolbarContainer() { + self.toolbar_container = d3.select(container_selector_string).append('div').classed('toolbar_container', true); + d3.select(container_selector_string).append('br'); + /*$.ajax({url: "toolbar.html", context: document.body, success: function(response) { + $(self.toolbar_container.node()).html(response); + }});*/ + })(); + (function initLabelContainer() { + self.label_container = d3.select(container_selector_string).append('div').classed('fixed_oncoprint_section_container', true); + self.label_svg = self.label_container.append('svg'); + $(self.label_svg.node()).on("mousedown", function(evt) { + // TODO: fix this shit UP + var in_track = -1; + var track_tops = self.getTrackTops(); + var mouse_y = evt.clientY - self.label_svg.node().offsetTop; + _.find(self.oncoprint.getTrackOrder(), function(id) { + if (mouse_y >= track_tops[id] && mouse_y <= track_tops[id] + self.getRenderedTrackHeight(id)) { + in_track = id; + return true; + } + }); + if (in_track > -1) { + self.dragLabel(in_track); + } + }); + })(); + (function initCellContainer() { + self.cell_container = d3.select(container_selector_string).append('div').classed('scrolling_oncoprint_section_container', true); + //self.cell_container.style('display', 'none'); + self.cell_container_node = self.cell_container.node(); + self.cell_div = self.cell_container.append('div').classed('cell_div', true); + + $(self.cell_container.node()).on('scroll', function() { + self.clipCells(); + }); + })(); + (function initLegend() { + if (config.legend) { + self.legend_svg = d3.select(container_selector_string).append('svg'); + } + })(); + + var render_all_events = [events.REMOVE_TRACK]; + var render_track_events = [events.ADD_TRACK, events.SET_TRACK_DATA]; + var reposition_events = [events.MOVE_TRACK, events.SET_CELL_PADDING, events.SET_CELL_WIDTH]; + var resize_cell_div_events = [events.SET_CELL_PADDING, events.SET_CELL_WIDTH]; + var reclip_events = [events.SET_CELL_PADDING, events.SET_CELL_WIDTH]; + var reposition_then_reclip_events = [events.SET_ID_ORDER]; + $(oncoprint).on(resize_cell_div_events.join(" "), function() { + self.resizeCellDiv(); + }); + $(oncoprint).on(render_all_events.join(" "), function() { + self.render(); + }); + $(oncoprint).on(render_track_events.join(" "), function(e, d) { + self.render(d.track_id); + }); + $(oncoprint).on(reposition_events.join(" "), function() { + self.positionCells(); + }); + $(oncoprint).on(reclip_events.join(" "), function() { + self.clipCells(); + }); + $(oncoprint).on(reposition_then_reclip_events.join(" "), function() { + self.positionCells(); + self.clipCells(true); + }); + $(oncoprint).on(events.MOVE_TRACK, function(evt, data) { + // TODO: only reposition tracks that have been moved as a result - this is a fairly slow op so necessary opt + self.positionCells(data.moved_tracks); + self.renderLabels(); + }) + } + utils.extends(OncoprintSVGRenderer, OncoprintRenderer); + + // Rule sets + OncoprintSVGRenderer.prototype.setRuleSet = function(track_id, type, params) { + OncoprintRenderer.prototype.setRuleSet.call(this, track_id, type, params); + this.render(track_id); + }; + OncoprintSVGRenderer.prototype.useSameRuleSet = function(target_track_id, source_track_id) { + OncoprintRenderer.prototype.useSameRuleSet.call(this, target_track_id, source_track_id); + this.render(target_track_id); + } + + // Containers + OncoprintSVGRenderer.prototype.getLabelSVG = function() { + return this.label_svg; + }; + OncoprintSVGRenderer.prototype.resizeCellDiv = function() { + this.cell_div.style('min-width', this.getCellAreaWidth()+'px') + .style('min-height', this.getCellAreaHeight()+'px'); + }; + OncoprintSVGRenderer.prototype.resizeLabelSVG = function() { + this.getLabelSVG().attr('width', this.getLabelAreaWidth()) + .attr('height', this.getLabelAreaHeight()); + }; + OncoprintSVGRenderer.prototype.resizeLegendSVG = function() { + var new_height = 0; + var new_width = 0; + var point = this.legend_svg.node().createSVGPoint(); + utils.d3SelectChildren(this.legend_svg, 'g').each(function() { + point.x = 0; + point.y = 0; + point = point.matrixTransform(this.getCTM()); + var bbox = this.getBBox(); + new_height = Math.max(new_height, point.y+bbox.height); + new_width = Math.max(new_width, point.x + bbox.width); + }); + this.legend_svg.attr('width', new_width).attr('height', new_height); + }; + + // Labels + OncoprintSVGRenderer.prototype.renderTrackLabel = function(oncoprint, track_id, rule_set, svg, label_y) { + var label_class = 'label'+track_id; + label_y = typeof label_y === "undefined" ? this.getLabelTops()[track_id] : label_y; + + var to_reposition; + if (rule_set) { + svg.selectAll('.'+label_class).remove(); + to_reposition = svg.append('text').classed(label_class, true).text(oncoprint.getTrackLabel(track_id)) + .attr('font', this.getLabelFont()) + .attr('alignment-baseline', 'hanging') + .classed('noselect', true) + } else { + to_reposition = svg.selectAll('.' + label_class); + } + to_reposition.attr('transform', utils.translate(0, label_y)); + + var altered_data_label_class = 'altered_data_label'+track_id; + if (rule_set && rule_set.alteredData && typeof rule_set.alteredData === 'function') { + svg.selectAll('.'+altered_data_label_class).remove(); + var data = oncoprint.getTrackData(track_id); + var num_altered = rule_set.alteredData(data).length; + var percent_altered = Math.floor(100*num_altered/data.length); + to_reposition = svg.append('text').classed(altered_data_label_class, true) + .text(percent_altered+'%') + .attr('font', this.getLabelFont()) + .attr('text-anchor', 'end') + .attr('alignment-baseline', 'hanging') + .classed('noselect', true) + } else { + to_reposition = svg.selectAll('.'+altered_data_label_class) + } + to_reposition.attr('transform', utils.translate(this.getLabelAreaWidth(), label_y)); + return svg.selectAll('.'+label_class+',.'+altered_data_label_class); + }; + + // Cells + OncoprintSVGRenderer.prototype.drawTrackCells = function(track_id, rule_set) { + var oncoprint = this.oncoprint; + var data = oncoprint.getTrackData(track_id); + var id_key = oncoprint.getTrackDatumIdKey(track_id); + var id_accessor = oncoprint.getTrackDatumIdAccessor(track_id); + var self = this; + this.cells[track_id] = this.cells[track_id] || {}; + + var cell_class = this.getCellCSSClass(); + var track_cell_class = this.getTrackCellCSSClass(track_id); + + + var bound_svg = this.cell_div.selectAll('svg.'+track_cell_class).data(data, id_accessor); + bound_svg.enter().append('svg').classed(track_cell_class, true).classed(cell_class, true); + bound_svg.style('width', oncoprint.getCellWidth()).style('height', oncoprint.getCellHeight(track_id)); + var tooltip = this.oncoprint.getTrackTooltip(track_id); + bound_svg.each(function(d,i) { + var dom_cell = this; + var id = id_accessor(d); + if (tooltip) { + var tooltip_html = tooltip(d); + $(dom_cell).one("mouseover", function() { + $(dom_cell).qtip({ + content: { + text: tooltip_html + }, + position: {my:'left bottom', at:'top middle', viewport: $(window)}, + style: { classes: 'cell-qtip', border: 'none'}, + show: {event: "mouseover"}, + hide: {fixed: true, delay: 100, event: "mouseout"} + }); + $(dom_cell).trigger("mouseover"); + }); + } + $(dom_cell).on("mouseover", function() { + d3.select(dom_cell).classed("cell_rollover", true); + }); + $(dom_cell).on("mouseout", function() { + d3.select(dom_cell).classed("cell_rollover", false); + }); + self.cells[track_id][id] = this; + }); + bound_svg.selectAll('*').remove(); + rule_set.apply(bound_svg, data, id_accessor, oncoprint.getCellWidth(), oncoprint.getCellHeight(track_id)); + }; + + // Positioning + OncoprintSVGRenderer.prototype.positionTrackCells = function(track_id, bound_svg) { + var oncoprint = this.oncoprint; + if (!bound_svg) { + bound_svg = this.cell_div.selectAll('svg.'+this.getTrackCellCSSClass(track_id)) + .data(oncoprint.getTrackData(track_id), oncoprint.getTrackDatumIdAccessor(track_id)); + } + var self = this; + var id_key = oncoprint.getTrackDatumIdKey(track_id); + var id_order = oncoprint.getIdOrder(); + var y = this.getCellTops()[track_id]; + bound_svg.style('left', function(d,i) { + return self.getCellX(id_order.indexOf(d[id_key])); + }).style('top', y); + }; + OncoprintSVGRenderer.prototype.positionCells = function(track_ids) { + track_ids = track_ids || this.oncoprint.getTrackOrder(); + var self = this; + _.each(track_ids, function(track_id) { + self.positionTrackCells(track_id); + }); + }; + + // Clipping + OncoprintSVGRenderer.prototype.getViewInterval = function() { + var parent = this.cell_container_node; + var parentRect = parent.getBoundingClientRect(); + return {x: parent.scrollLeft, width: parentRect.right - parentRect.left}; + }; + OncoprintSVGRenderer.prototype.getPreviousClipBounds = function() { + return this.prev_clip_bounds; + }; + OncoprintSVGRenderer.prototype.getClipViewInterval = function() { + var self = this; + var view = this.getViewInterval(); + var x = view.x; + var width = view.width; + var clip_buffer = Math.floor(0.05*width); + var clip_zone_size = 3*width; + + var variant=1; + if (variant === 0) { + var section0 = this.clip_zone_start; + var section2 = this.clip_zone_start + 2*width; + + if (x > section2) { + this.clip_zone_start += width; + } else if (x < section0) { + this.clip_zone_start -= width; + } + + return [this.clip_zone_start, this.clip_zone_start + clip_zone_size]; + } else if (variant === 1) { + var section1 = this.clip_zone_start + width; + + while (x > section1 + clip_buffer) { + this.clip_zone_start += clip_buffer; + section1 = this.clip_zone_start + width; + } + while (x < section1 - clip_buffer) { + this.clip_zone_start -= clip_buffer; + section1 = this.clip_zone_start + width; + } + return [this.clip_zone_start, this.clip_zone_start + clip_zone_size]; + } + }; + OncoprintSVGRenderer.prototype.clipCells = function(force) { + var self = this; + var oncoprint = this.oncoprint; + + var id_order = oncoprint.getIdOrder(); + var visible_bounds = this.curr_clip_bounds.fromViewInterval(this.getClipViewInterval(), this.oncoprint.getCellWidth() + this.oncoprint.getCellPadding()); + visible_bounds.first = Math.max(0, visible_bounds.first); + visible_bounds.last = Math.min(id_order.length-1, visible_bounds.last); + var prev_bounds = force ? this.prev_clip_bounds.set(id_order.length, -1) : this.getPreviousClipBounds(); + var to_show = prev_bounds.toShow(visible_bounds); + var to_hide = prev_bounds.toHide(visible_bounds); + if (to_show.length > 0 || to_hide.length > 0) { + var i, len; + for (i=0, len = to_show.length; i < len; i++) { + var datum_id = id_order[to_show[i]]; + _.each(self.cells, function(cell_map) { + var cell = cell_map[datum_id]; + if (cell) { + cell.style.display = 'inherit'; + } + }); + } + for (i=0, len = to_hide.length; i < len; i++) { + var datum_id = id_order[to_hide[i]]; + _.each(self.cells, function(cell_map) { + var cell = cell_map[datum_id]; + if (cell) { + cell.style.display = 'none'; + } + }); + } + } + + this.prev_clip_bounds.set(visible_bounds.first, visible_bounds.last); + }; + + OncoprintSVGRenderer.prototype.isTrackRenderable = function(track_id) { + return this.getRuleSet(track_id) && this.oncoprint.getTrackData(track_id).length > 0; + }; + OncoprintSVGRenderer.prototype.renderLabels = function() { + var self = this; + _.each(this.oncoprint.getTrackOrder(), function(track_id) { + var rule_set = self.getRuleSet(track_id); + self.renderTrackLabel(self.oncoprint, track_id, rule_set, self.getLabelSVG()); + }); + }; + OncoprintSVGRenderer.prototype.render = function(track_id) { + var self = this; + this.resizeLabelSVG(); + this.resizeCellDiv(); + + this.cell_div.style('display', 'none'); + var renderTrack = function(track_id) { + if (self.isTrackRenderable(track_id)) { + var rule_set = self.getRuleSet(track_id); + self.drawTrackCells(track_id, rule_set); + self.positionTrackCells(track_id); + self.renderTrackLabel(self.oncoprint, track_id, rule_set, self.getLabelSVG()); + } + }; + if (typeof track_id !== "undefined") { + renderTrack(track_id); + } else { + _.each(this.oncoprint.getTrackOrder(), function(track_id) { + renderTrack(track_id); + }); + } + self.clipCells(); + this.cell_div.style('display', 'inherit'); + this.renderLegend(); + }; + OncoprintSVGRenderer.prototype.renderLegend = function() { + var svg = this.legend_svg; + svg.selectAll('*').remove(); + var padding = 25; + var y = padding; + var rendered = {}; + var cell_width = this.oncoprint.getCellWidth(); + var self = this; + _.each(this.rule_sets, function(rule_set, track_id) { + var rule_set_id = rule_set.getRuleSetId(); + if (!rendered.hasOwnProperty(rule_set_id)) { + var text = svg.append('text').classed('ruleset_legend_label', true).text(rule_set.getLegendLabel()) + .attr('transform', utils.translate(0,y)); + var group = rule_set.putLegendGroup(svg, cell_width, self.oncoprint.getCellHeight(track_id)); + rendered[rule_set_id] = true; + group.attr('transform', utils.translate(200,y)); + var bounding_box = group.node().getBBox(); + y += bounding_box.height; + y += padding; + } + }); + this.resizeLegendSVG(); + } + OncoprintSVGRenderer.prototype.dragLabel = function(track_id) { + // TODO: everything about this method is technical debt + var self = this; + var other_tracks = this.oncoprint.getTrackOrder(); + var curr_pos = other_tracks.indexOf(track_id); + other_tracks.splice(curr_pos, 1); + + var new_track_pos; + var track_tops_true = this.getLabelTops(); + delete track_tops_true[track_id]; + var label_area_height = self.getLabelAreaHeight(); + var handler = function(evt) { + var track_tops = $.extend({},{},track_tops_true); + var mouse_y = evt.clientY - self.label_svg.node().offsetTop; + var render_y = utils.clamp(mouse_y, 0, label_area_height); + self.renderTrackLabel(self.oncoprint, track_id, false, self.label_svg, mouse_y).classed('dragging_label', true); + + var first_below = 0; + while (track_tops[other_tracks[first_below]] < render_y && first_below < other_tracks.length) { + first_below += 1; + } + if (first_below > 0) { + track_tops[other_tracks[first_below-1]] -= 3; + } + if (first_below < other_tracks.length) { + track_tops[other_tracks[first_below]] += 3; + } + new_track_pos = first_below; + _.each(track_tops, function(top, id) { + self.renderTrackLabel(self.oncoprint, id, false, self.label_svg, top); + }); + }; + $(self.label_svg.node()).on("mousemove", handler); + $(self.label_svg.node()).on("mouseup", function(evt) { + $(self.label_svg.node()).off("mousemove", handler); + self.oncoprint.moveTrack(track_id, new_track_pos); + }); + }; + return OncoprintSVGRenderer; +})(); diff --git a/packages/oncoprintjs/src/js/renderers.js b/packages/oncoprintjs/src/.js/renderers.js similarity index 100% rename from packages/oncoprintjs/src/js/renderers.js rename to packages/oncoprintjs/src/.js/renderers.js diff --git a/packages/oncoprintjs/src/js/rendering_engine.js b/packages/oncoprintjs/src/.js/rendering_engine.js similarity index 100% rename from packages/oncoprintjs/src/js/rendering_engine.js rename to packages/oncoprintjs/src/.js/rendering_engine.js diff --git a/packages/oncoprintjs/src/js/signals.js b/packages/oncoprintjs/src/.js/signals.js similarity index 100% rename from packages/oncoprintjs/src/js/signals.js rename to packages/oncoprintjs/src/.js/signals.js diff --git a/packages/oncoprintjs/src/js/sorting.js b/packages/oncoprintjs/src/.js/sorting.js similarity index 100% rename from packages/oncoprintjs/src/js/sorting.js rename to packages/oncoprintjs/src/.js/sorting.js diff --git a/packages/oncoprintjs/src/js/track.js b/packages/oncoprintjs/src/.js/track.js similarity index 100% rename from packages/oncoprintjs/src/js/track.js rename to packages/oncoprintjs/src/.js/track.js diff --git a/packages/oncoprintjs/src/.js/utils.js b/packages/oncoprintjs/src/.js/utils.js new file mode 100644 index 00000000000..e274404c262 --- /dev/null +++ b/packages/oncoprintjs/src/.js/utils.js @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2015 Memorial Sloan-Kettering Cancer Center. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS + * FOR A PARTICULAR PURPOSE. The software and documentation provided hereunder + * is on an "as is" basis, and Memorial Sloan-Kettering Cancer Center has no + * obligations to provide maintenance, support, updates, enhancements or + * modifications. In no event shall Memorial Sloan-Kettering Cancer Center be + * liable to any party for direct, indirect, special, incidental or + * consequential damages, including lost profits, arising out of the use of this + * software and its documentation, even if Memorial Sloan-Kettering Cancer + * Center has been advised of the possibility of such damage. + */ + +/* + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ +var _ = require("underscore"); +var $ = require("jquery"); + +var exports = module.exports = {}; + +exports.invert_array = function invert_array(arr) { + return arr.reduce(function(curr, next, index) { + curr[next] = index; + return curr; + }, {}); +}; + +exports.extends = function(child_class, parent_class) { + child_class.prototype = Object.create(parent_class.prototype); + child_class.prototype.constructor = child_class; +}; + +exports.makeIdCounter = function() { + var counter = 0; + return function() { + counter += 1; + return counter; + }; +}; + +exports.clamp = function(t, a, b) { + return Math.max(Math.min(b,t), a); +}; + +exports.makeD3SVGElement = function(tag) { + return d3.select(document.createElementNS('http://www.w3.org/2000/svg', tag)); +}; + +exports.appendD3SVGElement = function(elt, target, svg) { + return target.select(function() { + return this.appendChild(elt.node().cloneNode(true)); + }); +}; + +exports.spaceSVGElementsHorizontally = function(group, padding) { + var x = 0; + var elts = exports.d3SelectChildren(group, '*').each(function() { + if (this.tagName === 'defs') { + // don't adjust spacing for a defs element + return; + } + var transform = d3.select(this).attr('transform'); + var y = transform && transform.indexOf("translate") > -1 && parseFloat(transform.split(",")[1], 10); + y = y || 0; + d3.select(this).attr('transform', exports.translate(x, y)); + x += this.getBBox().width; + x += padding; + }); + return group; +}; + +exports.textWidth = function(string, font) { + var obj = $('
'+string+'
') + .css({position: 'absolute', float: 'left', + 'white-space':'nowrap', visibility: 'hidden', + font: font}) + .appendTo($('body')); + var width = obj.width(); + obj.remove(); + return width; +}; + +exports.d3SelectChildren = function(parent, selector) { + return parent.selectAll(selector).filter(function() { + return this.parentNode === parent.node(); + }); +}; + +exports.warn = function(str, context) { + console.log("Oncoprint error in "+context+": "+str); +}; + +exports.stableSort = function(arr, cmp) { + // cmp returns something in [-1,0,1] + + var zipped = []; + _.each(arr, function(val, ind) { + zipped.push([val, ind]); + }); + var stable_cmp = function(a,b) { + var res = cmp(a[0], b[0]); + if (res === 0) { + if (a[1] < b[1]) { + res = -1; + } else if (a[1] > b[1]) { + res = 1; + } + } + return res; + }; + zipped.sort(stable_cmp); + return _.map(zipped, function(x) { return x[0];}); +}; + +exports.lin_interp = function(t, a, b) { + if (a[0] === '#') { + var r = [parseInt(a.substring(1,3), 16), parseInt(b.substring(1,3), 16)]; + var g = [parseInt(a.substring(3,5), 16), parseInt(b.substring(3,5), 16)]; + var b = [parseInt(a.substring(5,7), 16), parseInt(b.substring(5,7), 16)]; + var R = Math.round(r[0]*(1-t) + r[1]*t).toString(16); + var G = Math.round(g[0]*(1-t) + g[1]*t).toString(16); + var B = Math.round(b[0]*(1-t) + b[1]*t).toString(16); + + R = R.length < 2 ? '0'+R : R; + G = G.length < 2 ? '0'+G : G; + B = B.length < 2 ? '0'+B : B; + + return '#' + R + G + B; + } else if (isNaN(a) && a.indexOf('%') > -1) { + var A = parseFloat(a, 10); + var B = parseFloat(b, 10); + return (A*(1-t) + B*t)+'%'; + } else { + return a*(1-t) + b*t; + } +}; + +exports.translate = function(x,y) { + return "translate(" + x + "," + y + ")"; +}; + +exports.assert = function(bool, msg) { + if (!bool) { + throw msg; + } +} diff --git a/packages/oncoprintjs/src/js/utils2.js b/packages/oncoprintjs/src/.js/utils2.js similarity index 100% rename from packages/oncoprintjs/src/js/utils2.js rename to packages/oncoprintjs/src/.js/utils2.js From 3442e2bcda3090effb4f274395ef05e000c46a37 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Mon, 29 Jun 2015 15:25:42 -0400 Subject: [PATCH 081/343] change from npm module exports to closure return pattern --- packages/oncoprintjs/src/js/RuleSet.js | 892 +++++++------- packages/oncoprintjs/src/js/events.js | 2 +- packages/oncoprintjs/src/js/oncoprint.js | 1402 +++++++++++----------- packages/oncoprintjs/src/js/utils.js | 249 ++-- 4 files changed, 1287 insertions(+), 1258 deletions(-) diff --git a/packages/oncoprintjs/src/js/RuleSet.js b/packages/oncoprintjs/src/js/RuleSet.js index 4e38bc03521..2308734ee97 100644 --- a/packages/oncoprintjs/src/js/RuleSet.js +++ b/packages/oncoprintjs/src/js/RuleSet.js @@ -1,469 +1,499 @@ -var _ = require('underscore'); -var utils = require('./utils'); +/* + * Copyright (c) 2015 Memorial Sloan-Kettering Cancer Center. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS + * FOR A PARTICULAR PURPOSE. The software and documentation provided hereunder + * is on an "as is" basis, and Memorial Sloan-Kettering Cancer Center has no + * obligations to provide maintenance, support, updates, enhancements or + * modifications. In no event shall Memorial Sloan-Kettering Cancer Center be + * liable to any party for direct, indirect, special, incidental or + * consequential damages, including lost profits, arising out of the use of this + * software and its documentation, even if Memorial Sloan-Kettering Cancer + * Center has been advised of the possibility of such damage. + */ -var CATEGORICAL_COLOR = 0; -var GRADIENT_COLOR = 1; -var GENETIC_ALTERATION = 2; -var BAR_CHART = 3; -module.exports = { - CATEGORICAL_COLOR: CATEGORICAL_COLOR, - GRADIENT_COLOR: GRADIENT_COLOR, - GENETIC_ALTERATION: GENETIC_ALTERATION, - BAR_CHART: BAR_CHART, - makeRuleSet: function(type, params) { - if (type === CATEGORICAL_COLOR) { - return new D3SVGCategoricalColorRuleSet(params); - } else if (type === GRADIENT_COLOR) { - return new D3SVGGradientColorRuleSet(params); - } else if (type === GENETIC_ALTERATION) { - return new D3SVGGeneticAlterationRuleSet(params); - } else if (type === BAR_CHART) { - return new D3SVGBarChartRuleSet(params); - } else { - return new D3SVGRuleSet(); - } - } -}; +/* + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ +window.oncoprint_RuleSet = (function(){ + var utils = window.oncoprint_utils; + var CATEGORICAL_COLOR = 0; + var GRADIENT_COLOR = 1; + var GENETIC_ALTERATION = 2; + var BAR_CHART = 3; -var getRuleSetId = utils.makeIdCounter(); + var getRuleSetId = utils.makeIdCounter(); -var D3SVGRuleSet = (function() { - function D3SVGRuleSet(params) { - this.rule_map = {}; - this.rule_set_id = getRuleSetId(); - this.legend_label = params.legend_label; - }; - var getRuleId = utils.makeIdCounter(); + var D3SVGRuleSet = (function() { + function D3SVGRuleSet(params) { + this.rule_map = {}; + this.rule_set_id = getRuleSetId(); + this.legend_label = params.legend_label; + }; + var getRuleId = utils.makeIdCounter(); - D3SVGRuleSet.prototype.getLegendLabel = function() { - return this.legend_label; - }; - D3SVGRuleSet.prototype.getRuleSetId = function() { - return this.rule_set_id; - }; - D3SVGRuleSet.prototype.addRule = function(params) { - var rule_id = getRuleId(); - this.rule_map[rule_id] = new D3SVGRule(params, rule_id); - return rule_id; - } - D3SVGRuleSet.prototype.addStaticRule = function(params) { - var rule_id = getRuleId(); - this.rule_map[rule_id] = new D3SVGStaticRule(params, rule_id); - return rule_id; - }; - D3SVGRuleSet.prototype.addGradientRule = function(params) { - var rule_id = getRuleId(); - this.rule_map[rule_id] = new D3SVGGradientRule(params, rule_id); - return rule_id; - }; - D3SVGRuleSet.prototype.addBarChartRule = function(params) { - var rule_id = getRuleId(); - this.rule_map[rule_id] = new D3SVGBarChartRule(params, rule_id); - return rule_id; - }; - D3SVGRuleSet.prototype.removeRule = function(rule_id) { - delete this.rule_map[rule_id]; - }; - D3SVGRuleSet.prototype.getRules = function() { - var self = this; - var rule_ids = Object.keys(this.rule_map); - var rules = _.map(rule_ids, function(id) { return self.rule_map[id]; }); - var sorted_rules = _.sortBy(rules, function(r) { return r.z_index; }); - return sorted_rules; - }; - D3SVGRuleSet.prototype.apply = function(g, data, datum_id_accessor, cell_width, cell_height) { - _.each(this.getRules(), function(rule) { - var affected_data = rule.filterData(data); - var affected_groups = g.data(affected_data, datum_id_accessor); - rule.apply(affected_groups, cell_width, cell_height); - }); - }; - D3SVGRuleSet.prototype.getRule = function(rule_id) { - return this.rule_map[rule_id]; - }; - return D3SVGRuleSet; -})(); + D3SVGRuleSet.prototype.getLegendLabel = function() { + return this.legend_label; + }; + D3SVGRuleSet.prototype.getRuleSetId = function() { + return this.rule_set_id; + }; + D3SVGRuleSet.prototype.addRule = function(params) { + var rule_id = getRuleId(); + this.rule_map[rule_id] = new D3SVGRule(params, rule_id); + return rule_id; + } + D3SVGRuleSet.prototype.addStaticRule = function(params) { + var rule_id = getRuleId(); + this.rule_map[rule_id] = new D3SVGStaticRule(params, rule_id); + return rule_id; + }; + D3SVGRuleSet.prototype.addGradientRule = function(params) { + var rule_id = getRuleId(); + this.rule_map[rule_id] = new D3SVGGradientRule(params, rule_id); + return rule_id; + }; + D3SVGRuleSet.prototype.addBarChartRule = function(params) { + var rule_id = getRuleId(); + this.rule_map[rule_id] = new D3SVGBarChartRule(params, rule_id); + return rule_id; + }; + D3SVGRuleSet.prototype.removeRule = function(rule_id) { + delete this.rule_map[rule_id]; + }; + D3SVGRuleSet.prototype.getRules = function() { + var self = this; + var rule_ids = Object.keys(this.rule_map); + var rules = _.map(rule_ids, function(id) { return self.rule_map[id]; }); + var sorted_rules = _.sortBy(rules, function(r) { return r.z_index; }); + return sorted_rules; + }; + D3SVGRuleSet.prototype.apply = function(g, data, datum_id_accessor, cell_width, cell_height) { + _.each(this.getRules(), function(rule) { + var affected_data = rule.filterData(data); + var affected_groups = g.data(affected_data, datum_id_accessor); + rule.apply(affected_groups, cell_width, cell_height); + }); + }; + D3SVGRuleSet.prototype.getRule = function(rule_id) { + return this.rule_map[rule_id]; + }; + return D3SVGRuleSet; + })(); -function D3SVGCategoricalColorRuleSet(params) { - D3SVGRuleSet.call(this, params); - this.type = CATEGORICAL_COLOR; - var self = this; - var d3_colors = _.shuffle(d3.scale.category20().range()); - var addColorRule = function(color, category) { - var colored_rect = utils.makeD3SVGElement('rect').attr('fill', color); - var condition = (function(cat) { - return function(d) { - return params.getCategory(d) === cat; - }; - })(category); - self.addStaticRule({ - condition: condition, - shape: colored_rect, - legend_label: category + function D3SVGCategoricalColorRuleSet(params) { + D3SVGRuleSet.call(this, params); + this.type = CATEGORICAL_COLOR; + var self = this; + var d3_colors = _.shuffle(d3.scale.category20().range()); + var addColorRule = function(color, category) { + var colored_rect = utils.makeD3SVGElement('rect').attr('fill', color); + var condition = (function(cat) { + return function(d) { + return params.getCategory(d) === cat; + }; + })(category); + self.addStaticRule({ + condition: condition, + shape: colored_rect, + legend_label: category + }); + }; + _.each(params.color, function(color, category) { + addColorRule(color, category); }); - }; - _.each(params.color, function(color, category) { - addColorRule(color, category); - }); - self.apply = function(g, data, datum_id_accessor, cell_width, cell_height) { - var missing_categories = []; - _.each(data, function(datum) { - var category = params.getCategory(datum); - if (!params.color.hasOwnProperty(category)) { - var new_color = d3_colors.pop(); - params.color[category] = new_color; - addColorRule(new_color, category); - } - }); - D3SVGRuleSet.prototype.apply.call(this, g, data, datum_id_accessor, cell_width, cell_height); - }; + self.apply = function(g, data, datum_id_accessor, cell_width, cell_height) { + var missing_categories = []; + _.each(data, function(datum) { + var category = params.getCategory(datum); + if (!params.color.hasOwnProperty(category)) { + var new_color = d3_colors.pop(); + params.color[category] = new_color; + addColorRule(new_color, category); + } + }); + D3SVGRuleSet.prototype.apply.call(this, g, data, datum_id_accessor, cell_width, cell_height); + }; - self.putLegendGroup = function(svg, cell_width, cell_height) { - var group = svg.append('g'); - _.each(self.getRules(), function(rule) { - rule.putLegendGroup(group, cell_width, cell_height); - }) - utils.spaceSVGElementsHorizontally(group, 20); - return group; - }; -} -D3SVGCategoricalColorRuleSet.prototype = Object.create(D3SVGRuleSet.prototype); + self.putLegendGroup = function(svg, cell_width, cell_height) { + var group = svg.append('g'); + _.each(self.getRules(), function(rule) { + rule.putLegendGroup(group, cell_width, cell_height); + }) + utils.spaceSVGElementsHorizontally(group, 20); + return group; + }; + } + D3SVGCategoricalColorRuleSet.prototype = Object.create(D3SVGRuleSet.prototype); -function D3SVGGradientColorRuleSet(params) { - D3SVGRuleSet.call(this, params); - this.type = GRADIENT_COLOR; - var rule = this.addGradientRule({ - shape: utils.makeD3SVGElement('rect'), - data_key: params.data_key, - data_range: params.data_range, - color_range: params.color_range, - scale: params.scale - }); - this.putLegendGroup = function(svg) { - return this.rule_map[rule].putLegendGroup(svg); - }; -} -D3SVGGradientColorRuleSet.prototype = Object.create(D3SVGRuleSet.prototype); + function D3SVGGradientColorRuleSet(params) { + D3SVGRuleSet.call(this, params); + this.type = GRADIENT_COLOR; + var rule = this.addGradientRule({ + shape: utils.makeD3SVGElement('rect'), + data_key: params.data_key, + data_range: params.data_range, + color_range: params.color_range, + scale: params.scale + }); + this.putLegendGroup = function(svg) { + return this.rule_map[rule].putLegendGroup(svg); + }; + } + D3SVGGradientColorRuleSet.prototype = Object.create(D3SVGRuleSet.prototype); -function D3SVGBarChartRuleSet(params) { - D3SVGRuleSet.call(this, params); - var self = this; - self.type = BAR_CHART; - var rule = this.addBarChartRule({ - data_key: params.data_key, - data_range: params.data_range, - scale: params.scale, - fill: params.fill, - }); - this.putLegendGroup = function(svg, cell_width, cell_height) { - return this.rule_map[rule].putLegendGroup(svg, cell_width, cell_height); - }; -} -D3SVGBarChartRuleSet.prototype = Object.create(D3SVGRuleSet.prototype); + function D3SVGBarChartRuleSet(params) { + D3SVGRuleSet.call(this, params); + var self = this; + self.type = BAR_CHART; + var rule = this.addBarChartRule({ + data_key: params.data_key, + data_range: params.data_range, + scale: params.scale, + fill: params.fill, + }); + this.putLegendGroup = function(svg, cell_width, cell_height) { + return this.rule_map[rule].putLegendGroup(svg, cell_width, cell_height); + }; + } + D3SVGBarChartRuleSet.prototype = Object.create(D3SVGRuleSet.prototype); -function D3SVGGeneticAlterationRuleSet(params) { - D3SVGRuleSet.call(this, params); - var self = this; - self.type = GENETIC_ALTERATION; - var default_rule = this.addStaticRule({ - shape: utils.makeD3SVGElement('rect').attr('fill', params.default_color), - exclude_from_legend: true, - z_index: -1 - }); - var altered_rules = []; - _.each(params.cna.color, function(color, name) { - var new_cna_rule = self.addStaticRule({ - condition: (function(_name) { - return function(d) { - return d[params.cna_key] === _name; - }; - })(name), - shape: utils.makeD3SVGElement('rect'), - legend_label: params.cna.label[name], - attrs: { - fill: color, - width: '100%', - height: '100%' - }, - z_index: 0 + function D3SVGGeneticAlterationRuleSet(params) { + D3SVGRuleSet.call(this, params); + var self = this; + self.type = GENETIC_ALTERATION; + var default_rule = this.addStaticRule({ + shape: utils.makeD3SVGElement('rect').attr('fill', params.default_color), + exclude_from_legend: true, + z_index: -1 }); - altered_rules.push(new_cna_rule); - }); - _.each(params.mut.color, function(color, name) { - var new_mut_rule = self.addStaticRule({ - condition: (function(_name) { - return function(d) { - return d[params.mut_type_key] === _name; // TODO: should be indexOf for multiple mutations? - } - })(name), - shape: utils.makeD3SVGElement('rect').attr('fill', color), - legend_label: params.mut.label[name], - attrs: { - width: '100%', - height: '33.33%', - y: '33.33%' - }, - z_index: 1 + var altered_rules = []; + _.each(params.cna.color, function(color, name) { + var new_cna_rule = self.addStaticRule({ + condition: (function(_name) { + return function(d) { + return d[params.cna_key] === _name; + }; + })(name), + shape: utils.makeD3SVGElement('rect'), + legend_label: params.cna.label[name], + attrs: { + fill: color, + width: '100%', + height: '100%' + }, + z_index: 0 + }); + altered_rules.push(new_cna_rule); }); - altered_rules.push(new_mut_rule); - }); - // TODO: mrna, rppa, other stuff? - self.putLegendGroup = function(svg, cell_width, cell_height) { - var group = svg.append('g'); - _.each(self.getRules(), function(rule) { - rule.putLegendGroup(group, cell_width, cell_height); - }) - utils.spaceSVGElementsHorizontally(group, 20); - return group; - }; - self.alteredData = function(data) { - var altered_data = []; - _.each(altered_rules, function(rule_id) { - altered_data = altered_data.concat(self.getRule(rule_id).filterData(data)); + _.each(params.mut.color, function(color, name) { + var new_mut_rule = self.addStaticRule({ + condition: (function(_name) { + return function(d) { + return d[params.mut_type_key] === _name; // TODO: should be indexOf for multiple mutations? + } + })(name), + shape: utils.makeD3SVGElement('rect').attr('fill', color), + legend_label: params.mut.label[name], + attrs: { + width: '100%', + height: '33.33%', + y: '33.33%' + }, + z_index: 1 + }); + altered_rules.push(new_mut_rule); }); - return _.uniq(altered_data); - }; -} -D3SVGGeneticAlterationRuleSet.prototype = Object.create(D3SVGRuleSet.prototype); + // TODO: mrna, rppa, other stuff? + self.putLegendGroup = function(svg, cell_width, cell_height) { + var group = svg.append('g'); + _.each(self.getRules(), function(rule) { + rule.putLegendGroup(group, cell_width, cell_height); + }) + utils.spaceSVGElementsHorizontally(group, 20); + return group; + }; + self.alteredData = function(data) { + var altered_data = []; + _.each(altered_rules, function(rule_id) { + altered_data = altered_data.concat(self.getRule(rule_id).filterData(data)); + }); + return _.uniq(altered_data); + }; + } + D3SVGGeneticAlterationRuleSet.prototype = Object.create(D3SVGRuleSet.prototype); -function D3SVGRule(params, rule_id) { - this.rule_id = rule_id; - this.condition = params.condition || function(d) { return true; }; - this.shape = typeof params.shape === 'undefined' ? utils.makeD3SVGElement('rect') : params.shape; - this.z_index = typeof params.z_index === 'undefined' ? this.rule_id : params.z_index; - this.legend_label = params.legend_label; - this.exclude_from_legend = params.exclude_from_legend; + function D3SVGRule(params, rule_id) { + this.rule_id = rule_id; + this.condition = params.condition || function(d) { return true; }; + this.shape = typeof params.shape === 'undefined' ? utils.makeD3SVGElement('rect') : params.shape; + this.z_index = typeof params.z_index === 'undefined' ? this.rule_id : params.z_index; + this.legend_label = params.legend_label; + this.exclude_from_legend = params.exclude_from_legend; - this.attrs = params.attrs || {}; - this.attrs.width = this.attrs.width || '100%'; - this.attrs.height = this.attrs.height || '100%'; + this.attrs = params.attrs || {}; + this.attrs.width = this.attrs.width || '100%'; + this.attrs.height = this.attrs.height || '100%'; - var percentToPx = function(attr_val, attr_name, cell_width, cell_height) { - // convert a percentage to a local pixel coordinate - var width_like = ['width', 'x']; - var height_like = ['height', 'y']; - attr_val = parseFloat(attr_val, 10)/100; - if (width_like.indexOf(attr_name) > -1) { - attr_val = attr_val*cell_width; - } else if (height_like.indexOf(attr_name) > -1) { - attr_val = attr_val*cell_height; - } - return attr_val+''; - }; + var percentToPx = function(attr_val, attr_name, cell_width, cell_height) { + // convert a percentage to a local pixel coordinate + var width_like = ['width', 'x']; + var height_like = ['height', 'y']; + attr_val = parseFloat(attr_val, 10)/100; + if (width_like.indexOf(attr_name) > -1) { + attr_val = attr_val*cell_width; + } else if (height_like.indexOf(attr_name) > -1) { + attr_val = attr_val*cell_height; + } + return attr_val+''; + }; - var convertAttr = function(d, i, attr_val, attr_name, cell_width, cell_height) { - var ret = attr_val; - if (typeof ret === 'function') { - ret = ret(d,i); - } - if (typeof ret === 'string' && ret.indexOf('%') > -1) { - ret = percentToPx(ret, attr_name, cell_width, cell_height); - } - return ret; - }; + var convertAttr = function(d, i, attr_val, attr_name, cell_width, cell_height) { + var ret = attr_val; + if (typeof ret === 'function') { + ret = ret(d,i); + } + if (typeof ret === 'string' && ret.indexOf('%') > -1) { + ret = percentToPx(ret, attr_name, cell_width, cell_height); + } + return ret; + }; - this.apply = function(g, cell_width, cell_height) { - var shape = this.shape; - var elts = utils.appendD3SVGElement(shape, g); - var attrs = this.attrs || {}; - attrs.width = attrs.width || '100%'; - attrs.height = attrs.height || '100%'; - attrs.x = attrs.x || 0; - attrs.y = attrs.y || 0; - _.each(attrs, function(val, key) { - elts.attr(key, function(d,i) { - if (key === 'x' || key === 'y') { - return; - } - return convertAttr(d, i, val, key, cell_width, cell_height); + this.apply = function(g, cell_width, cell_height) { + var shape = this.shape; + var elts = utils.appendD3SVGElement(shape, g); + var attrs = this.attrs || {}; + attrs.width = attrs.width || '100%'; + attrs.height = attrs.height || '100%'; + attrs.x = attrs.x || 0; + attrs.y = attrs.y || 0; + _.each(attrs, function(val, key) { + elts.attr(key, function(d,i) { + if (key === 'x' || key === 'y') { + return; + } + return convertAttr(d, i, val, key, cell_width, cell_height); + }); }); - }); - - elts.attr('transform', function(d,i) { - var x_val = convertAttr(d, i, attrs.x, 'x', cell_width, cell_height); - var y_val = convertAttr(d, i, attrs.y, 'y', cell_width, cell_height); - return utils.translate(x_val, y_val); - }); + + elts.attr('transform', function(d,i) { + var x_val = convertAttr(d, i, attrs.x, 'x', cell_width, cell_height); + var y_val = convertAttr(d, i, attrs.y, 'y', cell_width, cell_height); + return utils.translate(x_val, y_val); + }); + } + this.filterData = function(data) { + return data.filter(this.condition); + }; + this.isActive = function(data) { + return this.filterData(data).length > 0; + }; } - this.filterData = function(data) { - return data.filter(this.condition); - }; - this.isActive = function(data) { - return this.filterData(data).length > 0; - }; -} -function D3SVGBarChartRule(params, rule_id) { - D3SVGRule.call(this, params, rule_id); - this.data_key = params.data_key; - this.data_range = params.data_range; + function D3SVGBarChartRule(params, rule_id) { + D3SVGRule.call(this, params, rule_id); + this.data_key = params.data_key; + this.data_range = params.data_range; - var scale = function(x) { - if (params.scale === 'log') { - return Math.log10(Math.max(x, 0.1)); - } else { - return x; - } - }; + var scale = function(x) { + if (params.scale === 'log') { + return Math.log10(Math.max(x, 0.1)); + } else { + return x; + } + }; - var makeDatum = function(x) { - var ret = {}; - ret[params.data_key] = x; - return ret; - }; - var scaled_data_range = _.map(this.data_range, scale); - var height_helper = function(d) { - var datum = scale(d[params.data_key]); - var data_range = [scaled_data_range[0], scaled_data_range[1]]; - var distance = (datum-scaled_data_range[0]) / (scaled_data_range[1]-scaled_data_range[0]); - return distance * 100; - }; - var y_function = function(d) { - return (100 - height_helper(d)) + '%'; - }; - var height_function = function(d) { - return height_helper(d) + '%'; - }; - this.attrs.height = height_function; - this.attrs.y = y_function; - this.attrs.fill = params.fill || '#000000'; + var makeDatum = function(x) { + var ret = {}; + ret[params.data_key] = x; + return ret; + }; + var scaled_data_range = _.map(this.data_range, scale); + var height_helper = function(d) { + var datum = scale(d[params.data_key]); + var data_range = [scaled_data_range[0], scaled_data_range[1]]; + var distance = (datum-scaled_data_range[0]) / (scaled_data_range[1]-scaled_data_range[0]); + return distance * 100; + }; + var y_function = function(d) { + return (100 - height_helper(d)) + '%'; + }; + var height_function = function(d) { + return height_helper(d) + '%'; + }; + this.attrs.height = height_function; + this.attrs.y = y_function; + this.attrs.fill = params.fill || '#000000'; - this.putLegendGroup = function(svg, cell_width, cell_height) { - // TODO: triangle legend piece - if (params.exclude_from_legend) { - return; - } - var group = svg.append('g'); - group.append('text').text(this.data_range[0]).attr('alignment-baseline', 'hanging'); - var rect_group = group.append('g'); - var mesh = 50; - for (var i=0; i<=mesh; i++) { - var t = i/mesh; - var d = (1-t)*this.data_range[0] + t*this.data_range[1]; - var datum = makeDatum(d); - var height = cell_height*height_helper(datum)/100; - rect_group.append('rect') - .attr('width', 1) - .attr('height', height) - .attr('y', cell_height-height) - .attr('fill', this.attrs.fill); - } - utils.spaceSVGElementsHorizontally(rect_group, 0); - group.append('text').text(this.data_range[1]).attr('alignment-baseline', 'hanging'); - utils.spaceSVGElementsHorizontally(group, 10); + this.putLegendGroup = function(svg, cell_width, cell_height) { + // TODO: triangle legend piece + if (params.exclude_from_legend) { + return; + } + var group = svg.append('g'); + group.append('text').text(this.data_range[0]).attr('alignment-baseline', 'hanging'); + var rect_group = group.append('g'); + var mesh = 50; + for (var i=0; i<=mesh; i++) { + var t = i/mesh; + var d = (1-t)*this.data_range[0] + t*this.data_range[1]; + var datum = makeDatum(d); + var height = cell_height*height_helper(datum)/100; + rect_group.append('rect') + .attr('width', 1) + .attr('height', height) + .attr('y', cell_height-height) + .attr('fill', this.attrs.fill); + } + utils.spaceSVGElementsHorizontally(rect_group, 0); + group.append('text').text(this.data_range[1]).attr('alignment-baseline', 'hanging'); + utils.spaceSVGElementsHorizontally(group, 10); - return group; - }; -} -D3SVGBarChartRule.prototype = Object.create(D3SVGRule.prototype); + return group; + }; + } + D3SVGBarChartRule.prototype = Object.create(D3SVGRule.prototype); -function D3SVGGradientRule(params, rule_id) { - D3SVGRule.call(this, params, rule_id); - this.data_key = params.data_key; - this.data_range = params.data_range; - this.color_range = params.color_range; + function D3SVGGradientRule(params, rule_id) { + D3SVGRule.call(this, params, rule_id); + this.data_key = params.data_key; + this.data_range = params.data_range; + this.color_range = params.color_range; - var getGradientId = (function() { - var gradient_counter = 0; - return function() { - gradient_counter += 1; - return 'gradient'+'_'+rule_id+'_'+gradient_counter; - } - })(); - var scale = function(x) { - if (params.scale === 'log') { - return Math.log10(Math.max(x, 0.1)); - } else { - return x; - } - }; + var getGradientId = (function() { + var gradient_counter = 0; + return function() { + gradient_counter += 1; + return 'gradient'+'_'+rule_id+'_'+gradient_counter; + } + })(); + var scale = function(x) { + if (params.scale === 'log') { + return Math.log10(Math.max(x, 0.1)); + } else { + return x; + } + }; - var scaled_data_range = _.map(this.data_range, scale); - var fill_function = function(d) { - var datum = scale(d[params.data_key]); - var data_range = [scaled_data_range[0], scaled_data_range[1]]; - var distance = (datum-scaled_data_range[0]) / (scaled_data_range[1]-scaled_data_range[0]); - color_range = [d3.rgb(params.color_range[0]).toString(), - d3.rgb(params.color_range[1]).toString()]; - return utils.lin_interp(distance, params.color_range[0], params.color_range[1]); - }; - this.attrs.fill = fill_function; + var scaled_data_range = _.map(this.data_range, scale); + var fill_function = function(d) { + var datum = scale(d[params.data_key]); + var data_range = [scaled_data_range[0], scaled_data_range[1]]; + var distance = (datum-scaled_data_range[0]) / (scaled_data_range[1]-scaled_data_range[0]); + color_range = [d3.rgb(params.color_range[0]).toString(), + d3.rgb(params.color_range[1]).toString()]; + return utils.lin_interp(distance, params.color_range[0], params.color_range[1]); + }; + this.attrs.fill = fill_function; - var makeDatum = function(x) { - var ret = {}; - ret[params.data_key] = x; - return ret; - }; - var putLinearGradient = function(group, color_range, width, height) { - var gradient_id = getGradientId(); - var gradient = group.append('svg:defs').append('svg:linearGradient') - .attr('id', gradient_id) - .attr('x1', '0%').attr('y1', '0%') - .attr('x2', '100%').attr('y2', '0%') - .attr('spreadMethod', 'pad'); - gradient.append('svg:stop') - .attr('offset', '0%') - .attr('stop-color', color_range[0]) - .attr('stop-opacity', 1); - gradient.append('svg:stop') - .attr('offset', '100%') - .attr('stop-color', color_range[1]) - .attr('stop-opacity', 1); - group.append('rect') - .attr('width',width).attr('height', height) - .style('fill', 'url(#'+gradient_id+')'); - }; + var makeDatum = function(x) { + var ret = {}; + ret[params.data_key] = x; + return ret; + }; + var putLinearGradient = function(group, color_range, width, height) { + var gradient_id = getGradientId(); + var gradient = group.append('svg:defs').append('svg:linearGradient') + .attr('id', gradient_id) + .attr('x1', '0%').attr('y1', '0%') + .attr('x2', '100%').attr('y2', '0%') + .attr('spreadMethod', 'pad'); + gradient.append('svg:stop') + .attr('offset', '0%') + .attr('stop-color', color_range[0]) + .attr('stop-opacity', 1); + gradient.append('svg:stop') + .attr('offset', '100%') + .attr('stop-color', color_range[1]) + .attr('stop-opacity', 1); + group.append('rect') + .attr('width',width).attr('height', height) + .style('fill', 'url(#'+gradient_id+')'); + }; - var putLogGradient = function(group, color_range, width, height) { - // TODO: I think this is perceptually useless....but could still leave it I guess - var gradient_group = group.append('g'); - var t, datum; - for (var i=0; i. */ -module.exports = { +window.oncoprint_events = { ADD_TRACK: 'add_track.oncoprint', REMOVE_TRACK: 'remove_track.oncoprint', MOVE_TRACK: 'move_track.oncoprint', diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index d2e18a45827..c8c366ec8e3 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -27,757 +27,755 @@ * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ -var _ = require('underscore'); -var d3 = require('d3'); -var events = require('./events'); -var signals = require('./signals'); -var globals = require('./globals'); -var utils = require('./utils'); -var RuleSet = require('./RuleSet'); - -var defaultOncoprintConfig = { - cell_width: 6, - cell_padding: 3, - legend: true, -}; - -var hiddenOncoprintConfig = { - pre_track_padding: 0, -}; - -var defaultTrackConfig = { - label: 'Gene', - datum_id_key: 'sample', - cell_height: 23, - track_height: 20, - track_padding: 5, - sort_cmp: undefined, - tooltip: function(d) { - return d['sample']; - } -}; - -module.exports = { - CATEGORICAL_COLOR: RuleSet.CATEGORICAL_COLOR, - GRADIENT_COLOR: RuleSet.GRADIENT_COLOR, - GENETIC_ALTERATION: RuleSet.GENETIC_ALTERATION, - BAR_CHART: RuleSet.BAR_CHART, - create: function CreateOncoprint(container_selector_string, config) { - config = $.extend({}, defaultOncoprintConfig, config || {}); - config = $.extend(config, hiddenOncoprintConfig); - var oncoprint = new Oncoprint(config); - var renderer = new OncoprintSVGRenderer(container_selector_string, oncoprint, {label_font: '12px Arial', legend:config.legend}); - var ret = { - addTrack: function(config) { - var track_id = oncoprint.addTrack(config); - return track_id; - }, - removeTrack: function(track_id) { - oncoprint.removeTrack(track_id); - }, - moveTrack: function(track_id, position) { - oncoprint.moveTrack(track_id, position); - }, - setTrackData: function(track_id, data) { - oncoprint.setTrackData(track_id, data); - }, - setRuleSet: function(track_id, type, params) { - renderer.setRuleSet(track_id, type, params); - }, - useSameRuleSet: function(target_track_id, source_track_id) { - renderer.useSameRuleSet(target_track_id, source_track_id); - }, - setCellPadding: function(p) { - oncoprint.setCellPadding(p); - }, - toSVG: function(ctr) { - return renderer.toSVG(ctr); - }, - sort: function(track_id_list, cmp_list) { - oncoprint.sort(track_id_list, cmp_list); - } - }; - return ret; - } -}; - -function Oncoprint(config) { - var self = this; - var track_id_counter = 0; - self.config = config; - self.id_order = []; - self.track_order = []; - self.tracks = {}; - self.ids = {}; - - self.getCellWidth = function() { - return self.config.cell_width; - }; - self.getCellPadding = function() { - return self.config.cell_padding; - }; - self.getCellHeight = function(track_id) { - return self.tracks[track_id].config.cell_height; - }; - self.getTrackHeight = function(track_id) { - return self.tracks[track_id].config.track_height; - }; - self.getTrackPadding = function(track_id) { - return self.tracks[track_id].config.track_padding; - }; - self.getIdOrder = function() { - return self.id_order; - }; - self.setIdOrder = function(id_order) { - self.id_order = id_order; - $(self).trigger(events.SET_ID_ORDER); - }; - self.getTrackOrder = function() { - return self.track_order.slice(); - }; - self.getTrackLabel = function(track_id) { - return self.tracks[track_id].config.label; - }; - self.getTrackData = function(track_id) { - return self.tracks[track_id].data; - }; - self.getTrackTooltip = function(track_id) { - return self.tracks[track_id].config.tooltip; - }; - self.setTrackData = function(track_id, data) { - var id_accessor = self.getTrackDatumIdAccessor(track_id); - - self.tracks[track_id].data = data; - self.id_order = self.id_order.concat(_.difference(_.map(data, id_accessor), self.id_order)); - - self.tracks[track_id].id_data_map = {}; - var id_data_map = self.tracks[track_id].id_data_map; - _.each(self.tracks[track_id].data, function(datum) { - id_data_map[id_accessor(datum)] = datum; - }); - $(self).trigger(events.SET_TRACK_DATA, {track_id: track_id}); - }; - self.getTrackDatum = function(track_id, datum_id) { - return self.tracks[track_id].id_data_map[datum_id]; - }; + window.Oncoprint = (function() { + var events = window.oncoprint_events; + var utils = window.oncoprint_utils; + var RuleSet = window.oncoprint_RuleSet; + + var defaultOncoprintConfig = { + cell_width: 6, + cell_padding: 3, + legend: true, + }; + + var hiddenOncoprintConfig = { + pre_track_padding: 0, + }; + + var defaultTrackConfig = { + label: 'Gene', + datum_id_key: 'sample', + cell_height: 23, + track_height: 20, + track_padding: 5, + sort_cmp: undefined, + tooltip: function(d) { + return d['sample']; + } + }; - self.getTrackDatumIdAccessor = function(track_id) { - var key = self.getTrackDatumIdKey(track_id); - return function(d) { - return d[key]; + function Oncoprint(config) { + var self = this; + var track_id_counter = 0; + self.config = config; + self.id_order = []; + self.track_order = []; + self.tracks = {}; + self.ids = {}; + + self.getCellWidth = function() { + return self.config.cell_width; }; - }; - self.getTrackDatumIdKey = function(track_id) { - return self.tracks[track_id].config.datum_id_key; - }; + self.getCellPadding = function() { + return self.config.cell_padding; + }; + self.getCellHeight = function(track_id) { + return self.tracks[track_id].config.cell_height; + }; + self.getTrackHeight = function(track_id) { + return self.tracks[track_id].config.track_height; + }; + self.getTrackPadding = function(track_id) { + return self.tracks[track_id].config.track_padding; + }; + self.getIdOrder = function() { + return self.id_order; + }; + self.setIdOrder = function(id_order) { + self.id_order = id_order; + $(self).trigger(events.SET_ID_ORDER); + }; + self.getTrackOrder = function() { + return self.track_order.slice(); + }; + self.getTrackLabel = function(track_id) { + return self.tracks[track_id].config.label; + }; + self.getTrackData = function(track_id) { + return self.tracks[track_id].data; + }; + self.getTrackTooltip = function(track_id) { + return self.tracks[track_id].config.tooltip; + }; + self.setTrackData = function(track_id, data) { + var id_accessor = self.getTrackDatumIdAccessor(track_id); - self.removeTrack = function(track_id) { - var track = self.tracks[track_id]; - delete self.tracks[track_id]; + self.tracks[track_id].data = data; + self.id_order = self.id_order.concat(_.difference(_.map(data, id_accessor), self.id_order)); - var oldPosition = self.track_order.indexOf(track_id); - self.track_order.splice(oldPosition, 1); + self.tracks[track_id].id_data_map = {}; + var id_data_map = self.tracks[track_id].id_data_map; + _.each(self.tracks[track_id].data, function(datum) { + id_data_map[id_accessor(datum)] = datum; + }); + $(self).trigger(events.SET_TRACK_DATA, {track_id: track_id}); + }; + self.getTrackDatum = function(track_id, datum_id) { + return self.tracks[track_id].id_data_map[datum_id]; + }; - $(self).trigger(events.REMOVE_TRACK, {track: track, track_id: track_id}); - return true; - }; - self.moveTrack = function(track_id, new_position) { - new_position = Math.min(self.track_order.length-1, new_position); - new_position = Math.max(0, new_position); - var old_position = self.track_order.indexOf(track_id); - - var new_order = self.track_order.slice(); - var i; - var moved_tracks = []; - if (old_position > new_position) { - for (i=new_position+1; i<=old_position; i++) { - new_order[i] = self.track_order[i-1]; - moved_tracks.push(self.track_order[i-1]); - } - moved_tracks.push(track_id); - } else if (old_position < new_position) { - for (i=old_position; i new_position) { + for (i=new_position+1; i<=old_position; i++) { + new_order[i] = self.track_order[i-1]; + moved_tracks.push(self.track_order[i-1]); } - } - if (new_bounds.last > this.last) { - for (i=this.last + 1; i <= new_bounds.last; i++) { - ret.push(i); + moved_tracks.push(track_id); + } else if (old_position < new_position) { + for (i=old_position; i this.first) { - for (i=this.first; i < new_bounds.first; i++) { - ret.push(i); - } - } - if (new_bounds.last < this.last) { - for (i=new_bounds.last+1; i <= this.last; i++) { - ret.push(i); + self.addTrack = function(config) { + var track_id = track_id_counter; + track_id_counter += 1; + self.tracks[track_id] ={id: track_id, data: [], config: $.extend({}, defaultTrackConfig, config)}; + self.track_order.push(track_id); + + $(self).trigger(events.ADD_TRACK, {track_id: track_id}); + return track_id; + }; + + self.setCellWidth = function(w) { + self.config.cell_width = w; + $(self).trigger(events.SET_CELL_WIDTH); + }; + self.setCellPadding = function(p) { + self.config.cell_padding = p; + $(self).trigger(events.SET_CELL_PADDING); + }; + + self.sort = function(track_id_list, cmp_list) { + track_id_list = [].concat(track_id_list); + cmp_list = [].concat(cmp_list); + var lexicographically_ordered_cmp = function(id1,id2) { + var cmp_result; + for (var i=0, _len = track_id_list.length; i<_len; i++) { + cmp_result = cmp_list[i](self.getTrackDatum(track_id_list[i], id1),self.getTrackDatum(track_id_list[i], id2)); + if (cmp_result !== 0) { + break; + } } - } + return cmp_result; + }; + self.setIdOrder(utils.stableSort(self.getIdOrder(), lexicographically_ordered_cmp)); + $(self).trigger(events.SORT, {id_order: self.id_order}); + }; + } + + var OncoprintRenderer = (function() { + function OncoprintRenderer(oncoprint, config) { + this.rule_sets = {}; + this.clipping = true; + this.oncoprint = oncoprint; + this.config = config; + }; + OncoprintRenderer.prototype.getCellCSSClass = function() { + return 'cell'; + }; + OncoprintRenderer.prototype.getTrackCellCSSClass = function(track_id) { + return 'cell'+track_id; + }; + OncoprintRenderer.prototype.getLabelFont = function() { + return this.config.label_font; + }; + OncoprintRenderer.prototype.setRuleSet = function(track_id, type, params) { + var new_rule_set = RuleSet.makeRuleSet(type, params); + this.rule_sets[track_id] = new_rule_set; + }; + OncoprintRenderer.prototype.useSameRuleSet = function(target_track_id, source_track_id) { + this.rule_sets[target_track_id] = this.rule_sets[source_track_id]; + }; + OncoprintRenderer.prototype.getRuleSet = function(track_id) { + return this.rule_sets[track_id]; + }; + OncoprintRenderer.prototype.getTrackTops = function() { + var ret = {}; + var y = 0; + var self = this; + _.each(this.oncoprint.getTrackOrder(), function(id) { + ret[id] = y; + y += self.getRenderedTrackHeight(id); + }); return ret; }; - this.set = function(first, last) { - this.first = first; - this.last = last; - return this; + OncoprintRenderer.prototype.getCellTops = function() { + var tops = this.getTrackTops(); + var self = this; + _.each(tops, function(top, id) { + tops[id] = top + self.oncoprint.getTrackPadding(id); + }); + return tops; }; - this.fromViewInterval = function(interval, cell_unit) { - this.first = Math.floor(interval[0]/cell_unit); - this.last = Math.ceil(interval[1]/cell_unit); - return this; + OncoprintRenderer.prototype.getLabelTops = function() { + return this.getCellTops(); }; - } - function OncoprintSVGRenderer(container_selector_string, oncoprint, config) { - OncoprintRenderer.call(this, oncoprint, config); - var self = this; - this.toolbar_container; - this.label_svg; - this.label_container; - this.cell_container; - this.cell_container_node; - this.cell_div; - this.legend_svg; - this.cells = {}; - this.curr_clip_bounds = new VisibleIndexBounds(-1, -2); - this.prev_clip_bounds = new VisibleIndexBounds(-1, -2); - - this.clip_zone_start = 0; - - (function initToolbarContainer() { - self.toolbar_container = d3.select(container_selector_string).append('div').classed('toolbar_container', true); - d3.select(container_selector_string).append('br'); - /*$.ajax({url: "toolbar.html", context: document.body, success: function(response) { - $(self.toolbar_container.node()).html(response); - }});*/ - })(); - (function initLabelContainer() { - self.label_container = d3.select(container_selector_string).append('div').classed('fixed_oncoprint_section_container', true); - self.label_svg = self.label_container.append('svg'); - $(self.label_svg.node()).on("mousedown", function(evt) { - // TODO: fix this shit UP - var in_track = -1; - var track_tops = self.getTrackTops(); - var mouse_y = evt.clientY - self.label_svg.node().offsetTop; - _.find(self.oncoprint.getTrackOrder(), function(id) { - if (mouse_y >= track_tops[id] && mouse_y <= track_tops[id] + self.getRenderedTrackHeight(id)) { - in_track = id; - return true; + OncoprintRenderer.prototype.getRenderedTrackHeight = function(track_id) { + return this.oncoprint.getTrackHeight(track_id) + 2*this.oncoprint.getTrackPadding(track_id); + }; + OncoprintRenderer.prototype.getCellX = function(index) { + return index*(this.oncoprint.getCellWidth()+this.oncoprint.getCellPadding()); + }; + OncoprintRenderer.prototype.getCellAreaWidth = function() { + return this.oncoprint.getIdOrder().length*(this.oncoprint.getCellWidth() + this.oncoprint.getCellPadding()); + }; + OncoprintRenderer.prototype.getCellAreaHeight = function() { + var track_tops = this.getTrackTops(); + var track_order = this.oncoprint.getTrackOrder(); + var last_track = track_order[track_order.length-1]; + return track_tops[last_track] + this.getRenderedTrackHeight(last_track); + }; + OncoprintRenderer.prototype.getLabelAreaWidth = function() { + var label_font = this.getLabelFont(); + var labels = _.map(this.oncoprint.getTrackOrder(), this.oncoprint.getTrackLabel); + var label_widths = _.map(labels, function(label) { + return utils.textWidth(label, label_font); + }); + var max_label_width = Math.max(_.max(label_widths), 0); + var max_percent_altered_width = utils.textWidth('100%', label_font); + var buffer_width = 20; + return max_label_width + buffer_width + max_percent_altered_width ; + }; + OncoprintRenderer.prototype.getLabelAreaHeight = function() { + return this.getCellAreaHeight(); + }; + OncoprintRenderer.prototype.render = function() { + throw "not implemented in abstract class"; + } + return OncoprintRenderer; + })(); + + var OncoprintSVGRenderer = (function() { + function VisibleIndexBounds(first, last) { + this.first = first; + this.last = last; + this.toShow = function(new_bounds) { + var ret = []; + var i; + if (new_bounds.first < this.first) { + for (i=new_bounds.first; i < this.first; i++) { + ret.push(i); + } + } + if (new_bounds.last > this.last) { + for (i=this.last + 1; i <= new_bounds.last; i++) { + ret.push(i); + } + } + return ret; + }; + this.toHide = function(new_bounds) { + var ret = []; + var i; + if (new_bounds.first > this.first) { + for (i=this.first; i < new_bounds.first; i++) { + ret.push(i); } + } + if (new_bounds.last < this.last) { + for (i=new_bounds.last+1; i <= this.last; i++) { + ret.push(i); + } + } + return ret; + }; + this.set = function(first, last) { + this.first = first; + this.last = last; + return this; + }; + this.fromViewInterval = function(interval, cell_unit) { + this.first = Math.floor(interval[0]/cell_unit); + this.last = Math.ceil(interval[1]/cell_unit); + return this; + }; + } + function OncoprintSVGRenderer(container_selector_string, oncoprint, config) { + OncoprintRenderer.call(this, oncoprint, config); + var self = this; + this.toolbar_container; + this.label_svg; + this.label_container; + this.cell_container; + this.cell_container_node; + this.cell_div; + this.legend_svg; + this.cells = {}; + this.curr_clip_bounds = new VisibleIndexBounds(-1, -2); + this.prev_clip_bounds = new VisibleIndexBounds(-1, -2); + + this.clip_zone_start = 0; + + (function initToolbarContainer() { + self.toolbar_container = d3.select(container_selector_string).append('div').classed('toolbar_container', true); + d3.select(container_selector_string).append('br'); + /*$.ajax({url: "toolbar.html", context: document.body, success: function(response) { + $(self.toolbar_container.node()).html(response); + }});*/ + })(); + (function initLabelContainer() { + self.label_container = d3.select(container_selector_string).append('div').classed('fixed_oncoprint_section_container', true); + self.label_svg = self.label_container.append('svg'); + $(self.label_svg.node()).on("mousedown", function(evt) { + // TODO: fix this shit UP + var in_track = -1; + var track_tops = self.getTrackTops(); + var mouse_y = evt.clientY - self.label_svg.node().offsetTop; + _.find(self.oncoprint.getTrackOrder(), function(id) { + if (mouse_y >= track_tops[id] && mouse_y <= track_tops[id] + self.getRenderedTrackHeight(id)) { + in_track = id; + return true; + } + }); + if (in_track > -1) { + self.dragLabel(in_track); + } + }); + })(); + (function initCellContainer() { + self.cell_container = d3.select(container_selector_string).append('div').classed('scrolling_oncoprint_section_container', true); + //self.cell_container.style('display', 'none'); + self.cell_container_node = self.cell_container.node(); + self.cell_div = self.cell_container.append('div').classed('cell_div', true); + + $(self.cell_container.node()).on('scroll', function() { + self.clipCells(); }); - if (in_track > -1) { - self.dragLabel(in_track); + })(); + (function initLegend() { + if (config.legend) { + self.legend_svg = d3.select(container_selector_string).append('svg'); } + })(); + + var render_all_events = [events.REMOVE_TRACK]; + var render_track_events = [events.ADD_TRACK, events.SET_TRACK_DATA]; + var reposition_events = [events.MOVE_TRACK, events.SET_CELL_PADDING, events.SET_CELL_WIDTH]; + var resize_cell_div_events = [events.SET_CELL_PADDING, events.SET_CELL_WIDTH]; + var reclip_events = [events.SET_CELL_PADDING, events.SET_CELL_WIDTH]; + var reposition_then_reclip_events = [events.SET_ID_ORDER]; + $(oncoprint).on(resize_cell_div_events.join(" "), function() { + self.resizeCellDiv(); }); - })(); - (function initCellContainer() { - self.cell_container = d3.select(container_selector_string).append('div').classed('scrolling_oncoprint_section_container', true); - //self.cell_container.style('display', 'none'); - self.cell_container_node = self.cell_container.node(); - self.cell_div = self.cell_container.append('div').classed('cell_div', true); - - $(self.cell_container.node()).on('scroll', function() { + $(oncoprint).on(render_all_events.join(" "), function() { + self.render(); + }); + $(oncoprint).on(render_track_events.join(" "), function(e, d) { + self.render(d.track_id); + }); + $(oncoprint).on(reposition_events.join(" "), function() { + self.positionCells(); + }); + $(oncoprint).on(reclip_events.join(" "), function() { self.clipCells(); }); - })(); - (function initLegend() { - if (config.legend) { - self.legend_svg = d3.select(container_selector_string).append('svg'); - } - })(); - - var render_all_events = [events.REMOVE_TRACK]; - var render_track_events = [events.ADD_TRACK, events.SET_TRACK_DATA]; - var reposition_events = [events.MOVE_TRACK, events.SET_CELL_PADDING, events.SET_CELL_WIDTH]; - var resize_cell_div_events = [events.SET_CELL_PADDING, events.SET_CELL_WIDTH]; - var reclip_events = [events.SET_CELL_PADDING, events.SET_CELL_WIDTH]; - var reposition_then_reclip_events = [events.SET_ID_ORDER]; - $(oncoprint).on(resize_cell_div_events.join(" "), function() { - self.resizeCellDiv(); - }); - $(oncoprint).on(render_all_events.join(" "), function() { - self.render(); - }); - $(oncoprint).on(render_track_events.join(" "), function(e, d) { - self.render(d.track_id); - }); - $(oncoprint).on(reposition_events.join(" "), function() { - self.positionCells(); - }); - $(oncoprint).on(reclip_events.join(" "), function() { - self.clipCells(); - }); - $(oncoprint).on(reposition_then_reclip_events.join(" "), function() { - self.positionCells(); - self.clipCells(true); - }); - $(oncoprint).on(events.MOVE_TRACK, function(evt, data) { - // TODO: only reposition tracks that have been moved as a result - this is a fairly slow op so necessary opt - self.positionCells(data.moved_tracks); - self.renderLabels(); - }) - } - utils.extends(OncoprintSVGRenderer, OncoprintRenderer); - - // Rule sets - OncoprintSVGRenderer.prototype.setRuleSet = function(track_id, type, params) { - OncoprintRenderer.prototype.setRuleSet.call(this, track_id, type, params); - this.render(track_id); - }; - OncoprintSVGRenderer.prototype.useSameRuleSet = function(target_track_id, source_track_id) { - OncoprintRenderer.prototype.useSameRuleSet.call(this, target_track_id, source_track_id); - this.render(target_track_id); - } + $(oncoprint).on(reposition_then_reclip_events.join(" "), function() { + self.positionCells(); + self.clipCells(true); + }); + $(oncoprint).on(events.MOVE_TRACK, function(evt, data) { + // TODO: only reposition tracks that have been moved as a result - this is a fairly slow op so necessary opt + self.positionCells(data.moved_tracks); + self.renderLabels(); + }) + } + utils.extends(OncoprintSVGRenderer, OncoprintRenderer); - // Containers - OncoprintSVGRenderer.prototype.getLabelSVG = function() { - return this.label_svg; - }; - OncoprintSVGRenderer.prototype.resizeCellDiv = function() { - this.cell_div.style('min-width', this.getCellAreaWidth()+'px') - .style('min-height', this.getCellAreaHeight()+'px'); - }; - OncoprintSVGRenderer.prototype.resizeLabelSVG = function() { - this.getLabelSVG().attr('width', this.getLabelAreaWidth()) - .attr('height', this.getLabelAreaHeight()); - }; - OncoprintSVGRenderer.prototype.resizeLegendSVG = function() { - var new_height = 0; - var new_width = 0; - var point = this.legend_svg.node().createSVGPoint(); - utils.d3SelectChildren(this.legend_svg, 'g').each(function() { - point.x = 0; - point.y = 0; - point = point.matrixTransform(this.getCTM()); - var bbox = this.getBBox(); - new_height = Math.max(new_height, point.y+bbox.height); - new_width = Math.max(new_width, point.x + bbox.width); - }); - this.legend_svg.attr('width', new_width).attr('height', new_height); - }; + // Rule sets + OncoprintSVGRenderer.prototype.setRuleSet = function(track_id, type, params) { + OncoprintRenderer.prototype.setRuleSet.call(this, track_id, type, params); + this.render(track_id); + }; + OncoprintSVGRenderer.prototype.useSameRuleSet = function(target_track_id, source_track_id) { + OncoprintRenderer.prototype.useSameRuleSet.call(this, target_track_id, source_track_id); + this.render(target_track_id); + } - // Labels - OncoprintSVGRenderer.prototype.renderTrackLabel = function(oncoprint, track_id, rule_set, svg, label_y) { - var label_class = 'label'+track_id; - label_y = typeof label_y === "undefined" ? this.getLabelTops()[track_id] : label_y; + // Containers + OncoprintSVGRenderer.prototype.getLabelSVG = function() { + return this.label_svg; + }; + OncoprintSVGRenderer.prototype.resizeCellDiv = function() { + this.cell_div.style('min-width', this.getCellAreaWidth()+'px') + .style('min-height', this.getCellAreaHeight()+'px'); + }; + OncoprintSVGRenderer.prototype.resizeLabelSVG = function() { + this.getLabelSVG().attr('width', this.getLabelAreaWidth()) + .attr('height', this.getLabelAreaHeight()); + }; + OncoprintSVGRenderer.prototype.resizeLegendSVG = function() { + var new_height = 0; + var new_width = 0; + var point = this.legend_svg.node().createSVGPoint(); + utils.d3SelectChildren(this.legend_svg, 'g').each(function() { + point.x = 0; + point.y = 0; + point = point.matrixTransform(this.getCTM()); + var bbox = this.getBBox(); + new_height = Math.max(new_height, point.y+bbox.height); + new_width = Math.max(new_width, point.x + bbox.width); + }); + this.legend_svg.attr('width', new_width).attr('height', new_height); + }; - var to_reposition; - if (rule_set) { - svg.selectAll('.'+label_class).remove(); - to_reposition = svg.append('text').classed(label_class, true).text(oncoprint.getTrackLabel(track_id)) + // Labels + OncoprintSVGRenderer.prototype.renderTrackLabel = function(oncoprint, track_id, rule_set, svg, label_y) { + var label_class = 'label'+track_id; + label_y = typeof label_y === "undefined" ? this.getLabelTops()[track_id] : label_y; + + var to_reposition; + if (rule_set) { + svg.selectAll('.'+label_class).remove(); + to_reposition = svg.append('text').classed(label_class, true).text(oncoprint.getTrackLabel(track_id)) + .attr('font', this.getLabelFont()) + .attr('alignment-baseline', 'hanging') + .classed('noselect', true) + } else { + to_reposition = svg.selectAll('.' + label_class); + } + to_reposition.attr('transform', utils.translate(0, label_y)); + + var altered_data_label_class = 'altered_data_label'+track_id; + if (rule_set && rule_set.alteredData && typeof rule_set.alteredData === 'function') { + svg.selectAll('.'+altered_data_label_class).remove(); + var data = oncoprint.getTrackData(track_id); + var num_altered = rule_set.alteredData(data).length; + var percent_altered = Math.floor(100*num_altered/data.length); + to_reposition = svg.append('text').classed(altered_data_label_class, true) + .text(percent_altered+'%') .attr('font', this.getLabelFont()) + .attr('text-anchor', 'end') .attr('alignment-baseline', 'hanging') .classed('noselect', true) - } else { - to_reposition = svg.selectAll('.' + label_class); - } - to_reposition.attr('transform', utils.translate(0, label_y)); + } else { + to_reposition = svg.selectAll('.'+altered_data_label_class) + } + to_reposition.attr('transform', utils.translate(this.getLabelAreaWidth(), label_y)); + return svg.selectAll('.'+label_class+',.'+altered_data_label_class); + }; - var altered_data_label_class = 'altered_data_label'+track_id; - if (rule_set && rule_set.alteredData && typeof rule_set.alteredData === 'function') { - svg.selectAll('.'+altered_data_label_class).remove(); + // Cells + OncoprintSVGRenderer.prototype.drawTrackCells = function(track_id, rule_set) { + var oncoprint = this.oncoprint; var data = oncoprint.getTrackData(track_id); - var num_altered = rule_set.alteredData(data).length; - var percent_altered = Math.floor(100*num_altered/data.length); - to_reposition = svg.append('text').classed(altered_data_label_class, true) - .text(percent_altered+'%') - .attr('font', this.getLabelFont()) - .attr('text-anchor', 'end') - .attr('alignment-baseline', 'hanging') - .classed('noselect', true) - } else { - to_reposition = svg.selectAll('.'+altered_data_label_class) - } - to_reposition.attr('transform', utils.translate(this.getLabelAreaWidth(), label_y)); - return svg.selectAll('.'+label_class+',.'+altered_data_label_class); - }; - - // Cells - OncoprintSVGRenderer.prototype.drawTrackCells = function(track_id, rule_set) { - var oncoprint = this.oncoprint; - var data = oncoprint.getTrackData(track_id); - var id_key = oncoprint.getTrackDatumIdKey(track_id); - var id_accessor = oncoprint.getTrackDatumIdAccessor(track_id); - var self = this; - this.cells[track_id] = this.cells[track_id] || {}; - - var cell_class = this.getCellCSSClass(); - var track_cell_class = this.getTrackCellCSSClass(track_id); - - - var bound_svg = this.cell_div.selectAll('svg.'+track_cell_class).data(data, id_accessor); - bound_svg.enter().append('svg').classed(track_cell_class, true).classed(cell_class, true); - bound_svg.style('width', oncoprint.getCellWidth()).style('height', oncoprint.getCellHeight(track_id)); - var tooltip = this.oncoprint.getTrackTooltip(track_id); - bound_svg.each(function(d,i) { - var dom_cell = this; - var id = id_accessor(d); - if (tooltip) { - var tooltip_html = tooltip(d); - $(dom_cell).one("mouseover", function() { - $(dom_cell).qtip({ - content: { - text: tooltip_html - }, - position: {my:'left bottom', at:'top middle', viewport: $(window)}, - style: { classes: 'cell-qtip', border: 'none'}, - show: {event: "mouseover"}, - hide: {fixed: true, delay: 100, event: "mouseout"} + var id_key = oncoprint.getTrackDatumIdKey(track_id); + var id_accessor = oncoprint.getTrackDatumIdAccessor(track_id); + var self = this; + this.cells[track_id] = this.cells[track_id] || {}; + + var cell_class = this.getCellCSSClass(); + var track_cell_class = this.getTrackCellCSSClass(track_id); + + + var bound_svg = this.cell_div.selectAll('svg.'+track_cell_class).data(data, id_accessor); + bound_svg.enter().append('svg').classed(track_cell_class, true).classed(cell_class, true); + bound_svg.style('width', oncoprint.getCellWidth()).style('height', oncoprint.getCellHeight(track_id)); + var tooltip = this.oncoprint.getTrackTooltip(track_id); + bound_svg.each(function(d,i) { + var dom_cell = this; + var id = id_accessor(d); + if (tooltip) { + var tooltip_html = tooltip(d); + $(dom_cell).one("mouseover", function() { + $(dom_cell).qtip({ + content: { + text: tooltip_html + }, + position: {my:'left bottom', at:'top middle', viewport: $(window)}, + style: { classes: 'cell-qtip', border: 'none'}, + show: {event: "mouseover"}, + hide: {fixed: true, delay: 100, event: "mouseout"} + }); + $(dom_cell).trigger("mouseover"); }); - $(dom_cell).trigger("mouseover"); + } + $(dom_cell).on("mouseover", function() { + d3.select(dom_cell).classed("cell_rollover", true); }); - } - $(dom_cell).on("mouseover", function() { - d3.select(dom_cell).classed("cell_rollover", true); + $(dom_cell).on("mouseout", function() { + d3.select(dom_cell).classed("cell_rollover", false); + }); + self.cells[track_id][id] = this; }); - $(dom_cell).on("mouseout", function() { - d3.select(dom_cell).classed("cell_rollover", false); - }); - self.cells[track_id][id] = this; - }); - bound_svg.selectAll('*').remove(); - rule_set.apply(bound_svg, data, id_accessor, oncoprint.getCellWidth(), oncoprint.getCellHeight(track_id)); - }; - - // Positioning - OncoprintSVGRenderer.prototype.positionTrackCells = function(track_id, bound_svg) { - var oncoprint = this.oncoprint; - if (!bound_svg) { - bound_svg = this.cell_div.selectAll('svg.'+this.getTrackCellCSSClass(track_id)) - .data(oncoprint.getTrackData(track_id), oncoprint.getTrackDatumIdAccessor(track_id)); - } - var self = this; - var id_key = oncoprint.getTrackDatumIdKey(track_id); - var id_order = oncoprint.getIdOrder(); - var y = this.getCellTops()[track_id]; - bound_svg.style('left', function(d,i) { - return self.getCellX(id_order.indexOf(d[id_key])); - }).style('top', y); - }; - OncoprintSVGRenderer.prototype.positionCells = function(track_ids) { - track_ids = track_ids || this.oncoprint.getTrackOrder(); - var self = this; - _.each(track_ids, function(track_id) { - self.positionTrackCells(track_id); - }); - }; + bound_svg.selectAll('*').remove(); + rule_set.apply(bound_svg, data, id_accessor, oncoprint.getCellWidth(), oncoprint.getCellHeight(track_id)); + }; - // Clipping - OncoprintSVGRenderer.prototype.getViewInterval = function() { - var parent = this.cell_container_node; - var parentRect = parent.getBoundingClientRect(); - return {x: parent.scrollLeft, width: parentRect.right - parentRect.left}; - }; - OncoprintSVGRenderer.prototype.getPreviousClipBounds = function() { - return this.prev_clip_bounds; - }; - OncoprintSVGRenderer.prototype.getClipViewInterval = function() { - var self = this; - var view = this.getViewInterval(); - var x = view.x; - var width = view.width; - var clip_buffer = Math.floor(0.05*width); - var clip_zone_size = 3*width; - - var variant=1; - if (variant === 0) { - var section0 = this.clip_zone_start; - var section2 = this.clip_zone_start + 2*width; - - if (x > section2) { - this.clip_zone_start += width; - } else if (x < section0) { - this.clip_zone_start -= width; + // Positioning + OncoprintSVGRenderer.prototype.positionTrackCells = function(track_id, bound_svg) { + var oncoprint = this.oncoprint; + if (!bound_svg) { + bound_svg = this.cell_div.selectAll('svg.'+this.getTrackCellCSSClass(track_id)) + .data(oncoprint.getTrackData(track_id), oncoprint.getTrackDatumIdAccessor(track_id)); } + var self = this; + var id_key = oncoprint.getTrackDatumIdKey(track_id); + var id_order = oncoprint.getIdOrder(); + var y = this.getCellTops()[track_id]; + bound_svg.style('left', function(d,i) { + return self.getCellX(id_order.indexOf(d[id_key])); + }).style('top', y); + }; + OncoprintSVGRenderer.prototype.positionCells = function(track_ids) { + track_ids = track_ids || this.oncoprint.getTrackOrder(); + var self = this; + _.each(track_ids, function(track_id) { + self.positionTrackCells(track_id); + }); + }; - return [this.clip_zone_start, this.clip_zone_start + clip_zone_size]; - } else if (variant === 1) { - var section1 = this.clip_zone_start + width; + // Clipping + OncoprintSVGRenderer.prototype.getViewInterval = function() { + var parent = this.cell_container_node; + var parentRect = parent.getBoundingClientRect(); + return {x: parent.scrollLeft, width: parentRect.right - parentRect.left}; + }; + OncoprintSVGRenderer.prototype.getPreviousClipBounds = function() { + return this.prev_clip_bounds; + }; + OncoprintSVGRenderer.prototype.getClipViewInterval = function() { + var self = this; + var view = this.getViewInterval(); + var x = view.x; + var width = view.width; + var clip_buffer = Math.floor(0.05*width); + var clip_zone_size = 3*width; + + var variant=1; + if (variant === 0) { + var section0 = this.clip_zone_start; + var section2 = this.clip_zone_start + 2*width; + + if (x > section2) { + this.clip_zone_start += width; + } else if (x < section0) { + this.clip_zone_start -= width; + } - while (x > section1 + clip_buffer) { - this.clip_zone_start += clip_buffer; - section1 = this.clip_zone_start + width; - } - while (x < section1 - clip_buffer) { - this.clip_zone_start -= clip_buffer; - section1 = this.clip_zone_start + width; - } - return [this.clip_zone_start, this.clip_zone_start + clip_zone_size]; - } - }; - OncoprintSVGRenderer.prototype.clipCells = function(force) { - var self = this; - var oncoprint = this.oncoprint; - - var id_order = oncoprint.getIdOrder(); - var visible_bounds = this.curr_clip_bounds.fromViewInterval(this.getClipViewInterval(), this.oncoprint.getCellWidth() + this.oncoprint.getCellPadding()); - visible_bounds.first = Math.max(0, visible_bounds.first); - visible_bounds.last = Math.min(id_order.length-1, visible_bounds.last); - var prev_bounds = force ? this.prev_clip_bounds.set(id_order.length, -1) : this.getPreviousClipBounds(); - var to_show = prev_bounds.toShow(visible_bounds); - var to_hide = prev_bounds.toHide(visible_bounds); - if (to_show.length > 0 || to_hide.length > 0) { - var i, len; - for (i=0, len = to_show.length; i < len; i++) { - var datum_id = id_order[to_show[i]]; - _.each(self.cells, function(cell_map) { - var cell = cell_map[datum_id]; - if (cell) { - cell.style.display = 'inherit'; - } - }); + return [this.clip_zone_start, this.clip_zone_start + clip_zone_size]; + } else if (variant === 1) { + var section1 = this.clip_zone_start + width; + + while (x > section1 + clip_buffer) { + this.clip_zone_start += clip_buffer; + section1 = this.clip_zone_start + width; + } + while (x < section1 - clip_buffer) { + this.clip_zone_start -= clip_buffer; + section1 = this.clip_zone_start + width; + } + return [this.clip_zone_start, this.clip_zone_start + clip_zone_size]; } - for (i=0, len = to_hide.length; i < len; i++) { - var datum_id = id_order[to_hide[i]]; - _.each(self.cells, function(cell_map) { - var cell = cell_map[datum_id]; - if (cell) { - cell.style.display = 'none'; - } - }); + }; + OncoprintSVGRenderer.prototype.clipCells = function(force) { + var self = this; + var oncoprint = this.oncoprint; + + var id_order = oncoprint.getIdOrder(); + var visible_bounds = this.curr_clip_bounds.fromViewInterval(this.getClipViewInterval(), this.oncoprint.getCellWidth() + this.oncoprint.getCellPadding()); + visible_bounds.first = Math.max(0, visible_bounds.first); + visible_bounds.last = Math.min(id_order.length-1, visible_bounds.last); + var prev_bounds = force ? this.prev_clip_bounds.set(id_order.length, -1) : this.getPreviousClipBounds(); + var to_show = prev_bounds.toShow(visible_bounds); + var to_hide = prev_bounds.toHide(visible_bounds); + if (to_show.length > 0 || to_hide.length > 0) { + var i, len; + for (i=0, len = to_show.length; i < len; i++) { + var datum_id = id_order[to_show[i]]; + _.each(self.cells, function(cell_map) { + var cell = cell_map[datum_id]; + if (cell) { + cell.style.display = 'inherit'; + } + }); + } + for (i=0, len = to_hide.length; i < len; i++) { + var datum_id = id_order[to_hide[i]]; + _.each(self.cells, function(cell_map) { + var cell = cell_map[datum_id]; + if (cell) { + cell.style.display = 'none'; + } + }); + } } - } - - this.prev_clip_bounds.set(visible_bounds.first, visible_bounds.last); - }; - OncoprintSVGRenderer.prototype.isTrackRenderable = function(track_id) { - return this.getRuleSet(track_id) && this.oncoprint.getTrackData(track_id).length > 0; - }; - OncoprintSVGRenderer.prototype.renderLabels = function() { - var self = this; - _.each(this.oncoprint.getTrackOrder(), function(track_id) { - var rule_set = self.getRuleSet(track_id); - self.renderTrackLabel(self.oncoprint, track_id, rule_set, self.getLabelSVG()); - }); - }; - OncoprintSVGRenderer.prototype.render = function(track_id) { - var self = this; - this.resizeLabelSVG(); - this.resizeCellDiv(); + this.prev_clip_bounds.set(visible_bounds.first, visible_bounds.last); + }; - this.cell_div.style('display', 'none'); - var renderTrack = function(track_id) { - if (self.isTrackRenderable(track_id)) { + OncoprintSVGRenderer.prototype.isTrackRenderable = function(track_id) { + return this.getRuleSet(track_id) && this.oncoprint.getTrackData(track_id).length > 0; + }; + OncoprintSVGRenderer.prototype.renderLabels = function() { + var self = this; + _.each(this.oncoprint.getTrackOrder(), function(track_id) { var rule_set = self.getRuleSet(track_id); - self.drawTrackCells(track_id, rule_set); - self.positionTrackCells(track_id); self.renderTrackLabel(self.oncoprint, track_id, rule_set, self.getLabelSVG()); - } + }); }; - if (typeof track_id !== "undefined") { - renderTrack(track_id); - } else { - _.each(this.oncoprint.getTrackOrder(), function(track_id) { + OncoprintSVGRenderer.prototype.render = function(track_id) { + var self = this; + this.resizeLabelSVG(); + this.resizeCellDiv(); + + this.cell_div.style('display', 'none'); + var renderTrack = function(track_id) { + if (self.isTrackRenderable(track_id)) { + var rule_set = self.getRuleSet(track_id); + self.drawTrackCells(track_id, rule_set); + self.positionTrackCells(track_id); + self.renderTrackLabel(self.oncoprint, track_id, rule_set, self.getLabelSVG()); + } + }; + if (typeof track_id !== "undefined") { renderTrack(track_id); + } else { + _.each(this.oncoprint.getTrackOrder(), function(track_id) { + renderTrack(track_id); + }); + } + self.clipCells(); + this.cell_div.style('display', 'inherit'); + this.renderLegend(); + }; + OncoprintSVGRenderer.prototype.renderLegend = function() { + var svg = this.legend_svg; + svg.selectAll('*').remove(); + var padding = 25; + var y = padding; + var rendered = {}; + var cell_width = this.oncoprint.getCellWidth(); + var self = this; + _.each(this.rule_sets, function(rule_set, track_id) { + var rule_set_id = rule_set.getRuleSetId(); + if (!rendered.hasOwnProperty(rule_set_id)) { + var text = svg.append('text').classed('ruleset_legend_label', true).text(rule_set.getLegendLabel()) + .attr('transform', utils.translate(0,y)); + var group = rule_set.putLegendGroup(svg, cell_width, self.oncoprint.getCellHeight(track_id)); + rendered[rule_set_id] = true; + group.attr('transform', utils.translate(200,y)); + var bounding_box = group.node().getBBox(); + y += bounding_box.height; + y += padding; + } }); + this.resizeLegendSVG(); } - self.clipCells(); - this.cell_div.style('display', 'inherit'); - this.renderLegend(); - }; - OncoprintSVGRenderer.prototype.renderLegend = function() { - var svg = this.legend_svg; - svg.selectAll('*').remove(); - var padding = 25; - var y = padding; - var rendered = {}; - var cell_width = this.oncoprint.getCellWidth(); - var self = this; - _.each(this.rule_sets, function(rule_set, track_id) { - var rule_set_id = rule_set.getRuleSetId(); - if (!rendered.hasOwnProperty(rule_set_id)) { - var text = svg.append('text').classed('ruleset_legend_label', true).text(rule_set.getLegendLabel()) - .attr('transform', utils.translate(0,y)); - var group = rule_set.putLegendGroup(svg, cell_width, self.oncoprint.getCellHeight(track_id)); - rendered[rule_set_id] = true; - group.attr('transform', utils.translate(200,y)); - var bounding_box = group.node().getBBox(); - y += bounding_box.height; - y += padding; - } - }); - this.resizeLegendSVG(); - } - OncoprintSVGRenderer.prototype.dragLabel = function(track_id) { - // TODO: everything about this method is technical debt - var self = this; - var other_tracks = this.oncoprint.getTrackOrder(); - var curr_pos = other_tracks.indexOf(track_id); - other_tracks.splice(curr_pos, 1); - - var new_track_pos; - var track_tops_true = this.getLabelTops(); - delete track_tops_true[track_id]; - var label_area_height = self.getLabelAreaHeight(); - var handler = function(evt) { - var track_tops = $.extend({},{},track_tops_true); - var mouse_y = evt.clientY - self.label_svg.node().offsetTop; - var render_y = utils.clamp(mouse_y, 0, label_area_height); - self.renderTrackLabel(self.oncoprint, track_id, false, self.label_svg, mouse_y).classed('dragging_label', true); - - var first_below = 0; - while (track_tops[other_tracks[first_below]] < render_y && first_below < other_tracks.length) { - first_below += 1; - } - if (first_below > 0) { - track_tops[other_tracks[first_below-1]] -= 3; - } - if (first_below < other_tracks.length) { - track_tops[other_tracks[first_below]] += 3; - } - new_track_pos = first_below; - _.each(track_tops, function(top, id) { - self.renderTrackLabel(self.oncoprint, id, false, self.label_svg, top); + OncoprintSVGRenderer.prototype.dragLabel = function(track_id) { + // TODO: everything about this method is technical debt + var self = this; + var other_tracks = this.oncoprint.getTrackOrder(); + var curr_pos = other_tracks.indexOf(track_id); + other_tracks.splice(curr_pos, 1); + + var new_track_pos; + var track_tops_true = this.getLabelTops(); + delete track_tops_true[track_id]; + var label_area_height = self.getLabelAreaHeight(); + var handler = function(evt) { + var track_tops = $.extend({},{},track_tops_true); + var mouse_y = evt.clientY - self.label_svg.node().offsetTop; + var render_y = utils.clamp(mouse_y, 0, label_area_height); + self.renderTrackLabel(self.oncoprint, track_id, false, self.label_svg, mouse_y).classed('dragging_label', true); + + var first_below = 0; + while (track_tops[other_tracks[first_below]] < render_y && first_below < other_tracks.length) { + first_below += 1; + } + if (first_below > 0) { + track_tops[other_tracks[first_below-1]] -= 3; + } + if (first_below < other_tracks.length) { + track_tops[other_tracks[first_below]] += 3; + } + new_track_pos = first_below; + _.each(track_tops, function(top, id) { + self.renderTrackLabel(self.oncoprint, id, false, self.label_svg, top); + }); + }; + $(self.label_svg.node()).on("mousemove", handler); + $(self.label_svg.node()).on("mouseup", function(evt) { + $(self.label_svg.node()).off("mousemove", handler); + self.oncoprint.moveTrack(track_id, new_track_pos); }); }; - $(self.label_svg.node()).on("mousemove", handler); - $(self.label_svg.node()).on("mouseup", function(evt) { - $(self.label_svg.node()).off("mousemove", handler); - self.oncoprint.moveTrack(track_id, new_track_pos); - }); + return OncoprintSVGRenderer; + })(); + + return { + CATEGORICAL_COLOR: RuleSet.CATEGORICAL_COLOR, + GRADIENT_COLOR: RuleSet.GRADIENT_COLOR, + GENETIC_ALTERATION: RuleSet.GENETIC_ALTERATION, + BAR_CHART: RuleSet.BAR_CHART, + create: function CreateOncoprint(container_selector_string, config) { + config = $.extend({}, defaultOncoprintConfig, config || {}); + config = $.extend(config, hiddenOncoprintConfig); + var oncoprint = new Oncoprint(config); + var renderer = new OncoprintSVGRenderer(container_selector_string, oncoprint, {label_font: '12px Arial', legend:config.legend}); + var ret = { + addTrack: function(config) { + var track_id = oncoprint.addTrack(config); + return track_id; + }, + removeTrack: function(track_id) { + oncoprint.removeTrack(track_id); + }, + moveTrack: function(track_id, position) { + oncoprint.moveTrack(track_id, position); + }, + setTrackData: function(track_id, data) { + oncoprint.setTrackData(track_id, data); + }, + setRuleSet: function(track_id, type, params) { + renderer.setRuleSet(track_id, type, params); + }, + useSameRuleSet: function(target_track_id, source_track_id) { + renderer.useSameRuleSet(target_track_id, source_track_id); + }, + setCellPadding: function(p) { + oncoprint.setCellPadding(p); + }, + toSVG: function(ctr) { + return renderer.toSVG(ctr); + }, + sort: function(track_id_list, cmp_list) { + oncoprint.sort(track_id_list, cmp_list); + } + }; + return ret; + } }; - return OncoprintSVGRenderer; -})(); +}(); \ No newline at end of file diff --git a/packages/oncoprintjs/src/js/utils.js b/packages/oncoprintjs/src/js/utils.js index e274404c262..b60b69b35bf 100644 --- a/packages/oncoprintjs/src/js/utils.js +++ b/packages/oncoprintjs/src/js/utils.js @@ -27,134 +27,135 @@ * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ -var _ = require("underscore"); -var $ = require("jquery"); - -var exports = module.exports = {}; - -exports.invert_array = function invert_array(arr) { - return arr.reduce(function(curr, next, index) { - curr[next] = index; - return curr; - }, {}); -}; - -exports.extends = function(child_class, parent_class) { - child_class.prototype = Object.create(parent_class.prototype); - child_class.prototype.constructor = child_class; -}; - -exports.makeIdCounter = function() { - var counter = 0; - return function() { - counter += 1; - return counter; +window.oncoprint_utils = (function() { + + var exports ={}; + + exports.invert_array = function invert_array(arr) { + return arr.reduce(function(curr, next, index) { + curr[next] = index; + return curr; + }, {}); }; -}; - -exports.clamp = function(t, a, b) { - return Math.max(Math.min(b,t), a); -}; - -exports.makeD3SVGElement = function(tag) { - return d3.select(document.createElementNS('http://www.w3.org/2000/svg', tag)); -}; - -exports.appendD3SVGElement = function(elt, target, svg) { - return target.select(function() { - return this.appendChild(elt.node().cloneNode(true)); - }); -}; - -exports.spaceSVGElementsHorizontally = function(group, padding) { - var x = 0; - var elts = exports.d3SelectChildren(group, '*').each(function() { - if (this.tagName === 'defs') { - // don't adjust spacing for a defs element - return; - } - var transform = d3.select(this).attr('transform'); - var y = transform && transform.indexOf("translate") > -1 && parseFloat(transform.split(",")[1], 10); - y = y || 0; - d3.select(this).attr('transform', exports.translate(x, y)); - x += this.getBBox().width; - x += padding; - }); - return group; -}; - -exports.textWidth = function(string, font) { - var obj = $('
'+string+'
') - .css({position: 'absolute', float: 'left', - 'white-space':'nowrap', visibility: 'hidden', - font: font}) - .appendTo($('body')); - var width = obj.width(); - obj.remove(); - return width; -}; - -exports.d3SelectChildren = function(parent, selector) { - return parent.selectAll(selector).filter(function() { - return this.parentNode === parent.node(); - }); -}; - -exports.warn = function(str, context) { - console.log("Oncoprint error in "+context+": "+str); -}; - -exports.stableSort = function(arr, cmp) { - // cmp returns something in [-1,0,1] - - var zipped = []; - _.each(arr, function(val, ind) { - zipped.push([val, ind]); - }); - var stable_cmp = function(a,b) { - var res = cmp(a[0], b[0]); - if (res === 0) { - if (a[1] < b[1]) { - res = -1; - } else if (a[1] > b[1]) { - res = 1; + + exports.extends = function(child_class, parent_class) { + child_class.prototype = Object.create(parent_class.prototype); + child_class.prototype.constructor = child_class; + }; + + exports.makeIdCounter = function() { + var counter = 0; + return function() { + counter += 1; + return counter; + }; + }; + + exports.clamp = function(t, a, b) { + return Math.max(Math.min(b,t), a); + }; + + exports.makeD3SVGElement = function(tag) { + return d3.select(document.createElementNS('http://www.w3.org/2000/svg', tag)); + }; + + exports.appendD3SVGElement = function(elt, target, svg) { + return target.select(function() { + return this.appendChild(elt.node().cloneNode(true)); + }); + }; + + exports.spaceSVGElementsHorizontally = function(group, padding) { + var x = 0; + var elts = exports.d3SelectChildren(group, '*').each(function() { + if (this.tagName === 'defs') { + // don't adjust spacing for a defs element + return; } + var transform = d3.select(this).attr('transform'); + var y = transform && transform.indexOf("translate") > -1 && parseFloat(transform.split(",")[1], 10); + y = y || 0; + d3.select(this).attr('transform', exports.translate(x, y)); + x += this.getBBox().width; + x += padding; + }); + return group; + }; + + exports.textWidth = function(string, font) { + var obj = $('
'+string+'
') + .css({position: 'absolute', float: 'left', + 'white-space':'nowrap', visibility: 'hidden', + font: font}) + .appendTo($('body')); + var width = obj.width(); + obj.remove(); + return width; + }; + + exports.d3SelectChildren = function(parent, selector) { + return parent.selectAll(selector).filter(function() { + return this.parentNode === parent.node(); + }); + }; + + exports.warn = function(str, context) { + console.log("Oncoprint error in "+context+": "+str); + }; + + exports.stableSort = function(arr, cmp) { + // cmp returns something in [-1,0,1] + + var zipped = []; + _.each(arr, function(val, ind) { + zipped.push([val, ind]); + }); + var stable_cmp = function(a,b) { + var res = cmp(a[0], b[0]); + if (res === 0) { + if (a[1] < b[1]) { + res = -1; + } else if (a[1] > b[1]) { + res = 1; + } + } + return res; + }; + zipped.sort(stable_cmp); + return _.map(zipped, function(x) { return x[0];}); + }; + + exports.lin_interp = function(t, a, b) { + if (a[0] === '#') { + var r = [parseInt(a.substring(1,3), 16), parseInt(b.substring(1,3), 16)]; + var g = [parseInt(a.substring(3,5), 16), parseInt(b.substring(3,5), 16)]; + var b = [parseInt(a.substring(5,7), 16), parseInt(b.substring(5,7), 16)]; + var R = Math.round(r[0]*(1-t) + r[1]*t).toString(16); + var G = Math.round(g[0]*(1-t) + g[1]*t).toString(16); + var B = Math.round(b[0]*(1-t) + b[1]*t).toString(16); + + R = R.length < 2 ? '0'+R : R; + G = G.length < 2 ? '0'+G : G; + B = B.length < 2 ? '0'+B : B; + + return '#' + R + G + B; + } else if (isNaN(a) && a.indexOf('%') > -1) { + var A = parseFloat(a, 10); + var B = parseFloat(b, 10); + return (A*(1-t) + B*t)+'%'; + } else { + return a*(1-t) + b*t; } - return res; }; - zipped.sort(stable_cmp); - return _.map(zipped, function(x) { return x[0];}); -}; - -exports.lin_interp = function(t, a, b) { - if (a[0] === '#') { - var r = [parseInt(a.substring(1,3), 16), parseInt(b.substring(1,3), 16)]; - var g = [parseInt(a.substring(3,5), 16), parseInt(b.substring(3,5), 16)]; - var b = [parseInt(a.substring(5,7), 16), parseInt(b.substring(5,7), 16)]; - var R = Math.round(r[0]*(1-t) + r[1]*t).toString(16); - var G = Math.round(g[0]*(1-t) + g[1]*t).toString(16); - var B = Math.round(b[0]*(1-t) + b[1]*t).toString(16); - - R = R.length < 2 ? '0'+R : R; - G = G.length < 2 ? '0'+G : G; - B = B.length < 2 ? '0'+B : B; - - return '#' + R + G + B; - } else if (isNaN(a) && a.indexOf('%') > -1) { - var A = parseFloat(a, 10); - var B = parseFloat(b, 10); - return (A*(1-t) + B*t)+'%'; - } else { - return a*(1-t) + b*t; - } -}; -exports.translate = function(x,y) { - return "translate(" + x + "," + y + ")"; -}; + exports.translate = function(x,y) { + return "translate(" + x + "," + y + ")"; + }; -exports.assert = function(bool, msg) { - if (!bool) { - throw msg; + exports.assert = function(bool, msg) { + if (!bool) { + throw msg; + } } -} + return exports; +})(); From b24951e443af20851bdc81379fbf585d0e1087ff Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Mon, 29 Jun 2015 15:36:47 -0400 Subject: [PATCH 082/343] merging changes from trying it in production --- packages/oncoprintjs/src/js/RuleSet.js | 36 +------ packages/oncoprintjs/src/js/oncoprint.js | 114 ++++++++++++----------- packages/oncoprintjs/src/js/utils.js | 3 +- 3 files changed, 68 insertions(+), 85 deletions(-) diff --git a/packages/oncoprintjs/src/js/RuleSet.js b/packages/oncoprintjs/src/js/RuleSet.js index 2308734ee97..66cb7e87ca3 100644 --- a/packages/oncoprintjs/src/js/RuleSet.js +++ b/packages/oncoprintjs/src/js/RuleSet.js @@ -1,34 +1,6 @@ -/* - * Copyright (c) 2015 Memorial Sloan-Kettering Cancer Center. - * - * This library is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS - * FOR A PARTICULAR PURPOSE. The software and documentation provided hereunder - * is on an "as is" basis, and Memorial Sloan-Kettering Cancer Center has no - * obligations to provide maintenance, support, updates, enhancements or - * modifications. In no event shall Memorial Sloan-Kettering Cancer Center be - * liable to any party for direct, indirect, special, incidental or - * consequential damages, including lost profits, arising out of the use of this - * software and its documentation, even if Memorial Sloan-Kettering Cancer - * Center has been advised of the possibility of such damage. - */ - -/* - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ -window.oncoprint_RuleSet = (function(){ - var utils = window.oncoprint_utils; +window.oncoprint_RuleSet = (function() { + var utils = oncoprint_utils; + var CATEGORICAL_COLOR = 0; var GRADIENT_COLOR = 1; var GENETIC_ALTERATION = 2; @@ -111,6 +83,7 @@ window.oncoprint_RuleSet = (function(){ legend_label: category }); }; + params.color = params.color || {}; _.each(params.color, function(color, category) { addColorRule(color, category); }); @@ -477,6 +450,7 @@ window.oncoprint_RuleSet = (function(){ } D3SVGStaticRule.prototype = Object.create(D3SVGRule.prototype); + return { CATEGORICAL_COLOR: CATEGORICAL_COLOR, GRADIENT_COLOR: GRADIENT_COLOR, diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index c8c366ec8e3..87788d65848 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -27,10 +27,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ - window.Oncoprint = (function() { - var events = window.oncoprint_events; - var utils = window.oncoprint_utils; - var RuleSet = window.oncoprint_RuleSet; +window.Oncoprint = (function() { + var events = oncoprint_events; + var utils = oncoprint_utils; + var RuleSet = oncoprint_RuleSet; var defaultOncoprintConfig = { cell_width: 6, @@ -54,6 +54,8 @@ } }; + + function Oncoprint(config) { var self = this; var track_id_counter = 0; @@ -339,6 +341,7 @@ this.clip_zone_start = 0; + d3.select(container_selector_string).selectAll('*').remove(); (function initToolbarContainer() { self.toolbar_container = d3.select(container_selector_string).append('div').classed('toolbar_container', true); d3.select(container_selector_string).append('br'); @@ -353,7 +356,7 @@ // TODO: fix this shit UP var in_track = -1; var track_tops = self.getTrackTops(); - var mouse_y = evt.clientY - self.label_svg.node().offsetTop; + var mouse_y = evt.offsetY; _.find(self.oncoprint.getTrackOrder(), function(id) { if (mouse_y >= track_tops[id] && mouse_y <= track_tops[id] + self.getRenderedTrackHeight(id)) { in_track = id; @@ -381,12 +384,17 @@ } })(); - var render_all_events = [events.REMOVE_TRACK]; + var render_all_events = []; var render_track_events = [events.ADD_TRACK, events.SET_TRACK_DATA]; var reposition_events = [events.MOVE_TRACK, events.SET_CELL_PADDING, events.SET_CELL_WIDTH]; var resize_cell_div_events = [events.SET_CELL_PADDING, events.SET_CELL_WIDTH]; var reclip_events = [events.SET_CELL_PADDING, events.SET_CELL_WIDTH]; var reposition_then_reclip_events = [events.SET_ID_ORDER]; + $(oncoprint).on(events.REMOVE_TRACK, function(evt, data) { + delete self.cells[data.track_id]; + delete self.rule_sets[data.track_id]; + self.render(); + }); $(oncoprint).on(resize_cell_div_events.join(" "), function() { self.resizeCellDiv(); }); @@ -445,7 +453,7 @@ point.y = 0; point = point.matrixTransform(this.getCTM()); var bbox = this.getBBox(); - new_height = Math.max(new_height, point.y+bbox.height); + new_height = Math.max(new_height, point.y + bbox.height); new_width = Math.max(new_width, point.x + bbox.width); }); this.legend_svg.attr('width', new_width).attr('height', new_height); @@ -674,6 +682,9 @@ OncoprintSVGRenderer.prototype.renderLegend = function() { var svg = this.legend_svg; svg.selectAll('*').remove(); + // + svg.attr('width', 10000).attr('height', 10000); + // var padding = 25; var y = padding; var rendered = {}; @@ -707,7 +718,7 @@ var label_area_height = self.getLabelAreaHeight(); var handler = function(evt) { var track_tops = $.extend({},{},track_tops_true); - var mouse_y = evt.clientY - self.label_svg.node().offsetTop; + var mouse_y = evt.offsetY; var render_y = utils.clamp(mouse_y, 0, label_area_height); self.renderTrackLabel(self.oncoprint, track_id, false, self.label_svg, mouse_y).classed('dragging_label', true); @@ -734,48 +745,47 @@ }; return OncoprintSVGRenderer; })(); - return { - CATEGORICAL_COLOR: RuleSet.CATEGORICAL_COLOR, - GRADIENT_COLOR: RuleSet.GRADIENT_COLOR, - GENETIC_ALTERATION: RuleSet.GENETIC_ALTERATION, - BAR_CHART: RuleSet.BAR_CHART, - create: function CreateOncoprint(container_selector_string, config) { - config = $.extend({}, defaultOncoprintConfig, config || {}); - config = $.extend(config, hiddenOncoprintConfig); - var oncoprint = new Oncoprint(config); - var renderer = new OncoprintSVGRenderer(container_selector_string, oncoprint, {label_font: '12px Arial', legend:config.legend}); - var ret = { - addTrack: function(config) { - var track_id = oncoprint.addTrack(config); - return track_id; - }, - removeTrack: function(track_id) { - oncoprint.removeTrack(track_id); - }, - moveTrack: function(track_id, position) { - oncoprint.moveTrack(track_id, position); - }, - setTrackData: function(track_id, data) { - oncoprint.setTrackData(track_id, data); - }, - setRuleSet: function(track_id, type, params) { - renderer.setRuleSet(track_id, type, params); - }, - useSameRuleSet: function(target_track_id, source_track_id) { - renderer.useSameRuleSet(target_track_id, source_track_id); - }, - setCellPadding: function(p) { - oncoprint.setCellPadding(p); - }, - toSVG: function(ctr) { - return renderer.toSVG(ctr); - }, - sort: function(track_id_list, cmp_list) { - oncoprint.sort(track_id_list, cmp_list); - } - }; - return ret; - } - }; -}(); \ No newline at end of file + CATEGORICAL_COLOR: RuleSet.CATEGORICAL_COLOR, + GRADIENT_COLOR: RuleSet.GRADIENT_COLOR, + GENETIC_ALTERATION: RuleSet.GENETIC_ALTERATION, + BAR_CHART: RuleSet.BAR_CHART, + create: function CreateOncoprint(container_selector_string, config) { + config = $.extend({}, defaultOncoprintConfig, config || {}); + config = $.extend(config, hiddenOncoprintConfig); + var oncoprint = new Oncoprint(config); + var renderer = new OncoprintSVGRenderer(container_selector_string, oncoprint, {label_font: '12px Arial', legend:config.legend}); + var ret = { + addTrack: function(config) { + var track_id = oncoprint.addTrack(config); + return track_id; + }, + removeTrack: function(track_id) { + oncoprint.removeTrack(track_id); + }, + moveTrack: function(track_id, position) { + oncoprint.moveTrack(track_id, position); + }, + setTrackData: function(track_id, data) { + oncoprint.setTrackData(track_id, data); + }, + setRuleSet: function(track_id, type, params) { + renderer.setRuleSet(track_id, type, params); + }, + useSameRuleSet: function(target_track_id, source_track_id) { + renderer.useSameRuleSet(target_track_id, source_track_id); + }, + setCellPadding: function(p) { + oncoprint.setCellPadding(p); + }, + toSVG: function(ctr) { + return renderer.toSVG(ctr); + }, + sort: function(track_id_list, cmp_list) { + oncoprint.sort(track_id_list, cmp_list); + } + }; + return ret; + } +}; +})(); diff --git a/packages/oncoprintjs/src/js/utils.js b/packages/oncoprintjs/src/js/utils.js index b60b69b35bf..a0116a765e6 100644 --- a/packages/oncoprintjs/src/js/utils.js +++ b/packages/oncoprintjs/src/js/utils.js @@ -28,8 +28,7 @@ * along with this program. If not, see . */ window.oncoprint_utils = (function() { - - var exports ={}; + var exports = {}; exports.invert_array = function invert_array(arr) { return arr.reduce(function(curr, next, index) { From 7e1b09a86a7021a4456fc84c8c5d908d8cbb403e Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Mon, 29 Jun 2015 15:59:10 -0400 Subject: [PATCH 083/343] update test page for new situation --- packages/oncoprintjs/index.html | 53 +++++++++++++++++++++++ packages/oncoprintjs/test/index.html | 11 ++++- packages/oncoprintjs/test/js/test_page.js | 11 ++--- 3 files changed, 65 insertions(+), 10 deletions(-) create mode 100644 packages/oncoprintjs/index.html diff --git a/packages/oncoprintjs/index.html b/packages/oncoprintjs/index.html new file mode 100644 index 00000000000..284360f3d62 --- /dev/null +++ b/packages/oncoprintjs/index.html @@ -0,0 +1,53 @@ + + + +Oncoprint Test Page + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index 6d6cfa9afb6..53a3f8ce331 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -1,8 +1,3 @@ -var _ = require('underscore'); -var d3 = require('d3'); - -var globals = require('../../src/js/globals'); -var Oncoprint = require('../../src/js/Oncoprint'); var cell_padding = 3; var whitespace_on = true; @@ -43,15 +38,15 @@ $('#toggle_whitespace').click(function() { var gender_data; var gender_track_id; -var gender_data_promise = $.getJSON('./gbm/gender-gbm.json'); +var gender_data_promise = $.getJSON('dist/test/gbm/gender-gbm.json'); var mutation_data; var mutation_track_id; -var mutation_data_promise = $.getJSON('./gbm/mutations-gbm.json'); +var mutation_data_promise = $.getJSON('dist/test/gbm/mutations-gbm.json'); var alteration_data; var alteration_track_id; -var alteration_data_promise = $.getJSON('./gbm/tp53.json'); +var alteration_data_promise = $.getJSON('dist/test/gbm/tp53.json'); gender_data_promise.then(function(data) { gender_data = data.data; From 4d6ccb97592a361769b65107341aa12276ab0710 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Mon, 29 Jun 2015 17:58:55 -0400 Subject: [PATCH 084/343] adding mrna to genetic alteration rule, and starting thinking about fast zooming --- packages/oncoprintjs/index.html | 1 + packages/oncoprintjs/src/js/RuleSet.js | 41 ++++++++++++++++++---- packages/oncoprintjs/src/js/oncoprint.js | 15 ++++++-- packages/oncoprintjs/src/js/utils.js | 3 ++ packages/oncoprintjs/test/js/test_page.js | 42 +++++++++++++++-------- 5 files changed, 77 insertions(+), 25 deletions(-) diff --git a/packages/oncoprintjs/index.html b/packages/oncoprintjs/index.html index 284360f3d62..bbc3b008e77 100644 --- a/packages/oncoprintjs/index.html +++ b/packages/oncoprintjs/index.html @@ -10,6 +10,7 @@ + diff --git a/packages/oncoprintjs/src/js/RuleSet.js b/packages/oncoprintjs/src/js/RuleSet.js index 66cb7e87ca3..08332855f7b 100644 --- a/packages/oncoprintjs/src/js/RuleSet.js +++ b/packages/oncoprintjs/src/js/RuleSet.js @@ -6,6 +6,8 @@ window.oncoprint_RuleSet = (function() { var GENETIC_ALTERATION = 2; var BAR_CHART = 3; + var CELL = 0; + var getRuleSetId = utils.makeIdCounter(); var D3SVGRuleSet = (function() { @@ -184,12 +186,31 @@ window.oncoprint_RuleSet = (function() { attrs: { width: '100%', height: '33.33%', - y: '33.33%' + y: '33.33%', }, z_index: 1 }); altered_rules.push(new_mut_rule); }); + _.each(params.mrna.color, function(color, name) { + var new_mrna_rule = self.addStaticRule({ + condition: (function(_name) { + return function(d) { + return d[params.mrna_key] === _name; + } + })(name), + shape: CELL, + legend_label: params.mrna.label[name], + styles: { + 'outline-style':'solid', + 'outline-width':'2px', + 'outline-color':color, + }, + z_index: 2 + }); + altered_rules.push(new_mrna_rule); + }); + //_.each() // TODO: mrna, rppa, other stuff? self.putLegendGroup = function(svg, cell_width, cell_height) { var group = svg.append('g'); @@ -218,8 +239,12 @@ window.oncoprint_RuleSet = (function() { this.exclude_from_legend = params.exclude_from_legend; this.attrs = params.attrs || {}; - this.attrs.width = this.attrs.width || '100%'; - this.attrs.height = this.attrs.height || '100%'; + this.attrs.width = utils.ifndef(this.attrs.width, '100%'); + this.attrs.height = utils.ifndef(this.attrs.height, '100%'); + this.attrs.x = utils.ifndef(this.attrs.x, 0); + this.attrs.y = utils.ifndef(this.attrs.y, 0); + + this.styles = params.styles || {}; var percentToPx = function(attr_val, attr_name, cell_width, cell_height) { // convert a percentage to a local pixel coordinate @@ -247,10 +272,9 @@ window.oncoprint_RuleSet = (function() { this.apply = function(g, cell_width, cell_height) { var shape = this.shape; - var elts = utils.appendD3SVGElement(shape, g); - var attrs = this.attrs || {}; - attrs.width = attrs.width || '100%'; - attrs.height = attrs.height || '100%'; + var elts = shape === CELL ? g : utils.appendD3SVGElement(shape, g); + var styles = this.styles; + var attrs = this.attrs; attrs.x = attrs.x || 0; attrs.y = attrs.y || 0; _.each(attrs, function(val, key) { @@ -261,6 +285,9 @@ window.oncoprint_RuleSet = (function() { return convertAttr(d, i, val, key, cell_width, cell_height); }); }); + _.each(styles, function(val, key) { + elts.style(key, val); + }); elts.attr('transform', function(d,i) { var x_val = convertAttr(d, i, attrs.x, 'x', cell_width, cell_height); diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 87788d65848..435300cc8a6 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -173,7 +173,7 @@ window.Oncoprint = (function() { self.setCellWidth = function(w) { self.config.cell_width = w; - $(self).trigger(events.SET_CELL_WIDTH); + $(self).trigger(events.SET_CELL_WIDTH, {cell_width: w}); }; self.setCellPadding = function(p) { self.config.cell_padding = p; @@ -386,10 +386,14 @@ window.Oncoprint = (function() { var render_all_events = []; var render_track_events = [events.ADD_TRACK, events.SET_TRACK_DATA]; - var reposition_events = [events.MOVE_TRACK, events.SET_CELL_PADDING, events.SET_CELL_WIDTH]; + var reposition_events = [events.MOVE_TRACK, events.SET_CELL_PADDING]; var resize_cell_div_events = [events.SET_CELL_PADDING, events.SET_CELL_WIDTH]; var reclip_events = [events.SET_CELL_PADDING, events.SET_CELL_WIDTH]; var reposition_then_reclip_events = [events.SET_ID_ORDER]; + $(oncoprint).on(events.SET_CELL_WIDTH, function(evt, data) { + //self.render(); + self.resizeCells(data.cell_width); + }); $(oncoprint).on(events.REMOVE_TRACK, function(evt, data) { delete self.cells[data.track_id]; delete self.rule_sets[data.track_id]; @@ -415,7 +419,6 @@ window.Oncoprint = (function() { self.clipCells(true); }); $(oncoprint).on(events.MOVE_TRACK, function(evt, data) { - // TODO: only reposition tracks that have been moved as a result - this is a fairly slow op so necessary opt self.positionCells(data.moved_tracks); self.renderLabels(); }) @@ -496,6 +499,9 @@ window.Oncoprint = (function() { }; // Cells + OncoprintSVGRenderer.prototype.resizeCells = function(new_width) { + // todo + }; OncoprintSVGRenderer.prototype.drawTrackCells = function(track_id, rule_set) { var oncoprint = this.oncoprint; var data = oncoprint.getTrackData(track_id); @@ -778,6 +784,9 @@ window.Oncoprint = (function() { setCellPadding: function(p) { oncoprint.setCellPadding(p); }, + setCellWidth: function(w) { + oncoprint.setCellWidth(w); + }, toSVG: function(ctr) { return renderer.toSVG(ctr); }, diff --git a/packages/oncoprintjs/src/js/utils.js b/packages/oncoprintjs/src/js/utils.js index a0116a765e6..3020a339b46 100644 --- a/packages/oncoprintjs/src/js/utils.js +++ b/packages/oncoprintjs/src/js/utils.js @@ -37,6 +37,9 @@ window.oncoprint_utils = (function() { }, {}); }; + exports.ifndef = function(val, replacement) { + return (typeof val === 'undefined') ? replacement : val; + }; exports.extends = function(child_class, parent_class) { child_class.prototype = Object.create(parent_class.prototype); child_class.prototype.constructor = child_class; diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index 53a3f8ce331..58126fdf91f 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -17,6 +17,9 @@ $('#toggle_whitespace').click(function() { } else { onc.setCellPadding(0); } +}); +$('#reduce_cell_width').click(function() { + onc.setCellWidth(3); }); /*$('#to_svg_btn').click(function() { onc.toSVG(d3.select('#svg_container')); @@ -52,20 +55,20 @@ gender_data_promise.then(function(data) { gender_data = data.data; }); $.when(gender_data_promise).then(function() { - gender_track_id = onc.addTrack({label: 'Gender'}); - onc.setRuleSet(gender_track_id, Oncoprint.CATEGORICAL_COLOR, { - color: {}, - getCategory: function(d) { - return d.attr_val; - }, - legend_label: 'Gender' - }); - onc.setTrackData(gender_track_id, gender_data); - for (var i=0; i<8; i++) { - var dup_gender_track_id = onc.addTrack({label: 'Gender'}); - onc.useSameRuleSet(dup_gender_track_id, gender_track_id); - onc.setTrackData(dup_gender_track_id, gender_data); - } + //gender_track_id = onc.addTrack({label: 'Gender'}); + //onc.setRuleSet(gender_track_id, Oncoprint.CATEGORICAL_COLOR, { + //color: {}, + //getCategory: function(d) { + //return d.attr_val; + //}, + //legend_label: 'Gender' + //}); + //onc.setTrackData(gender_track_id, gender_data); + //for (var i=0; i<0; i++) { + // var dup_gender_track_id = onc.addTrack({label: 'Gender'}); + // onc.useSameRuleSet(dup_gender_track_id, gender_track_id); + // onc.setTrackData(dup_gender_track_id, gender_data); + //} }); mutation_data_promise.then(function(data) { @@ -138,7 +141,16 @@ $.when(alteration_data_promise).then(function() { MISSENSE: 'Missense Mutation' } }, - legend_label: 'Genetic Alteration' + legend_label: 'Genetic Alteration', + mrna_key: 'mut_type', + mrna: { + color: { + MISSENSE: '#C9C900' + }, + label: { + MISSENSE: 'MRNA MISSENSE' + } + } }); onc.setTrackData(alteration_track_id, alteration_data); From 7f2fa59a8dd39a85bc4c793bd4d3ca32e556b6a9 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Mon, 29 Jun 2015 18:21:50 -0400 Subject: [PATCH 085/343] fast zooming via svg viewboxes --- packages/oncoprintjs/src/js/events.js | 3 +- packages/oncoprintjs/src/js/oncoprint.js | 59 ++++++++++++++--------- packages/oncoprintjs/test/js/test_page.js | 29 +++++------ 3 files changed, 51 insertions(+), 40 deletions(-) diff --git a/packages/oncoprintjs/src/js/events.js b/packages/oncoprintjs/src/js/events.js index 14acdb0b527..1e616cf474d 100644 --- a/packages/oncoprintjs/src/js/events.js +++ b/packages/oncoprintjs/src/js/events.js @@ -45,5 +45,6 @@ window.oncoprint_events = { TRACK_INIT: 'init.track.oncoprint', UPDATE_RENDER_RULES: 'update_render_rules.cell_renderer.oncoprint', FINISHED_RENDERING: 'finished_rendering.renderer.oncoprint', - FINISHED_POSITIONING: 'finished_positioning.renderer.oncoprint' + FINISHED_POSITIONING: 'finished_positioning.renderer.oncoprint', + SET_ZOOM: 'set_zoom.oncoprint' }; diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 435300cc8a6..35bdda86a98 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -34,7 +34,7 @@ window.Oncoprint = (function() { var defaultOncoprintConfig = { cell_width: 6, - cell_padding: 3, + cell_padding: 5, legend: true, }; @@ -64,12 +64,29 @@ window.Oncoprint = (function() { self.track_order = []; self.tracks = {}; self.ids = {}; + self.zoom = 1; + self.cell_padding_on = true; + self.true_cell_width = self.config.cell_width; + self.toggleCellPadding = function() { + self.cell_padding_on = !self.cell_padding_on; + $(self).trigger(events.SET_CELL_PADDING); + }; + self.getZoom = function() { + return self.zoom; + }; + self.setZoom = function(z) { + self.zoom = z; + $(self).trigger(events.SET_ZOOM); + }; + self.getTrueCellWidth = function() { + return self.true_cell_width; + }; self.getCellWidth = function() { - return self.config.cell_width; + return self.true_cell_width*self.zoom; }; self.getCellPadding = function() { - return self.config.cell_padding; + return self.config.cell_padding*self.zoom*(+self.cell_padding_on); }; self.getCellHeight = function(track_id) { return self.tracks[track_id].config.cell_height; @@ -171,15 +188,6 @@ window.Oncoprint = (function() { return track_id; }; - self.setCellWidth = function(w) { - self.config.cell_width = w; - $(self).trigger(events.SET_CELL_WIDTH, {cell_width: w}); - }; - self.setCellPadding = function(p) { - self.config.cell_padding = p; - $(self).trigger(events.SET_CELL_PADDING); - }; - self.sort = function(track_id_list, cmp_list) { track_id_list = [].concat(track_id_list); cmp_list = [].concat(cmp_list); @@ -385,14 +393,14 @@ window.Oncoprint = (function() { })(); var render_all_events = []; + var resize_cell_events = [events.SET_CELL_WIDTH, events.SET_ZOOM]; var render_track_events = [events.ADD_TRACK, events.SET_TRACK_DATA]; - var reposition_events = [events.MOVE_TRACK, events.SET_CELL_PADDING]; - var resize_cell_div_events = [events.SET_CELL_PADDING, events.SET_CELL_WIDTH]; - var reclip_events = [events.SET_CELL_PADDING, events.SET_CELL_WIDTH]; + var reposition_events = [events.MOVE_TRACK, events.SET_CELL_PADDING, events.SET_ZOOM]; + var resize_cell_div_events = [events.SET_CELL_PADDING, events.SET_CELL_WIDTH, events.SET_ZOOM]; + var reclip_events = [events.SET_CELL_PADDING, events.SET_CELL_WIDTH, events.SET_ZOOM]; var reposition_then_reclip_events = [events.SET_ID_ORDER]; - $(oncoprint).on(events.SET_CELL_WIDTH, function(evt, data) { - //self.render(); - self.resizeCells(data.cell_width); + $(oncoprint).on(resize_cell_events.join(" "), function() { + self.resizeCells(); }); $(oncoprint).on(events.REMOVE_TRACK, function(evt, data) { delete self.cells[data.track_id]; @@ -500,6 +508,7 @@ window.Oncoprint = (function() { // Cells OncoprintSVGRenderer.prototype.resizeCells = function(new_width) { + this.cell_div.selectAll('svg.'+this.getCellCSSClass()).style('width', this.oncoprint.getCellWidth()); // todo }; OncoprintSVGRenderer.prototype.drawTrackCells = function(track_id, rule_set) { @@ -517,6 +526,10 @@ window.Oncoprint = (function() { var bound_svg = this.cell_div.selectAll('svg.'+track_cell_class).data(data, id_accessor); bound_svg.enter().append('svg').classed(track_cell_class, true).classed(cell_class, true); bound_svg.style('width', oncoprint.getCellWidth()).style('height', oncoprint.getCellHeight(track_id)); + bound_svg + .attr('preserveAspectRatio','none') + .attr('viewBox', '0 0 '+oncoprint.getTrueCellWidth()+' '+oncoprint.getCellHeight(track_id)); + var tooltip = this.oncoprint.getTrackTooltip(track_id); bound_svg.each(function(d,i) { var dom_cell = this; @@ -781,17 +794,17 @@ window.Oncoprint = (function() { useSameRuleSet: function(target_track_id, source_track_id) { renderer.useSameRuleSet(target_track_id, source_track_id); }, - setCellPadding: function(p) { - oncoprint.setCellPadding(p); - }, - setCellWidth: function(w) { - oncoprint.setCellWidth(w); + toggleCellPadding: function() { + oncoprint.toggleCellPadding(); }, toSVG: function(ctr) { return renderer.toSVG(ctr); }, sort: function(track_id_list, cmp_list) { oncoprint.sort(track_id_list, cmp_list); + }, + setZoom: function(z) { + oncoprint.setZoom(z); } }; return ret; diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index 58126fdf91f..d646cd85fcd 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -11,15 +11,12 @@ $('#shuffle_btn').click(function() { }); $('#toggle_whitespace').click(function() { - whitespace_on = !whitespace_on; - if (whitespace_on) { - onc.setCellPadding(cell_padding); - } else { - onc.setCellPadding(0); - } + onc.toggleCellPadding(); }); +var z = 1; $('#reduce_cell_width').click(function() { - onc.setCellWidth(3); + z *= 0.9; + onc.setZoom(z); }); /*$('#to_svg_btn').click(function() { onc.toSVG(d3.select('#svg_container')); @@ -55,15 +52,15 @@ gender_data_promise.then(function(data) { gender_data = data.data; }); $.when(gender_data_promise).then(function() { - //gender_track_id = onc.addTrack({label: 'Gender'}); - //onc.setRuleSet(gender_track_id, Oncoprint.CATEGORICAL_COLOR, { - //color: {}, - //getCategory: function(d) { - //return d.attr_val; - //}, - //legend_label: 'Gender' - //}); - //onc.setTrackData(gender_track_id, gender_data); + gender_track_id = onc.addTrack({label: 'Gender'}); + onc.setRuleSet(gender_track_id, Oncoprint.CATEGORICAL_COLOR, { + color: {}, + getCategory: function(d) { + return d.attr_val; + }, + legend_label: 'Gender' + }); + onc.setTrackData(gender_track_id, gender_data); //for (var i=0; i<0; i++) { // var dup_gender_track_id = onc.addTrack({label: 'Gender'}); // onc.useSameRuleSet(dup_gender_track_id, gender_track_id); From d6b1f72fa26c50e544a76d58d580fe5c69a6e7e0 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Mon, 29 Jun 2015 19:39:26 -0400 Subject: [PATCH 086/343] RPPA triangle and proper zoom scaling --- packages/oncoprintjs/src/js/RuleSet.js | 45 +++++++++++++++++------ packages/oncoprintjs/src/js/oncoprint.js | 14 ++++--- packages/oncoprintjs/test/js/test_page.js | 9 ++++- 3 files changed, 51 insertions(+), 17 deletions(-) diff --git a/packages/oncoprintjs/src/js/RuleSet.js b/packages/oncoprintjs/src/js/RuleSet.js index 08332855f7b..173e525ce28 100644 --- a/packages/oncoprintjs/src/js/RuleSet.js +++ b/packages/oncoprintjs/src/js/RuleSet.js @@ -210,7 +210,30 @@ window.oncoprint_RuleSet = (function() { }); altered_rules.push(new_mrna_rule); }); - //_.each() + var up_rppa_rule = self.addStaticRule({ + condition: function(d) { + return d[params.rppa_key] === params.rppa_up; + }, + shape: utils.makeD3SVGElement('polygon').attr('style', 'fill:black; stroke-width:0'), + legend_label: params.rppa_up_label, + attrs: { + points: "50%,0% 100%,25% 0%,25%" + }, + z_index: 3 + }); + altered_rules.push(up_rppa_rule); + var down_rppa_rule = self.addStaticRule({ + condition: function(d) { + return d[params.rppa_key] === params.rppa_down; + }, + shape: utils.makeD3SVGElement('polygon').attr('style', 'fill:black; stroke-width:0'), + legend_label: params.rppa_down_label, + attrs: { + points: "50%,100% 100%,75% 0%,75%" + }, + z_index: 3 + }); + altered_rules.push(down_rppa_rule); // TODO: mrna, rppa, other stuff? self.putLegendGroup = function(svg, cell_width, cell_height) { var group = svg.append('g'); @@ -265,7 +288,16 @@ window.oncoprint_RuleSet = (function() { ret = ret(d,i); } if (typeof ret === 'string' && ret.indexOf('%') > -1) { - ret = percentToPx(ret, attr_name, cell_width, cell_height); + if (attr_name === 'points') { + ret = _.map(ret.split(" "), function(pt) { + var split_pt = pt.split(","); + var pt_x = percentToPx(split_pt[0], 'x', cell_width, cell_height); + var pt_y = percentToPx(split_pt[1], 'y', cell_width, cell_height); + return pt_x+","+pt_y; + }).join(" "); + } else { + ret = percentToPx(ret, attr_name, cell_width, cell_height); + } } return ret; }; @@ -279,21 +311,12 @@ window.oncoprint_RuleSet = (function() { attrs.y = attrs.y || 0; _.each(attrs, function(val, key) { elts.attr(key, function(d,i) { - if (key === 'x' || key === 'y') { - return; - } return convertAttr(d, i, val, key, cell_width, cell_height); }); }); _.each(styles, function(val, key) { elts.style(key, val); }); - - elts.attr('transform', function(d,i) { - var x_val = convertAttr(d, i, attrs.x, 'x', cell_width, cell_height); - var y_val = convertAttr(d, i, attrs.y, 'y', cell_width, cell_height); - return utils.translate(x_val, y_val); - }); } this.filterData = function(data) { return data.filter(this.condition); diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 35bdda86a98..d4ad71abe64 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -59,6 +59,7 @@ window.Oncoprint = (function() { function Oncoprint(config) { var self = this; var track_id_counter = 0; + var MIN_CELL_WIDTH = 0.5; self.config = config; self.id_order = []; self.track_order = []; @@ -75,18 +76,22 @@ window.Oncoprint = (function() { self.getZoom = function() { return self.zoom; }; + self.getZoomMultiplier = function() { + var min_over_max = MIN_CELL_WIDTH/self.getTrueCellWidth(); + return (1-min_over_max)*self.zoom + min_over_max; + }; self.setZoom = function(z) { - self.zoom = z; + self.zoom = utils.clamp(z,0,1); $(self).trigger(events.SET_ZOOM); }; self.getTrueCellWidth = function() { return self.true_cell_width; }; self.getCellWidth = function() { - return self.true_cell_width*self.zoom; + return self.true_cell_width*self.getZoomMultiplier(); }; self.getCellPadding = function() { - return self.config.cell_padding*self.zoom*(+self.cell_padding_on); + return self.config.cell_padding*self.getZoomMultiplier()*(+self.cell_padding_on); }; self.getCellHeight = function(track_id) { return self.tracks[track_id].config.cell_height; @@ -509,7 +514,6 @@ window.Oncoprint = (function() { // Cells OncoprintSVGRenderer.prototype.resizeCells = function(new_width) { this.cell_div.selectAll('svg.'+this.getCellCSSClass()).style('width', this.oncoprint.getCellWidth()); - // todo }; OncoprintSVGRenderer.prototype.drawTrackCells = function(track_id, rule_set) { var oncoprint = this.oncoprint; @@ -572,7 +576,7 @@ window.Oncoprint = (function() { var id_key = oncoprint.getTrackDatumIdKey(track_id); var id_order = oncoprint.getIdOrder(); var y = this.getCellTops()[track_id]; - bound_svg.style('left', function(d,i) { + bound_svg.transition().style('left', function(d,i) { return self.getCellX(id_order.indexOf(d[id_key])); }).style('top', y); }; diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index d646cd85fcd..e99a1c99aa1 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -52,6 +52,7 @@ gender_data_promise.then(function(data) { gender_data = data.data; }); $.when(gender_data_promise).then(function() { + /* gender_track_id = onc.addTrack({label: 'Gender'}); onc.setRuleSet(gender_track_id, Oncoprint.CATEGORICAL_COLOR, { color: {}, @@ -61,6 +62,7 @@ $.when(gender_data_promise).then(function() { legend_label: 'Gender' }); onc.setTrackData(gender_track_id, gender_data); + */ //for (var i=0; i<0; i++) { // var dup_gender_track_id = onc.addTrack({label: 'Gender'}); // onc.useSameRuleSet(dup_gender_track_id, gender_track_id); @@ -147,7 +149,12 @@ $.when(alteration_data_promise).then(function() { label: { MISSENSE: 'MRNA MISSENSE' } - } + }, + rppa_key: 'mut_type', + rppa_down: 'MISSENSE', + rppa_up: 'MISSENSE', + rppa_down_label: 'Protein Downregulation', + rppa_up_label: 'Protein Upregulation' }); onc.setTrackData(alteration_track_id, alteration_data); From 945614cec0dbac4c2cc7c237e3070de86e7548c6 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Tue, 30 Jun 2015 16:38:22 -0400 Subject: [PATCH 087/343] Cluttered commit...CSS, track groups, GA ruleset impl Cleaned up CSS, by changing class names a bit and prepending 'oncoprint-' Implemented track groups, complete with rendering and dragging Implemented more of genetic alteration ruleset --- packages/oncoprintjs/src/css/oncoprint.css | 35 ++-- packages/oncoprintjs/src/js/RuleSet.js | 3 +- packages/oncoprintjs/src/js/events.js | 3 +- packages/oncoprintjs/src/js/oncoprint.js | 232 +++++++++++++-------- packages/oncoprintjs/test/js/test_page.js | 32 +-- 5 files changed, 180 insertions(+), 125 deletions(-) diff --git a/packages/oncoprintjs/src/css/oncoprint.css b/packages/oncoprintjs/src/css/oncoprint.css index d10fdf97934..eb0832fda22 100644 --- a/packages/oncoprintjs/src/css/oncoprint.css +++ b/packages/oncoprintjs/src/css/oncoprint.css @@ -1,44 +1,32 @@ -.toolbar_container { +.oncoprint-toolbar-ctr { display: inline-block; vertical-align: top; float: right; } -.content_container { - display: inline-block; - vertical-align: bottom; -} -.fixed_oncoprint_section_container { +.oncoprint-label-area-ctr { float:left; overflow: auto; width: auto; } -.scrolling_oncoprint_section_container { +.oncoprint-cell-area-ctr { overflow: auto; width: auto; } -.cell_div { +.oncoprint-cell-area { background-color: #fff; position: relative; } -.cell { +.oncoprint-cell { position: absolute; } -.cell_rollover { +.oncoprint-cell-hover { outline: 1px solid DarkGray; } -.scrolling_oncoprint_table_container table td { - padding: 0; - margin: 0; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} - -.ruleset_legend_label { +.oncoprint-legend-header { font-family: Arial; font-weight: bold; fill: gray; @@ -47,7 +35,10 @@ font-size: 12px; } -.dragging_label { +.oncoprint-label-draggable { + cursor: move; +} +.oncoprint-label-dragging { font-weight: bold; } @@ -60,10 +51,10 @@ user-select: none; } -.cell-qtip { +.oncoprint-cell-qtip { background-color: rgba(0,0,0,0) !important; border: none !important; } -.cell-qtip .qtip-content { +.oncoprint-cell-qtip .qtip-content { background-color: rgba(255,255,255,0.9) !important; } \ No newline at end of file diff --git a/packages/oncoprintjs/src/js/RuleSet.js b/packages/oncoprintjs/src/js/RuleSet.js index 173e525ce28..19bb6a2b8b8 100644 --- a/packages/oncoprintjs/src/js/RuleSet.js +++ b/packages/oncoprintjs/src/js/RuleSet.js @@ -6,7 +6,7 @@ window.oncoprint_RuleSet = (function() { var GENETIC_ALTERATION = 2; var BAR_CHART = 3; - var CELL = 0; + var CELL = "cell"; var getRuleSetId = utils.makeIdCounter(); @@ -234,7 +234,6 @@ window.oncoprint_RuleSet = (function() { z_index: 3 }); altered_rules.push(down_rppa_rule); - // TODO: mrna, rppa, other stuff? self.putLegendGroup = function(svg, cell_width, cell_height) { var group = svg.append('g'); _.each(self.getRules(), function(rule) { diff --git a/packages/oncoprintjs/src/js/events.js b/packages/oncoprintjs/src/js/events.js index 1e616cf474d..40ad9410ad1 100644 --- a/packages/oncoprintjs/src/js/events.js +++ b/packages/oncoprintjs/src/js/events.js @@ -46,5 +46,6 @@ window.oncoprint_events = { UPDATE_RENDER_RULES: 'update_render_rules.cell_renderer.oncoprint', FINISHED_RENDERING: 'finished_rendering.renderer.oncoprint', FINISHED_POSITIONING: 'finished_positioning.renderer.oncoprint', - SET_ZOOM: 'set_zoom.oncoprint' + SET_ZOOM: 'set_zoom.oncoprint', + ADD_TRACK_SEPARATOR: 'add_track_separator.oncoprint' }; diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index d4ad71abe64..66c0e44e5b8 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -62,7 +62,8 @@ window.Oncoprint = (function() { var MIN_CELL_WIDTH = 0.5; self.config = config; self.id_order = []; - self.track_order = []; + self.track_groups = [[],[]]; + //self.track_order = []; self.tracks = {}; self.ids = {}; self.zoom = 1; @@ -109,9 +110,15 @@ window.Oncoprint = (function() { self.id_order = id_order; $(self).trigger(events.SET_ID_ORDER); }; - self.getTrackOrder = function() { - return self.track_order.slice(); + self.getTrackGroups = function() { + return $.extend(true, [], self.track_groups); + }; + self.getTracks = function() { + return _.flatten(self.getTrackGroups()); }; + /*self.getTrackOrder = function() { + return self.track_order.slice(); + };*/ self.getTrackLabel = function(track_id) { return self.tracks[track_id].config.label; }; @@ -147,47 +154,51 @@ window.Oncoprint = (function() { self.getTrackDatumIdKey = function(track_id) { return self.tracks[track_id].config.datum_id_key; }; + self.getContainingTrackGroup = function(track_id) { + var group = false; + _.find(self.track_groups, function(grp) { + if (grp.indexOf(track_id) > -1) { + group = grp; + return true; + } + return false; + }); + return group; + }; self.removeTrack = function(track_id) { var track = self.tracks[track_id]; delete self.tracks[track_id]; - var oldPosition = self.track_order.indexOf(track_id); - self.track_order.splice(oldPosition, 1); + var track_group = self.getContainingTrackGroup(track_id); + if (!track_group) { + return false; + } else { + var old_position = track_group.indexOf(track_id); + track_group.splice(old_position, 1); - $(self).trigger(events.REMOVE_TRACK, {track: track, track_id: track_id}); - return true; + $(self).trigger(events.REMOVE_TRACK, {track: track, track_id: track_id}); + return true; + } }; self.moveTrack = function(track_id, new_position) { - new_position = Math.min(self.track_order.length-1, new_position); - new_position = Math.max(0, new_position); - var old_position = self.track_order.indexOf(track_id); - - var new_order = self.track_order.slice(); - var i; - var moved_tracks = []; - if (old_position > new_position) { - for (i=new_position+1; i<=old_position; i++) { - new_order[i] = self.track_order[i-1]; - moved_tracks.push(self.track_order[i-1]); - } - moved_tracks.push(track_id); - } else if (old_position < new_position) { - for (i=old_position; i= track_tops[id] && mouse_y <= track_tops[id] + self.getRenderedTrackHeight(id)) { in_track = id; return true; @@ -382,10 +417,10 @@ window.Oncoprint = (function() { }); })(); (function initCellContainer() { - self.cell_container = d3.select(container_selector_string).append('div').classed('scrolling_oncoprint_section_container', true); + self.cell_container = d3.select(container_selector_string).append('div').classed(CELL_AREA_CONTAINER_CLASS, true); //self.cell_container.style('display', 'none'); self.cell_container_node = self.cell_container.node(); - self.cell_div = self.cell_container.append('div').classed('cell_div', true); + self.cell_div = self.cell_container.append('div').classed(CELL_AREA_CLASS, true); $(self.cell_container.node()).on('scroll', function() { self.clipCells(); @@ -400,7 +435,7 @@ window.Oncoprint = (function() { var render_all_events = []; var resize_cell_events = [events.SET_CELL_WIDTH, events.SET_ZOOM]; var render_track_events = [events.ADD_TRACK, events.SET_TRACK_DATA]; - var reposition_events = [events.MOVE_TRACK, events.SET_CELL_PADDING, events.SET_ZOOM]; + var reposition_events = [events.ADD_TRACK, events.MOVE_TRACK, events.SET_CELL_PADDING, events.SET_ZOOM]; var resize_cell_div_events = [events.SET_CELL_PADDING, events.SET_CELL_WIDTH, events.SET_ZOOM]; var reclip_events = [events.SET_CELL_PADDING, events.SET_CELL_WIDTH, events.SET_ZOOM]; var reposition_then_reclip_events = [events.SET_ID_ORDER]; @@ -422,6 +457,7 @@ window.Oncoprint = (function() { self.render(d.track_id); }); $(oncoprint).on(reposition_events.join(" "), function() { + self.renderLabels(); self.positionCells(); }); $(oncoprint).on(reclip_events.join(" "), function() { @@ -483,7 +519,10 @@ window.Oncoprint = (function() { var to_reposition; if (rule_set) { svg.selectAll('.'+label_class).remove(); - to_reposition = svg.append('text').classed(label_class, true).text(oncoprint.getTrackLabel(track_id)) + to_reposition = svg.append('text') + .classed(label_class, true) + .classed(LABEL_DRAGGABLE_CLASS, true) + .text(oncoprint.getTrackLabel(track_id)) .attr('font', this.getLabelFont()) .attr('alignment-baseline', 'hanging') .classed('noselect', true) @@ -523,12 +562,11 @@ window.Oncoprint = (function() { var self = this; this.cells[track_id] = this.cells[track_id] || {}; - var cell_class = this.getCellCSSClass(); var track_cell_class = this.getTrackCellCSSClass(track_id); var bound_svg = this.cell_div.selectAll('svg.'+track_cell_class).data(data, id_accessor); - bound_svg.enter().append('svg').classed(track_cell_class, true).classed(cell_class, true); + bound_svg.enter().append('svg').classed(track_cell_class, true).classed(CELL_CLASS, true); bound_svg.style('width', oncoprint.getCellWidth()).style('height', oncoprint.getCellHeight(track_id)); bound_svg .attr('preserveAspectRatio','none') @@ -546,7 +584,7 @@ window.Oncoprint = (function() { text: tooltip_html }, position: {my:'left bottom', at:'top middle', viewport: $(window)}, - style: { classes: 'cell-qtip', border: 'none'}, + style: { classes: CELL_QTIP_CLASS, border: 'none'}, show: {event: "mouseover"}, hide: {fixed: true, delay: 100, event: "mouseout"} }); @@ -554,10 +592,10 @@ window.Oncoprint = (function() { }); } $(dom_cell).on("mouseover", function() { - d3.select(dom_cell).classed("cell_rollover", true); + d3.select(dom_cell).classed(CELL_HOVER_CLASS, true); }); $(dom_cell).on("mouseout", function() { - d3.select(dom_cell).classed("cell_rollover", false); + d3.select(dom_cell).classed(CELL_HOVER_CLASS, false); }); self.cells[track_id][id] = this; }); @@ -581,7 +619,7 @@ window.Oncoprint = (function() { }).style('top', y); }; OncoprintSVGRenderer.prototype.positionCells = function(track_ids) { - track_ids = track_ids || this.oncoprint.getTrackOrder(); + track_ids = track_ids || this.oncoprint.getTracks(); var self = this; _.each(track_ids, function(track_id) { self.positionTrackCells(track_id); @@ -672,12 +710,13 @@ window.Oncoprint = (function() { }; OncoprintSVGRenderer.prototype.renderLabels = function() { var self = this; - _.each(this.oncoprint.getTrackOrder(), function(track_id) { + _.each(this.oncoprint.getTracks(), function(track_id) { var rule_set = self.getRuleSet(track_id); self.renderTrackLabel(self.oncoprint, track_id, rule_set, self.getLabelSVG()); }); }; OncoprintSVGRenderer.prototype.render = function(track_id) { + // TODO: do this by for each track to render, render it, because there's stuff you should only get once like celltops var self = this; this.resizeLabelSVG(); this.resizeCellDiv(); @@ -694,7 +733,7 @@ window.Oncoprint = (function() { if (typeof track_id !== "undefined") { renderTrack(track_id); } else { - _.each(this.oncoprint.getTrackOrder(), function(track_id) { + _.each(this.oncoprint.getTracks(), function(track_id) { renderTrack(track_id); }); } @@ -716,7 +755,7 @@ window.Oncoprint = (function() { _.each(this.rule_sets, function(rule_set, track_id) { var rule_set_id = rule_set.getRuleSetId(); if (!rendered.hasOwnProperty(rule_set_id)) { - var text = svg.append('text').classed('ruleset_legend_label', true).text(rule_set.getLegendLabel()) + var text = svg.append('text').classed(LEGEND_HEADER_CLASS, true).text(rule_set.getLegendLabel()) .attr('transform', utils.translate(0,y)); var group = rule_set.putLegendGroup(svg, cell_width, self.oncoprint.getCellHeight(track_id)); rendered[rule_set_id] = true; @@ -729,42 +768,57 @@ window.Oncoprint = (function() { this.resizeLegendSVG(); } OncoprintSVGRenderer.prototype.dragLabel = function(track_id) { - // TODO: everything about this method is technical debt + var track_group = this.oncoprint.getContainingTrackGroup(track_id).slice(); + var first_track = track_group[0], last_track=track_group[track_group.length-1]; + var all_track_tops = this.getLabelTops(); + var track_tops = {}; + _.each(track_group, function(id) { + track_tops[id] = all_track_tops[id]; + }); + track_group.splice(track_group.indexOf(track_id), 1); + + var label_area_height = this.getLabelAreaHeight(); + var drag_bounds = [undefined, undefined]; + drag_bounds[0] = utils.clamp(track_tops[first_track], 0, label_area_height); + drag_bounds[1] = utils.clamp(track_tops[last_track]+this.getRenderedTrackHeight(last_track), 0, label_area_height); + var self = this; - var other_tracks = this.oncoprint.getTrackOrder(); - var curr_pos = other_tracks.indexOf(track_id); - other_tracks.splice(curr_pos, 1); - - var new_track_pos; - var track_tops_true = this.getLabelTops(); - delete track_tops_true[track_id]; - var label_area_height = self.getLabelAreaHeight(); - var handler = function(evt) { - var track_tops = $.extend({},{},track_tops_true); - var mouse_y = evt.offsetY; - var render_y = utils.clamp(mouse_y, 0, label_area_height); - self.renderTrackLabel(self.oncoprint, track_id, false, self.label_svg, mouse_y).classed('dragging_label', true); - - var first_below = 0; - while (track_tops[other_tracks[first_below]] < render_y && first_below < other_tracks.length) { - first_below += 1; - } - if (first_below > 0) { - track_tops[other_tracks[first_below-1]] -= 3; - } - if (first_below < other_tracks.length) { - track_tops[other_tracks[first_below]] += 3; + var $label_svg = $(self.label_svg.node()); + delete track_tops[track_id]; + + (function(track_id) { + var new_pos = 0; + var moveHandler = function(evt) { + if (evt.stopPropagation) { + evt.stopPropagation(); + } + if (evt.preventDefault) { + evt.preventDefault(); + } + var track_tops_tmp = $.extend({},{},track_tops); + var mouse_y = utils.clamp(evt.offsetY, drag_bounds[0], drag_bounds[1]); + self.renderTrackLabel(self.oncoprint, track_id, false, self.label_svg, mouse_y).classed(LABEL_DRAGGING_CLASS, true); + + new_pos = 0; + while (track_tops_tmp[track_group[new_pos]] < mouse_y && new_pos < track_group.length) { + new_pos += 1; + } + if (new_pos > 0) { + track_tops_tmp[track_group[new_pos-1]] -= 3; + } + if (new_pos < track_group.length) { + track_tops_tmp[track_group[new_pos]] += 3; + } + _.each(track_tops_tmp, function(top, id) { + self.renderTrackLabel(self.oncoprint, id, false, self.label_svg, top); + }); } - new_track_pos = first_below; - _.each(track_tops, function(top, id) { - self.renderTrackLabel(self.oncoprint, id, false, self.label_svg, top); + $label_svg.on("mousemove", moveHandler); + $label_svg.one("mouseup", function(evt) { + $label_svg.off("mousemove", moveHandler); + self.oncoprint.moveTrack(track_id, new_pos); }); - }; - $(self.label_svg.node()).on("mousemove", handler); - $(self.label_svg.node()).on("mouseup", function(evt) { - $(self.label_svg.node()).off("mousemove", handler); - self.oncoprint.moveTrack(track_id, new_track_pos); - }); + })(track_id); }; return OncoprintSVGRenderer; })(); @@ -779,8 +833,10 @@ window.Oncoprint = (function() { var oncoprint = new Oncoprint(config); var renderer = new OncoprintSVGRenderer(container_selector_string, oncoprint, {label_font: '12px Arial', legend:config.legend}); var ret = { - addTrack: function(config) { - var track_id = oncoprint.addTrack(config); + onc_dev: oncoprint, + ren_dev: renderer, + addTrack: function(config, group) { + var track_id = oncoprint.addTrack(config, group); return track_id; }, removeTrack: function(track_id) { diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index e99a1c99aa1..1fd8e4abb39 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -78,16 +78,16 @@ $.when(mutation_data_promise).then(function() { tooltip: function(d) { return '

'+d.sample+': '+d.attr_val+'

'; } - }); + }, 0); onc.setRuleSet(mutation_track_id, Oncoprint.GRADIENT_COLOR, { data_key: 'attr_val', - data_range: [0,100], color_range: ['#A9A9A9', '#FF0000'], + data_range: [0,100], legend_label: 'Mutations', }); onc.setTrackData(mutation_track_id, mutation_data); - var log_mut_track_id = onc.addTrack({label: 'Log Mutations'}); + var log_mut_track_id = onc.addTrack({label: 'Log Mutations'}, 0); onc.setRuleSet(log_mut_track_id, Oncoprint.BAR_CHART, { data_key: 'attr_val', data_range: [0,100], @@ -97,7 +97,7 @@ $.when(mutation_data_promise).then(function() { }); onc.setTrackData(log_mut_track_id, mutation_data); - var dup_mut_track_id = onc.addTrack({label: 'Mutations'}); + var dup_mut_track_id = onc.addTrack({label: 'Mutations'}, 0); onc.setRuleSet(dup_mut_track_id, Oncoprint.BAR_CHART, { data_key: 'attr_val', data_range: [0,100], @@ -112,15 +112,15 @@ alteration_data_promise.then(function(data) { alteration_data = _.map(data, function(x) { if (Math.random() < 0.3) { x.mut_type='MISSENSE'; } return x; }); }); $.when(alteration_data_promise).then(function() { - alteration_track_id = onc.addTrack({label: 'TP53'}); + alteration_track_id = onc.addTrack({label: 'TP53'}, 1); onc.setRuleSet(alteration_track_id, Oncoprint.GENETIC_ALTERATION, { default_color: '#D3D3D3', cna_key: 'cna', cna: { color: { - AMPLIFIED: '#FF0000', + AMPLIFIED: 'red', GAINED: '#FFB6C1', - HOMODELETED: '#8FD8D8', + HOMODELETED: '#0000FF', HETLOSS: '#8FD8D8', }, label: { @@ -134,20 +134,28 @@ $.when(alteration_data_promise).then(function() { mut_type_key: 'mut_type', mut: { color: { - MISSENSE: 'green' + MISSENSE: 'green', + INFRAME: '#9F8170', + TRUNC: 'black', }, label: { - MISSENSE: 'Missense Mutation' + MISSENSE: 'Missense Mutation', + INFRAME: 'In-Frame Insertion/Deletion', + TRUNC: 'Truncating Mutation' } }, legend_label: 'Genetic Alteration', mrna_key: 'mut_type', mrna: { color: { - MISSENSE: '#C9C900' + MISSENSE: '#C9C900', + UPREGULATED: '#FF9999', + DOWNREGULATED: '#6699CC' }, label: { - MISSENSE: 'MRNA MISSENSE' + MISSENSE: 'MRNA MISSENSE', + UPREGULATED: 'mRNA Upregulation', + DOWNREGULATED: 'mRNA Downregulation' } }, rppa_key: 'mut_type', @@ -158,7 +166,7 @@ $.when(alteration_data_promise).then(function() { }); onc.setTrackData(alteration_track_id, alteration_data); - var second_alt_track = onc.addTrack({Label: 'TP53 duplicate'}); + var second_alt_track = onc.addTrack({Label: 'TP53 duplicate'}, 1); onc.useSameRuleSet(second_alt_track, alteration_track_id); onc.setTrackData(second_alt_track, alteration_data); }); From b6fd1493beaa951e647f5058d9d4ce973a927762 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Thu, 2 Jul 2015 15:56:07 -0400 Subject: [PATCH 088/343] clean up a lot, move stuff into another file --- packages/oncoprintjs/index.html | 2 + packages/oncoprintjs/src/css/oncoprint.css | 6 +- .../oncoprintjs/src/js/OncoprintRenderer.js | 127 +++ .../src/js/OncoprintSVGRenderer.js | 516 +++++++++++ packages/oncoprintjs/src/js/oncoprint.js | 866 +++--------------- packages/oncoprintjs/src/js/utils.js | 3 + 6 files changed, 796 insertions(+), 724 deletions(-) create mode 100644 packages/oncoprintjs/src/js/OncoprintRenderer.js create mode 100644 packages/oncoprintjs/src/js/OncoprintSVGRenderer.js diff --git a/packages/oncoprintjs/index.html b/packages/oncoprintjs/index.html index bbc3b008e77..00cb9c47c12 100644 --- a/packages/oncoprintjs/index.html +++ b/packages/oncoprintjs/index.html @@ -20,6 +20,8 @@ + + diff --git a/packages/oncoprintjs/src/css/oncoprint.css b/packages/oncoprintjs/src/css/oncoprint.css index eb0832fda22..4e25b6635e8 100644 --- a/packages/oncoprintjs/src/css/oncoprint.css +++ b/packages/oncoprintjs/src/css/oncoprint.css @@ -35,7 +35,11 @@ font-size: 12px; } -.oncoprint-label-draggable { +.oncoprint-track-label { + alignment-baseline: hanging; + pointer-events: none; +} +.oncoprint-track-label-draggable { cursor: move; } .oncoprint-label-dragging { diff --git a/packages/oncoprintjs/src/js/OncoprintRenderer.js b/packages/oncoprintjs/src/js/OncoprintRenderer.js new file mode 100644 index 00000000000..3ece935b7f1 --- /dev/null +++ b/packages/oncoprintjs/src/js/OncoprintRenderer.js @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2015 Memorial Sloan-Kettering Cancer Center. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS + * FOR A PARTICULAR PURPOSE. The software and documentation provided hereunder + * is on an "as is" basis, and Memorial Sloan-Kettering Cancer Center has no + * obligations to provide maintenance, support, updates, enhancements or + * modifications. In no event shall Memorial Sloan-Kettering Cancer Center be + * liable to any party for direct, indirect, special, incidental or + * consequential damages, including lost profits, arising out of the use of this + * software and its documentation, even if Memorial Sloan-Kettering Cancer + * Center has been advised of the possibility of such damage. + */ + +/* + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ +window.OncoprintRenderer = (function() { + var events = oncoprint_events; + var utils = oncoprint_utils; + var RuleSet = oncoprint_RuleSet; + + function OncoprintRenderer(oncoprint, config) { + this.rule_sets = {}; + this.clipping = true; + this.oncoprint = oncoprint; + this.config = config; + }; + OncoprintRenderer.prototype.getTrackGroupSeparation = function() { + // TODO: configurable + return 40; + }; + OncoprintRenderer.prototype.getCellCSSClass = function() { + return 'oncoprint-cell'; + }; + OncoprintRenderer.prototype.getTrackCellCSSClass = function(track_id) { + return this.getCellCSSClass()+track_id; + }; + OncoprintRenderer.prototype.getTrackLabelCSSClass = function(track_id) { + return 'oncoprint-track-label oncoprint-track-label'+track_id; + }; + OncoprintRenderer.prototype.getTrackLabelCSSSelector = function(track_id) { + return "."+this.getTrackLabelCSSClass(track_id).split(" ").join("."); + }; + OncoprintRenderer.prototype.getLabelFont = function() { + return this.config.label_font; + }; + OncoprintRenderer.prototype.setRuleSet = function(track_id, type, params) { + var new_rule_set = RuleSet.makeRuleSet(type, params); + this.rule_sets[track_id] = new_rule_set; + }; + OncoprintRenderer.prototype.useSameRuleSet = function(target_track_id, source_track_id) { + this.rule_sets[target_track_id] = this.rule_sets[source_track_id]; + }; + OncoprintRenderer.prototype.getRuleSet = function(track_id) { + return this.rule_sets[track_id]; + }; + OncoprintRenderer.prototype.getTrackTops = function() { + var ret = {}; + var y = 0; + var self = this; + _.each(this.oncoprint.getTrackGroups(), function(group) { + _.each(group, function(id) { + ret[id] = y; + y+= self.getRenderedTrackHeight(id); + }); + y += self.getTrackGroupSeparation(); + }); + return ret; + }; + OncoprintRenderer.prototype.getTrackCellTops = function() { + var tops = this.getTrackTops(); + var self = this; + _.each(tops, function(top, id) { + tops[id] = top + self.oncoprint.getTrackPadding(id); + }); + return tops; + }; + OncoprintRenderer.prototype.getTrackLabelTops = function() { + return this.getTrackCellTops(); + }; + OncoprintRenderer.prototype.getRenderedTrackHeight = function(track_id) { + return this.oncoprint.getTrackHeight(track_id) + 2*this.oncoprint.getTrackPadding(track_id); + }; + OncoprintRenderer.prototype.getCellX = function(index) { + return index*(this.oncoprint.getZoomedCellWidth()+this.oncoprint.getCellPadding()); + }; + OncoprintRenderer.prototype.getCellAreaWidth = function() { + return this.oncoprint.getIdOrder().length*(this.oncoprint.getZoomedCellWidth() + this.oncoprint.getCellPadding()); + }; + OncoprintRenderer.prototype.getCellAreaHeight = function() { + var track_tops = this.getTrackTops(); + var track_order = this.oncoprint.getTracks(); + var last_track = track_order[track_order.length-1]; + return track_tops[last_track] + this.getRenderedTrackHeight(last_track); + }; + OncoprintRenderer.prototype.getLabelAreaWidth = function() { + var label_font = this.getLabelFont(); + var labels = _.map(this.oncoprint.getTracks(), this.oncoprint.getTrackLabel); + var label_widths = _.map(labels, function(label) { + return utils.textWidth(label, label_font); + }); + var max_label_width = Math.max(_.max(label_widths), 0); + var max_percent_altered_width = utils.textWidth('100%', label_font); + var buffer_width = 20; + return max_label_width + buffer_width + max_percent_altered_width ; + }; + OncoprintRenderer.prototype.getLabelAreaHeight = function() { + return this.getCellAreaHeight(); + }; + OncoprintRenderer.prototype.render = function() { + throw "not implemented in abstract class"; + } + return OncoprintRenderer; +})(); \ No newline at end of file diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js new file mode 100644 index 00000000000..edc1e247ae4 --- /dev/null +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -0,0 +1,516 @@ +/* + * Copyright (c) 2015 Memorial Sloan-Kettering Cancer Center. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS + * FOR A PARTICULAR PURPOSE. The software and documentation provided hereunder + * is on an "as is" basis, and Memorial Sloan-Kettering Cancer Center has no + * obligations to provide maintenance, support, updates, enhancements or + * modifications. In no event shall Memorial Sloan-Kettering Cancer Center be + * liable to any party for direct, indirect, special, incidental or + * consequential damages, including lost profits, arising out of the use of this + * software and its documentation, even if Memorial Sloan-Kettering Cancer + * Center has been advised of the possibility of such damage. + */ + +/* + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + window.OncoprintSVGRenderer = (function() { + var events = oncoprint_events; + var utils = oncoprint_utils; + + var TOOLBAR_CONTAINER_CLASS = 'oncoprint-toolbar-ctr'; + var LABEL_AREA_CONTAINER_CLASS = 'oncoprint-label-area-ctr'; + var CELL_AREA_CONTAINER_CLASS = 'oncoprint-cell-area-ctr'; + var CELL_AREA_CLASS = 'oncoprint-cell-area'; + + var CELL_HOVER_CLASS = 'oncoprint-cell-hover'; + var LEGEND_HEADER_CLASS = 'oncoprint-legend-header'; + var LABEL_DRAGGING_CLASS = 'oncoprint-label-dragging'; + var LABEL_DRAGGABLE_CLASS = 'oncoprint-label-draggable'; + var CELL_QTIP_CLASS = 'oncoprint-cell-qtip'; + + var CellClipper = new (function() { + this.view_bounds_start = 0; + this.prev_index_range = []; + + this.getClipViewBounds = function(view_bounds) { + var x = view_bounds[0]; + var width = view_bounds[1] - view_bounds[0]; + + var buffer = Math.floor(0.05*width); + var view_bounds_size = 3*width; + var mid_section = this.view_bounds_start + width; + + while (x > mid_section + buffer) { + this.view_bounds_start += buffer; + mid_section = this.view_bounds_start + width; + } + while (x < mid_section - buffer) { + this.view_bounds_start -= buffer; + mid_section = this.view_bounds_start + width; + } + return [this.view_bounds_start, this.view_bounds_start+view_bounds_size]; + }; + this.toShow = function(new_bounds) { + if (typeof this.prev_index_bounds === "undefined") { + return new_bounds; + } + return _.difference(_.range(new_bounds[0], new_bounds[1]), this.prev_index_range); + }; + this.toHide = function(new_bounds) { + if (typeof this.prev_index_bounds === "undefined") { + return []; + } + return _.difference(this.prev_index_range, _.range(new_bounds.first, new_bounds.last)); + }; + this.doClip = function(view_bounds, cell_unit, force_render_all) { + if (force_render_all) { + this.prev_index_range = []; + } + var clip_view_bounds = this.getClipViewBounds(view_bounds); + var new_bounds = [undefined,undefined]; + new_bounds[0] = Math.floor(clip_view_bounds[0]/cell_unit); + new_bounds[1] = Math.ceil(clip_view_bounds[1]/cell_unit); + var to_show = this.toShow(new_bounds); + var to_hide = this.toHide(new_bounds); + this.prev_index_range = _.range(new_bounds[0], new_bounds[1]+1); + return {to_show: to_show, to_hide: to_hide}; + }; + })(); + function OncoprintSVGRenderer(container_selector_string, oncoprint, config) { + OncoprintRenderer.call(this, oncoprint, config); + var self = this; + this.toolbar_container; + this.label_svg; + this.label_container; + this.cell_container; + this.cell_container_node; + this.cell_div; + this.legend_svg; + this.cells = {}; + + this.clip_zone_start = 0; + + d3.select(container_selector_string).selectAll('*').remove(); + (function initToolbarContainer() { + self.toolbar_container = d3.select(container_selector_string).append('div').classed(TOOLBAR_CONTAINER_CLASS, true); + d3.select(container_selector_string).append('br'); + /*$.ajax({url: "toolbar.html", context: document.body, success: function(response) { + $(self.toolbar_container.node()).html(response); + }});*/ + })(); + (function initLabelContainer() { + self.label_container = d3.select(container_selector_string).append('div').classed(LABEL_AREA_CONTAINER_CLASS, true); + self.label_svg = self.label_container.append('svg').attr('viewport-fill', '#ffffff'); + $(self.label_svg.node()).on("mousedown", + function startDraggingLabel(evt) { + console.log(evt); + if (evt.stopPropagation) { + evt.stopPropagation(); + } + if (evt.preventDefault) { + evt.preventDefault(); + } + var to_drag = false; + var track_tops = self.getTrackTops(); + //var mouse_y = utils.mouseY(evt); + var mouse_y = evt.offsetY; + console.log(mouse_y); + _.find(track_tops, function(track_top, track_id) { + track_id = parseInt(track_id); + if (mouse_y >= track_top && mouse_y <= track_top + self.getRenderedTrackHeight(track_id)) { + to_drag = track_id; + return true; + } + }); + if (to_drag !== false) { + self.dragLabel(to_drag); + } + } + ); + })(); + (function initCellContainer() { + self.cell_container = d3.select(container_selector_string).append('div').classed(CELL_AREA_CONTAINER_CLASS, true); + //self.cell_container.style('display', 'none'); + self.cell_container_node = self.cell_container.node(); + self.cell_div = self.cell_container.append('div').classed(CELL_AREA_CLASS, true); + + $(self.cell_container.node()).on('scroll', function() { + self.clipCells(); + }); + })(); + (function initLegend() { + if (config.legend) { + self.legend_svg = d3.select(container_selector_string).append('svg'); + } + })(); + (function reactToOncoprint() { + $(oncoprint).on(events.REMOVE_TRACK, function(evt, data) { + delete self.cells[data.track_id]; + delete self.rule_sets[data.track_id]; + self.render(); + }); + $(oncoprint).on(events.MOVE_TRACK, function(evt, data) { + self.positionCells(data.moved_tracks); + self.renderTrackLabels(); + }); + + var resize_cell_events = [events.SET_CELL_WIDTH, events.SET_ZOOM]; + $(oncoprint).on(resize_cell_events.join(" "), function() { + self.resizeCells(); + }); + + var resize_cell_div_events = [events.SET_CELL_PADDING, events.SET_CELL_WIDTH, events.SET_ZOOM]; + $(oncoprint).on(resize_cell_div_events.join(" "), function() { + self.resizeCellDiv(); + }); + + var render_all_events = []; + $(oncoprint).on(render_all_events.join(" "), function() { + self.render(); + }); + + var render_track_events = [events.ADD_TRACK, events.SET_TRACK_DATA]; + $(oncoprint).on(render_track_events.join(" "), function(e, d) { + self.render(d.track_id); + }); + + var reposition_events = [events.ADD_TRACK, events.MOVE_TRACK, events.SET_CELL_PADDING, events.SET_ZOOM]; + $(oncoprint).on(reposition_events.join(" "), function() { + self.renderTrackLabels(); + self.positionCells(); + }); + + var reclip_events = [events.SET_CELL_PADDING, events.SET_CELL_WIDTH, events.SET_ZOOM]; + $(oncoprint).on(reclip_events.join(" "), function() { + self.clipCells(); + }); + + var reposition_then_reclip_events = [events.SET_ID_ORDER]; + $(oncoprint).on(reposition_then_reclip_events.join(" "), function() { + self.positionCells(); + self.clipCells(true); + }); + })(); + + } + utils.extends(OncoprintSVGRenderer, OncoprintRenderer); + + // Rule sets + OncoprintSVGRenderer.prototype.setRuleSet = function(track_id, type, params) { + OncoprintRenderer.prototype.setRuleSet.call(this, track_id, type, params); + this.render(track_id); + }; + OncoprintSVGRenderer.prototype.useSameRuleSet = function(target_track_id, source_track_id) { + OncoprintRenderer.prototype.useSameRuleSet.call(this, target_track_id, source_track_id); + this.render(target_track_id); + } + + // Containers + OncoprintSVGRenderer.prototype.getLabelSVG = function() { + return this.label_svg; + }; + OncoprintSVGRenderer.prototype.resizeCellDiv = function() { + this.cell_div.style('min-width', this.getCellAreaWidth()+'px') + .style('min-height', this.getCellAreaHeight()+'px'); + }; + OncoprintSVGRenderer.prototype.resizeLabelSVG = function() { + this.getLabelSVG().attr('width', this.getLabelAreaWidth()+'px') + .attr('height', this.getLabelAreaHeight()+'px'); + }; + OncoprintSVGRenderer.prototype.resizeLegendSVG = function() { + var new_height = 0; + var new_width = 0; + var point = this.legend_svg.node().createSVGPoint(); + utils.d3SelectChildren(this.legend_svg, 'g').each(function() { + point.x = 0; + point.y = 0; + point = point.matrixTransform(this.getCTM()); + var bbox = this.getBBox(); + new_height = Math.max(new_height, point.y + bbox.height); + new_width = Math.max(new_width, point.x + bbox.width); + }); + this.legend_svg.attr('width', new_width+'px').attr('height', new_height+'px'); + }; + + // Labels + OncoprintSVGRenderer.prototype.getTrackLabelCSSClass = function(track_id) { + return OncoprintRenderer.prototype.getTrackLabelCSSClass.call(this, track_id)+' oncoprint-track-label-draggable'; + }; + OncoprintSVGRenderer.prototype.renderTrackLabels = function(track_ids, y) { + var svg = this.label_svg; + track_ids = typeof track_ids === "undefined" ? this.oncoprint.getTracks() : track_ids; + if (typeof y !== "undefined") { + svg.selectAll(this.getTrackLabelCSSSelector(track_ids)).attr('y', y+'px'); + } else { + track_ids = [].concat(track_ids); + var label_tops = this.getTrackLabelTops(); + var self = this; + var label_area_width = this.getLabelAreaWidth(); + _.each(track_ids, function(track_id) { + var label_top = label_tops[track_id]; + var track_label_class = self.getTrackLabelCSSClass(track_id); + svg.selectAll(self.getTrackLabelCSSSelector(track_id)).remove(); + svg.append('text') + .classed(self.getTrackLabelCSSClass(track_id), true) + .classed('noselect', true) + .attr('font', self.getLabelFont()) + .text(self.oncoprint.getTrackLabel(track_id)) + .attr('y', label_top+'px'); + + var rule_set = self.getRuleSet(track_id); + if (rule_set && rule_set.alteredData) { + var data = self.oncoprint.getTrackData(track_id); + var num_altered = rule_set.alteredData(data).length; + var percent_altered = Math.floor(100*num_altered/data.length); + svg.append('text') + .classed(self.getTrackLabelCSSClass(track_id), true) + .classed('noselect', true) + .attr('font', self.getLabelFont()) + .text(percent_altered + '%') + .attr('text-anchor', 'end') + .attr('y', label_top+'px') + .attr('x', label_area_width+'px'); + } + }); + } + }; + + // Cells + OncoprintSVGRenderer.prototype.resizeCells = function(new_width) { + this.cell_div.selectAll('svg.'+this.getCellCSSClass()).style('width', this.oncoprint.getZoomedCellWidth()+'px'); + }; + OncoprintSVGRenderer.prototype.drawTrackCells = function(track_id, rule_set) { + var oncoprint = this.oncoprint; + var data = oncoprint.getTrackData(track_id); + var id_key = oncoprint.getTrackDatumIdKey(track_id); + var id_accessor = oncoprint.getTrackDatumIdAccessor(track_id); + var self = this; + this.cells[track_id] = this.cells[track_id] || {}; + + var cell_class = this.getCellCSSClass(); + var track_cell_class = this.getTrackCellCSSClass(track_id); + + + var bound_svg = this.cell_div.selectAll('svg.'+track_cell_class).data(data, id_accessor); + bound_svg.enter().append('svg').classed(track_cell_class, true).classed(cell_class, true); + bound_svg.style('width', oncoprint.getZoomedCellWidth()+'px').style('height', oncoprint.getCellHeight(track_id)+'px'); + bound_svg + .attr('preserveAspectRatio','none') + .attr('viewBox', '0 0 '+oncoprint.getFullCellWidth()+' '+oncoprint.getCellHeight(track_id)); + + var tooltip = this.oncoprint.getTrackTooltip(track_id); + bound_svg.each(function(d,i) { + var dom_cell = this; + var id = id_accessor(d); + if (tooltip) { + var tooltip_html = tooltip(d); + $(dom_cell).one("mouseover", function() { + $(dom_cell).qtip({ + content: { + text: tooltip_html + }, + position: {my:'left bottom', at:'top middle', viewport: $(window)}, + style: { classes: CELL_QTIP_CLASS, border: 'none'}, + show: {event: "mouseover"}, + hide: {fixed: true, delay: 100, event: "mouseout"} + }); + $(dom_cell).trigger("mouseover"); + }); + } + $(dom_cell).on("mouseover", function() { + d3.select(dom_cell).classed(CELL_HOVER_CLASS, true); + }); + $(dom_cell).on("mouseout", function() { + d3.select(dom_cell).classed(CELL_HOVER_CLASS, false); + }); + self.cells[track_id][id] = this; + }); + bound_svg.selectAll('*').remove(); + rule_set.apply(bound_svg, data, id_accessor, oncoprint.getZoomedCellWidth(), oncoprint.getCellHeight(track_id)); + }; + + // Positioning + OncoprintSVGRenderer.prototype.positionTrackCells = function(track_id, bound_svg) { + var oncoprint = this.oncoprint; + if (!bound_svg) { + bound_svg = this.cell_div.selectAll('svg.'+this.getTrackCellCSSClass(track_id)) + .data(oncoprint.getTrackData(track_id), oncoprint.getTrackDatumIdAccessor(track_id)); + } + var self = this; + var id_key = oncoprint.getTrackDatumIdKey(track_id); + var id_order = oncoprint.getIdOrder(); + var y = this.getTrackCellTops()[track_id]; + bound_svg.transition().style('left', function(d,i) { + return self.getCellX(id_order.indexOf(d[id_key]))+'px'; + }).style('top', y+'px'); + }; + OncoprintSVGRenderer.prototype.positionCells = function(track_ids) { + track_ids = track_ids || this.oncoprint.getTracks(); + var self = this; + _.each(track_ids, function(track_id) { + self.positionTrackCells(track_id); + }); + }; + + // Clipping + OncoprintSVGRenderer.prototype.getViewBounds = function() { + var parent = this.cell_container_node; + var parentRect = parent.getBoundingClientRect(); + var x = parent.scrollLeft; + var width = parentRect.right - parentRect.left; + return [x, x+width]; + }; + OncoprintSVGRenderer.prototype.clipCells = function(force_render_all) { + var self = this; + var oncoprint = this.oncoprint; + + var id_order = oncoprint.getIdOrder(); + var clip_changes = CellClipper.doClip(this.getViewBounds(), this.oncoprint.getZoomedCellWidth() + this.oncoprint.getCellPadding(), force_render_all); + var datum_id, cell; + _.each(clip_changes.to_show, function(ind) { + datum_id = id_order[ind]; + _.each(self.cells, function(cell_map) { + cell = cell_map[datum_id]; + if (cell) { + cell.style.display = 'inherit'; + } + }); + }); + _.each(clip_changes.to_hide, function(ind) { + datum_id = id_order[ind]; + _.each(self.cells, function(cell_map) { + cell = cell_map[datum_id]; + if (cell) { + cell.style.display = 'none'; + } + }); + }); + }; + + OncoprintSVGRenderer.prototype.isTrackRenderable = function(track_id) { + return this.getRuleSet(track_id) && this.oncoprint.getTrackData(track_id).length > 0; + }; + OncoprintSVGRenderer.prototype.render = function(track_id) { + // TODO: do this by for each track to render, render it, because there's stuff you should only get once like celltops + var self = this; + this.resizeLabelSVG(); + this.resizeCellDiv(); + + this.cell_div.style('display', 'none'); + var renderTrack = function(track_id) { + if (self.isTrackRenderable(track_id)) { + var rule_set = self.getRuleSet(track_id); + self.drawTrackCells(track_id, rule_set); + self.positionTrackCells(track_id); + self.renderTrackLabels(track_id); + } + }; + if (typeof track_id !== "undefined") { + renderTrack(track_id); + } else { + _.each(this.oncoprint.getTracks(), function(track_id) { + renderTrack(track_id); + }); + } + self.clipCells(); + this.cell_div.style('display', 'inherit'); + this.renderLegend(); + }; + OncoprintSVGRenderer.prototype.renderLegend = function() { + var svg = this.legend_svg; + svg.selectAll('*').remove(); + // + svg.attr('width', 10000+'px').attr('height', 10000+'px'); + // + var padding = 25; + var y = padding; + var rendered = {}; + var cell_width = this.oncoprint.getZoomedCellWidth(); + var self = this; + _.each(this.rule_sets, function(rule_set, track_id) { + var rule_set_id = rule_set.getRuleSetId(); + if (!rendered.hasOwnProperty(rule_set_id)) { + var text = svg.append('text').classed(LEGEND_HEADER_CLASS, true).text(rule_set.getLegendLabel()) + .attr('transform', utils.translate(0,y)); + var group = rule_set.putLegendGroup(svg, cell_width, self.oncoprint.getCellHeight(track_id)); + rendered[rule_set_id] = true; + group.attr('transform', utils.translate(200,y)); + var bounding_box = group.node().getBBox(); + y += bounding_box.height; + y += padding; + } + }); + this.resizeLegendSVG(); + } + OncoprintSVGRenderer.prototype.dragLabel = function(track_id) { + var track_group = this.oncoprint.getContainingTrackGroup(track_id); + var first_track = track_group[0], last_track=track_group[track_group.length-1]; + var all_track_tops = this.getTrackLabelTops(); + var track_tops = {}; + _.each(track_group, function(id) { + track_tops[id] = all_track_tops[id]; + }); + track_group.splice(track_group.indexOf(track_id), 1); + + var label_area_height = this.getLabelAreaHeight(); + var drag_bounds = [undefined, undefined]; + drag_bounds[0] = utils.clamp(track_tops[first_track], 0, label_area_height); + drag_bounds[1] = utils.clamp(track_tops[last_track]+this.getRenderedTrackHeight(last_track), 0, label_area_height); + + var self = this; + var $label_svg = $(self.label_svg.node()); + delete track_tops[track_id]; + + (function(track_id) { + var new_pos = -1; + var moveHandler = function(evt) { + if (evt.stopPropagation) { + evt.stopPropagation(); + } + if (evt.preventDefault) { + evt.preventDefault(); + } + var track_tops_tmp = $.extend({},{},track_tops); + var mouse_y = utils.clamp(utils.mouseY(evt), drag_bounds[0], drag_bounds[1]); + self.renderTrackLabels(track_id, mouse_y); + d3.selectAll(self.getTrackLabelCSSSelector(track_id)).classed(LABEL_DRAGGING_CLASS, true); + + new_pos = 0; + while (track_tops_tmp[track_group[new_pos]] < mouse_y && new_pos < track_group.length) { + new_pos += 1; + } + if (new_pos > 0) { + track_tops_tmp[track_group[new_pos-1]] -= 3; + } + if (new_pos < track_group.length) { + track_tops_tmp[track_group[new_pos]] += 3; + } + _.each(track_tops_tmp, function(top, id) { + self.renderTrackLabels(id, top); + }); + } + $label_svg.on("mousemove", moveHandler); + $label_svg.one("mouseleave mouseup", function(evt) { + $label_svg.off("mousemove", moveHandler); + if (new_pos > -1) { + self.oncoprint.moveTrack(track_id, new_pos); + } + }); + })(track_id); + }; + return OncoprintSVGRenderer; +})(); \ No newline at end of file diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 66c0e44e5b8..983f223bfb0 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -58,51 +58,65 @@ window.Oncoprint = (function() { function Oncoprint(config) { var self = this; - var track_id_counter = 0; + var getTrackId = utils.makeIdCounter(); var MIN_CELL_WIDTH = 0.5; + self.config = config; + self.id_order = []; self.track_groups = [[],[]]; - //self.track_order = []; self.tracks = {}; - self.ids = {}; + self.zoom = 1; self.cell_padding_on = true; - self.true_cell_width = self.config.cell_width; + self.true_cell_width = config.cell_width; + // Cell Padding self.toggleCellPadding = function() { self.cell_padding_on = !self.cell_padding_on; $(self).trigger(events.SET_CELL_PADDING); }; + self.getCellPadding = function() { + return self.config.cell_padding*self.getZoomMultiplier()*(+self.cell_padding_on); + }; + + // Zoom self.getZoom = function() { return self.zoom; }; self.getZoomMultiplier = function() { - var min_over_max = MIN_CELL_WIDTH/self.getTrueCellWidth(); + var min_over_max = MIN_CELL_WIDTH/self.getFullCellWidth(); return (1-min_over_max)*self.zoom + min_over_max; }; self.setZoom = function(z) { self.zoom = utils.clamp(z,0,1); $(self).trigger(events.SET_ZOOM); }; - self.getTrueCellWidth = function() { + + // Cell Width + self.getFullCellWidth = function() { return self.true_cell_width; }; - self.getCellWidth = function() { + self.getZoomedCellWidth = function() { return self.true_cell_width*self.getZoomMultiplier(); }; - self.getCellPadding = function() { - return self.config.cell_padding*self.getZoomMultiplier()*(+self.cell_padding_on); - }; + + // Cell Height self.getCellHeight = function(track_id) { return self.tracks[track_id].config.cell_height; }; + + // Track Height self.getTrackHeight = function(track_id) { return self.tracks[track_id].config.track_height; }; + + // Track Padding self.getTrackPadding = function(track_id) { return self.tracks[track_id].config.track_padding; }; + + // Id Order self.getIdOrder = function() { return self.id_order; }; @@ -110,67 +124,38 @@ window.Oncoprint = (function() { self.id_order = id_order; $(self).trigger(events.SET_ID_ORDER); }; - self.getTrackGroups = function() { - return $.extend(true, [], self.track_groups); - }; - self.getTracks = function() { - return _.flatten(self.getTrackGroups()); - }; - /*self.getTrackOrder = function() { - return self.track_order.slice(); - };*/ - self.getTrackLabel = function(track_id) { - return self.tracks[track_id].config.label; - }; - self.getTrackData = function(track_id) { - return self.tracks[track_id].data; - }; - self.getTrackTooltip = function(track_id) { - return self.tracks[track_id].config.tooltip; + self.sort = function(track_id_list, cmp_list) { + track_id_list = [].concat(track_id_list); + cmp_list = [].concat(cmp_list); + var lexicographically_ordered_cmp = function(id1,id2) { + var cmp_result; + for (var i=0, _len = track_id_list.length; i<_len; i++) { + cmp_result = cmp_list[i](self.getTrackDatum(track_id_list[i], id1),self.getTrackDatum(track_id_list[i], id2)); + if (cmp_result !== 0) { + break; + } + } + return cmp_result; + }; + self.setIdOrder(utils.stableSort(self.getIdOrder(), lexicographically_ordered_cmp)); + $(self).trigger(events.SORT, {id_order: self.id_order}); }; - self.setTrackData = function(track_id, data) { - var id_accessor = self.getTrackDatumIdAccessor(track_id); - - self.tracks[track_id].data = data; - self.id_order = self.id_order.concat(_.difference(_.map(data, id_accessor), self.id_order)); - self.tracks[track_id].id_data_map = {}; - var id_data_map = self.tracks[track_id].id_data_map; - _.each(self.tracks[track_id].data, function(datum) { - id_data_map[id_accessor(datum)] = datum; - }); - $(self).trigger(events.SET_TRACK_DATA, {track_id: track_id}); - }; - self.getTrackDatum = function(track_id, datum_id) { - return self.tracks[track_id].id_data_map[datum_id]; - }; + // Track Creation/Destruction + self.addTrack = function(config, group) { + group = utils.ifndef(group, 1); + var track_id = getTrackId(); + self.tracks[track_id] ={id: track_id, data: [], config: $.extend({}, defaultTrackConfig, config)}; + self.track_groups[group].push(track_id); - self.getTrackDatumIdAccessor = function(track_id) { - var key = self.getTrackDatumIdKey(track_id); - return function(d) { - return d[key]; - }; - }; - self.getTrackDatumIdKey = function(track_id) { - return self.tracks[track_id].config.datum_id_key; - }; - self.getContainingTrackGroup = function(track_id) { - var group = false; - _.find(self.track_groups, function(grp) { - if (grp.indexOf(track_id) > -1) { - group = grp; - return true; - } - return false; - }); - return group; + $(self).trigger(events.ADD_TRACK, {track_id: track_id}); + return track_id; }; - self.removeTrack = function(track_id) { var track = self.tracks[track_id]; delete self.tracks[track_id]; - var track_group = self.getContainingTrackGroup(track_id); + var track_group = self.getContainingTrackGroup(track_id, true); if (!track_group) { return false; } else { @@ -181,8 +166,27 @@ window.Oncoprint = (function() { return true; } }; + + // Track Ordering + self.getTrackGroups = function(reference) { + return (reference === true ? self.track_groups : $.extend(true, [], self.track_groups)); + }; + self.getTracks = function() { + return _.flatten(self.getTrackGroups()); + }; + self.getContainingTrackGroup = function(track_id, reference) { + var group = false; + _.find(self.track_groups, function(grp) { + if (grp.indexOf(track_id) > -1) { + group = grp; + return true; + } + return false; + }); + return (reference === true ? group : group.slice()); + }; self.moveTrack = function(track_id, new_position) { - var track_group = self.getContainingTrackGroup(track_id); + var track_group = self.getContainingTrackGroup(track_id, true); if (!track_group) { return false; } @@ -190,684 +194,100 @@ window.Oncoprint = (function() { new_position = utils.clamp(new_position, 0, track_group.length-1); track_group.splice(old_position, 1); track_group.splice(new_position, 0, track_id); - var moved_tracks = _.range(Math.min(old_position, new_position), Math.max(old_position, new_position)+1) + var moved_tracks = track_group.slice(Math.min(old_position, new_position), Math.max(old_position, new_position) + 1); $(self).trigger(events.MOVE_TRACK, {moved_tracks: moved_tracks}); }; - self.addTrack = function(config, group) { - group = utils.ifndef(group, 1); - var track_id = track_id_counter; - track_id_counter += 1; - self.tracks[track_id] ={id: track_id, data: [], config: $.extend({}, defaultTrackConfig, config)}; - self.track_groups[group].push(track_id); - - $(self).trigger(events.ADD_TRACK, {track_id: track_id}); - return track_id; - }; - - self.sort = function(track_id_list, cmp_list) { - track_id_list = [].concat(track_id_list); - cmp_list = [].concat(cmp_list); - var lexicographically_ordered_cmp = function(id1,id2) { - var cmp_result; - for (var i=0, _len = track_id_list.length; i<_len; i++) { - cmp_result = cmp_list[i](self.getTrackDatum(track_id_list[i], id1),self.getTrackDatum(track_id_list[i], id2)); - if (cmp_result !== 0) { - break; - } - } - return cmp_result; - }; - self.setIdOrder(utils.stableSort(self.getIdOrder(), lexicographically_ordered_cmp)); - $(self).trigger(events.SORT, {id_order: self.id_order}); - }; - } - - var OncoprintRenderer = (function() { - function OncoprintRenderer(oncoprint, config) { - this.rule_sets = {}; - this.clipping = true; - this.oncoprint = oncoprint; - this.config = config; - }; - OncoprintRenderer.prototype.getTrackGroupSeparation = function() { - // TODO: configurable - return 40; - }; - OncoprintRenderer.prototype.getCellCSSClass = function() { - return 'cell'; - }; - OncoprintRenderer.prototype.getTrackCellCSSClass = function(track_id) { - return 'cell'+track_id; - }; - OncoprintRenderer.prototype.getLabelFont = function() { - return this.config.label_font; - }; - OncoprintRenderer.prototype.setRuleSet = function(track_id, type, params) { - var new_rule_set = RuleSet.makeRuleSet(type, params); - this.rule_sets[track_id] = new_rule_set; - }; - OncoprintRenderer.prototype.useSameRuleSet = function(target_track_id, source_track_id) { - this.rule_sets[target_track_id] = this.rule_sets[source_track_id]; - }; - OncoprintRenderer.prototype.getRuleSet = function(track_id) { - return this.rule_sets[track_id]; - }; - OncoprintRenderer.prototype.getTrackTops = function() { - var ret = {}; - var y = 0; - var self = this; - _.each(this.oncoprint.getTrackGroups(), function(group) { - _.each(group, function(id) { - ret[id] = y; - y+= self.getRenderedTrackHeight(id); - }); - y += self.getTrackGroupSeparation(); - }); - return ret; - }; - OncoprintRenderer.prototype.getCellTops = function() { - var tops = this.getTrackTops(); - var self = this; - _.each(tops, function(top, id) { - tops[id] = top + self.oncoprint.getTrackPadding(id); - }); - return tops; - }; - OncoprintRenderer.prototype.getLabelTops = function() { - return this.getCellTops(); - }; - OncoprintRenderer.prototype.getRenderedTrackHeight = function(track_id) { - return this.oncoprint.getTrackHeight(track_id) + 2*this.oncoprint.getTrackPadding(track_id); - }; - OncoprintRenderer.prototype.getCellX = function(index) { - return index*(this.oncoprint.getCellWidth()+this.oncoprint.getCellPadding()); - }; - OncoprintRenderer.prototype.getCellAreaWidth = function() { - return this.oncoprint.getIdOrder().length*(this.oncoprint.getCellWidth() + this.oncoprint.getCellPadding()); - }; - OncoprintRenderer.prototype.getCellAreaHeight = function() { - var track_tops = this.getTrackTops(); - var track_order = this.oncoprint.getTracks(); - var last_track = track_order[track_order.length-1]; - return track_tops[last_track] + this.getRenderedTrackHeight(last_track); - }; - OncoprintRenderer.prototype.getLabelAreaWidth = function() { - var label_font = this.getLabelFont(); - var labels = _.map(this.oncoprint.getTracks(), this.oncoprint.getTrackLabel); - var label_widths = _.map(labels, function(label) { - return utils.textWidth(label, label_font); - }); - var max_label_width = Math.max(_.max(label_widths), 0); - var max_percent_altered_width = utils.textWidth('100%', label_font); - var buffer_width = 20; - return max_label_width + buffer_width + max_percent_altered_width ; - }; - OncoprintRenderer.prototype.getLabelAreaHeight = function() { - return this.getCellAreaHeight(); - }; - OncoprintRenderer.prototype.render = function() { - throw "not implemented in abstract class"; - } - return OncoprintRenderer; - })(); - - var OncoprintSVGRenderer = (function() { - var TOOLBAR_CONTAINER_CLASS = 'oncoprint-toolbar-ctr'; - var LABEL_AREA_CONTAINER_CLASS = 'oncoprint-label-area-ctr'; - var CELL_AREA_CONTAINER_CLASS = 'oncoprint-cell-area-ctr'; - var CELL_AREA_CLASS = 'oncoprint-cell-area'; - var CELL_CLASS = 'oncoprint-cell'; - var CELL_HOVER_CLASS = 'oncoprint-cell-hover'; - var LEGEND_HEADER_CLASS = 'oncoprint-legend-header'; - var LABEL_DRAGGING_CLASS = 'oncoprint-label-dragging'; - var LABEL_DRAGGABLE_CLASS = 'oncoprint-label-draggable'; - var CELL_QTIP_CLASS = 'oncoprint-cell-qtip'; - - function VisibleIndexBounds(first, last) { - this.first = first; - this.last = last; - this.toShow = function(new_bounds) { - var ret = []; - var i; - if (new_bounds.first < this.first) { - for (i=new_bounds.first; i < this.first; i++) { - ret.push(i); - } - } - if (new_bounds.last > this.last) { - for (i=this.last + 1; i <= new_bounds.last; i++) { - ret.push(i); - } - } - return ret; - }; - this.toHide = function(new_bounds) { - var ret = []; - var i; - if (new_bounds.first > this.first) { - for (i=this.first; i < new_bounds.first; i++) { - ret.push(i); - } - } - if (new_bounds.last < this.last) { - for (i=new_bounds.last+1; i <= this.last; i++) { - ret.push(i); - } - } - return ret; - }; - this.set = function(first, last) { - this.first = first; - this.last = last; - return this; - }; - this.fromViewInterval = function(interval, cell_unit) { - this.first = Math.floor(interval[0]/cell_unit); - this.last = Math.ceil(interval[1]/cell_unit); - return this; - }; - } - function OncoprintSVGRenderer(container_selector_string, oncoprint, config) { - OncoprintRenderer.call(this, oncoprint, config); - var self = this; - this.toolbar_container; - this.label_svg; - this.label_container; - this.cell_container; - this.cell_container_node; - this.cell_div; - this.legend_svg; - this.cells = {}; - this.curr_clip_bounds = new VisibleIndexBounds(-1, -2); - this.prev_clip_bounds = new VisibleIndexBounds(-1, -2); - - this.clip_zone_start = 0; - - d3.select(container_selector_string).selectAll('*').remove(); - (function initToolbarContainer() { - self.toolbar_container = d3.select(container_selector_string).append('div').classed(TOOLBAR_CONTAINER_CLASS, true); - d3.select(container_selector_string).append('br'); - /*$.ajax({url: "toolbar.html", context: document.body, success: function(response) { - $(self.toolbar_container.node()).html(response); - }});*/ - })(); - (function initLabelContainer() { - self.label_container = d3.select(container_selector_string).append('div').classed(LABEL_AREA_CONTAINER_CLASS, true); - self.label_svg = self.label_container.append('svg'); - $(self.label_svg.node()).on("mousedown", function(evt) { - // TODO: fix this shit UP - if (evt.stopPropagation) { - evt.stopPropagation(); - } - if (evt.preventDefault) { - evt.preventDefault(); - } - var in_track = -1; - var track_tops = self.getTrackTops(); - var mouse_y = evt.offsetY; - _.find(self.oncoprint.getTracks(), function(id) { - if (mouse_y >= track_tops[id] && mouse_y <= track_tops[id] + self.getRenderedTrackHeight(id)) { - in_track = id; - return true; - } - }); - if (in_track > -1) { - self.dragLabel(in_track); - } - }); - })(); - (function initCellContainer() { - self.cell_container = d3.select(container_selector_string).append('div').classed(CELL_AREA_CONTAINER_CLASS, true); - //self.cell_container.style('display', 'none'); - self.cell_container_node = self.cell_container.node(); - self.cell_div = self.cell_container.append('div').classed(CELL_AREA_CLASS, true); - - $(self.cell_container.node()).on('scroll', function() { - self.clipCells(); - }); - })(); - (function initLegend() { - if (config.legend) { - self.legend_svg = d3.select(container_selector_string).append('svg'); - } - })(); - var render_all_events = []; - var resize_cell_events = [events.SET_CELL_WIDTH, events.SET_ZOOM]; - var render_track_events = [events.ADD_TRACK, events.SET_TRACK_DATA]; - var reposition_events = [events.ADD_TRACK, events.MOVE_TRACK, events.SET_CELL_PADDING, events.SET_ZOOM]; - var resize_cell_div_events = [events.SET_CELL_PADDING, events.SET_CELL_WIDTH, events.SET_ZOOM]; - var reclip_events = [events.SET_CELL_PADDING, events.SET_CELL_WIDTH, events.SET_ZOOM]; - var reposition_then_reclip_events = [events.SET_ID_ORDER]; - $(oncoprint).on(resize_cell_events.join(" "), function() { - self.resizeCells(); - }); - $(oncoprint).on(events.REMOVE_TRACK, function(evt, data) { - delete self.cells[data.track_id]; - delete self.rule_sets[data.track_id]; - self.render(); - }); - $(oncoprint).on(resize_cell_div_events.join(" "), function() { - self.resizeCellDiv(); - }); - $(oncoprint).on(render_all_events.join(" "), function() { - self.render(); - }); - $(oncoprint).on(render_track_events.join(" "), function(e, d) { - self.render(d.track_id); - }); - $(oncoprint).on(reposition_events.join(" "), function() { - self.renderLabels(); - self.positionCells(); - }); - $(oncoprint).on(reclip_events.join(" "), function() { - self.clipCells(); - }); - $(oncoprint).on(reposition_then_reclip_events.join(" "), function() { - self.positionCells(); - self.clipCells(true); - }); - $(oncoprint).on(events.MOVE_TRACK, function(evt, data) { - self.positionCells(data.moved_tracks); - self.renderLabels(); - }) - } - utils.extends(OncoprintSVGRenderer, OncoprintRenderer); - - // Rule sets - OncoprintSVGRenderer.prototype.setRuleSet = function(track_id, type, params) { - OncoprintRenderer.prototype.setRuleSet.call(this, track_id, type, params); - this.render(track_id); - }; - OncoprintSVGRenderer.prototype.useSameRuleSet = function(target_track_id, source_track_id) { - OncoprintRenderer.prototype.useSameRuleSet.call(this, target_track_id, source_track_id); - this.render(target_track_id); - } - // Containers - OncoprintSVGRenderer.prototype.getLabelSVG = function() { - return this.label_svg; - }; - OncoprintSVGRenderer.prototype.resizeCellDiv = function() { - this.cell_div.style('min-width', this.getCellAreaWidth()+'px') - .style('min-height', this.getCellAreaHeight()+'px'); - }; - OncoprintSVGRenderer.prototype.resizeLabelSVG = function() { - this.getLabelSVG().attr('width', this.getLabelAreaWidth()) - .attr('height', this.getLabelAreaHeight()); - }; - OncoprintSVGRenderer.prototype.resizeLegendSVG = function() { - var new_height = 0; - var new_width = 0; - var point = this.legend_svg.node().createSVGPoint(); - utils.d3SelectChildren(this.legend_svg, 'g').each(function() { - point.x = 0; - point.y = 0; - point = point.matrixTransform(this.getCTM()); - var bbox = this.getBBox(); - new_height = Math.max(new_height, point.y + bbox.height); - new_width = Math.max(new_width, point.x + bbox.width); - }); - this.legend_svg.attr('width', new_width).attr('height', new_height); + // Track Label + self.getTrackLabel = function(track_id) { + return self.tracks[track_id].config.label; }; - // Labels - OncoprintSVGRenderer.prototype.renderTrackLabel = function(oncoprint, track_id, rule_set, svg, label_y) { - var label_class = 'label'+track_id; - label_y = typeof label_y === "undefined" ? this.getLabelTops()[track_id] : label_y; - - var to_reposition; - if (rule_set) { - svg.selectAll('.'+label_class).remove(); - to_reposition = svg.append('text') - .classed(label_class, true) - .classed(LABEL_DRAGGABLE_CLASS, true) - .text(oncoprint.getTrackLabel(track_id)) - .attr('font', this.getLabelFont()) - .attr('alignment-baseline', 'hanging') - .classed('noselect', true) - } else { - to_reposition = svg.selectAll('.' + label_class); - } - to_reposition.attr('transform', utils.translate(0, label_y)); - - var altered_data_label_class = 'altered_data_label'+track_id; - if (rule_set && rule_set.alteredData && typeof rule_set.alteredData === 'function') { - svg.selectAll('.'+altered_data_label_class).remove(); - var data = oncoprint.getTrackData(track_id); - var num_altered = rule_set.alteredData(data).length; - var percent_altered = Math.floor(100*num_altered/data.length); - to_reposition = svg.append('text').classed(altered_data_label_class, true) - .text(percent_altered+'%') - .attr('font', this.getLabelFont()) - .attr('text-anchor', 'end') - .attr('alignment-baseline', 'hanging') - .classed('noselect', true) - } else { - to_reposition = svg.selectAll('.'+altered_data_label_class) - } - to_reposition.attr('transform', utils.translate(this.getLabelAreaWidth(), label_y)); - return svg.selectAll('.'+label_class+',.'+altered_data_label_class); + // Track Tooltip + self.getTrackTooltip = function(track_id) { + return self.tracks[track_id].config.tooltip; }; - // Cells - OncoprintSVGRenderer.prototype.resizeCells = function(new_width) { - this.cell_div.selectAll('svg.'+this.getCellCSSClass()).style('width', this.oncoprint.getCellWidth()); + // Track Data + self.getTrackData = function(track_id) { + return self.tracks[track_id].data; }; - OncoprintSVGRenderer.prototype.drawTrackCells = function(track_id, rule_set) { - var oncoprint = this.oncoprint; - var data = oncoprint.getTrackData(track_id); - var id_key = oncoprint.getTrackDatumIdKey(track_id); - var id_accessor = oncoprint.getTrackDatumIdAccessor(track_id); - var self = this; - this.cells[track_id] = this.cells[track_id] || {}; - - var track_cell_class = this.getTrackCellCSSClass(track_id); - - - var bound_svg = this.cell_div.selectAll('svg.'+track_cell_class).data(data, id_accessor); - bound_svg.enter().append('svg').classed(track_cell_class, true).classed(CELL_CLASS, true); - bound_svg.style('width', oncoprint.getCellWidth()).style('height', oncoprint.getCellHeight(track_id)); - bound_svg - .attr('preserveAspectRatio','none') - .attr('viewBox', '0 0 '+oncoprint.getTrueCellWidth()+' '+oncoprint.getCellHeight(track_id)); + self.setTrackData = function(track_id, data) { + var id_accessor = self.getTrackDatumIdAccessor(track_id); - var tooltip = this.oncoprint.getTrackTooltip(track_id); - bound_svg.each(function(d,i) { - var dom_cell = this; - var id = id_accessor(d); - if (tooltip) { - var tooltip_html = tooltip(d); - $(dom_cell).one("mouseover", function() { - $(dom_cell).qtip({ - content: { - text: tooltip_html - }, - position: {my:'left bottom', at:'top middle', viewport: $(window)}, - style: { classes: CELL_QTIP_CLASS, border: 'none'}, - show: {event: "mouseover"}, - hide: {fixed: true, delay: 100, event: "mouseout"} - }); - $(dom_cell).trigger("mouseover"); - }); - } - $(dom_cell).on("mouseover", function() { - d3.select(dom_cell).classed(CELL_HOVER_CLASS, true); - }); - $(dom_cell).on("mouseout", function() { - d3.select(dom_cell).classed(CELL_HOVER_CLASS, false); - }); - self.cells[track_id][id] = this; - }); - bound_svg.selectAll('*').remove(); - rule_set.apply(bound_svg, data, id_accessor, oncoprint.getCellWidth(), oncoprint.getCellHeight(track_id)); - }; + self.tracks[track_id].data = data; + self.id_order = self.id_order.concat(_.difference(_.map(data, id_accessor), self.id_order)); - // Positioning - OncoprintSVGRenderer.prototype.positionTrackCells = function(track_id, bound_svg) { - var oncoprint = this.oncoprint; - if (!bound_svg) { - bound_svg = this.cell_div.selectAll('svg.'+this.getTrackCellCSSClass(track_id)) - .data(oncoprint.getTrackData(track_id), oncoprint.getTrackDatumIdAccessor(track_id)); - } - var self = this; - var id_key = oncoprint.getTrackDatumIdKey(track_id); - var id_order = oncoprint.getIdOrder(); - var y = this.getCellTops()[track_id]; - bound_svg.transition().style('left', function(d,i) { - return self.getCellX(id_order.indexOf(d[id_key])); - }).style('top', y); - }; - OncoprintSVGRenderer.prototype.positionCells = function(track_ids) { - track_ids = track_ids || this.oncoprint.getTracks(); - var self = this; - _.each(track_ids, function(track_id) { - self.positionTrackCells(track_id); + self.tracks[track_id].id_data_map = {}; + var id_data_map = self.tracks[track_id].id_data_map; + _.each(self.tracks[track_id].data, function(datum) { + id_data_map[id_accessor(datum)] = datum; }); + $(self).trigger(events.SET_TRACK_DATA, {track_id: track_id}); }; - - // Clipping - OncoprintSVGRenderer.prototype.getViewInterval = function() { - var parent = this.cell_container_node; - var parentRect = parent.getBoundingClientRect(); - return {x: parent.scrollLeft, width: parentRect.right - parentRect.left}; - }; - OncoprintSVGRenderer.prototype.getPreviousClipBounds = function() { - return this.prev_clip_bounds; - }; - OncoprintSVGRenderer.prototype.getClipViewInterval = function() { - var self = this; - var view = this.getViewInterval(); - var x = view.x; - var width = view.width; - var clip_buffer = Math.floor(0.05*width); - var clip_zone_size = 3*width; - - var variant=1; - if (variant === 0) { - var section0 = this.clip_zone_start; - var section2 = this.clip_zone_start + 2*width; - - if (x > section2) { - this.clip_zone_start += width; - } else if (x < section0) { - this.clip_zone_start -= width; - } - - return [this.clip_zone_start, this.clip_zone_start + clip_zone_size]; - } else if (variant === 1) { - var section1 = this.clip_zone_start + width; - - while (x > section1 + clip_buffer) { - this.clip_zone_start += clip_buffer; - section1 = this.clip_zone_start + width; - } - while (x < section1 - clip_buffer) { - this.clip_zone_start -= clip_buffer; - section1 = this.clip_zone_start + width; - } - return [this.clip_zone_start, this.clip_zone_start + clip_zone_size]; - } - }; - OncoprintSVGRenderer.prototype.clipCells = function(force) { - var self = this; - var oncoprint = this.oncoprint; - - var id_order = oncoprint.getIdOrder(); - var visible_bounds = this.curr_clip_bounds.fromViewInterval(this.getClipViewInterval(), this.oncoprint.getCellWidth() + this.oncoprint.getCellPadding()); - visible_bounds.first = Math.max(0, visible_bounds.first); - visible_bounds.last = Math.min(id_order.length-1, visible_bounds.last); - var prev_bounds = force ? this.prev_clip_bounds.set(id_order.length, -1) : this.getPreviousClipBounds(); - var to_show = prev_bounds.toShow(visible_bounds); - var to_hide = prev_bounds.toHide(visible_bounds); - if (to_show.length > 0 || to_hide.length > 0) { - var i, len; - for (i=0, len = to_show.length; i < len; i++) { - var datum_id = id_order[to_show[i]]; - _.each(self.cells, function(cell_map) { - var cell = cell_map[datum_id]; - if (cell) { - cell.style.display = 'inherit'; - } - }); - } - for (i=0, len = to_hide.length; i < len; i++) { - var datum_id = id_order[to_hide[i]]; - _.each(self.cells, function(cell_map) { - var cell = cell_map[datum_id]; - if (cell) { - cell.style.display = 'none'; - } - }); - } - } - - this.prev_clip_bounds.set(visible_bounds.first, visible_bounds.last); + self.getTrackDatum = function(track_id, datum_id) { + return self.tracks[track_id].id_data_map[datum_id]; }; - OncoprintSVGRenderer.prototype.isTrackRenderable = function(track_id) { - return this.getRuleSet(track_id) && this.oncoprint.getTrackData(track_id).length > 0; + // Track Datum Id + self.getTrackDatumIdAccessor = function(track_id) { + var key = self.getTrackDatumIdKey(track_id); + return function(d) { + return d[key]; + }; }; - OncoprintSVGRenderer.prototype.renderLabels = function() { - var self = this; - _.each(this.oncoprint.getTracks(), function(track_id) { - var rule_set = self.getRuleSet(track_id); - self.renderTrackLabel(self.oncoprint, track_id, rule_set, self.getLabelSVG()); - }); + self.getTrackDatumIdKey = function(track_id) { + return self.tracks[track_id].config.datum_id_key; }; - OncoprintSVGRenderer.prototype.render = function(track_id) { - // TODO: do this by for each track to render, render it, because there's stuff you should only get once like celltops - var self = this; - this.resizeLabelSVG(); - this.resizeCellDiv(); + } - this.cell_div.style('display', 'none'); - var renderTrack = function(track_id) { - if (self.isTrackRenderable(track_id)) { - var rule_set = self.getRuleSet(track_id); - self.drawTrackCells(track_id, rule_set); - self.positionTrackCells(track_id); - self.renderTrackLabel(self.oncoprint, track_id, rule_set, self.getLabelSVG()); + return { + CATEGORICAL_COLOR: RuleSet.CATEGORICAL_COLOR, + GRADIENT_COLOR: RuleSet.GRADIENT_COLOR, + GENETIC_ALTERATION: RuleSet.GENETIC_ALTERATION, + BAR_CHART: RuleSet.BAR_CHART, + create: function CreateOncoprint(container_selector_string, config) { + config = $.extend({}, defaultOncoprintConfig, config || {}); + config = $.extend(config, hiddenOncoprintConfig); + var oncoprint = new Oncoprint(config); + var renderer = new OncoprintSVGRenderer(container_selector_string, oncoprint, {label_font: '12px Arial', legend:config.legend}); + var ret = { + onc_dev: oncoprint, + ren_dev: renderer, + addTrack: function(config, group) { + var track_id = oncoprint.addTrack(config, group); + return track_id; + }, + removeTrack: function(track_id) { + oncoprint.removeTrack(track_id); + }, + moveTrack: function(track_id, position) { + oncoprint.moveTrack(track_id, position); + }, + setTrackData: function(track_id, data) { + oncoprint.setTrackData(track_id, data); + }, + setRuleSet: function(track_id, type, params) { + renderer.setRuleSet(track_id, type, params); + }, + useSameRuleSet: function(target_track_id, source_track_id) { + renderer.useSameRuleSet(target_track_id, source_track_id); + }, + toggleCellPadding: function() { + oncoprint.toggleCellPadding(); + }, + toSVG: function(ctr) { + return renderer.toSVG(ctr); + }, + sort: function(track_id_list, cmp_list) { + oncoprint.sort(track_id_list, cmp_list); + }, + setZoom: function(z) { + oncoprint.setZoom(z); } }; - if (typeof track_id !== "undefined") { - renderTrack(track_id); - } else { - _.each(this.oncoprint.getTracks(), function(track_id) { - renderTrack(track_id); - }); - } - self.clipCells(); - this.cell_div.style('display', 'inherit'); - this.renderLegend(); - }; - OncoprintSVGRenderer.prototype.renderLegend = function() { - var svg = this.legend_svg; - svg.selectAll('*').remove(); - // - svg.attr('width', 10000).attr('height', 10000); - // - var padding = 25; - var y = padding; - var rendered = {}; - var cell_width = this.oncoprint.getCellWidth(); - var self = this; - _.each(this.rule_sets, function(rule_set, track_id) { - var rule_set_id = rule_set.getRuleSetId(); - if (!rendered.hasOwnProperty(rule_set_id)) { - var text = svg.append('text').classed(LEGEND_HEADER_CLASS, true).text(rule_set.getLegendLabel()) - .attr('transform', utils.translate(0,y)); - var group = rule_set.putLegendGroup(svg, cell_width, self.oncoprint.getCellHeight(track_id)); - rendered[rule_set_id] = true; - group.attr('transform', utils.translate(200,y)); - var bounding_box = group.node().getBBox(); - y += bounding_box.height; - y += padding; - } - }); - this.resizeLegendSVG(); + return ret; } - OncoprintSVGRenderer.prototype.dragLabel = function(track_id) { - var track_group = this.oncoprint.getContainingTrackGroup(track_id).slice(); - var first_track = track_group[0], last_track=track_group[track_group.length-1]; - var all_track_tops = this.getLabelTops(); - var track_tops = {}; - _.each(track_group, function(id) { - track_tops[id] = all_track_tops[id]; - }); - track_group.splice(track_group.indexOf(track_id), 1); - - var label_area_height = this.getLabelAreaHeight(); - var drag_bounds = [undefined, undefined]; - drag_bounds[0] = utils.clamp(track_tops[first_track], 0, label_area_height); - drag_bounds[1] = utils.clamp(track_tops[last_track]+this.getRenderedTrackHeight(last_track), 0, label_area_height); - - var self = this; - var $label_svg = $(self.label_svg.node()); - delete track_tops[track_id]; - - (function(track_id) { - var new_pos = 0; - var moveHandler = function(evt) { - if (evt.stopPropagation) { - evt.stopPropagation(); - } - if (evt.preventDefault) { - evt.preventDefault(); - } - var track_tops_tmp = $.extend({},{},track_tops); - var mouse_y = utils.clamp(evt.offsetY, drag_bounds[0], drag_bounds[1]); - self.renderTrackLabel(self.oncoprint, track_id, false, self.label_svg, mouse_y).classed(LABEL_DRAGGING_CLASS, true); - - new_pos = 0; - while (track_tops_tmp[track_group[new_pos]] < mouse_y && new_pos < track_group.length) { - new_pos += 1; - } - if (new_pos > 0) { - track_tops_tmp[track_group[new_pos-1]] -= 3; - } - if (new_pos < track_group.length) { - track_tops_tmp[track_group[new_pos]] += 3; - } - _.each(track_tops_tmp, function(top, id) { - self.renderTrackLabel(self.oncoprint, id, false, self.label_svg, top); - }); - } - $label_svg.on("mousemove", moveHandler); - $label_svg.one("mouseup", function(evt) { - $label_svg.off("mousemove", moveHandler); - self.oncoprint.moveTrack(track_id, new_pos); - }); - })(track_id); - }; - return OncoprintSVGRenderer; - })(); - return { - CATEGORICAL_COLOR: RuleSet.CATEGORICAL_COLOR, - GRADIENT_COLOR: RuleSet.GRADIENT_COLOR, - GENETIC_ALTERATION: RuleSet.GENETIC_ALTERATION, - BAR_CHART: RuleSet.BAR_CHART, - create: function CreateOncoprint(container_selector_string, config) { - config = $.extend({}, defaultOncoprintConfig, config || {}); - config = $.extend(config, hiddenOncoprintConfig); - var oncoprint = new Oncoprint(config); - var renderer = new OncoprintSVGRenderer(container_selector_string, oncoprint, {label_font: '12px Arial', legend:config.legend}); - var ret = { - onc_dev: oncoprint, - ren_dev: renderer, - addTrack: function(config, group) { - var track_id = oncoprint.addTrack(config, group); - return track_id; - }, - removeTrack: function(track_id) { - oncoprint.removeTrack(track_id); - }, - moveTrack: function(track_id, position) { - oncoprint.moveTrack(track_id, position); - }, - setTrackData: function(track_id, data) { - oncoprint.setTrackData(track_id, data); - }, - setRuleSet: function(track_id, type, params) { - renderer.setRuleSet(track_id, type, params); - }, - useSameRuleSet: function(target_track_id, source_track_id) { - renderer.useSameRuleSet(target_track_id, source_track_id); - }, - toggleCellPadding: function() { - oncoprint.toggleCellPadding(); - }, - toSVG: function(ctr) { - return renderer.toSVG(ctr); - }, - sort: function(track_id_list, cmp_list) { - oncoprint.sort(track_id_list, cmp_list); - }, - setZoom: function(z) { - oncoprint.setZoom(z); - } - }; - return ret; - } -}; + }; })(); diff --git a/packages/oncoprintjs/src/js/utils.js b/packages/oncoprintjs/src/js/utils.js index 3020a339b46..8040a6ce849 100644 --- a/packages/oncoprintjs/src/js/utils.js +++ b/packages/oncoprintjs/src/js/utils.js @@ -37,6 +37,9 @@ window.oncoprint_utils = (function() { }, {}); }; + exports.mouseY = function(evt) { + return exports.ifndef(evt.offsetY, evt.originalEvent && evt.originalEvent.layerY); + }; exports.ifndef = function(val, replacement) { return (typeof val === 'undefined') ? replacement : val; }; From f4b5399627bcdeee7001e07ce4a9441b57136315 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Thu, 2 Jul 2015 19:07:22 -0400 Subject: [PATCH 089/343] taking out useless clipping...browser clips fine apparently. Also added useful document fragment rendering --- .../oncoprintjs/src/js/OncoprintRenderer.js | 4 + .../src/js/OncoprintSVGRenderer.js | 182 ++++++------------ packages/oncoprintjs/src/js/oncoprint.js | 6 + packages/oncoprintjs/src/js/utils.js | 3 + packages/oncoprintjs/test/js/test_page.js | 32 ++- 5 files changed, 95 insertions(+), 132 deletions(-) diff --git a/packages/oncoprintjs/src/js/OncoprintRenderer.js b/packages/oncoprintjs/src/js/OncoprintRenderer.js index 3ece935b7f1..5e848632744 100644 --- a/packages/oncoprintjs/src/js/OncoprintRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintRenderer.js @@ -52,8 +52,12 @@ window.OncoprintRenderer = (function() { return 'oncoprint-track-label oncoprint-track-label'+track_id; }; OncoprintRenderer.prototype.getTrackLabelCSSSelector = function(track_id) { + // TODO: replace with utils.cssClassToSelector return "."+this.getTrackLabelCSSClass(track_id).split(" ").join("."); }; + OncoprintRenderer.prototype.getTrackCellCtrCSSClass = function(track_id) { + return 'oncoprint-track-cell-ctr'+track_id; + }; OncoprintRenderer.prototype.getLabelFont = function() { return this.config.label_font; }; diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index edc1e247ae4..66bff645dee 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -42,54 +42,6 @@ var LABEL_DRAGGABLE_CLASS = 'oncoprint-label-draggable'; var CELL_QTIP_CLASS = 'oncoprint-cell-qtip'; - var CellClipper = new (function() { - this.view_bounds_start = 0; - this.prev_index_range = []; - - this.getClipViewBounds = function(view_bounds) { - var x = view_bounds[0]; - var width = view_bounds[1] - view_bounds[0]; - - var buffer = Math.floor(0.05*width); - var view_bounds_size = 3*width; - var mid_section = this.view_bounds_start + width; - - while (x > mid_section + buffer) { - this.view_bounds_start += buffer; - mid_section = this.view_bounds_start + width; - } - while (x < mid_section - buffer) { - this.view_bounds_start -= buffer; - mid_section = this.view_bounds_start + width; - } - return [this.view_bounds_start, this.view_bounds_start+view_bounds_size]; - }; - this.toShow = function(new_bounds) { - if (typeof this.prev_index_bounds === "undefined") { - return new_bounds; - } - return _.difference(_.range(new_bounds[0], new_bounds[1]), this.prev_index_range); - }; - this.toHide = function(new_bounds) { - if (typeof this.prev_index_bounds === "undefined") { - return []; - } - return _.difference(this.prev_index_range, _.range(new_bounds.first, new_bounds.last)); - }; - this.doClip = function(view_bounds, cell_unit, force_render_all) { - if (force_render_all) { - this.prev_index_range = []; - } - var clip_view_bounds = this.getClipViewBounds(view_bounds); - var new_bounds = [undefined,undefined]; - new_bounds[0] = Math.floor(clip_view_bounds[0]/cell_unit); - new_bounds[1] = Math.ceil(clip_view_bounds[1]/cell_unit); - var to_show = this.toShow(new_bounds); - var to_hide = this.toHide(new_bounds); - this.prev_index_range = _.range(new_bounds[0], new_bounds[1]+1); - return {to_show: to_show, to_hide: to_hide}; - }; - })(); function OncoprintSVGRenderer(container_selector_string, oncoprint, config) { OncoprintRenderer.call(this, oncoprint, config); var self = this; @@ -100,9 +52,7 @@ this.cell_container_node; this.cell_div; this.legend_svg; - this.cells = {}; - - this.clip_zone_start = 0; + this.document_fragment; d3.select(container_selector_string).selectAll('*').remove(); (function initToolbarContainer() { @@ -117,7 +67,6 @@ self.label_svg = self.label_container.append('svg').attr('viewport-fill', '#ffffff'); $(self.label_svg.node()).on("mousedown", function startDraggingLabel(evt) { - console.log(evt); if (evt.stopPropagation) { evt.stopPropagation(); } @@ -128,7 +77,6 @@ var track_tops = self.getTrackTops(); //var mouse_y = utils.mouseY(evt); var mouse_y = evt.offsetY; - console.log(mouse_y); _.find(track_tops, function(track_top, track_id) { track_id = parseInt(track_id); if (mouse_y >= track_top && mouse_y <= track_top + self.getRenderedTrackHeight(track_id)) { @@ -147,10 +95,6 @@ //self.cell_container.style('display', 'none'); self.cell_container_node = self.cell_container.node(); self.cell_div = self.cell_container.append('div').classed(CELL_AREA_CLASS, true); - - $(self.cell_container.node()).on('scroll', function() { - self.clipCells(); - }); })(); (function initLegend() { if (config.legend) { @@ -159,8 +103,8 @@ })(); (function reactToOncoprint() { $(oncoprint).on(events.REMOVE_TRACK, function(evt, data) { - delete self.cells[data.track_id]; delete self.rule_sets[data.track_id]; + throw "not implemented"; self.render(); }); $(oncoprint).on(events.MOVE_TRACK, function(evt, data) { @@ -168,47 +112,57 @@ self.renderTrackLabels(); }); - var resize_cell_events = [events.SET_CELL_WIDTH, events.SET_ZOOM]; - $(oncoprint).on(resize_cell_events.join(" "), function() { - self.resizeCells(); + $(oncoprint).on(events.ADD_TRACK, function(e,d) { + //this.cell_div.style('display', 'none'); + self.drawCells(d.track_id); + self.positionCells(); + self.renderTrackLabels(); + //this.cell_div.style('display','inherit'); }); - var resize_cell_div_events = [events.SET_CELL_PADDING, events.SET_CELL_WIDTH, events.SET_ZOOM]; - $(oncoprint).on(resize_cell_div_events.join(" "), function() { + $(oncoprint).on(events.SET_TRACK_DATA, function(e,d) { + //this.cell_div.style('display', 'none'); + self.drawCells(d.track_id); + self.positionTrackCells(d.track_id); + self.renderTrackLabels(d.track_id); self.resizeCellDiv(); + //this.cell_div.style('display','inherit'); }); - var render_all_events = []; - $(oncoprint).on(render_all_events.join(" "), function() { - self.render(); - }); - - var render_track_events = [events.ADD_TRACK, events.SET_TRACK_DATA]; - $(oncoprint).on(render_track_events.join(" "), function(e, d) { - self.render(d.track_id); + $(oncoprint).on(events.MOVE_TRACK, function(e,d) { + self.positionCells(d.moved_tracks); }); - var reposition_events = [events.ADD_TRACK, events.MOVE_TRACK, events.SET_CELL_PADDING, events.SET_ZOOM]; - $(oncoprint).on(reposition_events.join(" "), function() { - self.renderTrackLabels(); + $(oncoprint).on(events.SET_CELL_PADDING, function(e,d) { self.positionCells(); + self.resizeCellDiv(); }); - var reclip_events = [events.SET_CELL_PADDING, events.SET_CELL_WIDTH, events.SET_ZOOM]; - $(oncoprint).on(reclip_events.join(" "), function() { - self.clipCells(); + $(oncoprint).on(events.SET_ZOOM, function(e,d) { + self.positionCells(); + self.resizeCells(); + self.resizeCellDiv(); }); - var reposition_then_reclip_events = [events.SET_ID_ORDER]; - $(oncoprint).on(reposition_then_reclip_events.join(" "), function() { + $(oncoprint).on(events.SET_ID_ORDER, function() { self.positionCells(); - self.clipCells(true); - }); + }) })(); - } utils.extends(OncoprintSVGRenderer, OncoprintRenderer); + OncoprintSVGRenderer.prototype.cellRenderTarget = function() { + return d3.select(this.document_fragment || this.cell_div.node()); + }; + OncoprintSVGRenderer.prototype.suppressRendering = function() { + this.document_fragment = document.createDocumentFragment(); + }; + OncoprintSVGRenderer.prototype.releaseRendering = function() { + this.cell_div.node().appendChild(this.document_fragment); + this.document_fragment = undefined; + this.resizeCells(); + this.positionCells(); + }; // Rule sets OncoprintSVGRenderer.prototype.setRuleSet = function(track_id, type, params) { OncoprintRenderer.prototype.setRuleSet.call(this, track_id, type, params); @@ -293,19 +247,23 @@ OncoprintSVGRenderer.prototype.resizeCells = function(new_width) { this.cell_div.selectAll('svg.'+this.getCellCSSClass()).style('width', this.oncoprint.getZoomedCellWidth()+'px'); }; - OncoprintSVGRenderer.prototype.drawTrackCells = function(track_id, rule_set) { + OncoprintSVGRenderer.prototype.drawTrackCells = function(track_id, fragment) { var oncoprint = this.oncoprint; var data = oncoprint.getTrackData(track_id); var id_key = oncoprint.getTrackDatumIdKey(track_id); var id_accessor = oncoprint.getTrackDatumIdAccessor(track_id); + var rule_set = this.getRuleSet(track_id); + if (!rule_set) { + return; + } var self = this; - this.cells[track_id] = this.cells[track_id] || {}; var cell_class = this.getCellCSSClass(); var track_cell_class = this.getTrackCellCSSClass(track_id); - var bound_svg = this.cell_div.selectAll('svg.'+track_cell_class).data(data, id_accessor); + //var bound_svg = this.cell_div.selectAll('svg.'+track_cell_class).data(data, id_accessor); + var bound_svg = d3.select(fragment).selectAll('svg.'+track_cell_class).data(data, id_accessor); bound_svg.enter().append('svg').classed(track_cell_class, true).classed(cell_class, true); bound_svg.style('width', oncoprint.getZoomedCellWidth()+'px').style('height', oncoprint.getCellHeight(track_id)+'px'); bound_svg @@ -337,11 +295,21 @@ $(dom_cell).on("mouseout", function() { d3.select(dom_cell).classed(CELL_HOVER_CLASS, false); }); - self.cells[track_id][id] = this; }); bound_svg.selectAll('*').remove(); rule_set.apply(bound_svg, data, id_accessor, oncoprint.getZoomedCellWidth(), oncoprint.getCellHeight(track_id)); }; + OncoprintSVGRenderer.prototype.drawCells = function(track_ids) { + var fragment = document.createDocumentFragment(); + track_ids = typeof track_ids === "undefined" ? this.oncoprint.getTracks() : track_ids; + track_ids = [].concat(track_ids); + var self = this; + _.each(track_ids, function(track_id) { + self.drawTrackCells(track_ids, fragment); + }); + //this.cell_div.node().appendChild(fragment); + this.cellRenderTarget().node().appendChild(fragment); + }; // Positioning OncoprintSVGRenderer.prototype.positionTrackCells = function(track_id, bound_svg) { @@ -359,48 +327,14 @@ }).style('top', y+'px'); }; OncoprintSVGRenderer.prototype.positionCells = function(track_ids) { - track_ids = track_ids || this.oncoprint.getTracks(); + track_ids = typeof track_ids === "undefined" ? this.oncoprint.getTracks() : track_ids; + track_ids = [].concat(track_ids); var self = this; _.each(track_ids, function(track_id) { self.positionTrackCells(track_id); }); }; - // Clipping - OncoprintSVGRenderer.prototype.getViewBounds = function() { - var parent = this.cell_container_node; - var parentRect = parent.getBoundingClientRect(); - var x = parent.scrollLeft; - var width = parentRect.right - parentRect.left; - return [x, x+width]; - }; - OncoprintSVGRenderer.prototype.clipCells = function(force_render_all) { - var self = this; - var oncoprint = this.oncoprint; - - var id_order = oncoprint.getIdOrder(); - var clip_changes = CellClipper.doClip(this.getViewBounds(), this.oncoprint.getZoomedCellWidth() + this.oncoprint.getCellPadding(), force_render_all); - var datum_id, cell; - _.each(clip_changes.to_show, function(ind) { - datum_id = id_order[ind]; - _.each(self.cells, function(cell_map) { - cell = cell_map[datum_id]; - if (cell) { - cell.style.display = 'inherit'; - } - }); - }); - _.each(clip_changes.to_hide, function(ind) { - datum_id = id_order[ind]; - _.each(self.cells, function(cell_map) { - cell = cell_map[datum_id]; - if (cell) { - cell.style.display = 'none'; - } - }); - }); - }; - OncoprintSVGRenderer.prototype.isTrackRenderable = function(track_id) { return this.getRuleSet(track_id) && this.oncoprint.getTrackData(track_id).length > 0; }; @@ -413,8 +347,7 @@ this.cell_div.style('display', 'none'); var renderTrack = function(track_id) { if (self.isTrackRenderable(track_id)) { - var rule_set = self.getRuleSet(track_id); - self.drawTrackCells(track_id, rule_set); + self.drawTrackCells(track_id); self.positionTrackCells(track_id); self.renderTrackLabels(track_id); } @@ -426,7 +359,6 @@ renderTrack(track_id); }); } - self.clipCells(); this.cell_div.style('display', 'inherit'); this.renderLegend(); }; diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 983f223bfb0..25d1fa3d478 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -285,6 +285,12 @@ window.Oncoprint = (function() { }, setZoom: function(z) { oncoprint.setZoom(z); + }, + suppressRendering: function() { + renderer.suppressRendering(); + }, + releaseRendering: function() { + renderer.releaseRendering(); } }; return ret; diff --git a/packages/oncoprintjs/src/js/utils.js b/packages/oncoprintjs/src/js/utils.js index 8040a6ce849..74983a22416 100644 --- a/packages/oncoprintjs/src/js/utils.js +++ b/packages/oncoprintjs/src/js/utils.js @@ -37,6 +37,9 @@ window.oncoprint_utils = (function() { }, {}); }; + exports.cssClassToSelector = function(classes) { + return "."+classes.split(" ").join("."); + }; exports.mouseY = function(evt) { return exports.ifndef(evt.offsetY, evt.originalEvent && evt.originalEvent.layerY); }; diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index 1fd8e4abb39..ef1d9402078 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -48,12 +48,15 @@ var alteration_data; var alteration_track_id; var alteration_data_promise = $.getJSON('dist/test/gbm/tp53.json'); +var tracks_to_load = 15; +onc.suppressRendering(); + gender_data_promise.then(function(data) { gender_data = data.data; }); $.when(gender_data_promise).then(function() { - /* gender_track_id = onc.addTrack({label: 'Gender'}); + tracks_to_load -= 1; onc.setRuleSet(gender_track_id, Oncoprint.CATEGORICAL_COLOR, { color: {}, getCategory: function(d) { @@ -61,13 +64,17 @@ $.when(gender_data_promise).then(function() { }, legend_label: 'Gender' }); + onc.setTrackData(gender_track_id, gender_data); - */ - //for (var i=0; i<0; i++) { - // var dup_gender_track_id = onc.addTrack({label: 'Gender'}); - // onc.useSameRuleSet(dup_gender_track_id, gender_track_id); - // onc.setTrackData(dup_gender_track_id, gender_data); - //} + for (var i=0; i<9; i++) { + var dup_gender_track_id = onc.addTrack({label: 'Gender'}); + onc.useSameRuleSet(dup_gender_track_id, gender_track_id); + onc.setTrackData(dup_gender_track_id, gender_data); + tracks_to_load -= 1; + } + if (tracks_to_load === 0) { + onc.releaseRendering(); + } }); mutation_data_promise.then(function(data) { @@ -79,6 +86,7 @@ $.when(mutation_data_promise).then(function() { return '

'+d.sample+': '+d.attr_val+'

'; } }, 0); + tracks_to_load -= 1; onc.setRuleSet(mutation_track_id, Oncoprint.GRADIENT_COLOR, { data_key: 'attr_val', color_range: ['#A9A9A9', '#FF0000'], @@ -88,6 +96,7 @@ $.when(mutation_data_promise).then(function() { onc.setTrackData(mutation_track_id, mutation_data); var log_mut_track_id = onc.addTrack({label: 'Log Mutations'}, 0); + tracks_to_load -= 1; onc.setRuleSet(log_mut_track_id, Oncoprint.BAR_CHART, { data_key: 'attr_val', data_range: [0,100], @@ -98,6 +107,7 @@ $.when(mutation_data_promise).then(function() { onc.setTrackData(log_mut_track_id, mutation_data); var dup_mut_track_id = onc.addTrack({label: 'Mutations'}, 0); + tracks_to_load -= 1; onc.setRuleSet(dup_mut_track_id, Oncoprint.BAR_CHART, { data_key: 'attr_val', data_range: [0,100], @@ -105,6 +115,9 @@ $.when(mutation_data_promise).then(function() { legend_label: 'Mutations (Linear scale)' }); onc.setTrackData(dup_mut_track_id, mutation_data); + if (tracks_to_load === 0) { + onc.releaseRendering(); + } }); @@ -113,6 +126,7 @@ alteration_data_promise.then(function(data) { }); $.when(alteration_data_promise).then(function() { alteration_track_id = onc.addTrack({label: 'TP53'}, 1); + tracks_to_load -= 1; onc.setRuleSet(alteration_track_id, Oncoprint.GENETIC_ALTERATION, { default_color: '#D3D3D3', cna_key: 'cna', @@ -167,8 +181,12 @@ $.when(alteration_data_promise).then(function() { onc.setTrackData(alteration_track_id, alteration_data); var second_alt_track = onc.addTrack({Label: 'TP53 duplicate'}, 1); + tracks_to_load -= 1; onc.useSameRuleSet(second_alt_track, alteration_track_id); onc.setTrackData(second_alt_track, alteration_data); + if (tracks_to_load === 0) { + onc.releaseRendering(); + } }); $('#change_color_scheme').click(function() { From eca930887babf8a65410bfa9b1beae584e718c42 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Thu, 2 Jul 2015 19:21:26 -0400 Subject: [PATCH 090/343] speeding up dragging a bit --- .../src/js/OncoprintSVGLegendRenderer.js | 44 +++++++++++++++++++ .../src/js/OncoprintSVGRenderer.js | 15 +++---- 2 files changed, 51 insertions(+), 8 deletions(-) create mode 100644 packages/oncoprintjs/src/js/OncoprintSVGLegendRenderer.js diff --git a/packages/oncoprintjs/src/js/OncoprintSVGLegendRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGLegendRenderer.js new file mode 100644 index 00000000000..77a4bc7c946 --- /dev/null +++ b/packages/oncoprintjs/src/js/OncoprintSVGLegendRenderer.js @@ -0,0 +1,44 @@ +window.OncoprintSVGLegendRenderer = (function() { + var events = oncoprint_events; + var utils = oncoprint_utils; +} +OncoprintSVGRenderer.prototype.resizeLegendSVG = function() { + var new_height = 0; + var new_width = 0; + var point = this.legend_svg.node().createSVGPoint(); + utils.d3SelectChildren(this.legend_svg, 'g').each(function() { + point.x = 0; + point.y = 0; + point = point.matrixTransform(this.getCTM()); + var bbox = this.getBBox(); + new_height = Math.max(new_height, point.y + bbox.height); + new_width = Math.max(new_width, point.x + bbox.width); + }); + this.legend_svg.attr('width', new_width+'px').attr('height', new_height+'px'); + }; + OncoprintSVGRenderer.prototype.renderLegend = function() { + var svg = this.legend_svg; + svg.selectAll('*').remove(); + // + svg.attr('width', 10000+'px').attr('height', 10000+'px'); + // + var padding = 25; + var y = padding; + var rendered = {}; + var cell_width = this.oncoprint.getZoomedCellWidth(); + var self = this; + _.each(this.rule_sets, function(rule_set, track_id) { + var rule_set_id = rule_set.getRuleSetId(); + if (!rendered.hasOwnProperty(rule_set_id)) { + var text = svg.append('text').classed(LEGEND_HEADER_CLASS, true).text(rule_set.getLegendLabel()) + .attr('transform', utils.translate(0,y)); + var group = rule_set.putLegendGroup(svg, cell_width, self.oncoprint.getCellHeight(track_id)); + rendered[rule_set_id] = true; + group.attr('transform', utils.translate(200,y)); + var bounding_box = group.node().getBBox(); + y += bounding_box.height; + y += padding; + } + }); + this.resizeLegendSVG(); + } diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index 66bff645dee..25cb8e26702 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -397,7 +397,9 @@ track_tops[id] = all_track_tops[id]; }); track_group.splice(track_group.indexOf(track_id), 1); - + var group_track_tops = _.map(track_group, function(id) { + return track_tops[id]; + }); var label_area_height = this.getLabelAreaHeight(); var drag_bounds = [undefined, undefined]; drag_bounds[0] = utils.clamp(track_tops[first_track], 0, label_area_height); @@ -416,22 +418,19 @@ if (evt.preventDefault) { evt.preventDefault(); } - var track_tops_tmp = $.extend({},{},track_tops); + var track_tops_tmp = $.extend({},{}, track_tops); var mouse_y = utils.clamp(utils.mouseY(evt), drag_bounds[0], drag_bounds[1]); self.renderTrackLabels(track_id, mouse_y); d3.selectAll(self.getTrackLabelCSSSelector(track_id)).classed(LABEL_DRAGGING_CLASS, true); - - new_pos = 0; - while (track_tops_tmp[track_group[new_pos]] < mouse_y && new_pos < track_group.length) { - new_pos += 1; - } + + new_pos = _.sortedIndex(group_track_tops, mouse_y); if (new_pos > 0) { track_tops_tmp[track_group[new_pos-1]] -= 3; } if (new_pos < track_group.length) { track_tops_tmp[track_group[new_pos]] += 3; } - _.each(track_tops_tmp, function(top, id) { + _.each(track_tops_tmp, function(top,id){ self.renderTrackLabels(id, top); }); } From c14b833113a48859619ef9a08188867d9fc62feb Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Mon, 6 Jul 2015 15:50:20 -0400 Subject: [PATCH 091/343] default genetic alteration config and sorting --- packages/oncoprintjs/src/js/RuleSet.js | 2 + packages/oncoprintjs/src/js/defaults.js | 85 ++++++++++++++++++++++++ packages/oncoprintjs/src/js/oncoprint.js | 1 - packages/oncoprintjs/src/js/utils.js | 4 ++ 4 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 packages/oncoprintjs/src/js/defaults.js diff --git a/packages/oncoprintjs/src/js/RuleSet.js b/packages/oncoprintjs/src/js/RuleSet.js index 19bb6a2b8b8..f28c630d0f2 100644 --- a/packages/oncoprintjs/src/js/RuleSet.js +++ b/packages/oncoprintjs/src/js/RuleSet.js @@ -1,5 +1,6 @@ window.oncoprint_RuleSet = (function() { var utils = oncoprint_utils; + var defaults = oncoprint_defaults; var CATEGORICAL_COLOR = 0; var GRADIENT_COLOR = 1; @@ -150,6 +151,7 @@ window.oncoprint_RuleSet = (function() { D3SVGRuleSet.call(this, params); var self = this; self.type = GENETIC_ALTERATION; + params = params || defaults.genetic_alteration_config; var default_rule = this.addStaticRule({ shape: utils.makeD3SVGElement('rect').attr('fill', params.default_color), exclude_from_legend: true, diff --git a/packages/oncoprintjs/src/js/defaults.js b/packages/oncoprintjs/src/js/defaults.js new file mode 100644 index 00000000000..47a340e6431 --- /dev/null +++ b/packages/oncoprintjs/src/js/defaults.js @@ -0,0 +1,85 @@ +window.oncoprint_defaults = (function() { + var genetic_alteration_config = { + default_color: '#D3D3D3', + cna_key: 'cna', + cna: { + color: { + AMPLIFIED: 'red', + GAINED: '#FFB6C1', + HOMODELETED: '#0000FF', + HETLOSS: '#8FD8D8', + }, + label: { + AMPLIFIED: 'Amplification', + GAINED: 'Gain', + HOMODELETED: 'Homozygous Deletion', + HETLOSS: 'Heterozygous Deletion' + } + + }, + mut_type_key: 'mut_type', + mut: { + color: { + MISSENSE: 'green', + INFRAME: '#9F8170', + TRUNC: 'black', + }, + label: { + MISSENSE: 'Missense Mutation', + INFRAME: 'In-Frame Insertion/Deletion', + TRUNC: 'Truncating Mutation' + } + }, + legend_label: 'Genetic Alteration', + mrna_key: 'mut_type', + mrna: { + color: { + MISSENSE: '#C9C900', + UPREGULATED: '#FF9999', + DOWNREGULATED: '#6699CC' + }, + label: { + MISSENSE: 'MRNA MISSENSE', + UPREGULATED: 'mRNA Upregulation', + DOWNREGULATED: 'mRNA Downregulation' + } + }, + rppa_key: 'mut_type', + rppa_down: 'MISSENSE', + rppa_up: 'MISSENSE', + rppa_down_label: 'Protein Downregulation', + rppa_up_label: 'Protein Upregulation' + }; + + var genetic_alteration_comparator = function(d1,d2) { + var cna_order = utils.invert_array(['AMPLIFIED', 'HOMODELETED', 'GAINED', 'HEMIZYGOUSLYDELETED', 'DIPLOID', undefined]); + var mut_order = utils.invert_array([]); // TODO: mutation order + var regulation_order = utils.invert_array(['UPREGULATED', 'DOWNREGULATED', undefined]); + + var cna_key = genetic_alteration_config.cna_key; + var cna_diff = utils.sign(cna_order[d1[cna_key]] - cna_order[d2[cna_key]]); + if (cna_diff !== 0) { + return cna_diff; + } + + var mut_type_key = genetic_alteration_config.mut_type_key; + var mut_type_diff = utils.sign(mut_order[d1[mut_type_key]] - mut_order[d2[mut_type_key]]); + if (mut_type_diff !== 0) { + return mut_type_diff; + } + + var mrna_key = genetic_alteration_config.mrna_key; + var mrna_diff = utils.sign(regulation_order[d1[mrna_key]] - regulation_order[d2[mrna_key]]); + if (mrna_diff !== 0) { + return mrna_diff; + } + + var rppa_key = genetic_alteration_config.rppa_key; + var rppa_diff = utils.sign(regulation_order[d1[rppa_key]] - regulation_order[d2[rppa_key]]); + if (rppa_diff !== 0) { + return rppa_diff; + } + + return 0; + }; +}); diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 25d1fa3d478..66a54c4fe7f 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -138,7 +138,6 @@ window.Oncoprint = (function() { return cmp_result; }; self.setIdOrder(utils.stableSort(self.getIdOrder(), lexicographically_ordered_cmp)); - $(self).trigger(events.SORT, {id_order: self.id_order}); }; // Track Creation/Destruction diff --git a/packages/oncoprintjs/src/js/utils.js b/packages/oncoprintjs/src/js/utils.js index 74983a22416..f32670f8670 100644 --- a/packages/oncoprintjs/src/js/utils.js +++ b/packages/oncoprintjs/src/js/utils.js @@ -30,6 +30,10 @@ window.oncoprint_utils = (function() { var exports = {}; + exports.sign = function(number) { + return number?((number<0)?-1:1):0 + }; + exports.invert_array = function invert_array(arr) { return arr.reduce(function(curr, next, index) { curr[next] = index; From 9ca6df9c3616c8d19a7c9240467eff50d1a50afc Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Mon, 6 Jul 2015 16:14:36 -0400 Subject: [PATCH 092/343] small fix for using default genetic alteration params --- packages/oncoprintjs/src/js/RuleSet.js | 2 +- packages/oncoprintjs/src/js/defaults.js | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/oncoprintjs/src/js/RuleSet.js b/packages/oncoprintjs/src/js/RuleSet.js index f28c630d0f2..444549dc867 100644 --- a/packages/oncoprintjs/src/js/RuleSet.js +++ b/packages/oncoprintjs/src/js/RuleSet.js @@ -148,10 +148,10 @@ window.oncoprint_RuleSet = (function() { D3SVGBarChartRuleSet.prototype = Object.create(D3SVGRuleSet.prototype); function D3SVGGeneticAlterationRuleSet(params) { + params = params || defaults.genetic_alteration_config; D3SVGRuleSet.call(this, params); var self = this; self.type = GENETIC_ALTERATION; - params = params || defaults.genetic_alteration_config; var default_rule = this.addStaticRule({ shape: utils.makeD3SVGElement('rect').attr('fill', params.default_color), exclude_from_legend: true, diff --git a/packages/oncoprintjs/src/js/defaults.js b/packages/oncoprintjs/src/js/defaults.js index 47a340e6431..4a594b4d22e 100644 --- a/packages/oncoprintjs/src/js/defaults.js +++ b/packages/oncoprintjs/src/js/defaults.js @@ -48,7 +48,7 @@ window.oncoprint_defaults = (function() { rppa_down: 'MISSENSE', rppa_up: 'MISSENSE', rppa_down_label: 'Protein Downregulation', - rppa_up_label: 'Protein Upregulation' + rppa_up_label: 'Protein Upregulation', }; var genetic_alteration_comparator = function(d1,d2) { @@ -82,4 +82,9 @@ window.oncoprint_defaults = (function() { return 0; }; -}); + + return { + genetic_alteration_config: genetic_alteration_config, + genetic_alteration_comparator: genetic_alteration_comparator + } +})(); From b8da9c433d6247be940a9ed467d9bfbff7c52109 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Mon, 6 Jul 2015 19:15:37 -0400 Subject: [PATCH 093/343] legend table instead of svg --- packages/oncoprintjs/index.html | 1 + packages/oncoprintjs/src/css/oncoprint.css | 17 ++- .../src/js/OncoprintSVGRenderer.js | 52 +++------ packages/oncoprintjs/src/js/RuleSet.js | 102 +++++++++--------- packages/oncoprintjs/test/js/test_page.js | 4 +- 5 files changed, 88 insertions(+), 88 deletions(-) diff --git a/packages/oncoprintjs/index.html b/packages/oncoprintjs/index.html index 00cb9c47c12..69fb0a99014 100644 --- a/packages/oncoprintjs/index.html +++ b/packages/oncoprintjs/index.html @@ -19,6 +19,7 @@ + diff --git a/packages/oncoprintjs/src/css/oncoprint.css b/packages/oncoprintjs/src/css/oncoprint.css index 4e25b6635e8..35f110deed8 100644 --- a/packages/oncoprintjs/src/css/oncoprint.css +++ b/packages/oncoprintjs/src/css/oncoprint.css @@ -27,14 +27,25 @@ } .oncoprint-legend-header { - font-family: Arial; - font-weight: bold; - fill: gray; + font-family: Arial !important; + font-weight: bold !important; + fill: gray !important; text-anchor: start; alignment-baseline: hanging; font-size: 12px; } +.oncoprint-legend-label { + font-family: Arial; + alignment-baseline: hanging; + font-size: 12px; + padding-right: 20px; +} + +.oncoprint-legend-element { + display: inline-block; +} + .oncoprint-track-label { alignment-baseline: hanging; pointer-events: none; diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index 25cb8e26702..ab7b8c1fac8 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -51,7 +51,7 @@ this.cell_container; this.cell_container_node; this.cell_div; - this.legend_svg; + this.legend_table; this.document_fragment; d3.select(container_selector_string).selectAll('*').remove(); @@ -98,7 +98,7 @@ })(); (function initLegend() { if (config.legend) { - self.legend_svg = d3.select(container_selector_string).append('svg'); + self.legend_table = d3.select(container_selector_string).append('table').style('border-collapse', 'collapse'); } })(); (function reactToOncoprint() { @@ -166,11 +166,13 @@ // Rule sets OncoprintSVGRenderer.prototype.setRuleSet = function(track_id, type, params) { OncoprintRenderer.prototype.setRuleSet.call(this, track_id, type, params); - this.render(track_id); + this.renderLegend(); + //this.render(track_id); }; OncoprintSVGRenderer.prototype.useSameRuleSet = function(target_track_id, source_track_id) { OncoprintRenderer.prototype.useSameRuleSet.call(this, target_track_id, source_track_id); - this.render(target_track_id); + this.renderLegend(); + //this.render(target_track_id); } // Containers @@ -185,20 +187,6 @@ this.getLabelSVG().attr('width', this.getLabelAreaWidth()+'px') .attr('height', this.getLabelAreaHeight()+'px'); }; - OncoprintSVGRenderer.prototype.resizeLegendSVG = function() { - var new_height = 0; - var new_width = 0; - var point = this.legend_svg.node().createSVGPoint(); - utils.d3SelectChildren(this.legend_svg, 'g').each(function() { - point.x = 0; - point.y = 0; - point = point.matrixTransform(this.getCTM()); - var bbox = this.getBBox(); - new_height = Math.max(new_height, point.y + bbox.height); - new_width = Math.max(new_width, point.x + bbox.width); - }); - this.legend_svg.attr('width', new_width+'px').attr('height', new_height+'px'); - }; // Labels OncoprintSVGRenderer.prototype.getTrackLabelCSSClass = function(track_id) { @@ -363,31 +351,25 @@ this.renderLegend(); }; OncoprintSVGRenderer.prototype.renderLegend = function() { - var svg = this.legend_svg; - svg.selectAll('*').remove(); - // - svg.attr('width', 10000+'px').attr('height', 10000+'px'); - // - var padding = 25; - var y = padding; - var rendered = {}; var cell_width = this.oncoprint.getZoomedCellWidth(); var self = this; + var rendered = {}; + self.legend_table.selectAll('*').remove(); _.each(this.rule_sets, function(rule_set, track_id) { var rule_set_id = rule_set.getRuleSetId(); if (!rendered.hasOwnProperty(rule_set_id)) { - var text = svg.append('text').classed(LEGEND_HEADER_CLASS, true).text(rule_set.getLegendLabel()) - .attr('transform', utils.translate(0,y)); - var group = rule_set.putLegendGroup(svg, cell_width, self.oncoprint.getCellHeight(track_id)); + var tr = self.legend_table.append('tr'); + var label_header = tr.append('td').style('padding-top', '1em').style('padding-bottom', '1em') + .append('h1').classed('oncoprint-legend-header', true); + label_header.text(rule_set.getLegendLabel()); + var legend_body_td = tr.append('td'); + var legend_div = rule_set.getLegendDiv(cell_width, self.oncoprint.getCellHeight(track_id)); + legend_body_td.node().appendChild(legend_div); + d3.select(legend_div).selectAll('*').classed('oncoprint-legend-element', true); rendered[rule_set_id] = true; - group.attr('transform', utils.translate(200,y)); - var bounding_box = group.node().getBBox(); - y += bounding_box.height; - y += padding; } }); - this.resizeLegendSVG(); - } + }; OncoprintSVGRenderer.prototype.dragLabel = function(track_id) { var track_group = this.oncoprint.getContainingTrackGroup(track_id); var first_track = track_group[0], last_track=track_group[track_group.length-1]; diff --git a/packages/oncoprintjs/src/js/RuleSet.js b/packages/oncoprintjs/src/js/RuleSet.js index 444549dc867..8c9cf78c193 100644 --- a/packages/oncoprintjs/src/js/RuleSet.js +++ b/packages/oncoprintjs/src/js/RuleSet.js @@ -104,13 +104,16 @@ window.oncoprint_RuleSet = (function() { D3SVGRuleSet.prototype.apply.call(this, g, data, datum_id_accessor, cell_width, cell_height); }; - self.putLegendGroup = function(svg, cell_width, cell_height) { - var group = svg.append('g'); + self.getLegendDiv = function(cell_width, cell_height) { + var div = d3.select(document.createElement('div')); _.each(self.getRules(), function(rule) { - rule.putLegendGroup(group, cell_width, cell_height); - }) - utils.spaceSVGElementsHorizontally(group, 20); - return group; + var legend_div = rule.getLegendDiv(cell_width, cell_height); + if (legend_div) { + div.node().appendChild(legend_div); + } + }); + utils.d3SelectChildren(div, '*').style('padding-right', '20px'); + return div.node(); }; } D3SVGCategoricalColorRuleSet.prototype = Object.create(D3SVGRuleSet.prototype); @@ -125,8 +128,8 @@ window.oncoprint_RuleSet = (function() { color_range: params.color_range, scale: params.scale }); - this.putLegendGroup = function(svg) { - return this.rule_map[rule].putLegendGroup(svg); + this.getLegendDiv = function() { + return this.rule_map[rule].getLegendDiv(); }; } D3SVGGradientColorRuleSet.prototype = Object.create(D3SVGRuleSet.prototype); @@ -141,8 +144,8 @@ window.oncoprint_RuleSet = (function() { scale: params.scale, fill: params.fill, }); - this.putLegendGroup = function(svg, cell_width, cell_height) { - return this.rule_map[rule].putLegendGroup(svg, cell_width, cell_height); + this.getLegendDiv = function(cell_width, cell_height) { + return this.rule_map[rule].getLegendDiv(cell_width, cell_height); }; } D3SVGBarChartRuleSet.prototype = Object.create(D3SVGRuleSet.prototype); @@ -236,13 +239,16 @@ window.oncoprint_RuleSet = (function() { z_index: 3 }); altered_rules.push(down_rppa_rule); - self.putLegendGroup = function(svg, cell_width, cell_height) { - var group = svg.append('g'); + self.getLegendDiv = function(cell_width, cell_height) { + var div = d3.select(document.createElement('div')); _.each(self.getRules(), function(rule) { - rule.putLegendGroup(group, cell_width, cell_height); - }) - utils.spaceSVGElementsHorizontally(group, 20); - return group; + var legend_div = rule.getLegendDiv(cell_width, cell_height); + if (legend_div) { + div.node().appendChild(legend_div); + } + }); + utils.d3SelectChildren(div, '*').style('padding-right', '20px'); + return div.node(); }; self.alteredData = function(data) { var altered_data = []; @@ -362,31 +368,29 @@ window.oncoprint_RuleSet = (function() { this.attrs.y = y_function; this.attrs.fill = params.fill || '#000000'; - this.putLegendGroup = function(svg, cell_width, cell_height) { - // TODO: triangle legend piece + this.getLegendDiv = function(cell_width, cell_height) { if (params.exclude_from_legend) { return; } - var group = svg.append('g'); - group.append('text').text(this.data_range[0]).attr('alignment-baseline', 'hanging'); - var rect_group = group.append('g'); + var div = d3.select(document.createElement('div')); + div.append('h2').text(this.data_range[0]).classed('oncoprint-legend-label', true); var mesh = 50; + var svg = div.append('svg').attr('width', mesh+'px').attr('height', cell_height+'px'); for (var i=0; i<=mesh; i++) { var t = i/mesh; var d = (1-t)*this.data_range[0] + t*this.data_range[1]; var datum = makeDatum(d); var height = cell_height*height_helper(datum)/100; - rect_group.append('rect') - .attr('width', 1) + svg.append('rect') + .attr('width', '1px') .attr('height', height) - .attr('y', cell_height-height) - .attr('fill', this.attrs.fill); + .attr('y', (cell_height-height)+'px') + .attr('fill', this.attrs.fill) + .attr('x', i+'px'); } - utils.spaceSVGElementsHorizontally(rect_group, 0); - group.append('text').text(this.data_range[1]).attr('alignment-baseline', 'hanging'); - utils.spaceSVGElementsHorizontally(group, 10); - - return group; + div.append('h2').text(this.data_range[1]).classed('oncoprint-legend-label', true); + utils.d3SelectChildren(div, '*').style('padding-right', '10px'); + return div.node(); }; } D3SVGBarChartRule.prototype = Object.create(D3SVGRule.prototype); @@ -461,22 +465,24 @@ window.oncoprint_RuleSet = (function() { utils.spaceSVGElementsHorizontally(gradient_group, 0); }; - this.putLegendGroup = function(svg) { + this.getLegendDiv = function() { if (params.exclude_from_legend) { return; } - var group = svg.append('g'); - - group.append('text').text(this.data_range[0]).attr('alignment-baseline', 'hanging'); + var div = d3.select(document.createElement('div')); + div.append('h2').text(this.data_range[0]).classed('oncoprint-legend-label', true); + var gradient_height = 20; + var gradient_width = 100; + var gradient_group = div.append('svg').attr('width', gradient_width).attr('height', gradient_height) + .append('g'); if (params.scale === 'log') { - putLogGradient(group, this.color_range, 100, 20); + putLogGradient(gradient_group, this.color_range, gradient_width, gradient_height); } else { - putLinearGradient(group, this.color_range, 100, 20); + putLinearGradient(gradient_group, this.color_range, gradient_width, gradient_height); } - group.append('text').text(this.data_range[1]).attr('alignment-baseline', 'hanging'); - - utils.spaceSVGElementsHorizontally(group, 10); - return group; + div.append('h2').text(this.data_range[1]).classed('oncoprint-legend-label', true); + utils.d3SelectChildren(div, '*').style('padding-right', '10px'); + return div.node(); }; } D3SVGGradientRule.prototype = Object.create(D3SVGRule.prototype); @@ -484,19 +490,19 @@ window.oncoprint_RuleSet = (function() { function D3SVGStaticRule(params, rule_id) { D3SVGRule.call(this, params, rule_id); - this.putLegendGroup = function(svg, cell_width, cell_height) { + this.getLegendDiv = function(cell_width, cell_height) { if (params.exclude_from_legend) { return; } - var group = svg.append('g'); - var g = group.append('g'); - this.apply(g, cell_width, cell_height); + var div = d3.select(document.createElement('div')); + var svg_ctr = div.append('div'); + var svg = svg_ctr.append('svg').attr('width', cell_width+'px').attr('height', cell_height+'px'); + this.apply(svg, cell_width, cell_height); if (this.legend_label) { - group.append('text').text(this.legend_label) - .attr('alignment-baseline', 'hanging'); + div.append('h2').text(this.legend_label).classed('oncoprint-legend-label', true); } - utils.spaceSVGElementsHorizontally(group, 10); - return group; + utils.d3SelectChildren(div, '*').style('padding-right', '10px'); + return div.node(); }; } D3SVGStaticRule.prototype = Object.create(D3SVGRule.prototype); diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index ef1d9402078..69ce6effcb5 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -48,7 +48,7 @@ var alteration_data; var alteration_track_id; var alteration_data_promise = $.getJSON('dist/test/gbm/tp53.json'); -var tracks_to_load = 15; +var tracks_to_load = 8; onc.suppressRendering(); gender_data_promise.then(function(data) { @@ -66,7 +66,7 @@ $.when(gender_data_promise).then(function() { }); onc.setTrackData(gender_track_id, gender_data); - for (var i=0; i<9; i++) { + for (var i=0; i<2; i++) { var dup_gender_track_id = onc.addTrack({label: 'Gender'}); onc.useSameRuleSet(dup_gender_track_id, gender_track_id); onc.setTrackData(dup_gender_track_id, gender_data); From 8eb10692086ba97f6e4d2f5295e270f93a5fe76e Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Mon, 6 Jul 2015 19:19:35 -0400 Subject: [PATCH 094/343] small bug fix --- packages/oncoprintjs/src/js/defaults.js | 1 + packages/oncoprintjs/test/js/test_page.js | 52 +---------------------- 2 files changed, 2 insertions(+), 51 deletions(-) diff --git a/packages/oncoprintjs/src/js/defaults.js b/packages/oncoprintjs/src/js/defaults.js index 4a594b4d22e..f6725971306 100644 --- a/packages/oncoprintjs/src/js/defaults.js +++ b/packages/oncoprintjs/src/js/defaults.js @@ -1,4 +1,5 @@ window.oncoprint_defaults = (function() { + var utils = window.oncoprint_utils; var genetic_alteration_config = { default_color: '#D3D3D3', cna_key: 'cna', diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index 69ce6effcb5..7e70a79cd8f 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -127,57 +127,7 @@ alteration_data_promise.then(function(data) { $.when(alteration_data_promise).then(function() { alteration_track_id = onc.addTrack({label: 'TP53'}, 1); tracks_to_load -= 1; - onc.setRuleSet(alteration_track_id, Oncoprint.GENETIC_ALTERATION, { - default_color: '#D3D3D3', - cna_key: 'cna', - cna: { - color: { - AMPLIFIED: 'red', - GAINED: '#FFB6C1', - HOMODELETED: '#0000FF', - HETLOSS: '#8FD8D8', - }, - label: { - AMPLIFIED: 'Amplification', - GAINED: 'Gain', - HOMODELETED: 'Homozygous Deletion', - HETLOSS: 'Heterozygous Deletion' - } - - }, - mut_type_key: 'mut_type', - mut: { - color: { - MISSENSE: 'green', - INFRAME: '#9F8170', - TRUNC: 'black', - }, - label: { - MISSENSE: 'Missense Mutation', - INFRAME: 'In-Frame Insertion/Deletion', - TRUNC: 'Truncating Mutation' - } - }, - legend_label: 'Genetic Alteration', - mrna_key: 'mut_type', - mrna: { - color: { - MISSENSE: '#C9C900', - UPREGULATED: '#FF9999', - DOWNREGULATED: '#6699CC' - }, - label: { - MISSENSE: 'MRNA MISSENSE', - UPREGULATED: 'mRNA Upregulation', - DOWNREGULATED: 'mRNA Downregulation' - } - }, - rppa_key: 'mut_type', - rppa_down: 'MISSENSE', - rppa_up: 'MISSENSE', - rppa_down_label: 'Protein Downregulation', - rppa_up_label: 'Protein Upregulation' - }); + onc.setRuleSet(alteration_track_id, Oncoprint.GENETIC_ALTERATION); onc.setTrackData(alteration_track_id, alteration_data); var second_alt_track = onc.addTrack({Label: 'TP53 duplicate'}, 1); From 7e7916066f1e210f0aabefb8115211a54557b0fc Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Tue, 7 Jul 2015 14:33:38 -0400 Subject: [PATCH 095/343] html for label area instead of svg --- .../src/js/OncoprintSVGRenderer.js | 74 +++++++++++-------- 1 file changed, 42 insertions(+), 32 deletions(-) diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index ab7b8c1fac8..3c08285c663 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -46,7 +46,7 @@ OncoprintRenderer.call(this, oncoprint, config); var self = this; this.toolbar_container; - this.label_svg; + this.label_div; this.label_container; this.cell_container; this.cell_container_node; @@ -64,8 +64,8 @@ })(); (function initLabelContainer() { self.label_container = d3.select(container_selector_string).append('div').classed(LABEL_AREA_CONTAINER_CLASS, true); - self.label_svg = self.label_container.append('svg').attr('viewport-fill', '#ffffff'); - $(self.label_svg.node()).on("mousedown", + self.label_div = self.label_container.append('div').style('position', 'relative').attr('viewport-fill', '#ffffff'); + $(self.label_div.node()).on("mousedown", function startDraggingLabel(evt) { if (evt.stopPropagation) { evt.stopPropagation(); @@ -85,7 +85,9 @@ } }); if (to_drag !== false) { - self.dragLabel(to_drag); + if (self.oncoprint.getContainingTrackGroup(to_drag).length > 1) { + self.dragLabel(to_drag); + } } } ); @@ -176,16 +178,25 @@ } // Containers - OncoprintSVGRenderer.prototype.getLabelSVG = function() { - return this.label_svg; + OncoprintSVGRenderer.prototype.getLabelDiv = function() { + return this.label_div; }; OncoprintSVGRenderer.prototype.resizeCellDiv = function() { this.cell_div.style('min-width', this.getCellAreaWidth()+'px') .style('min-height', this.getCellAreaHeight()+'px'); }; - OncoprintSVGRenderer.prototype.resizeLabelSVG = function() { - this.getLabelSVG().attr('width', this.getLabelAreaWidth()+'px') - .attr('height', this.getLabelAreaHeight()+'px'); + OncoprintSVGRenderer.prototype.resizeLabelDiv = function() { + var div = this.getLabelDiv(); + var width = 0; + var height = 0; + utils.d3SelectChildren(div, '*').each(function() { + var max_x = (parseInt(this.style.left) || 0) + this.offsetWidth; + var max_y = (parseInt(this.style.top) || 0) + this.offsetHeight; + width = Math.max(max_x, width); + height = Math.max(max_y, height); + }); + this.getLabelDiv().style('width', width+10+'px') + .style('height', height+'px'); }; // Labels @@ -193,10 +204,10 @@ return OncoprintRenderer.prototype.getTrackLabelCSSClass.call(this, track_id)+' oncoprint-track-label-draggable'; }; OncoprintSVGRenderer.prototype.renderTrackLabels = function(track_ids, y) { - var svg = this.label_svg; + var div = this.label_div; track_ids = typeof track_ids === "undefined" ? this.oncoprint.getTracks() : track_ids; if (typeof y !== "undefined") { - svg.selectAll(this.getTrackLabelCSSSelector(track_ids)).attr('y', y+'px'); + div.selectAll(this.getTrackLabelCSSSelector(track_ids)).style('top', y+'px'); } else { track_ids = [].concat(track_ids); var label_tops = this.getTrackLabelTops(); @@ -205,30 +216,34 @@ _.each(track_ids, function(track_id) { var label_top = label_tops[track_id]; var track_label_class = self.getTrackLabelCSSClass(track_id); - svg.selectAll(self.getTrackLabelCSSSelector(track_id)).remove(); - svg.append('text') + div.selectAll(self.getTrackLabelCSSSelector(track_id)).remove(); + div.append('span') + .style('position','absolute') .classed(self.getTrackLabelCSSClass(track_id), true) .classed('noselect', true) - .attr('font', self.getLabelFont()) + .style('font-family', self.getLabelFont()) .text(self.oncoprint.getTrackLabel(track_id)) - .attr('y', label_top+'px'); + .style('top', label_top+'px') + .style('cursor', 'move'); var rule_set = self.getRuleSet(track_id); if (rule_set && rule_set.alteredData) { var data = self.oncoprint.getTrackData(track_id); var num_altered = rule_set.alteredData(data).length; var percent_altered = Math.floor(100*num_altered/data.length); - svg.append('text') + div.append('span') + .style('position','absolute') .classed(self.getTrackLabelCSSClass(track_id), true) .classed('noselect', true) - .attr('font', self.getLabelFont()) + .attr('font-family', self.getLabelFont()) .text(percent_altered + '%') - .attr('text-anchor', 'end') - .attr('y', label_top+'px') - .attr('x', label_area_width+'px'); + .style('text-anchor', 'end') + .style('top', label_top+'px') + .style('left', label_area_width+'px'); } }); } + this.resizeLabelDiv(); }; // Cells @@ -388,7 +403,7 @@ drag_bounds[1] = utils.clamp(track_tops[last_track]+this.getRenderedTrackHeight(last_track), 0, label_area_height); var self = this; - var $label_svg = $(self.label_svg.node()); + var $label_div = $(self.label_div.node()); delete track_tops[track_id]; (function(track_id) { @@ -400,25 +415,20 @@ if (evt.preventDefault) { evt.preventDefault(); } - var track_tops_tmp = $.extend({},{}, track_tops); var mouse_y = utils.clamp(utils.mouseY(evt), drag_bounds[0], drag_bounds[1]); self.renderTrackLabels(track_id, mouse_y); d3.selectAll(self.getTrackLabelCSSSelector(track_id)).classed(LABEL_DRAGGING_CLASS, true); new_pos = _.sortedIndex(group_track_tops, mouse_y); - if (new_pos > 0) { - track_tops_tmp[track_group[new_pos-1]] -= 3; - } - if (new_pos < track_group.length) { - track_tops_tmp[track_group[new_pos]] += 3; - } - _.each(track_tops_tmp, function(top,id){ + _.each(track_tops, function(top, id) { + top += 3*(+(new_pos < track_group.length && track_group[new_pos] == id)); + top -= 3*(+(new_pos > 0 && track_group[new_pos-1] == id)); self.renderTrackLabels(id, top); }); } - $label_svg.on("mousemove", moveHandler); - $label_svg.one("mouseleave mouseup", function(evt) { - $label_svg.off("mousemove", moveHandler); + $label_div.on("mousemove", moveHandler); + $label_div.one("mouseleave mouseup", function(evt) { + $label_div.off("mousemove", moveHandler); if (new_pos > -1) { self.oncoprint.moveTrack(track_id, new_pos); } From 24a8ef7a4dccafa6a4ebe3ff03b818b54d5c6ea8 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Tue, 7 Jul 2015 15:40:42 -0400 Subject: [PATCH 096/343] dragging in html container, and styling bug fixes --- packages/oncoprintjs/src/css/oncoprint.css | 2 +- .../src/js/OncoprintSVGRenderer.js | 25 ++++++------------- 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/packages/oncoprintjs/src/css/oncoprint.css b/packages/oncoprintjs/src/css/oncoprint.css index 35f110deed8..3b5b697bd69 100644 --- a/packages/oncoprintjs/src/css/oncoprint.css +++ b/packages/oncoprintjs/src/css/oncoprint.css @@ -54,7 +54,7 @@ cursor: move; } .oncoprint-label-dragging { - font-weight: bold; + font-weight: bold !important; } .noselect { diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index 3c08285c663..dbc8a6cda44 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -64,7 +64,7 @@ })(); (function initLabelContainer() { self.label_container = d3.select(container_selector_string).append('div').classed(LABEL_AREA_CONTAINER_CLASS, true); - self.label_div = self.label_container.append('div').style('position', 'relative').attr('viewport-fill', '#ffffff'); + self.label_div = self.label_container.append('div').style('position', 'relative').attr('viewport-fill', '#ffffff').style('overflow', 'hidden'); $(self.label_div.node()).on("mousedown", function startDraggingLabel(evt) { if (evt.stopPropagation) { @@ -119,6 +119,7 @@ self.drawCells(d.track_id); self.positionCells(); self.renderTrackLabels(); + self.resizeLabelDiv(); //this.cell_div.style('display','inherit'); }); @@ -186,17 +187,8 @@ .style('min-height', this.getCellAreaHeight()+'px'); }; OncoprintSVGRenderer.prototype.resizeLabelDiv = function() { - var div = this.getLabelDiv(); - var width = 0; - var height = 0; - utils.d3SelectChildren(div, '*').each(function() { - var max_x = (parseInt(this.style.left) || 0) + this.offsetWidth; - var max_y = (parseInt(this.style.top) || 0) + this.offsetHeight; - width = Math.max(max_x, width); - height = Math.max(max_y, height); - }); - this.getLabelDiv().style('width', width+10+'px') - .style('height', height+'px'); + this.getLabelDiv().style('width', this.getLabelAreaWidth()+'px') + .style('height', this.getLabelAreaHeight()+'px'); }; // Labels @@ -213,6 +205,7 @@ var label_tops = this.getTrackLabelTops(); var self = this; var label_area_width = this.getLabelAreaWidth(); + var percent_altered_left = label_area_width - utils.textWidth('100%', self.getLabelFont()); _.each(track_ids, function(track_id) { var label_top = label_tops[track_id]; var track_label_class = self.getTrackLabelCSSClass(track_id); @@ -221,7 +214,7 @@ .style('position','absolute') .classed(self.getTrackLabelCSSClass(track_id), true) .classed('noselect', true) - .style('font-family', self.getLabelFont()) + .style('font', self.getLabelFont()) .text(self.oncoprint.getTrackLabel(track_id)) .style('top', label_top+'px') .style('cursor', 'move'); @@ -235,15 +228,13 @@ .style('position','absolute') .classed(self.getTrackLabelCSSClass(track_id), true) .classed('noselect', true) - .attr('font-family', self.getLabelFont()) + .style('font', self.getLabelFont()) .text(percent_altered + '%') - .style('text-anchor', 'end') .style('top', label_top+'px') - .style('left', label_area_width+'px'); + .style('left', percent_altered_left+'px'); } }); } - this.resizeLabelDiv(); }; // Cells From f6066f56c19abbaf0a5daac4d1d93a6c8580ddd4 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Tue, 7 Jul 2015 17:15:56 -0400 Subject: [PATCH 097/343] update genetic_alteration_config and _comparator --- packages/oncoprintjs/src/js/defaults.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/oncoprintjs/src/js/defaults.js b/packages/oncoprintjs/src/js/defaults.js index f6725971306..bdbd818fc03 100644 --- a/packages/oncoprintjs/src/js/defaults.js +++ b/packages/oncoprintjs/src/js/defaults.js @@ -32,29 +32,27 @@ window.oncoprint_defaults = (function() { } }, legend_label: 'Genetic Alteration', - mrna_key: 'mut_type', + mrna_key: 'mrna', mrna: { color: { - MISSENSE: '#C9C900', UPREGULATED: '#FF9999', DOWNREGULATED: '#6699CC' }, label: { - MISSENSE: 'MRNA MISSENSE', UPREGULATED: 'mRNA Upregulation', DOWNREGULATED: 'mRNA Downregulation' } }, - rppa_key: 'mut_type', - rppa_down: 'MISSENSE', - rppa_up: 'MISSENSE', + rppa_key: 'rppa', + rppa_down: 'DOWNREGULATED', + rppa_up: 'UPREGULATED', rppa_down_label: 'Protein Downregulation', rppa_up_label: 'Protein Upregulation', }; var genetic_alteration_comparator = function(d1,d2) { var cna_order = utils.invert_array(['AMPLIFIED', 'HOMODELETED', 'GAINED', 'HEMIZYGOUSLYDELETED', 'DIPLOID', undefined]); - var mut_order = utils.invert_array([]); // TODO: mutation order + var mut_order = utils.invert_array(['MISSENSE', 'INFRAME', 'TRUNC', undefined]); var regulation_order = utils.invert_array(['UPREGULATED', 'DOWNREGULATED', undefined]); var cna_key = genetic_alteration_config.cna_key; From 69d7f72d8ae914ad548c0b97a3a2b6797724f380 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Tue, 7 Jul 2015 17:45:04 -0400 Subject: [PATCH 098/343] beginning of toolbar and adding default sort to genetic alteration ruleset --- packages/oncoprintjs/src/js/OncoprintRenderer.js | 9 ++++++++- .../oncoprintjs/src/js/OncoprintSVGRenderer.js | 4 ++-- packages/oncoprintjs/src/js/RuleSet.js | 1 + packages/oncoprintjs/src/js/events.js | 3 ++- packages/oncoprintjs/src/js/oncoprint.js | 15 ++++++++++++--- 5 files changed, 25 insertions(+), 7 deletions(-) diff --git a/packages/oncoprintjs/src/js/OncoprintRenderer.js b/packages/oncoprintjs/src/js/OncoprintRenderer.js index 5e848632744..10ae7757dac 100644 --- a/packages/oncoprintjs/src/js/OncoprintRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintRenderer.js @@ -64,9 +64,16 @@ window.OncoprintRenderer = (function() { OncoprintRenderer.prototype.setRuleSet = function(track_id, type, params) { var new_rule_set = RuleSet.makeRuleSet(type, params); this.rule_sets[track_id] = new_rule_set; + if (new_rule_set.sort_cmp) { + this.oncoprint.setTrackDataComparator(track_id, new_rule_set.sort_cmp); + } }; OncoprintRenderer.prototype.useSameRuleSet = function(target_track_id, source_track_id) { - this.rule_sets[target_track_id] = this.rule_sets[source_track_id]; + var rule_set = this.rule_sets[source_track_id]; + this.rule_sets[target_track_id] = rule_set; + if (rule_set.sort_cmp) { + this.oncoprint.setTrackDataComparator(target_track_id, rule_set.sort_cmp); + } }; OncoprintRenderer.prototype.getRuleSet = function(track_id) { return this.rule_sets[track_id]; diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index dbc8a6cda44..571e32f5107 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -58,9 +58,9 @@ (function initToolbarContainer() { self.toolbar_container = d3.select(container_selector_string).append('div').classed(TOOLBAR_CONTAINER_CLASS, true); d3.select(container_selector_string).append('br'); - /*$.ajax({url: "toolbar.html", context: document.body, success: function(response) { + $.ajax({url: "src/html/toolbar.html", context: document.body, success: function(response) { $(self.toolbar_container.node()).html(response); - }});*/ + }}); })(); (function initLabelContainer() { self.label_container = d3.select(container_selector_string).append('div').classed(LABEL_AREA_CONTAINER_CLASS, true); diff --git a/packages/oncoprintjs/src/js/RuleSet.js b/packages/oncoprintjs/src/js/RuleSet.js index 8c9cf78c193..1912b6104c5 100644 --- a/packages/oncoprintjs/src/js/RuleSet.js +++ b/packages/oncoprintjs/src/js/RuleSet.js @@ -155,6 +155,7 @@ window.oncoprint_RuleSet = (function() { D3SVGRuleSet.call(this, params); var self = this; self.type = GENETIC_ALTERATION; + self.sort_cmp = params.sort_cmp || defaults.genetic_alteration_comparator; var default_rule = this.addStaticRule({ shape: utils.makeD3SVGElement('rect').attr('fill', params.default_color), exclude_from_legend: true, diff --git a/packages/oncoprintjs/src/js/events.js b/packages/oncoprintjs/src/js/events.js index 40ad9410ad1..6ba7186bb9e 100644 --- a/packages/oncoprintjs/src/js/events.js +++ b/packages/oncoprintjs/src/js/events.js @@ -47,5 +47,6 @@ window.oncoprint_events = { FINISHED_RENDERING: 'finished_rendering.renderer.oncoprint', FINISHED_POSITIONING: 'finished_positioning.renderer.oncoprint', SET_ZOOM: 'set_zoom.oncoprint', - ADD_TRACK_SEPARATOR: 'add_track_separator.oncoprint' + ADD_TRACK_SEPARATOR: 'add_track_separator.oncoprint', + SET_DATA_COMPARATOR: 'set_data_comparator.oncoprint' }; diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 66a54c4fe7f..9b2329dd4ab 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -34,7 +34,7 @@ window.Oncoprint = (function() { var defaultOncoprintConfig = { cell_width: 6, - cell_padding: 5, + cell_padding: 2.5, legend: true, }; @@ -124,13 +124,22 @@ window.Oncoprint = (function() { self.id_order = id_order; $(self).trigger(events.SET_ID_ORDER); }; + self.setTrackDataComparator = function(track_id, cmp) { + self.tracks[track_id].config.sort_cmp = cmp; + $(self).trigger(events.SET_DATA_COMPARATOR); + }; + self.getTrackDataComparator = function(track_id) { + return self.tracks[track_id].config.sort_cmp; + }; self.sort = function(track_id_list, cmp_list) { - track_id_list = [].concat(track_id_list); + track_id_list = track_id_list ? [].concat(track_id_list) : self.getTracks(); cmp_list = [].concat(cmp_list); var lexicographically_ordered_cmp = function(id1,id2) { var cmp_result; for (var i=0, _len = track_id_list.length; i<_len; i++) { - cmp_result = cmp_list[i](self.getTrackDatum(track_id_list[i], id1),self.getTrackDatum(track_id_list[i], id2)); + var track_id = track_id_list[i]; + var cmp = cmp_list[i] || self.getTrackDataComparator(track_id); + cmp_result = cmp(self.getTrackDatum(track_id, id1),self.getTrackDatum(track_id, id2)); if (cmp_result !== 0) { break; } From 091f8f17fdac7cd48279e2be5fdfe588fa41af0f Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Tue, 7 Jul 2015 18:58:22 -0400 Subject: [PATCH 099/343] positioning optimization --- packages/oncoprintjs/src/html/toolbar.html | 16 ++------ .../src/js/OncoprintSVGRenderer.js | 41 ++++++++++--------- 2 files changed, 25 insertions(+), 32 deletions(-) diff --git a/packages/oncoprintjs/src/html/toolbar.html b/packages/oncoprintjs/src/html/toolbar.html index 74bf57e627c..5f1e74a33a9 100644 --- a/packages/oncoprintjs/src/html/toolbar.html +++ b/packages/oncoprintjs/src/html/toolbar.html @@ -1,12 +1,4 @@ - - - - - - JS Bin - - -
+
- +
-
- - \ No newline at end of file +
\ No newline at end of file diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index 571e32f5107..d02208cfbff 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -58,9 +58,9 @@ (function initToolbarContainer() { self.toolbar_container = d3.select(container_selector_string).append('div').classed(TOOLBAR_CONTAINER_CLASS, true); d3.select(container_selector_string).append('br'); - $.ajax({url: "src/html/toolbar.html", context: document.body, success: function(response) { + /*$.ajax({url: "src/html/toolbar.html", context: document.body, success: function(response) { $(self.toolbar_container.node()).html(response); - }}); + }});*/ })(); (function initLabelContainer() { self.label_container = d3.select(container_selector_string).append('div').classed(LABEL_AREA_CONTAINER_CLASS, true); @@ -97,6 +97,8 @@ //self.cell_container.style('display', 'none'); self.cell_container_node = self.cell_container.node(); self.cell_div = self.cell_container.append('div').classed(CELL_AREA_CLASS, true); + // TODO: magic number + self.cell_div.style('max-width', '1000px'); })(); (function initLegend() { if (config.legend) { @@ -110,7 +112,7 @@ self.render(); }); $(oncoprint).on(events.MOVE_TRACK, function(evt, data) { - self.positionCells(data.moved_tracks); + self.positionCells(data.moved_tracks, 'top'); self.renderTrackLabels(); }); @@ -132,23 +134,20 @@ //this.cell_div.style('display','inherit'); }); - $(oncoprint).on(events.MOVE_TRACK, function(e,d) { - self.positionCells(d.moved_tracks); - }); $(oncoprint).on(events.SET_CELL_PADDING, function(e,d) { - self.positionCells(); + self.positionCells(undefined, 'left'); self.resizeCellDiv(); }); $(oncoprint).on(events.SET_ZOOM, function(e,d) { - self.positionCells(); + self.positionCells(undefined, 'left'); self.resizeCells(); self.resizeCellDiv(); }); $(oncoprint).on(events.SET_ID_ORDER, function() { - self.positionCells(); + self.positionCells(undefined, 'left'); }) })(); } @@ -306,26 +305,30 @@ }; // Positioning - OncoprintSVGRenderer.prototype.positionTrackCells = function(track_id, bound_svg) { + OncoprintSVGRenderer.prototype.positionTrackCells = function(track_id, axis) { var oncoprint = this.oncoprint; - if (!bound_svg) { - bound_svg = this.cell_div.selectAll('svg.'+this.getTrackCellCSSClass(track_id)) + var bound_svg = this.cell_div.selectAll('svg.'+this.getTrackCellCSSClass(track_id)) .data(oncoprint.getTrackData(track_id), oncoprint.getTrackDatumIdAccessor(track_id)); - } var self = this; var id_key = oncoprint.getTrackDatumIdKey(track_id); - var id_order = oncoprint.getIdOrder(); + var id_order = utils.invert_array(oncoprint.getIdOrder()); var y = this.getTrackCellTops()[track_id]; - bound_svg.transition().style('left', function(d,i) { - return self.getCellX(id_order.indexOf(d[id_key]))+'px'; - }).style('top', y+'px'); + var moving = bound_svg.transition(); + if (!axis || axis === 'left') { + moving.style('left', function(d,i) { + return self.getCellX(id_order[d[id_key]])+'px'; + }); + } + if (!axis || axis === 'top') { + moving.style('top', y+'px'); + } }; - OncoprintSVGRenderer.prototype.positionCells = function(track_ids) { + OncoprintSVGRenderer.prototype.positionCells = function(track_ids, axis) { track_ids = typeof track_ids === "undefined" ? this.oncoprint.getTracks() : track_ids; track_ids = [].concat(track_ids); var self = this; _.each(track_ids, function(track_id) { - self.positionTrackCells(track_id); + self.positionTrackCells(track_id, axis); }); }; From ec8c5ea9407432caa0f3f8431a8a8b56061895fb Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Tue, 7 Jul 2015 19:04:14 -0400 Subject: [PATCH 100/343] turn off transition when its too slow --- packages/oncoprintjs/src/js/OncoprintSVGRenderer.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index d02208cfbff..7c7c3cbc693 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -42,6 +42,8 @@ var LABEL_DRAGGABLE_CLASS = 'oncoprint-label-draggable'; var CELL_QTIP_CLASS = 'oncoprint-cell-qtip'; + var SMOOTH_TRANSITION_CELL_LIMIT = 2000; + function OncoprintSVGRenderer(container_selector_string, oncoprint, config) { OncoprintRenderer.call(this, oncoprint, config); var self = this; @@ -313,7 +315,7 @@ var id_key = oncoprint.getTrackDatumIdKey(track_id); var id_order = utils.invert_array(oncoprint.getIdOrder()); var y = this.getTrackCellTops()[track_id]; - var moving = bound_svg.transition(); + var moving = bound_svg[0].length > SMOOTH_TRANSITION_CELL_LIMIT ? bound_svg : bound_svg.transition(); if (!axis || axis === 'left') { moving.style('left', function(d,i) { return self.getCellX(id_order[d[id_key]])+'px'; From e5f52ff81d2c371d07a95bb7f466a21829122025 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Tue, 7 Jul 2015 19:16:59 -0400 Subject: [PATCH 101/343] smart transitioning - only animate cells in view --- .../src/js/OncoprintSVGRenderer.js | 38 +++++++++++++++---- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index 7c7c3cbc693..b6d7f863cd0 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -42,8 +42,6 @@ var LABEL_DRAGGABLE_CLASS = 'oncoprint-label-draggable'; var CELL_QTIP_CLASS = 'oncoprint-cell-qtip'; - var SMOOTH_TRANSITION_CELL_LIMIT = 2000; - function OncoprintSVGRenderer(container_selector_string, oncoprint, config) { OncoprintRenderer.call(this, oncoprint, config); var self = this; @@ -154,7 +152,12 @@ })(); } utils.extends(OncoprintSVGRenderer, OncoprintRenderer); - + OncoprintSVGRenderer.prototype.getVisibleInterval = function() { + var cell_unit = this.oncoprint.getZoomedCellWidth() + this.oncoprint.getCellPadding(); + var cell_ctr_rect = this.cell_container_node.getBoundingClientRect(); + var view_interval = [this.cell_container_node.scrollLeft, this.cell_container_node.scrollLeft + cell_ctr_rect.right - cell_ctr_rect.left]; + return view_interval; + }; OncoprintSVGRenderer.prototype.cellRenderTarget = function() { return d3.select(this.document_fragment || this.cell_div.node()); }; @@ -315,14 +318,33 @@ var id_key = oncoprint.getTrackDatumIdKey(track_id); var id_order = utils.invert_array(oncoprint.getIdOrder()); var y = this.getTrackCellTops()[track_id]; - var moving = bound_svg[0].length > SMOOTH_TRANSITION_CELL_LIMIT ? bound_svg : bound_svg.transition(); + var visible_interval = this.getVisibleInterval(); + + var in_view = function(i) { + return i >= visible_interval[0] && i <= visible_interval[1]; + }; + var left_fn = function(d) { + return self.getCellX(id_order[d[id_key]]); + }; + var animated = bound_svg.filter(function(d,i) { + var ret = (this.style.left && in_view(parseInt(this.style.left))) || in_view(left_fn(d)); + return ret; + }).transition(); + var nonanimated = bound_svg.filter(function(d,i) { + var ret = !((this.style.left && in_view(parseInt(this.style.left))) || in_view(left_fn(d))); + return ret; + }); if (!axis || axis === 'left') { - moving.style('left', function(d,i) { - return self.getCellX(id_order[d[id_key]])+'px'; - }); + var left_px_fn = function(d,i) { + return left_fn(d)+'px'; + }; + animated.style('left', left_px_fn); + nonanimated.style('left', left_px_fn); } if (!axis || axis === 'top') { - moving.style('top', y+'px'); + var top = y + 'px'; + animated.style('top', top); + nonanimated.style('top', top); } }; OncoprintSVGRenderer.prototype.positionCells = function(track_ids, axis) { From 9423929f2376e55e51114b9377aaf602d351c6e0 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Wed, 8 Jul 2015 13:26:06 -0400 Subject: [PATCH 102/343] fix random swapping issue --- packages/oncoprintjs/src/js/OncoprintSVGRenderer.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index b6d7f863cd0..4649660d8d0 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -447,6 +447,7 @@ $label_div.on("mousemove", moveHandler); $label_div.one("mouseleave mouseup", function(evt) { $label_div.off("mousemove", moveHandler); + $label_div.off("mouseleave mouseup"); if (new_pos > -1) { self.oncoprint.moveTrack(track_id, new_pos); } From 3324362346d21fff44412dff930157a596b4e436 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Thu, 9 Jul 2015 11:49:42 -0400 Subject: [PATCH 103/343] changes toward fixing smart transitions...currently no transitions --- .../src/js/OncoprintSVGRenderer.js | 49 +++++++++++-------- packages/oncoprintjs/src/js/defaults.js | 2 +- 2 files changed, 30 insertions(+), 21 deletions(-) diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index 4649660d8d0..b4927e7f8a9 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -167,8 +167,12 @@ OncoprintSVGRenderer.prototype.releaseRendering = function() { this.cell_div.node().appendChild(this.document_fragment); this.document_fragment = undefined; - this.resizeCells(); - this.positionCells(); + var self = this; + $(this.cell_div.node()).ready(function() { + console.log(self.cell_div.selectAll('.'+self.getCellCSSClass())[0].length); + self.resizeCells(); + self.positionCells(); + }); }; // Rule sets OncoprintSVGRenderer.prototype.setRuleSet = function(track_id, type, params) { @@ -305,7 +309,6 @@ _.each(track_ids, function(track_id) { self.drawTrackCells(track_ids, fragment); }); - //this.cell_div.node().appendChild(fragment); this.cellRenderTarget().node().appendChild(fragment); }; @@ -323,29 +326,35 @@ var in_view = function(i) { return i >= visible_interval[0] && i <= visible_interval[1]; }; + var currently_in_view = function(node) { + return !isNaN(parseInt(node.style.left)) && in_view(parseInt(node.style.left)); + }; var left_fn = function(d) { return self.getCellX(id_order[d[id_key]]); }; - var animated = bound_svg.filter(function(d,i) { - var ret = (this.style.left && in_view(parseInt(this.style.left))) || in_view(left_fn(d)); - return ret; - }).transition(); - var nonanimated = bound_svg.filter(function(d,i) { - var ret = !((this.style.left && in_view(parseInt(this.style.left))) || in_view(left_fn(d))); - return ret; + var coming_into_view = function(d) { + return in_view(left_fn(d)); + }; + + var animated = bound_svg.filter(function(d) { + return currently_in_view(this) || coming_into_view(d); + }); + var nonanimated = bound_svg.filter(function(d) { + return !(currently_in_view(this) || coming_into_view(d)); }); - if (!axis || axis === 'left') { - var left_px_fn = function(d,i) { - return left_fn(d)+'px'; - }; - animated.style('left', left_px_fn); - nonanimated.style('left', left_px_fn); + + var left_px_fn = function(d,i) { + return left_fn(d)+'px'; + }; + if (!axis || axis === "left") { + animated.style('left', left_px_fn) + nonanimated.style('left', left_px_fn) } - if (!axis || axis === 'top') { - var top = y + 'px'; - animated.style('top', top); - nonanimated.style('top', top); + if (!axis || axis === "top") { + animated.style('top', y+'px'); + nonanimated.style('top', y+'px'); } + // TODO: integrate transitions above }; OncoprintSVGRenderer.prototype.positionCells = function(track_ids, axis) { track_ids = typeof track_ids === "undefined" ? this.oncoprint.getTracks() : track_ids; diff --git a/packages/oncoprintjs/src/js/defaults.js b/packages/oncoprintjs/src/js/defaults.js index bdbd818fc03..fbffa67a3b8 100644 --- a/packages/oncoprintjs/src/js/defaults.js +++ b/packages/oncoprintjs/src/js/defaults.js @@ -52,7 +52,7 @@ window.oncoprint_defaults = (function() { var genetic_alteration_comparator = function(d1,d2) { var cna_order = utils.invert_array(['AMPLIFIED', 'HOMODELETED', 'GAINED', 'HEMIZYGOUSLYDELETED', 'DIPLOID', undefined]); - var mut_order = utils.invert_array(['MISSENSE', 'INFRAME', 'TRUNC', undefined]); + var mut_order = utils.invert_array(['TRUNC', 'INFRAME', 'MISSENSE', undefined]); var regulation_order = utils.invert_array(['UPREGULATED', 'DOWNREGULATED', undefined]); var cna_key = genetic_alteration_config.cna_key; From 7b97d8af1a5f37af772311f123dd7e3339ebd629 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Thu, 9 Jul 2015 14:27:31 -0400 Subject: [PATCH 104/343] move cursor on labels --- packages/oncoprintjs/src/css/oncoprint.css | 1 - .../src/js/OncoprintSVGRenderer.js | 56 ++++++++----------- 2 files changed, 22 insertions(+), 35 deletions(-) diff --git a/packages/oncoprintjs/src/css/oncoprint.css b/packages/oncoprintjs/src/css/oncoprint.css index 3b5b697bd69..05eca7a840e 100644 --- a/packages/oncoprintjs/src/css/oncoprint.css +++ b/packages/oncoprintjs/src/css/oncoprint.css @@ -48,7 +48,6 @@ .oncoprint-track-label { alignment-baseline: hanging; - pointer-events: none; } .oncoprint-track-label-draggable { cursor: move; diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index b4927e7f8a9..e0a93c97193 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -47,6 +47,7 @@ var self = this; this.toolbar_container; this.label_div; + this.label_drag_div; this.label_container; this.cell_container; this.cell_container_node; @@ -63,34 +64,11 @@ }});*/ })(); (function initLabelContainer() { - self.label_container = d3.select(container_selector_string).append('div').classed(LABEL_AREA_CONTAINER_CLASS, true); - self.label_div = self.label_container.append('div').style('position', 'relative').attr('viewport-fill', '#ffffff').style('overflow', 'hidden'); - $(self.label_div.node()).on("mousedown", - function startDraggingLabel(evt) { - if (evt.stopPropagation) { - evt.stopPropagation(); - } - if (evt.preventDefault) { - evt.preventDefault(); - } - var to_drag = false; - var track_tops = self.getTrackTops(); - //var mouse_y = utils.mouseY(evt); - var mouse_y = evt.offsetY; - _.find(track_tops, function(track_top, track_id) { - track_id = parseInt(track_id); - if (mouse_y >= track_top && mouse_y <= track_top + self.getRenderedTrackHeight(track_id)) { - to_drag = track_id; - return true; - } - }); - if (to_drag !== false) { - if (self.oncoprint.getContainingTrackGroup(to_drag).length > 1) { - self.dragLabel(to_drag); - } - } - } - ); + self.label_container = d3.select(container_selector_string).append('div').classed(LABEL_AREA_CONTAINER_CLASS, true).style('position', 'relative'); + self.label_div = self.label_container.append('div').style('position', 'relative').style('overflow', 'hidden'); + self.label_drag_div = self.label_container.append('div').style('position', 'absolute').style('overflow', 'hidden') + .style('top', '0px').style('left','0px') + .style('display','none'); })(); (function initCellContainer() { self.cell_container = d3.select(container_selector_string).append('div').classed(CELL_AREA_CONTAINER_CLASS, true); @@ -190,6 +168,9 @@ OncoprintSVGRenderer.prototype.getLabelDiv = function() { return this.label_div; }; + OncoprintSVGRenderer.prototype.getLabelDragDiv = function() { + return this.label_drag_div; + }; OncoprintSVGRenderer.prototype.resizeCellDiv = function() { this.cell_div.style('min-width', this.getCellAreaWidth()+'px') .style('min-height', this.getCellAreaHeight()+'px'); @@ -197,6 +178,8 @@ OncoprintSVGRenderer.prototype.resizeLabelDiv = function() { this.getLabelDiv().style('width', this.getLabelAreaWidth()+'px') .style('height', this.getLabelAreaHeight()+'px'); + this.getLabelDragDiv().style('width', this.getLabelAreaWidth()+'px') + .style('height', this.getLabelAreaHeight()+'px'); }; // Labels @@ -225,7 +208,10 @@ .style('font', self.getLabelFont()) .text(self.oncoprint.getTrackLabel(track_id)) .style('top', label_top+'px') - .style('cursor', 'move'); + .style('cursor', 'move') + .on("mousedown", function() { + self.dragLabel(track_id); + }); var rule_set = self.getRuleSet(track_id); if (rule_set && rule_set.alteredData) { @@ -413,6 +399,7 @@ }); }; OncoprintSVGRenderer.prototype.dragLabel = function(track_id) { + this.getLabelDragDiv().style('display','block'); var track_group = this.oncoprint.getContainingTrackGroup(track_id); var first_track = track_group[0], last_track=track_group[track_group.length-1]; var all_track_tops = this.getTrackLabelTops(); @@ -430,7 +417,7 @@ drag_bounds[1] = utils.clamp(track_tops[last_track]+this.getRenderedTrackHeight(last_track), 0, label_area_height); var self = this; - var $label_div = $(self.label_div.node()); + var $label_drag_div = $(self.getLabelDragDiv().node()); delete track_tops[track_id]; (function(track_id) { @@ -453,10 +440,11 @@ self.renderTrackLabels(id, top); }); } - $label_div.on("mousemove", moveHandler); - $label_div.one("mouseleave mouseup", function(evt) { - $label_div.off("mousemove", moveHandler); - $label_div.off("mouseleave mouseup"); + $label_drag_div.on("mousemove", moveHandler); + $label_drag_div.one("mouseleave mouseup", function(evt) { + $label_drag_div.hide(); + $label_drag_div.off("mousemove", moveHandler); + $label_drag_div.off("mouseleave mouseup"); if (new_pos > -1) { self.oncoprint.moveTrack(track_id, new_pos); } From 098545672fd9c59b69458ef277fbab1749f0304e Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Thu, 9 Jul 2015 15:07:15 -0400 Subject: [PATCH 105/343] infer data range for bar chart and gradient rules --- .../src/js/OncoprintSVGRenderer.js | 1 - packages/oncoprintjs/src/js/RuleSet.js | 215 +++++++++--------- packages/oncoprintjs/test/js/test_page.js | 3 - 3 files changed, 113 insertions(+), 106 deletions(-) diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index e0a93c97193..6a045902392 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -147,7 +147,6 @@ this.document_fragment = undefined; var self = this; $(this.cell_div.node()).ready(function() { - console.log(self.cell_div.selectAll('.'+self.getCellCSSClass())[0].length); self.resizeCells(); self.positionCells(); }); diff --git a/packages/oncoprintjs/src/js/RuleSet.js b/packages/oncoprintjs/src/js/RuleSet.js index 1912b6104c5..b4749d45c7b 100644 --- a/packages/oncoprintjs/src/js/RuleSet.js +++ b/packages/oncoprintjs/src/js/RuleSet.js @@ -128,8 +128,8 @@ window.oncoprint_RuleSet = (function() { color_range: params.color_range, scale: params.scale }); - this.getLegendDiv = function() { - return this.rule_map[rule].getLegendDiv(); + this.getLegendDiv = function(cell_width, cell_height) { + return this.rule_map[rule].getLegendDiv(cell_width, cell_height); }; } D3SVGGradientColorRuleSet.prototype = Object.create(D3SVGRuleSet.prototype); @@ -261,21 +261,23 @@ window.oncoprint_RuleSet = (function() { } D3SVGGeneticAlterationRuleSet.prototype = Object.create(D3SVGRuleSet.prototype); - function D3SVGRule(params, rule_id) { - this.rule_id = rule_id; - this.condition = params.condition || function(d) { return true; }; - this.shape = typeof params.shape === 'undefined' ? utils.makeD3SVGElement('rect') : params.shape; - this.z_index = typeof params.z_index === 'undefined' ? this.rule_id : params.z_index; - this.legend_label = params.legend_label; - this.exclude_from_legend = params.exclude_from_legend; + var D3SVGRule = (function() { + function D3SVGRule(params, rule_id) { + this.rule_id = rule_id; + this.condition = params.condition || function(d) { return true; }; + this.shape = typeof params.shape === 'undefined' ? utils.makeD3SVGElement('rect') : params.shape; + this.z_index = typeof params.z_index === 'undefined' ? this.rule_id : params.z_index; + this.legend_label = params.legend_label; + this.exclude_from_legend = params.exclude_from_legend; - this.attrs = params.attrs || {}; - this.attrs.width = utils.ifndef(this.attrs.width, '100%'); - this.attrs.height = utils.ifndef(this.attrs.height, '100%'); - this.attrs.x = utils.ifndef(this.attrs.x, 0); - this.attrs.y = utils.ifndef(this.attrs.y, 0); + this.attrs = params.attrs || {}; + this.attrs.width = utils.ifndef(this.attrs.width, '100%'); + this.attrs.height = utils.ifndef(this.attrs.height, '100%'); + this.attrs.x = utils.ifndef(this.attrs.x, 0); + this.attrs.y = utils.ifndef(this.attrs.y, 0); - this.styles = params.styles || {}; + this.styles = params.styles || {}; + } var percentToPx = function(attr_val, attr_name, cell_width, cell_height) { // convert a percentage to a local pixel coordinate @@ -310,7 +312,7 @@ window.oncoprint_RuleSet = (function() { return ret; }; - this.apply = function(g, cell_width, cell_height) { + D3SVGRule.prototype.apply = function(g, cell_width, cell_height) { var shape = this.shape; var elts = shape === CELL ? g : utils.appendD3SVGElement(shape, g); var styles = this.styles; @@ -326,18 +328,22 @@ window.oncoprint_RuleSet = (function() { elts.style(key, val); }); } - this.filterData = function(data) { + D3SVGRule.prototype.filterData = function(data) { return data.filter(this.condition); }; - this.isActive = function(data) { + D3SVGRule.prototype.isActive = function(data) { return this.filterData(data).length > 0; }; - } + return D3SVGRule; + })(); + function D3SVGBarChartRule(params, rule_id) { D3SVGRule.call(this, params, rule_id); this.data_key = params.data_key; this.data_range = params.data_range; + this.inferred_data_range; + this.attrs.fill = params.fill || '#000000'; var scale = function(x) { if (params.scale === 'log') { @@ -346,53 +352,73 @@ window.oncoprint_RuleSet = (function() { return x; } }; - var makeDatum = function(x) { var ret = {}; ret[params.data_key] = x; return ret; }; - var scaled_data_range = _.map(this.data_range, scale); - var height_helper = function(d) { - var datum = scale(d[params.data_key]); - var data_range = [scaled_data_range[0], scaled_data_range[1]]; - var distance = (datum-scaled_data_range[0]) / (scaled_data_range[1]-scaled_data_range[0]); - return distance * 100; - }; - var y_function = function(d) { - return (100 - height_helper(d)) + '%'; + + this.setUpHelperFunctions = function(data_range) { + var scaled_data_range = _.map(data_range, scale); + var height_helper = function(d) { + var datum = scale(d[params.data_key]); + var distance = (datum-scaled_data_range[0]) / (scaled_data_range[1]-scaled_data_range[0]); + return distance * 100; + }; + var y_function = function(d) { + return (100 - height_helper(d)) + '%'; + }; + var height_function = function(d) { + return height_helper(d) + '%'; + }; + this.attrs.height = height_function; + this.attrs.y = y_function; }; - var height_function = function(d) { - return height_helper(d) + '%'; + + this.inferDataRange = function(g) { + var self = this; + var min = Number.POSITIVE_INFINITY; + var max = Number.NEGATIVE_INFINITY; + g.each(function(d,i) { + min = Math.min(min, d[self.data_key]); + max = Math.max(max, d[self.data_key]); + }); + return [min, max]; }; - this.attrs.height = height_function; - this.attrs.y = y_function; - this.attrs.fill = params.fill || '#000000'; this.getLegendDiv = function(cell_width, cell_height) { if (params.exclude_from_legend) { return; } var div = d3.select(document.createElement('div')); - div.append('h2').text(this.data_range[0]).classed('oncoprint-legend-label', true); + var data_range = this.data_range || this.inferred_data_range; + if (!data_range) { + return div.node(); + } + div.append('h2').text(data_range[0]).classed('oncoprint-legend-label', true); var mesh = 50; var svg = div.append('svg').attr('width', mesh+'px').attr('height', cell_height+'px'); for (var i=0; i<=mesh; i++) { var t = i/mesh; - var d = (1-t)*this.data_range[0] + t*this.data_range[1]; + var d = (1-t)*data_range[0] + t*data_range[1]; var datum = makeDatum(d); - var height = cell_height*height_helper(datum)/100; + var height = cell_height*parseInt(this.attrs.height(datum))/100; svg.append('rect') .attr('width', '1px') - .attr('height', height) + .attr('height', height+'px') .attr('y', (cell_height-height)+'px') .attr('fill', this.attrs.fill) .attr('x', i+'px'); } - div.append('h2').text(this.data_range[1]).classed('oncoprint-legend-label', true); + div.append('h2').text(data_range[1]).classed('oncoprint-legend-label', true); utils.d3SelectChildren(div, '*').style('padding-right', '10px'); return div.node(); }; + this.apply = function(g, cell_width, cell_height) { + this.setUpHelperFunctions(this.data_range || (this.inferred_data_range = this.inferDataRange(g))); + D3SVGRule.prototype.apply.call(this, g, cell_width, cell_height); + }; + } D3SVGBarChartRule.prototype = Object.create(D3SVGRule.prototype); @@ -400,15 +426,14 @@ window.oncoprint_RuleSet = (function() { D3SVGRule.call(this, params, rule_id); this.data_key = params.data_key; this.data_range = params.data_range; + this.inferred_data_range; this.color_range = params.color_range; - var getGradientId = (function() { - var gradient_counter = 0; - return function() { - gradient_counter += 1; - return 'gradient'+'_'+rule_id+'_'+gradient_counter; - } - })(); + var makeDatum = function(x) { + var ret = {}; + ret[params.data_key] = x; + return ret; + }; var scale = function(x) { if (params.scale === 'log') { return Math.log10(Math.max(x, 0.1)); @@ -417,74 +442,60 @@ window.oncoprint_RuleSet = (function() { } }; - var scaled_data_range = _.map(this.data_range, scale); - var fill_function = function(d) { - var datum = scale(d[params.data_key]); - var data_range = [scaled_data_range[0], scaled_data_range[1]]; - var distance = (datum-scaled_data_range[0]) / (scaled_data_range[1]-scaled_data_range[0]); - color_range = [d3.rgb(params.color_range[0]).toString(), - d3.rgb(params.color_range[1]).toString()]; - return utils.lin_interp(distance, params.color_range[0], params.color_range[1]); + this.setUpHelperFunctions = function(data_range) { + var scaled_data_range = _.map(data_range, scale); + var fill_function = function(d) { + var datum = scale(d[params.data_key]); + var data_range = [scaled_data_range[0], scaled_data_range[1]]; + var distance = (datum-scaled_data_range[0]) / (scaled_data_range[1]-scaled_data_range[0]); + color_range = [d3.rgb(params.color_range[0]).toString(), + d3.rgb(params.color_range[1]).toString()]; + return utils.lin_interp(distance, params.color_range[0], params.color_range[1]); + }; + this.attrs.fill = fill_function; }; - this.attrs.fill = fill_function; - var makeDatum = function(x) { - var ret = {}; - ret[params.data_key] = x; - return ret; - }; - var putLinearGradient = function(group, color_range, width, height) { - var gradient_id = getGradientId(); - var gradient = group.append('svg:defs').append('svg:linearGradient') - .attr('id', gradient_id) - .attr('x1', '0%').attr('y1', '0%') - .attr('x2', '100%').attr('y2', '0%') - .attr('spreadMethod', 'pad'); - gradient.append('svg:stop') - .attr('offset', '0%') - .attr('stop-color', color_range[0]) - .attr('stop-opacity', 1); - gradient.append('svg:stop') - .attr('offset', '100%') - .attr('stop-color', color_range[1]) - .attr('stop-opacity', 1); - group.append('rect') - .attr('width',width).attr('height', height) - .style('fill', 'url(#'+gradient_id+')'); - }; - - var putLogGradient = function(group, color_range, width, height) { - // TODO: I think this is perceptually useless....but could still leave it I guess - var gradient_group = group.append('g'); - var t, datum; - for (var i=0; i Date: Thu, 9 Jul 2015 15:32:12 -0400 Subject: [PATCH 106/343] tooltip style changes --- packages/oncoprintjs/src/css/oncoprint.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/oncoprintjs/src/css/oncoprint.css b/packages/oncoprintjs/src/css/oncoprint.css index 05eca7a840e..d777271ff1d 100644 --- a/packages/oncoprintjs/src/css/oncoprint.css +++ b/packages/oncoprintjs/src/css/oncoprint.css @@ -70,5 +70,6 @@ border: none !important; } .oncoprint-cell-qtip .qtip-content { - background-color: rgba(255,255,255,0.9) !important; + background-color: rgba(255,255,255,0.92) !important; + font-size: 13px !important; } \ No newline at end of file From de01a24f611dbb65c675027641824e62be7debbd Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Thu, 9 Jul 2015 17:36:35 -0400 Subject: [PATCH 107/343] new genetic alteration specification system --- packages/oncoprintjs/src/js/RuleSet.js | 142 +++++++++++------------- packages/oncoprintjs/src/js/defaults.js | 118 ++++++++++++-------- 2 files changed, 133 insertions(+), 127 deletions(-) diff --git a/packages/oncoprintjs/src/js/RuleSet.js b/packages/oncoprintjs/src/js/RuleSet.js index b4749d45c7b..2f68f98e75c 100644 --- a/packages/oncoprintjs/src/js/RuleSet.js +++ b/packages/oncoprintjs/src/js/RuleSet.js @@ -153,93 +153,75 @@ window.oncoprint_RuleSet = (function() { function D3SVGGeneticAlterationRuleSet(params) { params = params || defaults.genetic_alteration_config; D3SVGRuleSet.call(this, params); + var vocab = ['full-rect', 'middle-rect', 'large-right-arrow', 'small-up-arrow', 'small-down-arrow']; var self = this; self.type = GENETIC_ALTERATION; self.sort_cmp = params.sort_cmp || defaults.genetic_alteration_comparator; - var default_rule = this.addStaticRule({ - shape: utils.makeD3SVGElement('rect').attr('fill', params.default_color), - exclude_from_legend: true, - z_index: -1 - }); - var altered_rules = []; - _.each(params.cna.color, function(color, name) { - var new_cna_rule = self.addStaticRule({ - condition: (function(_name) { - return function(d) { - return d[params.cna_key] === _name; - }; - })(name), - shape: utils.makeD3SVGElement('rect'), - legend_label: params.cna.label[name], - attrs: { - fill: color, - width: '100%', - height: '100%' - }, - z_index: 0 - }); - altered_rules.push(new_cna_rule); - }); - _.each(params.mut.color, function(color, name) { - var new_mut_rule = self.addStaticRule({ - condition: (function(_name) { - return function(d) { - return d[params.mut_type_key] === _name; // TODO: should be indexOf for multiple mutations? - } - })(name), - shape: utils.makeD3SVGElement('rect').attr('fill', color), - legend_label: params.mut.label[name], - attrs: { - width: '100%', - height: '33.33%', - y: '33.33%', - }, - z_index: 1 + + var makeStaticShapeRule = function(rule_spec, key, value) { + var condition = typeof key !== 'undefined' && typeof value !== 'undefined' ? (function(_key, _value) { + return function(d) { + return d[_key] === _value; + }; + })(key, value) : undefined; + var shape, attrs, styles, z_index; + switch (rule_spec.shape) { + case 'full-rect': + shape = utils.makeD3SVGElement('rect'); + attrs = {fill: rule_spec.color, width: '100%', height: '100%'}; + styles = {}; + z_index = 0; + break; + case 'middle-rect': + shape = utils.makeD3SVGElement('rect'); + attrs = {fill: rule_spec.color, width: '100%', height: '33.33%', y: '33.33%'}; + styles = {}; + z_index = 1; + break; + case 'large-right-arrow': + shape = utils.makeD3SVGElement('polygon'); + attrs = {points: "0%,0% 100%,50% 0%,100%"}; + styles = {'stroke-width':'0px', 'fill': rule_spec.color}; + z_index = 2; + break; + case 'small-up-arrow': + shape = utils.makeD3SVGElement('polygon'); + attrs = {points: "50%,0% 100%,25% 0%,25%"}; + styles = {'stroke-width':'0px', 'fill': rule_spec.color}; + z_index = 3; + break; + case 'small-down-arrow': + shape = utils.makeD3SVGElement('polygon'); + attrs = {points: "50%,100% 100%,75% 0%,75%"}; + styles = {'stroke-width':'0px', 'fill': rule_spec.color}; + z_index = 4; + break; + case 'outline': + shape = CELL; + styles = {'outline-color':rule_spec.color, 'outline-style':'solid', 'outline-width':'2px'}; + z_index = 5; + break; + } + var new_rule = self.addStaticRule({ + condition: condition, + shape: shape, + attrs: attrs, + styles: styles, + z_index: z_index, + legend_label: rule_spec.legend_label, + exclude_from_legend: (typeof rule_spec.legend_label === "undefined") }); - altered_rules.push(new_mut_rule); - }); - _.each(params.mrna.color, function(color, name) { - var new_mrna_rule = self.addStaticRule({ - condition: (function(_name) { - return function(d) { - return d[params.mrna_key] === _name; - } - })(name), - shape: CELL, - legend_label: params.mrna.label[name], - styles: { - 'outline-style':'solid', - 'outline-width':'2px', - 'outline-color':color, - }, - z_index: 2 + return new_rule; + }; + var altered_rules = []; + _.each(params.altered, function(values, key) { + _.each(values, function(rule_spec, value) { + altered_rules.push(makeStaticShapeRule(rule_spec, key, value)); }); - altered_rules.push(new_mrna_rule); - }); - var up_rppa_rule = self.addStaticRule({ - condition: function(d) { - return d[params.rppa_key] === params.rppa_up; - }, - shape: utils.makeD3SVGElement('polygon').attr('style', 'fill:black; stroke-width:0'), - legend_label: params.rppa_up_label, - attrs: { - points: "50%,0% 100%,25% 0%,25%" - }, - z_index: 3 }); - altered_rules.push(up_rppa_rule); - var down_rppa_rule = self.addStaticRule({ - condition: function(d) { - return d[params.rppa_key] === params.rppa_down; - }, - shape: utils.makeD3SVGElement('polygon').attr('style', 'fill:black; stroke-width:0'), - legend_label: params.rppa_down_label, - attrs: { - points: "50%,100% 100%,75% 0%,75%" - }, - z_index: 3 + _.each(params.default, function(rule_spec) { + makeStaticShapeRule(rule_spec); }); - altered_rules.push(down_rppa_rule); self.getLegendDiv = function(cell_width, cell_height) { var div = d3.select(document.createElement('div')); _.each(self.getRules(), function(rule) { diff --git a/packages/oncoprintjs/src/js/defaults.js b/packages/oncoprintjs/src/js/defaults.js index fbffa67a3b8..93114690c02 100644 --- a/packages/oncoprintjs/src/js/defaults.js +++ b/packages/oncoprintjs/src/js/defaults.js @@ -1,53 +1,77 @@ window.oncoprint_defaults = (function() { var utils = window.oncoprint_utils; var genetic_alteration_config = { - default_color: '#D3D3D3', - cna_key: 'cna', - cna: { - color: { - AMPLIFIED: 'red', - GAINED: '#FFB6C1', - HOMODELETED: '#0000FF', - HETLOSS: '#8FD8D8', + default: [{shape: 'full-rect', color: '#D3D3D3'}], + altered: { + 'cna': { + 'AMPLIFIED': { + shape: 'full-rect', + color: 'red', + legend_label: 'Amplification' + }, + 'GAINED': { + shape: 'full-rect', + color: '#FFB6C1', + legend_label: 'Gain' + }, + 'HOMODELETED':{ + shape: 'full-rect', + color: '#0000FF', + legend_label: 'Homozygous Deletion' + }, + 'HETLOSS': { + shape: 'full-rect', + color: '#8FD8D8', + legend_label: 'Heterozygous Deletion' + } }, - label: { - AMPLIFIED: 'Amplification', - GAINED: 'Gain', - HOMODELETED: 'Homozygous Deletion', - HETLOSS: 'Heterozygous Deletion' - } - - }, - mut_type_key: 'mut_type', - mut: { - color: { - MISSENSE: 'green', - INFRAME: '#9F8170', - TRUNC: 'black', + 'mut_type': { + 'MISSENSE': { + shape: 'middle-rect', + color: 'green', + legend_label: 'Missense Mutation' + }, + 'INFRAME': { + shape: 'middle-rect', + color: '#9F8170', + legend_label: 'Inframe Insertion/Deletion' + }, + 'TRUNC': { + shape: 'middle-rect', + color: 'black', + legend_label: 'Truncating Mutation' + }, + 'FUSION':{ + shape: 'large-right-arrow', + color: 'black', + legend_label: 'Fusion' + } }, - label: { - MISSENSE: 'Missense Mutation', - INFRAME: 'In-Frame Insertion/Deletion', - TRUNC: 'Truncating Mutation' - } - }, - legend_label: 'Genetic Alteration', - mrna_key: 'mrna', - mrna: { - color: { - UPREGULATED: '#FF9999', - DOWNREGULATED: '#6699CC' + 'mrna': { + 'UPREGULATED': { + shape: 'outline', + color: '#FF9999', + legend_label: 'mRNA Upregulation' + }, + 'DOWNREGULATED': { + shape: 'outline', + color: '#6699CC', + legend_label: 'mRNA Downregulation' + } }, - label: { - UPREGULATED: 'mRNA Upregulation', - DOWNREGULATED: 'mRNA Downregulation' + 'rppa': { + 'UPREGULATED': { + shape: 'small-up-arrow', + color: 'black', + legend_label: 'Protein Upregulation' + }, + 'DOWNREGULATED': { + shape: 'small-down-arrow', + color: 'black', + legend_label: 'Protein Downregulation' + } } - }, - rppa_key: 'rppa', - rppa_down: 'DOWNREGULATED', - rppa_up: 'UPREGULATED', - rppa_down_label: 'Protein Downregulation', - rppa_up_label: 'Protein Upregulation', + } }; var genetic_alteration_comparator = function(d1,d2) { @@ -55,25 +79,25 @@ window.oncoprint_defaults = (function() { var mut_order = utils.invert_array(['TRUNC', 'INFRAME', 'MISSENSE', undefined]); var regulation_order = utils.invert_array(['UPREGULATED', 'DOWNREGULATED', undefined]); - var cna_key = genetic_alteration_config.cna_key; + var cna_key = 'cna'; var cna_diff = utils.sign(cna_order[d1[cna_key]] - cna_order[d2[cna_key]]); if (cna_diff !== 0) { return cna_diff; } - var mut_type_key = genetic_alteration_config.mut_type_key; + var mut_type_key = 'mut_type'; var mut_type_diff = utils.sign(mut_order[d1[mut_type_key]] - mut_order[d2[mut_type_key]]); if (mut_type_diff !== 0) { return mut_type_diff; } - var mrna_key = genetic_alteration_config.mrna_key; + var mrna_key = 'mrna'; var mrna_diff = utils.sign(regulation_order[d1[mrna_key]] - regulation_order[d2[mrna_key]]); if (mrna_diff !== 0) { return mrna_diff; } - var rppa_key = genetic_alteration_config.rppa_key; + var rppa_key = 'rppa'; var rppa_diff = utils.sign(regulation_order[d1[rppa_key]] - regulation_order[d2[rppa_key]]); if (rppa_diff !== 0) { return rppa_diff; From 00a44b610cbdb44855429191d262add554cc147b Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Thu, 9 Jul 2015 17:41:06 -0400 Subject: [PATCH 108/343] specify zindex in genetic alteration rule spec --- packages/oncoprintjs/src/js/RuleSet.js | 12 ++++++------ packages/oncoprintjs/src/js/defaults.js | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/oncoprintjs/src/js/RuleSet.js b/packages/oncoprintjs/src/js/RuleSet.js index 2f68f98e75c..56f1af578eb 100644 --- a/packages/oncoprintjs/src/js/RuleSet.js +++ b/packages/oncoprintjs/src/js/RuleSet.js @@ -170,36 +170,36 @@ window.oncoprint_RuleSet = (function() { shape = utils.makeD3SVGElement('rect'); attrs = {fill: rule_spec.color, width: '100%', height: '100%'}; styles = {}; - z_index = 0; + z_index = utils.ifndef(rule_spec.z_index, 0); break; case 'middle-rect': shape = utils.makeD3SVGElement('rect'); attrs = {fill: rule_spec.color, width: '100%', height: '33.33%', y: '33.33%'}; styles = {}; - z_index = 1; + z_index = utils.ifndef(rule_spec.z_index, 1); break; case 'large-right-arrow': shape = utils.makeD3SVGElement('polygon'); attrs = {points: "0%,0% 100%,50% 0%,100%"}; styles = {'stroke-width':'0px', 'fill': rule_spec.color}; - z_index = 2; + z_index = utils.ifndef(rule_spec.z_index, 2); break; case 'small-up-arrow': shape = utils.makeD3SVGElement('polygon'); attrs = {points: "50%,0% 100%,25% 0%,25%"}; styles = {'stroke-width':'0px', 'fill': rule_spec.color}; - z_index = 3; + z_index = utils.ifndef(rule_spec.z_index, 3); break; case 'small-down-arrow': shape = utils.makeD3SVGElement('polygon'); attrs = {points: "50%,100% 100%,75% 0%,75%"}; styles = {'stroke-width':'0px', 'fill': rule_spec.color}; - z_index = 4; + z_index = utils.ifndef(rule_spec.z_index, 4); break; case 'outline': shape = CELL; styles = {'outline-color':rule_spec.color, 'outline-style':'solid', 'outline-width':'2px'}; - z_index = 5; + z_index = utils.ifndef(rule_spec.z_index, 5); break; } var new_rule = self.addStaticRule({ diff --git a/packages/oncoprintjs/src/js/defaults.js b/packages/oncoprintjs/src/js/defaults.js index 93114690c02..a01ded97ded 100644 --- a/packages/oncoprintjs/src/js/defaults.js +++ b/packages/oncoprintjs/src/js/defaults.js @@ -1,7 +1,7 @@ window.oncoprint_defaults = (function() { var utils = window.oncoprint_utils; var genetic_alteration_config = { - default: [{shape: 'full-rect', color: '#D3D3D3'}], + default: [{shape: 'full-rect', color: '#D3D3D3', z_index: -1}], altered: { 'cna': { 'AMPLIFIED': { From f35dafe39980a721082c733548dd5f40ee928c15 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Fri, 10 Jul 2015 18:04:02 -0400 Subject: [PATCH 109/343] add genetic alteration label back into default config --- packages/oncoprintjs/src/js/defaults.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/oncoprintjs/src/js/defaults.js b/packages/oncoprintjs/src/js/defaults.js index a01ded97ded..7e6b162e7f8 100644 --- a/packages/oncoprintjs/src/js/defaults.js +++ b/packages/oncoprintjs/src/js/defaults.js @@ -71,7 +71,8 @@ window.oncoprint_defaults = (function() { legend_label: 'Protein Downregulation' } } - } + }, + legend_label: "Genetic Alteration" }; var genetic_alteration_comparator = function(d1,d2) { From 20b619ee5b77de0f4d219494855297b9e1db8aaf Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Fri, 10 Jul 2015 18:05:37 -0400 Subject: [PATCH 110/343] render legend when track data set, which jives with rule setting inferred data range --- packages/oncoprintjs/src/js/OncoprintSVGRenderer.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index 6a045902392..70d635bc758 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -109,6 +109,7 @@ self.positionTrackCells(d.track_id); self.renderTrackLabels(d.track_id); self.resizeCellDiv(); + self.renderLegend(); //this.cell_div.style('display','inherit'); }); From 4162c1dc0486c4475875bc5502f272e827d48e52 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Fri, 10 Jul 2015 18:11:10 -0400 Subject: [PATCH 111/343] sort on move --- packages/oncoprintjs/src/js/OncoprintSVGRenderer.js | 5 +++++ packages/oncoprintjs/src/js/defaults.js | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index 70d635bc758..66e35ec7834 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -447,6 +447,11 @@ $label_drag_div.off("mouseleave mouseup"); if (new_pos > -1) { self.oncoprint.moveTrack(track_id, new_pos); + try { + self.oncoprint.sort(); + } catch (e) { + console.log("error sorting - no comparator set for some track"); + } } }); })(track_id); diff --git a/packages/oncoprintjs/src/js/defaults.js b/packages/oncoprintjs/src/js/defaults.js index 7e6b162e7f8..8ea3cb0e345 100644 --- a/packages/oncoprintjs/src/js/defaults.js +++ b/packages/oncoprintjs/src/js/defaults.js @@ -72,7 +72,7 @@ window.oncoprint_defaults = (function() { } } }, - legend_label: "Genetic Alteration" + legend_label: "Genetic Alteration", }; var genetic_alteration_comparator = function(d1,d2) { From 53229ee996d3a866f95346c11cc46b19aaf47a6a Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Mon, 13 Jul 2015 13:49:36 -0400 Subject: [PATCH 112/343] add default sort for gradient and bar chart, and add different sorting/display for mutation settings --- packages/oncoprintjs/src/js/RuleSet.js | 31 +++++- packages/oncoprintjs/src/js/defaults.js | 120 ++++++++++++++---------- 2 files changed, 100 insertions(+), 51 deletions(-) diff --git a/packages/oncoprintjs/src/js/RuleSet.js b/packages/oncoprintjs/src/js/RuleSet.js index 56f1af578eb..2df266d3d54 100644 --- a/packages/oncoprintjs/src/js/RuleSet.js +++ b/packages/oncoprintjs/src/js/RuleSet.js @@ -8,6 +8,7 @@ window.oncoprint_RuleSet = (function() { var BAR_CHART = 3; var CELL = "cell"; + var ANY = '*'; var getRuleSetId = utils.makeIdCounter(); @@ -128,6 +129,15 @@ window.oncoprint_RuleSet = (function() { color_range: params.color_range, scale: params.scale }); + this.sort_cmp = params.sort_cmp || function(d1,d2) { + if (d1[params.data_key] < d2[params.data_key]) { + return -1; + } else if (d1[params.data_key] > d2[params.data_key]) { + return 1; + } else { + return 0; + } + }; this.getLegendDiv = function(cell_width, cell_height) { return this.rule_map[rule].getLegendDiv(cell_width, cell_height); }; @@ -144,6 +154,15 @@ window.oncoprint_RuleSet = (function() { scale: params.scale, fill: params.fill, }); + this.sort_cmp = params.sort_cmp || function(d1,d2) { + if (d1[params.data_key] < d2[params.data_key]) { + return -1; + } else if (d1[params.data_key] > d2[params.data_key]) { + return 1; + } else { + return 0; + } + }; this.getLegendDiv = function(cell_width, cell_height) { return this.rule_map[rule].getLegendDiv(cell_width, cell_height); }; @@ -160,9 +179,15 @@ window.oncoprint_RuleSet = (function() { var makeStaticShapeRule = function(rule_spec, key, value) { var condition = typeof key !== 'undefined' && typeof value !== 'undefined' ? (function(_key, _value) { - return function(d) { - return d[_key] === _value; - }; + if (_value === ANY) { + return function(d) { + return typeof d[_key] !== 'undefined'; + } + } else { + return function(d) { + return d[_key] === _value; + }; + } })(key, value) : undefined; var shape, attrs, styles, z_index; switch (rule_spec.shape) { diff --git a/packages/oncoprintjs/src/js/defaults.js b/packages/oncoprintjs/src/js/defaults.js index 8ea3cb0e345..c0eee4c5804 100644 --- a/packages/oncoprintjs/src/js/defaults.js +++ b/packages/oncoprintjs/src/js/defaults.js @@ -1,6 +1,6 @@ window.oncoprint_defaults = (function() { var utils = window.oncoprint_utils; - var genetic_alteration_config = { + var genetic_alteration_config_base = { default: [{shape: 'full-rect', color: '#D3D3D3', z_index: -1}], altered: { 'cna': { @@ -25,28 +25,6 @@ window.oncoprint_defaults = (function() { legend_label: 'Heterozygous Deletion' } }, - 'mut_type': { - 'MISSENSE': { - shape: 'middle-rect', - color: 'green', - legend_label: 'Missense Mutation' - }, - 'INFRAME': { - shape: 'middle-rect', - color: '#9F8170', - legend_label: 'Inframe Insertion/Deletion' - }, - 'TRUNC': { - shape: 'middle-rect', - color: 'black', - legend_label: 'Truncating Mutation' - }, - 'FUSION':{ - shape: 'large-right-arrow', - color: 'black', - legend_label: 'Fusion' - } - }, 'mrna': { 'UPREGULATED': { shape: 'outline', @@ -74,41 +52,87 @@ window.oncoprint_defaults = (function() { }, legend_label: "Genetic Alteration", }; + var genetic_alteration_config_nondistinct_mutations = $.extend({},{},genetic_alteration_config_base); + genetic_alteration_config_nondistinct_mutations.altered.mut_type = { + '*': { + shape: 'middle-rect', + color: 'green', + legend_label: 'Mutation' + } + }; + var genetic_alteration_config = $.extend({},{},genetic_alteration_config_base); + genetic_alteration_config.altered.mut_type = { + 'MISSENSE': { + shape: 'middle-rect', + color: 'green', + legend_label: 'Missense Mutation' + }, + 'INFRAME': { + shape: 'middle-rect', + color: '#9F8170', + legend_label: 'Inframe Insertion/Deletion' + }, + 'TRUNC': { + shape: 'middle-rect', + color: 'black', + legend_label: 'Truncating Mutation' + }, + 'FUSION':{ + shape: 'large-right-arrow', + color: 'black', + legend_label: 'Fusion' + } + }; - var genetic_alteration_comparator = function(d1,d2) { + var makeGeneticAlterationComparator = function(distinguish_mutations) { + var cna_key = 'cna'; var cna_order = utils.invert_array(['AMPLIFIED', 'HOMODELETED', 'GAINED', 'HEMIZYGOUSLYDELETED', 'DIPLOID', undefined]); - var mut_order = utils.invert_array(['TRUNC', 'INFRAME', 'MISSENSE', undefined]); + var mut_type_key = 'mut_type'; + var mut_order = (function() { + if (!distinguish_mutations) { + return function(m) { + return +(typeof m === 'undefined'); + } + } else { + var _order = utils.invert_array(['TRUNC', 'INFRAME', 'MISSENSE', undefined]); + return function(m) { + return _order[m]; + } + } + })(); + var mrna_key = 'mrna'; + var rppa_key = 'rppa'; var regulation_order = utils.invert_array(['UPREGULATED', 'DOWNREGULATED', undefined]); - var cna_key = 'cna'; - var cna_diff = utils.sign(cna_order[d1[cna_key]] - cna_order[d2[cna_key]]); - if (cna_diff !== 0) { - return cna_diff; - } + return function(d1, d2) { + var cna_diff = utils.sign(cna_order[d1[cna_key]] - cna_order[d2[cna_key]]); + if (cna_diff !== 0) { + return cna_diff; + } - var mut_type_key = 'mut_type'; - var mut_type_diff = utils.sign(mut_order[d1[mut_type_key]] - mut_order[d2[mut_type_key]]); - if (mut_type_diff !== 0) { - return mut_type_diff; - } + var mut_type_diff = utils.sign(mut_order(d1[mut_type_key]) - mut_order(d2[mut_type_key])); + if (mut_type_diff !== 0) { + return mut_type_diff; + } - var mrna_key = 'mrna'; - var mrna_diff = utils.sign(regulation_order[d1[mrna_key]] - regulation_order[d2[mrna_key]]); - if (mrna_diff !== 0) { - return mrna_diff; - } + var mrna_diff = utils.sign(regulation_order[d1[mrna_key]] - regulation_order[d2[mrna_key]]); + if (mrna_diff !== 0) { + return mrna_diff; + } - var rppa_key = 'rppa'; - var rppa_diff = utils.sign(regulation_order[d1[rppa_key]] - regulation_order[d2[rppa_key]]); - if (rppa_diff !== 0) { - return rppa_diff; - } + var rppa_diff = utils.sign(regulation_order[d1[rppa_key]] - regulation_order[d2[rppa_key]]); + if (rppa_diff !== 0) { + return rppa_diff; + } - return 0; + return 0; + }; }; return { genetic_alteration_config: genetic_alteration_config, - genetic_alteration_comparator: genetic_alteration_comparator - } + genetic_alteration_config_nondistinct_mutations: genetic_alteration_config, + genetic_alteration_comparator: makeGeneticAlterationComparator(true), + genetic_alteration_comparator_nondistinct_mutations: makeGeneticAlterationComparator(false) + }; })(); From b7b1ba4b8e0f0d0132832ec9e8e4b1ac8580a5c4 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Mon, 13 Jul 2015 19:09:34 -0400 Subject: [PATCH 113/343] more advanced sorting functionality --- .../oncoprintjs/src/js/OncoprintRenderer.js | 4 +- .../src/js/OncoprintSVGRenderer.js | 5 -- packages/oncoprintjs/src/js/RuleSet.js | 15 +++- packages/oncoprintjs/src/js/events.js | 3 +- packages/oncoprintjs/src/js/oncoprint.js | 85 ++++++++++++++++--- packages/oncoprintjs/test/js/test_page.js | 3 + 6 files changed, 89 insertions(+), 26 deletions(-) diff --git a/packages/oncoprintjs/src/js/OncoprintRenderer.js b/packages/oncoprintjs/src/js/OncoprintRenderer.js index 10ae7757dac..bc57f55b328 100644 --- a/packages/oncoprintjs/src/js/OncoprintRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintRenderer.js @@ -65,14 +65,14 @@ window.OncoprintRenderer = (function() { var new_rule_set = RuleSet.makeRuleSet(type, params); this.rule_sets[track_id] = new_rule_set; if (new_rule_set.sort_cmp) { - this.oncoprint.setTrackDataComparator(track_id, new_rule_set.sort_cmp); + this.oncoprint.setTrackSortComparator(track_id, new_rule_set.sort_cmp); } }; OncoprintRenderer.prototype.useSameRuleSet = function(target_track_id, source_track_id) { var rule_set = this.rule_sets[source_track_id]; this.rule_sets[target_track_id] = rule_set; if (rule_set.sort_cmp) { - this.oncoprint.setTrackDataComparator(target_track_id, rule_set.sort_cmp); + this.oncoprint.setTrackSortComparator(target_track_id, rule_set.sort_cmp); } }; OncoprintRenderer.prototype.getRuleSet = function(track_id) { diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index 66e35ec7834..70d635bc758 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -447,11 +447,6 @@ $label_drag_div.off("mouseleave mouseup"); if (new_pos > -1) { self.oncoprint.moveTrack(track_id, new_pos); - try { - self.oncoprint.sort(); - } catch (e) { - console.log("error sorting - no comparator set for some track"); - } } }); })(track_id); diff --git a/packages/oncoprintjs/src/js/RuleSet.js b/packages/oncoprintjs/src/js/RuleSet.js index 2df266d3d54..e82efde32d1 100644 --- a/packages/oncoprintjs/src/js/RuleSet.js +++ b/packages/oncoprintjs/src/js/RuleSet.js @@ -92,6 +92,9 @@ window.oncoprint_RuleSet = (function() { addColorRule(color, category); }); + this.sort_cmp = params.sort_cmp || function(d1,d2) { + return params.getCategory(d1).toString().localeCompare(params.getCategory(d2).toString()); + }; self.apply = function(g, data, datum_id_accessor, cell_width, cell_height) { var missing_categories = []; _.each(data, function(datum) { @@ -130,9 +133,11 @@ window.oncoprint_RuleSet = (function() { scale: params.scale }); this.sort_cmp = params.sort_cmp || function(d1,d2) { - if (d1[params.data_key] < d2[params.data_key]) { + var f1 = parseFloat(d1[params.data_key], 10); + var f2 = parseFloat(d2[params.data_key], 10); + if (f1 < f2) { return -1; - } else if (d1[params.data_key] > d2[params.data_key]) { + } else if (f1 > f2) { return 1; } else { return 0; @@ -155,9 +160,11 @@ window.oncoprint_RuleSet = (function() { fill: params.fill, }); this.sort_cmp = params.sort_cmp || function(d1,d2) { - if (d1[params.data_key] < d2[params.data_key]) { + var f1 = parseFloat(d1[params.data_key], 10); + var f2 = parseFloat(d2[params.data_key], 10); + if (f1 < f2) { return -1; - } else if (d1[params.data_key] > d2[params.data_key]) { + } else if (f1 > f2) { return 1; } else { return 0; diff --git a/packages/oncoprintjs/src/js/events.js b/packages/oncoprintjs/src/js/events.js index 6ba7186bb9e..678b22b8723 100644 --- a/packages/oncoprintjs/src/js/events.js +++ b/packages/oncoprintjs/src/js/events.js @@ -47,6 +47,5 @@ window.oncoprint_events = { FINISHED_RENDERING: 'finished_rendering.renderer.oncoprint', FINISHED_POSITIONING: 'finished_positioning.renderer.oncoprint', SET_ZOOM: 'set_zoom.oncoprint', - ADD_TRACK_SEPARATOR: 'add_track_separator.oncoprint', - SET_DATA_COMPARATOR: 'set_data_comparator.oncoprint' + SET_SORT_DIRECTION: 'set_sort_direction.oncoprint' }; diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 9b2329dd4ab..b80601f4c07 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -31,6 +31,7 @@ window.Oncoprint = (function() { var events = oncoprint_events; var utils = oncoprint_utils; var RuleSet = oncoprint_RuleSet; + var defaults = oncoprint_defaults; var defaultOncoprintConfig = { cell_width: 6, @@ -65,6 +66,8 @@ window.Oncoprint = (function() { self.id_order = []; self.track_groups = [[],[]]; + self.track_group_sort_order = [0,1]; + self.sort_direction = {}; self.tracks = {}; self.zoom = 1; @@ -118,28 +121,70 @@ window.Oncoprint = (function() { // Id Order self.getIdOrder = function() { - return self.id_order; + return self.id_order.slice(); }; self.setIdOrder = function(id_order) { - self.id_order = id_order; + self.id_order = id_order.slice(); $(self).trigger(events.SET_ID_ORDER); }; - self.setTrackDataComparator = function(track_id, cmp) { + + // Sorting + self.setTrackSortComparator = function(track_id, cmp) { self.tracks[track_id].config.sort_cmp = cmp; - $(self).trigger(events.SET_DATA_COMPARATOR); }; - self.getTrackDataComparator = function(track_id) { + self.getTrackSortComparator = function(track_id) { return self.tracks[track_id].config.sort_cmp; }; - self.sort = function(track_id_list, cmp_list) { - track_id_list = track_id_list ? [].concat(track_id_list) : self.getTracks(); - cmp_list = [].concat(cmp_list); + self.toggleTrackSortDirection = function(track_id) { + var dir = self.sort_direction[track_id]; + self.sort_direction[track_id] = -dir; + $(self).trigger(events.SET_SORT_DIRECTION); + }; + self.setTrackGroupSortOrder = function(order) { + self.track_group_sort_order = order.slice(); + }; + self.getTrackGroupSortOrder = function() { + return self.track_group_sort_order.slice(); + }; + self.getTrackSortOrder = function() { + var ret = []; + var track_groups = self.getTrackGroups(); + _.each(self.getTrackGroupSortOrder(), function(group_id) { + ret = ret.concat(track_groups[group_id]); + }); + return ret; + }; + self.sortById = function(desc) { + var ret = _.sortBy(self.getIdOrder(), _.identity); + if (desc) { + ret.reverse(); + } + self.setIdOrder(ret); + }; + self.sortByTrack = function() { + var track_id_list = self.getTrackSortOrder(); + var cmp_list = _.map(track_id_list, function(track_id) { + return self.getTrackSortComparator(track_id); + }); var lexicographically_ordered_cmp = function(id1,id2) { - var cmp_result; + var cmp_result = 0; for (var i=0, _len = track_id_list.length; i<_len; i++) { var track_id = track_id_list[i]; - var cmp = cmp_list[i] || self.getTrackDataComparator(track_id); - cmp_result = cmp(self.getTrackDatum(track_id, id1),self.getTrackDatum(track_id, id2)); + var cmp = cmp_list[i]; + var d1 = self.getTrackDatum(track_id, id1); + var d2 = self.getTrackDatum(track_id, id2); + var d1_undef = (typeof d1 === "undefined"); + var d2_undef = (typeof d2 === "undefined"); + if (!d1_undef && !d2_undef) { + cmp_result = cmp(self.getTrackDatum(track_id, id1),self.getTrackDatum(track_id, id2)); + } else if (d1_undef && d2_undef) { + cmp_result = 0; + } else if (d1_undef) { + cmp_result = 1; + } else { + cmp_result = -1; + } + cmp_result *= self.sort_direction[track_id]; if (cmp_result !== 0) { break; } @@ -149,12 +194,14 @@ window.Oncoprint = (function() { self.setIdOrder(utils.stableSort(self.getIdOrder(), lexicographically_ordered_cmp)); }; + // Track Creation/Destruction self.addTrack = function(config, group) { group = utils.ifndef(group, 1); var track_id = getTrackId(); self.tracks[track_id] ={id: track_id, data: [], config: $.extend({}, defaultTrackConfig, config)}; self.track_groups[group].push(track_id); + self.sort_direction[track_id] = 1; $(self).trigger(events.ADD_TRACK, {track_id: track_id}); return track_id; @@ -237,6 +284,9 @@ window.Oncoprint = (function() { self.getTrackDatum = function(track_id, datum_id) { return self.tracks[track_id].id_data_map[datum_id]; }; + self.getTrackDatumDataKey = function(track_id) { + return self.tracks[track_id].config.datum_data_key; + }; // Track Datum Id self.getTrackDatumIdAccessor = function(track_id) { @@ -288,8 +338,14 @@ window.Oncoprint = (function() { toSVG: function(ctr) { return renderer.toSVG(ctr); }, - sort: function(track_id_list, cmp_list) { - oncoprint.sort(track_id_list, cmp_list); + sortByTrack: function() { + oncoprint.sortByTrack(); + }, + sortById: function() { + oncoprint.sortById(); + }, + toggleTrackSortDirection: function(track_id) { + oncoprint.toggleTrackSortDirection(track_id); }, setZoom: function(z) { oncoprint.setZoom(z); @@ -301,6 +357,9 @@ window.Oncoprint = (function() { renderer.releaseRendering(); } }; + $(oncoprint).on(events.MOVE_TRACK, function() { + $(ret).trigger(events.MOVE_TRACK); + }); return ret; } }; diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index d96e49908db..f1a45858a0f 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -17,6 +17,9 @@ var z = 1; $('#reduce_cell_width').click(function() { z *= 0.9; onc.setZoom(z); +}); +$(onc).on("move_track.oncoprint", function() { + onc.sortByTrack(); }); /*$('#to_svg_btn').click(function() { onc.toSVG(d3.select('#svg_container')); From 9f26cb157bd75377b78157bb1001fc3a5f0bd4f2 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Mon, 13 Jul 2015 19:41:46 -0400 Subject: [PATCH 114/343] more sort controls --- packages/oncoprintjs/index.html | 1 + packages/oncoprintjs/src/js/RuleSet.js | 12 +++- packages/oncoprintjs/src/js/defaults.js | 90 ++++++++++++------------ packages/oncoprintjs/src/js/oncoprint.js | 3 + 4 files changed, 59 insertions(+), 47 deletions(-) diff --git a/packages/oncoprintjs/index.html b/packages/oncoprintjs/index.html index 69fb0a99014..29f77a00176 100644 --- a/packages/oncoprintjs/index.html +++ b/packages/oncoprintjs/index.html @@ -11,6 +11,7 @@ + diff --git a/packages/oncoprintjs/src/js/RuleSet.js b/packages/oncoprintjs/src/js/RuleSet.js index e82efde32d1..5ee65f7263c 100644 --- a/packages/oncoprintjs/src/js/RuleSet.js +++ b/packages/oncoprintjs/src/js/RuleSet.js @@ -177,12 +177,20 @@ window.oncoprint_RuleSet = (function() { D3SVGBarChartRuleSet.prototype = Object.create(D3SVGRuleSet.prototype); function D3SVGGeneticAlterationRuleSet(params) { - params = params || defaults.genetic_alteration_config; + if (params.dont_distinguish_mutation_color) { + params = $.extend({}, params, defaults.genetic_alteration_config_nondistinct_mutations); + } else { + params = $.extend({}, params, defaults.genetic_alteration_config); + } + if (params.dont_distinguish_mutation_order) { + this.sort_cmp = defaults.genetic_alteration_comparator_nondistinct_mutations; + } else { + this.sort_cmp = defaults.genetic_alteration_comparator; + } D3SVGRuleSet.call(this, params); var vocab = ['full-rect', 'middle-rect', 'large-right-arrow', 'small-up-arrow', 'small-down-arrow']; var self = this; self.type = GENETIC_ALTERATION; - self.sort_cmp = params.sort_cmp || defaults.genetic_alteration_comparator; var makeStaticShapeRule = function(rule_spec, key, value) { var condition = typeof key !== 'undefined' && typeof value !== 'undefined' ? (function(_key, _value) { diff --git a/packages/oncoprintjs/src/js/defaults.js b/packages/oncoprintjs/src/js/defaults.js index c0eee4c5804..3830c9f42f8 100644 --- a/packages/oncoprintjs/src/js/defaults.js +++ b/packages/oncoprintjs/src/js/defaults.js @@ -1,5 +1,50 @@ window.oncoprint_defaults = (function() { var utils = window.oncoprint_utils; + var makeGeneticAlterationComparator = function(distinguish_mutations) { + var cna_key = 'cna'; + var cna_order = utils.invert_array(['AMPLIFIED', 'HOMODELETED', 'GAINED', 'HEMIZYGOUSLYDELETED', 'DIPLOID', undefined]); + var mut_type_key = 'mut_type'; + var mut_order = (function() { + if (!distinguish_mutations) { + return function(m) { + return +(typeof m === 'undefined'); + } + } else { + var _order = utils.invert_array(['TRUNC', 'INFRAME', 'MISSENSE', undefined]); + return function(m) { + return _order[m]; + } + } + })(); + var mrna_key = 'mrna'; + var rppa_key = 'rppa'; + var regulation_order = utils.invert_array(['UPREGULATED', 'DOWNREGULATED', undefined]); + + return function(d1, d2) { + var cna_diff = utils.sign(cna_order[d1[cna_key]] - cna_order[d2[cna_key]]); + if (cna_diff !== 0) { + return cna_diff; + } + + var mut_type_diff = utils.sign(mut_order(d1[mut_type_key]) - mut_order(d2[mut_type_key])); + if (mut_type_diff !== 0) { + return mut_type_diff; + } + + var mrna_diff = utils.sign(regulation_order[d1[mrna_key]] - regulation_order[d2[mrna_key]]); + if (mrna_diff !== 0) { + return mrna_diff; + } + + var rppa_diff = utils.sign(regulation_order[d1[rppa_key]] - regulation_order[d2[rppa_key]]); + if (rppa_diff !== 0) { + return rppa_diff; + } + + return 0; + }; + ; + var genetic_alteration_config_base = { default: [{shape: 'full-rect', color: '#D3D3D3', z_index: -1}], altered: { @@ -84,51 +129,6 @@ window.oncoprint_defaults = (function() { } }; - var makeGeneticAlterationComparator = function(distinguish_mutations) { - var cna_key = 'cna'; - var cna_order = utils.invert_array(['AMPLIFIED', 'HOMODELETED', 'GAINED', 'HEMIZYGOUSLYDELETED', 'DIPLOID', undefined]); - var mut_type_key = 'mut_type'; - var mut_order = (function() { - if (!distinguish_mutations) { - return function(m) { - return +(typeof m === 'undefined'); - } - } else { - var _order = utils.invert_array(['TRUNC', 'INFRAME', 'MISSENSE', undefined]); - return function(m) { - return _order[m]; - } - } - })(); - var mrna_key = 'mrna'; - var rppa_key = 'rppa'; - var regulation_order = utils.invert_array(['UPREGULATED', 'DOWNREGULATED', undefined]); - - return function(d1, d2) { - var cna_diff = utils.sign(cna_order[d1[cna_key]] - cna_order[d2[cna_key]]); - if (cna_diff !== 0) { - return cna_diff; - } - - var mut_type_diff = utils.sign(mut_order(d1[mut_type_key]) - mut_order(d2[mut_type_key])); - if (mut_type_diff !== 0) { - return mut_type_diff; - } - - var mrna_diff = utils.sign(regulation_order[d1[mrna_key]] - regulation_order[d2[mrna_key]]); - if (mrna_diff !== 0) { - return mrna_diff; - } - - var rppa_diff = utils.sign(regulation_order[d1[rppa_key]] - regulation_order[d2[rppa_key]]); - if (rppa_diff !== 0) { - return rppa_diff; - } - - return 0; - }; - }; - return { genetic_alteration_config: genetic_alteration_config, genetic_alteration_config_nondistinct_mutations: genetic_alteration_config, diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index b80601f4c07..a8dc00173c8 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -338,6 +338,9 @@ window.Oncoprint = (function() { toSVG: function(ctr) { return renderer.toSVG(ctr); }, + setTrackGroupSortOrder: function(order) { + oncoprint.setTrackGroupSortOrder(order); + }, sortByTrack: function() { oncoprint.sortByTrack(); }, From 49b267936dfcf5e6c2ac24636cd059c2a1e496f9 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Mon, 13 Jul 2015 19:57:01 -0400 Subject: [PATCH 115/343] small bug fix --- packages/oncoprintjs/src/js/RuleSet.js | 4 ++-- packages/oncoprintjs/src/js/defaults.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/oncoprintjs/src/js/RuleSet.js b/packages/oncoprintjs/src/js/RuleSet.js index 5ee65f7263c..b4dc47136bb 100644 --- a/packages/oncoprintjs/src/js/RuleSet.js +++ b/packages/oncoprintjs/src/js/RuleSet.js @@ -177,12 +177,12 @@ window.oncoprint_RuleSet = (function() { D3SVGBarChartRuleSet.prototype = Object.create(D3SVGRuleSet.prototype); function D3SVGGeneticAlterationRuleSet(params) { - if (params.dont_distinguish_mutation_color) { + if (params && params.dont_distinguish_mutation_color) { params = $.extend({}, params, defaults.genetic_alteration_config_nondistinct_mutations); } else { params = $.extend({}, params, defaults.genetic_alteration_config); } - if (params.dont_distinguish_mutation_order) { + if (params && params.dont_distinguish_mutation_order) { this.sort_cmp = defaults.genetic_alteration_comparator_nondistinct_mutations; } else { this.sort_cmp = defaults.genetic_alteration_comparator; diff --git a/packages/oncoprintjs/src/js/defaults.js b/packages/oncoprintjs/src/js/defaults.js index 3830c9f42f8..c254047f249 100644 --- a/packages/oncoprintjs/src/js/defaults.js +++ b/packages/oncoprintjs/src/js/defaults.js @@ -43,7 +43,7 @@ window.oncoprint_defaults = (function() { return 0; }; - ; + }; var genetic_alteration_config_base = { default: [{shape: 'full-rect', color: '#D3D3D3', z_index: -1}], From 47cf2ee46375437afa3e3bc22cf2ef81d2bbe14b Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Mon, 13 Jul 2015 20:27:35 -0400 Subject: [PATCH 116/343] draw cells when you set rule set --- packages/oncoprintjs/src/js/OncoprintSVGRenderer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index 70d635bc758..10d6ff26326 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -155,13 +155,13 @@ // Rule sets OncoprintSVGRenderer.prototype.setRuleSet = function(track_id, type, params) { OncoprintRenderer.prototype.setRuleSet.call(this, track_id, type, params); + this.drawCells(track_id); this.renderLegend(); - //this.render(track_id); }; OncoprintSVGRenderer.prototype.useSameRuleSet = function(target_track_id, source_track_id) { OncoprintRenderer.prototype.useSameRuleSet.call(this, target_track_id, source_track_id); + this.drawCells(target_track_id); this.renderLegend(); - //this.render(target_track_id); } // Containers From 401a807a92f962542d51850354c8a69efaceb189 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Mon, 13 Jul 2015 20:52:36 -0400 Subject: [PATCH 117/343] bug fixes --- .../oncoprintjs/src/js/OncoprintSVGRenderer.js | 7 ++++++- packages/oncoprintjs/src/js/RuleSet.js | 15 +++++++++++---- packages/oncoprintjs/src/js/defaults.js | 6 +++--- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index 10d6ff26326..d3514dc8111 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -92,6 +92,7 @@ $(oncoprint).on(events.MOVE_TRACK, function(evt, data) { self.positionCells(data.moved_tracks, 'top'); self.renderTrackLabels(); + oncoprint.sortByTrack(); }); $(oncoprint).on(events.ADD_TRACK, function(e,d) { @@ -156,11 +157,13 @@ OncoprintSVGRenderer.prototype.setRuleSet = function(track_id, type, params) { OncoprintRenderer.prototype.setRuleSet.call(this, track_id, type, params); this.drawCells(track_id); + this.positionCells(track_id); this.renderLegend(); }; OncoprintSVGRenderer.prototype.useSameRuleSet = function(target_track_id, source_track_id) { OncoprintRenderer.prototype.useSameRuleSet.call(this, target_track_id, source_track_id); this.drawCells(target_track_id); + this.positionTrackCells(target_track_id); this.renderLegend(); } @@ -206,6 +209,7 @@ .classed(self.getTrackLabelCSSClass(track_id), true) .classed('noselect', true) .style('font', self.getLabelFont()) + .style('font-weight', 'bold') .text(self.oncoprint.getTrackLabel(track_id)) .style('top', label_top+'px') .style('cursor', 'move') @@ -251,6 +255,7 @@ //var bound_svg = this.cell_div.selectAll('svg.'+track_cell_class).data(data, id_accessor); + this.cellRenderTarget().selectAll('svg.'+track_cell_class).remove(); var bound_svg = d3.select(fragment).selectAll('svg.'+track_cell_class).data(data, id_accessor); bound_svg.enter().append('svg').classed(track_cell_class, true).classed(cell_class, true); bound_svg.style('width', oncoprint.getZoomedCellWidth()+'px').style('height', oncoprint.getCellHeight(track_id)+'px'); @@ -293,7 +298,7 @@ track_ids = [].concat(track_ids); var self = this; _.each(track_ids, function(track_id) { - self.drawTrackCells(track_ids, fragment); + self.drawTrackCells(track_id, fragment); }); this.cellRenderTarget().node().appendChild(fragment); }; diff --git a/packages/oncoprintjs/src/js/RuleSet.js b/packages/oncoprintjs/src/js/RuleSet.js index b4dc47136bb..323d976622c 100644 --- a/packages/oncoprintjs/src/js/RuleSet.js +++ b/packages/oncoprintjs/src/js/RuleSet.js @@ -402,8 +402,10 @@ window.oncoprint_RuleSet = (function() { var min = Number.POSITIVE_INFINITY; var max = Number.NEGATIVE_INFINITY; g.each(function(d,i) { - min = Math.min(min, d[self.data_key]); - max = Math.max(max, d[self.data_key]); + var datum = d[self.data_key]; + var datumIsNaN = isNaN(datum); + min = Math.min(min, datumIsNaN ? Number.POSITIVE_INFINITY : datum); + max = Math.max(max, datumIsNaN ? Number.NEGATIVE_INFINITY : datum); }); return [min, max]; }; @@ -437,6 +439,9 @@ window.oncoprint_RuleSet = (function() { return div.node(); }; this.apply = function(g, cell_width, cell_height) { + if (g[0].length === 0) { + return; + } this.setUpHelperFunctions(this.data_range || (this.inferred_data_range = this.inferDataRange(g))); D3SVGRule.prototype.apply.call(this, g, cell_width, cell_height); }; @@ -482,8 +487,10 @@ window.oncoprint_RuleSet = (function() { var min = Number.POSITIVE_INFINITY; var max = Number.NEGATIVE_INFINITY; g.each(function(d,i) { - min = Math.min(min, d[self.data_key]); - max = Math.max(max, d[self.data_key]); + var datum = d[self.data_key]; + var datumIsNaN = isNaN(datum); + min = Math.min(min, datumIsNaN ? Number.POSITIVE_INFINITY : datum); + max = Math.max(max, datumIsNaN ? Number.NEGATIVE_INFINITY : datum); }); return [min, max]; }; diff --git a/packages/oncoprintjs/src/js/defaults.js b/packages/oncoprintjs/src/js/defaults.js index c254047f249..142ba4c4637 100644 --- a/packages/oncoprintjs/src/js/defaults.js +++ b/packages/oncoprintjs/src/js/defaults.js @@ -97,7 +97,7 @@ window.oncoprint_defaults = (function() { }, legend_label: "Genetic Alteration", }; - var genetic_alteration_config_nondistinct_mutations = $.extend({},{},genetic_alteration_config_base); + var genetic_alteration_config_nondistinct_mutations = $.extend(true,{},genetic_alteration_config_base); genetic_alteration_config_nondistinct_mutations.altered.mut_type = { '*': { shape: 'middle-rect', @@ -105,7 +105,7 @@ window.oncoprint_defaults = (function() { legend_label: 'Mutation' } }; - var genetic_alteration_config = $.extend({},{},genetic_alteration_config_base); + var genetic_alteration_config = $.extend(true,{},genetic_alteration_config_base); genetic_alteration_config.altered.mut_type = { 'MISSENSE': { shape: 'middle-rect', @@ -131,7 +131,7 @@ window.oncoprint_defaults = (function() { return { genetic_alteration_config: genetic_alteration_config, - genetic_alteration_config_nondistinct_mutations: genetic_alteration_config, + genetic_alteration_config_nondistinct_mutations: genetic_alteration_config_nondistinct_mutations, genetic_alteration_comparator: makeGeneticAlterationComparator(true), genetic_alteration_comparator_nondistinct_mutations: makeGeneticAlterationComparator(false) }; From 250230aa1bc40a17b8b6df811b2a38b4f9f4660c Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Tue, 14 Jul 2015 15:54:15 -0400 Subject: [PATCH 118/343] dealing with nans in numerical data --- packages/oncoprintjs/src/js/RuleSet.js | 72 ++++++++++++++++---------- 1 file changed, 44 insertions(+), 28 deletions(-) diff --git a/packages/oncoprintjs/src/js/RuleSet.js b/packages/oncoprintjs/src/js/RuleSet.js index 323d976622c..6361e6210f4 100644 --- a/packages/oncoprintjs/src/js/RuleSet.js +++ b/packages/oncoprintjs/src/js/RuleSet.js @@ -12,6 +12,27 @@ window.oncoprint_RuleSet = (function() { var getRuleSetId = utils.makeIdCounter(); + var numericalNaNSort = function(d1, d2) { + var f1 = parseFloat(d1[this.data_key], 10); + var f2 = parseFloat(d2[this.data_key], 10); + var f1_isNaN = isNaN(f1); + var f2_isNaN = isNaN(f2); + if (f1_isNaN && f2_isNaN) { + return 0; + } else if (!f1_isNaN && !f2_isNaN) { + if (f1 < f2) { + return -1; + } else if (f1 > f2) { + return 1; + } else { + return 0; + } + } else if (f1_isNaN) { + return 1; + } else { + return -1; + } + }; var D3SVGRuleSet = (function() { function D3SVGRuleSet(params) { this.rule_map = {}; @@ -125,24 +146,16 @@ window.oncoprint_RuleSet = (function() { function D3SVGGradientColorRuleSet(params) { D3SVGRuleSet.call(this, params); this.type = GRADIENT_COLOR; + this.data_key = params.data_key; var rule = this.addGradientRule({ shape: utils.makeD3SVGElement('rect'), data_key: params.data_key, data_range: params.data_range, color_range: params.color_range, - scale: params.scale + scale: params.scale, + na_color: params.na_color }); - this.sort_cmp = params.sort_cmp || function(d1,d2) { - var f1 = parseFloat(d1[params.data_key], 10); - var f2 = parseFloat(d2[params.data_key], 10); - if (f1 < f2) { - return -1; - } else if (f1 > f2) { - return 1; - } else { - return 0; - } - }; + this.sort_cmp = params.sort_cmp || $.proxy(numericalNaNSort, this); this.getLegendDiv = function(cell_width, cell_height) { return this.rule_map[rule].getLegendDiv(cell_width, cell_height); }; @@ -153,23 +166,15 @@ window.oncoprint_RuleSet = (function() { D3SVGRuleSet.call(this, params); var self = this; self.type = BAR_CHART; + self.data_key = params.data_key; var rule = this.addBarChartRule({ data_key: params.data_key, data_range: params.data_range, scale: params.scale, fill: params.fill, + na_color: params.na_color }); - this.sort_cmp = params.sort_cmp || function(d1,d2) { - var f1 = parseFloat(d1[params.data_key], 10); - var f2 = parseFloat(d2[params.data_key], 10); - if (f1 < f2) { - return -1; - } else if (f1 > f2) { - return 1; - } else { - return 0; - } - }; + this.sort_cmp = params.sort_cmp || $.proxy(numericalNaNSort, this); this.getLegendDiv = function(cell_width, cell_height) { return this.rule_map[rule].getLegendDiv(cell_width, cell_height); }; @@ -365,7 +370,14 @@ window.oncoprint_RuleSet = (function() { this.data_key = params.data_key; this.data_range = params.data_range; this.inferred_data_range; - this.attrs.fill = params.fill || '#000000'; + this.attrs.fill = function(d) { + if (isNaN(d[params.data_key])) { + return params.na_color; + } else { + return params.fill; + } + }; + this.na_color = params.na_color; var scale = function(x) { if (params.scale === 'log') { @@ -388,10 +400,10 @@ window.oncoprint_RuleSet = (function() { return distance * 100; }; var y_function = function(d) { - return (100 - height_helper(d)) + '%'; + return (isNaN(d[params.data_key]) ? "0" : (100 - height_helper(d))) + '%'; }; var height_function = function(d) { - return height_helper(d) + '%'; + return (isNaN(d[params.data_key]) ? "100" : height_helper(d)) + '%'; }; this.attrs.height = height_function; this.attrs.y = y_function; @@ -431,7 +443,7 @@ window.oncoprint_RuleSet = (function() { .attr('width', '1px') .attr('height', height+'px') .attr('y', (cell_height-height)+'px') - .attr('fill', this.attrs.fill) + .attr('fill', params.fill) .attr('x', i+'px'); } div.append('h2').text(data_range[1]).classed('oncoprint-legend-label', true); @@ -455,6 +467,7 @@ window.oncoprint_RuleSet = (function() { this.data_range = params.data_range; this.inferred_data_range; this.color_range = params.color_range; + this.na_color = params.na_color; var makeDatum = function(x) { var ret = {}; @@ -472,7 +485,10 @@ window.oncoprint_RuleSet = (function() { this.setUpHelperFunctions = function(data_range) { var scaled_data_range = _.map(data_range, scale); var fill_function = function(d) { - var datum = scale(d[params.data_key]); + if (isNaN(d[params.data_key])) { + return params.na_color; + } + var datum = scale(d[params.data_key]); var data_range = [scaled_data_range[0], scaled_data_range[1]]; var distance = (datum-scaled_data_range[0]) / (scaled_data_range[1]-scaled_data_range[0]); color_range = [d3.rgb(params.color_range[0]).toString(), From 2842b621d66050ee4a89f68558d69ba955bbbbfa Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Tue, 14 Jul 2015 17:06:22 -0400 Subject: [PATCH 119/343] bringing transition back, just left --- packages/oncoprintjs/src/js/OncoprintSVGRenderer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index d3514dc8111..abe5971a4f2 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -338,7 +338,7 @@ return left_fn(d)+'px'; }; if (!axis || axis === "left") { - animated.style('left', left_px_fn) + animated.transition().duration(500).style('left', left_px_fn) nonanimated.style('left', left_px_fn) } if (!axis || axis === "top") { From 0457158a9d93740f0b70d770d29883d4bc9d9ae0 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Tue, 14 Jul 2015 17:41:55 -0400 Subject: [PATCH 120/343] trigger finished rendering event --- packages/oncoprintjs/src/js/OncoprintSVGRenderer.js | 3 +++ packages/oncoprintjs/src/js/events.js | 2 +- packages/oncoprintjs/src/js/oncoprint.js | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index abe5971a4f2..af09f27fe9f 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -301,6 +301,9 @@ self.drawTrackCells(track_id, fragment); }); this.cellRenderTarget().node().appendChild(fragment); + setTimeout(function() { + $(self).trigger(events.FINISHED_RENDERING); + }, 0); }; // Positioning diff --git a/packages/oncoprintjs/src/js/events.js b/packages/oncoprintjs/src/js/events.js index 678b22b8723..c370aa41607 100644 --- a/packages/oncoprintjs/src/js/events.js +++ b/packages/oncoprintjs/src/js/events.js @@ -44,7 +44,7 @@ window.oncoprint_events = { SET_PRE_TRACK_PADDING: 'set_pre_track_padding.oncoprint', TRACK_INIT: 'init.track.oncoprint', UPDATE_RENDER_RULES: 'update_render_rules.cell_renderer.oncoprint', - FINISHED_RENDERING: 'finished_rendering.renderer.oncoprint', + FINISHED_RENDERING: 'finished_rendering.oncoprint', FINISHED_POSITIONING: 'finished_positioning.renderer.oncoprint', SET_ZOOM: 'set_zoom.oncoprint', SET_SORT_DIRECTION: 'set_sort_direction.oncoprint' diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index a8dc00173c8..577cc671c35 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -363,6 +363,9 @@ window.Oncoprint = (function() { $(oncoprint).on(events.MOVE_TRACK, function() { $(ret).trigger(events.MOVE_TRACK); }); + $(renderer).on(events.FINISHED_RENDERING, function() { + $(ret).trigger(events.FINISHED_RENDERING); + }); return ret; } }; From 10273b0aeba0283a59f512c4bc10a255e6d8ac15 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Tue, 14 Jul 2015 18:16:25 -0400 Subject: [PATCH 121/343] show/hide legends --- packages/oncoprintjs/src/js/OncoprintSVGRenderer.js | 11 +++++++++++ packages/oncoprintjs/src/js/RuleSet.js | 1 + packages/oncoprintjs/src/js/oncoprint.js | 3 +++ 3 files changed, 15 insertions(+) diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index af09f27fe9f..545f34ecf7f 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -386,12 +386,23 @@ this.cell_div.style('display', 'inherit'); this.renderLegend(); }; + OncoprintSVGRenderer.prototype.setLegendVisible = function(track_ids, visible) { + var self = this; + track_ids = typeof track_ids === "undefined" ? this.oncoprint.getTracks() : [].concat(track_ids); + _.each(track_ids, function(id) { + self.getRuleSet(id).exclude_from_legend = !visible; + }); + this.renderLegend(); + }; OncoprintSVGRenderer.prototype.renderLegend = function() { var cell_width = this.oncoprint.getZoomedCellWidth(); var self = this; var rendered = {}; self.legend_table.selectAll('*').remove(); _.each(this.rule_sets, function(rule_set, track_id) { + if (rule_set.exclude_from_legend) { + return; + } var rule_set_id = rule_set.getRuleSetId(); if (!rendered.hasOwnProperty(rule_set_id)) { var tr = self.legend_table.append('tr'); diff --git a/packages/oncoprintjs/src/js/RuleSet.js b/packages/oncoprintjs/src/js/RuleSet.js index 6361e6210f4..85a94f72291 100644 --- a/packages/oncoprintjs/src/js/RuleSet.js +++ b/packages/oncoprintjs/src/js/RuleSet.js @@ -38,6 +38,7 @@ window.oncoprint_RuleSet = (function() { this.rule_map = {}; this.rule_set_id = getRuleSetId(); this.legend_label = params.legend_label; + this.exclude_from_legend = false; }; var getRuleId = utils.makeIdCounter(); diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 577cc671c35..c61a06a838e 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -358,6 +358,9 @@ window.Oncoprint = (function() { }, releaseRendering: function() { renderer.releaseRendering(); + }, + setLegendVisible: function(track_ids, visible) { + renderer.setLegendVisible(track_ids, visible); } }; $(oncoprint).on(events.MOVE_TRACK, function() { From 01dd47249030e6697e613c2c49e909f7a81a7d65 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Tue, 14 Jul 2015 18:59:40 -0400 Subject: [PATCH 122/343] hide unused rules from legend --- .../src/js/OncoprintSVGRenderer.js | 10 ++++- packages/oncoprintjs/src/js/RuleSet.js | 44 ++++++++++++------- 2 files changed, 36 insertions(+), 18 deletions(-) diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index 545f34ecf7f..b61beefd8c9 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -45,6 +45,7 @@ function OncoprintSVGRenderer(container_selector_string, oncoprint, config) { OncoprintRenderer.call(this, oncoprint, config); var self = this; + this.active_rule_set_rules = {}; this.toolbar_container; this.label_div; this.label_drag_div; @@ -156,6 +157,7 @@ // Rule sets OncoprintSVGRenderer.prototype.setRuleSet = function(track_id, type, params) { OncoprintRenderer.prototype.setRuleSet.call(this, track_id, type, params); + this.active_rule_set_rules[this.getRuleSet(track_id).getRuleSetId()] = {}; this.drawCells(track_id); this.positionCells(track_id); this.renderLegend(); @@ -290,7 +292,7 @@ }); }); bound_svg.selectAll('*').remove(); - rule_set.apply(bound_svg, data, id_accessor, oncoprint.getZoomedCellWidth(), oncoprint.getCellHeight(track_id)); + this.active_rule_set_rules[rule_set.getRuleSetId()][track_id] = rule_set.apply(bound_svg, data, id_accessor, oncoprint.getZoomedCellWidth(), oncoprint.getCellHeight(track_id)); }; OncoprintSVGRenderer.prototype.drawCells = function(track_ids) { var fragment = document.createDocumentFragment(); @@ -404,13 +406,17 @@ return; } var rule_set_id = rule_set.getRuleSetId(); + var active_rules = {}; + _.each(self.active_rule_set_rules[rule_set_id], function(track_map, track_id) { + $.extend(active_rules, track_map); + }); if (!rendered.hasOwnProperty(rule_set_id)) { var tr = self.legend_table.append('tr'); var label_header = tr.append('td').style('padding-top', '1em').style('padding-bottom', '1em') .append('h1').classed('oncoprint-legend-header', true); label_header.text(rule_set.getLegendLabel()); var legend_body_td = tr.append('td'); - var legend_div = rule_set.getLegendDiv(cell_width, self.oncoprint.getCellHeight(track_id)); + var legend_div = rule_set.getLegendDiv(active_rules, cell_width, self.oncoprint.getCellHeight(track_id)); legend_body_td.node().appendChild(legend_div); d3.select(legend_div).selectAll('*').classed('oncoprint-legend-element', true); rendered[rule_set_id] = true; diff --git a/packages/oncoprintjs/src/js/RuleSet.js b/packages/oncoprintjs/src/js/RuleSet.js index 85a94f72291..789ed308fe2 100644 --- a/packages/oncoprintjs/src/js/RuleSet.js +++ b/packages/oncoprintjs/src/js/RuleSet.js @@ -79,11 +79,16 @@ window.oncoprint_RuleSet = (function() { return sorted_rules; }; D3SVGRuleSet.prototype.apply = function(g, data, datum_id_accessor, cell_width, cell_height) { + var active_rules = {}; _.each(this.getRules(), function(rule) { var affected_data = rule.filterData(data); + if (affected_data.length > 0) { + active_rules[rule.rule_id] = true; + } var affected_groups = g.data(affected_data, datum_id_accessor); rule.apply(affected_groups, cell_width, cell_height); }); + return active_rules; }; D3SVGRuleSet.prototype.getRule = function(rule_id) { return this.rule_map[rule_id]; @@ -127,15 +132,17 @@ window.oncoprint_RuleSet = (function() { addColorRule(new_color, category); } }); - D3SVGRuleSet.prototype.apply.call(this, g, data, datum_id_accessor, cell_width, cell_height); + return D3SVGRuleSet.prototype.apply.call(this, g, data, datum_id_accessor, cell_width, cell_height); }; - self.getLegendDiv = function(cell_width, cell_height) { + self.getLegendDiv = function(active_rules, cell_width, cell_height) { var div = d3.select(document.createElement('div')); _.each(self.getRules(), function(rule) { - var legend_div = rule.getLegendDiv(cell_width, cell_height); - if (legend_div) { - div.node().appendChild(legend_div); + if (active_rules[rule.rule_id]) { + var legend_div = rule.getLegendDiv(cell_width, cell_height); + if (legend_div) { + div.node().appendChild(legend_div); + } } }); utils.d3SelectChildren(div, '*').style('padding-right', '20px'); @@ -157,8 +164,8 @@ window.oncoprint_RuleSet = (function() { na_color: params.na_color }); this.sort_cmp = params.sort_cmp || $.proxy(numericalNaNSort, this); - this.getLegendDiv = function(cell_width, cell_height) { - return this.rule_map[rule].getLegendDiv(cell_width, cell_height); + this.getLegendDiv = function(active_rules, cell_width, cell_height) { + return (active_rules[rule] && this.rule_map[rule].getLegendDiv(cell_width, cell_height)) || $('
')[0]; }; } D3SVGGradientColorRuleSet.prototype = Object.create(D3SVGRuleSet.prototype); @@ -176,8 +183,8 @@ window.oncoprint_RuleSet = (function() { na_color: params.na_color }); this.sort_cmp = params.sort_cmp || $.proxy(numericalNaNSort, this); - this.getLegendDiv = function(cell_width, cell_height) { - return this.rule_map[rule].getLegendDiv(cell_width, cell_height); + this.getLegendDiv = function(active_rules, cell_width, cell_height) { + return (active_rules[rule] && this.rule_map[rule].getLegendDiv(cell_width, cell_height)) || $('
')[0]; }; } D3SVGBarChartRuleSet.prototype = Object.create(D3SVGRuleSet.prototype); @@ -268,12 +275,14 @@ window.oncoprint_RuleSet = (function() { _.each(params.default, function(rule_spec) { makeStaticShapeRule(rule_spec); }); - self.getLegendDiv = function(cell_width, cell_height) { + self.getLegendDiv = function(active_rules, cell_width, cell_height) { var div = d3.select(document.createElement('div')); _.each(self.getRules(), function(rule) { - var legend_div = rule.getLegendDiv(cell_width, cell_height); - if (legend_div) { - div.node().appendChild(legend_div); + if (active_rules[rule.rule_id]) { + var legend_div = rule.getLegendDiv(cell_width, cell_height); + if (legend_div) { + div.node().appendChild(legend_div); + } } }); utils.d3SelectChildren(div, '*').style('padding-right', '20px'); @@ -362,6 +371,9 @@ window.oncoprint_RuleSet = (function() { D3SVGRule.prototype.isActive = function(data) { return this.filterData(data).length > 0; }; + D3SVGRule.prototype.showInLegend = function() { + return !this.exclude_from_legend; + }; return D3SVGRule; })(); @@ -424,7 +436,7 @@ window.oncoprint_RuleSet = (function() { }; this.getLegendDiv = function(cell_width, cell_height) { - if (params.exclude_from_legend) { + if (!this.showInLegend()) { return; } var div = d3.select(document.createElement('div')); @@ -513,7 +525,7 @@ window.oncoprint_RuleSet = (function() { }; this.getLegendDiv = function(cell_width, cell_height) { - if (params.exclude_from_legend) { + if (!this.showInLegend()) { return; } var div = d3.select(document.createElement('div')); @@ -549,7 +561,7 @@ window.oncoprint_RuleSet = (function() { D3SVGRule.call(this, params, rule_id); this.getLegendDiv = function(cell_width, cell_height) { - if (params.exclude_from_legend) { + if (!this.showInLegend()) { return; } var div = d3.select(document.createElement('div')); From 386cf461665ef72e0b1a50fca46ed6322b8ed75c Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Wed, 15 Jul 2015 15:13:07 -0400 Subject: [PATCH 123/343] CSS3 transitions --- packages/oncoprintjs/src/css/oncoprint.css | 7 +++++++ .../oncoprintjs/src/js/OncoprintSVGRenderer.js | 14 +++++++------- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/packages/oncoprintjs/src/css/oncoprint.css b/packages/oncoprintjs/src/css/oncoprint.css index d777271ff1d..e8ed3b43a1c 100644 --- a/packages/oncoprintjs/src/css/oncoprint.css +++ b/packages/oncoprintjs/src/css/oncoprint.css @@ -22,6 +22,13 @@ position: absolute; } +.oncoprint-animated { + -webkit-transition: 0.5s ease-out; + -moz-transition: 0.5s ease-out; + -o-transition: 0.5s ease-out; + transition: 0.5s ease-out; +} + .oncoprint-cell-hover { outline: 1px solid DarkGray; } diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index b61beefd8c9..60b00d9dd26 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -338,19 +338,19 @@ var nonanimated = bound_svg.filter(function(d) { return !(currently_in_view(this) || coming_into_view(d)); }); + bound_svg.classed('oncoprint-animated', false); + if (!axis || axis === "top") { + bound_svg.style('top', y+'px'); + } var left_px_fn = function(d,i) { return left_fn(d)+'px'; }; if (!axis || axis === "left") { - animated.transition().duration(500).style('left', left_px_fn) - nonanimated.style('left', left_px_fn) + animated.classed('oncoprint-animated', true); + bound_svg.style('left', left_px_fn); } - if (!axis || axis === "top") { - animated.style('top', y+'px'); - nonanimated.style('top', y+'px'); - } - // TODO: integrate transitions above + }; OncoprintSVGRenderer.prototype.positionCells = function(track_ids, axis) { track_ids = typeof track_ids === "undefined" ? this.oncoprint.getTracks() : track_ids; From 9643cb131511ec121d3b1727be0e81a82a783b16 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Wed, 15 Jul 2015 17:54:41 -0400 Subject: [PATCH 124/343] take out transitions, add back clipping --- .../src/js/OncoprintSVGRenderer.js | 35 +++++++++++++++---- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index 60b00d9dd26..63dad03b18a 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -76,6 +76,9 @@ //self.cell_container.style('display', 'none'); self.cell_container_node = self.cell_container.node(); self.cell_div = self.cell_container.append('div').classed(CELL_AREA_CLASS, true); + self.cell_container_node.addEventListener("scroll", function() { + self.clipCells(); + }); // TODO: magic number self.cell_div.style('max-width', '1000px'); })(); @@ -88,7 +91,6 @@ $(oncoprint).on(events.REMOVE_TRACK, function(evt, data) { delete self.rule_sets[data.track_id]; throw "not implemented"; - self.render(); }); $(oncoprint).on(events.MOVE_TRACK, function(evt, data) { self.positionCells(data.moved_tracks, 'top'); @@ -102,6 +104,7 @@ self.positionCells(); self.renderTrackLabels(); self.resizeLabelDiv(); + self.clipCells(true, d.track_id); //this.cell_div.style('display','inherit'); }); @@ -112,12 +115,14 @@ self.renderTrackLabels(d.track_id); self.resizeCellDiv(); self.renderLegend(); + self.clipCells(true); //this.cell_div.style('display','inherit'); }); $(oncoprint).on(events.SET_CELL_PADDING, function(e,d) { self.positionCells(undefined, 'left'); + self.clipCells(true); self.resizeCellDiv(); }); @@ -125,11 +130,13 @@ self.positionCells(undefined, 'left'); self.resizeCells(); self.resizeCellDiv(); + self.clipCells(true); }); $(oncoprint).on(events.SET_ID_ORDER, function() { self.positionCells(undefined, 'left'); - }) + self.clipCells(true); + }); })(); } utils.extends(OncoprintSVGRenderer, OncoprintRenderer); @@ -193,10 +200,10 @@ }; OncoprintSVGRenderer.prototype.renderTrackLabels = function(track_ids, y) { var div = this.label_div; - track_ids = typeof track_ids === "undefined" ? this.oncoprint.getTracks() : track_ids; if (typeof y !== "undefined") { div.selectAll(this.getTrackLabelCSSSelector(track_ids)).style('top', y+'px'); } else { + track_ids = typeof track_ids === "undefined" ? this.oncoprint.getTracks() : track_ids; track_ids = [].concat(track_ids); var label_tops = this.getTrackLabelTops(); var self = this; @@ -308,6 +315,25 @@ }, 0); }; + // Clipping + OncoprintSVGRenderer.prototype.clipCells = function(force, track_id) { + var visible_interval = this.getVisibleInterval(); + var interval_width = visible_interval[1] - visible_interval[0]; + var interval_number = Math.floor(visible_interval[0] / interval_width); + visible_interval = _.map([-interval_width, 2*interval_width], function(x) { return x + interval_number*interval_width; }); + if (interval_number !== this.prev_interval_number || force) { + var selector_class = (typeof track_id === 'undefined' ? this.getTrackCellCSSClass(track_id) : this.getCellCSSClass()); + this.cell_div.selectAll('svg.'+ selector_class).each(function(d,i) { + var x = parseInt(this.style.left) || 0; + var disp = this.style.display; + var new_disp = (x < visible_interval[0] || x > visible_interval[1]) ? 'none' : 'inherit'; + if (disp !== new_disp) { + this.style.display = new_disp; + } + }); + } + this.prev_interval_number = interval_number; + }; // Positioning OncoprintSVGRenderer.prototype.positionTrackCells = function(track_id, axis) { var oncoprint = this.oncoprint; @@ -338,7 +364,6 @@ var nonanimated = bound_svg.filter(function(d) { return !(currently_in_view(this) || coming_into_view(d)); }); - bound_svg.classed('oncoprint-animated', false); if (!axis || axis === "top") { bound_svg.style('top', y+'px'); @@ -347,10 +372,8 @@ return left_fn(d)+'px'; }; if (!axis || axis === "left") { - animated.classed('oncoprint-animated', true); bound_svg.style('left', left_px_fn); } - }; OncoprintSVGRenderer.prototype.positionCells = function(track_ids, axis) { track_ids = typeof track_ids === "undefined" ? this.oncoprint.getTracks() : track_ids; From 974b3085628c4566e4f1b20177df5b70d9355193 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Wed, 15 Jul 2015 18:45:36 -0400 Subject: [PATCH 125/343] experimental clipping and positioning... --- .../src/js/OncoprintSVGRenderer.js | 58 +++++++++++++++---- 1 file changed, 46 insertions(+), 12 deletions(-) diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index 63dad03b18a..83fec976b08 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -77,7 +77,7 @@ self.cell_container_node = self.cell_container.node(); self.cell_div = self.cell_container.append('div').classed(CELL_AREA_CLASS, true); self.cell_container_node.addEventListener("scroll", function() { - self.clipCells(); + self.clipAndPositionCells(); }); // TODO: magic number self.cell_div.style('max-width', '1000px'); @@ -93,7 +93,7 @@ throw "not implemented"; }); $(oncoprint).on(events.MOVE_TRACK, function(evt, data) { - self.positionCells(data.moved_tracks, 'top'); + self.clipAndPositionCells(data.moved_tracks, 'top', true); self.renderTrackLabels(); oncoprint.sortByTrack(); }); @@ -101,41 +101,41 @@ $(oncoprint).on(events.ADD_TRACK, function(e,d) { //this.cell_div.style('display', 'none'); self.drawCells(d.track_id); - self.positionCells(); + self.clipAndPositionCells(undefined, 'top', true); self.renderTrackLabels(); self.resizeLabelDiv(); - self.clipCells(true, d.track_id); + //self.clipCells(true, d.track_id); //this.cell_div.style('display','inherit'); }); $(oncoprint).on(events.SET_TRACK_DATA, function(e,d) { //this.cell_div.style('display', 'none'); self.drawCells(d.track_id); - self.positionTrackCells(d.track_id); + self.clipAndPositionCells(d.track_id, undefined, true); self.renderTrackLabels(d.track_id); self.resizeCellDiv(); self.renderLegend(); - self.clipCells(true); + //self.clipCells(true); //this.cell_div.style('display','inherit'); }); $(oncoprint).on(events.SET_CELL_PADDING, function(e,d) { - self.positionCells(undefined, 'left'); - self.clipCells(true); + self.clipAndPositionCells(undefined, undefined, true); + //self.clipCells(true); self.resizeCellDiv(); }); $(oncoprint).on(events.SET_ZOOM, function(e,d) { - self.positionCells(undefined, 'left'); + self.clipAndPositionCells(undefined, undefined, true); self.resizeCells(); self.resizeCellDiv(); - self.clipCells(true); + //self.clipCells(true); }); $(oncoprint).on(events.SET_ID_ORDER, function() { - self.positionCells(undefined, 'left'); - self.clipCells(true); + self.clipAndPositionCells(undefined, undefined, true); + //self.clipCells(true); }); })(); } @@ -335,6 +335,40 @@ this.prev_interval_number = interval_number; }; // Positioning + OncoprintSVGRenderer.prototype.clipAndPositionCells = function(track_ids, axis, force) { + track_ids = typeof track_ids === "undefined" ? this.oncoprint.getTracks() : track_ids; + var visible_interval = this.getVisibleInterval(); + var interval_width = visible_interval[1] - visible_interval[0]; + var interval_number = Math.floor(visible_interval[0] / interval_width); + visible_interval = _.map([-interval_width, 2*interval_width], function(x) { return x + interval_number*interval_width; }); + var self = this; + _.each(track_ids, function(track_id) { + var y;// = 20;//this.getTrackCellTops()[track_id]; uncomment if its fast + if (!axis || axis === 'top') { + y = self.getTrackCellTops()[track_id]; + } + var id_key = self.oncoprint.getTrackDatumIdKey(track_id); + var id_order = utils.invert_array(self.oncoprint.getIdOrder()); + if ((interval_number !== self.prev_interval_number) || force) { + var selector_class = self.getTrackCellCSSClass(track_id); + self.cell_div.selectAll('svg.'+ selector_class).each(function(d,i) { + var new_x = self.getCellX(id_order[d[id_key]]); + var disp = this.style.display; + var new_disp = (new_x < visible_interval[0] || new_x > visible_interval[1]) ? 'none' : 'inherit'; + if (disp !== new_disp) { + this.style.display = new_disp; + } + if ((!axis || axis === 'left') && new_disp !== 'none') { + this.style.left = new_x + 'px'; + } + if ((!axis || axis === 'top') && new_disp !== 'none') { + this.style.top = y+'px'; + } + }); + } + }); + this.prev_interval_number = interval_number; + }; OncoprintSVGRenderer.prototype.positionTrackCells = function(track_id, axis) { var oncoprint = this.oncoprint; var bound_svg = this.cell_div.selectAll('svg.'+this.getTrackCellCSSClass(track_id)) From 67de80b575c5fde1e5eb23cebcc721bbfd46124b Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Thu, 16 Jul 2015 13:06:02 -0400 Subject: [PATCH 126/343] fix sorting a bit --- .../oncoprintjs/src/js/OncoprintSVGRenderer.js | 4 +++- packages/oncoprintjs/src/js/RuleSet.js | 18 +++++++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index 83fec976b08..e1b99af62db 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -336,6 +336,7 @@ }; // Positioning OncoprintSVGRenderer.prototype.clipAndPositionCells = function(track_ids, axis, force) { + this.cell_div.node().display = 'none'; track_ids = typeof track_ids === "undefined" ? this.oncoprint.getTracks() : track_ids; var visible_interval = this.getVisibleInterval(); var interval_width = visible_interval[1] - visible_interval[0]; @@ -343,7 +344,7 @@ visible_interval = _.map([-interval_width, 2*interval_width], function(x) { return x + interval_number*interval_width; }); var self = this; _.each(track_ids, function(track_id) { - var y;// = 20;//this.getTrackCellTops()[track_id]; uncomment if its fast + var y; if (!axis || axis === 'top') { y = self.getTrackCellTops()[track_id]; } @@ -368,6 +369,7 @@ } }); this.prev_interval_number = interval_number; + this.cell_div.node().display = 'block'; }; OncoprintSVGRenderer.prototype.positionTrackCells = function(track_id, axis) { var oncoprint = this.oncoprint; diff --git a/packages/oncoprintjs/src/js/RuleSet.js b/packages/oncoprintjs/src/js/RuleSet.js index 789ed308fe2..45b560ca207 100644 --- a/packages/oncoprintjs/src/js/RuleSet.js +++ b/packages/oncoprintjs/src/js/RuleSet.js @@ -120,7 +120,23 @@ window.oncoprint_RuleSet = (function() { }); this.sort_cmp = params.sort_cmp || function(d1,d2) { - return params.getCategory(d1).toString().localeCompare(params.getCategory(d2).toString()); + var cat1 = params.getCategory(d1); + var cat2 = params.getCategory(d2); + if (typeof cat1 !== 'string') { + cat1 = cat1.toString(); + } + if (typeof cat2 !== 'string') { + cat2 = cat2.toString(); + } + if (cat1 === cat2) { + return 0; + } else if (cat1 === 'NA') { + return 1; + } else if (cat2 === 'NA') { + return -1; + } else { + return cat1.localeCompare(cat2); + } }; self.apply = function(g, data, datum_id_accessor, cell_width, cell_height) { var missing_categories = []; From 1b04ffd700968238023bab43f8bd42c66c749e1d Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Thu, 16 Jul 2015 13:39:20 -0400 Subject: [PATCH 127/343] clipping/positioning optimizations --- .../src/js/OncoprintSVGRenderer.js | 36 ++++++++++--------- packages/oncoprintjs/src/js/oncoprint.js | 7 +++- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index e1b99af62db..296ca326334 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -45,6 +45,7 @@ function OncoprintSVGRenderer(container_selector_string, oncoprint, config) { OncoprintRenderer.call(this, oncoprint, config); var self = this; + this.track_cell_selections = {}; this.active_rule_set_rules = {}; this.toolbar_container; this.label_div; @@ -300,6 +301,7 @@ }); bound_svg.selectAll('*').remove(); this.active_rule_set_rules[rule_set.getRuleSetId()][track_id] = rule_set.apply(bound_svg, data, id_accessor, oncoprint.getZoomedCellWidth(), oncoprint.getCellHeight(track_id)); + self.track_cell_selections[track_id] = bound_svg; }; OncoprintSVGRenderer.prototype.drawCells = function(track_ids) { var fragment = document.createDocumentFragment(); @@ -339,7 +341,7 @@ this.cell_div.node().display = 'none'; track_ids = typeof track_ids === "undefined" ? this.oncoprint.getTracks() : track_ids; var visible_interval = this.getVisibleInterval(); - var interval_width = visible_interval[1] - visible_interval[0]; + var interval_width = 2*(visible_interval[1] - visible_interval[0]); var interval_number = Math.floor(visible_interval[0] / interval_width); visible_interval = _.map([-interval_width, 2*interval_width], function(x) { return x + interval_number*interval_width; }); var self = this; @@ -349,23 +351,25 @@ y = self.getTrackCellTops()[track_id]; } var id_key = self.oncoprint.getTrackDatumIdKey(track_id); - var id_order = utils.invert_array(self.oncoprint.getIdOrder()); + var id_order = self.oncoprint.getInvertedIdOrder(); if ((interval_number !== self.prev_interval_number) || force) { var selector_class = self.getTrackCellCSSClass(track_id); - self.cell_div.selectAll('svg.'+ selector_class).each(function(d,i) { - var new_x = self.getCellX(id_order[d[id_key]]); - var disp = this.style.display; - var new_disp = (new_x < visible_interval[0] || new_x > visible_interval[1]) ? 'none' : 'inherit'; - if (disp !== new_disp) { - this.style.display = new_disp; - } - if ((!axis || axis === 'left') && new_disp !== 'none') { - this.style.left = new_x + 'px'; - } - if ((!axis || axis === 'top') && new_disp !== 'none') { - this.style.top = y+'px'; - } - }); + if (self.track_cell_selections.hasOwnProperty(track_id)) { + self.track_cell_selections[track_id].each(function(d,i) { + var new_x = self.getCellX(id_order[d[id_key]]); + var disp = this.style.display; + var new_disp = (new_x < visible_interval[0] || new_x > visible_interval[1]) ? 'none' : 'inherit'; + if (disp !== new_disp) { + this.style.display = new_disp; + } + if ((!axis || axis === 'left') && new_disp !== 'none') { + this.style.left = new_x + 'px'; + } + if ((!axis || axis === 'top') && new_disp !== 'none') { + this.style.top = y+'px'; + } + }); + } } }); this.prev_interval_number = interval_number; diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index c61a06a838e..7b0e0e23d50 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -65,6 +65,7 @@ window.Oncoprint = (function() { self.config = config; self.id_order = []; + self.inverted_id_order = {}; self.track_groups = [[],[]]; self.track_group_sort_order = [0,1]; self.sort_direction = {}; @@ -123,8 +124,12 @@ window.Oncoprint = (function() { self.getIdOrder = function() { return self.id_order.slice(); }; + self.getInvertedIdOrder = function() { + return self.inverted_id_order; + }; self.setIdOrder = function(id_order) { self.id_order = id_order.slice(); + self.inverted_id_order = utils.invert_array(self.id_order); $(self).trigger(events.SET_ID_ORDER); }; @@ -272,7 +277,7 @@ window.Oncoprint = (function() { var id_accessor = self.getTrackDatumIdAccessor(track_id); self.tracks[track_id].data = data; - self.id_order = self.id_order.concat(_.difference(_.map(data, id_accessor), self.id_order)); + self.setIdOrder(self.id_order.concat(_.difference(_.map(data, id_accessor), self.id_order))); self.tracks[track_id].id_data_map = {}; var id_data_map = self.tracks[track_id].id_data_map; From 8e70340c289eb58effb7fb6935b5e1ace466aec4 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Thu, 16 Jul 2015 14:06:25 -0400 Subject: [PATCH 128/343] change clipping interval width, add more auto-fill colors, and delete some legacy code --- .../src/js/OncoprintSVGRenderer.js | 104 +----------------- packages/oncoprintjs/src/js/RuleSet.js | 3 +- 2 files changed, 7 insertions(+), 100 deletions(-) diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index 296ca326334..a834bae0135 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -159,7 +159,7 @@ var self = this; $(this.cell_div.node()).ready(function() { self.resizeCells(); - self.positionCells(); + self.clipAndPositionCells(undefined, undefined, true); }); }; // Rule sets @@ -167,13 +167,13 @@ OncoprintRenderer.prototype.setRuleSet.call(this, track_id, type, params); this.active_rule_set_rules[this.getRuleSet(track_id).getRuleSetId()] = {}; this.drawCells(track_id); - this.positionCells(track_id); + this.clipAndPositionCells(track_id, undefined, true); this.renderLegend(); }; OncoprintSVGRenderer.prototype.useSameRuleSet = function(target_track_id, source_track_id) { OncoprintRenderer.prototype.useSameRuleSet.call(this, target_track_id, source_track_id); this.drawCells(target_track_id); - this.positionTrackCells(target_track_id); + this.clipAndPositionCells(target_track_id, undefined, true); this.renderLegend(); } @@ -317,31 +317,13 @@ }, 0); }; - // Clipping - OncoprintSVGRenderer.prototype.clipCells = function(force, track_id) { - var visible_interval = this.getVisibleInterval(); - var interval_width = visible_interval[1] - visible_interval[0]; - var interval_number = Math.floor(visible_interval[0] / interval_width); - visible_interval = _.map([-interval_width, 2*interval_width], function(x) { return x + interval_number*interval_width; }); - if (interval_number !== this.prev_interval_number || force) { - var selector_class = (typeof track_id === 'undefined' ? this.getTrackCellCSSClass(track_id) : this.getCellCSSClass()); - this.cell_div.selectAll('svg.'+ selector_class).each(function(d,i) { - var x = parseInt(this.style.left) || 0; - var disp = this.style.display; - var new_disp = (x < visible_interval[0] || x > visible_interval[1]) ? 'none' : 'inherit'; - if (disp !== new_disp) { - this.style.display = new_disp; - } - }); - } - this.prev_interval_number = interval_number; - }; // Positioning OncoprintSVGRenderer.prototype.clipAndPositionCells = function(track_ids, axis, force) { this.cell_div.node().display = 'none'; track_ids = typeof track_ids === "undefined" ? this.oncoprint.getTracks() : track_ids; + track_ids = [].concat(track_ids); var visible_interval = this.getVisibleInterval(); - var interval_width = 2*(visible_interval[1] - visible_interval[0]); + var interval_width = 4*(visible_interval[1] - visible_interval[0]); var interval_number = Math.floor(visible_interval[0] / interval_width); visible_interval = _.map([-interval_width, 2*interval_width], function(x) { return x + interval_number*interval_width; }); var self = this; @@ -353,7 +335,6 @@ var id_key = self.oncoprint.getTrackDatumIdKey(track_id); var id_order = self.oncoprint.getInvertedIdOrder(); if ((interval_number !== self.prev_interval_number) || force) { - var selector_class = self.getTrackCellCSSClass(track_id); if (self.track_cell_selections.hasOwnProperty(track_id)) { self.track_cell_selections[track_id].each(function(d,i) { var new_x = self.getCellX(id_order[d[id_key]]); @@ -375,82 +356,7 @@ this.prev_interval_number = interval_number; this.cell_div.node().display = 'block'; }; - OncoprintSVGRenderer.prototype.positionTrackCells = function(track_id, axis) { - var oncoprint = this.oncoprint; - var bound_svg = this.cell_div.selectAll('svg.'+this.getTrackCellCSSClass(track_id)) - .data(oncoprint.getTrackData(track_id), oncoprint.getTrackDatumIdAccessor(track_id)); - var self = this; - var id_key = oncoprint.getTrackDatumIdKey(track_id); - var id_order = utils.invert_array(oncoprint.getIdOrder()); - var y = this.getTrackCellTops()[track_id]; - var visible_interval = this.getVisibleInterval(); - - var in_view = function(i) { - return i >= visible_interval[0] && i <= visible_interval[1]; - }; - var currently_in_view = function(node) { - return !isNaN(parseInt(node.style.left)) && in_view(parseInt(node.style.left)); - }; - var left_fn = function(d) { - return self.getCellX(id_order[d[id_key]]); - }; - var coming_into_view = function(d) { - return in_view(left_fn(d)); - }; - - var animated = bound_svg.filter(function(d) { - return currently_in_view(this) || coming_into_view(d); - }); - var nonanimated = bound_svg.filter(function(d) { - return !(currently_in_view(this) || coming_into_view(d)); - }); - - if (!axis || axis === "top") { - bound_svg.style('top', y+'px'); - } - var left_px_fn = function(d,i) { - return left_fn(d)+'px'; - }; - if (!axis || axis === "left") { - bound_svg.style('left', left_px_fn); - } - }; - OncoprintSVGRenderer.prototype.positionCells = function(track_ids, axis) { - track_ids = typeof track_ids === "undefined" ? this.oncoprint.getTracks() : track_ids; - track_ids = [].concat(track_ids); - var self = this; - _.each(track_ids, function(track_id) { - self.positionTrackCells(track_id, axis); - }); - }; - OncoprintSVGRenderer.prototype.isTrackRenderable = function(track_id) { - return this.getRuleSet(track_id) && this.oncoprint.getTrackData(track_id).length > 0; - }; - OncoprintSVGRenderer.prototype.render = function(track_id) { - // TODO: do this by for each track to render, render it, because there's stuff you should only get once like celltops - var self = this; - this.resizeLabelSVG(); - this.resizeCellDiv(); - - this.cell_div.style('display', 'none'); - var renderTrack = function(track_id) { - if (self.isTrackRenderable(track_id)) { - self.drawTrackCells(track_id); - self.positionTrackCells(track_id); - self.renderTrackLabels(track_id); - } - }; - if (typeof track_id !== "undefined") { - renderTrack(track_id); - } else { - _.each(this.oncoprint.getTracks(), function(track_id) { - renderTrack(track_id); - }); - } - this.cell_div.style('display', 'inherit'); - this.renderLegend(); - }; OncoprintSVGRenderer.prototype.setLegendVisible = function(track_ids, visible) { var self = this; track_ids = typeof track_ids === "undefined" ? this.oncoprint.getTracks() : [].concat(track_ids); diff --git a/packages/oncoprintjs/src/js/RuleSet.js b/packages/oncoprintjs/src/js/RuleSet.js index 45b560ca207..a6caf816b98 100644 --- a/packages/oncoprintjs/src/js/RuleSet.js +++ b/packages/oncoprintjs/src/js/RuleSet.js @@ -100,7 +100,7 @@ window.oncoprint_RuleSet = (function() { D3SVGRuleSet.call(this, params); this.type = CATEGORICAL_COLOR; var self = this; - var d3_colors = _.shuffle(d3.scale.category20().range()); + var d3_colors = _.shuffle(d3.scale.category20().range().concat(d3.scale.category20b().range()).concat(d3.scale.category20c().range())); var addColorRule = function(color, category) { var colored_rect = utils.makeD3SVGElement('rect').attr('fill', color); var condition = (function(cat) { @@ -144,6 +144,7 @@ window.oncoprint_RuleSet = (function() { var category = params.getCategory(datum); if (!params.color.hasOwnProperty(category)) { var new_color = d3_colors.pop(); + console.log(d3_colors); params.color[category] = new_color; addColorRule(new_color, category); } From 7cc2c38d824035e021453015361baff86f91ac44 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Thu, 16 Jul 2015 14:31:02 -0400 Subject: [PATCH 129/343] fixing up available d3 colors - excluding grayscales --- packages/oncoprintjs/src/js/RuleSet.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/oncoprintjs/src/js/RuleSet.js b/packages/oncoprintjs/src/js/RuleSet.js index a6caf816b98..5e8bc70193f 100644 --- a/packages/oncoprintjs/src/js/RuleSet.js +++ b/packages/oncoprintjs/src/js/RuleSet.js @@ -100,7 +100,11 @@ window.oncoprint_RuleSet = (function() { D3SVGRuleSet.call(this, params); this.type = CATEGORICAL_COLOR; var self = this; - var d3_colors = _.shuffle(d3.scale.category20().range().concat(d3.scale.category20b().range()).concat(d3.scale.category20c().range())); + var d3_colors = _.shuffle(_.filter(d3.scale.category20().range().concat(d3.scale.category20b().range()).concat(d3.scale.category20c().range()), + function(color) { + var rgb = d3.rgb(color); + return !(rgb.r === rgb.g && rgb.g === rgb.b); + })); var addColorRule = function(color, category) { var colored_rect = utils.makeD3SVGElement('rect').attr('fill', color); var condition = (function(cat) { @@ -144,7 +148,6 @@ window.oncoprint_RuleSet = (function() { var category = params.getCategory(datum); if (!params.color.hasOwnProperty(category)) { var new_color = d3_colors.pop(); - console.log(d3_colors); params.color[category] = new_color; addColorRule(new_color, category); } From 8adc5d5beff7fcfccf811e1f2cce1e37c015fecd Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Thu, 16 Jul 2015 14:49:59 -0400 Subject: [PATCH 130/343] small usability changes - padding on the top so you can drag off more easily, and no select oncoprint area so it doesnt select when you drag --- packages/oncoprintjs/src/js/OncoprintRenderer.js | 3 ++- packages/oncoprintjs/src/js/OncoprintSVGRenderer.js | 10 ++-------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/packages/oncoprintjs/src/js/OncoprintRenderer.js b/packages/oncoprintjs/src/js/OncoprintRenderer.js index bc57f55b328..a2d6069147e 100644 --- a/packages/oncoprintjs/src/js/OncoprintRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintRenderer.js @@ -37,6 +37,7 @@ window.OncoprintRenderer = (function() { this.clipping = true; this.oncoprint = oncoprint; this.config = config; + this.upper_padding = utils.ifndef(config.upper_padding, 10); }; OncoprintRenderer.prototype.getTrackGroupSeparation = function() { // TODO: configurable @@ -80,7 +81,7 @@ window.OncoprintRenderer = (function() { }; OncoprintRenderer.prototype.getTrackTops = function() { var ret = {}; - var y = 0; + var y = this.upper_padding; var self = this; _.each(this.oncoprint.getTrackGroups(), function(group) { _.each(group, function(id) { diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index a834bae0135..548b599b56d 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -57,14 +57,8 @@ this.legend_table; this.document_fragment; - d3.select(container_selector_string).selectAll('*').remove(); - (function initToolbarContainer() { - self.toolbar_container = d3.select(container_selector_string).append('div').classed(TOOLBAR_CONTAINER_CLASS, true); - d3.select(container_selector_string).append('br'); - /*$.ajax({url: "src/html/toolbar.html", context: document.body, success: function(response) { - $(self.toolbar_container.node()).html(response); - }});*/ - })(); + d3.select(container_selector_string).classed('noselect', true).selectAll('*').remove(); + d3.select(container_selector_string).append('br'); (function initLabelContainer() { self.label_container = d3.select(container_selector_string).append('div').classed(LABEL_AREA_CONTAINER_CLASS, true).style('position', 'relative'); self.label_div = self.label_container.append('div').style('position', 'relative').style('overflow', 'hidden'); From 1abea167d7b1e2f932cd12bd6af1b2528e846ddc Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Thu, 16 Jul 2015 14:58:26 -0400 Subject: [PATCH 131/343] fix zooming then adding track issue --- packages/oncoprintjs/src/js/OncoprintSVGRenderer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index 548b599b56d..c3403645738 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -294,7 +294,7 @@ }); }); bound_svg.selectAll('*').remove(); - this.active_rule_set_rules[rule_set.getRuleSetId()][track_id] = rule_set.apply(bound_svg, data, id_accessor, oncoprint.getZoomedCellWidth(), oncoprint.getCellHeight(track_id)); + this.active_rule_set_rules[rule_set.getRuleSetId()][track_id] = rule_set.apply(bound_svg, data, id_accessor, oncoprint.getFullCellWidth(), oncoprint.getCellHeight(track_id)); self.track_cell_selections[track_id] = bound_svg; }; OncoprintSVGRenderer.prototype.drawCells = function(track_ids) { From 98a2bb8f80eea8d22090f9ecd46f300413e60811 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Thu, 16 Jul 2015 16:48:08 -0400 Subject: [PATCH 132/343] abbreviate long track names and add tooltips --- .../oncoprintjs/src/js/OncoprintRenderer.js | 7 ++----- .../oncoprintjs/src/js/OncoprintSVGRenderer.js | 17 +++++++++++++++-- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/packages/oncoprintjs/src/js/OncoprintRenderer.js b/packages/oncoprintjs/src/js/OncoprintRenderer.js index a2d6069147e..842bc6c44d0 100644 --- a/packages/oncoprintjs/src/js/OncoprintRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintRenderer.js @@ -38,6 +38,7 @@ window.OncoprintRenderer = (function() { this.oncoprint = oncoprint; this.config = config; this.upper_padding = utils.ifndef(config.upper_padding, 10); + this.max_label_length = utils.ifndef(config.max_label_length, 20); }; OncoprintRenderer.prototype.getTrackGroupSeparation = function() { // TODO: configurable @@ -120,11 +121,7 @@ window.OncoprintRenderer = (function() { }; OncoprintRenderer.prototype.getLabelAreaWidth = function() { var label_font = this.getLabelFont(); - var labels = _.map(this.oncoprint.getTracks(), this.oncoprint.getTrackLabel); - var label_widths = _.map(labels, function(label) { - return utils.textWidth(label, label_font); - }); - var max_label_width = Math.max(_.max(label_widths), 0); + var max_label_width = utils.textWidth((Math.pow(10,this.max_label_length)-1).toString(), label_font); var max_percent_altered_width = utils.textWidth('100%', label_font); var buffer_width = 20; return max_label_width + buffer_width + max_percent_altered_width ; diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index c3403645738..9d3ccfc4d6b 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -207,19 +207,32 @@ _.each(track_ids, function(track_id) { var label_top = label_tops[track_id]; var track_label_class = self.getTrackLabelCSSClass(track_id); + var label_text = self.oncoprint.getTrackLabel(track_id); + var disp_label_text = label_text; + if (label_text.length > self.max_label_length) { + disp_label_text = label_text.substring(0,self.max_label_length-3)+'...'; + } + _.each(div.selectAll(self.getTrackLabelCSSSelector(track_id)), function(node) { + $(node).qtip('destroy'); + }); div.selectAll(self.getTrackLabelCSSSelector(track_id)).remove(); - div.append('span') + var span = div.append('span') .style('position','absolute') .classed(self.getTrackLabelCSSClass(track_id), true) .classed('noselect', true) .style('font', self.getLabelFont()) .style('font-weight', 'bold') - .text(self.oncoprint.getTrackLabel(track_id)) + .text(disp_label_text) .style('top', label_top+'px') .style('cursor', 'move') .on("mousedown", function() { self.dragLabel(track_id); }); + $(span.node()).qtip( {content: {text: (label_text.length > this.max_label_length ? disp_label_text+'
hold to drag' : 'hold to drag') }, + position: {my:'middle right', at:'middle left', viewport: $(window)}, + style: { classes: 'qtip-light qtip-rounded qtip-shadow qtip-lightyellow'}, + show: {event: "mouseover"} + }); var rule_set = self.getRuleSet(track_id); if (rule_set && rule_set.alteredData) { From f7aa9f9c6edc2f76b8fe82e5d17b1359cea0fc73 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Fri, 17 Jul 2015 18:41:07 -0400 Subject: [PATCH 133/343] beginning of hiding unaltered cases --- packages/oncoprintjs/src/js/oncoprint.js | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 7b0e0e23d50..4334b278db3 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -66,6 +66,8 @@ window.Oncoprint = (function() { self.id_order = []; self.inverted_id_order = {}; + self.visible_id_order = []; + self.hidden_ids = {}; self.track_groups = [[],[]]; self.track_group_sort_order = [0,1]; self.sort_direction = {}; @@ -122,17 +124,32 @@ window.Oncoprint = (function() { // Id Order self.getIdOrder = function() { - return self.id_order.slice(); + return self.id_order; }; self.getInvertedIdOrder = function() { return self.inverted_id_order; }; + self.getVisibleIdOrder = function() { + return self.visible_id_order; + }; self.setIdOrder = function(id_order) { self.id_order = id_order.slice(); self.inverted_id_order = utils.invert_array(self.id_order); $(self).trigger(events.SET_ID_ORDER); }; + // Hide Ids + self.hideIds = function(ids, clear_existing) { + if (clear_existing) { + self.hidden_ids = {}; + } + _.each(ids, function(id) { + self.hidden_ids[id] = true; + }); + }; + self.showIds = function(ids) { + }; + // Sorting self.setTrackSortComparator = function(track_id, cmp) { self.tracks[track_id].config.sort_cmp = cmp; From af863cd634773eb0867ed8d65e9c6b6ab0a851d3 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Mon, 20 Jul 2015 15:23:14 -0400 Subject: [PATCH 134/343] more functionality towards hiding/showing ids --- packages/oncoprintjs/src/js/events.js | 3 +- packages/oncoprintjs/src/js/oncoprint.js | 39 ++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/packages/oncoprintjs/src/js/events.js b/packages/oncoprintjs/src/js/events.js index c370aa41607..455c8644f59 100644 --- a/packages/oncoprintjs/src/js/events.js +++ b/packages/oncoprintjs/src/js/events.js @@ -47,5 +47,6 @@ window.oncoprint_events = { FINISHED_RENDERING: 'finished_rendering.oncoprint', FINISHED_POSITIONING: 'finished_positioning.renderer.oncoprint', SET_ZOOM: 'set_zoom.oncoprint', - SET_SORT_DIRECTION: 'set_sort_direction.oncoprint' + SET_SORT_DIRECTION: 'set_sort_direction.oncoprint', + SET_VISIBLE_IDS: 'set_visible_ids.oncoprint' }; diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 4334b278db3..49c7be78864 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -123,6 +123,15 @@ window.Oncoprint = (function() { }; // Id Order + self.getFilteredIdOrder = function(data_filter_fn, track_ids) { + var tracks = track_ids || self.getTracks(); + return _.filter(self.id_order, function(id) { + var d = _.map(tracks, function(track_id) { + return self.getTrackDatum(track_id, id); + }); + return data_filter_fn(d); + }); + }; self.getIdOrder = function() { return self.id_order; }; @@ -132,12 +141,18 @@ window.Oncoprint = (function() { self.getVisibleIdOrder = function() { return self.visible_id_order; }; + var updateVisibleIdOrder = function() { + self.visible_id_order = _.filter(self.id_order, function(id) { + return !self.hidden_ids[id]; + }); + $(self).trigger(events.SET_VISIBLE_IDS); + } self.setIdOrder = function(id_order) { self.id_order = id_order.slice(); self.inverted_id_order = utils.invert_array(self.id_order); + updateVisibleIdOrder(); $(self).trigger(events.SET_ID_ORDER); }; - // Hide Ids self.hideIds = function(ids, clear_existing) { if (clear_existing) { @@ -146,8 +161,17 @@ window.Oncoprint = (function() { _.each(ids, function(id) { self.hidden_ids[id] = true; }); + updateVisibleIdOrder(); }; self.showIds = function(ids) { + if (!ids) { + self.hidden_ids = {}; + } else { + _.each(ids, function(id) { + delete self.hidden_ids[id]; + }); + } + updateVisibleIdOrder(); }; // Sorting @@ -294,7 +318,9 @@ window.Oncoprint = (function() { var id_accessor = self.getTrackDatumIdAccessor(track_id); self.tracks[track_id].data = data; - self.setIdOrder(self.id_order.concat(_.difference(_.map(data, id_accessor), self.id_order))); + var current_id_order = self.getIdOrder(); + var augmented_id_order = current_id_order.concat(_.difference(_.map(data, id_accessor), current_id_order)); + self.setIdOrder(augmented_id_order); self.tracks[track_id].id_data_map = {}; var id_data_map = self.tracks[track_id].id_data_map; @@ -383,6 +409,15 @@ window.Oncoprint = (function() { }, setLegendVisible: function(track_ids, visible) { renderer.setLegendVisible(track_ids, visible); + }, + getFilteredIdOrder: function(data_filter_fn, track_ids) { + return oncoprint.getFilteredIdOrder(data_filter_fn, track_ids); + }, + hideIds: function(ids) { + oncoprint.hideIds(ids); + }, + showIds: function(ids) { + oncoprint.showIds(ids); } }; $(oncoprint).on(events.MOVE_TRACK, function() { From c47e3ab62b994efea5315230b38ae9a04fd96d58 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Mon, 20 Jul 2015 16:29:05 -0400 Subject: [PATCH 135/343] more stuff related to visible ids --- packages/oncoprintjs/src/js/OncoprintRenderer.js | 4 ++-- .../oncoprintjs/src/js/OncoprintSVGRenderer.js | 14 +++++++++----- packages/oncoprintjs/src/js/oncoprint.js | 7 ++++++- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/packages/oncoprintjs/src/js/OncoprintRenderer.js b/packages/oncoprintjs/src/js/OncoprintRenderer.js index 842bc6c44d0..c168d5580bb 100644 --- a/packages/oncoprintjs/src/js/OncoprintRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintRenderer.js @@ -108,10 +108,10 @@ window.OncoprintRenderer = (function() { return this.oncoprint.getTrackHeight(track_id) + 2*this.oncoprint.getTrackPadding(track_id); }; OncoprintRenderer.prototype.getCellX = function(index) { - return index*(this.oncoprint.getZoomedCellWidth()+this.oncoprint.getCellPadding()); + return (typeof index === 'number' ? index*(this.oncoprint.getZoomedCellWidth()+this.oncoprint.getCellPadding()) : -1); }; OncoprintRenderer.prototype.getCellAreaWidth = function() { - return this.oncoprint.getIdOrder().length*(this.oncoprint.getZoomedCellWidth() + this.oncoprint.getCellPadding()); + return this.oncoprint.getVisibleIdOrder().length*(this.oncoprint.getZoomedCellWidth() + this.oncoprint.getCellPadding()); }; OncoprintRenderer.prototype.getCellAreaHeight = function() { var track_tops = this.getTrackTops(); diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index 9d3ccfc4d6b..758dc1fbf84 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -117,7 +117,6 @@ $(oncoprint).on(events.SET_CELL_PADDING, function(e,d) { self.clipAndPositionCells(undefined, undefined, true); - //self.clipCells(true); self.resizeCellDiv(); }); @@ -125,13 +124,16 @@ self.clipAndPositionCells(undefined, undefined, true); self.resizeCells(); self.resizeCellDiv(); - //self.clipCells(true); }); $(oncoprint).on(events.SET_ID_ORDER, function() { self.clipAndPositionCells(undefined, undefined, true); - //self.clipCells(true); }); + + $(oncoprint).on(events.SET_VISIBLE_IDS, function() { + self.clipAndPositionCells(undefined, undefined, true); + self.resizeCellDiv(); + }) })(); } utils.extends(OncoprintSVGRenderer, OncoprintRenderer); @@ -332,7 +334,9 @@ var visible_interval = this.getVisibleInterval(); var interval_width = 4*(visible_interval[1] - visible_interval[0]); var interval_number = Math.floor(visible_interval[0] / interval_width); - visible_interval = _.map([-interval_width, 2*interval_width], function(x) { return x + interval_number*interval_width; }); + visible_interval = _.map([-interval_width, 2*interval_width], function(x) { + return Math.max(x + interval_number*interval_width, 0); + }); var self = this; _.each(track_ids, function(track_id) { var y; @@ -340,7 +344,7 @@ y = self.getTrackCellTops()[track_id]; } var id_key = self.oncoprint.getTrackDatumIdKey(track_id); - var id_order = self.oncoprint.getInvertedIdOrder(); + var id_order = self.oncoprint.getVisibleInvertedIdOrder(); if ((interval_number !== self.prev_interval_number) || force) { if (self.track_cell_selections.hasOwnProperty(track_id)) { self.track_cell_selections[track_id].each(function(d,i) { diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 49c7be78864..70abe278987 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -67,6 +67,7 @@ window.Oncoprint = (function() { self.id_order = []; self.inverted_id_order = {}; self.visible_id_order = []; + self.visible_inverted_id_order = {}; self.hidden_ids = {}; self.track_groups = [[],[]]; self.track_group_sort_order = [0,1]; @@ -141,12 +142,16 @@ window.Oncoprint = (function() { self.getVisibleIdOrder = function() { return self.visible_id_order; }; + self.getVisibleInvertedIdOrder = function() { + return self.visible_inverted_id_order; + }; var updateVisibleIdOrder = function() { self.visible_id_order = _.filter(self.id_order, function(id) { return !self.hidden_ids[id]; }); + self.visible_inverted_id_order = utils.invert_array(self.visible_id_order); $(self).trigger(events.SET_VISIBLE_IDS); - } + }; self.setIdOrder = function(id_order) { self.id_order = id_order.slice(); self.inverted_id_order = utils.invert_array(self.id_order); From 488948cd53c8245448819907ca6f798d5dfb15ac Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Mon, 20 Jul 2015 16:58:36 -0400 Subject: [PATCH 136/343] fixing track initialization --- packages/oncoprintjs/src/js/oncoprint.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 70abe278987..d873481c240 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -250,7 +250,10 @@ window.Oncoprint = (function() { self.addTrack = function(config, group) { group = utils.ifndef(group, 1); var track_id = getTrackId(); - self.tracks[track_id] ={id: track_id, data: [], config: $.extend({}, defaultTrackConfig, config)}; + self.tracks[track_id] ={id: track_id, + data: [], + config: $.extend({}, defaultTrackConfig, config), + id_data_map: {}}; self.track_groups[group].push(track_id); self.sort_direction[track_id] = 1; From 2adaf726b94dedafc599658b88b5453a170b05c7 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Mon, 20 Jul 2015 17:13:38 -0400 Subject: [PATCH 137/343] expose setIdOrder --- packages/oncoprintjs/src/js/oncoprint.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index d873481c240..b7772003bf1 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -403,6 +403,9 @@ window.Oncoprint = (function() { sortById: function() { oncoprint.sortById(); }, + setIdOrder: function(id_order) { + oncoprint.setIdOrder(id_order); + }, toggleTrackSortDirection: function(track_id) { oncoprint.toggleTrackSortDirection(track_id); }, From 98b3ee5315a58dfb5969167790fdbab7320fae19 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Mon, 20 Jul 2015 17:22:34 -0400 Subject: [PATCH 138/343] stateful sort config --- .../src/js/OncoprintSVGRenderer.js | 2 +- packages/oncoprintjs/src/js/oncoprint.js | 25 +++++++++++++------ packages/oncoprintjs/test/js/test_page.js | 3 --- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index 758dc1fbf84..19b1bd17d5e 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -90,7 +90,7 @@ $(oncoprint).on(events.MOVE_TRACK, function(evt, data) { self.clipAndPositionCells(data.moved_tracks, 'top', true); self.renderTrackLabels(); - oncoprint.sortByTrack(); + oncoprint.sort(); }); $(oncoprint).on(events.ADD_TRACK, function(e,d) { diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index b7772003bf1..ec114968b46 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -73,6 +73,7 @@ window.Oncoprint = (function() { self.track_group_sort_order = [0,1]; self.sort_direction = {}; self.tracks = {}; + self.sort_config = {type: 'track'}; self.zoom = 1; self.cell_padding_on = true; @@ -205,14 +206,17 @@ window.Oncoprint = (function() { }); return ret; }; - self.sortById = function(desc) { + self.setSortConfig = function(config) { + self.sort_config = config; + }; + var sortById = function(desc) { var ret = _.sortBy(self.getIdOrder(), _.identity); if (desc) { ret.reverse(); } self.setIdOrder(ret); }; - self.sortByTrack = function() { + var sortByTrack = function() { var track_id_list = self.getTrackSortOrder(); var cmp_list = _.map(track_id_list, function(track_id) { return self.getTrackSortComparator(track_id); @@ -244,7 +248,14 @@ window.Oncoprint = (function() { }; self.setIdOrder(utils.stableSort(self.getIdOrder(), lexicographically_ordered_cmp)); }; - + self.sort = function() { + var config = self.sort_config; + if (config.type === 'track') { + sortByTrack(); + } else if (config.type === 'id') { + sortById(config.desc); + } + }; // Track Creation/Destruction self.addTrack = function(config, group) { @@ -397,11 +408,11 @@ window.Oncoprint = (function() { setTrackGroupSortOrder: function(order) { oncoprint.setTrackGroupSortOrder(order); }, - sortByTrack: function() { - oncoprint.sortByTrack(); + sort: function() { + oncoprint.sort(); }, - sortById: function() { - oncoprint.sortById(); + setSortConfig: function(config) { + oncoprint.setSortConfig(config); }, setIdOrder: function(id_order) { oncoprint.setIdOrder(id_order); diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index f1a45858a0f..d96e49908db 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -17,9 +17,6 @@ var z = 1; $('#reduce_cell_width').click(function() { z *= 0.9; onc.setZoom(z); -}); -$(onc).on("move_track.oncoprint", function() { - onc.sortByTrack(); }); /*$('#to_svg_btn').click(function() { onc.toSVG(d3.select('#svg_container')); From ba20654764627792812cd195652e34b3bf9afa1d Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Mon, 20 Jul 2015 18:32:01 -0400 Subject: [PATCH 139/343] fix label qtip on overflow --- packages/oncoprintjs/src/js/OncoprintSVGRenderer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index 19b1bd17d5e..0b5a8c342b6 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -230,7 +230,7 @@ .on("mousedown", function() { self.dragLabel(track_id); }); - $(span.node()).qtip( {content: {text: (label_text.length > this.max_label_length ? disp_label_text+'
hold to drag' : 'hold to drag') }, + $(span.node()).qtip( {content: {text: (label_text.length > self.max_label_length ? label_text+'
hold to drag' : 'hold to drag') }, position: {my:'middle right', at:'middle left', viewport: $(window)}, style: { classes: 'qtip-light qtip-rounded qtip-shadow qtip-lightyellow'}, show: {event: "mouseover"} From a733515de8402675cf01ba42b029a17df4441616 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Mon, 20 Jul 2015 19:52:38 -0400 Subject: [PATCH 140/343] remove track, toggle track sort direction buttons --- packages/oncoprintjs/src/css/oncoprint.css | 3 - .../src/js/OncoprintSVGRenderer.js | 91 +++++++++++++++++-- packages/oncoprintjs/src/js/oncoprint.js | 15 ++- packages/oncoprintjs/test/js/test_page.js | 7 +- 4 files changed, 101 insertions(+), 15 deletions(-) diff --git a/packages/oncoprintjs/src/css/oncoprint.css b/packages/oncoprintjs/src/css/oncoprint.css index e8ed3b43a1c..f546e6e11f2 100644 --- a/packages/oncoprintjs/src/css/oncoprint.css +++ b/packages/oncoprintjs/src/css/oncoprint.css @@ -53,9 +53,6 @@ display: inline-block; } -.oncoprint-track-label { - alignment-baseline: hanging; -} .oncoprint-track-label-draggable { cursor: move; } diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index 0b5a8c342b6..336ee6cb674 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -84,12 +84,23 @@ })(); (function reactToOncoprint() { $(oncoprint).on(events.REMOVE_TRACK, function(evt, data) { - delete self.rule_sets[data.track_id]; - throw "not implemented"; + var track_id = data.track_id; + delete self.rule_sets[track_id]; + delete self.track_cell_selections[track_id]; + self.removeTrackCells(track_id); + self.removeTrackLabels(track_id); + self.removeTrackButtons(track_id); + + self.renderTrackLabels(); + self.renderTrackButtons(); + self.resizeLabelDiv(); + self.resizeCellDiv(); + oncoprint.sort(); }); $(oncoprint).on(events.MOVE_TRACK, function(evt, data) { self.clipAndPositionCells(data.moved_tracks, 'top', true); self.renderTrackLabels(); + self.renderTrackButtons(); oncoprint.sort(); }); @@ -98,6 +109,7 @@ self.drawCells(d.track_id); self.clipAndPositionCells(undefined, 'top', true); self.renderTrackLabels(); + self.renderTrackButtons(); self.resizeLabelDiv(); //self.clipCells(true, d.track_id); //this.cell_div.style('display','inherit'); @@ -133,7 +145,7 @@ $(oncoprint).on(events.SET_VISIBLE_IDS, function() { self.clipAndPositionCells(undefined, undefined, true); self.resizeCellDiv(); - }) + }); })(); } utils.extends(OncoprintSVGRenderer, OncoprintRenderer); @@ -192,9 +204,15 @@ }; // Labels - OncoprintSVGRenderer.prototype.getTrackLabelCSSClass = function(track_id) { - return OncoprintRenderer.prototype.getTrackLabelCSSClass.call(this, track_id)+' oncoprint-track-label-draggable'; - }; + OncoprintSVGRenderer.prototype.removeTrackLabels =function(track_ids) { + track_ids = typeof track_ids === "undefined" ? this.oncoprint.getTracks() : track_ids; + track_ids = [].concat(track_ids); + var div = this.label_div; + var self = this; + _.each(track_ids, function(track_id) { + div.selectAll(self.getTrackLabelCSSSelector(track_id)).remove(); + }); + } OncoprintSVGRenderer.prototype.renderTrackLabels = function(track_ids, y) { var div = this.label_div; if (typeof y !== "undefined") { @@ -221,12 +239,13 @@ var span = div.append('span') .style('position','absolute') .classed(self.getTrackLabelCSSClass(track_id), true) + .classed('oncoprint-track-label-draggable', true) + .classed('oncoprint-track-label-main', true) .classed('noselect', true) .style('font', self.getLabelFont()) .style('font-weight', 'bold') .text(disp_label_text) .style('top', label_top+'px') - .style('cursor', 'move') .on("mousedown", function() { self.dragLabel(track_id); }); @@ -254,10 +273,68 @@ } }; + // Buttons + OncoprintSVGRenderer.prototype.getTrackButtonCSSClass = function(track_id) { + return 'oncoprint-track-button'+track_id; + }; + OncoprintSVGRenderer.prototype.removeTrackButtons = function(track_ids) { + var div = this.label_div; + track_ids = typeof track_ids === "undefined" ? this.oncoprint.getTracks() : track_ids; + track_ids = [].concat(track_ids); + var self = this; + _.each(track_ids, function(track_id) { + div.selectAll('.'+self.getTrackButtonCSSClass(track_id)).remove(); + }); + }; + OncoprintSVGRenderer.prototype.renderTrackButtons = function(track_ids) { + var div = this.label_div; + track_ids = typeof track_ids === "undefined" ? this.oncoprint.getTracks() : track_ids; + track_ids = [].concat(track_ids); + var label_tops = this.getTrackLabelTops(); + var self = this; + _.each(track_ids, function(track_id) { + var button_class = self.getTrackButtonCSSClass(track_id); + div.selectAll('.'+button_class).remove(); + var left = $(div.node()).find('.'+self.getTrackLabelCSSClass(track_id)+',.oncoprint-track-label-main').width(); + if (self.oncoprint.isTrackSortDirectionChangable(track_id)) { + var direction = -1; + (function() { + var new_btn = div.append('button') + .classed(button_class, true).on('click', function() { + self.oncoprint.toggleTrackSortDirection(track_id); + direction = -direction; + if (direction > 0) { + new_btn.text('>'); + } else { + new_btn.text('<'); + } + }) + .style('position', 'absolute').style('left', left+'px').style('top', label_tops[track_id]+'px'); + new_btn.text('<'); + })(); + left += 25; + } + if (self.oncoprint.isTrackRemovable(track_id)) { + (function() { + var new_btn = div.append('button') + .classed(button_class, true).on('click', function() { + self.oncoprint.removeTrack(track_id); + }) + .style('position', 'absolute').style('left', left+'px').style('top', label_tops[track_id]+'px'); + new_btn.text('X'); + left += 25; + })(); + } + }); + }; + // Cells OncoprintSVGRenderer.prototype.resizeCells = function(new_width) { this.cell_div.selectAll('svg.'+this.getCellCSSClass()).style('width', this.oncoprint.getZoomedCellWidth()+'px'); }; + OncoprintSVGRenderer.prototype.removeTrackCells = function(track_id) { + this.cell_div.selectAll('svg.'+this.getTrackCellCSSClass(track_id)).remove(); + }; OncoprintSVGRenderer.prototype.drawTrackCells = function(track_id, fragment) { var oncoprint = this.oncoprint; var data = oncoprint.getTrackData(track_id); diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index ec114968b46..25f295c2d7d 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -52,7 +52,9 @@ window.Oncoprint = (function() { sort_cmp: undefined, tooltip: function(d) { return d['sample']; - } + }, + removable: false, + sort_direction_changable: false }; @@ -190,7 +192,7 @@ window.Oncoprint = (function() { self.toggleTrackSortDirection = function(track_id) { var dir = self.sort_direction[track_id]; self.sort_direction[track_id] = -dir; - $(self).trigger(events.SET_SORT_DIRECTION); + self.sort(); }; self.setTrackGroupSortOrder = function(order) { self.track_group_sort_order = order.slice(); @@ -274,6 +276,7 @@ window.Oncoprint = (function() { self.removeTrack = function(track_id) { var track = self.tracks[track_id]; delete self.tracks[track_id]; + delete self.sort_direction[track_id]; var track_group = self.getContainingTrackGroup(track_id, true); if (!track_group) { @@ -365,6 +368,14 @@ window.Oncoprint = (function() { self.getTrackDatumIdKey = function(track_id) { return self.tracks[track_id].config.datum_id_key; }; + + // Track info + self.isTrackRemovable = function(track_id) { + return self.tracks[track_id].config.removable; + }; + self.isTrackSortDirectionChangable = function(track_id) { + return self.tracks[track_id].config.sort_direction_changable; + }; } return { diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index d96e49908db..cb724bfa87a 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -55,7 +55,7 @@ gender_data_promise.then(function(data) { gender_data = data.data; }); $.when(gender_data_promise).then(function() { - gender_track_id = onc.addTrack({label: 'Gender'}); + gender_track_id = onc.addTrack({label: 'Gender', removable:true}); tracks_to_load -= 1; onc.setRuleSet(gender_track_id, Oncoprint.CATEGORICAL_COLOR, { color: {}, @@ -84,8 +84,9 @@ $.when(mutation_data_promise).then(function() { mutation_track_id = onc.addTrack({label: 'Mutations', tooltip: function(d) { return '

'+d.sample+': '+d.attr_val+'

'; - } - }, 0); + }, + removable: true, + sort_direction_changable: true}, 0); tracks_to_load -= 1; onc.setRuleSet(mutation_track_id, Oncoprint.GRADIENT_COLOR, { data_key: 'attr_val', From c474d85b049a3130c212fb9213c47d28ced4b7a2 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Tue, 21 Jul 2015 19:30:55 -0400 Subject: [PATCH 141/343] additions towards patient-sample switching and svg downloading --- packages/oncoprintjs/src/js/OncoprintSVGRenderer.js | 3 +++ packages/oncoprintjs/src/js/oncoprint.js | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index 336ee6cb674..d3b4db01f6b 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -533,5 +533,8 @@ }); })(track_id); }; + OncoprintSVGRenderer.prototype.toSVG = function() { + var svg = d3.select(document.createElement('svg')); + }; return OncoprintSVGRenderer; })(); \ No newline at end of file diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 25f295c2d7d..03ad41ee7ed 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -368,6 +368,9 @@ window.Oncoprint = (function() { self.getTrackDatumIdKey = function(track_id) { return self.tracks[track_id].config.datum_id_key; }; + self.setTrackDatumIdKey = function(track_id, key) { + self.tracks[track_id].config.datum_id_key = key; + }; // Track info self.isTrackRemovable = function(track_id) { @@ -401,6 +404,9 @@ window.Oncoprint = (function() { moveTrack: function(track_id, position) { oncoprint.moveTrack(track_id, position); }, + setTrackDatumIdKey: function(track_id, key) { + oncoprint.setTrackDatumIdKey(track_id, key); + }, setTrackData: function(track_id, data) { oncoprint.setTrackData(track_id, data); }, @@ -459,6 +465,9 @@ window.Oncoprint = (function() { $(renderer).on(events.FINISHED_RENDERING, function() { $(ret).trigger(events.FINISHED_RENDERING); }); + $(oncoprint).on(events.REMOVE_TRACK, function(evt, data) { + $(ret).trigger(events.REMOVE_TRACK, {track_id: data.track_id}); + }); return ret; } }; From ed5f124e9c0c05ff3dceb2c80ca994b55318fe6a Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Tue, 21 Jul 2015 20:00:28 -0400 Subject: [PATCH 142/343] adding hover events --- .../oncoprintjs/src/js/OncoprintSVGRenderer.js | 16 +++++++++++----- packages/oncoprintjs/src/js/oncoprint.js | 6 ++++++ 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index d3b4db01f6b..b47e1fb98fc 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -59,15 +59,16 @@ d3.select(container_selector_string).classed('noselect', true).selectAll('*').remove(); d3.select(container_selector_string).append('br'); + var content_area = d3.select(container_selector_string).append('div').classed('oncoprint-content-area', true); (function initLabelContainer() { - self.label_container = d3.select(container_selector_string).append('div').classed(LABEL_AREA_CONTAINER_CLASS, true).style('position', 'relative'); + self.label_container = content_area.append('div').classed(LABEL_AREA_CONTAINER_CLASS, true).style('position', 'relative'); self.label_div = self.label_container.append('div').style('position', 'relative').style('overflow', 'hidden'); self.label_drag_div = self.label_container.append('div').style('position', 'absolute').style('overflow', 'hidden') .style('top', '0px').style('left','0px') .style('display','none'); })(); (function initCellContainer() { - self.cell_container = d3.select(container_selector_string).append('div').classed(CELL_AREA_CONTAINER_CLASS, true); + self.cell_container = content_area.append('div').classed(CELL_AREA_CONTAINER_CLASS, true); //self.cell_container.style('display', 'none'); self.cell_container_node = self.cell_container.node(); self.cell_div = self.cell_container.append('div').classed(CELL_AREA_CLASS, true); @@ -77,6 +78,11 @@ // TODO: magic number self.cell_div.style('max-width', '1000px'); })(); + $(content_area.node()).hover(function() { + $(self.label_div.node()).find('.'+self.getTrackButtonCSSClass()).stop().fadeTo(80,1); + }, function() { + $(self.label_div.node()).find('.'+self.getTrackButtonCSSClass()).stop().fadeOut(500); + }); (function initLegend() { if (config.legend) { self.legend_table = d3.select(container_selector_string).append('table').style('border-collapse', 'collapse'); @@ -275,7 +281,7 @@ // Buttons OncoprintSVGRenderer.prototype.getTrackButtonCSSClass = function(track_id) { - return 'oncoprint-track-button'+track_id; + return 'oncoprint-track-button'+utils.ifndef(track_id, ""); }; OncoprintSVGRenderer.prototype.removeTrackButtons = function(track_ids) { var div = this.label_div; @@ -300,7 +306,7 @@ var direction = -1; (function() { var new_btn = div.append('button') - .classed(button_class, true).on('click', function() { + .classed(button_class, true).classed(self.getTrackButtonCSSClass(), true).on('click', function() { self.oncoprint.toggleTrackSortDirection(track_id); direction = -direction; if (direction > 0) { @@ -317,7 +323,7 @@ if (self.oncoprint.isTrackRemovable(track_id)) { (function() { var new_btn = div.append('button') - .classed(button_class, true).on('click', function() { + .classed(button_class, true).classed(self.getTrackButtonCSSClass(), true).on('click', function() { self.oncoprint.removeTrack(track_id); }) .style('position', 'absolute').style('left', left+'px').style('top', label_tops[track_id]+'px'); diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 03ad41ee7ed..b90c322130e 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -468,6 +468,12 @@ window.Oncoprint = (function() { $(oncoprint).on(events.REMOVE_TRACK, function(evt, data) { $(ret).trigger(events.REMOVE_TRACK, {track_id: data.track_id}); }); + $(renderer).on(events.CONTENT_AREA_MOUSEENTER, function(evt, data) { + $(ret).trigger(events.CONTENT_AREA_MOUSEENTER); + }); + $(renderer).on(events.CONTENT_AREA_MOUSELEAVE, function(evt, data) { + $(ret).trigger(events.CONTENT_AREA_MOUSELEAVE); + }); return ret; } }; From e4db07391acfa13d655772b0004a0f15cca3f9a9 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Tue, 21 Jul 2015 20:26:16 -0400 Subject: [PATCH 143/343] trying to get rid of aliasing --- packages/oncoprintjs/src/js/oncoprint.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index b90c322130e..e26dce6c67a 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -87,7 +87,7 @@ window.Oncoprint = (function() { $(self).trigger(events.SET_CELL_PADDING); }; self.getCellPadding = function() { - return self.config.cell_padding*self.getZoomMultiplier()*(+self.cell_padding_on); + return Math.round(self.config.cell_padding*self.getZoomMultiplier())*(+self.cell_padding_on); }; // Zoom @@ -108,7 +108,7 @@ window.Oncoprint = (function() { return self.true_cell_width; }; self.getZoomedCellWidth = function() { - return self.true_cell_width*self.getZoomMultiplier(); + return Math.round(self.true_cell_width*self.getZoomMultiplier()); }; // Cell Height From c7be4d2bd157efe044f831fb56c2e1ae00d1012d Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Wed, 22 Jul 2015 13:59:48 -0400 Subject: [PATCH 144/343] get rid of antialiasing effort, and style track buttons --- .../src/js/OncoprintSVGRenderer.js | 57 ++++++++++++------- packages/oncoprintjs/src/js/oncoprint.js | 19 ++++--- 2 files changed, 50 insertions(+), 26 deletions(-) diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index b47e1fb98fc..e8a3082f4ed 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -298,37 +298,56 @@ track_ids = [].concat(track_ids); var label_tops = this.getTrackLabelTops(); var self = this; + var label_area_width = this.getLabelAreaWidth(); _.each(track_ids, function(track_id) { var button_class = self.getTrackButtonCSSClass(track_id); div.selectAll('.'+button_class).remove(); - var left = $(div.node()).find('.'+self.getTrackLabelCSSClass(track_id)+',.oncoprint-track-label-main').width(); - if (self.oncoprint.isTrackSortDirectionChangable(track_id)) { - var direction = -1; + var left = label_area_width - 15; + if (self.oncoprint.isTrackRemovable(track_id)) { (function() { - var new_btn = div.append('button') + var new_btn = div.append('span').classed('noselect', true).style('font-size','12px').style('color', '#87CEFA').style('cursor', 'pointer') .classed(button_class, true).classed(self.getTrackButtonCSSClass(), true).on('click', function() { - self.oncoprint.toggleTrackSortDirection(track_id); - direction = -direction; - if (direction > 0) { - new_btn.text('>'); - } else { - new_btn.text('<'); - } + self.oncoprint.removeTrack(track_id); }) .style('position', 'absolute').style('left', left+'px').style('top', label_tops[track_id]+'px'); - new_btn.text('<'); + new_btn.text('X'); + $(new_btn.node()).hover(function() { + new_btn.style('font-size', '15px').style('color', '#0000FF'); + }, function() { + new_btn.style('font-size','12px').style('color', '#87CEFA'); + }).qtip({ + content: {text: 'Click to remove'}, + position: {my:'bottom middle', at:'top middle', viewport: $(window)}, + style: { classes: 'qtip-light qtip-rounded qtip-shadow qtip-lightyellow' }, + show: {event: "mouseover"}, + hide: {fixed: true, delay: 100, event: "mouseout"} + }); })(); - left += 25; + left -= 35; } - if (self.oncoprint.isTrackRemovable(track_id)) { + if (self.oncoprint.isTrackSortDirectionChangable(track_id)) { (function() { - var new_btn = div.append('button') - .classed(button_class, true).classed(self.getTrackButtonCSSClass(), true).on('click', function() { - self.oncoprint.removeTrack(track_id); + var imgs = ['images/decreaseSort.svg', 'images/increaseSort.svg', 'images/nonSort.svg']; + var descs = ['Sort in descending order', 'Don\'t sort on this track', 'Sort in ascending order']; + var sort_direction = [1, -1, 0]; + var current_sort_setting = sort_direction.indexOf(self.oncoprint.getTrackSortDirection(track_id)); + var new_btn = div.append('img'); + new_btn.attr('src', imgs[current_sort_setting]).style('cursor','pointer'); + $(new_btn.node()).qtip({ + content: {text: function() { + return descs[current_sort_setting]; + }}, + position: {my:'bottom middle', at:'top middle', viewport: $(window)}, + style: { classes: 'qtip-light qtip-rounded qtip-shadow qtip-lightyellow' }, + show: {event: "mouseover"}, + hide: {fixed: true, delay: 100, event: "mouseout"} + }); + new_btn.classed(button_class, true).classed(self.getTrackButtonCSSClass(), true).on('click', function() { + current_sort_setting = (current_sort_setting + 1) % 3; + self.oncoprint.setTrackSortDirection(track_id, sort_direction[current_sort_setting]);//toggleTrackSortDirection(track_id); + new_btn.attr('src', imgs[current_sort_setting]); }) .style('position', 'absolute').style('left', left+'px').style('top', label_tops[track_id]+'px'); - new_btn.text('X'); - left += 25; })(); } }); diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index e26dce6c67a..92a2b02f2a9 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -87,7 +87,7 @@ window.Oncoprint = (function() { $(self).trigger(events.SET_CELL_PADDING); }; self.getCellPadding = function() { - return Math.round(self.config.cell_padding*self.getZoomMultiplier())*(+self.cell_padding_on); + return self.config.cell_padding*self.getZoomMultiplier()*(+self.cell_padding_on); }; // Zoom @@ -108,7 +108,7 @@ window.Oncoprint = (function() { return self.true_cell_width; }; self.getZoomedCellWidth = function() { - return Math.round(self.true_cell_width*self.getZoomMultiplier()); + return self.true_cell_width*self.getZoomMultiplier(); }; // Cell Height @@ -189,9 +189,11 @@ window.Oncoprint = (function() { self.getTrackSortComparator = function(track_id) { return self.tracks[track_id].config.sort_cmp; }; - self.toggleTrackSortDirection = function(track_id) { - var dir = self.sort_direction[track_id]; - self.sort_direction[track_id] = -dir; + self.getTrackSortDirection = function(track_id) { + return self.sort_direction[track_id]; + }; + self.setTrackSortDirection = function(track_id, dir) { + self.sort_direction[track_id] = dir; self.sort(); }; self.setTrackGroupSortOrder = function(order) { @@ -434,8 +436,11 @@ window.Oncoprint = (function() { setIdOrder: function(id_order) { oncoprint.setIdOrder(id_order); }, - toggleTrackSortDirection: function(track_id) { - oncoprint.toggleTrackSortDirection(track_id); + getTrackSortDirection: function(track_id) { + return oncoprint.getTrackSortDirection(track_id); + }, + setTrackSortDirection: function(track_id, dir) { + oncoprint.setTrackSortDirection(track_id, dir); }, setZoom: function(z) { oncoprint.setZoom(z); From ece039f3b273b2be4aee6dce97f73bb313435c1b Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Wed, 22 Jul 2015 14:38:16 -0400 Subject: [PATCH 145/343] re-render legend on track remove --- packages/oncoprintjs/src/js/OncoprintSVGRenderer.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index e8a3082f4ed..9ffa247b5f3 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -97,6 +97,7 @@ self.removeTrackLabels(track_id); self.removeTrackButtons(track_id); + self.renderLegend(); self.renderTrackLabels(); self.renderTrackButtons(); self.resizeLabelDiv(); From 71023a6bdf6ee8d3fe8a489bb1a0d7f00bd25098 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Wed, 22 Jul 2015 17:48:43 -0400 Subject: [PATCH 146/343] fix zoom aliasing --- packages/oncoprintjs/src/js/OncoprintSVGRenderer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index 9ffa247b5f3..2af24005b79 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -379,7 +379,7 @@ //var bound_svg = this.cell_div.selectAll('svg.'+track_cell_class).data(data, id_accessor); this.cellRenderTarget().selectAll('svg.'+track_cell_class).remove(); var bound_svg = d3.select(fragment).selectAll('svg.'+track_cell_class).data(data, id_accessor); - bound_svg.enter().append('svg').classed(track_cell_class, true).classed(cell_class, true); + bound_svg.enter().append('svg').classed(track_cell_class, true).classed(cell_class, true).attr('shape-rendering','geometricPrecision'); bound_svg.style('width', oncoprint.getZoomedCellWidth()+'px').style('height', oncoprint.getCellHeight(track_id)+'px'); bound_svg .attr('preserveAspectRatio','none') From 3bc4b1ac29566b5801ca1c5573f38891791d101f Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Wed, 22 Jul 2015 18:28:31 -0400 Subject: [PATCH 147/343] clear data function --- packages/oncoprintjs/src/js/oncoprint.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 92a2b02f2a9..fb7e251f143 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -381,6 +381,14 @@ window.Oncoprint = (function() { self.isTrackSortDirectionChangable = function(track_id) { return self.tracks[track_id].config.sort_direction_changable; }; + + // Clearing + self.clearData = function() { + _.each(self.getTracks(), function(track_id) { + self.setTrackData(track_id, []); + }); + self.setIdOrder([]); + } } return { @@ -462,6 +470,9 @@ window.Oncoprint = (function() { }, showIds: function(ids) { oncoprint.showIds(ids); + }, + clearData: function() { + oncoprint.clearData(); } }; $(oncoprint).on(events.MOVE_TRACK, function() { From 94a459a4cb364a5dc53b3af427a4406f2418db95 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Wed, 22 Jul 2015 18:30:27 -0400 Subject: [PATCH 148/343] clear data function --- packages/oncoprintjs/src/js/OncoprintSVGRenderer.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index 2af24005b79..0922ca44e9a 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -378,6 +378,7 @@ //var bound_svg = this.cell_div.selectAll('svg.'+track_cell_class).data(data, id_accessor); this.cellRenderTarget().selectAll('svg.'+track_cell_class).remove(); + this.cell_div.selectAll('svg.'+track_cell_class).remove(); var bound_svg = d3.select(fragment).selectAll('svg.'+track_cell_class).data(data, id_accessor); bound_svg.enter().append('svg').classed(track_cell_class, true).classed(cell_class, true).attr('shape-rendering','geometricPrecision'); bound_svg.style('width', oncoprint.getZoomedCellWidth()+'px').style('height', oncoprint.getCellHeight(track_id)+'px'); From ecc05e8f75effff764fe8d83feb9577f14d98cd4 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Wed, 22 Jul 2015 19:06:07 -0400 Subject: [PATCH 149/343] make patient data default and add ability to change track tooltip --- packages/oncoprintjs/src/js/oncoprint.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index fb7e251f143..3d6501b11de 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -45,13 +45,13 @@ window.Oncoprint = (function() { var defaultTrackConfig = { label: 'Gene', - datum_id_key: 'sample', + datum_id_key: 'patient', cell_height: 23, track_height: 20, track_padding: 5, sort_cmp: undefined, tooltip: function(d) { - return d['sample']; + return d['patient']; }, removable: false, sort_direction_changable: false @@ -333,6 +333,9 @@ window.Oncoprint = (function() { self.getTrackTooltip = function(track_id) { return self.tracks[track_id].config.tooltip; }; + self.setTrackTooltip = function(track_id, tooltip) { + self.tracks[track_id].config.tooltip = tooltip; + }; // Track Data self.getTrackData = function(track_id) { @@ -473,6 +476,9 @@ window.Oncoprint = (function() { }, clearData: function() { oncoprint.clearData(); + }, + setTrackTooltip: function(track_id, tooltip) { + oncoprint.setTrackTooltip(track_id, tooltip); } }; $(oncoprint).on(events.MOVE_TRACK, function() { From 1dfff3e30d47a0c087c093668e2e9dc29bed1baa Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Thu, 23 Jul 2015 16:04:42 -0400 Subject: [PATCH 150/343] attaching data to g and trying to reuse gs --- .../src/js/OncoprintSVGRenderer.js | 21 +++++++++++++------ packages/oncoprintjs/src/js/RuleSet.js | 21 +++++++++---------- packages/oncoprintjs/test/js/test_page.js | 6 +++--- 3 files changed, 28 insertions(+), 20 deletions(-) diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index 0922ca44e9a..ddaa5c99aa3 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -377,10 +377,11 @@ //var bound_svg = this.cell_div.selectAll('svg.'+track_cell_class).data(data, id_accessor); - this.cellRenderTarget().selectAll('svg.'+track_cell_class).remove(); - this.cell_div.selectAll('svg.'+track_cell_class).remove(); - var bound_svg = d3.select(fragment).selectAll('svg.'+track_cell_class).data(data, id_accessor); + //this.cellRenderTarget().selectAll('svg.'+track_cell_class).remove(); + //this.cell_div.selectAll('svg.'+track_cell_class).remove(); + var bound_svg = d3.select(fragment).selectAll('svg.'+track_cell_class).data(data); bound_svg.enter().append('svg').classed(track_cell_class, true).classed(cell_class, true).attr('shape-rendering','geometricPrecision'); + bound_svg.exit().remove(); bound_svg.style('width', oncoprint.getZoomedCellWidth()+'px').style('height', oncoprint.getCellHeight(track_id)+'px'); bound_svg .attr('preserveAspectRatio','none') @@ -413,18 +414,26 @@ }); }); bound_svg.selectAll('*').remove(); - this.active_rule_set_rules[rule_set.getRuleSetId()][track_id] = rule_set.apply(bound_svg, data, id_accessor, oncoprint.getFullCellWidth(), oncoprint.getCellHeight(track_id)); + this.active_rule_set_rules[rule_set.getRuleSetId()][track_id] = rule_set.apply(bound_svg, oncoprint.getFullCellWidth(), oncoprint.getCellHeight(track_id)); self.track_cell_selections[track_id] = bound_svg; }; OncoprintSVGRenderer.prototype.drawCells = function(track_ids) { - var fragment = document.createDocumentFragment(); + var fragment; + if (this.document_fragment) { + //HACK + fragment = document.createDocumentFragment(); + } else { + fragment = this.cell_div.node(); + } track_ids = typeof track_ids === "undefined" ? this.oncoprint.getTracks() : track_ids; track_ids = [].concat(track_ids); var self = this; _.each(track_ids, function(track_id) { self.drawTrackCells(track_id, fragment); }); - this.cellRenderTarget().node().appendChild(fragment); + if (this.document_fragment) { + this.cellRenderTarget().node().appendChild(fragment); + } setTimeout(function() { $(self).trigger(events.FINISHED_RENDERING); }, 0); diff --git a/packages/oncoprintjs/src/js/RuleSet.js b/packages/oncoprintjs/src/js/RuleSet.js index 5e8bc70193f..95f657f9dbc 100644 --- a/packages/oncoprintjs/src/js/RuleSet.js +++ b/packages/oncoprintjs/src/js/RuleSet.js @@ -78,14 +78,13 @@ window.oncoprint_RuleSet = (function() { var sorted_rules = _.sortBy(rules, function(r) { return r.z_index; }); return sorted_rules; }; - D3SVGRuleSet.prototype.apply = function(g, data, datum_id_accessor, cell_width, cell_height) { + D3SVGRuleSet.prototype.apply = function(g, cell_width, cell_height) { var active_rules = {}; _.each(this.getRules(), function(rule) { - var affected_data = rule.filterData(data); - if (affected_data.length > 0) { + var affected_groups = rule.filter(g); + if (affected_groups[0].length > 0) { active_rules[rule.rule_id] = true; } - var affected_groups = g.data(affected_data, datum_id_accessor); rule.apply(affected_groups, cell_width, cell_height); }); return active_rules; @@ -142,17 +141,17 @@ window.oncoprint_RuleSet = (function() { return cat1.localeCompare(cat2); } }; - self.apply = function(g, data, datum_id_accessor, cell_width, cell_height) { + self.apply = function(g, cell_width, cell_height) { var missing_categories = []; - _.each(data, function(datum) { - var category = params.getCategory(datum); + g.each(function(d,i) { + var category = params.getCategory(d); if (!params.color.hasOwnProperty(category)) { var new_color = d3_colors.pop(); params.color[category] = new_color; addColorRule(new_color, category); } }); - return D3SVGRuleSet.prototype.apply.call(this, g, data, datum_id_accessor, cell_width, cell_height); + return D3SVGRuleSet.prototype.apply.call(this, g, cell_width, cell_height); }; self.getLegendDiv = function(active_rules, cell_width, cell_height) { @@ -385,12 +384,12 @@ window.oncoprint_RuleSet = (function() { elts.style(key, val); }); } + D3SVGRule.prototype.filter = function(g) { + return g.filter(this.condition); + }; D3SVGRule.prototype.filterData = function(data) { return data.filter(this.condition); }; - D3SVGRule.prototype.isActive = function(data) { - return this.filterData(data).length > 0; - }; D3SVGRule.prototype.showInLegend = function() { return !this.exclude_from_legend; }; diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index cb724bfa87a..644158ffe86 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -52,7 +52,7 @@ var tracks_to_load = 8; onc.suppressRendering(); gender_data_promise.then(function(data) { - gender_data = data.data; + gender_data = _.map(data.data, function(d) { d.patient = d.sample; return d; }); }); $.when(gender_data_promise).then(function() { gender_track_id = onc.addTrack({label: 'Gender', removable:true}); @@ -78,7 +78,7 @@ $.when(gender_data_promise).then(function() { }); mutation_data_promise.then(function(data) { - mutation_data = data.data; + mutation_data = _.map(data.data, function(d) { d.patient = d.sample; return d; }); }); $.when(mutation_data_promise).then(function() { mutation_track_id = onc.addTrack({label: 'Mutations', @@ -120,7 +120,7 @@ $.when(mutation_data_promise).then(function() { alteration_data_promise.then(function(data) { - alteration_data = _.map(data, function(x) { if (Math.random() < 0.3) { x.mut_type='MISSENSE'; } return x; }); + alteration_data = _.map(data, function(x) { if (Math.random() < 0.3) { x.mut_type='MISSENSE'; } x.patient = x.sample; return x; }); }); $.when(alteration_data_promise).then(function() { alteration_track_id = onc.addTrack({label: 'TP53'}, 1); From 42dd1b0fcb1488f12b9c2f5dc000bc96672a9906 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Thu, 23 Jul 2015 16:24:41 -0400 Subject: [PATCH 151/343] some speed optimizations --- packages/oncoprintjs/src/js/OncoprintRenderer.js | 14 +++++++++----- .../oncoprintjs/src/js/OncoprintSVGRenderer.js | 10 +++++++--- packages/oncoprintjs/src/js/oncoprint.js | 5 ++++- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/packages/oncoprintjs/src/js/OncoprintRenderer.js b/packages/oncoprintjs/src/js/OncoprintRenderer.js index c168d5580bb..75140d9aff7 100644 --- a/packages/oncoprintjs/src/js/OncoprintRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintRenderer.js @@ -39,6 +39,14 @@ window.OncoprintRenderer = (function() { this.config = config; this.upper_padding = utils.ifndef(config.upper_padding, 10); this.max_label_length = utils.ifndef(config.max_label_length, 20); + + (function computeLabelAreaWidth(self) { + var label_font = self.getLabelFont(); + var max_label_width = utils.textWidth((Math.pow(10,self.max_label_length)-1).toString(), label_font); + var max_percent_altered_width = utils.textWidth('100%', label_font); + var buffer_width = 20; + self.label_area_width = max_label_width + buffer_width + max_percent_altered_width; + })(this); }; OncoprintRenderer.prototype.getTrackGroupSeparation = function() { // TODO: configurable @@ -120,11 +128,7 @@ window.OncoprintRenderer = (function() { return track_tops[last_track] + this.getRenderedTrackHeight(last_track); }; OncoprintRenderer.prototype.getLabelAreaWidth = function() { - var label_font = this.getLabelFont(); - var max_label_width = utils.textWidth((Math.pow(10,this.max_label_length)-1).toString(), label_font); - var max_percent_altered_width = utils.textWidth('100%', label_font); - var buffer_width = 20; - return max_label_width + buffer_width + max_percent_altered_width ; + return this.label_area_width; }; OncoprintRenderer.prototype.getLabelAreaHeight = function() { return this.getCellAreaHeight(); diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index ddaa5c99aa3..ecf7ce8ec9b 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -73,6 +73,7 @@ self.cell_container_node = self.cell_container.node(); self.cell_div = self.cell_container.append('div').classed(CELL_AREA_CLASS, true); self.cell_container_node.addEventListener("scroll", function() { + self.calculateVisibleInterval(); self.clipAndPositionCells(); }); // TODO: magic number @@ -156,11 +157,14 @@ })(); } utils.extends(OncoprintSVGRenderer, OncoprintRenderer); - OncoprintSVGRenderer.prototype.getVisibleInterval = function() { + OncoprintSVGRenderer.prototype.calculateVisibleInterval = function() { var cell_unit = this.oncoprint.getZoomedCellWidth() + this.oncoprint.getCellPadding(); var cell_ctr_rect = this.cell_container_node.getBoundingClientRect(); - var view_interval = [this.cell_container_node.scrollLeft, this.cell_container_node.scrollLeft + cell_ctr_rect.right - cell_ctr_rect.left]; - return view_interval; + this.visible_interval = [this.cell_container_node.scrollLeft, this.cell_container_node.scrollLeft + cell_ctr_rect.right - cell_ctr_rect.left]; + return this.visible_interval; + }; + OncoprintSVGRenderer.prototype.getVisibleInterval = function() { + return (this.visible_interval || this.calculateVisibleInterval()); }; OncoprintSVGRenderer.prototype.cellRenderTarget = function() { return d3.select(this.document_fragment || this.cell_div.node()); diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 3d6501b11de..e5e37246284 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -345,9 +345,12 @@ window.Oncoprint = (function() { var id_accessor = self.getTrackDatumIdAccessor(track_id); self.tracks[track_id].data = data; + + /* var current_id_order = self.getIdOrder(); - var augmented_id_order = current_id_order.concat(_.difference(_.map(data, id_accessor), current_id_order)); + var augmented_id_order = _.uniq(current_id_order.concat(_.map(data, id_accessor))); self.setIdOrder(augmented_id_order); + */ self.tracks[track_id].id_data_map = {}; var id_data_map = self.tracks[track_id].id_data_map; From d4cdee564b2d2ac7596316498f529c3a1ac1d38a Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Thu, 23 Jul 2015 16:38:26 -0400 Subject: [PATCH 152/343] fix cell area height edge case --- packages/oncoprintjs/src/js/OncoprintRenderer.js | 8 ++++++-- packages/oncoprintjs/src/js/oncoprint.js | 13 ++++++++----- packages/oncoprintjs/test/js/test_page.js | 2 +- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/packages/oncoprintjs/src/js/OncoprintRenderer.js b/packages/oncoprintjs/src/js/OncoprintRenderer.js index 75140d9aff7..d8632c454d0 100644 --- a/packages/oncoprintjs/src/js/OncoprintRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintRenderer.js @@ -124,8 +124,12 @@ window.OncoprintRenderer = (function() { OncoprintRenderer.prototype.getCellAreaHeight = function() { var track_tops = this.getTrackTops(); var track_order = this.oncoprint.getTracks(); - var last_track = track_order[track_order.length-1]; - return track_tops[last_track] + this.getRenderedTrackHeight(last_track); + if (track_order.length === 0) { + return 0; + } else { + var last_track = track_order[track_order.length-1]; + return track_tops[last_track] + this.getRenderedTrackHeight(last_track); + } }; OncoprintRenderer.prototype.getLabelAreaWidth = function() { return this.label_area_width; diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index e5e37246284..7d81a8826bb 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -346,12 +346,15 @@ window.Oncoprint = (function() { self.tracks[track_id].data = data; - /* var current_id_order = self.getIdOrder(); - var augmented_id_order = _.uniq(current_id_order.concat(_.map(data, id_accessor))); - self.setIdOrder(augmented_id_order); - */ - + var current_inverted_id_order = self.getInvertedIdOrder(); + _.each(_.map(data, id_accessor), function(id) { + if (!(id in current_inverted_id_order)) { + current_id_order.push(id); + } + }); + self.setIdOrder(current_id_order); + self.tracks[track_id].id_data_map = {}; var id_data_map = self.tracks[track_id].id_data_map; _.each(self.tracks[track_id].data, function(datum) { diff --git a/packages/oncoprintjs/test/js/test_page.js b/packages/oncoprintjs/test/js/test_page.js index 644158ffe86..2a52eedd5f2 100644 --- a/packages/oncoprintjs/test/js/test_page.js +++ b/packages/oncoprintjs/test/js/test_page.js @@ -126,7 +126,7 @@ $.when(alteration_data_promise).then(function() { alteration_track_id = onc.addTrack({label: 'TP53'}, 1); tracks_to_load -= 1; onc.setRuleSet(alteration_track_id, Oncoprint.GENETIC_ALTERATION); - onc.setTrackData(alteration_track_id, alteration_data); + onc.setTrackData(alteration_track_id, alteration_data, true); var second_alt_track = onc.addTrack({Label: 'TP53 duplicate'}, 1); tracks_to_load -= 1; From 97fc63050298f9339ae381c7fd7164c77a3585b1 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Thu, 23 Jul 2015 16:51:50 -0400 Subject: [PATCH 153/343] more speed optimizations --- .../oncoprintjs/src/js/OncoprintRenderer.js | 4 ++++ .../src/js/OncoprintSVGRenderer.js | 23 +++++++++++-------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/packages/oncoprintjs/src/js/OncoprintRenderer.js b/packages/oncoprintjs/src/js/OncoprintRenderer.js index d8632c454d0..1c0d492704a 100644 --- a/packages/oncoprintjs/src/js/OncoprintRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintRenderer.js @@ -118,6 +118,10 @@ window.OncoprintRenderer = (function() { OncoprintRenderer.prototype.getCellX = function(index) { return (typeof index === 'number' ? index*(this.oncoprint.getZoomedCellWidth()+this.oncoprint.getCellPadding()) : -1); }; + OncoprintRenderer.prototype.getCellXArray = function(length) { + var cell_unit = this.oncoprint.getZoomedCellWidth() + this.oncoprint.getCellPadding(); + return _.map(_.range(0,length), function(x) { return x*cell_unit; }); + }; OncoprintRenderer.prototype.getCellAreaWidth = function() { return this.oncoprint.getVisibleIdOrder().length*(this.oncoprint.getZoomedCellWidth() + this.oncoprint.getCellPadding()); }; diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index ecf7ce8ec9b..0d99f5234d6 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -56,6 +56,7 @@ this.cell_div; this.legend_table; this.document_fragment; + this.percent_altered_max_width = utils.textWidth('100%', self.getLabelFont()); d3.select(container_selector_string).classed('noselect', true).selectAll('*').remove(); d3.select(container_selector_string).append('br'); @@ -234,7 +235,7 @@ var label_tops = this.getTrackLabelTops(); var self = this; var label_area_width = this.getLabelAreaWidth(); - var percent_altered_left = label_area_width - utils.textWidth('100%', self.getLabelFont()); + var percent_altered_left = label_area_width - this.percent_altered_max_width; _.each(track_ids, function(track_id) { var label_top = label_tops[track_id]; var track_label_class = self.getTrackLabelCSSClass(track_id); @@ -384,14 +385,14 @@ //this.cellRenderTarget().selectAll('svg.'+track_cell_class).remove(); //this.cell_div.selectAll('svg.'+track_cell_class).remove(); var bound_svg = d3.select(fragment).selectAll('svg.'+track_cell_class).data(data); - bound_svg.enter().append('svg').classed(track_cell_class, true).classed(cell_class, true).attr('shape-rendering','geometricPrecision'); - bound_svg.exit().remove(); - bound_svg.style('width', oncoprint.getZoomedCellWidth()+'px').style('height', oncoprint.getCellHeight(track_id)+'px'); - bound_svg + bound_svg.enter().append('svg').classed(track_cell_class, true).classed(cell_class, true) + .attr('shape-rendering','geometricPrecision') .attr('preserveAspectRatio','none') - .attr('viewBox', '0 0 '+oncoprint.getFullCellWidth()+' '+oncoprint.getCellHeight(track_id)); + .attr('viewBox', '0 0 '+oncoprint.getFullCellWidth()+' '+oncoprint.getCellHeight(track_id)) + .style('width', oncoprint.getZoomedCellWidth()+'px').style('height', oncoprint.getCellHeight(track_id)+'px'); + bound_svg.exit().remove(); - var tooltip = this.oncoprint.getTrackTooltip(track_id); + var tooltip = oncoprint.getTrackTooltip(track_id); bound_svg.each(function(d,i) { var dom_cell = this; var id = id_accessor(d); @@ -455,17 +456,19 @@ return Math.max(x + interval_number*interval_width, 0); }); var self = this; + var track_cell_tops = this.getTrackCellTops(); + var id_order = this.oncoprint.getVisibleInvertedIdOrder(); + var cell_x = this.getCellXArray(Object.keys(id_order).length); _.each(track_ids, function(track_id) { var y; if (!axis || axis === 'top') { - y = self.getTrackCellTops()[track_id]; + y = track_cell_tops[track_id]; } var id_key = self.oncoprint.getTrackDatumIdKey(track_id); - var id_order = self.oncoprint.getVisibleInvertedIdOrder(); if ((interval_number !== self.prev_interval_number) || force) { if (self.track_cell_selections.hasOwnProperty(track_id)) { self.track_cell_selections[track_id].each(function(d,i) { - var new_x = self.getCellX(id_order[d[id_key]]); + var new_x = cell_x[id_order[d[id_key]]]; var disp = this.style.display; var new_disp = (new_x < visible_interval[0] || new_x > visible_interval[1]) ? 'none' : 'inherit'; if (disp !== new_disp) { From 304bcfd6d1015c52488f111e05e2a42314574f2f Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Mon, 27 Jul 2015 13:29:53 -0400 Subject: [PATCH 154/343] force NAs to be sorted to the end in either direction --- packages/oncoprintjs/src/js/RuleSet.js | 8 ++++---- packages/oncoprintjs/src/js/oncoprint.js | 5 ++++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/oncoprintjs/src/js/RuleSet.js b/packages/oncoprintjs/src/js/RuleSet.js index 95f657f9dbc..58b3bcd18fb 100644 --- a/packages/oncoprintjs/src/js/RuleSet.js +++ b/packages/oncoprintjs/src/js/RuleSet.js @@ -28,9 +28,9 @@ window.oncoprint_RuleSet = (function() { return 0; } } else if (f1_isNaN) { - return 1; + return Number.POSITIVE_INFINITY; } else { - return -1; + return Number.NEGATIVE_INFINITY; } }; var D3SVGRuleSet = (function() { @@ -134,9 +134,9 @@ window.oncoprint_RuleSet = (function() { if (cat1 === cat2) { return 0; } else if (cat1 === 'NA') { - return 1; + return Number.POSITIVE_INFINITY; } else if (cat2 === 'NA') { - return -1; + return Number.NEGATIVE_INFINITY; } else { return cat1.localeCompare(cat2); } diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 7d81a8826bb..80733c24145 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -243,7 +243,10 @@ window.Oncoprint = (function() { } else { cmp_result = -1; } - cmp_result *= self.sort_direction[track_id]; + if (isFinite(cmp_result)) { + // reverse direction unless infinite, which is a signal that an NA is involved + cmp_result *= self.sort_direction[track_id]; + } if (cmp_result !== 0) { break; } From 9ecf6f5027bffdb66cc557c96790a0a57b2f6064 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Mon, 27 Jul 2015 13:57:44 -0400 Subject: [PATCH 155/343] reduce number of digits in gradient and bar chart legends --- packages/oncoprintjs/src/js/RuleSet.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/oncoprintjs/src/js/RuleSet.js b/packages/oncoprintjs/src/js/RuleSet.js index 58b3bcd18fb..9cecfc8d0bf 100644 --- a/packages/oncoprintjs/src/js/RuleSet.js +++ b/packages/oncoprintjs/src/js/RuleSet.js @@ -463,7 +463,11 @@ window.oncoprint_RuleSet = (function() { if (!data_range) { return div.node(); } - div.append('h2').text(data_range[0]).classed('oncoprint-legend-label', true); + var display_data_range = _.map(data_range, function(x) { + var num_digit_multiplier = Math.pow(10, utils.ifndef(params.legend_num_decimal_digits,2)); + return Math.round(x * num_digit_multiplier) / num_digit_multiplier; + }); + div.append('h2').text(display_data_range[0]).classed('oncoprint-legend-label', true); var mesh = 50; var svg = div.append('svg').attr('width', mesh+'px').attr('height', cell_height+'px'); for (var i=0; i<=mesh; i++) { @@ -478,7 +482,7 @@ window.oncoprint_RuleSet = (function() { .attr('fill', params.fill) .attr('x', i+'px'); } - div.append('h2').text(data_range[1]).classed('oncoprint-legend-label', true); + div.append('h2').text(display_data_range[1]).classed('oncoprint-legend-label', true); utils.d3SelectChildren(div, '*').style('padding-right', '10px'); return div.node(); }; @@ -552,7 +556,11 @@ window.oncoprint_RuleSet = (function() { if (!data_range) { return div.node(); } - div.append('h2').text(data_range[0]).classed('oncoprint-legend-label', true); + var display_data_range = _.map(data_range, function(x) { + var num_digit_multiplier = Math.pow(10, utils.ifndef(params.legend_num_decimal_digits,2)); + return Math.round(x * num_digit_multiplier) / num_digit_multiplier; + }); + div.append('h2').text(display_data_range[0]).classed('oncoprint-legend-label', true); var mesh = 50; var svg = div.append('svg').attr('width', mesh+'px').attr('height', cell_height+'px'); for (var i=0; i<=mesh; i++) { @@ -565,7 +573,7 @@ window.oncoprint_RuleSet = (function() { .attr('fill', this.attrs.fill(datum)) .attr('x', i+'px'); } - div.append('h2').text(data_range[1]).classed('oncoprint-legend-label', true); + div.append('h2').text(display_data_range[1]).classed('oncoprint-legend-label', true); utils.d3SelectChildren(div, '*').style('padding-right', '10px'); return div.node(); }; From 41d4ceded5eac68458ad8d9b7e179565b36f9ab8 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Mon, 27 Jul 2015 15:14:06 -0400 Subject: [PATCH 156/343] by default dont distinguish mutation order --- packages/oncoprintjs/src/js/RuleSet.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/oncoprintjs/src/js/RuleSet.js b/packages/oncoprintjs/src/js/RuleSet.js index 9cecfc8d0bf..33a3223bf29 100644 --- a/packages/oncoprintjs/src/js/RuleSet.js +++ b/packages/oncoprintjs/src/js/RuleSet.js @@ -214,10 +214,10 @@ window.oncoprint_RuleSet = (function() { } else { params = $.extend({}, params, defaults.genetic_alteration_config); } - if (params && params.dont_distinguish_mutation_order) { - this.sort_cmp = defaults.genetic_alteration_comparator_nondistinct_mutations; - } else { + if (params && params.distinguish_mutation_order) { this.sort_cmp = defaults.genetic_alteration_comparator; + } else { + this.sort_cmp = defaults.genetic_alteration_comparator_nondistinct_mutations; } D3SVGRuleSet.call(this, params); var vocab = ['full-rect', 'middle-rect', 'large-right-arrow', 'small-up-arrow', 'small-down-arrow']; From 65423613a59f530a2647988eabbd52ac6e7fe80c Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Mon, 27 Jul 2015 17:46:30 -0400 Subject: [PATCH 157/343] fix qtip bug --- packages/oncoprintjs/src/js/OncoprintSVGRenderer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index 0d99f5234d6..04bbb13a76e 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -405,10 +405,10 @@ }, position: {my:'left bottom', at:'top middle', viewport: $(window)}, style: { classes: CELL_QTIP_CLASS, border: 'none'}, - show: {event: "mouseover"}, + show: {event: "mouseover qtip-first-show"}, hide: {fixed: true, delay: 100, event: "mouseout"} }); - $(dom_cell).trigger("mouseover"); + $(dom_cell).trigger("qtip-first-show"); }); } $(dom_cell).on("mouseover", function() { From 7becc94b9a009b59a09df08a87b39bee9063b1bc Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Tue, 28 Jul 2015 14:02:58 -0400 Subject: [PATCH 158/343] mouseovers and qtips handled by cell div and mousemove handler --- packages/oncoprintjs/src/css/oncoprint.css | 1 + .../oncoprintjs/src/js/OncoprintRenderer.js | 4 + .../src/js/OncoprintSVGRenderer.js | 96 ++++++++++++++++++- packages/oncoprintjs/src/js/utils.js | 3 + 4 files changed, 102 insertions(+), 2 deletions(-) diff --git a/packages/oncoprintjs/src/css/oncoprint.css b/packages/oncoprintjs/src/css/oncoprint.css index f546e6e11f2..ed82da12e81 100644 --- a/packages/oncoprintjs/src/css/oncoprint.css +++ b/packages/oncoprintjs/src/css/oncoprint.css @@ -11,6 +11,7 @@ .oncoprint-cell-area-ctr { overflow: auto; width: auto; + position:relative; } .oncoprint-cell-area { diff --git a/packages/oncoprintjs/src/js/OncoprintRenderer.js b/packages/oncoprintjs/src/js/OncoprintRenderer.js index 1c0d492704a..5fdeb8ded75 100644 --- a/packages/oncoprintjs/src/js/OncoprintRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintRenderer.js @@ -102,11 +102,15 @@ window.OncoprintRenderer = (function() { return ret; }; OncoprintRenderer.prototype.getTrackCellTops = function() { + return this.track_cell_tops || this.computeTrackCellTops(); + }; + OncoprintRenderer.prototype.computeTrackCellTops = function() { var tops = this.getTrackTops(); var self = this; _.each(tops, function(top, id) { tops[id] = top + self.oncoprint.getTrackPadding(id); }); + this.track_cell_tops = tops; return tops; }; OncoprintRenderer.prototype.getTrackLabelTops = function() { diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index 04bbb13a76e..c4f2f1ddd70 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -46,6 +46,7 @@ OncoprintRenderer.call(this, oncoprint, config); var self = this; this.track_cell_selections = {}; + this.track_cells = {}; this.active_rule_set_rules = {}; this.toolbar_container; this.label_div; @@ -73,10 +74,91 @@ //self.cell_container.style('display', 'none'); self.cell_container_node = self.cell_container.node(); self.cell_div = self.cell_container.append('div').classed(CELL_AREA_CLASS, true); + + $(self.cell_div.node()).qtip({ + content: 'SHARED QTIP', + position: {target: 'event', my:'left bottom', at:'top middle', viewport: $(window)}, + style: { classes: CELL_QTIP_CLASS, border: 'none'}, + show: {event: "mouseover"}, + hide: {fixed: true, delay: 100, event: "mouseout"}, + }); + + self.cell_mouseover_div = self.cell_container.append('div').style('position', 'absolute').style('overflow', 'hidden') + .style('top', '0px').style('left','0px'); self.cell_container_node.addEventListener("scroll", function() { self.calculateVisibleInterval(); self.clipAndPositionCells(); }); + var mouseMove = (function() { + var prev_track, prev_cell_index, prev_dom; + var hover_cell = function(dom) { + $('.'+CELL_QTIP_CLASS).finish(); + dom.classed(CELL_HOVER_CLASS, true); + $(dom.node()).trigger("mouseover"); + }; + var unhover_cell = function(dom) { + $('.'+CELL_QTIP_CLASS).finish(); + dom.classed(CELL_HOVER_CLASS, false); + $(dom.node()).trigger("mouseout"); + }; + var clear_and_unhover = function() { + prev_track = undefined; + prev_cell_index = undefined; + prev_dom && unhover_cell(prev_dom); + prev_dom = undefined; + }; + return function(evt) { + var mouseX = utils.mouseX(evt); + var mouseY = utils.mouseY(evt); + var track_cell_tops = self.getTrackCellTops(); + var track = (function() { + var closest_track_dist = Number.POSITIVE_INFINITY; + var closest_track = undefined; + _.each(track_cell_tops, function(top, track_id) { + var dist = mouseY - top; + if (dist >= 0 && dist < closest_track_dist) { + closest_track_dist = dist; + closest_track = track_id; + } + }); + return closest_track; + })(); + if (!track) { + clear_and_unhover(); + return; + } + var track_height = oncoprint.getCellHeight(track); + if (mouseY > track_cell_tops[track] + track_height) { + clear_and_unhover(); + return; + } + var cell_width = oncoprint.getZoomedCellWidth(); + var cell_unit = cell_width + oncoprint.getCellPadding(); + if (mouseX % cell_unit > cell_width) { + clear_and_unhover(); + return; + } + // at this point, we are hovered over a cell position + var cell_index = Math.floor(mouseX / cell_unit); + if (cell_index !== prev_cell_index || track !== prev_track) { + // not the same cell as before + var track_cell = self.track_cells[track][oncoprint.getIdOrder()[cell_index]]; + if (!track_cell) { + // track doesn't have a cell there + clear_and_unhover(); + return; + } + // otherwise, we're over a cell + prev_dom && unhover_cell(prev_dom); + prev_cell_index = cell_index; + prev_track = track; + prev_dom = track_cell.dom; + hover_cell(prev_dom); + $(self.cell_div.node()).qtip('option', 'content.text', oncoprint.getTrackTooltip(track)(track_cell.d)); + } + }; + })(); + self.cell_mouseover_div.node().addEventListener('mousemove', mouseMove); // TODO: magic number self.cell_div.style('max-width', '1000px'); })(); @@ -99,6 +181,7 @@ self.removeTrackLabels(track_id); self.removeTrackButtons(track_id); + self.computeTrackCellTops(); self.renderLegend(); self.renderTrackLabels(); self.renderTrackButtons(); @@ -107,6 +190,7 @@ oncoprint.sort(); }); $(oncoprint).on(events.MOVE_TRACK, function(evt, data) { + self.computeTrackCellTops(); self.clipAndPositionCells(data.moved_tracks, 'top', true); self.renderTrackLabels(); self.renderTrackButtons(); @@ -117,6 +201,7 @@ //this.cell_div.style('display', 'none'); self.drawCells(d.track_id); self.clipAndPositionCells(undefined, 'top', true); + self.computeTrackCellTops(); self.renderTrackLabels(); self.renderTrackButtons(); self.resizeLabelDiv(); @@ -207,6 +292,10 @@ OncoprintSVGRenderer.prototype.resizeCellDiv = function() { this.cell_div.style('min-width', this.getCellAreaWidth()+'px') .style('min-height', this.getCellAreaHeight()+'px'); + + this.cell_mouseover_div.style('min-width', this.getCellAreaWidth()+'px') + .style('min-height', this.getCellAreaHeight()+'px'); + }; OncoprintSVGRenderer.prototype.resizeLabelDiv = function() { this.getLabelDiv().style('width', this.getLabelAreaWidth()+'px') @@ -377,8 +466,10 @@ } var self = this; + this.track_cells[track_id] = {}; var cell_class = this.getCellCSSClass(); var track_cell_class = this.getTrackCellCSSClass(track_id); + var track_cells = this.track_cells[track_id]; //var bound_svg = this.cell_div.selectAll('svg.'+track_cell_class).data(data, id_accessor); @@ -396,7 +487,8 @@ bound_svg.each(function(d,i) { var dom_cell = this; var id = id_accessor(d); - if (tooltip) { + track_cells[id] = {dom: d3.select(this), d: d}; + /*if (tooltip) { var tooltip_html = tooltip(d); $(dom_cell).one("mouseover", function() { $(dom_cell).qtip({ @@ -416,7 +508,7 @@ }); $(dom_cell).on("mouseout", function() { d3.select(dom_cell).classed(CELL_HOVER_CLASS, false); - }); + });*/ }); bound_svg.selectAll('*').remove(); this.active_rule_set_rules[rule_set.getRuleSetId()][track_id] = rule_set.apply(bound_svg, oncoprint.getFullCellWidth(), oncoprint.getCellHeight(track_id)); diff --git a/packages/oncoprintjs/src/js/utils.js b/packages/oncoprintjs/src/js/utils.js index f32670f8670..c37cabfe994 100644 --- a/packages/oncoprintjs/src/js/utils.js +++ b/packages/oncoprintjs/src/js/utils.js @@ -47,6 +47,9 @@ window.oncoprint_utils = (function() { exports.mouseY = function(evt) { return exports.ifndef(evt.offsetY, evt.originalEvent && evt.originalEvent.layerY); }; + exports.mouseX = function(evt) { + return exports.ifndef(evt.offsetX, evt.originalEvent && evt.originalEvent.layerX); + }; exports.ifndef = function(val, replacement) { return (typeof val === 'undefined') ? replacement : val; }; From 19547999f2da330244754903af3757b47d8d4405 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Tue, 28 Jul 2015 15:07:47 -0400 Subject: [PATCH 159/343] some changes to highlighting and qtip --- packages/oncoprintjs/src/css/oncoprint.css | 3 ++- .../oncoprintjs/src/js/OncoprintSVGRenderer.js | 17 +++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/packages/oncoprintjs/src/css/oncoprint.css b/packages/oncoprintjs/src/css/oncoprint.css index ed82da12e81..ce344202f40 100644 --- a/packages/oncoprintjs/src/css/oncoprint.css +++ b/packages/oncoprintjs/src/css/oncoprint.css @@ -15,7 +15,7 @@ } .oncoprint-cell-area { - background-color: #fff; + background-color: transparent; position: relative; } @@ -32,6 +32,7 @@ .oncoprint-cell-hover { outline: 1px solid DarkGray; + position: absolute; } .oncoprint-legend-header { diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index c4f2f1ddd70..2166648bfa2 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -72,6 +72,10 @@ (function initCellContainer() { self.cell_container = content_area.append('div').classed(CELL_AREA_CONTAINER_CLASS, true); //self.cell_container.style('display', 'none'); + self.cell_highlight = self.cell_container.append('div').classed(CELL_HOVER_CLASS, true) + .style('width', oncoprint.getZoomedCellWidth()+'px') + .style('visibility', 'hidden'); + self.cell_container_node = self.cell_container.node(); self.cell_div = self.cell_container.append('div').classed(CELL_AREA_CLASS, true); @@ -93,15 +97,16 @@ var prev_track, prev_cell_index, prev_dom; var hover_cell = function(dom) { $('.'+CELL_QTIP_CLASS).finish(); - dom.classed(CELL_HOVER_CLASS, true); + //dom.classed(CELL_HOVER_CLASS, true); $(dom.node()).trigger("mouseover"); }; var unhover_cell = function(dom) { $('.'+CELL_QTIP_CLASS).finish(); - dom.classed(CELL_HOVER_CLASS, false); + //dom.classed(CELL_HOVER_CLASS, false); $(dom.node()).trigger("mouseout"); }; var clear_and_unhover = function() { + self.cell_highlight.style('visibility','hidden'); prev_track = undefined; prev_cell_index = undefined; prev_dom && unhover_cell(prev_dom); @@ -149,12 +154,15 @@ return; } // otherwise, we're over a cell - prev_dom && unhover_cell(prev_dom); + //prev_dom && unhover_cell(prev_dom); + $('.'+CELL_QTIP_CLASS).finish().hide(); prev_cell_index = cell_index; prev_track = track; prev_dom = track_cell.dom; - hover_cell(prev_dom); $(self.cell_div.node()).qtip('option', 'content.text', oncoprint.getTrackTooltip(track)(track_cell.d)); + hover_cell(prev_dom); + self.cell_highlight.style('height', track_height+'px').style('top', track_cell_tops[track]+'px').style('left', cell_unit*cell_index + 'px'); + self.cell_highlight.style('visibility','visible'); } }; })(); @@ -230,6 +238,7 @@ self.clipAndPositionCells(undefined, undefined, true); self.resizeCells(); self.resizeCellDiv(); + self.cell_highlight.style('width', oncoprint.getZoomedCellWidth() + 'px'); }); $(oncoprint).on(events.SET_ID_ORDER, function() { From 8cb2bc4e31648f57c3a99a1df3cd048aae351a63 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Tue, 28 Jul 2015 15:19:53 -0400 Subject: [PATCH 160/343] stop event propagation --- packages/oncoprintjs/src/js/OncoprintSVGRenderer.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index 2166648bfa2..7414640dedd 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -85,6 +85,11 @@ style: { classes: CELL_QTIP_CLASS, border: 'none'}, show: {event: "mouseover"}, hide: {fixed: true, delay: 100, event: "mouseout"}, + events: { + show: function(evt, api) { + evt.originalEvent.stopPropagation(); + } + } }); self.cell_mouseover_div = self.cell_container.append('div').style('position', 'absolute').style('overflow', 'hidden') From 22bbd1db8b16979d4ba7ea67637057ce5c743d89 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Tue, 28 Jul 2015 15:27:18 -0400 Subject: [PATCH 161/343] fix qtip issue by renaming event --- .../oncoprintjs/src/js/OncoprintSVGRenderer.js | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index 7414640dedd..1f6acfd6fee 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -83,13 +83,8 @@ content: 'SHARED QTIP', position: {target: 'event', my:'left bottom', at:'top middle', viewport: $(window)}, style: { classes: CELL_QTIP_CLASS, border: 'none'}, - show: {event: "mouseover"}, - hide: {fixed: true, delay: 100, event: "mouseout"}, - events: { - show: function(evt, api) { - evt.originalEvent.stopPropagation(); - } - } + show: {event: "cell-mouseover"}, + hide: {fixed: true, delay: 100, event: "cell-mouseout"}, }); self.cell_mouseover_div = self.cell_container.append('div').style('position', 'absolute').style('overflow', 'hidden') @@ -102,13 +97,12 @@ var prev_track, prev_cell_index, prev_dom; var hover_cell = function(dom) { $('.'+CELL_QTIP_CLASS).finish(); - //dom.classed(CELL_HOVER_CLASS, true); - $(dom.node()).trigger("mouseover"); + $(dom.node()).trigger("cell-mouseover"); }; var unhover_cell = function(dom) { $('.'+CELL_QTIP_CLASS).finish(); //dom.classed(CELL_HOVER_CLASS, false); - $(dom.node()).trigger("mouseout"); + $(dom.node()).trigger("cell-mouseout"); }; var clear_and_unhover = function() { self.cell_highlight.style('visibility','hidden'); From 122796d774124ef8f0da2810c9628ad3a7b4e8a6 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Tue, 28 Jul 2015 17:41:33 -0400 Subject: [PATCH 162/343] fix tooltip problem, remove cell highlight, add column highlight line --- packages/oncoprintjs/src/css/oncoprint.css | 7 ++++ .../src/js/OncoprintSVGRenderer.js | 38 ++++++++++++++----- 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/packages/oncoprintjs/src/css/oncoprint.css b/packages/oncoprintjs/src/css/oncoprint.css index ce344202f40..27dc6152e92 100644 --- a/packages/oncoprintjs/src/css/oncoprint.css +++ b/packages/oncoprintjs/src/css/oncoprint.css @@ -19,6 +19,13 @@ position: relative; } +.oncoprint-column-highlight { + position:absolute; + width: 1px; + border-left: 1px dashed rgba(0,0,0,0.75); + top: 0px; +} + .oncoprint-cell { position: absolute; } diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index 1f6acfd6fee..ece6db95d33 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -35,6 +35,7 @@ var LABEL_AREA_CONTAINER_CLASS = 'oncoprint-label-area-ctr'; var CELL_AREA_CONTAINER_CLASS = 'oncoprint-cell-area-ctr'; var CELL_AREA_CLASS = 'oncoprint-cell-area'; + var COLUMN_HIGHLIGHT_CLASS = 'oncoprint-column-highlight' var CELL_HOVER_CLASS = 'oncoprint-cell-hover'; var LEGEND_HEADER_CLASS = 'oncoprint-legend-header'; @@ -71,20 +72,28 @@ })(); (function initCellContainer() { self.cell_container = content_area.append('div').classed(CELL_AREA_CONTAINER_CLASS, true); - //self.cell_container.style('display', 'none'); - self.cell_highlight = self.cell_container.append('div').classed(CELL_HOVER_CLASS, true) - .style('width', oncoprint.getZoomedCellWidth()+'px') + self.cell_column_highlight = self.cell_container.append('div').classed(COLUMN_HIGHLIGHT_CLASS, true) + .style('height', self.getCellAreaHeight()) .style('visibility', 'hidden'); self.cell_container_node = self.cell_container.node(); self.cell_div = self.cell_container.append('div').classed(CELL_AREA_CLASS, true); + var tooltip_html = ''; $(self.cell_div.node()).qtip({ content: 'SHARED QTIP', - position: {target: 'event', my:'left bottom', at:'top middle', viewport: $(window)}, + position: {target: 'event', my:'bottom middle', at:'top middle', viewport: $(window)}, style: { classes: CELL_QTIP_CLASS, border: 'none'}, show: {event: "cell-mouseover"}, hide: {fixed: true, delay: 100, event: "cell-mouseout"}, + events: { + show: function() { + $('.' + CELL_QTIP_CLASS+' .qtip-content').html(tooltip_html); + }, + render: function(){ + $('.' + CELL_QTIP_CLASS+' .qtip-content').html(tooltip_html); + } + } }); self.cell_mouseover_div = self.cell_container.append('div').style('position', 'absolute').style('overflow', 'hidden') @@ -95,8 +104,10 @@ }); var mouseMove = (function() { var prev_track, prev_cell_index, prev_dom; + var column_highlight_timeout; var hover_cell = function(dom) { $('.'+CELL_QTIP_CLASS).finish(); + //dom.classed(CELL_HOVER_CLASS, true); $(dom.node()).trigger("cell-mouseover"); }; var unhover_cell = function(dom) { @@ -105,11 +116,13 @@ $(dom.node()).trigger("cell-mouseout"); }; var clear_and_unhover = function() { - self.cell_highlight.style('visibility','hidden'); + //self.cell_highlight.style('visibility','hidden'); prev_track = undefined; prev_cell_index = undefined; prev_dom && unhover_cell(prev_dom); prev_dom = undefined; + self.cell_column_highlight.style('visibility', 'hidden'); + column_highlight_timeout && clearTimeout(column_highlight_timeout) }; return function(evt) { var mouseX = utils.mouseX(evt); @@ -145,6 +158,8 @@ // at this point, we are hovered over a cell position var cell_index = Math.floor(mouseX / cell_unit); if (cell_index !== prev_cell_index || track !== prev_track) { + self.cell_column_highlight.style('visibility', 'hidden'); + column_highlight_timeout && clearTimeout(column_highlight_timeout) // not the same cell as before var track_cell = self.track_cells[track][oncoprint.getIdOrder()[cell_index]]; if (!track_cell) { @@ -158,10 +173,13 @@ prev_cell_index = cell_index; prev_track = track; prev_dom = track_cell.dom; - $(self.cell_div.node()).qtip('option', 'content.text', oncoprint.getTrackTooltip(track)(track_cell.d)); + tooltip_html = oncoprint.getTrackTooltip(track)(track_cell.d); hover_cell(prev_dom); - self.cell_highlight.style('height', track_height+'px').style('top', track_cell_tops[track]+'px').style('left', cell_unit*cell_index + 'px'); - self.cell_highlight.style('visibility','visible'); + column_highlight_timeout = setTimeout(function() { + self.cell_column_highlight.style('left', cell_unit*cell_index + cell_width/2 + 'px').transition().style('visibility', 'visible'); + }, 1000); + //self.cell_highlight.style('height', track_height+'px').style('top', track_cell_tops[track]+'px').style('left', cell_unit*cell_index + 'px'); + //self.cell_highlight.style('visibility','visible'); } }; })(); @@ -237,7 +255,7 @@ self.clipAndPositionCells(undefined, undefined, true); self.resizeCells(); self.resizeCellDiv(); - self.cell_highlight.style('width', oncoprint.getZoomedCellWidth() + 'px'); + //self.cell_highlight.style('width', oncoprint.getZoomedCellWidth() + 'px'); }); $(oncoprint).on(events.SET_ID_ORDER, function() { @@ -303,7 +321,7 @@ this.cell_mouseover_div.style('min-width', this.getCellAreaWidth()+'px') .style('min-height', this.getCellAreaHeight()+'px'); - + this.cell_column_highlight.style('height', this.getCellAreaHeight() + 'px'); }; OncoprintSVGRenderer.prototype.resizeLabelDiv = function() { this.getLabelDiv().style('width', this.getLabelAreaWidth()+'px') From 953194d3455b4f6b54a39d03bb4157f12bcb7ec7 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Tue, 28 Jul 2015 18:19:45 -0400 Subject: [PATCH 163/343] comment out column highlight --- .../src/js/OncoprintSVGRenderer.js | 48 ++++++++----------- 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index ece6db95d33..c0e6932b1e0 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -79,22 +79,6 @@ self.cell_container_node = self.cell_container.node(); self.cell_div = self.cell_container.append('div').classed(CELL_AREA_CLASS, true); - var tooltip_html = ''; - $(self.cell_div.node()).qtip({ - content: 'SHARED QTIP', - position: {target: 'event', my:'bottom middle', at:'top middle', viewport: $(window)}, - style: { classes: CELL_QTIP_CLASS, border: 'none'}, - show: {event: "cell-mouseover"}, - hide: {fixed: true, delay: 100, event: "cell-mouseout"}, - events: { - show: function() { - $('.' + CELL_QTIP_CLASS+' .qtip-content').html(tooltip_html); - }, - render: function(){ - $('.' + CELL_QTIP_CLASS+' .qtip-content').html(tooltip_html); - } - } - }); self.cell_mouseover_div = self.cell_container.append('div').style('position', 'absolute').style('overflow', 'hidden') .style('top', '0px').style('left','0px'); @@ -105,24 +89,37 @@ var mouseMove = (function() { var prev_track, prev_cell_index, prev_dom; var column_highlight_timeout; + var tooltip_html = ''; + $(self.cell_div.node()).qtip({ + content: 'SHARED QTIP', + position: {target: 'event', my:'bottom middle', at:'top middle', viewport: $(window)}, + style: { classes: CELL_QTIP_CLASS, border: 'none'}, + show: {event: "cell-mouseover"}, + hide: {fixed: true, delay: 100, event: "cell-mouseout"}, + events: { + show: function() { + $('.' + CELL_QTIP_CLASS+' .qtip-content').html(tooltip_html); + }, + render: function(){ + $('.' + CELL_QTIP_CLASS+' .qtip-content').html(tooltip_html); + } + } + }); var hover_cell = function(dom) { $('.'+CELL_QTIP_CLASS).finish(); - //dom.classed(CELL_HOVER_CLASS, true); $(dom.node()).trigger("cell-mouseover"); }; var unhover_cell = function(dom) { $('.'+CELL_QTIP_CLASS).finish(); - //dom.classed(CELL_HOVER_CLASS, false); $(dom.node()).trigger("cell-mouseout"); }; var clear_and_unhover = function() { - //self.cell_highlight.style('visibility','hidden'); prev_track = undefined; prev_cell_index = undefined; prev_dom && unhover_cell(prev_dom); prev_dom = undefined; - self.cell_column_highlight.style('visibility', 'hidden'); - column_highlight_timeout && clearTimeout(column_highlight_timeout) + //self.cell_column_highlight.style('visibility', 'hidden'); + //column_highlight_timeout && clearTimeout(column_highlight_timeout) }; return function(evt) { var mouseX = utils.mouseX(evt); @@ -158,7 +155,7 @@ // at this point, we are hovered over a cell position var cell_index = Math.floor(mouseX / cell_unit); if (cell_index !== prev_cell_index || track !== prev_track) { - self.cell_column_highlight.style('visibility', 'hidden'); + //self.cell_column_highlight.style('visibility', 'hidden'); column_highlight_timeout && clearTimeout(column_highlight_timeout) // not the same cell as before var track_cell = self.track_cells[track][oncoprint.getIdOrder()[cell_index]]; @@ -168,18 +165,15 @@ return; } // otherwise, we're over a cell - //prev_dom && unhover_cell(prev_dom); $('.'+CELL_QTIP_CLASS).finish().hide(); prev_cell_index = cell_index; prev_track = track; prev_dom = track_cell.dom; tooltip_html = oncoprint.getTrackTooltip(track)(track_cell.d); hover_cell(prev_dom); - column_highlight_timeout = setTimeout(function() { + /*column_highlight_timeout = setTimeout(function() { self.cell_column_highlight.style('left', cell_unit*cell_index + cell_width/2 + 'px').transition().style('visibility', 'visible'); - }, 1000); - //self.cell_highlight.style('height', track_height+'px').style('top', track_cell_tops[track]+'px').style('left', cell_unit*cell_index + 'px'); - //self.cell_highlight.style('visibility','visible'); + }, 1000);*/ } }; })(); From 88fdb6277c291af2142446a79b51f6aa2186528e Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Tue, 28 Jul 2015 19:00:05 -0400 Subject: [PATCH 164/343] trigger mousemove event on mouseout to hide tooltip, and take out legacy tooltip code --- .../src/js/OncoprintSVGRenderer.js | 26 +------------------ 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index c0e6932b1e0..6a1b9e45628 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -178,6 +178,7 @@ }; })(); self.cell_mouseover_div.node().addEventListener('mousemove', mouseMove); + self.cell_mouseover_div.node().addEventListener('mouseout', mouseMove); // TODO: magic number self.cell_div.style('max-width', '1000px'); })(); @@ -491,10 +492,6 @@ var track_cell_class = this.getTrackCellCSSClass(track_id); var track_cells = this.track_cells[track_id]; - - //var bound_svg = this.cell_div.selectAll('svg.'+track_cell_class).data(data, id_accessor); - //this.cellRenderTarget().selectAll('svg.'+track_cell_class).remove(); - //this.cell_div.selectAll('svg.'+track_cell_class).remove(); var bound_svg = d3.select(fragment).selectAll('svg.'+track_cell_class).data(data); bound_svg.enter().append('svg').classed(track_cell_class, true).classed(cell_class, true) .attr('shape-rendering','geometricPrecision') @@ -508,27 +505,6 @@ var dom_cell = this; var id = id_accessor(d); track_cells[id] = {dom: d3.select(this), d: d}; - /*if (tooltip) { - var tooltip_html = tooltip(d); - $(dom_cell).one("mouseover", function() { - $(dom_cell).qtip({ - content: { - text: tooltip_html - }, - position: {my:'left bottom', at:'top middle', viewport: $(window)}, - style: { classes: CELL_QTIP_CLASS, border: 'none'}, - show: {event: "mouseover qtip-first-show"}, - hide: {fixed: true, delay: 100, event: "mouseout"} - }); - $(dom_cell).trigger("qtip-first-show"); - }); - } - $(dom_cell).on("mouseover", function() { - d3.select(dom_cell).classed(CELL_HOVER_CLASS, true); - }); - $(dom_cell).on("mouseout", function() { - d3.select(dom_cell).classed(CELL_HOVER_CLASS, false); - });*/ }); bound_svg.selectAll('*').remove(); this.active_rule_set_rules[rule_set.getRuleSetId()][track_id] = rule_set.apply(bound_svg, oncoprint.getFullCellWidth(), oncoprint.getCellHeight(track_id)); From d3fb5a925587b936a78805095073869ba60a4513 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Tue, 28 Jul 2015 19:09:29 -0400 Subject: [PATCH 165/343] fix problem where hiding cases didnt work --- packages/oncoprintjs/src/js/OncoprintSVGRenderer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index 6a1b9e45628..ad96bb225c1 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -158,7 +158,7 @@ //self.cell_column_highlight.style('visibility', 'hidden'); column_highlight_timeout && clearTimeout(column_highlight_timeout) // not the same cell as before - var track_cell = self.track_cells[track][oncoprint.getIdOrder()[cell_index]]; + var track_cell = self.track_cells[track][oncoprint.getVisibleIdOrder()[cell_index]]; if (!track_cell) { // track doesn't have a cell there clear_and_unhover(); @@ -558,7 +558,7 @@ self.track_cell_selections[track_id].each(function(d,i) { var new_x = cell_x[id_order[d[id_key]]]; var disp = this.style.display; - var new_disp = (new_x < visible_interval[0] || new_x > visible_interval[1]) ? 'none' : 'inherit'; + var new_disp = (isNaN(new_x) || new_x < visible_interval[0] || new_x > visible_interval[1]) ? 'none' : 'inherit'; if (disp !== new_disp) { this.style.display = new_disp; } From 39d7036f35ecb08661e3f4122a17b5464aa13933 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Wed, 29 Jul 2015 16:11:47 -0400 Subject: [PATCH 166/343] toSVG implemented --- packages/oncoprintjs/src/css/oncoprint.css | 7 ++ .../src/js/OncoprintSVGRenderer.js | 94 ++++++++++++++++--- packages/oncoprintjs/src/js/RuleSet.js | 18 ++-- packages/oncoprintjs/src/js/oncoprint.js | 9 +- 4 files changed, 104 insertions(+), 24 deletions(-) diff --git a/packages/oncoprintjs/src/css/oncoprint.css b/packages/oncoprintjs/src/css/oncoprint.css index 27dc6152e92..ca9461e7579 100644 --- a/packages/oncoprintjs/src/css/oncoprint.css +++ b/packages/oncoprintjs/src/css/oncoprint.css @@ -58,10 +58,17 @@ padding-right: 20px; } +.oncoprint-legend-block { + display: inline-block; +} .oncoprint-legend-element { display: inline-block; } +.oncoprint-track-label { + font-size: 12px; +} + .oncoprint-track-label-draggable { cursor: move; } diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index ad96bb225c1..21b8597157c 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -60,8 +60,9 @@ this.document_fragment; this.percent_altered_max_width = utils.textWidth('100%', self.getLabelFont()); - d3.select(container_selector_string).classed('noselect', true).selectAll('*').remove(); - d3.select(container_selector_string).append('br'); + this.container = d3.select(container_selector_string); + this.container.classed('noselect', true).selectAll('*').remove(); + this.container.append('br'); var content_area = d3.select(container_selector_string).append('div').classed('oncoprint-content-area', true); (function initLabelContainer() { self.label_container = content_area.append('div').classed(LABEL_AREA_CONTAINER_CLASS, true).style('position', 'relative'); @@ -335,7 +336,7 @@ div.selectAll(self.getTrackLabelCSSSelector(track_id)).remove(); }); } - OncoprintSVGRenderer.prototype.renderTrackLabels = function(track_ids, y) { + OncoprintSVGRenderer.prototype.renderTrackLabels = function(track_ids, y, render_whole_labels) { var div = this.label_div; if (typeof y !== "undefined") { div.selectAll(this.getTrackLabelCSSSelector(track_ids)).style('top', y+'px'); @@ -351,7 +352,7 @@ var track_label_class = self.getTrackLabelCSSClass(track_id); var label_text = self.oncoprint.getTrackLabel(track_id); var disp_label_text = label_text; - if (label_text.length > self.max_label_length) { + if (label_text.length > self.max_label_length && !render_whole_labels) { disp_label_text = label_text.substring(0,self.max_label_length-3)+'...'; } _.each(div.selectAll(self.getTrackLabelCSSSelector(track_id)), function(node) { @@ -363,8 +364,9 @@ .classed(self.getTrackLabelCSSClass(track_id), true) .classed('oncoprint-track-label-draggable', true) .classed('oncoprint-track-label-main', true) + .classed('oncoprint-track-label', true) .classed('noselect', true) - .style('font', self.getLabelFont()) + .style('font-family', self.getLabelFont()) .style('font-weight', 'bold') .text(disp_label_text) .style('top', label_top+'px') @@ -385,8 +387,9 @@ div.append('span') .style('position','absolute') .classed(self.getTrackLabelCSSClass(track_id), true) + .classed('oncoprint-track-label', true) .classed('noselect', true) - .style('font', self.getLabelFont()) + .style('font-family', self.getLabelFont()) .text(percent_altered + '%') .style('top', label_top+'px') .style('left', percent_altered_left+'px'); @@ -533,7 +536,7 @@ }; // Positioning - OncoprintSVGRenderer.prototype.clipAndPositionCells = function(track_ids, axis, force) { + OncoprintSVGRenderer.prototype.clipAndPositionCells = function(track_ids, axis, force, display_all) { this.cell_div.node().display = 'none'; track_ids = typeof track_ids === "undefined" ? this.oncoprint.getTracks() : track_ids; track_ids = [].concat(track_ids); @@ -558,7 +561,7 @@ self.track_cell_selections[track_id].each(function(d,i) { var new_x = cell_x[id_order[d[id_key]]]; var disp = this.style.display; - var new_disp = (isNaN(new_x) || new_x < visible_interval[0] || new_x > visible_interval[1]) ? 'none' : 'inherit'; + var new_disp = ((isNaN(new_x) || new_x < visible_interval[0] || new_x > visible_interval[1]) && !display_all) ? 'none' : 'inherit'; if (disp !== new_disp) { this.style.display = new_disp; } @@ -584,13 +587,13 @@ }); this.renderLegend(); }; - OncoprintSVGRenderer.prototype.renderLegend = function() { + OncoprintSVGRenderer.prototype.renderLegend = function(include_all) { var cell_width = this.oncoprint.getZoomedCellWidth(); var self = this; var rendered = {}; self.legend_table.selectAll('*').remove(); _.each(this.rule_sets, function(rule_set, track_id) { - if (rule_set.exclude_from_legend) { + if (rule_set.exclude_from_legend && !include_all) { return; } var rule_set_id = rule_set.getRuleSetId(); @@ -606,7 +609,7 @@ var legend_body_td = tr.append('td'); var legend_div = rule_set.getLegendDiv(active_rules, cell_width, self.oncoprint.getCellHeight(track_id)); legend_body_td.node().appendChild(legend_div); - d3.select(legend_div).selectAll('*').classed('oncoprint-legend-element', true); + utils.d3SelectChildren(d3.select(legend_div), '*').classed('oncoprint-legend-block', true); rendered[rule_set_id] = true; } }); @@ -665,7 +668,74 @@ })(track_id); }; OncoprintSVGRenderer.prototype.toSVG = function() { - var svg = d3.select(document.createElement('svg')); + var self = this; + var root = $(this.container.node()).offset(); + var svg = d3.select(document.createElementNS('http://www.w3.org/2000/svg', 'svg')); + svg.attr('width', this.getLabelAreaWidth() + this.getCellAreaWidth() + 'px'); + this.renderLegend(true); + this.renderTrackLabels(undefined, undefined, true); + svg.attr('height', $(this.container.node()).height()+'px'); + (function addLabels() { + self.label_div.selectAll('.oncoprint-track-label').each(function() { + var text_elt = d3.select(this); + var font = text_elt.style('font-family') || 'Arial'; + var weight = text_elt.style('font-weight'); + var size = text_elt.style('font-size') || '12px'; + var pos = $(text_elt.node()).offset(); + var text = text_elt.text(); + svg.append('text').style('font-family', font).style('font-weight', weight).style('font-size', size) + .attr('transform', utils.translate(pos.left - root.left,pos.top - root.top)) + .style('alignment-baseline', 'hanging') + .text(text); + }); + })(); + (function addCells() { + self.clipAndPositionCells(undefined, undefined, true, true); + self.cell_div.selectAll('.oncoprint-cell').each(function() { + var cell_elt = d3.select(this); + var pos = $(cell_elt.node()).offset(); + var g = svg.append('g').attr('transform', utils.translate(pos.left - root.left, pos.top - root.top)); + cell_elt.selectAll('*').each(function() { + utils.appendD3SVGElement(d3.select(this), g); + }); + }); + self.clipAndPositionCells(undefined, undefined, true); + })(); + (function addLegend() { + self.legend_table.selectAll('tr').each(function() { + d3.select(this).selectAll('td').each(function() { + d3.select(this).selectAll('.oncoprint-legend-header,.oncoprint-legend-element').each(function() { + if ($(this).text().trim().length) { + // text type element + var text_elt = d3.select(this); + var font = text_elt.style('font-family') || 'Arial'; + if (font !== 'Arial') { + console.log(this); + } + var weight = text_elt.style('font-weight'); + var size = text_elt.style('font-size') || '12px'; + var text = text_elt.text(); + var pos = $(text_elt.node()).offset(); + svg.append('text').style('font-family', font).style('font-weight', weight) + .style('font-size', size) + .attr('transform', utils.translate(pos.left - root.left, pos.top - root.top)) + .style('alignment-baseline', 'hanging') + .text(text); + } else if (this.tagName.toLowerCase() === 'svg') { + var elt = d3.select(this); + var pos = $(elt.node()).offset(); + var g = svg.append('g').attr('transform', utils.translate(pos.left - root.left, pos.top - root.top)); + elt.selectAll('*').each(function() { + utils.appendD3SVGElement(d3.select(this), g); + }); + } + }); + }); + }); + })(); + this.renderLegend(); + this.renderTrackLabels(); + return svg.node(); }; return OncoprintSVGRenderer; })(); \ No newline at end of file diff --git a/packages/oncoprintjs/src/js/RuleSet.js b/packages/oncoprintjs/src/js/RuleSet.js index 33a3223bf29..002b43aad39 100644 --- a/packages/oncoprintjs/src/js/RuleSet.js +++ b/packages/oncoprintjs/src/js/RuleSet.js @@ -467,9 +467,9 @@ window.oncoprint_RuleSet = (function() { var num_digit_multiplier = Math.pow(10, utils.ifndef(params.legend_num_decimal_digits,2)); return Math.round(x * num_digit_multiplier) / num_digit_multiplier; }); - div.append('h2').text(display_data_range[0]).classed('oncoprint-legend-label', true); + div.append('h2').text(display_data_range[0]).classed('oncoprint-legend-label oncoprint-legend-element', true); var mesh = 50; - var svg = div.append('svg').attr('width', mesh+'px').attr('height', cell_height+'px'); + var svg = div.append('svg').attr('width', mesh+'px').attr('height', cell_height+'px').classed('oncoprint-legend-element', true) for (var i=0; i<=mesh; i++) { var t = i/mesh; var d = (1-t)*data_range[0] + t*data_range[1]; @@ -482,7 +482,7 @@ window.oncoprint_RuleSet = (function() { .attr('fill', params.fill) .attr('x', i+'px'); } - div.append('h2').text(display_data_range[1]).classed('oncoprint-legend-label', true); + div.append('h2').text(display_data_range[1]).classed('oncoprint-legend-label oncoprint-legend-element', true); utils.d3SelectChildren(div, '*').style('padding-right', '10px'); return div.node(); }; @@ -560,9 +560,9 @@ window.oncoprint_RuleSet = (function() { var num_digit_multiplier = Math.pow(10, utils.ifndef(params.legend_num_decimal_digits,2)); return Math.round(x * num_digit_multiplier) / num_digit_multiplier; }); - div.append('h2').text(display_data_range[0]).classed('oncoprint-legend-label', true); + div.append('h2').text(display_data_range[0]).classed('oncoprint-legend-label oncoprint-legend-element', true); var mesh = 50; - var svg = div.append('svg').attr('width', mesh+'px').attr('height', cell_height+'px'); + var svg = div.append('svg').attr('width', mesh+'px').attr('height', cell_height+'px').classed('oncoprint-legend-element', true); for (var i=0; i<=mesh; i++) { var t = i/mesh; var d = (1-t)*data_range[0] + t*data_range[1]; @@ -573,7 +573,7 @@ window.oncoprint_RuleSet = (function() { .attr('fill', this.attrs.fill(datum)) .attr('x', i+'px'); } - div.append('h2').text(display_data_range[1]).classed('oncoprint-legend-label', true); + div.append('h2').text(display_data_range[1]).classed('oncoprint-legend-label oncoprint-legend-element', true); utils.d3SelectChildren(div, '*').style('padding-right', '10px'); return div.node(); }; @@ -592,11 +592,11 @@ window.oncoprint_RuleSet = (function() { return; } var div = d3.select(document.createElement('div')); - var svg_ctr = div.append('div'); - var svg = svg_ctr.append('svg').attr('width', cell_width+'px').attr('height', cell_height+'px'); + var svg_ctr = div.append('div').classed('oncoprint-legend-block', true); + var svg = svg_ctr.append('svg').attr('width', cell_width+'px').attr('height', cell_height+'px').classed('oncoprint-legend-element', true); this.apply(svg, cell_width, cell_height); if (this.legend_label) { - div.append('h2').text(this.legend_label).classed('oncoprint-legend-label', true); + div.append('h2').text(this.legend_label).classed('oncoprint-legend-label oncoprint-legend-element', true); } utils.d3SelectChildren(div, '*').style('padding-right', '10px'); return div.node(); diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 80733c24145..61f9b4754d3 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -412,7 +412,7 @@ window.Oncoprint = (function() { config = $.extend({}, defaultOncoprintConfig, config || {}); config = $.extend(config, hiddenOncoprintConfig); var oncoprint = new Oncoprint(config); - var renderer = new OncoprintSVGRenderer(container_selector_string, oncoprint, {label_font: '12px Arial', legend:config.legend}); + var renderer = new OncoprintSVGRenderer(container_selector_string, oncoprint, {label_font: 'Arial', legend:config.legend}); var ret = { onc_dev: oncoprint, ren_dev: renderer, @@ -441,8 +441,8 @@ window.Oncoprint = (function() { toggleCellPadding: function() { oncoprint.toggleCellPadding(); }, - toSVG: function(ctr) { - return renderer.toSVG(ctr); + toSVG: function() { + return renderer.toSVG(); }, setTrackGroupSortOrder: function(order) { oncoprint.setTrackGroupSortOrder(order); @@ -477,6 +477,9 @@ window.Oncoprint = (function() { getFilteredIdOrder: function(data_filter_fn, track_ids) { return oncoprint.getFilteredIdOrder(data_filter_fn, track_ids); }, + getVisibleIdOrder: function() { + return oncoprint.getVisibleIdOrder(); + }, hideIds: function(ids) { oncoprint.hideIds(ids); }, From e3395ea58ff441b1f5d7f0e720e5284faf238157 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Wed, 29 Jul 2015 18:54:38 -0400 Subject: [PATCH 167/343] change ruleset name constants so you can just use strings --- packages/oncoprintjs/src/js/RuleSet.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/oncoprintjs/src/js/RuleSet.js b/packages/oncoprintjs/src/js/RuleSet.js index 002b43aad39..7038b7e3d51 100644 --- a/packages/oncoprintjs/src/js/RuleSet.js +++ b/packages/oncoprintjs/src/js/RuleSet.js @@ -2,10 +2,10 @@ window.oncoprint_RuleSet = (function() { var utils = oncoprint_utils; var defaults = oncoprint_defaults; - var CATEGORICAL_COLOR = 0; - var GRADIENT_COLOR = 1; - var GENETIC_ALTERATION = 2; - var BAR_CHART = 3; + var CATEGORICAL_COLOR = 'categorical_color'; + var GRADIENT_COLOR = 'gradient_color'; + var GENETIC_ALTERATION = 'genetic_alteration'; + var BAR_CHART = 'bar_chart'; var CELL = "cell"; var ANY = '*'; From ff6e67c3b9e67cd9a67c2783d1cf4d661c15e842 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Thu, 30 Jul 2015 12:23:30 -0400 Subject: [PATCH 168/343] mouseout handler --- packages/oncoprintjs/src/js/OncoprintSVGRenderer.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index 21b8597157c..c582b60b4fa 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -87,7 +87,8 @@ self.calculateVisibleInterval(); self.clipAndPositionCells(); }); - var mouseMove = (function() { + var mouseMove, mouseOut; + (function() { var prev_track, prev_cell_index, prev_dom; var column_highlight_timeout; var tooltip_html = ''; @@ -122,7 +123,10 @@ //self.cell_column_highlight.style('visibility', 'hidden'); //column_highlight_timeout && clearTimeout(column_highlight_timeout) }; - return function(evt) { + mouseOut = function() { + clear_and_unhover(); + }; + mouseMove = function(evt) { var mouseX = utils.mouseX(evt); var mouseY = utils.mouseY(evt); var track_cell_tops = self.getTrackCellTops(); @@ -179,7 +183,7 @@ }; })(); self.cell_mouseover_div.node().addEventListener('mousemove', mouseMove); - self.cell_mouseover_div.node().addEventListener('mouseout', mouseMove); + self.cell_mouseover_div.node().addEventListener('mouseout', mouseOut); // TODO: magic number self.cell_div.style('max-width', '1000px'); })(); From 79165b14c59df023e378933201bf9f5ced4ac5d2 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Thu, 30 Jul 2015 12:25:43 -0400 Subject: [PATCH 169/343] fix space issue --- packages/oncoprintjs/src/js/OncoprintRenderer.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/oncoprintjs/src/js/OncoprintRenderer.js b/packages/oncoprintjs/src/js/OncoprintRenderer.js index 5fdeb8ded75..54a2cf47b2d 100644 --- a/packages/oncoprintjs/src/js/OncoprintRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintRenderer.js @@ -93,6 +93,9 @@ window.OncoprintRenderer = (function() { var y = this.upper_padding; var self = this; _.each(this.oncoprint.getTrackGroups(), function(group) { + if (group.length === 0) { + return; + } _.each(group, function(id) { ret[id] = y; y+= self.getRenderedTrackHeight(id); From eac3bb4b658d827d0be4ef2ceb68d5c9f02537e4 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Thu, 30 Jul 2015 14:38:26 -0400 Subject: [PATCH 170/343] small adjustments --- packages/oncoprintjs/src/css/oncoprint.css | 1 + packages/oncoprintjs/src/js/OncoprintRenderer.js | 2 +- packages/oncoprintjs/src/js/OncoprintSVGRenderer.js | 6 +++--- packages/oncoprintjs/src/js/oncoprint.js | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/oncoprintjs/src/css/oncoprint.css b/packages/oncoprintjs/src/css/oncoprint.css index ca9461e7579..45ce9026386 100644 --- a/packages/oncoprintjs/src/css/oncoprint.css +++ b/packages/oncoprintjs/src/css/oncoprint.css @@ -74,6 +74,7 @@ } .oncoprint-label-dragging { font-weight: bold !important; + color: #ff0000; } .noselect { diff --git a/packages/oncoprintjs/src/js/OncoprintRenderer.js b/packages/oncoprintjs/src/js/OncoprintRenderer.js index 54a2cf47b2d..fce20bc4daf 100644 --- a/packages/oncoprintjs/src/js/OncoprintRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintRenderer.js @@ -37,7 +37,7 @@ window.OncoprintRenderer = (function() { this.clipping = true; this.oncoprint = oncoprint; this.config = config; - this.upper_padding = utils.ifndef(config.upper_padding, 10); + this.upper_padding = utils.ifndef(config.upper_padding, 0); this.max_label_length = utils.ifndef(config.max_label_length, 20); (function computeLabelAreaWidth(self) { diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index c582b60b4fa..51d61c20340 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -661,14 +661,14 @@ }); } $label_drag_div.on("mousemove", moveHandler); - $label_drag_div.one("mouseleave mouseup", function(evt) { + var mouseUpHandler = function(evt) { $label_drag_div.hide(); $label_drag_div.off("mousemove", moveHandler); - $label_drag_div.off("mouseleave mouseup"); if (new_pos > -1) { self.oncoprint.moveTrack(track_id, new_pos); } - }); + }; + $(document).one("mouseup", mouseUpHandler); })(track_id); }; OncoprintSVGRenderer.prototype.toSVG = function() { diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 61f9b4754d3..fcf0cbf43ee 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -62,7 +62,7 @@ window.Oncoprint = (function() { function Oncoprint(config) { var self = this; var getTrackId = utils.makeIdCounter(); - var MIN_CELL_WIDTH = 0.5; + var MIN_CELL_WIDTH = 1; self.config = config; From 52862c8e57acaad5e52198acb53ea9eb53d05b34 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Thu, 30 Jul 2015 15:00:21 -0400 Subject: [PATCH 171/343] some styling changes --- packages/oncoprintjs/src/css/oncoprint.css | 2 +- .../oncoprintjs/src/js/OncoprintSVGRenderer.js | 2 +- packages/oncoprintjs/src/js/RuleSet.js | 15 ++++++++++----- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/oncoprintjs/src/css/oncoprint.css b/packages/oncoprintjs/src/css/oncoprint.css index 45ce9026386..dd872f1d2cb 100644 --- a/packages/oncoprintjs/src/css/oncoprint.css +++ b/packages/oncoprintjs/src/css/oncoprint.css @@ -49,13 +49,13 @@ text-anchor: start; alignment-baseline: hanging; font-size: 12px; + padding-right: 30px; } .oncoprint-legend-label { font-family: Arial; alignment-baseline: hanging; font-size: 12px; - padding-right: 20px; } .oncoprint-legend-block { diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index 51d61c20340..f50fa445a42 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -608,7 +608,7 @@ if (!rendered.hasOwnProperty(rule_set_id)) { var tr = self.legend_table.append('tr'); var label_header = tr.append('td').style('padding-top', '1em').style('padding-bottom', '1em') - .append('h1').classed('oncoprint-legend-header', true); + .append('span').classed('oncoprint-legend-header', true); label_header.text(rule_set.getLegendLabel()); var legend_body_td = tr.append('td'); var legend_div = rule_set.getLegendDiv(active_rules, cell_width, self.oncoprint.getCellHeight(track_id)); diff --git a/packages/oncoprintjs/src/js/RuleSet.js b/packages/oncoprintjs/src/js/RuleSet.js index 7038b7e3d51..8c95cc6da8f 100644 --- a/packages/oncoprintjs/src/js/RuleSet.js +++ b/packages/oncoprintjs/src/js/RuleSet.js @@ -467,7 +467,8 @@ window.oncoprint_RuleSet = (function() { var num_digit_multiplier = Math.pow(10, utils.ifndef(params.legend_num_decimal_digits,2)); return Math.round(x * num_digit_multiplier) / num_digit_multiplier; }); - div.append('h2').text(display_data_range[0]).classed('oncoprint-legend-label oncoprint-legend-element', true); + div.append('span').text(display_data_range[0]).classed('oncoprint-legend-label oncoprint-legend-element', true) + .style('position', 'relative').style('bottom', '0px'); var mesh = 50; var svg = div.append('svg').attr('width', mesh+'px').attr('height', cell_height+'px').classed('oncoprint-legend-element', true) for (var i=0; i<=mesh; i++) { @@ -482,7 +483,8 @@ window.oncoprint_RuleSet = (function() { .attr('fill', params.fill) .attr('x', i+'px'); } - div.append('h2').text(display_data_range[1]).classed('oncoprint-legend-label oncoprint-legend-element', true); + div.append('span').text(display_data_range[1]).classed('oncoprint-legend-label oncoprint-legend-element', true) + .style('position', 'relative').style('bottom', cell_height - 3 + 'px'); utils.d3SelectChildren(div, '*').style('padding-right', '10px'); return div.node(); }; @@ -560,7 +562,8 @@ window.oncoprint_RuleSet = (function() { var num_digit_multiplier = Math.pow(10, utils.ifndef(params.legend_num_decimal_digits,2)); return Math.round(x * num_digit_multiplier) / num_digit_multiplier; }); - div.append('h2').text(display_data_range[0]).classed('oncoprint-legend-label oncoprint-legend-element', true); + div.append('span').text(display_data_range[0]).classed('oncoprint-legend-label oncoprint-legend-element', true) + .style('position', 'relative').style('bottom', cell_height / 2 - 3 + 'px'); var mesh = 50; var svg = div.append('svg').attr('width', mesh+'px').attr('height', cell_height+'px').classed('oncoprint-legend-element', true); for (var i=0; i<=mesh; i++) { @@ -573,7 +576,8 @@ window.oncoprint_RuleSet = (function() { .attr('fill', this.attrs.fill(datum)) .attr('x', i+'px'); } - div.append('h2').text(display_data_range[1]).classed('oncoprint-legend-label oncoprint-legend-element', true); + div.append('span').text(display_data_range[1]).classed('oncoprint-legend-label oncoprint-legend-element', true) + .style('position', 'relative').style('bottom', cell_height / 2 - 3 + 'px'); utils.d3SelectChildren(div, '*').style('padding-right', '10px'); return div.node(); }; @@ -596,7 +600,8 @@ window.oncoprint_RuleSet = (function() { var svg = svg_ctr.append('svg').attr('width', cell_width+'px').attr('height', cell_height+'px').classed('oncoprint-legend-element', true); this.apply(svg, cell_width, cell_height); if (this.legend_label) { - div.append('h2').text(this.legend_label).classed('oncoprint-legend-label oncoprint-legend-element', true); + div.append('span').text(this.legend_label).classed('oncoprint-legend-label oncoprint-legend-element', true) + .style('position', 'relative').style('bottom', cell_height / 2 - 3 + 'px'); } utils.d3SelectChildren(div, '*').style('padding-right', '10px'); return div.node(); From 674366ab0fdc8db64f6cb7b67bce24b789841d26 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Thu, 30 Jul 2015 15:02:35 -0400 Subject: [PATCH 172/343] small change to toSVG --- packages/oncoprintjs/src/js/OncoprintSVGRenderer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index f50fa445a42..1ddddcda406 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -671,13 +671,13 @@ $(document).one("mouseup", mouseUpHandler); })(track_id); }; - OncoprintSVGRenderer.prototype.toSVG = function() { + OncoprintSVGRenderer.prototype.toSVG = function(full_labels) { var self = this; var root = $(this.container.node()).offset(); var svg = d3.select(document.createElementNS('http://www.w3.org/2000/svg', 'svg')); svg.attr('width', this.getLabelAreaWidth() + this.getCellAreaWidth() + 'px'); this.renderLegend(true); - this.renderTrackLabels(undefined, undefined, true); + this.renderTrackLabels(undefined, undefined, full_labels); svg.attr('height', $(this.container.node()).height()+'px'); (function addLabels() { self.label_div.selectAll('.oncoprint-track-label').each(function() { From fda29a93c8b2beb0b0b721e8709b257ab7d9bde5 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Mon, 3 Aug 2015 16:17:51 -0400 Subject: [PATCH 173/343] use full cell width for legend --- packages/oncoprintjs/src/js/OncoprintSVGRenderer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index 1ddddcda406..9085e9992fd 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -592,7 +592,7 @@ this.renderLegend(); }; OncoprintSVGRenderer.prototype.renderLegend = function(include_all) { - var cell_width = this.oncoprint.getZoomedCellWidth(); + var cell_width = this.oncoprint.getFullCellWidth(); var self = this; var rendered = {}; self.legend_table.selectAll('*').remove(); From b52938343b108e117dc8a43dc1bf695d4dcc79b9 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Tue, 4 Aug 2015 13:44:33 -0400 Subject: [PATCH 174/343] reduce default track group separation --- packages/oncoprintjs/src/js/OncoprintRenderer.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/oncoprintjs/src/js/OncoprintRenderer.js b/packages/oncoprintjs/src/js/OncoprintRenderer.js index fce20bc4daf..6da1da8dda8 100644 --- a/packages/oncoprintjs/src/js/OncoprintRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintRenderer.js @@ -39,6 +39,7 @@ window.OncoprintRenderer = (function() { this.config = config; this.upper_padding = utils.ifndef(config.upper_padding, 0); this.max_label_length = utils.ifndef(config.max_label_length, 20); + this.track_group_separation = 15; (function computeLabelAreaWidth(self) { var label_font = self.getLabelFont(); @@ -48,10 +49,6 @@ window.OncoprintRenderer = (function() { self.label_area_width = max_label_width + buffer_width + max_percent_altered_width; })(this); }; - OncoprintRenderer.prototype.getTrackGroupSeparation = function() { - // TODO: configurable - return 40; - }; OncoprintRenderer.prototype.getCellCSSClass = function() { return 'oncoprint-cell'; }; @@ -100,7 +97,7 @@ window.OncoprintRenderer = (function() { ret[id] = y; y+= self.getRenderedTrackHeight(id); }); - y += self.getTrackGroupSeparation(); + y += self.track_group_separation; }); return ret; }; From 7ce97910fe6b37c7ed2bfe21d3ffc1ed5df5d225 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Tue, 4 Aug 2015 14:36:51 -0400 Subject: [PATCH 175/343] nested rules (for more than one svg element in a single rule) and application to NA --- .../oncoprintjs/src/js/OncoprintRenderer.js | 2 +- packages/oncoprintjs/src/js/RuleSet.js | 51 ++++++++++++++++++- 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/packages/oncoprintjs/src/js/OncoprintRenderer.js b/packages/oncoprintjs/src/js/OncoprintRenderer.js index 6da1da8dda8..598c2f49fc9 100644 --- a/packages/oncoprintjs/src/js/OncoprintRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintRenderer.js @@ -39,7 +39,7 @@ window.OncoprintRenderer = (function() { this.config = config; this.upper_padding = utils.ifndef(config.upper_padding, 0); this.max_label_length = utils.ifndef(config.max_label_length, 20); - this.track_group_separation = 15; + this.track_group_separation = 12; (function computeLabelAreaWidth(self) { var label_font = self.getLabelFont(); diff --git a/packages/oncoprintjs/src/js/RuleSet.js b/packages/oncoprintjs/src/js/RuleSet.js index 8c95cc6da8f..02bb11ae712 100644 --- a/packages/oncoprintjs/src/js/RuleSet.js +++ b/packages/oncoprintjs/src/js/RuleSet.js @@ -33,6 +33,27 @@ window.oncoprint_RuleSet = (function() { return Number.NEGATIVE_INFINITY; } }; + var makeNARuleParams = function(condition, label) { + return { + condition: condition, + shape: utils.makeD3SVGElement('rect'), + attrs: {fill: '#d3d3d3', width: '100%', height:'100%'}, + legend_label: label, + children: [{ + condition: condition, + shape: utils.makeD3SVGElement('path'), + attrs: {d: "m 0% 0% L 100% 100%"}, + styles: {'stroke-width':'1px', 'stroke':'#dfdfdf'}, + legend_label: label, + }, { + condition: condition, + shape: utils.makeD3SVGElement('path'), + attrs: {d: "m 100% 0% L 0% 100%"}, + styles: {'stroke-width':'1px', 'stroke':'#dddddd'}, + legend_label: label, + }], + }; + }; var D3SVGRuleSet = (function() { function D3SVGRuleSet(params) { this.rule_map = {}; @@ -121,6 +142,9 @@ window.oncoprint_RuleSet = (function() { _.each(params.color, function(color, category) { addColorRule(color, category); }); + self.addStaticRule(makeNARuleParams(function(d) { + return params.getCategory(d) === 'NA'; + }, 'NA')); this.sort_cmp = params.sort_cmp || function(d1,d2) { var cat1 = params.getCategory(d1); @@ -145,7 +169,7 @@ window.oncoprint_RuleSet = (function() { var missing_categories = []; g.each(function(d,i) { var category = params.getCategory(d); - if (!params.color.hasOwnProperty(category)) { + if (!params.color.hasOwnProperty(category) && category !== "NA") { var new_color = d3_colors.pop(); params.color[category] = new_color; addColorRule(new_color, category); @@ -182,6 +206,9 @@ window.oncoprint_RuleSet = (function() { scale: params.scale, na_color: params.na_color }); + this.addStaticRule(makeNARuleParams(function(d) { + return isNaN(d[params.data_key]); + }, 'NA')); this.sort_cmp = params.sort_cmp || $.proxy(numericalNaNSort, this); this.getLegendDiv = function(active_rules, cell_width, cell_height) { return (active_rules[rule] && this.rule_map[rule].getLegendDiv(cell_width, cell_height)) || $('
')[0]; @@ -201,6 +228,9 @@ window.oncoprint_RuleSet = (function() { fill: params.fill, na_color: params.na_color }); + this.addStaticRule(makeNARuleParams(function(d) { + return isNaN(d[params.data_key]); + }, 'NA')); this.sort_cmp = params.sort_cmp || $.proxy(numericalNaNSort, this); this.getLegendDiv = function(active_rules, cell_width, cell_height) { return (active_rules[rule] && this.rule_map[rule].getLegendDiv(cell_width, cell_height)) || $('
')[0]; @@ -333,6 +363,10 @@ window.oncoprint_RuleSet = (function() { this.attrs.y = utils.ifndef(this.attrs.y, 0); this.styles = params.styles || {}; + + this.children = _.map(params.children, function(p) { + return new D3SVGRule(p); + }); } var percentToPx = function(attr_val, attr_name, cell_width, cell_height) { @@ -361,6 +395,17 @@ window.oncoprint_RuleSet = (function() { var pt_y = percentToPx(split_pt[1], 'y', cell_width, cell_height); return pt_x+","+pt_y; }).join(" "); + } else if (attr_name === 'd') { + var split = ret.split(/\s+/); + for (var i=0, _len = split.length; i<_len; i++) { + var c = split[i].toLowerCase(); + if (c === 'm' || c === 'l') { + split[i+1] = percentToPx(split[i+1], 'x', cell_width, cell_height); + split[i+2] = percentToPx(split[i+2], 'y', cell_width, cell_height); + i += 2; + } + } + return split.join(" "); } else { ret = percentToPx(ret, attr_name, cell_width, cell_height); } @@ -383,6 +428,9 @@ window.oncoprint_RuleSet = (function() { _.each(styles, function(val, key) { elts.style(key, val); }); + _.each(this.children, function(r) { + r.apply(g, cell_width, cell_height); + }); } D3SVGRule.prototype.filter = function(g) { return g.filter(this.condition); @@ -609,7 +657,6 @@ window.oncoprint_RuleSet = (function() { } D3SVGStaticRule.prototype = Object.create(D3SVGRule.prototype); - return { CATEGORICAL_COLOR: CATEGORICAL_COLOR, GRADIENT_COLOR: GRADIENT_COLOR, From d0e1cccb7748d9a71d656a0adbb538a9f7ed8d76 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Tue, 4 Aug 2015 14:57:24 -0400 Subject: [PATCH 176/343] change qtip messages and add border to cell qtips --- packages/oncoprintjs/src/css/oncoprint.css | 2 +- packages/oncoprintjs/src/js/OncoprintSVGRenderer.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/oncoprintjs/src/css/oncoprint.css b/packages/oncoprintjs/src/css/oncoprint.css index dd872f1d2cb..7bf699c0b45 100644 --- a/packages/oncoprintjs/src/css/oncoprint.css +++ b/packages/oncoprintjs/src/css/oncoprint.css @@ -88,7 +88,7 @@ .oncoprint-cell-qtip { background-color: rgba(0,0,0,0) !important; - border: none !important; + //border: none !important; } .oncoprint-cell-qtip .qtip-content { background-color: rgba(255,255,255,0.92) !important; diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index 9085e9992fd..1d97b3db4c7 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -451,7 +451,7 @@ if (self.oncoprint.isTrackSortDirectionChangable(track_id)) { (function() { var imgs = ['images/decreaseSort.svg', 'images/increaseSort.svg', 'images/nonSort.svg']; - var descs = ['Sort in descending order', 'Don\'t sort on this track', 'Sort in ascending order']; + var descs = ['Click to sort in descending order', 'Click to not sort on this track', 'Click to sort in ascending order']; var sort_direction = [1, -1, 0]; var current_sort_setting = sort_direction.indexOf(self.oncoprint.getTrackSortDirection(track_id)); var new_btn = div.append('img'); From 7e366e4dd265ba1f5520d9350b1a9383d5516177 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Tue, 4 Aug 2015 16:34:21 -0400 Subject: [PATCH 177/343] quantize zoom --- packages/oncoprintjs/src/js/oncoprint.js | 46 ++++++++++++++++++------ 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index fcf0cbf43ee..135b2ce4838 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -62,7 +62,6 @@ window.Oncoprint = (function() { function Oncoprint(config) { var self = this; var getTrackId = utils.makeIdCounter(); - var MIN_CELL_WIDTH = 1; self.config = config; @@ -77,30 +76,51 @@ window.Oncoprint = (function() { self.tracks = {}; self.sort_config = {type: 'track'}; - self.zoom = 1; self.cell_padding_on = true; self.true_cell_width = config.cell_width; + self.zoomed_cell_width = self.true_cell_width; + self.zoom = 1; + // Cell Padding self.toggleCellPadding = function() { self.cell_padding_on = !self.cell_padding_on; $(self).trigger(events.SET_CELL_PADDING); }; self.getCellPadding = function() { - return self.config.cell_padding*self.getZoomMultiplier()*(+self.cell_padding_on); + return Math.floor(self.config.cell_padding*self.getZoom())*(+self.cell_padding_on); }; // Zoom self.getZoom = function() { return self.zoom; }; - self.getZoomMultiplier = function() { - var min_over_max = MIN_CELL_WIDTH/self.getFullCellWidth(); - return (1-min_over_max)*self.zoom + min_over_max; - }; self.setZoom = function(z) { - self.zoom = utils.clamp(z,0,1); + self.zoom = utils.clamp(z, 0, 1); + updateZoomedCellWidth(); + updateZoom(); $(self).trigger(events.SET_ZOOM); + return self.zoom; + }; + var updateZoom = function() { + // maps {1, ... , true_cell_width} to [0,1] + self.zoom = (self.zoomed_cell_width-1)/(self.true_cell_width - 1); + }; + var updateZoomedCellWidth = function() { + // maps [0,1] to {1, ... , true_cell_width} + self.zoomed_cell_width = Math.round(self.zoom*(self.true_cell_width-1) + 1); + }; + self.increaseZoom = function() { + self.zoomed_cell_width = utils.clamp(self.zoomed_cell_width+1, 1, self.true_cell_width); + updateZoom(); + $(self).trigger(events.SET_ZOOM); + return self.zoom; + }; + self.decreaseZoom = function() { + self.zoomed_cell_width = utils.clamp(self.zoomed_cell_width-1, 1, self.true_cell_width); + updateZoom(); + $(self).trigger(events.SET_ZOOM); + return self.zoom; }; // Cell Width @@ -108,7 +128,7 @@ window.Oncoprint = (function() { return self.true_cell_width; }; self.getZoomedCellWidth = function() { - return self.true_cell_width*self.getZoomMultiplier(); + return self.zoomed_cell_width; }; // Cell Height @@ -463,7 +483,13 @@ window.Oncoprint = (function() { oncoprint.setTrackSortDirection(track_id, dir); }, setZoom: function(z) { - oncoprint.setZoom(z); + return oncoprint.setZoom(z); + }, + increaseZoom: function() { + return oncoprint.increaseZoom(); + }, + decreaseZoom: function() { + return oncoprint.decreaseZoom(); }, suppressRendering: function() { renderer.suppressRendering(); From 1e8059098e6d37622975d9358b1756e793e229d8 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Tue, 4 Aug 2015 17:32:58 -0400 Subject: [PATCH 178/343] small adjustment to padding zooming, and small adjustments to mutation labels, and changing default hetloss name --- packages/oncoprintjs/src/js/defaults.js | 8 ++++---- packages/oncoprintjs/src/js/oncoprint.js | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/oncoprintjs/src/js/defaults.js b/packages/oncoprintjs/src/js/defaults.js index 142ba4c4637..a42743f14f5 100644 --- a/packages/oncoprintjs/src/js/defaults.js +++ b/packages/oncoprintjs/src/js/defaults.js @@ -62,12 +62,12 @@ window.oncoprint_defaults = (function() { 'HOMODELETED':{ shape: 'full-rect', color: '#0000FF', - legend_label: 'Homozygous Deletion' + legend_label: 'Deep Deletion' }, - 'HETLOSS': { + 'HEMIZYGOUSLYDELETED': { shape: 'full-rect', color: '#8FD8D8', - legend_label: 'Heterozygous Deletion' + legend_label: 'Shallow Deletion' } }, 'mrna': { @@ -115,7 +115,7 @@ window.oncoprint_defaults = (function() { 'INFRAME': { shape: 'middle-rect', color: '#9F8170', - legend_label: 'Inframe Insertion/Deletion' + legend_label: 'Inframe Mutation' }, 'TRUNC': { shape: 'middle-rect', diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 135b2ce4838..6a5f067db69 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -88,7 +88,7 @@ window.Oncoprint = (function() { $(self).trigger(events.SET_CELL_PADDING); }; self.getCellPadding = function() { - return Math.floor(self.config.cell_padding*self.getZoom())*(+self.cell_padding_on); + return Math.ceil(self.config.cell_padding*self.getZoom())*(+self.cell_padding_on); }; // Zoom From cd703033915a72c685db81e1ccc7892ebc7fad78 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Wed, 5 Aug 2015 13:19:09 -0400 Subject: [PATCH 179/343] some optimizations relating to sorting, computing altered data percentage --- .../src/js/OncoprintSVGRenderer.js | 42 ++++++++++++------- packages/oncoprintjs/src/js/events.js | 2 +- packages/oncoprintjs/src/js/oncoprint.js | 16 +++++-- 3 files changed, 40 insertions(+), 20 deletions(-) diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index 1d97b3db4c7..1bc9a79944a 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -59,6 +59,9 @@ this.legend_table; this.document_fragment; this.percent_altered_max_width = utils.textWidth('100%', self.getLabelFont()); + this.altered_data_percentage = {}; + + this.cell_tooltip_html = ''; this.container = d3.select(container_selector_string); this.container.classed('noselect', true).selectAll('*').remove(); @@ -91,7 +94,6 @@ (function() { var prev_track, prev_cell_index, prev_dom; var column_highlight_timeout; - var tooltip_html = ''; $(self.cell_div.node()).qtip({ content: 'SHARED QTIP', position: {target: 'event', my:'bottom middle', at:'top middle', viewport: $(window)}, @@ -100,10 +102,10 @@ hide: {fixed: true, delay: 100, event: "cell-mouseout"}, events: { show: function() { - $('.' + CELL_QTIP_CLASS+' .qtip-content').html(tooltip_html); + $(this).find('.qtip-content').html(self.cell_tooltip_html); }, render: function(){ - $('.' + CELL_QTIP_CLASS+' .qtip-content').html(tooltip_html); + $(this).find('.qtip-content').html(self.cell_tooltip_html); } } }); @@ -174,7 +176,7 @@ prev_cell_index = cell_index; prev_track = track; prev_dom = track_cell.dom; - tooltip_html = oncoprint.getTrackTooltip(track)(track_cell.d); + self.cell_tooltip_html = oncoprint.getTrackTooltip(track)(track_cell.d); hover_cell(prev_dom); /*column_highlight_timeout = setTimeout(function() { self.cell_column_highlight.style('left', cell_unit*cell_index + cell_width/2 + 'px').transition().style('visibility', 'visible'); @@ -202,6 +204,7 @@ var track_id = data.track_id; delete self.rule_sets[track_id]; delete self.track_cell_selections[track_id]; + delete self.altered_data_percentage[track_id]; self.removeTrackCells(track_id); self.removeTrackLabels(track_id); self.removeTrackButtons(track_id); @@ -238,6 +241,7 @@ //this.cell_div.style('display', 'none'); self.drawCells(d.track_id); self.clipAndPositionCells(d.track_id, undefined, true); + self.computeAlteredDataPercentage(d.track_id); self.renderTrackLabels(d.track_id); self.resizeCellDiv(); self.renderLegend(); @@ -258,17 +262,25 @@ //self.cell_highlight.style('width', oncoprint.getZoomedCellWidth() + 'px'); }); - $(oncoprint).on(events.SET_ID_ORDER, function() { - self.clipAndPositionCells(undefined, undefined, true); - }); - - $(oncoprint).on(events.SET_VISIBLE_IDS, function() { + $(oncoprint).on(events.SET_VISIBLE_ID_ORDER, function() { self.clipAndPositionCells(undefined, undefined, true); self.resizeCellDiv(); }); })(); } utils.extends(OncoprintSVGRenderer, OncoprintRenderer); + OncoprintSVGRenderer.prototype.computeAlteredDataPercentage = function(track_id) { + var rule_set = this.getRuleSet(track_id); + if (rule_set && rule_set.alteredData) { + var data = this.oncoprint.getTrackData(track_id); + var num_altered = rule_set.alteredData(data).length; + var percent_altered = Math.floor(100 * num_altered / data.length); + this.altered_data_percentage[track_id] = percent_altered; + } + }; + OncoprintSVGRenderer.prototype.getAlteredDataPercentage = function(track_id) { + return this.altered_data_percentage[track_id]; + }; OncoprintSVGRenderer.prototype.calculateVisibleInterval = function() { var cell_unit = this.oncoprint.getZoomedCellWidth() + this.oncoprint.getCellPadding(); var cell_ctr_rect = this.cell_container_node.getBoundingClientRect(); @@ -300,12 +312,16 @@ this.drawCells(track_id); this.clipAndPositionCells(track_id, undefined, true); this.renderLegend(); + this.computeAlteredDataPercentage(track_id); + this.renderTrackLabels(track_id); }; OncoprintSVGRenderer.prototype.useSameRuleSet = function(target_track_id, source_track_id) { OncoprintRenderer.prototype.useSameRuleSet.call(this, target_track_id, source_track_id); this.drawCells(target_track_id); this.clipAndPositionCells(target_track_id, undefined, true); this.renderLegend(); + this.computeAlteredDataPercentage(target_track_id); + this.renderTrackLabels(target_track_id); } // Containers @@ -382,12 +398,8 @@ style: { classes: 'qtip-light qtip-rounded qtip-shadow qtip-lightyellow'}, show: {event: "mouseover"} }); - - var rule_set = self.getRuleSet(track_id); - if (rule_set && rule_set.alteredData) { - var data = self.oncoprint.getTrackData(track_id); - var num_altered = rule_set.alteredData(data).length; - var percent_altered = Math.floor(100*num_altered/data.length); + var percent_altered = self.getAlteredDataPercentage(track_id); + if (typeof percent_altered !== 'undefined') { div.append('span') .style('position','absolute') .classed(self.getTrackLabelCSSClass(track_id), true) diff --git a/packages/oncoprintjs/src/js/events.js b/packages/oncoprintjs/src/js/events.js index 455c8644f59..6788cb82f96 100644 --- a/packages/oncoprintjs/src/js/events.js +++ b/packages/oncoprintjs/src/js/events.js @@ -48,5 +48,5 @@ window.oncoprint_events = { FINISHED_POSITIONING: 'finished_positioning.renderer.oncoprint', SET_ZOOM: 'set_zoom.oncoprint', SET_SORT_DIRECTION: 'set_sort_direction.oncoprint', - SET_VISIBLE_IDS: 'set_visible_ids.oncoprint' + SET_VISIBLE_ID_ORDER: 'set_visible_ids.oncoprint' }; diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 6a5f067db69..721d028e3f5 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -173,7 +173,7 @@ window.Oncoprint = (function() { return !self.hidden_ids[id]; }); self.visible_inverted_id_order = utils.invert_array(self.visible_id_order); - $(self).trigger(events.SET_VISIBLE_IDS); + $(self).trigger(events.SET_VISIBLE_ID_ORDER); }; self.setIdOrder = function(id_order) { self.id_order = id_order.slice(); @@ -245,17 +245,25 @@ window.Oncoprint = (function() { var cmp_list = _.map(track_id_list, function(track_id) { return self.getTrackSortComparator(track_id); }); + var data = {}; + var id_order = self.getIdOrder(); + _.each(id_order, function(id) { + data[id] = {}; + _.each(track_id_list, function(track_id) { + data[id][track_id] = self.getTrackDatum(track_id, id); + }); + }); var lexicographically_ordered_cmp = function(id1,id2) { var cmp_result = 0; for (var i=0, _len = track_id_list.length; i<_len; i++) { var track_id = track_id_list[i]; var cmp = cmp_list[i]; - var d1 = self.getTrackDatum(track_id, id1); - var d2 = self.getTrackDatum(track_id, id2); + var d1 = data[id1][track_id]; + var d2 = data[id2][track_id]; var d1_undef = (typeof d1 === "undefined"); var d2_undef = (typeof d2 === "undefined"); if (!d1_undef && !d2_undef) { - cmp_result = cmp(self.getTrackDatum(track_id, id1),self.getTrackDatum(track_id, id2)); + cmp_result = cmp(d1, d2); } else if (d1_undef && d2_undef) { cmp_result = 0; } else if (d1_undef) { From b3f436347fe615fb716ae5a994037c1c80f710b9 Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Thu, 27 Aug 2015 18:18:20 -0400 Subject: [PATCH 180/343] changes from implementation --- .../src/js/OncoprintSVGLegendRenderer.js | 44 --------------- .../src/js/OncoprintSVGRenderer.js | 55 +++++++++++++++---- packages/oncoprintjs/src/js/RuleSet.js | 47 ++++++++++------ packages/oncoprintjs/src/js/oncoprint.js | 4 +- 4 files changed, 77 insertions(+), 73 deletions(-) delete mode 100644 packages/oncoprintjs/src/js/OncoprintSVGLegendRenderer.js diff --git a/packages/oncoprintjs/src/js/OncoprintSVGLegendRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGLegendRenderer.js deleted file mode 100644 index 77a4bc7c946..00000000000 --- a/packages/oncoprintjs/src/js/OncoprintSVGLegendRenderer.js +++ /dev/null @@ -1,44 +0,0 @@ -window.OncoprintSVGLegendRenderer = (function() { - var events = oncoprint_events; - var utils = oncoprint_utils; -} -OncoprintSVGRenderer.prototype.resizeLegendSVG = function() { - var new_height = 0; - var new_width = 0; - var point = this.legend_svg.node().createSVGPoint(); - utils.d3SelectChildren(this.legend_svg, 'g').each(function() { - point.x = 0; - point.y = 0; - point = point.matrixTransform(this.getCTM()); - var bbox = this.getBBox(); - new_height = Math.max(new_height, point.y + bbox.height); - new_width = Math.max(new_width, point.x + bbox.width); - }); - this.legend_svg.attr('width', new_width+'px').attr('height', new_height+'px'); - }; - OncoprintSVGRenderer.prototype.renderLegend = function() { - var svg = this.legend_svg; - svg.selectAll('*').remove(); - // - svg.attr('width', 10000+'px').attr('height', 10000+'px'); - // - var padding = 25; - var y = padding; - var rendered = {}; - var cell_width = this.oncoprint.getZoomedCellWidth(); - var self = this; - _.each(this.rule_sets, function(rule_set, track_id) { - var rule_set_id = rule_set.getRuleSetId(); - if (!rendered.hasOwnProperty(rule_set_id)) { - var text = svg.append('text').classed(LEGEND_HEADER_CLASS, true).text(rule_set.getLegendLabel()) - .attr('transform', utils.translate(0,y)); - var group = rule_set.putLegendGroup(svg, cell_width, self.oncoprint.getCellHeight(track_id)); - rendered[rule_set_id] = true; - group.attr('transform', utils.translate(200,y)); - var bounding_box = group.node().getBBox(); - y += bounding_box.height; - y += padding; - } - }); - this.resizeLegendSVG(); - } diff --git a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js index 1bc9a79944a..a163ca44f7b 100644 --- a/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js +++ b/packages/oncoprintjs/src/js/OncoprintSVGRenderer.js @@ -92,7 +92,7 @@ }); var mouseMove, mouseOut; (function() { - var prev_track, prev_cell_index, prev_dom; + var prev_track, prev_cell_index, prev_dom, highlighted_col_cells = []; var column_highlight_timeout; $(self.cell_div.node()).qtip({ content: 'SHARED QTIP', @@ -111,11 +111,11 @@ }); var hover_cell = function(dom) { $('.'+CELL_QTIP_CLASS).finish(); - $(dom.node()).trigger("cell-mouseover"); + $(dom).trigger("cell-mouseover"); }; var unhover_cell = function(dom) { $('.'+CELL_QTIP_CLASS).finish(); - $(dom.node()).trigger("cell-mouseout"); + $(dom).trigger("cell-mouseout"); }; var clear_and_unhover = function() { prev_track = undefined; @@ -123,7 +123,14 @@ prev_dom && unhover_cell(prev_dom); prev_dom = undefined; //self.cell_column_highlight.style('visibility', 'hidden'); - //column_highlight_timeout && clearTimeout(column_highlight_timeout) + column_highlight_timeout && clearTimeout(column_highlight_timeout) + _.each(highlighted_col_cells, function(cell) { + if (cell) { + cell.style.border = ''; + cell.style.margin = ''; + } + }); + highlighted_col_cells = []; }; mouseOut = function() { clear_and_unhover(); @@ -165,10 +172,11 @@ //self.cell_column_highlight.style('visibility', 'hidden'); column_highlight_timeout && clearTimeout(column_highlight_timeout) // not the same cell as before - var track_cell = self.track_cells[track][oncoprint.getVisibleIdOrder()[cell_index]]; + clear_and_unhover(); + var cell_id = oncoprint.getVisibleIdOrder()[cell_index]; + var track_cell = self.track_cells[track][cell_id]; if (!track_cell) { // track doesn't have a cell there - clear_and_unhover(); return; } // otherwise, we're over a cell @@ -178,9 +186,21 @@ prev_dom = track_cell.dom; self.cell_tooltip_html = oncoprint.getTrackTooltip(track)(track_cell.d); hover_cell(prev_dom); - /*column_highlight_timeout = setTimeout(function() { - self.cell_column_highlight.style('left', cell_unit*cell_index + cell_width/2 + 'px').transition().style('visibility', 'visible'); - }, 1000);*/ + column_highlight_timeout = setTimeout(function() { + highlighted_col_cells = _.map(self.track_cells, function(cells, track_id) { + var cell = cells[cell_id].dom; + if (cell) { + if (track_id === track) { + cell.style.border = "1px solid #000000"; + cell.style.margin = "-1px"; + } else { + cell.style.border = "1px solid #999999"; + cell.style.margin = "-1px"; + } + } + return cell; + }); + }, 200); } }; })(); @@ -523,7 +543,7 @@ bound_svg.each(function(d,i) { var dom_cell = this; var id = id_accessor(d); - track_cells[id] = {dom: d3.select(this), d: d}; + track_cells[id] = {dom: this, d: d}; }); bound_svg.selectAll('*').remove(); this.active_rule_set_rules[rule_set.getRuleSetId()][track_id] = rule_set.apply(bound_svg, oncoprint.getFullCellWidth(), oncoprint.getCellHeight(track_id)); @@ -709,12 +729,20 @@ self.clipAndPositionCells(undefined, undefined, true, true); self.cell_div.selectAll('.oncoprint-cell').each(function() { var cell_elt = d3.select(this); + var cell_rect = cell_elt.node().getBoundingClientRect(); + var cell_dim = {width: cell_rect.width, height: cell_rect.height}; var pos = $(cell_elt.node()).offset(); var g = svg.append('g').attr('transform', utils.translate(pos.left - root.left, pos.top - root.top)); cell_elt.selectAll('*').each(function() { utils.appendD3SVGElement(d3.select(this), g); }); + var outline_styles = {color: cell_elt.style('outline-color'), width: cell_elt.style('outline-width')}; + if (outline_styles.color) { + g.append('rect').attr('width', cell_dim.width+'px').attr('height', cell_dim.height+'px') + .style('fill', 'none').style('stroke', outline_styles.color).style('stroke-width', outline_styles.width); + } }); + //styles = {'outline-color':rule_spec.color, 'outline-style':'solid', 'outline-width':'2px'}; self.clipAndPositionCells(undefined, undefined, true); })(); (function addLegend() { @@ -741,6 +769,13 @@ var elt = d3.select(this); var pos = $(elt.node()).offset(); var g = svg.append('g').attr('transform', utils.translate(pos.left - root.left, pos.top - root.top)); + var cell_rect = elt.node().getBoundingClientRect(); + var cell_dim = {width: cell_rect.width, height: cell_rect.height}; + var outline_styles = {color: elt.style('outline-color'), width: elt.style('outline-width')}; + if (outline_styles.color) { + g.append('rect').attr('width', cell_dim.width+'px').attr('height', cell_dim.height+'px') + .style('fill', 'none').style('stroke', outline_styles.color).style('stroke-width', outline_styles.width); + } elt.selectAll('*').each(function() { utils.appendD3SVGElement(d3.select(this), g); }); diff --git a/packages/oncoprintjs/src/js/RuleSet.js b/packages/oncoprintjs/src/js/RuleSet.js index 02bb11ae712..787ce856634 100644 --- a/packages/oncoprintjs/src/js/RuleSet.js +++ b/packages/oncoprintjs/src/js/RuleSet.js @@ -37,19 +37,13 @@ window.oncoprint_RuleSet = (function() { return { condition: condition, shape: utils.makeD3SVGElement('rect'), - attrs: {fill: '#d3d3d3', width: '100%', height:'100%'}, + attrs: {fill: '#eeeeee', width: '100%', height:'100%'}, legend_label: label, children: [{ condition: condition, shape: utils.makeD3SVGElement('path'), attrs: {d: "m 0% 0% L 100% 100%"}, - styles: {'stroke-width':'1px', 'stroke':'#dfdfdf'}, - legend_label: label, - }, { - condition: condition, - shape: utils.makeD3SVGElement('path'), - attrs: {d: "m 100% 0% L 0% 100%"}, - styles: {'stroke-width':'1px', 'stroke':'#dddddd'}, + styles: {'stroke-width':'1px', 'stroke':'#555555'}, legend_label: label, }], }; @@ -120,11 +114,18 @@ window.oncoprint_RuleSet = (function() { D3SVGRuleSet.call(this, params); this.type = CATEGORICAL_COLOR; var self = this; - var d3_colors = _.shuffle(_.filter(d3.scale.category20().range().concat(d3.scale.category20b().range()).concat(d3.scale.category20c().range()), - function(color) { - var rgb = d3.rgb(color); - return !(rgb.r === rgb.g && rgb.g === rgb.b); - })); + var d3_colors = ["#3366cc","#dc3912","#ff9900","#109618", + "#990099","#0099c6","#dd4477","#66aa00", + "#b82e2e","#316395","#994499","#22aa99", + "#aaaa11","#6633cc","#e67300","#8b0707", + "#651067","#329262","#5574a6","#3b3eac", + "#b77322","#16d620","#b91383","#f4359e", + "#9c5935","#a9c413","#2a778d","#668d1c", + "#bea413","#0c5922","#743411"];/*_.shuffle(_.filter(d3.scale.category20().range().concat(d3.scale.category20b().range()).concat(d3.scale.category20c().range()), + function(color) { + var rgb = d3.rgb(color); + return !(rgb.r === rgb.g && rgb.g === rgb.b); + }));*/ var addColorRule = function(color, category) { var colored_rect = utils.makeD3SVGElement('rect').attr('fill', color); var condition = (function(cat) { @@ -166,7 +167,6 @@ window.oncoprint_RuleSet = (function() { } }; self.apply = function(g, cell_width, cell_height) { - var missing_categories = []; g.each(function(d,i) { var category = params.getCategory(d); if (!params.color.hasOwnProperty(category) && category !== "NA") { @@ -461,7 +461,7 @@ window.oncoprint_RuleSet = (function() { var scale = function(x) { if (params.scale === 'log') { - return Math.log10(Math.max(x, 0.1)); + return Math.log10(Math.max(Math.abs(x), 0.1)); } else { return x; } @@ -476,7 +476,7 @@ window.oncoprint_RuleSet = (function() { var scaled_data_range = _.map(data_range, scale); var height_helper = function(d) { var datum = scale(d[params.data_key]); - var distance = (datum-scaled_data_range[0]) / (scaled_data_range[1]-scaled_data_range[0]); + var distance = Math.abs(datum-scaled_data_range[0]) / Math.abs(scaled_data_range[1]-scaled_data_range[0]); return distance * 100; }; var y_function = function(d) { @@ -502,12 +502,22 @@ window.oncoprint_RuleSet = (function() { return [min, max]; }; + this.getEffectiveDataRange = function() { + if (typeof this.data_range === "undefined") { + return this.inferred_data_range; + } else { + var ret = []; + ret[0] = (typeof this.data_range[0] === 'undefined' ? this.inferred_data_range[0] : this.data_range[0]); + ret[1] = (typeof this.data_range[1] === 'undefined' ? this.inferred_data_range[1] : this.data_range[1]); + return ret; + } + }; this.getLegendDiv = function(cell_width, cell_height) { if (!this.showInLegend()) { return; } var div = d3.select(document.createElement('div')); - var data_range = this.data_range || this.inferred_data_range; + var data_range = this.getEffectiveDataRange(); if (!data_range) { return div.node(); } @@ -540,7 +550,8 @@ window.oncoprint_RuleSet = (function() { if (g[0].length === 0) { return; } - this.setUpHelperFunctions(this.data_range || (this.inferred_data_range = this.inferDataRange(g))); + this.inferred_data_range = this.inferDataRange(g); + this.setUpHelperFunctions(this.getEffectiveDataRange()); D3SVGRule.prototype.apply.call(this, g, cell_width, cell_height); }; diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.js index 721d028e3f5..143a2907e96 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.js @@ -31,7 +31,6 @@ window.Oncoprint = (function() { var events = oncoprint_events; var utils = oncoprint_utils; var RuleSet = oncoprint_RuleSet; - var defaults = oncoprint_defaults; var defaultOncoprintConfig = { cell_width: 6, @@ -203,6 +202,9 @@ window.Oncoprint = (function() { }; // Sorting + self.getTopmostTrack = function() { + return (self.track_groups[0].length > 0 ? self.track_groups[0][0] : self.track_groups[1][0]); + }; self.setTrackSortComparator = function(track_id, cmp) { self.tracks[track_id].config.sort_cmp = cmp; }; From c5f5a38fcadbb628c9d5624d202a7c08d9ecd9ad Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Tue, 15 Sep 2015 12:01:46 -0400 Subject: [PATCH 181/343] deleting some legacy and development stuff from repository, and adjusting README to reflect current state of affairs --- packages/oncoprintjs/README.md | 16 +------ .../current_genetic_alteration_design.txt | 24 ---------- packages/oncoprintjs/makefakedata.py | 14 ------ .../oncoprint-naming-conventions.jpg | Bin 22519 -> 0 bytes packages/oncoprintjs/src/html/toolbar.html | 41 ------------------ 5 files changed, 1 insertion(+), 94 deletions(-) delete mode 100644 packages/oncoprintjs/current_genetic_alteration_design.txt delete mode 100644 packages/oncoprintjs/makefakedata.py delete mode 100644 packages/oncoprintjs/oncoprint-naming-conventions.jpg delete mode 100644 packages/oncoprintjs/src/html/toolbar.html diff --git a/packages/oncoprintjs/README.md b/packages/oncoprintjs/README.md index d4f4ef068e3..397c03d6aa2 100644 --- a/packages/oncoprintjs/README.md +++ b/packages/oncoprintjs/README.md @@ -20,21 +20,7 @@ This is not exactly a perfect solution to getting the gulp binary into your `PAT ## Build -Run `gulp spec && gulp` to run unit tests and generate a minified artifact in `dist/prod`. - -## Tests - -Run `gulp spec` for unit tests. - -Run `gulp test` to run both unit tests and to build development environment in `dist/test`. Run your favorite lightweight static resource server from `dist/test`. I use [http-server][http-server] because I've found it to be faster and more reliable than `python -m SimpleHttpServer`. You should be able to navigate your browser to wherever your local server is running ([http://localhost:8080](http://localhost:8080) is likely) and see some sample OncoPrints. - -## Development - -While developing, I recommend using - -```gulp watch``` - -This will run `gulp test` every time a source file is modified. +TODO [nodejs]:https://nodejs.org/ [http-server]:https://github.com/indexzero/http-server diff --git a/packages/oncoprintjs/current_genetic_alteration_design.txt b/packages/oncoprintjs/current_genetic_alteration_design.txt deleted file mode 100644 index ee425586ccb..00000000000 --- a/packages/oncoprintjs/current_genetic_alteration_design.txt +++ /dev/null @@ -1,24 +0,0 @@ -The current oncoprint genetic alteration design settings: - -CNA: -- Amplification -> full red cell -- Deep Deletion -> full blue cell -TODO - Heterozygeous Deletion -> ? -TODO - Gain -> ? - -MUT: -TODO: anything else? -- Missense Mutation -> mid-cell green square -- Truncating Mutation -> mid-cell black square -- Inframe Mutation -> mid-cell brown square -TODO - Frameshift Mutation -> ? - -MRNA: -TODO: anything else? -- mRNA Downregulation -> blue cell outline -- mRNA Upregulation -> red cell outline - -PROTEIN: -TODO: anything else? -- RPPA Upregulation -> upper cell small black uparrow -- RPPA Downregulation -> lower cell small black downarrow diff --git a/packages/oncoprintjs/makefakedata.py b/packages/oncoprintjs/makefakedata.py deleted file mode 100644 index 4c955e14d7b..00000000000 --- a/packages/oncoprintjs/makefakedata.py +++ /dev/null @@ -1,14 +0,0 @@ -from random import random -def samplename(): - return str(random())+str(random())+str(random())+str(random()); - -f = open("fakedata.txt","w") -for i in xrange(4000): - f.write("{\n") - f.write('"attr_id":"GENDER",\n') - f.write('"attr_val":"%s",\n'%("MALE" if random() < 0.5 else "FEMALE")) - f.write('"sample":"%s"\n'%samplename()) - f.write("},\n") -f.close() - - diff --git a/packages/oncoprintjs/oncoprint-naming-conventions.jpg b/packages/oncoprintjs/oncoprint-naming-conventions.jpg deleted file mode 100644 index 452adad64e025bc1b1b7de17308873824dd2929a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22519 zcmeHu1yEewmhQnJ5G=SuH}38pECf$*ch_LS2@;_30D<7{?(U5fBoN#!xI?hVotc{X z?|m}w&8t`Szgz$5T3vnW?Cv_>K5Kt#t#9pq`uVg9V7`%-lLla50RZOt1w1VPk^nLy zA`&73G7=IJ3JUTIG@O@csHkX!*sm~fNQubFNQp>DD8MYV6jV$g5)wLIdL}k@E-o%| zT7F?Z4j~p!E{?zb2n-4e3K}XJ!ONEf9F!!K9RJ71Qzw9h3`+vn2?s+3z+%C`VZl80 z0_4y4i2(C22k>tP3@jWx0wNMJ$_v!z0d<%FEDRhREIb?n0zCZlXrJfn06Z1~HYK|_ z;wu#+Bq}Eyj)2%KWROHv7q05W37FH^IS}Oq9zFpf5j71h9X$gVHxDl#zkuXxDQOv5 zxi@O>-m7b9YH6F8nweWzT3Ne%aCLL{@cj5GC^#hab69v>{Fj8pq_4>-**Up+`2~eV z#nm;nb@dI6P0igsy?y=Afx)54sp%gxvp?tN*VZ>Sx3+h7_kNw8onKsDUEkdP{u?eB z0PbI5J-`1euz$dX^^6M^9v%)J>2J7TVBMb$4htTEk{uCSTm{L<=@k`605Xn5Y*tkl z3W!tn1lQPk;sqX$2ZsfS0@uIkvi)ED zW&h(jU<|(r182Wv!AaMQ(+MOWc5b@$DT%$qco1IpTb0yEgN)J{ZfB`pf82FlJ9+Pf z&2M+w zSHVT0R!T*46T0sUf|zKk12vkad4;T@_zKhE-yShX1s{D~G45YjuPpRJW7B+ahdkJc zmUncjUnzijuEkBTn~V`x)O3l!4K+)n+*@-lP892IKy5K8FfWooBpiclqDM7G&&9hU9gLbLaI6gTBctr`T7NBY4G`a)~$=*9lLKm0r1Q1 z2I>%!EgP~lDHjpJxYsAUnp<(A9NShsqb_hAp2Es?N)KH=B|Ono1S*G*mNd-(2X)P+;rMrLU)n8f)SdUp+=La@fQk4-D9_%G}96fauR(cxfjsY=3X?Hc5S>k9~e2lY=tYcch}9 z9NsYc3&gYvN`X4l&N?H~lVjbWtoe=9n%B$H7>&9?M1JJk&nk=r33S-i*qLUYlm3|B zUy=@Op8(6aEtZArGu!i69>#;ETGCcB{?0K+24VvwOE;I>%6Yl7(g+diL|e?r@Q1^)ENKtA*Ia*H~DuII)S{Q(OJnKh2iecqsMa z3ydVyU&k$ci*O<)n3=TEQgie0$Qy)XaP0bQ|IxUjBZSM!&aTX~N+WQ7@PJC%jndFD zAZGJCmdM%JV}G!Ss|>bBCeF%E-7UrJooBhdX}Uy7T1m4st>vPlfU}P!oMJd#COT-H z_P(oQf2-qyXve1NG(F6(RNQ#}WAB;Hh;C_g0|i8b`w2iE4;S4T8-IKPW=D6l+(5Y6 z9J%Fj$J<_nZY&8e?77L+MEq{9lOB5ROLCup6wxQZ-e9KMpnI9lV@?EW&I>C#JNy3O zllv3!;t9BW#r{xn&~C7N>+afblLPKj@9wv*t~)SGHny5|`mj99y7vT}y{vlz%IWWe zwaT60)^C{CgRHbYu`-nv26y0aY11|^e#1YHy1}+-n@H{(3%yZ$lq3T^0To?OK;|r} z$rHe8aCy%97-aLkl{Md*N7dS0;EN-advjf!M5z%5;#w~Mh((UAGwxWqFfT{^_Ndgn zU!4D<+;#y)<5kGJJ;s)t92k8Z8~)Kf<2>Dy@%w0=jLSo6@)O8S&ZGCi6L9=sTMy^c1ELgq{*+8o#(x zl(x*nCthTb6OI(1%dX@6_ei|SA|zSV0gNGk`rT{irMaOg(s9y}???T-@UdUW%YpHB8;P8d=NZ6~ z2*AX46qXwlRh8o}q64zS|IT2lMU!4Ns`kB0%PG1l=b7qD;^kG>cDr-p{?W+nNMO!S z1jln23Y@b(K_REZEnFaKE@8dD^gC)DR1!h(^{H9wC$} z9=mT#k;hYg{fSQ>BTfj9ghX&j8U=9VpZYvRUXI{PPvk{Oe`Xl0=N6@b0lW5K^Kt|t zUzH@1(KlvDw)`-YozPyNGuF~Uft5!?lV>9dwj$U$*=Uf-U-q-yb4$t+s&aZEU&Fu9 zB;N0JS@I61*RR{k)5Ysb*3M9+9c#N!pk0z+(6Inb`Iv#&A?vvJ+(sa?;bc177;pQB zdGmZRku(i1ltGLzCY{aW7h`3%KbsJ3k-oLtt6?viYxN-ddHiD?iJgKwMd)eSZ@Ns_ z=Y{{zYVc<0d77}fhM$(33O|hEttdvF3D#ra)*Cl-@Xu$V+3<`Uf+_p&Ka88`fy1_6 zpT>!qfk1_$&MF@2znFR6zqFnjw5DP>K{SgIqr5d8$6B>tfSh@5h6A&$4HE|+EX(ZW z^S;K>WxtD)7+UoYc$3x{n3K=`>G-|*lsCVTkQ~dUl{QDAMMAx_UNS{b zzERz#?;9;s*)_4>nOsoBWvf!LJ$H^>hTZx34z=*wQ4?vxj<~^bvdf^ahgYy58)rn+ z^bT{=W20anW%EUIYs@6%$0oyfPJD%J@y1FHQOSK z5eJE_CYl6nIiiSqkv0KKckkO#&euEba*s*KmC6_v#dc7+Y#q_z;JWz8%bXIQ0Q3nw zgIWV9r*KmcmQ5`qj_ESK6L7zT4;RI*!E%oiud77eHmzQ)JL<5t@bYB1>b>(q8 z;Hoo{kTR0=^-PfqZi`}|*1KkUQ4d&{2Q5DVFWt`YgDGiT^jYc7P z)4^5T>2)lqkB=JEa&QH*&ss8|h-7&B38XiTVssZscZ9DlNwI~aP$3=MnHnDwV~;m( z!jS<`ARs<`clRHO>(6oyQ$h7Qc+>SYsa6zF-1BZ~V4%Go^w`3BGs}4@z;AY9J-eb{1Sb6HtN0Zq!WZDcWqQG9qbc*rENUkCM_?sQr~HJlYw?GOWP)uJ{9^ z(%l>Aqo3V#82t4FM0yBE_T{DvCJtRu$C|tI`Kk6=oI`i?>Sh9R^KwV(*%6U@Nj~sp zV!TmD%lZE8N^Z$gaq5Q$0I!Us=%Bcz{3XIR5>7 zKU#s?sD<~#o*T)WkrRzZ8HE)XQ#q66-VUDm-#)O@QJM_i%8v7o`$X!k^bEu;pBn^H zFW|Vpz;*fV{X>7~OVNbJ=-T8A(zv^I8b8Fwe-eWj@HiJ zM~QT+bPSvEtKRrYseej(yZZ_Jq$uR#a7^h~=8XG7%H~aR37M(mHUZ(PP4g?OI=j~H zaJn*dihl^E|LBu2)edJF!xVD0hvRRr%JG(`l;d8C$p0)~Pzq!|Tbr^jnA0NR*zrsn zH6KT+(4BP!-8u;dGrgCa7s#;QDD{5t{DVGY$t^xH48~1o?Ftf!$7(@@7V!QAaPnCQX`M z>70p-5n&VM36&LZMpDqN#Lkb<@BUg%Q{07I4`^CYzLxqa{UwO1OdSmsbIUb(GDl=u zlaSXW)lD^Fq9pD}G5Pi2w&Z!0T!_i05gdUc!KZ*J{t*i(FYGDvWdE|+B2pUZWKb$T z!&{xt%(ZBhz(zqEj{&ZA?4Xs~X?Kk5=jxu8&B!@Pdke;x;_7}m=t&*$raqlXcFmKv zDW)0AiAWC9D2%dG!V!jX$0ehev~fJVr{C@BRA(;Y^LwgTjx&iRW9Nd3m)kf8GL0nf zp{GkD4mc)nwcdWKm&eKNe*y%h2$9I46k@_~pW>s;D^DY@atfhVWNEO3EJ6G>G5zfI z=9E92-&bKsM(L3E#KGic*JrwZyYnp9w;|@B3X$ou(h96vbmV_)5;Zzb)vzD`!&{Ff z!`%zbQM;)j7KGJ$XWHZG*JdaUi@|m#d)MZ<#zl^4@T>X4X(XG`lX)wCNVObB#i2Ho zROXl@%Jb!+7^xkOiWtJ5#?kyqky%FmVj>) zUY1)-DaUJYl)5-A8WFvB)4PD!9^5YY9qzJ!2SU=&i*bwp>al(8Ih`&p)4mCK0;tyg zW*0?&T zlknlVCm%;Tsc^(+^vots*keMd+Dq6o5V}{`&{Ic38cBKrBr0@I&?~te;CXadN$g_x zVqBhpXv9nmg%gMSu-^s`PUIuAtcBPeSlp^5O~J*O!isp!(TGpLKt|qkJ%x2Y>Vn^K zrgACz`zrdD`NP^HQO4SX%3Sb`^%D?!Df4Z2nRM3n-h6(sD&GDe3wrJ7YpkSn?Cbig z9cD-M6#v1y;&D`WG%>lj&&h;ylp+v0fkqtkaxCw@rv_f+OdP3b+!pQ7n=wSk!&TE< zwYqD3}pPq9DYrlvK8I_akM;Qgd$L8pL1F6}u3=F@`4^ticg^zU?dTxdt zfzhe!ez$~56TY&~(#yVx_kSk9NK?LxPlZWGv`I9;y>Y6YE1NL4(VTpr2o4xe3@;vy z5qMcmOLo&Qjids$;!?s1CnSASoxXZmu0OQUZ7x$?Kb*XFw%{FCY5IZ0!6Zr*7nB~3 zym|)X;ZgBM0=|q3WdH-rR1(vrX+bbl!;s+;1;r6vqxpVpuTZ=o$6cR=T|0TJ0I3Xn z#|I^O^J$?&GQvGYBQ$qfWY87;_IOen3qK}HZ(vLe$=a-*1Y-I-#kDLtw0(s+p;qoH z!6J4-{*ooGYFw%HG@}=$vk%Y^5k9&8Y1)c!`(36kd4@ww9X{kYW?9q@LBB^Cs=`Oq zL$d>laMdCbh=mzLsmc|bZj_@l=i#n+am89RhP-Y45yTk2v*c>fSNUC0q^~bNAu$r! z^aA}ozj;z~Uuh{e_OM`LLLffnz&rlUICLCmr83~dyZ5LH=H0(H7rHnJ{h3TG=a2zP zel#SjSLY=cfQl5(pTt}wp#n1VrjZ*RQ8>*uJS|@QLIDf|CZLgiV3}w0GzHkREO*=K z|L9Yn!vDIZ$xMB1~UImiIQ(dd6izGmo0l(N&;{FWyXA9Ij-Z5niLM^zJ?7iZc@ z9T?CJfyDcBAgPHFCwwd3t^Cz!5xee0EY$>?@g>22_3Q;bQsH5E)NF5x*>a?Nap^c| zTly1FhmvFJ@dB;V6+{s*{ymV7TR5b~q{wo@`nw|D*lX5p`7>KMilgx*ia_*_hTq|d z6a(yXZO?KO)nB`+QNjnF5t2M;eICEdt}Bj;E}65LH`R_xV@sJ-cOvn}B(}i?dzmTd zKNnnmm8Vi6d4+NoHZSiyo#n6^d7P1XrTa8dWak~W>~j^j3^;Qu1*hU7=4}&5Ui47skV-<`aN}8&ZbFAZBIJ1*#ZfMCxvcv(l&W=d+yqHsop)h+!LDh&yfX6Slf? z`|6_O_qu3ux$*Vm8SV@)aYwRaZ=DQKc6*1uUF=m}T; zKzL!EVT~-8iyzU>^%qV&?&_d$i z=m|-sZIYfffz!G9*WFk&yF%wR3_@RrATH;K6i(0~1y}4pq=*&5^=&O$wzlQ^2MeB} zd&=dD$RYy^*A*)(I~GiQgK+!|8~bv!l-+8f<7Rl`?_)2cRdG^kYccG1Lg zwVP;$qVVThoZ23fl$OjEX?Z%zW}fLZkP#D~&M4%bF9$t1tA$`a{2D{(L!OR=zZc-Kw2zcBRRQ<7BFGy z%M?K&+ewR_35}&Cq)q?YFL3x`#*3L~d3nuk=vxEYrypUzgd+jVi9ZSTYD6t*=?g~{ zSU8Y}5vx7UH8nO=+t4FG3Jz^|^+t@|ni_?zJmMa_Q$|W1Uu*k#u6!CimR$Txz^*r` z@Tv+ZLKwjN4%sX}z!9_9?#WZWbMRS8+$~-mtIEQy0Z}sQyI25({at|BD zj;iU)GJLH-;ugVV#4NO``RavuN)b~J<4(-|4)~1A$Q1lv~f(Iw12Y8K; zWj|?cbF1XB|9Tlh>m|xyPI7HfJ5Lq+nsFYPikJ`BxbU){vOG<3!rd z7Mh+8NLYYqEi+13qaRNW1sklQQ&N3l9u8Lj9XKTIHPg`t_|>ipx86Ph7nF)O8hJi7 z8jVe6x5kk33*zWs_~+$4nZKjE{4tQM3^8s^GNxzQpSg~j{5d96SUv%9vr7L{22U03 zQM(r{7G&#qX>jsbI8k_y@B~nbV)@MuJpl(lsAX1JZ&7X{9^0SwZ}aTvlj4u8qln}; z+&=h^Um(X2(KEQ}XPjmCC4G$ZkI#+30rcU==7zHhoQjMQ5$;`|4x}fbdHQ)(??&aU z9p6W#xIeg(&$LHWq=}F{kKQZrn^k$R*=;`P@RJ&+^tccmFj1Thy(@{2WUM^y=0sj`2u#5`coH0z4**jhSFMz9I9-GRlJyNForT~ z?(`)g!QIA0vU+*ztX6D*=y`p4-h7b&lOG2)$`yCx&KBx0^ zL`#O8$=aG4gFaV&DZhOyElm=cL%sF#M`rqAAFe4n zBkur)?0m$pJbIMEe@!ySd%CLI8!610As=D#a)i@t!E!9nYJ zP2Ior$=mDd)tDEVs%5K;lusKsxDWA89@6LO-0jc4(>Tq-;qLfSJQu8j4!`dr{#MM& zWwSv(gd^aRMn^x=`3*?0{yr9c3}S4$QOJEr>TYm{G+G(kk{6Djq3AmFdJyfX*9C~& zC#h!M>);-=lUM4Ac2)J3G<-n2xdKH`;Yten{ZC8X$Cpn)tmt!QQx3XK*=F_I%=zVa zc}RYyz1MTkE!y)trv~-p#}k0@E0XAsX`AXhU)X2r%lQ1yFZ6(I#!bhI!aYy!%dqiM;rc&B4|*p2NdMUY3lU zN=7DZO~m$$|7VxDvQ`+}rhXbbR86~98PS`FIk;8>OXk(B#tW4VtF$ll<3pKoINs3U z&f-ZA4e1nu^|%>n%^4~3J#5o3_Ez?##IYY^E|c|^nP@I+KBN(NwdP}vL{`X(a!jxdh}~%@)za_ed8-RLIyZ8L}6c(TMaqdU~f6F z9kKO{peClD<3}Q3;SUnfKLkdbY%8eTgdG!+I7pm8V%3DEwRyi=1l025keksH7i1ZK z7)`Gi#t`z#9w|PFkO1EB!6pZTuH3T0ro@--SN9NOI)~e~`n}o%dVS6b{(aEB)DvL9 z_yp9b+b66=6V%6=oBm+N8lihLOt{>NPQ10hf2_wEbT8zVO>Ahq;wC$t92cx3q57VZegfl$pyo3xF6&7~?5|5j<{xga4WxU^)M=e-| z0{f2^bxs1l8D1|T8qRglpSe=_m5HivjlYM2eGVTjmAx)6x#veI!%@I+)HwB=kW~#- zj40Nl^q)NliRKTp3EM}AlHAP}A2%_xjTI+dqH;2UiMHszm*^qP_v754pyg&RFRcdJ zugJz2ODhB8`pI3Thms1XrYW!M%cz$I(mRo`hF^5dYxAISI0{ud#RyAtaH_}myT=d3 z=*-QxR`am$u%C&@ea-b}#kfn)IP@~@eB^FxNn9qDWnPPOiwv@3*)t)xYJi05I+B`$ z$={M^BDPl{EdHKE{w@$yZ`lC6LuistO+8Huj5I^%5^-OF*-J-cM&9JuZv>X>d3FkZ^mk@IZ1r>-IB%~-c2SH*si(3r^vjpfFm9!T4h$vu`g}c!Y1y5c!DjLVCBC%`VGsDLOq9$B zeS;UjRDNZfCpij!&A*P=I%g}?A#s2TD*=v-);2mdWpeKJn5;kFs??AtjV#5+&f<

lvw`HQP zrHxfRd|fU-`C!wA!%T{O5sPC@wqHbAe4IQ=L7(WM_Yv!HsX;SBqI%#Y@Xc_|$HSKk z+rXwh2_^@+btE*B#nvp@o@q7y;I8#%^-D77#cptTY3X%X zvTc693n%?2-Wvcv&x_YNNI;YLUyD&N@v@YGa_DT~IvGZq87E15^!ZRPraqPFbes*! zSmZ&tn_QLnsHXLLmMohH{xuooN$CNC^3`031tDQ6@&{J*1)Lz+N27p{%a&NN_w1n{ z8z87Mny=qa0H|)K3DMDM`COJq@8E*aaf~?nX!$v$i;w_23V9qi-fZ0t(6z>|rU+lB z#Eeat>(q*))inja&2{c_R#xrxPZLRpnOQMmft?nPg9h@==gSDh$+728I-Q4V%;*HP4(q zGm+%i7pU2xX!sm&r~1%hzb1R9oqLKkK}kYDvkr|p8mD^sNu=Lp^V2<)5(_bdJlW7Y zZ$K1mv=xN=f%K-8C-=Wo+EI0rl!~a1SOlH#s$SBYZ0dgK#vB<2i)Cq)|olw8ZlRN zJ(=9j-fe87%ZOf$b||T+{K6AZ=-c_m6-l~L-CHfm$yj3R*zw`S@fdPC=Ji7U?j@2= za^x?aMIp*e!$}hdhJ##-Dk+i0-zh+;kWQ{%@+96XpZE_oB}E9>9FmprrU{0KeEfUr zw)bjz2G$vKRd4s*qc2sZ-|bj>yR29}*SXV<@pHRwZb>yGH}y>+GG7UoVsgxK8JZ|8 zz;T%t+{VkpiVE6Rnb@eAaI)~xHL;bXO>-?MkPRVa#DxDr68oQp+yD31H8@N32g$>X z>aEg@oM3~oIRN7EWpPyw-`D%8zFaURoGWj3}4$xW~ma8WX@5M%5q`4ruBESKzZa%iZ ztIE4D6x>V8dKTwN&Fbm{IB+r6;F#~J5M~d-F2JaNLrON}f-_7#n6M4s z3*%_y(QCSMv~|Aw(S6i%?<3RU+P8hw&iYCHx-KKw;IrPKRezM{C>Q8?WP`5bKvGvQ z>bOT?Eb2^U4ld!d<)oV@?8ZvXpd-Olbht^M>g9F*`r#B@e>T^uN>xqeVCIyXIPSWE z)G*Sz3De+25>CQA#XeZ@45Wkoeo7jX^(!Bfit>fXQN#lT_kpL_(*YfrN>ID#H7vub&lv>;PEJ_nT)yR!O*f?b{ngtlQt*7Cd* znm7<;xO8g1qT7y3uOf!YY@Wp}8TT-~=TIeNptGk1Vy5gsDC{h!2y+KT^rtZZ?5fhI z4o`8?`BgS;R3~Y!5<(NV29<@@1>>V~XqqEwm59`nUxp8EgZbYXHwrpCWvD^ST2j+5 zM{S6tn+mlof9ZSD$~z}15$Afu)G9P3f&Jhv#!xPbn3-GOlQjV8>uSbv96PqQM4~*p z4uYTT>9-7nMMFnp(3}5^X+(D7JW3jwrb%oJX6X=$E`;Qx>ATv$Yp48Ud*wenhogz< zgO7uW3)dm+cVrBWSVxPaPWsJU6=xG1{!Yi=5mmZL?OKP-Tye%et=jEc@l6h{YA;$a z=|c(HO^p(SEse#*qbE5uZlo%xGbRxZp573JtV0{<_am})yHe2`%@Z4BlBdd5<5_D) z`U;oO2{%lU;eECJ1l&{eGOlHn5ECqh&68HWEwA{YT%^~t?Gphxjh`i13hlkZZ-AZY z8oAcVL8N+ti-w9VM`9%DOM#A3AkrxFd3_R8zpUfw=^g`m@p&<_HO^E8@+e#g_d=9p zoI#59Csi02>r1$nY@))LiCsyQ8%GWA9iPy<#B|1%#%ZqI^7cOS@`ZxvvEi?yE%J@_ zaGOY$%+QhC_rt72+Io7e9P@f&SC_c zw*mE0iIEM)+y>$nRSUv34pRfj=yBgZSm6BJxSsNqCCw!}QX4NtUXAK?3l+FUkf9K8 z9W>j?Jcf|GPwH=GqHo*!%5qBfmI?78FWlu`j1gPVWEKbLDYCY4ayI^u^dL0Xhx5t0 zkfD?yhQsOHX@wg%I{e7qTT~=R_=}2c_ELiPRF%4!^l<$$?6F{!06o{Pw`(5vM7TR9 z6r9DJJ1AnEmLaML!vbvnK7r{i=$~f`Gc|8~?J@b96^_nu^Po$}dqFeWP_7R!<&Dml zjzJaloTnT9Y?zwN!ZyXSn?zD^BtD+wzRNXpJ3F&+1i>@-)rjpcPN<0vkvwa-M7zqQ@4QPqaFG+h5`TW;I=pSVB zKgni;|G=Vd1`Vxkskp4~-O6RI*U(!wLg+EPAL>ObTHVOAnh+zLNx@4tDU|cGvy5Gm z>+tqZt-nH>+i3#s+=^xuYW2ickj0uPKGAF}(g|6RV)-%DNdczhXqJZvz04NPf{@$r z$)+*+RM%DC(;Kq(?hKo?Lyoc4ad-|CS;w)dfxb`hYN=J@SeZNd%k0`;lo7ew2`lYb z zEgJ;{(J03Ad7o5)6kt5M=EFT{KyMZ%D>t#9a_N;ve_3KC;Y2SN+9``E5>C=PGpEfpZ`c0R!Dv9MfJBofi*uQ(Xclw^m- zpE4dQz}?)kC{)?o;#SiyeNeu=Q8Va=wwP&y?~JwBIOj7jCdyl9&7 zK3d4lOc;5DH$^Fw`Ea{E4Wg3oK8QOV0YO|uoEevJM$$#wfx|k_Nec_WqWpH5%xySjNp{a4 z_JOYeJB?Iw6k{)&*v&VRj$Pj0Lb-`8)5)h4XP|Dv%l8k9)eC-FBf%xve5CT^hq0DB^{1ee;U08JlGv-U@>iZV90{T%{+Z z-6n05QI@j)xguFOkNTO{mT6bANVT-|eE?FU8R(=`{d+4nLa*WgTD&YQoD0a_5yyh~ z^B9)1Pt(4Ro8y9{Lxe)Re9LfxU1v>|IsdgejX%@>&BFPUeiw$cmuh`pQ=S6_Gs~0@ z1R_M5X`*XI!cXE$S)Xo=X|>wY3VO{HlPTy$5}VA?Bua z^OCval$wtwTSd1n*A1o@MHQ`NvmbwqWhcc-o0~cj>m(s0Z4z4+E!Ej80ptBRlEOe(pJ1u51dW*147fKFdqqF1SocSCfIu=>NRY3UHQPLXsl>KqR zoyJ#t_4i0NM`R{Rg4J6(@TbB0B6Z0xDLCGS;Trcxf%guf^|z>`lVD>k0x~u4QcCs@ z==etPae0bz_MYnoHxfH@951DpxtbFBEi@0V0#!{o4sIXyH9w{9%vb1W|4K}!Tfmm( z@(E(tf|Yi}AW5Wk^FlMd?=?iTDZ#*fv&SW|s`pyQr8VZuW=uadJl2^bFV+}mlpIjS z>u+S^K!y6vW4hDO@G8I`kuTmkvIpZXf#MvJQO4ZizoDSKtfw#Km@f``ZC6!Q4$bv*cS|4VPAt*V4PVVP9k=J&SwUgxxl*8^p(T{b}zhRG8b!THW!w z`O0!aB(9PzOe3cpDMP+58-ZO`(rs)cChfUwj4Wrd6cDw}suC!}#`w*_CQab@<+|N} z%5)6z?O3JkgugC5V|p?acW35NsI^(1*rTsSo@2o?zI!!h#E9iZdarCx6r#XRz%J+i zo`~Lxapopz30B-yI)gX?Z13D@05Sq5>13PeiLvT4Y zTdldtwyDGK- z1`~)#YowBW`i?DP9mh&0=&8h{shx>E<3?>#cc9c`$m zMRxo=v?KD6sG{yxa3b{fms6!u>*O0J92FSvi(A~~bw7s3YQ?v*)g3|8bYQ&>4spzs z8yD@FcANav+ibd{io*CQzaZI}h~q`Y=$OD00OvFt?esPG)I0$#2Fj`QYLKsC0nC)A zd(g>GVlg5a)o6Rg2lR??wl%ihfz>PNR$kmr=rSI?+=~14{MXe`2J{ulbdM_9@S}_4 zm%Hu_c=^9{hPe_okQ_#^$P|J7t=n>>JAz~NoYOo=1ADf(BZWttS;D%b9wCjRiWoRm zOv(1sepkvuU!q2RbMTLBp~^WO&jT5C$~@IfFM?=(ifw;}-ABge>AJegBt=c!95fe! z+la3-Z@;NLuPOXK`BAwRs@b*l!N{5*o znVVra{juKvkU=WM<{LNoR;FZ^xmO>n+xN1?u2~}9K4GzuIvO1JvBlRlPYSS*%(AM~ zqaIPuGM7d#Yipzs_%u8Dymhg?Jt5~$vj6u_n(h7Rl%l`p>7QRR`0E+}^^E`Wfxmp< cFCX~J2mbPbzkJ{?ANb1${z-iR=4tM~0fWW~^8f$< diff --git a/packages/oncoprintjs/src/html/toolbar.html b/packages/oncoprintjs/src/html/toolbar.html deleted file mode 100644 index 5f1e74a33a9..00000000000 --- a/packages/oncoprintjs/src/html/toolbar.html +++ /dev/null @@ -1,41 +0,0 @@ -

- -
- - -
- - - - - -
- - - -
- - - -
\ No newline at end of file From 0ea3367f48a5f5821380942e8ed021bb0ba5274e Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Tue, 15 Sep 2015 12:02:31 -0400 Subject: [PATCH 182/343] Deleting more legacy stuff --- .../oncoprintjs/src/.js/D3SVGCellRenderer.js | 632 -------------- .../oncoprintjs/src/.js/OncoprintToolbar.js | 45 - .../oncoprintjs/src/.js/ReadOnlyObject.js | 50 -- packages/oncoprintjs/src/.js/RuleSet.js | 469 ----------- packages/oncoprintjs/src/.js/Toolbar.js | 16 - packages/oncoprintjs/src/.js/events.js | 49 -- packages/oncoprintjs/src/.js/globals.js | 1 - packages/oncoprintjs/src/.js/main.js | 183 ---- packages/oncoprintjs/src/.js/oncoprint.js | 783 ------------------ packages/oncoprintjs/src/.js/renderers.js | 114 --- .../oncoprintjs/src/.js/rendering_engine.js | 178 ---- packages/oncoprintjs/src/.js/signals.js | 32 - packages/oncoprintjs/src/.js/sorting.js | 51 -- packages/oncoprintjs/src/.js/track.js | 159 ---- packages/oncoprintjs/src/.js/utils.js | 160 ---- packages/oncoprintjs/src/.js/utils2.js | 113 --- 16 files changed, 3035 deletions(-) delete mode 100644 packages/oncoprintjs/src/.js/D3SVGCellRenderer.js delete mode 100644 packages/oncoprintjs/src/.js/OncoprintToolbar.js delete mode 100644 packages/oncoprintjs/src/.js/ReadOnlyObject.js delete mode 100644 packages/oncoprintjs/src/.js/RuleSet.js delete mode 100644 packages/oncoprintjs/src/.js/Toolbar.js delete mode 100644 packages/oncoprintjs/src/.js/events.js delete mode 100644 packages/oncoprintjs/src/.js/globals.js delete mode 100644 packages/oncoprintjs/src/.js/main.js delete mode 100644 packages/oncoprintjs/src/.js/oncoprint.js delete mode 100644 packages/oncoprintjs/src/.js/renderers.js delete mode 100644 packages/oncoprintjs/src/.js/rendering_engine.js delete mode 100644 packages/oncoprintjs/src/.js/signals.js delete mode 100644 packages/oncoprintjs/src/.js/sorting.js delete mode 100644 packages/oncoprintjs/src/.js/track.js delete mode 100644 packages/oncoprintjs/src/.js/utils.js delete mode 100644 packages/oncoprintjs/src/.js/utils2.js diff --git a/packages/oncoprintjs/src/.js/D3SVGCellRenderer.js b/packages/oncoprintjs/src/.js/D3SVGCellRenderer.js deleted file mode 100644 index eb108708467..00000000000 --- a/packages/oncoprintjs/src/.js/D3SVGCellRenderer.js +++ /dev/null @@ -1,632 +0,0 @@ -/* - * Copyright (c) 2015 Memorial Sloan-Kettering Cancer Center. - * - * This library is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS - * FOR A PARTICULAR PURPOSE. The software and documentation provided hereunder - * is on an "as is" basis, and Memorial Sloan-Kettering Cancer Center has no - * obligations to provide maintenance, support, updates, enhancements or - * modifications. In no event shall Memorial Sloan-Kettering Cancer Center be - * liable to any party for direct, indirect, special, incidental or - * consequential damages, including lost profits, arising out of the use of this - * software and its documentation, even if Memorial Sloan-Kettering Cancer - * Center has been advised of the possibility of such damage. - */ - -/* - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -var utils = require('./utils'); -var $ = require('jquery'); -var _ = require('underscore'); -var events = require('./events'); -var signals = require('./signals'); -var globals = require('./globals'); - -function D3SVGRule() { - this.exclude_from_legend; - - var percentToPx = function(attr_val, attr_name, cell_width, cell_height) { - // convert a percentage to a local pixel coordinate - var width_like = ['width', 'x']; - var height_like = ['height', 'y']; - attr_val = parseFloat(attr_val, 10)/100; - if (width_like.indexOf(attr_name) > -1) { - attr_val = attr_val*cell_width; - } else if (height_like.indexOf(attr_name) > -1) { - attr_val = attr_val*cell_height; - } - return attr_val+''; - }; - - this.apply = function(d3_g_selection, cell_width, cell_height) { - var shape = this.shape; - var elts = utils.appendD3SVGElement(shape, d3_g_selection); - var attrs = this.attrs || {}; - attrs.width = attrs.width || '100%'; - attrs.height = attrs.height || '100%'; - _.each(attrs, function(val, key) { - elts.attr(key, function(d,i) { - var curr_val = val; - if (typeof curr_val === 'function') { - curr_val = curr_val(d,i); - } - if (typeof curr_val === 'string' && curr_val.indexOf('%') > -1) { - curr_val = percentToPx(curr_val, key, cell_width, cell_height); - } - return curr_val; - }); - }); - } - this.filterData = function(d3_data) { - return d3_data.filter(this.condition || function(d) { return true; }); - }; - -} - -function D3SVGLinearGradientRule(condition, d3_shape, data_key, data_range, color_range, z_index, rule_id, exclude_from_legend) { - var self = this; - self.rule_id = rule_id; - self.condition = condition; - self.shape = d3_shape; - self.exclude_from_legend = exclude_from_legend; - - var fill_function = (function(_data_range, _color_range) { - return function(d) { - var datum = d[data_key]; - distance = (datum - _data_range[0]) / (_data_range[1] - _data_range[0]); - _color_range = [d3.rgb(_color_range[0]).toString(), d3.rgb(_color_range[1]).toString()]; - return utils.lin_interp(distance, _color_range[0], _color_range[1]); - } - })(data_range, color_range); - self.attrs = { - fill: fill_function - }; - self.z_index = z_index; - - self.getLegendGroup = (function(_data_range, _color_range) { - return function() { - var group = utils.makeD3SVGElement('g'); - var gradient_id = 'gradient'+self.rule_id; - - var gradient = group.append('svg:defs').append('svg:linearGradient') - .attr('id', gradient_id) - .attr('x1', '0%').attr('y1', '0%') - .attr('x2', '100%').attr('y2', '0%') - .attr('spreadMethod', 'pad'); - gradient.append('svg:stop') - .attr('offset', '0%') - .attr('stop-color', _color_range[0]) - .attr('stop-opacity', 1); - gradient.append('svg:stop') - .attr('offset', '100%') - .attr('stop-color', _color_range[1]) - .attr('stop-opacity', 1); - - group.append('text').text(_data_range[0]).attr('alignment-baseline', 'hanging'); - group.append('rect') - .attr('width', '100px').attr('height', '20px') - .style('fill', 'url(#'+gradient_id+')'); - group.append('text').text(_data_range[1]).attr('alignment-baseline', 'hanging'); - - return group; - }; - })(data_range, color_range); -}; -D3SVGLinearGradientRule.prototype = new D3SVGRule(); -D3SVGLinearGradientRule.prototype.constructor=D3SVGLinearGradientRule; - - -function D3SVGStaticRule(condition, d3_shape, attrs, z_index, rule_id, legend_label, exclude_from_legend) { - var self = this; - self.rule_id = rule_id; - self.condition = condition; - self.shape = d3_shape; - self.attrs = attrs; - self.z_index = z_index; - self.legend_label = legend_label; - self.exclude_from_legend = exclude_from_legend; - - self.getLegendGroup = function(cell_width, cell_height) { - var group = utils.makeD3SVGElement('g'); - if (self.legend_label) { - group.append('text').text(self.legend_label) - .attr('alignment-baseline', 'hanging'); - } - var g = group.append('g'); - self.apply(g, cell_width, cell_height); - return group; - }; -}; -D3SVGStaticRule.prototype = new D3SVGRule(); -D3SVGStaticRule.prototype.constructor = D3SVGStaticRule; - -function D3SVGRuleset(track_config) { - var self = this; - self.rule_map = {}; - self.track_config = track_config; - - self.addStaticRule = function(condition, d3_shape, attrs, z_index, legend_label, exclude_from_legend) { - var rule_id = Object.keys(self.rule_map).length; - attrs = attrs || {}; - if (z_index === undefined) { - z_index = rule_id; - } - self.rule_map[rule_id] = new D3SVGStaticRule(condition, d3_shape, attrs, z_index, rule_id, legend_label, exclude_from_legend); - globals.rulesvgs = globals.rulesvgs || []; - globals.rulesvgs.push(self.rule_map[rule_id].getLegendGroup(10, 20)); - return rule_id; - }; - - self.addLinearGradientRule = function(condition, d3_shape, data_key, data_range, color_range, z_index, exclude_from_legend) { - var rule_id = Object.keys(self.rule_map).length; - if (z_index === undefined) { - z_index = rule_id; - } - self.rule_map[rule_id] = new D3SVGLinearGradientRule(condition, d3_shape, data_key, data_range, color_range, z_index, rule_id, exclude_from_legend); - globals.rulesvgs = globals.rulesvgs || []; - globals.rulesvgs.push(self.rule_map[rule_id].getLegendGroup(10, 20)); - return rule_id; - }; - - self.removeRule = function(rule_id) { - delete self.rule_map[rule_id]; - }; - - self.getRule = function(rule_id) { - return self.rule_map[rule_id]; - }; - - var filterG = function(rule, d3_g_selection, d3_data) { - return d3_g_selection.data(rule.filterData(d3_data), self.track_config.get('datum_id')); - }; - - var getOrderedRules = function() { - // returns a list of rules to render in order of z_index - return _.map( - _.sortBy(Object.keys(self.rule_map), function(x) { return self.rule_map[x].z_index;}), - function(x) { return self.rule_map[x]; } - ); - }; - - self.apply = function(d3_g_selection, d3_data) { - var rules = getOrderedRules(); - _.each(rules, function(rule) { - rule.apply(filterG(rule, d3_g_selection, d3_data), self.track_config.get('cell_width'), self.track_config.get('cell_height')); - }); - }; - - self.fromJSON = function(json_rules) { - self.rule_map = {}; - _.each(json_rules, function(rule) { - self.addRule(rule.condition, rule.d3_shape, rule.attrs, rule.z_index); - }); - }; - - self.getLegendMap = function(d3_data, only_active) { - // returns map from rule_id to g element - // if only_active is true, then only give back the rules that are used at least once - var legend_map = {}; - _.each(getOrderedRules(), function(rule) { - if ((!only_active || filterData(rule, d3_data).length > 0) && !rule.exclude_from_legend) { - legend_map[rule.rule_id] = rule.legend_g; - } - }); - return legend_map; - }; -} - -function D3SVGCellRenderer(data, track_config) { - var self = this; - self.track_config = track_config; - self.rule_set = new D3SVGRuleset(self.track_config); - self.data = data; - self.cell_area; - self.svg; - self.g; - - self.getLegendMap = function(only_active) { - return self.rule_set.getLegendMap(only_active, self.data); - }; - - self.setRuleset = function(json_rules) { - self.rule_set.fromJSON(json_rules); - $(self).trigger(events.UPDATE_RENDER_RULES); - }; - - self.addRule = function(params) { - var rule_id = self.rule_set.addRule(params.condition, params.d3_shape, params.attrs, params.z_index); - updateCells(); - $(self).trigger(events.UPDATE_RENDER_RULES); - return rule_id; - }; - - self.addStaticRule = function(params) { - var rule_id = self.rule_set.addStaticRule(params.condition, params.d3_shape, params.attrs, params.z_index, params.legend_label, params.exclude_from_legend); - updateCells(); - $(self).trigger(events.UPDATE_RENDER_RULES); - return rule_id; - }; - - self.addLinearGradientRule = function(params) { - var rule_id = self.rule_set.addLinearGradientRule(params.condition, params.d3_shape, params.data_key, params.data_range, params.color_range, params.z_index, params.exclude_from_legend); - updateCells(); - $(self).trigger(events.UPDATE_RENDER_RULES); - return rule_id; - }; - - self.removeRule = function(rule_id) { - self.rule_set.removeRule(rule_id); - updateCells(); - $(self).trigger(events.UPDATE_RENDER_RULES); - }; - - self.init = function(cell_area) { - self.cell_area = cell_area; - - self.cell_area.selectAll('*').remove(); - self.svg = self.cell_area.append('svg'); - updateCellArea(); - - self.g = self.svg.selectAll('g').data(self.data, self.track_config.get('datum_id')).enter().append('g').classed('cell', true); - updateCells(); - }; - - var updateCellArea = function() { - self.svg.attr('width', self.track_config.get('pre_track_padding') + (self.track_config.get('cell_width') + self.track_config.get('cell_padding'))*self.track_config.get('id_order').length) - .attr('height', self.track_config.get('track_height')); - }; - - var updateCells = function() { - // enter/exit as necessary - var g_attached = self.svg.selectAll('g.cell').data(self.data, self.track_config.get('datum_id')); - g_attached.enter().append('g').classed('cell', true); - g_attached.exit().remove(); - self.g = self.svg.selectAll('g').data(self.data, self.track_config.get('datum_id')) - var id_order = utils.invert_array(self.track_config.get('id_order')); - var g_target; - if (self.track_config.get('transition') > 0) { - g_target = self.g.transition().duration(self.track_config.get('transition')); - } else { - g_target = self.g; - } - g_target.attr('transform', function(d,i) { - - return utils.translate(self.track_config.get('pre_track_padding') + id_order[self.track_config.get('datum_id')(d)]*(self.track_config.get('cell_width') + self.track_config.get('cell_padding')), 0); - }); - - drawCells(); - }; - - var drawCells = function() { - self.g.selectAll('*').remove(); - self.rule_set.apply(self.g, self.data); - drawHitZones(); - }; - - var drawHitZones = function() { - self.g.selectAll('rect.hit').remove(); - var hits = self.g.append('rect').classed('hit', true) - .attr('width', self.track_config.get('cell_width')) - .attr('height', self.track_config.get('cell_height')) - .attr('stroke', 'rgba(0,0,0,0)') - .attr('fill', 'rgba(0,0,0,0)'); - // bind events - var eventData = function(d, i, ctx) { - return {datum: d, index: i, g:d3.select(ctx.parentNode)}; - } - hits.on('click', function(d, i){ - $(self).trigger('cell_click.oncoprint', eventData(d,i,this)); - }).on('mouseenter', function(d,i) { - $(self).trigger('cell_mouseenter.oncoprint', eventData(d,i,this)); - }).on('mouseleave', function(d,i) { - $(self).trigger('cell_mouseleave.oncoprint', eventData(d,i,this)); - }); - }; - - self.useTemplate = function(templName, params) { - // TODO - // (1) make sure these are the params you want to pass in - // (1a) Question: for genetic alteration, should we pass in design configurations like color? - // (2) implement - - - // DEF: data accessor = string: datum member which holds the category - // | function: takes in datum, outputs value - if (templName === 'categorical_color') { - // params: - map from category to color - // - data accessor - _.each(params.color, function(color, category) { - var rect = utils.makeD3SVGElement('rect'); - rect.attr('fill', color); - var condition = (function(cat) { - return function(d) { - return d.attr_val === cat; - }; - })(category); - self.addStaticRule({ condition: condition, - d3_shape: rect, - legend_label: category - }); - }); - } else if (templName === 'continuous_color') { - // params: - data accessor - // - endpoints of the value range - // - endpoints of the gradient (in same order) - var rect = utils.makeD3SVGElement('rect'); - self.addLinearGradientRule({ - d3_shape: rect, - data_key: params.data_key, - data_range: params.data_range, - color_range: params.color_range - }); - } else if (templName === 'heat_map') { - // params: - data accessor - // - endpoints of the value range - - } else if (templName === 'bar_chart') { - // params: - data accessor - // - endpoints of the value range - // - color: string or function of datum - // - scale - /* - var rect = utils.makeD3SVGElement('rect'); - var range = params.range.slice(); - var effective_range = params.range.slice(); - var _data = params.data; - var data = params.data; - if (params.log_scale) { - if (range[0] <= 0 || range[1] <= 0) { - utils.warn("Using log scale with range that includes a number <= 0", "Bar chart template"); - } - effective_range[0] = Math.log(range[0]); - effective_range[1] = Math.log(range[1]); - data = function(d) { - return Math.log(_data(d)); - } - } - var range_len = effective_range[1] - effective_range[0]; - var color = params.color; - var height_perc = function(d) { - return ((data(d) - effective_range[0])/range_len)*100; - }; - var attrs = { - width: '100%', - height: function(d) { - return height_perc(d)+'%'; - }, - y: function(d) { - return (100 - height_perc(d))+ '%'; - }, - fill: color || '#000000' - }; - self.addRule({ - d3_shape: rect, - attrs: attrs - }); - // add range markers - self.svg.selectAll('text.bar_chart_range_marker').remove(); - var range_font_size = params.range_font_size || 10; - var range_label_width = range_font_size * Math.max(range[0].toString().length, range[1].toString().length) + 2; - $(self).trigger(signals.REQUEST_PRE_TRACK_PADDING, {pre_track_padding: range_label_width}); - var range_font_color = params.range_font_color || '#FF0000'; - self.svg.append('text').attr('font-size', range_font_size) - .attr('fill', range_font_color) - .attr('x', 0) - .attr('y', range_font_size) - .text(range[1]); - self.svg.append('text').attr('font-size', range_font_size) - .attr('fill', range_font_color) - .attr('x', 0) - .attr('y', track_config.get('track_height')) - .text(range[0]); - */ - } else if (templName === 'genetic_alteration') { - params = $.extend({}, params); - var rect = utils.makeD3SVGElement('rect'); - // background (CNA) - var cna = params.cna_key || 'cna'; - self.addStaticRule({ - condition: function(d) { - return d[cna] === params.cna_amp_name; - }, - d3_shape: rect, - legend_label: 'Amplification', - attrs: { - fill: params.cna_amp_color - } - }); - self.addStaticRule({ - condition: function(d) { - return d[cna] === params.cna_homdel_name; - }, - d3_shape: rect, - legend_label: 'Homozygous Deletion', - attrs: { - fill: params.cna_homdel_color - } - }); - console.log(params); - self.addStaticRule({ - condition: function(d) { - return d[cna] === params.cna_gain_name; - }, - d3_shape: rect, - legend_label: 'Gain', - attrs: { - fill: params.cna_gain_color - } - }); - self.addStaticRule({ - condition: function(d) { - return d[cna] === params.cna_hetloss_name; - }, - d3_shape: rect, - legend_label: 'Hemizygous Deletion', - attrs: { - fill: params.cna_hetloss_color - } - }); - self.addStaticRule({ - condition: function(d) { - return !d[cna]; - }, - d3_shape: rect, - attrs: { - fill: params.default_cell_color - }, - exclude_from_legend: true - }); - /* - self.addRule({ - d3_shape: rect, - attrs: { - width:'100%', - height: '100%', - fill: function(d) { - if (!d[cna]) { - return params.default_cell_color || '#D3D3D3'; - } else if (d[cna] === params.cna_amplified_name) { - return params.cna_amplified_color || '#FF0000'; - } else if (d[cna] === params.cna_homodeleted_name) { - return params.cna_homodeleted_color || '#0000FF'; - } - } - } - });*/ - // mutations - var mut = params.mut_key || 'mut'; - self.addStaticRule({ - condition: function(d) { - return utils.mutationType(d[mut]) - } - - }); - /* - self.addStaticRule({ - condition: function(d) {} - }); - self.addRule({ - condition: function(d) { return !!d[mut]; }, - d3_shape: rect, - attrs: { - width: '100%', - height: '33.33%', - y: '33.33%', - fill: function(d) { - var m = d[mut]; - // TODO: look up defaults in real data - if (m === (params.mut_missense_name || 'MISSENSE')) { - return params.mut_missense_color || '#008000'; - } else if (m === (params.mut_trunc_name || 'TRUNC')) { - return params.mut_trunc_color || '#000000'; - } else if (m === (params.mut_inframe_name || 'INFRAME')) { - return params.mut_inframe_color || '#9F8170'; - } else if (m === (params.mut_frameshift_name || 'FRAMESHIFT')) { - return params.mut_frameshift_color || '#000000'; // TODO - is this default? - } else { - return params.mut_default_color || '#000000'; - } - } - } - });*/ - // mrna - var mrna = params.mrna_name || 'mrna'; - /* - self.addRule({ - condition: function(d) { return !!d[mrna]; }, - d3_shape: rect, - attrs: { - width: '100%', - height: '100%', - fill: 'rgba(0,0,0,0)', - 'stroke-width': 2, - stroke: function(d) { - var m = d[mrna]; - // TODO: look up defaults in real data. or maybe just have no defaults here - put defaults in a different file - if (m === (params.mrna_upregulated_name || 'UPREGULATED')) { - return params.mrna_upregulated_color || '#FF9999'; - } else if (m === (params.mrna_downregulated_name || 'DOWNREGULATED')) { - return params.mrna_downregulated_color || '#6699CC'; - } - } - } - });*/ - // TODO: rppa - var triangle_up = utils.makeD3SVGElement('path').attr('d', 'triangle-up'); - } - }; - self.bindEvents = function(track) { - $(track).on(events.SORT, function() { - updateCells(); - }).on(events.SET_CELL_WIDTH, function() { - updateCells(); - updateCellArea(); - }).on(events.SET_CELL_PADDING, function() { - updateCells(); - updateCellArea(); - }).on(events.SET_PRE_TRACK_PADDING, function(e,data) { - updateCells(); - updateCellArea(); - }); - }; -}; - -module.exports = D3SVGCellRenderer; - -/* TODO!! -function D3SVGLinearRangeRule(condition, d3_shape, data_key, data_range, attr_range, z_index, rule_id, legend_label) { - var self = this; - self.rule_id = rule_id; - self.condition = condition; - self.shape = d3_shape; - self.attrs = {}; - self.legend_label = legend_label; - - _.each(attr_range, function(range, attr) { - self.attrs[attr] = (function(_data_range, _range) { - return function(d) { - var datum = parseFloat(d[data_key], 10); - distance = (datum - _data_range[0]) / (_data_range[1] - _data_range[0]); - return utils.lin_interp(distance, _range[0], _range[1]); - }; - })(data_range, range); - }); - self.z_index = z_index; - - self.getLegendGroup = function(cell_width, cell_height) { - var ret = utils.makeD3SVGElement('g'); - var lower_group = ret.append('g'); - var upper_group = ret.append('g'); - - var lower_cell = (function(_data_range) { - return lower_group.append('g').data({data_key: _data_range[0]}); - })(data_range); - var upper_cell = (function(_data_range) { - return upper_group.append('g').data({data_key: _data_range[1]}); - })(data_range); - - self.apply(lower_cell, cell_width, cell_height); - self.apply(upper_cell, cell_width, cell_height); - - lower_group.append('text').text() - utils.appendD3SVGElement(lower_group); - utils.appendD3SVGElement(upper_group); - - return ret; - }; -}; -D3SVGLinearRangeRule.prototype = new D3SVGRule(); -D3SVGLinearRangeRule.prototype.constructor=D3SVGLinearRangeRule;*/ diff --git a/packages/oncoprintjs/src/.js/OncoprintToolbar.js b/packages/oncoprintjs/src/.js/OncoprintToolbar.js deleted file mode 100644 index f1dfcd15923..00000000000 --- a/packages/oncoprintjs/src/.js/OncoprintToolbar.js +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2015 Memorial Sloan-Kettering Cancer Center. - * - * This library is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS - * FOR A PARTICULAR PURPOSE. The software and documentation provided hereunder - * is on an "as is" basis, and Memorial Sloan-Kettering Cancer Center has no - * obligations to provide maintenance, support, updates, enhancements or - * modifications. In no event shall Memorial Sloan-Kettering Cancer Center be - * liable to any party for direct, indirect, special, incidental or - * consequential damages, including lost profits, arising out of the use of this - * software and its documentation, even if Memorial Sloan-Kettering Cancer - * Center has been advised of the possibility of such damage. - */ - -/* - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ -var toolbar_events = { - -}; - -function OncoprintToolbar(config) { - var self = this; - - self.bindEvents = function(oncoprint) { - } -} - -function ToolbarRenderer() { - -} - -module.exports = OncoprintToolbar; diff --git a/packages/oncoprintjs/src/.js/ReadOnlyObject.js b/packages/oncoprintjs/src/.js/ReadOnlyObject.js deleted file mode 100644 index 962af464e1e..00000000000 --- a/packages/oncoprintjs/src/.js/ReadOnlyObject.js +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2015 Memorial Sloan-Kettering Cancer Center. - * - * This library is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS - * FOR A PARTICULAR PURPOSE. The software and documentation provided hereunder - * is on an "as is" basis, and Memorial Sloan-Kettering Cancer Center has no - * obligations to provide maintenance, support, updates, enhancements or - * modifications. In no event shall Memorial Sloan-Kettering Cancer Center be - * liable to any party for direct, indirect, special, incidental or - * consequential damages, including lost profits, arising out of the use of this - * software and its documentation, even if Memorial Sloan-Kettering Cancer - * Center has been advised of the possibility of such damage. - */ - -/* - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ -var $ = require('jquery'); -function ReadOnlyObject(obj) { - var self = this; - var objs = [obj]; - self.get = function(name) { - var i = objs.length - 1; - var ret = undefined; - while (typeof ret === 'undefined' && i >= 0) { - ret = objs[i][name]; - i -= 1; - } - return ret; - }; - self.extend = function(new_obj) { - // later objects override earlier objects when keys are the same - objs.push(new_obj); - return self; - } -}; - -module.exports = ReadOnlyObject; diff --git a/packages/oncoprintjs/src/.js/RuleSet.js b/packages/oncoprintjs/src/.js/RuleSet.js deleted file mode 100644 index 4e38bc03521..00000000000 --- a/packages/oncoprintjs/src/.js/RuleSet.js +++ /dev/null @@ -1,469 +0,0 @@ -var _ = require('underscore'); -var utils = require('./utils'); - -var CATEGORICAL_COLOR = 0; -var GRADIENT_COLOR = 1; -var GENETIC_ALTERATION = 2; -var BAR_CHART = 3; -module.exports = { - CATEGORICAL_COLOR: CATEGORICAL_COLOR, - GRADIENT_COLOR: GRADIENT_COLOR, - GENETIC_ALTERATION: GENETIC_ALTERATION, - BAR_CHART: BAR_CHART, - makeRuleSet: function(type, params) { - if (type === CATEGORICAL_COLOR) { - return new D3SVGCategoricalColorRuleSet(params); - } else if (type === GRADIENT_COLOR) { - return new D3SVGGradientColorRuleSet(params); - } else if (type === GENETIC_ALTERATION) { - return new D3SVGGeneticAlterationRuleSet(params); - } else if (type === BAR_CHART) { - return new D3SVGBarChartRuleSet(params); - } else { - return new D3SVGRuleSet(); - } - } -}; - -var getRuleSetId = utils.makeIdCounter(); - -var D3SVGRuleSet = (function() { - function D3SVGRuleSet(params) { - this.rule_map = {}; - this.rule_set_id = getRuleSetId(); - this.legend_label = params.legend_label; - }; - var getRuleId = utils.makeIdCounter(); - - D3SVGRuleSet.prototype.getLegendLabel = function() { - return this.legend_label; - }; - D3SVGRuleSet.prototype.getRuleSetId = function() { - return this.rule_set_id; - }; - D3SVGRuleSet.prototype.addRule = function(params) { - var rule_id = getRuleId(); - this.rule_map[rule_id] = new D3SVGRule(params, rule_id); - return rule_id; - } - D3SVGRuleSet.prototype.addStaticRule = function(params) { - var rule_id = getRuleId(); - this.rule_map[rule_id] = new D3SVGStaticRule(params, rule_id); - return rule_id; - }; - D3SVGRuleSet.prototype.addGradientRule = function(params) { - var rule_id = getRuleId(); - this.rule_map[rule_id] = new D3SVGGradientRule(params, rule_id); - return rule_id; - }; - D3SVGRuleSet.prototype.addBarChartRule = function(params) { - var rule_id = getRuleId(); - this.rule_map[rule_id] = new D3SVGBarChartRule(params, rule_id); - return rule_id; - }; - D3SVGRuleSet.prototype.removeRule = function(rule_id) { - delete this.rule_map[rule_id]; - }; - D3SVGRuleSet.prototype.getRules = function() { - var self = this; - var rule_ids = Object.keys(this.rule_map); - var rules = _.map(rule_ids, function(id) { return self.rule_map[id]; }); - var sorted_rules = _.sortBy(rules, function(r) { return r.z_index; }); - return sorted_rules; - }; - D3SVGRuleSet.prototype.apply = function(g, data, datum_id_accessor, cell_width, cell_height) { - _.each(this.getRules(), function(rule) { - var affected_data = rule.filterData(data); - var affected_groups = g.data(affected_data, datum_id_accessor); - rule.apply(affected_groups, cell_width, cell_height); - }); - }; - D3SVGRuleSet.prototype.getRule = function(rule_id) { - return this.rule_map[rule_id]; - }; - return D3SVGRuleSet; -})(); - -function D3SVGCategoricalColorRuleSet(params) { - D3SVGRuleSet.call(this, params); - this.type = CATEGORICAL_COLOR; - var self = this; - var d3_colors = _.shuffle(d3.scale.category20().range()); - var addColorRule = function(color, category) { - var colored_rect = utils.makeD3SVGElement('rect').attr('fill', color); - var condition = (function(cat) { - return function(d) { - return params.getCategory(d) === cat; - }; - })(category); - self.addStaticRule({ - condition: condition, - shape: colored_rect, - legend_label: category - }); - }; - _.each(params.color, function(color, category) { - addColorRule(color, category); - }); - - self.apply = function(g, data, datum_id_accessor, cell_width, cell_height) { - var missing_categories = []; - _.each(data, function(datum) { - var category = params.getCategory(datum); - if (!params.color.hasOwnProperty(category)) { - var new_color = d3_colors.pop(); - params.color[category] = new_color; - addColorRule(new_color, category); - } - }); - D3SVGRuleSet.prototype.apply.call(this, g, data, datum_id_accessor, cell_width, cell_height); - }; - - self.putLegendGroup = function(svg, cell_width, cell_height) { - var group = svg.append('g'); - _.each(self.getRules(), function(rule) { - rule.putLegendGroup(group, cell_width, cell_height); - }) - utils.spaceSVGElementsHorizontally(group, 20); - return group; - }; -} -D3SVGCategoricalColorRuleSet.prototype = Object.create(D3SVGRuleSet.prototype); - -function D3SVGGradientColorRuleSet(params) { - D3SVGRuleSet.call(this, params); - this.type = GRADIENT_COLOR; - var rule = this.addGradientRule({ - shape: utils.makeD3SVGElement('rect'), - data_key: params.data_key, - data_range: params.data_range, - color_range: params.color_range, - scale: params.scale - }); - this.putLegendGroup = function(svg) { - return this.rule_map[rule].putLegendGroup(svg); - }; -} -D3SVGGradientColorRuleSet.prototype = Object.create(D3SVGRuleSet.prototype); - -function D3SVGBarChartRuleSet(params) { - D3SVGRuleSet.call(this, params); - var self = this; - self.type = BAR_CHART; - var rule = this.addBarChartRule({ - data_key: params.data_key, - data_range: params.data_range, - scale: params.scale, - fill: params.fill, - }); - this.putLegendGroup = function(svg, cell_width, cell_height) { - return this.rule_map[rule].putLegendGroup(svg, cell_width, cell_height); - }; -} -D3SVGBarChartRuleSet.prototype = Object.create(D3SVGRuleSet.prototype); - -function D3SVGGeneticAlterationRuleSet(params) { - D3SVGRuleSet.call(this, params); - var self = this; - self.type = GENETIC_ALTERATION; - var default_rule = this.addStaticRule({ - shape: utils.makeD3SVGElement('rect').attr('fill', params.default_color), - exclude_from_legend: true, - z_index: -1 - }); - var altered_rules = []; - _.each(params.cna.color, function(color, name) { - var new_cna_rule = self.addStaticRule({ - condition: (function(_name) { - return function(d) { - return d[params.cna_key] === _name; - }; - })(name), - shape: utils.makeD3SVGElement('rect'), - legend_label: params.cna.label[name], - attrs: { - fill: color, - width: '100%', - height: '100%' - }, - z_index: 0 - }); - altered_rules.push(new_cna_rule); - }); - _.each(params.mut.color, function(color, name) { - var new_mut_rule = self.addStaticRule({ - condition: (function(_name) { - return function(d) { - return d[params.mut_type_key] === _name; // TODO: should be indexOf for multiple mutations? - } - })(name), - shape: utils.makeD3SVGElement('rect').attr('fill', color), - legend_label: params.mut.label[name], - attrs: { - width: '100%', - height: '33.33%', - y: '33.33%' - }, - z_index: 1 - }); - altered_rules.push(new_mut_rule); - }); - // TODO: mrna, rppa, other stuff? - self.putLegendGroup = function(svg, cell_width, cell_height) { - var group = svg.append('g'); - _.each(self.getRules(), function(rule) { - rule.putLegendGroup(group, cell_width, cell_height); - }) - utils.spaceSVGElementsHorizontally(group, 20); - return group; - }; - self.alteredData = function(data) { - var altered_data = []; - _.each(altered_rules, function(rule_id) { - altered_data = altered_data.concat(self.getRule(rule_id).filterData(data)); - }); - return _.uniq(altered_data); - }; -} -D3SVGGeneticAlterationRuleSet.prototype = Object.create(D3SVGRuleSet.prototype); - -function D3SVGRule(params, rule_id) { - this.rule_id = rule_id; - this.condition = params.condition || function(d) { return true; }; - this.shape = typeof params.shape === 'undefined' ? utils.makeD3SVGElement('rect') : params.shape; - this.z_index = typeof params.z_index === 'undefined' ? this.rule_id : params.z_index; - this.legend_label = params.legend_label; - this.exclude_from_legend = params.exclude_from_legend; - - this.attrs = params.attrs || {}; - this.attrs.width = this.attrs.width || '100%'; - this.attrs.height = this.attrs.height || '100%'; - - var percentToPx = function(attr_val, attr_name, cell_width, cell_height) { - // convert a percentage to a local pixel coordinate - var width_like = ['width', 'x']; - var height_like = ['height', 'y']; - attr_val = parseFloat(attr_val, 10)/100; - if (width_like.indexOf(attr_name) > -1) { - attr_val = attr_val*cell_width; - } else if (height_like.indexOf(attr_name) > -1) { - attr_val = attr_val*cell_height; - } - return attr_val+''; - }; - - var convertAttr = function(d, i, attr_val, attr_name, cell_width, cell_height) { - var ret = attr_val; - if (typeof ret === 'function') { - ret = ret(d,i); - } - if (typeof ret === 'string' && ret.indexOf('%') > -1) { - ret = percentToPx(ret, attr_name, cell_width, cell_height); - } - return ret; - }; - - this.apply = function(g, cell_width, cell_height) { - var shape = this.shape; - var elts = utils.appendD3SVGElement(shape, g); - var attrs = this.attrs || {}; - attrs.width = attrs.width || '100%'; - attrs.height = attrs.height || '100%'; - attrs.x = attrs.x || 0; - attrs.y = attrs.y || 0; - _.each(attrs, function(val, key) { - elts.attr(key, function(d,i) { - if (key === 'x' || key === 'y') { - return; - } - return convertAttr(d, i, val, key, cell_width, cell_height); - }); - }); - - elts.attr('transform', function(d,i) { - var x_val = convertAttr(d, i, attrs.x, 'x', cell_width, cell_height); - var y_val = convertAttr(d, i, attrs.y, 'y', cell_width, cell_height); - return utils.translate(x_val, y_val); - }); - } - this.filterData = function(data) { - return data.filter(this.condition); - }; - this.isActive = function(data) { - return this.filterData(data).length > 0; - }; -} - -function D3SVGBarChartRule(params, rule_id) { - D3SVGRule.call(this, params, rule_id); - this.data_key = params.data_key; - this.data_range = params.data_range; - - var scale = function(x) { - if (params.scale === 'log') { - return Math.log10(Math.max(x, 0.1)); - } else { - return x; - } - }; - - var makeDatum = function(x) { - var ret = {}; - ret[params.data_key] = x; - return ret; - }; - var scaled_data_range = _.map(this.data_range, scale); - var height_helper = function(d) { - var datum = scale(d[params.data_key]); - var data_range = [scaled_data_range[0], scaled_data_range[1]]; - var distance = (datum-scaled_data_range[0]) / (scaled_data_range[1]-scaled_data_range[0]); - return distance * 100; - }; - var y_function = function(d) { - return (100 - height_helper(d)) + '%'; - }; - var height_function = function(d) { - return height_helper(d) + '%'; - }; - this.attrs.height = height_function; - this.attrs.y = y_function; - this.attrs.fill = params.fill || '#000000'; - - this.putLegendGroup = function(svg, cell_width, cell_height) { - // TODO: triangle legend piece - if (params.exclude_from_legend) { - return; - } - var group = svg.append('g'); - group.append('text').text(this.data_range[0]).attr('alignment-baseline', 'hanging'); - var rect_group = group.append('g'); - var mesh = 50; - for (var i=0; i<=mesh; i++) { - var t = i/mesh; - var d = (1-t)*this.data_range[0] + t*this.data_range[1]; - var datum = makeDatum(d); - var height = cell_height*height_helper(datum)/100; - rect_group.append('rect') - .attr('width', 1) - .attr('height', height) - .attr('y', cell_height-height) - .attr('fill', this.attrs.fill); - } - utils.spaceSVGElementsHorizontally(rect_group, 0); - group.append('text').text(this.data_range[1]).attr('alignment-baseline', 'hanging'); - utils.spaceSVGElementsHorizontally(group, 10); - - return group; - }; -} -D3SVGBarChartRule.prototype = Object.create(D3SVGRule.prototype); - -function D3SVGGradientRule(params, rule_id) { - D3SVGRule.call(this, params, rule_id); - this.data_key = params.data_key; - this.data_range = params.data_range; - this.color_range = params.color_range; - - var getGradientId = (function() { - var gradient_counter = 0; - return function() { - gradient_counter += 1; - return 'gradient'+'_'+rule_id+'_'+gradient_counter; - } - })(); - var scale = function(x) { - if (params.scale === 'log') { - return Math.log10(Math.max(x, 0.1)); - } else { - return x; - } - }; - - var scaled_data_range = _.map(this.data_range, scale); - var fill_function = function(d) { - var datum = scale(d[params.data_key]); - var data_range = [scaled_data_range[0], scaled_data_range[1]]; - var distance = (datum-scaled_data_range[0]) / (scaled_data_range[1]-scaled_data_range[0]); - color_range = [d3.rgb(params.color_range[0]).toString(), - d3.rgb(params.color_range[1]).toString()]; - return utils.lin_interp(distance, params.color_range[0], params.color_range[1]); - }; - this.attrs.fill = fill_function; - - var makeDatum = function(x) { - var ret = {}; - ret[params.data_key] = x; - return ret; - }; - var putLinearGradient = function(group, color_range, width, height) { - var gradient_id = getGradientId(); - var gradient = group.append('svg:defs').append('svg:linearGradient') - .attr('id', gradient_id) - .attr('x1', '0%').attr('y1', '0%') - .attr('x2', '100%').attr('y2', '0%') - .attr('spreadMethod', 'pad'); - gradient.append('svg:stop') - .attr('offset', '0%') - .attr('stop-color', color_range[0]) - .attr('stop-opacity', 1); - gradient.append('svg:stop') - .attr('offset', '100%') - .attr('stop-color', color_range[1]) - .attr('stop-opacity', 1); - group.append('rect') - .attr('width',width).attr('height', height) - .style('fill', 'url(#'+gradient_id+')'); - }; - - var putLogGradient = function(group, color_range, width, height) { - // TODO: I think this is perceptually useless....but could still leave it I guess - var gradient_group = group.append('g'); - var t, datum; - for (var i=0; i. - */ -module.exports = { - ADD_TRACK: 'add_track.oncoprint', - REMOVE_TRACK: 'remove_track.oncoprint', - MOVE_TRACK: 'move_track.oncoprint', - SORT: 'sort.oncoprint', - SET_CELL_PADDING: 'set_cell_padding.oncoprint', - SET_CELL_WIDTH: 'set_cell_width.oncoprint', - SET_TRACK_DATA: 'set_track_data.oncoprint', - SET_ID_ORDER: 'set_id_order.oncoprint', - CELL_CLICK: 'cell_click.oncoprint', - CELL_MOUSEENTER: 'cell_mouseenter.oncoprint', - CELL_MOUSELEAVE: 'cell_mouseleave.oncoprint', - ONCOPRINT_MOUSEENTER: 'oncoprint_mouseenter.oncoprint', - ONCOPRINT_MOUSELEAVE: 'oncoprint_mouseleave.oncoprint', - SET_PRE_TRACK_PADDING: 'set_pre_track_padding.oncoprint', - TRACK_INIT: 'init.track.oncoprint', - UPDATE_RENDER_RULES: 'update_render_rules.cell_renderer.oncoprint', - FINISHED_RENDERING: 'finished_rendering.renderer.oncoprint', - FINISHED_POSITIONING: 'finished_positioning.renderer.oncoprint' -}; diff --git a/packages/oncoprintjs/src/.js/globals.js b/packages/oncoprintjs/src/.js/globals.js deleted file mode 100644 index a0995453769..00000000000 --- a/packages/oncoprintjs/src/.js/globals.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = {}; \ No newline at end of file diff --git a/packages/oncoprintjs/src/.js/main.js b/packages/oncoprintjs/src/.js/main.js deleted file mode 100644 index 307661573a5..00000000000 --- a/packages/oncoprintjs/src/.js/main.js +++ /dev/null @@ -1,183 +0,0 @@ -var d3 = require('d3'); -var _ = require('underscore'); - -var renderers = require('./renderers'); -var rendering_engine = require('./rendering_engine'); -var sorting = require('./sorting'); -var utils = require('./utils'); - -module.exports = function() { - var cna_fills = { - AMPLIFIED: 'red', - HOMODELETED: 'blue', - null: 'grey', - undefined: 'grey' - }; - var rect_height = 20; - var rect_padding = 3; - var rect_width = 10; - var rendering_rules = []; - var row_height = 25; - var mutation_fill = 'green'; - var width = 750; - - var engine = rendering_engine(); - - var me = function(container_selector_string, data) { - var container = prepare_container(d3.select(container_selector_string), data) - - engine.config(get_config()); - engine.container_width(width); - engine.element_width(rect_width); - engine.element_padding(rect_padding); - engine.label_function(rows_to_labels); - engine.renderers(rendering_rules_or_default(container)); - container.call(engine); - }; - -// me.insert_row = engine.insert_row; - - me.insert_row = function(container_selector_string, row, rendering_rule) { - var container = d3.select(container_selector_string); - - var sorted_row = utils.sort_row_by_rows(row, container.datum()); - - // update the list of renderers - rendering_rules.unshift(renderers.gender_rule); - engine.renderers(rendering_rules); - - engine.insert_row(container, sorted_row, rendering_rule); - } - - me.resort = function(container_selector_string, sample_id_to_array_index) { - // TODO this function should live more in the rendering_engine than here. - - var container = d3.select(container_selector_string); - - var resorted_rows = container.datum().map(function(row) { - return _.sortBy(row, function(d) { - return sample_id_to_array_index[d.sample || d.sample_id]; - })}); - - container.datum(resorted_rows); - - var row_groups = container.selectAll('.oncoprint-row'); - row_groups = row_groups[0].map(d3.select); - utils.assert(row_groups.length === rendering_rules.length, - "Rows don't matchup with rendering rules."); - row_groups = row_groups.reverse(); - - _.each(_.zip(row_groups, rendering_rules), function(row_group_and_rr) { - var row_group = row_group_and_rr[0]; - var rr = row_group_and_rr[1]; - rr(get_config()).resort(row_group, sample_id_to_array_index); - }); - }; - - // - // getters and setters - // - - me.cna_fills = function(value) { - if (!arguments.length) return cna_fills; - cna_fills = value; - return me; - }; - - me.rect_height = function(value) { - if (!arguments.length) return rect_height; - rect_height = value; - return me; - }; - - me.rect_padding = function(value) { - if (!arguments.length) return rect_padding; - rect_padding = value; - return me; - }; - - me.rect_width = function(value) { - if (!arguments.length) return rect_width; - rect_width = value; - return me; - }; - - me.rendering_rules = function(value) { - if (!arguments.length) return rendering_rules; - rendering_rules = value; - return me; - }; - - me.row_height = function(value) { - if (!arguments.length) return row_height; - row_height = value; - return me; - }; - - me.mutation_fill = function(value) { - if (!arguments.length) return mutation_fill; - mutation_fill = value; - return me; - }; - - me.width = function(value) { - if (!arguments.length) return width; - width = value; - return me; - }; - - // - // HELPER FUNCTIONS - // - - function calculate_row_label(row) { - var percent_altered = _.filter(row, utils.is_sample_genetically_altered).length / row.length; - percent_altered = Math.round(percent_altered*100); - return [{align: 'left', text: row[0].gene}, {align: 'right', text: percent_altered + "%"}]; - } - - function rendering_rules_or_default(container) { - if (rendering_rules.length === 0) { - rendering_rules = _.map(container.datum(), function(row) { - return renderers.gene_rule; - }); - } - - return rendering_rules; - } - - function get_config() { - return { - cna_fills: cna_fills, - rect_height: rect_height, - rect_padding: rect_padding, - rect_width: rect_width, - rendering_rules: rendering_rules, - row_height: row_height, - mutation_fill: mutation_fill, - width: width - }; - } - - function rows_to_labels(rows) { - return _.flatten(_.map(rows, calculate_row_label)); - } - - // reorganize the flat data into a list of sorted rows - // bind those rows to the container using .datum() - function prepare_container(container, data) { - var rows = _.chain(data).groupBy(function(d) { return d.gene; }).values().value(); - var sorted_rows = sorting.sort_rows(rows, sorting.genomic_metric); - container.datum(sorted_rows); - return container; - }; - - return me; -}; - - - - - - - diff --git a/packages/oncoprintjs/src/.js/oncoprint.js b/packages/oncoprintjs/src/.js/oncoprint.js deleted file mode 100644 index d2e18a45827..00000000000 --- a/packages/oncoprintjs/src/.js/oncoprint.js +++ /dev/null @@ -1,783 +0,0 @@ -/* - * Copyright (c) 2015 Memorial Sloan-Kettering Cancer Center. - * - * This library is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS - * FOR A PARTICULAR PURPOSE. The software and documentation provided hereunder - * is on an "as is" basis, and Memorial Sloan-Kettering Cancer Center has no - * obligations to provide maintenance, support, updates, enhancements or - * modifications. In no event shall Memorial Sloan-Kettering Cancer Center be - * liable to any party for direct, indirect, special, incidental or - * consequential damages, including lost profits, arising out of the use of this - * software and its documentation, even if Memorial Sloan-Kettering Cancer - * Center has been advised of the possibility of such damage. - */ - -/* - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ -var _ = require('underscore'); -var d3 = require('d3'); -var events = require('./events'); -var signals = require('./signals'); -var globals = require('./globals'); -var utils = require('./utils'); -var RuleSet = require('./RuleSet'); - -var defaultOncoprintConfig = { - cell_width: 6, - cell_padding: 3, - legend: true, -}; - -var hiddenOncoprintConfig = { - pre_track_padding: 0, -}; - -var defaultTrackConfig = { - label: 'Gene', - datum_id_key: 'sample', - cell_height: 23, - track_height: 20, - track_padding: 5, - sort_cmp: undefined, - tooltip: function(d) { - return d['sample']; - } -}; - -module.exports = { - CATEGORICAL_COLOR: RuleSet.CATEGORICAL_COLOR, - GRADIENT_COLOR: RuleSet.GRADIENT_COLOR, - GENETIC_ALTERATION: RuleSet.GENETIC_ALTERATION, - BAR_CHART: RuleSet.BAR_CHART, - create: function CreateOncoprint(container_selector_string, config) { - config = $.extend({}, defaultOncoprintConfig, config || {}); - config = $.extend(config, hiddenOncoprintConfig); - var oncoprint = new Oncoprint(config); - var renderer = new OncoprintSVGRenderer(container_selector_string, oncoprint, {label_font: '12px Arial', legend:config.legend}); - var ret = { - addTrack: function(config) { - var track_id = oncoprint.addTrack(config); - return track_id; - }, - removeTrack: function(track_id) { - oncoprint.removeTrack(track_id); - }, - moveTrack: function(track_id, position) { - oncoprint.moveTrack(track_id, position); - }, - setTrackData: function(track_id, data) { - oncoprint.setTrackData(track_id, data); - }, - setRuleSet: function(track_id, type, params) { - renderer.setRuleSet(track_id, type, params); - }, - useSameRuleSet: function(target_track_id, source_track_id) { - renderer.useSameRuleSet(target_track_id, source_track_id); - }, - setCellPadding: function(p) { - oncoprint.setCellPadding(p); - }, - toSVG: function(ctr) { - return renderer.toSVG(ctr); - }, - sort: function(track_id_list, cmp_list) { - oncoprint.sort(track_id_list, cmp_list); - } - }; - return ret; - } -}; - -function Oncoprint(config) { - var self = this; - var track_id_counter = 0; - self.config = config; - self.id_order = []; - self.track_order = []; - self.tracks = {}; - self.ids = {}; - - self.getCellWidth = function() { - return self.config.cell_width; - }; - self.getCellPadding = function() { - return self.config.cell_padding; - }; - self.getCellHeight = function(track_id) { - return self.tracks[track_id].config.cell_height; - }; - self.getTrackHeight = function(track_id) { - return self.tracks[track_id].config.track_height; - }; - self.getTrackPadding = function(track_id) { - return self.tracks[track_id].config.track_padding; - }; - self.getIdOrder = function() { - return self.id_order; - }; - self.setIdOrder = function(id_order) { - self.id_order = id_order; - $(self).trigger(events.SET_ID_ORDER); - }; - self.getTrackOrder = function() { - return self.track_order.slice(); - }; - self.getTrackLabel = function(track_id) { - return self.tracks[track_id].config.label; - }; - self.getTrackData = function(track_id) { - return self.tracks[track_id].data; - }; - self.getTrackTooltip = function(track_id) { - return self.tracks[track_id].config.tooltip; - }; - self.setTrackData = function(track_id, data) { - var id_accessor = self.getTrackDatumIdAccessor(track_id); - - self.tracks[track_id].data = data; - self.id_order = self.id_order.concat(_.difference(_.map(data, id_accessor), self.id_order)); - - self.tracks[track_id].id_data_map = {}; - var id_data_map = self.tracks[track_id].id_data_map; - _.each(self.tracks[track_id].data, function(datum) { - id_data_map[id_accessor(datum)] = datum; - }); - $(self).trigger(events.SET_TRACK_DATA, {track_id: track_id}); - }; - self.getTrackDatum = function(track_id, datum_id) { - return self.tracks[track_id].id_data_map[datum_id]; - }; - - self.getTrackDatumIdAccessor = function(track_id) { - var key = self.getTrackDatumIdKey(track_id); - return function(d) { - return d[key]; - }; - }; - self.getTrackDatumIdKey = function(track_id) { - return self.tracks[track_id].config.datum_id_key; - }; - - self.removeTrack = function(track_id) { - var track = self.tracks[track_id]; - delete self.tracks[track_id]; - - var oldPosition = self.track_order.indexOf(track_id); - self.track_order.splice(oldPosition, 1); - - $(self).trigger(events.REMOVE_TRACK, {track: track, track_id: track_id}); - return true; - }; - self.moveTrack = function(track_id, new_position) { - new_position = Math.min(self.track_order.length-1, new_position); - new_position = Math.max(0, new_position); - var old_position = self.track_order.indexOf(track_id); - - var new_order = self.track_order.slice(); - var i; - var moved_tracks = []; - if (old_position > new_position) { - for (i=new_position+1; i<=old_position; i++) { - new_order[i] = self.track_order[i-1]; - moved_tracks.push(self.track_order[i-1]); - } - moved_tracks.push(track_id); - } else if (old_position < new_position) { - for (i=old_position; i this.last) { - for (i=this.last + 1; i <= new_bounds.last; i++) { - ret.push(i); - } - } - return ret; - }; - this.toHide = function(new_bounds) { - var ret = []; - var i; - if (new_bounds.first > this.first) { - for (i=this.first; i < new_bounds.first; i++) { - ret.push(i); - } - } - if (new_bounds.last < this.last) { - for (i=new_bounds.last+1; i <= this.last; i++) { - ret.push(i); - } - } - return ret; - }; - this.set = function(first, last) { - this.first = first; - this.last = last; - return this; - }; - this.fromViewInterval = function(interval, cell_unit) { - this.first = Math.floor(interval[0]/cell_unit); - this.last = Math.ceil(interval[1]/cell_unit); - return this; - }; - } - function OncoprintSVGRenderer(container_selector_string, oncoprint, config) { - OncoprintRenderer.call(this, oncoprint, config); - var self = this; - this.toolbar_container; - this.label_svg; - this.label_container; - this.cell_container; - this.cell_container_node; - this.cell_div; - this.legend_svg; - this.cells = {}; - this.curr_clip_bounds = new VisibleIndexBounds(-1, -2); - this.prev_clip_bounds = new VisibleIndexBounds(-1, -2); - - this.clip_zone_start = 0; - - (function initToolbarContainer() { - self.toolbar_container = d3.select(container_selector_string).append('div').classed('toolbar_container', true); - d3.select(container_selector_string).append('br'); - /*$.ajax({url: "toolbar.html", context: document.body, success: function(response) { - $(self.toolbar_container.node()).html(response); - }});*/ - })(); - (function initLabelContainer() { - self.label_container = d3.select(container_selector_string).append('div').classed('fixed_oncoprint_section_container', true); - self.label_svg = self.label_container.append('svg'); - $(self.label_svg.node()).on("mousedown", function(evt) { - // TODO: fix this shit UP - var in_track = -1; - var track_tops = self.getTrackTops(); - var mouse_y = evt.clientY - self.label_svg.node().offsetTop; - _.find(self.oncoprint.getTrackOrder(), function(id) { - if (mouse_y >= track_tops[id] && mouse_y <= track_tops[id] + self.getRenderedTrackHeight(id)) { - in_track = id; - return true; - } - }); - if (in_track > -1) { - self.dragLabel(in_track); - } - }); - })(); - (function initCellContainer() { - self.cell_container = d3.select(container_selector_string).append('div').classed('scrolling_oncoprint_section_container', true); - //self.cell_container.style('display', 'none'); - self.cell_container_node = self.cell_container.node(); - self.cell_div = self.cell_container.append('div').classed('cell_div', true); - - $(self.cell_container.node()).on('scroll', function() { - self.clipCells(); - }); - })(); - (function initLegend() { - if (config.legend) { - self.legend_svg = d3.select(container_selector_string).append('svg'); - } - })(); - - var render_all_events = [events.REMOVE_TRACK]; - var render_track_events = [events.ADD_TRACK, events.SET_TRACK_DATA]; - var reposition_events = [events.MOVE_TRACK, events.SET_CELL_PADDING, events.SET_CELL_WIDTH]; - var resize_cell_div_events = [events.SET_CELL_PADDING, events.SET_CELL_WIDTH]; - var reclip_events = [events.SET_CELL_PADDING, events.SET_CELL_WIDTH]; - var reposition_then_reclip_events = [events.SET_ID_ORDER]; - $(oncoprint).on(resize_cell_div_events.join(" "), function() { - self.resizeCellDiv(); - }); - $(oncoprint).on(render_all_events.join(" "), function() { - self.render(); - }); - $(oncoprint).on(render_track_events.join(" "), function(e, d) { - self.render(d.track_id); - }); - $(oncoprint).on(reposition_events.join(" "), function() { - self.positionCells(); - }); - $(oncoprint).on(reclip_events.join(" "), function() { - self.clipCells(); - }); - $(oncoprint).on(reposition_then_reclip_events.join(" "), function() { - self.positionCells(); - self.clipCells(true); - }); - $(oncoprint).on(events.MOVE_TRACK, function(evt, data) { - // TODO: only reposition tracks that have been moved as a result - this is a fairly slow op so necessary opt - self.positionCells(data.moved_tracks); - self.renderLabels(); - }) - } - utils.extends(OncoprintSVGRenderer, OncoprintRenderer); - - // Rule sets - OncoprintSVGRenderer.prototype.setRuleSet = function(track_id, type, params) { - OncoprintRenderer.prototype.setRuleSet.call(this, track_id, type, params); - this.render(track_id); - }; - OncoprintSVGRenderer.prototype.useSameRuleSet = function(target_track_id, source_track_id) { - OncoprintRenderer.prototype.useSameRuleSet.call(this, target_track_id, source_track_id); - this.render(target_track_id); - } - - // Containers - OncoprintSVGRenderer.prototype.getLabelSVG = function() { - return this.label_svg; - }; - OncoprintSVGRenderer.prototype.resizeCellDiv = function() { - this.cell_div.style('min-width', this.getCellAreaWidth()+'px') - .style('min-height', this.getCellAreaHeight()+'px'); - }; - OncoprintSVGRenderer.prototype.resizeLabelSVG = function() { - this.getLabelSVG().attr('width', this.getLabelAreaWidth()) - .attr('height', this.getLabelAreaHeight()); - }; - OncoprintSVGRenderer.prototype.resizeLegendSVG = function() { - var new_height = 0; - var new_width = 0; - var point = this.legend_svg.node().createSVGPoint(); - utils.d3SelectChildren(this.legend_svg, 'g').each(function() { - point.x = 0; - point.y = 0; - point = point.matrixTransform(this.getCTM()); - var bbox = this.getBBox(); - new_height = Math.max(new_height, point.y+bbox.height); - new_width = Math.max(new_width, point.x + bbox.width); - }); - this.legend_svg.attr('width', new_width).attr('height', new_height); - }; - - // Labels - OncoprintSVGRenderer.prototype.renderTrackLabel = function(oncoprint, track_id, rule_set, svg, label_y) { - var label_class = 'label'+track_id; - label_y = typeof label_y === "undefined" ? this.getLabelTops()[track_id] : label_y; - - var to_reposition; - if (rule_set) { - svg.selectAll('.'+label_class).remove(); - to_reposition = svg.append('text').classed(label_class, true).text(oncoprint.getTrackLabel(track_id)) - .attr('font', this.getLabelFont()) - .attr('alignment-baseline', 'hanging') - .classed('noselect', true) - } else { - to_reposition = svg.selectAll('.' + label_class); - } - to_reposition.attr('transform', utils.translate(0, label_y)); - - var altered_data_label_class = 'altered_data_label'+track_id; - if (rule_set && rule_set.alteredData && typeof rule_set.alteredData === 'function') { - svg.selectAll('.'+altered_data_label_class).remove(); - var data = oncoprint.getTrackData(track_id); - var num_altered = rule_set.alteredData(data).length; - var percent_altered = Math.floor(100*num_altered/data.length); - to_reposition = svg.append('text').classed(altered_data_label_class, true) - .text(percent_altered+'%') - .attr('font', this.getLabelFont()) - .attr('text-anchor', 'end') - .attr('alignment-baseline', 'hanging') - .classed('noselect', true) - } else { - to_reposition = svg.selectAll('.'+altered_data_label_class) - } - to_reposition.attr('transform', utils.translate(this.getLabelAreaWidth(), label_y)); - return svg.selectAll('.'+label_class+',.'+altered_data_label_class); - }; - - // Cells - OncoprintSVGRenderer.prototype.drawTrackCells = function(track_id, rule_set) { - var oncoprint = this.oncoprint; - var data = oncoprint.getTrackData(track_id); - var id_key = oncoprint.getTrackDatumIdKey(track_id); - var id_accessor = oncoprint.getTrackDatumIdAccessor(track_id); - var self = this; - this.cells[track_id] = this.cells[track_id] || {}; - - var cell_class = this.getCellCSSClass(); - var track_cell_class = this.getTrackCellCSSClass(track_id); - - - var bound_svg = this.cell_div.selectAll('svg.'+track_cell_class).data(data, id_accessor); - bound_svg.enter().append('svg').classed(track_cell_class, true).classed(cell_class, true); - bound_svg.style('width', oncoprint.getCellWidth()).style('height', oncoprint.getCellHeight(track_id)); - var tooltip = this.oncoprint.getTrackTooltip(track_id); - bound_svg.each(function(d,i) { - var dom_cell = this; - var id = id_accessor(d); - if (tooltip) { - var tooltip_html = tooltip(d); - $(dom_cell).one("mouseover", function() { - $(dom_cell).qtip({ - content: { - text: tooltip_html - }, - position: {my:'left bottom', at:'top middle', viewport: $(window)}, - style: { classes: 'cell-qtip', border: 'none'}, - show: {event: "mouseover"}, - hide: {fixed: true, delay: 100, event: "mouseout"} - }); - $(dom_cell).trigger("mouseover"); - }); - } - $(dom_cell).on("mouseover", function() { - d3.select(dom_cell).classed("cell_rollover", true); - }); - $(dom_cell).on("mouseout", function() { - d3.select(dom_cell).classed("cell_rollover", false); - }); - self.cells[track_id][id] = this; - }); - bound_svg.selectAll('*').remove(); - rule_set.apply(bound_svg, data, id_accessor, oncoprint.getCellWidth(), oncoprint.getCellHeight(track_id)); - }; - - // Positioning - OncoprintSVGRenderer.prototype.positionTrackCells = function(track_id, bound_svg) { - var oncoprint = this.oncoprint; - if (!bound_svg) { - bound_svg = this.cell_div.selectAll('svg.'+this.getTrackCellCSSClass(track_id)) - .data(oncoprint.getTrackData(track_id), oncoprint.getTrackDatumIdAccessor(track_id)); - } - var self = this; - var id_key = oncoprint.getTrackDatumIdKey(track_id); - var id_order = oncoprint.getIdOrder(); - var y = this.getCellTops()[track_id]; - bound_svg.style('left', function(d,i) { - return self.getCellX(id_order.indexOf(d[id_key])); - }).style('top', y); - }; - OncoprintSVGRenderer.prototype.positionCells = function(track_ids) { - track_ids = track_ids || this.oncoprint.getTrackOrder(); - var self = this; - _.each(track_ids, function(track_id) { - self.positionTrackCells(track_id); - }); - }; - - // Clipping - OncoprintSVGRenderer.prototype.getViewInterval = function() { - var parent = this.cell_container_node; - var parentRect = parent.getBoundingClientRect(); - return {x: parent.scrollLeft, width: parentRect.right - parentRect.left}; - }; - OncoprintSVGRenderer.prototype.getPreviousClipBounds = function() { - return this.prev_clip_bounds; - }; - OncoprintSVGRenderer.prototype.getClipViewInterval = function() { - var self = this; - var view = this.getViewInterval(); - var x = view.x; - var width = view.width; - var clip_buffer = Math.floor(0.05*width); - var clip_zone_size = 3*width; - - var variant=1; - if (variant === 0) { - var section0 = this.clip_zone_start; - var section2 = this.clip_zone_start + 2*width; - - if (x > section2) { - this.clip_zone_start += width; - } else if (x < section0) { - this.clip_zone_start -= width; - } - - return [this.clip_zone_start, this.clip_zone_start + clip_zone_size]; - } else if (variant === 1) { - var section1 = this.clip_zone_start + width; - - while (x > section1 + clip_buffer) { - this.clip_zone_start += clip_buffer; - section1 = this.clip_zone_start + width; - } - while (x < section1 - clip_buffer) { - this.clip_zone_start -= clip_buffer; - section1 = this.clip_zone_start + width; - } - return [this.clip_zone_start, this.clip_zone_start + clip_zone_size]; - } - }; - OncoprintSVGRenderer.prototype.clipCells = function(force) { - var self = this; - var oncoprint = this.oncoprint; - - var id_order = oncoprint.getIdOrder(); - var visible_bounds = this.curr_clip_bounds.fromViewInterval(this.getClipViewInterval(), this.oncoprint.getCellWidth() + this.oncoprint.getCellPadding()); - visible_bounds.first = Math.max(0, visible_bounds.first); - visible_bounds.last = Math.min(id_order.length-1, visible_bounds.last); - var prev_bounds = force ? this.prev_clip_bounds.set(id_order.length, -1) : this.getPreviousClipBounds(); - var to_show = prev_bounds.toShow(visible_bounds); - var to_hide = prev_bounds.toHide(visible_bounds); - if (to_show.length > 0 || to_hide.length > 0) { - var i, len; - for (i=0, len = to_show.length; i < len; i++) { - var datum_id = id_order[to_show[i]]; - _.each(self.cells, function(cell_map) { - var cell = cell_map[datum_id]; - if (cell) { - cell.style.display = 'inherit'; - } - }); - } - for (i=0, len = to_hide.length; i < len; i++) { - var datum_id = id_order[to_hide[i]]; - _.each(self.cells, function(cell_map) { - var cell = cell_map[datum_id]; - if (cell) { - cell.style.display = 'none'; - } - }); - } - } - - this.prev_clip_bounds.set(visible_bounds.first, visible_bounds.last); - }; - - OncoprintSVGRenderer.prototype.isTrackRenderable = function(track_id) { - return this.getRuleSet(track_id) && this.oncoprint.getTrackData(track_id).length > 0; - }; - OncoprintSVGRenderer.prototype.renderLabels = function() { - var self = this; - _.each(this.oncoprint.getTrackOrder(), function(track_id) { - var rule_set = self.getRuleSet(track_id); - self.renderTrackLabel(self.oncoprint, track_id, rule_set, self.getLabelSVG()); - }); - }; - OncoprintSVGRenderer.prototype.render = function(track_id) { - var self = this; - this.resizeLabelSVG(); - this.resizeCellDiv(); - - this.cell_div.style('display', 'none'); - var renderTrack = function(track_id) { - if (self.isTrackRenderable(track_id)) { - var rule_set = self.getRuleSet(track_id); - self.drawTrackCells(track_id, rule_set); - self.positionTrackCells(track_id); - self.renderTrackLabel(self.oncoprint, track_id, rule_set, self.getLabelSVG()); - } - }; - if (typeof track_id !== "undefined") { - renderTrack(track_id); - } else { - _.each(this.oncoprint.getTrackOrder(), function(track_id) { - renderTrack(track_id); - }); - } - self.clipCells(); - this.cell_div.style('display', 'inherit'); - this.renderLegend(); - }; - OncoprintSVGRenderer.prototype.renderLegend = function() { - var svg = this.legend_svg; - svg.selectAll('*').remove(); - var padding = 25; - var y = padding; - var rendered = {}; - var cell_width = this.oncoprint.getCellWidth(); - var self = this; - _.each(this.rule_sets, function(rule_set, track_id) { - var rule_set_id = rule_set.getRuleSetId(); - if (!rendered.hasOwnProperty(rule_set_id)) { - var text = svg.append('text').classed('ruleset_legend_label', true).text(rule_set.getLegendLabel()) - .attr('transform', utils.translate(0,y)); - var group = rule_set.putLegendGroup(svg, cell_width, self.oncoprint.getCellHeight(track_id)); - rendered[rule_set_id] = true; - group.attr('transform', utils.translate(200,y)); - var bounding_box = group.node().getBBox(); - y += bounding_box.height; - y += padding; - } - }); - this.resizeLegendSVG(); - } - OncoprintSVGRenderer.prototype.dragLabel = function(track_id) { - // TODO: everything about this method is technical debt - var self = this; - var other_tracks = this.oncoprint.getTrackOrder(); - var curr_pos = other_tracks.indexOf(track_id); - other_tracks.splice(curr_pos, 1); - - var new_track_pos; - var track_tops_true = this.getLabelTops(); - delete track_tops_true[track_id]; - var label_area_height = self.getLabelAreaHeight(); - var handler = function(evt) { - var track_tops = $.extend({},{},track_tops_true); - var mouse_y = evt.clientY - self.label_svg.node().offsetTop; - var render_y = utils.clamp(mouse_y, 0, label_area_height); - self.renderTrackLabel(self.oncoprint, track_id, false, self.label_svg, mouse_y).classed('dragging_label', true); - - var first_below = 0; - while (track_tops[other_tracks[first_below]] < render_y && first_below < other_tracks.length) { - first_below += 1; - } - if (first_below > 0) { - track_tops[other_tracks[first_below-1]] -= 3; - } - if (first_below < other_tracks.length) { - track_tops[other_tracks[first_below]] += 3; - } - new_track_pos = first_below; - _.each(track_tops, function(top, id) { - self.renderTrackLabel(self.oncoprint, id, false, self.label_svg, top); - }); - }; - $(self.label_svg.node()).on("mousemove", handler); - $(self.label_svg.node()).on("mouseup", function(evt) { - $(self.label_svg.node()).off("mousemove", handler); - self.oncoprint.moveTrack(track_id, new_track_pos); - }); - }; - return OncoprintSVGRenderer; -})(); diff --git a/packages/oncoprintjs/src/.js/renderers.js b/packages/oncoprintjs/src/.js/renderers.js deleted file mode 100644 index ab4ba92adac..00000000000 --- a/packages/oncoprintjs/src/.js/renderers.js +++ /dev/null @@ -1,114 +0,0 @@ -var utils = require('./utils'); -var exports = module.exports = {}; - -exports.continuous_data_rule = function continuous_data_rule(config) { -}; - -exports.discrete_data_rule = function discrete_data_rule(config) { -}; - -exports.gender_rule = function gender_rule(config) { - var ret = function(selection) { - selection.selectAll('rect') - .data(function(d) { return d; }) - .enter() - .append('rect') - .attr('x', function(d, i) { - return i * (config.rect_width + config.rect_padding); - }) - .attr('fill', function(d) { - if (d.attr_val === "MALE") - return 'black'; - if (d.attr_val === "FEMALE") - return 'pink'; - return 'grey'; - }) - .attr('height', config.rect_height) - .attr('width', config.rect_width); - - update(selection.selectAll('rect')); - }; - - ret.resort = function(selection, sample_order) { - selection.selectAll('rect') - .transition(function(d, i) { return i; }) - .attr('x', function(d, i) { - return sample_order[d.sample_id || d.sample] * - (config.rect_width + config.rect_padding); - }); - }; - - return ret; -}; - -exports.gene_rule = function gene_rule(config) { - var ret = function(selection) { - var sample_group = bind_sample_group(selection); - align_sample_group_horizontally(sample_group, config.rect_width, config.rect_padding); - cna_visualization(sample_group, config.cna_fills, config.rect_width, config.rect_height); - mutation_visualization(sample_group, config.rect_height / 3, config.rect_width, config.mutation_fill); - - update(sample_group); - }; - - ret.resort = function(selection, sample_order) { - selection.selectAll('g') - .transition(function(d, i) { return i; }) - .attr('transform', function(d, i) { - return utils.translate(sample_order[d.sample_id || d.sample] - * (config.rect_width + config.rect_padding), 0); - }); - } - - return ret; -}; - -// -// HELPER FUNCTIONS -// - -function align_sample_group_horizontally(sample_group, rect_width, rect_padding) { - return sample_group.attr('transform', function(d, i) { - return utils.translate(i * (rect_width + rect_padding), 0); - }); -} - -function bind_sample_group(selection) { - // binds the row-wise data to the row group, . See Bostock's - // explaination on nested selections: http://bost.ocks.org/mike/nest/#data - return selection.selectAll('g') - .data(function(d) { return d; }) - .enter().append('g'); -} - -// copy number alteration "subrule" -function cna_visualization(sample_group, cna_fills, rect_width, rect_height) { - return sample_group.append('rect') - .attr('fill', function(d) { return cna_fills[d.cna]; }) - .attr('height', rect_height) - .attr('width', rect_width); -} - -// mutation "subrule" -function mutation_visualization(sample_group, one_third_height, width, fill) { - var mutation = sample_group.append('rect') - .attr('y', one_third_height) - .attr('fill', function(d) { - // leave the ones without mutations uncolored - return d.mutation !== undefined ? fill : 'none'; - }) - .attr('height', one_third_height) - .attr('width', width); - - // remove the ones without mutations - mutation.filter(function(d) { - return d.mutation === undefined; - }).remove(); -} - -// TODO dev only -function update(sample_group) { - sample_group.on("click", function(d) { - d3.selectAll('.selected_sample').text(JSON.stringify(d)); - }); -} diff --git a/packages/oncoprintjs/src/.js/rendering_engine.js b/packages/oncoprintjs/src/.js/rendering_engine.js deleted file mode 100644 index 2d7910781f8..00000000000 --- a/packages/oncoprintjs/src/.js/rendering_engine.js +++ /dev/null @@ -1,178 +0,0 @@ -var d3 = require('d3'); -var _ = require('underscore'); -var utils = require('./utils'); -var renderer_functions = require('./renderers'); - -module.exports = function rendering_engine() { - var config = { row_height: 15 }; - var container_width = 100; - var element_padding = 1; - var element_width = 1; - var label_function = undefined; - var renderers = []; - - var me = function(container) { - - container = container.append('table').append('tr') - var label_container = container.append('td') - var oncoprint_container = container.append('td').append('div') - var svg = create_svg_for_container(oncoprint_container); - - var element_height = 20; - - // TODO! - label_container.append('svg').append('g').selectAll('text') - .data(label_function(container.datum())) - .enter() - .append('text') - .attr('text-anchor', function(d) { - return d.align === 'right' ? 'end' : 'start'; - }) - .attr('x', function(d) { return d.align === 'right' ? 50 : 0 }) - .attr('y', function(d, i) { - return (element_padding + 20 - 12 / 2) + i * 1.5 * (element_padding + 20 - 12 / 2); - }) - .attr('font-size', '12px') - .append('tspan') - .text(function(d) { return d.text; }) - - var bind_renderers_to_config = _.map(renderers, function(r) { - return r(config); - }); - - svg.selectAll('g') - .data(svg.data()[0], function(d) { - return oncoprint_key_function(d[0]); - }) - .enter().append('g') - .attr('transform', function(d,i) { - return utils.translate(0, i * config.row_height); - }) - .each(function(d,i) { - d3.select(this).call(bind_renderers_to_config[i]); - }) - .attr('class', 'oncoprint-row'); - }; - - me.insert_row = function(container, row, rendering_rule) { - var internal_data = container.datum(); - - utils.validate_row_against_rows(row, internal_data); - - var svg = get_svg_from_container(container); - - // make the svg one row taller - svg.attr('height', parseInt(svg.attr('height')) + config.row_height); - - // slide the current rows down - svg.selectAll('.oncoprint-row') - .attr('transform', function(d, i) { - return utils.translate(0, config.row_height + (i * config.row_height)); - }); - - // update the data which is bound to the container - internal_data.unshift(row); - container.datum(internal_data); - - // use d3 to detect which row is new and use the rendering function to render. - svg.selectAll('.oncoprint-row') - .data(internal_data, function(d) { - return oncoprint_key_function(d[0]) - }) - .enter() - .append('g') - .attr('class', 'oncoprint-row') - .attr('transform', utils.translate(0,0)) - .each(function(d,i) { - d3.select(this).call(rendering_rule(config)) - }) - }; - - // - // HELPER FUNCTIONS - // - - function compute_svg_width(rect_width, rect_padding, row_length) { - return (rect_width + rect_padding) * row_length; - } - - function infer_row_length(container) { - var rows = container.datum(); - if (rows === undefined) throw "Cannot infer row length from a container without rows."; - - var is_well_formed_matrix = _.every(rows, function(row) { - return row.length === rows[0].length; - }); - - if (!is_well_formed_matrix) throw "Uneven rows, cannot infer row length." - return rows[0].length; - } - - // styles, appends, does all the right stuff to the container - // so that we can go on to work with the inner . - function create_svg_for_container(container) { - container.style('width', container_width + "px") - .style('display', 'inline-block') - .style('overflow-x', 'auto') - .style('overflow-y', 'hidden'); - - // infer from the data that is already bound to the div. - var rows = container.datum(); - var row_length = infer_row_length(container) - - return container.append('svg') - .attr('width', compute_svg_width(element_width, element_padding, row_length)) - .attr('height', config.row_height * rows.length); - } - - function get_svg_from_container(container) { - // the first child contains the labels - return container.selectAll("table tr td:nth-child(2) div svg"); - } - - function oncoprint_key_function(d) { - return d.gene || d.attr_id; - } - - // - // GETTERS / SETTERS - // - - me.config = function(value) { - if (!arguments.length) return config; - config = value; - return me; - }; - - me.container_width = function(value) { - if (!arguments.length) return container_width; - container_width = value; - return me; - }; - - me.element_padding = function(value) { - if (!arguments.length) return element_padding; - element_padding = value; - return me; - }; - - me.element_width = function(value) { - if (!arguments.length) return element_width; - element_width = value; - return me; - }; - - me.label_function = function(value) { - if (!arguments.length) return label_function; - label_function = value; - return me; - }; - - me.renderers = function(value) { - if (!arguments.length) return renderers; - renderers = value; - return me; - }; - - return me; -}; diff --git a/packages/oncoprintjs/src/.js/signals.js b/packages/oncoprintjs/src/.js/signals.js deleted file mode 100644 index 8436eef5d37..00000000000 --- a/packages/oncoprintjs/src/.js/signals.js +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2015 Memorial Sloan-Kettering Cancer Center. - * - * This library is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS - * FOR A PARTICULAR PURPOSE. The software and documentation provided hereunder - * is on an "as is" basis, and Memorial Sloan-Kettering Cancer Center has no - * obligations to provide maintenance, support, updates, enhancements or - * modifications. In no event shall Memorial Sloan-Kettering Cancer Center be - * liable to any party for direct, indirect, special, incidental or - * consequential damages, including lost profits, arising out of the use of this - * software and its documentation, even if Memorial Sloan-Kettering Cancer - * Center has been advised of the possibility of such damage. - */ - -/* - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ -module.exports = { - REQUEST_PRE_TRACK_PADDING: 'request_pre_track_padding.signal.oncoprint', -} diff --git a/packages/oncoprintjs/src/.js/sorting.js b/packages/oncoprintjs/src/.js/sorting.js deleted file mode 100644 index f26c343afd8..00000000000 --- a/packages/oncoprintjs/src/.js/sorting.js +++ /dev/null @@ -1,51 +0,0 @@ -var _ = require('underscore'); - -var exports = module.exports = {}; - -function copy_array(array) { - return array.slice(); -} - -function rows_to_indexers(rows) { - return _.range(rows.length) - .reverse() // least significant first - .map(function(ith_row) { - return function(index) { return rows[ith_row][index]; }; - }); -} - -exports.genomic_metric = function genomic_metric(x) { - var cna_order = {AMPLIFIED:4, HOMODELETED:3, GAINED:2, HEMIZYGOUSLYDELETED:1, DIPLOID: 0, undefined: 0}; - var regulated_order = {UPREGULATED: 2, DOWNREGULATED: 1, undefined: 0}; - var mutation_order_f = function(m) { - // fusion > non-fusion mutations. - return m === undefined ? 0 : (/fusion($|,)/i.test(m)?2:1); - }; - - // need -1 to flip the order. - return -1 * (1000 * cna_order[x.cna] - + 100 * regulated_order[x.mrna] - + 10 * regulated_order[x.rppa] - + mutation_order_f(x.mutation)); -}; - -// indexers is least significant first. -exports.radix_sort = function radix_sort(datums, compare, indexers) { - var to_return = copy_array(datums); - - indexers.forEach(function(indexer) { - to_return = _.sortBy(to_return, function(x) { - return compare(indexer(x)); - }); - }); - - return to_return; -}; - -exports.sort_rows = function sort_rows(rows, metric) { - var indexers = rows_to_indexers(rows); - var sorted_column_indices = exports.radix_sort(_.range(rows[0].length), metric, indexers); - return _.map(rows, function(row) { - return sorted_column_indices.map(function(i) { return row[i]; }); - }); -}; diff --git a/packages/oncoprintjs/src/.js/track.js b/packages/oncoprintjs/src/.js/track.js deleted file mode 100644 index 85a081f1879..00000000000 --- a/packages/oncoprintjs/src/.js/track.js +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright (c) 2015 Memorial Sloan-Kettering Cancer Center. - * - * This library is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS - * FOR A PARTICULAR PURPOSE. The software and documentation provided hereunder - * is on an "as is" basis, and Memorial Sloan-Kettering Cancer Center has no - * obligations to provide maintenance, support, updates, enhancements or - * modifications. In no event shall Memorial Sloan-Kettering Cancer Center be - * liable to any party for direct, indirect, special, incidental or - * consequential damages, including lost profits, arising out of the use of this - * software and its documentation, even if Memorial Sloan-Kettering Cancer - * Center has been advised of the possibility of such damage. - */ - -/* - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ -var _ = require('underscore'); -var d3 = require('d3'); -var $ = require('jquery'); -var D3SVGCellRenderer = require('./D3SVGCellRenderer'); -var ReadOnlyObject = require('./ReadOnlyObject'); -var utils = require('./utils'); -var events = require('./events'); -var signals = require('./signals'); -var globals = require('./globals'); - -var defaultTrackConfig = { - label: 'Gene', - datum_id: function(d) { return d['sample'];}, - cell_height: 20, - track_height: 20, - track_padding: 5, - sort_cmp: undefined -}; - -function Track(data, config, oncoprint_config) { - var self = this; - self.config = $.extend({}, defaultTrackConfig, config || {}); // inherit from default - self.oncoprint_config = oncoprint_config; - - self.data = data; - self.filtered_data = data; - var cell_renderer; - - cell_renderer = new D3SVGCellRenderer(self.data, self.oncoprint_config.extend(self.config)); - cell_renderer.bindEvents(self); - self.renderer = new TrackSVGRenderer(self.oncoprint_config.extend(self.config), cell_renderer); - self.renderer.bindEvents(self); - - (function bindEvents() { - var pass_up_from_cell_renderer = [events.UPDATE_RENDER_RULES, events.CELL_CLICK, events.CELL_MOUSEENTER, events.CELL_MOUSELEAVE, signals.REQUEST_PRE_TRACK_PADDING]; - _.each(pass_up_from_cell_renderer, function(evt) { - $(cell_renderer).on(evt, function(e, data) { - $(self).trigger(evt, $.extend({}, data, {track: self})); - }) - }); - })(); - - self.getLabel = function() { - // TODO: label decorations - return self.config.label; - }; - - self.getDatumIds = function(sort_cmp, filter) { - filter = filter || function(d) { return true; }; - return _.map( - _.filter( - (sort_cmp && utils.stableSort(self.data, sort_cmp)) || self.data, - filter - ), - self.config.datum_id - ); - }; - - self.getSortedData = function(sort_cmp) { - return utils.stableSort(self.data, sort_cmp); - }; - - - self.useRenderTemplate = function(templName, params) { - self.renderer.useTemplate(templName, params); - }; - - self.filterData = function(filter) { - self.filtered_data = self.data.filter() - - $(self).trigger(events.TRACK_FILTER_DATA, {filtered_data: self.filtered_data}); - }; - - $(self).trigger(events.TRACK_INIT, {label_text: self.getLabel()}); -} - -function TrackTableRenderer(track_config, cell_renderer) { - // coupled with OncoprintTableRenderer - - var self = this; - var cell_renderer = cell_renderer; - self.fixed_row; - self.$fixed_row; - self.scrolling_row; - self.$scrolling_row; - self.label_area; - self.between_area; - self.cell_area; - var label_text; - - self.bindEvents = function(track) { - $(track).on(events.TRACK_INIT, function(e, data) { - label_text = data.label_text; - }); - }; - - var renderLabel = function(label_area) { - label_area.selectAll('*').remove(); - label_area.append('p').text(label_text).style('display', 'inline'); - }; - - var initCells = function(cell_area) { - cell_renderer.init(cell_area); - }; - - self.init = function(fixed_row, scrolling_row) { - self.fixed_row = fixed_row; - self.$fixed_row = $(self.fixed_row.node()); - self.scrolling_row = scrolling_row; - self.$scrolling_row = $(self.scrolling_row.node()); - self.scrolling_row.attr('height', track_config.get('track_height')); - - self.label_area = self.fixed_row.append('td').classed('track_label', true).attr('height', track_config.get('track_height')+5); - self.between_area = self.fixed_row.append('td').classed('track_between', true).style('position', 'relative').attr('height', track_config.get('track_height')+5); - self.between_area.append('p').style('display', 'inline').text('yoyoyo'); - self.cell_area = self.scrolling_row.append('td').classed('track_cells', true).attr('height', track_config.get('track_height')+5); - renderLabel(self.label_area); - initCells(self.cell_area) - }; - - self.addRule = function(params) { - cell_renderer.addRule(params); - }; - - self.useTemplate = function(templName, params) { - cell_renderer.useTemplate(templName, params); - }; - -} -module.exports = Track; diff --git a/packages/oncoprintjs/src/.js/utils.js b/packages/oncoprintjs/src/.js/utils.js deleted file mode 100644 index e274404c262..00000000000 --- a/packages/oncoprintjs/src/.js/utils.js +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (c) 2015 Memorial Sloan-Kettering Cancer Center. - * - * This library is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS - * FOR A PARTICULAR PURPOSE. The software and documentation provided hereunder - * is on an "as is" basis, and Memorial Sloan-Kettering Cancer Center has no - * obligations to provide maintenance, support, updates, enhancements or - * modifications. In no event shall Memorial Sloan-Kettering Cancer Center be - * liable to any party for direct, indirect, special, incidental or - * consequential damages, including lost profits, arising out of the use of this - * software and its documentation, even if Memorial Sloan-Kettering Cancer - * Center has been advised of the possibility of such damage. - */ - -/* - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ -var _ = require("underscore"); -var $ = require("jquery"); - -var exports = module.exports = {}; - -exports.invert_array = function invert_array(arr) { - return arr.reduce(function(curr, next, index) { - curr[next] = index; - return curr; - }, {}); -}; - -exports.extends = function(child_class, parent_class) { - child_class.prototype = Object.create(parent_class.prototype); - child_class.prototype.constructor = child_class; -}; - -exports.makeIdCounter = function() { - var counter = 0; - return function() { - counter += 1; - return counter; - }; -}; - -exports.clamp = function(t, a, b) { - return Math.max(Math.min(b,t), a); -}; - -exports.makeD3SVGElement = function(tag) { - return d3.select(document.createElementNS('http://www.w3.org/2000/svg', tag)); -}; - -exports.appendD3SVGElement = function(elt, target, svg) { - return target.select(function() { - return this.appendChild(elt.node().cloneNode(true)); - }); -}; - -exports.spaceSVGElementsHorizontally = function(group, padding) { - var x = 0; - var elts = exports.d3SelectChildren(group, '*').each(function() { - if (this.tagName === 'defs') { - // don't adjust spacing for a defs element - return; - } - var transform = d3.select(this).attr('transform'); - var y = transform && transform.indexOf("translate") > -1 && parseFloat(transform.split(",")[1], 10); - y = y || 0; - d3.select(this).attr('transform', exports.translate(x, y)); - x += this.getBBox().width; - x += padding; - }); - return group; -}; - -exports.textWidth = function(string, font) { - var obj = $('
'+string+'
') - .css({position: 'absolute', float: 'left', - 'white-space':'nowrap', visibility: 'hidden', - font: font}) - .appendTo($('body')); - var width = obj.width(); - obj.remove(); - return width; -}; - -exports.d3SelectChildren = function(parent, selector) { - return parent.selectAll(selector).filter(function() { - return this.parentNode === parent.node(); - }); -}; - -exports.warn = function(str, context) { - console.log("Oncoprint error in "+context+": "+str); -}; - -exports.stableSort = function(arr, cmp) { - // cmp returns something in [-1,0,1] - - var zipped = []; - _.each(arr, function(val, ind) { - zipped.push([val, ind]); - }); - var stable_cmp = function(a,b) { - var res = cmp(a[0], b[0]); - if (res === 0) { - if (a[1] < b[1]) { - res = -1; - } else if (a[1] > b[1]) { - res = 1; - } - } - return res; - }; - zipped.sort(stable_cmp); - return _.map(zipped, function(x) { return x[0];}); -}; - -exports.lin_interp = function(t, a, b) { - if (a[0] === '#') { - var r = [parseInt(a.substring(1,3), 16), parseInt(b.substring(1,3), 16)]; - var g = [parseInt(a.substring(3,5), 16), parseInt(b.substring(3,5), 16)]; - var b = [parseInt(a.substring(5,7), 16), parseInt(b.substring(5,7), 16)]; - var R = Math.round(r[0]*(1-t) + r[1]*t).toString(16); - var G = Math.round(g[0]*(1-t) + g[1]*t).toString(16); - var B = Math.round(b[0]*(1-t) + b[1]*t).toString(16); - - R = R.length < 2 ? '0'+R : R; - G = G.length < 2 ? '0'+G : G; - B = B.length < 2 ? '0'+B : B; - - return '#' + R + G + B; - } else if (isNaN(a) && a.indexOf('%') > -1) { - var A = parseFloat(a, 10); - var B = parseFloat(b, 10); - return (A*(1-t) + B*t)+'%'; - } else { - return a*(1-t) + b*t; - } -}; - -exports.translate = function(x,y) { - return "translate(" + x + "," + y + ")"; -}; - -exports.assert = function(bool, msg) { - if (!bool) { - throw msg; - } -} diff --git a/packages/oncoprintjs/src/.js/utils2.js b/packages/oncoprintjs/src/.js/utils2.js deleted file mode 100644 index e209a5a4529..00000000000 --- a/packages/oncoprintjs/src/.js/utils2.js +++ /dev/null @@ -1,113 +0,0 @@ -var _ = require("underscore"); - -var exports = module.exports = {}; - -exports.invert_array = function invert_array(arr) { - return arr.reduce(function(curr, next, index) { - curr[next] = index; - return curr; - }, {}); -}; - -exports.is_sample_genetically_altered = function is_sample_genetically_altered(datum) { - return datum.cna !== undefined - || datum.mutation !== undefined - || datum.rna !== undefined - || datum.protein !== undefined; -}; - -exports.makeD3SVGElement = function(tag) { - return d3.select(document.createElementNS('http://www.w3.org/2000/svg', tag)); -}; - -exports.warn = function(str, context) { - console.log("Oncoprint error in "+context+": "+str); -} - -exports.stableSort = function(arr, cmp) { - cmp = - var zipped = []; - _.each(arr, function(val, ind) { - zipped.push([val, ind]); - }); - zipped.sort(function(a,b) { - var cmp_res = cmp(a[0],b[0]); - if (cmp_res === 0) { - return a[1]-b[1]; - } else { - return cmp_res; - } - }); - // unzip - return _.map(zipped, function(x) { return x[0];}); -}; - -exports.sort_row_by_rows = function(row, rows) { - // TODO test this - var ordering = exports.invert_array( - rows[0].map(function(d) { - return d.sample || d.sample_id; - })); - - return _.sortBy(row, function(d) { - return ordering[d.sample || d.sample_id]; - }); -}; - -exports.translate = function translate(x,y) { - return "translate(" + x + "," + y + ")"; -}; - -exports.validate_rows = function(rows) { - // TODO -}; - -exports.validate_row_against_rows = function validate_row_against_rows(row, rows) { - if (rows.length === 0) { - throw "Rows are empty"; - } - - // make sure array lengths match - var lengths = rows.map(function(row) { - return row.length; - }); - - var matrix_width = lengths[0]; - - assert(matrix_width === row.length, - "Row lengths don't match: " + row.length + " and " + matrix_width); - - // TODO, jeese this is a lot of sorting and other computation - // just to validate the data. Is there a better way? - - // make sure sample_ids match - var matrix_sample_ids = rows[0].map(pluck_sample_id); - assert(matrix_sample_ids.length !== 0, "Cannot find sample identifier for rows."); - matrix_sample_ids = _.sortBy(matrix_sample_ids, _.identity); - - var row_sample_ids = row.map(pluck_sample_id); - assert(row_sample_ids.length !== 0, "Cannot find sample identifier for row."); - row_sample_ids = _.sortBy(row_sample_ids, _.identity); - - var intersection_stringified = JSON.stringify( - _.sortBy(_.intersection(matrix_sample_ids, row_sample_ids), - _.identity)); - - assert(JSON.stringify(row_sample_ids) === intersection_stringified, - "Sample ids do not match between new row and given rows.") - - assert(JSON.stringify(matrix_sample_ids) === intersection_stringified, - "Sample ids do not match between new row and given rows.") - - return true; -}; - -function assert(bool, msg) { - if (bool) return; - throw msg; -} -exports.assert = assert; - -function pluck_sample_id(datum) { - return datum.sample || datum.sample_id; -} From d63c90010612a97fbeaa21e110306358d4d937bc Mon Sep 17 00:00:00 2001 From: "Abeshouse, Adam A./Sloan Kettering Institute" Date: Thu, 3 Mar 2016 18:53:55 -0500 Subject: [PATCH 183/343] webgl implementation --- packages/oncoprintjs/webgl/CachedProperty.js | 26 + packages/oncoprintjs/webgl/README | 2 + packages/oncoprintjs/webgl/binarysearch.js | 21 + packages/oncoprintjs/webgl/gulpfile.js | 5 + packages/oncoprintjs/webgl/index.html | 9 + packages/oncoprintjs/webgl/main.js | 1 + packages/oncoprintjs/webgl/makesvgelement.js | 9 + .../oncoprintjs/webgl/oncoprint-bundle.js | 8957 ++++++++++++++++ .../webgl/oncoprint-test-bundle.js | 8990 +++++++++++++++++ packages/oncoprintjs/webgl/oncoprint.js | 474 + .../oncoprintjs/webgl/oncoprintlabelview.js | 264 + .../webgl/oncoprintlegendrenderer.js | 158 + packages/oncoprintjs/webgl/oncoprintmodel.js | 838 ++ .../oncoprintjs/webgl/oncoprintruleset.js | 846 ++ packages/oncoprintjs/webgl/oncoprintshape.js | 135 + .../oncoprintjs/webgl/oncoprintshapetosvg.js | 69 + .../oncoprintjs/webgl/oncoprintsvgcellview.js | 133 + .../oncoprintjs/webgl/oncoprinttooltip.js | 90 + .../webgl/oncoprinttrackinfoview.js | 79 + .../webgl/oncoprinttrackoptionsview.js | 190 + .../webgl/oncoprintwebglcellview.js | 673 ++ packages/oncoprintjs/webgl/promise.js | 24 + packages/oncoprintjs/webgl/setup.js | 727 ++ packages/oncoprintjs/webgl/svgfactory.js | 40 + packages/oncoprintjs/webgl/test.js | 27 + packages/oncoprintjs/webgl/todo | 1 + 26 files changed, 22788 insertions(+) create mode 100644 packages/oncoprintjs/webgl/CachedProperty.js create mode 100644 packages/oncoprintjs/webgl/README create mode 100644 packages/oncoprintjs/webgl/binarysearch.js create mode 100644 packages/oncoprintjs/webgl/gulpfile.js create mode 100644 packages/oncoprintjs/webgl/index.html create mode 100644 packages/oncoprintjs/webgl/main.js create mode 100644 packages/oncoprintjs/webgl/makesvgelement.js create mode 100644 packages/oncoprintjs/webgl/oncoprint-bundle.js create mode 100644 packages/oncoprintjs/webgl/oncoprint-test-bundle.js create mode 100644 packages/oncoprintjs/webgl/oncoprint.js create mode 100644 packages/oncoprintjs/webgl/oncoprintlabelview.js create mode 100644 packages/oncoprintjs/webgl/oncoprintlegendrenderer.js create mode 100644 packages/oncoprintjs/webgl/oncoprintmodel.js create mode 100644 packages/oncoprintjs/webgl/oncoprintruleset.js create mode 100644 packages/oncoprintjs/webgl/oncoprintshape.js create mode 100644 packages/oncoprintjs/webgl/oncoprintshapetosvg.js create mode 100644 packages/oncoprintjs/webgl/oncoprintsvgcellview.js create mode 100644 packages/oncoprintjs/webgl/oncoprinttooltip.js create mode 100644 packages/oncoprintjs/webgl/oncoprinttrackinfoview.js create mode 100644 packages/oncoprintjs/webgl/oncoprinttrackoptionsview.js create mode 100644 packages/oncoprintjs/webgl/oncoprintwebglcellview.js create mode 100644 packages/oncoprintjs/webgl/promise.js create mode 100644 packages/oncoprintjs/webgl/setup.js create mode 100644 packages/oncoprintjs/webgl/svgfactory.js create mode 100644 packages/oncoprintjs/webgl/test.js create mode 100644 packages/oncoprintjs/webgl/todo diff --git a/packages/oncoprintjs/webgl/CachedProperty.js b/packages/oncoprintjs/webgl/CachedProperty.js new file mode 100644 index 00000000000..1e7415b96ff --- /dev/null +++ b/packages/oncoprintjs/webgl/CachedProperty.js @@ -0,0 +1,26 @@ +var CachedProperty = (function() { + function CachedProperty(init_val, updateFn) { + this.value = init_val; + this.updateFn = updateFn; + this.bound_properties = []; + } + CachedProperty.prototype.update = function() { + this.value = this.updateFn.apply(null, arguments); + for (var i=0; i middle_key) { + lower_incl = middle + 1; + } else if (target_key < middle_key) { + upper_excl = middle; + } + } + if (return_closest_if_not_found) { + return lower_incl-1; + } else { + return null; + } +} \ No newline at end of file diff --git a/packages/oncoprintjs/webgl/gulpfile.js b/packages/oncoprintjs/webgl/gulpfile.js new file mode 100644 index 00000000000..a975039c91b --- /dev/null +++ b/packages/oncoprintjs/webgl/gulpfile.js @@ -0,0 +1,5 @@ +var gulp = require('gulp'); +var shell = require('gulp-shell'); + +gulp.task('default', shell.task(['browserify main.js -o oncoprint-bundle.js'])); +gulp.task('test', shell.task(['browserify test.js -o oncoprint-test-bundle.js'])); diff --git a/packages/oncoprintjs/webgl/index.html b/packages/oncoprintjs/webgl/index.html new file mode 100644 index 00000000000..2d4dfa43e16 --- /dev/null +++ b/packages/oncoprintjs/webgl/index.html @@ -0,0 +1,9 @@ + + + + + + +
+ + diff --git a/packages/oncoprintjs/webgl/main.js b/packages/oncoprintjs/webgl/main.js new file mode 100644 index 00000000000..bf1b4f9e3e4 --- /dev/null +++ b/packages/oncoprintjs/webgl/main.js @@ -0,0 +1 @@ +window.Oncoprint = require('./oncoprint.js'); \ No newline at end of file diff --git a/packages/oncoprintjs/webgl/makesvgelement.js b/packages/oncoprintjs/webgl/makesvgelement.js new file mode 100644 index 00000000000..3253f17cd7a --- /dev/null +++ b/packages/oncoprintjs/webgl/makesvgelement.js @@ -0,0 +1,9 @@ +module.exports = function (tag, attrs) { + var el = document.createElementNS('http://www.w3.org/2000/svg', tag); + for (var k in attrs) { + if (attrs.hasOwnProperty(k)) { + el.setAttribute(k, attrs[k]); + } + } + return el; +}; \ No newline at end of file diff --git a/packages/oncoprintjs/webgl/oncoprint-bundle.js b/packages/oncoprintjs/webgl/oncoprint-bundle.js new file mode 100644 index 00000000000..59f040a3367 --- /dev/null +++ b/packages/oncoprintjs/webgl/oncoprint-bundle.js @@ -0,0 +1,8957 @@ +(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o middle_key) { + lower_incl = middle + 1; + } else if (target_key < middle_key) { + upper_excl = middle; + } + } + if (return_closest_if_not_found) { + return lower_incl-1; + } else { + return null; + } +} +},{}],3:[function(require,module,exports){ +window.Oncoprint = require('./oncoprint.js'); +},{"./oncoprint.js":5}],4:[function(require,module,exports){ +module.exports = function (tag, attrs) { + var el = document.createElementNS('http://www.w3.org/2000/svg', tag); + for (var k in attrs) { + if (attrs.hasOwnProperty(k)) { + el.setAttribute(k, attrs[k]); + } + } + return el; +}; +},{}],5:[function(require,module,exports){ +var OncoprintModel = require('./oncoprintmodel.js'); +var OncoprintSVGCellView = require('./oncoprintsvgcellview.js'); +var OncoprintWebGLCellView = require('./oncoprintwebglcellview.js'); +var OncoprintLabelView = require('./oncoprintlabelview.js'); +var OncoprintRuleSet = require('./oncoprintruleset.js'); +var OncoprintTrackOptionsView = require('./oncoprinttrackoptionsview.js'); +var OncoprintLegendView = require('./oncoprintlegendrenderer.js');//TODO: rename +var OncoprintToolTip = require('./oncoprinttooltip.js'); +var OncoprintTrackInfoView = require('./oncoprinttrackinfoview.js'); + +var svgfactory = require('./svgfactory.js'); + +var Oncoprint = (function () { + // this is the controller + var nextTrackId = (function () { + var ctr = 0; + return function () { + ctr += 1; + return ctr; + } + })(); + function Oncoprint(ctr_selector, width) { + var self = this; + this.ctr_selector = ctr_selector; + + var $oncoprint_ctr = $('') + .css({'position':'relative', 'display':'inline-block'}) + .appendTo(ctr_selector); + + var $label_canvas = $('') + .css({'display':'inline-block', + 'position':'absolute', + 'left':'0px', + 'top':'0px'}) + .addClass("noselect") + .attr({'width':'150', 'height':'250'}); + + var $track_options_div = $('
') + .css({'position':'absolute', + 'left':'150px', + 'top':'0px'}) + .addClass("noselect") + .attr({'width':'50', 'height':'250'}); + + var $legend_div = $('
') + .css({'position':'absolute', + 'top':'250px'}) + .addClass("noselect"); + + var $cell_div = $('
') + .css({'width':width, + 'overflow-x':'scroll', + 'overflow-y':'hidden', + 'display':'inline-block', + 'position':'absolute', + 'left':'200px', + 'top':'0px'}) + .addClass("noselect"); + + var $cell_canvas = $('') + .attr('width', width) + .css({'position':'absolute', 'top':'0px', 'left':'0px'}) + .addClass("noselect"); + + var $dummy_scroll_div = $('
') + .css({'position':'absolute', + 'top':'0', + 'left':'0px', + 'height':'1px'}); + + var $cell_overlay_canvas = $('') + .attr('width', width) + .css({'position':'absolute', + 'top':'0px', + 'left':'0px'}) + .addClass("noselect"); + + var $track_info_div = $('
') + .css({'position':'absolute'}); + + $label_canvas.appendTo($oncoprint_ctr); + $cell_div.appendTo($oncoprint_ctr); + $track_options_div.appendTo($oncoprint_ctr); + $track_info_div.appendTo($oncoprint_ctr); + $legend_div.appendTo($oncoprint_ctr); + + + $cell_canvas.appendTo($cell_div); + $dummy_scroll_div.appendTo($cell_div); + $cell_overlay_canvas.appendTo($cell_div); + + this.$container = $oncoprint_ctr; + this.$cell_div = $cell_div; + this.$legend_div = $legend_div; + this.$track_options_div = $track_options_div; + this.$track_info_div = $track_info_div; + + this.model = new OncoprintModel(); + // Precisely one of the following should be uncommented + // this.cell_view = new OncoprintSVGCellView($svg_dev); + this.cell_view = new OncoprintWebGLCellView($cell_div, $cell_canvas, $cell_overlay_canvas, $dummy_scroll_div, this.model, new OncoprintToolTip($oncoprint_ctr), function(left, right) { + var curr_zoom = self.model.getHorzZoom(); + var unzoomed_left = left/curr_zoom; + var unzoomed_right = right/curr_zoom; + var new_zoom = Math.min(1, self.cell_view.visible_area_width / (unzoomed_right-unzoomed_left)); + self.setHorzZoom(new_zoom); + self.$cell_div.scrollLeft(unzoomed_left*new_zoom); + }); + + this.track_options_view = new OncoprintTrackOptionsView($track_options_div, + function(track_id) { self.removeTrack(track_id); }, + function(track_id, dir) { self.setTrackSortDirection(track_id, dir); }); + this.track_info_view = new OncoprintTrackInfoView($track_info_div); + + //this.track_info_view = new OncoprintTrackInfoView($track_info_div); + + this.label_view = new OncoprintLabelView($label_canvas, this.model, new OncoprintToolTip($oncoprint_ctr)); + this.label_view.setDragCallback(function(target_track, new_previous_track) { + self.moveTrack(target_track, new_previous_track); + }); + + this.legend_view = new OncoprintLegendView($legend_div, 10, 20); + + this.rendering_suppressed = false; + + this.keep_sorted = false; + // We need to handle scrolling this way because for some reason huge + // canvas elements have terrible resolution. + var cell_view = this.cell_view; + var model = this.model; + $cell_div.scroll(function() { + var scroll_left = $cell_div.scrollLeft(); + $cell_canvas.css('left', scroll_left); + $cell_overlay_canvas.css('left', scroll_left); + cell_view.scroll(model, scroll_left); + }); + + this.horz_zoom_callbacks = []; + + + $(window).resize(function() { + resizeAndOrganize(self); + }); + } + + var resizeLegendAfterTimeout = function(oncoprint) { + setTimeout(function() { + oncoprint.$container.css({'min-height':oncoprint.model.getCellViewHeight() + oncoprint.$legend_div.height() + 20}); + oncoprint.$legend_div.css({'top':oncoprint.model.getCellViewHeight() + 20}); + }, 0); + }; + var resizeAndOrganize = function(oncoprint) { + var ctr_width = $(oncoprint.ctr_selector).width(); + oncoprint.$container.css({'min-height':oncoprint.model.getCellViewHeight() + oncoprint.$legend_div.height() + 20}); + oncoprint.$track_options_div.css({'left':oncoprint.label_view.getWidth()}); + oncoprint.$track_info_div.css({'left':oncoprint.label_view.getWidth() + oncoprint.track_options_view.getWidth()}); + var cell_div_left = oncoprint.label_view.getWidth() + oncoprint.track_options_view.getWidth() + oncoprint.track_info_view.getWidth(); + oncoprint.$cell_div.css({'left':cell_div_left}); + oncoprint.cell_view.setWidth(ctr_width - cell_div_left-20, oncoprint.model); + oncoprint.$legend_div.css({'top':oncoprint.model.getCellViewHeight() + 20}); + }; + + var resizeAndOrganizeAfterTimeout = function(oncoprint) { + setTimeout(function() { + resizeAndOrganize(oncoprint); + }, 0); + }; + + + Oncoprint.prototype.scrollTo = function(left) { + this.$cell_div.scrollLeft(left); + } + Oncoprint.prototype.onHorzZoom = function(callback) { + this.horz_zoom_callbacks.push(callback); + } + Oncoprint.prototype.moveTrack = function(target_track, new_previous_track) { + this.model.moveTrack(target_track, new_previous_track); + this.cell_view.moveTrack(this.model); + this.label_view.moveTrack(this.model); + this.track_options_view.moveTrack(this.model); + this.track_info_view.moveTrack(this.model); + + if (this.keep_sorted) { + this.sort(); + } + + resizeAndOrganizeAfterTimeout(this); + } + + Oncoprint.prototype.keepSorted = function(keep_sorted) { + this.keep_sorted = (typeof keep_sorted === 'undefined' ? true : keep_sorted); + if (this.keep_sorted) { + this.sort(); + } + } + + Oncoprint.prototype.addTracks = function (params_list) { + // Update model + var track_ids = []; + params_list = params_list.map(function (o) { + o.track_id = nextTrackId(); + o.rule_set = OncoprintRuleSet(o.rule_set_params); + track_ids.push(o.track_id); + return o; + }); + + this.model.addTracks(params_list); + // Update views + this.cell_view.addTracks(this.model, track_ids); + this.label_view.addTracks(this.model, track_ids); + this.track_options_view.addTracks(this.model); + this.track_info_view.addTracks(this.model); + this.legend_view.addTracks(this.model); + + if (this.keep_sorted) { + this.sort(); + } + if (!this.rendering_suppressed) { + resizeAndOrganizeAfterTimeout(this); + } + return track_ids; + } + + Oncoprint.prototype.removeTrack = function (track_id) { + // Update model + this.model.removeTrack(track_id); + // Update views + this.cell_view.removeTrack(this.model, track_id); + this.label_view.removeTrack(this.model, track_id); + this.track_options_view.removeTrack(this.model, track_id); + this.track_info_view.removeTrack(this.model); + this.legend_view.removeTrack(this.model); + + if (this.keep_sorted) { + this.sort(); + } + resizeAndOrganizeAfterTimeout(this); + } + + Oncoprint.prototype.getZoomToFitHorz = function(ids) { + var width_to_fit_in; + if (typeof ids === 'undefined') { + width_to_fit_in = this.cell_view.getTotalWidth(this.model, true); + } else { + var furthest_right_id_index = -1; + var furthest_right_id; + var id_to_index_map = this.model.getIdToIndexMap(); + for (var i=0; i furthest_right_id_index) { + furthest_right_id_index = id_to_index_map[ids[i]]; + furthest_right_id = ids[i]; + } + } + width_to_fit_in = this.model.getColumnLeft(furthest_right_id) + this.model.getCellWidth(true); + } + var zoom = Math.min(1, this.cell_view.visible_area_width / width_to_fit_in); + return zoom; + } + Oncoprint.prototype.getHorzZoom = function () { + return this.model.getHorzZoom(); + } + + Oncoprint.prototype.getMinZoom = function() { + return this.model.getMinZoom(); + } + + Oncoprint.prototype.setHorzZoom = function (z) { + // Update model + this.model.setHorzZoom(z); + // Update views + this.cell_view.setHorzZoom(this.model); + + for (var i=0; i' + tooltip_html; + } + view.tooltip.fadeIn(200, renderedLabelWidth(view, view.labels[hovered_track]) + offset.left, view.label_tops[hovered_track] + offset.top, tooltip_html); + } else { + view.$canvas.css('cursor', 'auto'); + view.tooltip.hide(); + } + } + }); + + view.$canvas.on("mouseup mouseleave", function(evt) { + if (view.dragged_label_track_id !== null) { + var track_group = model.getContainingTrackGroup(view.dragged_label_track_id); + var previous_track_id = getLabelAbove(view, track_group, evt.offsetY, view.dragged_label_track_id); + stopDragging(view, previous_track_id); + } + view.tooltip.hide(); + }); + })(this); + + } + var renderedLabelWidth = function(view, label) { + return view.ctx.measureText(shortenLabelIfNecessary(view, label)).width; + }; + var updateFromModel = function(view, model) { + var track_tops = model.getTrackTops(); + var label_tops = model.getLabelTops(); + view.track_tops = track_tops; + view.label_tops = label_tops; + view.tracks = model.getTracks(); + + view.minimum_track_height = Number.POSITIVE_INFINITY; + view.maximum_label_width = 0; + for (var i=0; i view.maximum_label_length; + }; + var shortenLabelIfNecessary = function(view, label) { + if (isNecessaryToShortenLabel(view, label)) { + return label.substring(0, view.maximum_label_length-3) + '...'; + } else { + return label; + } + }; + var renderAllLabels = function(view) { + if (view.rendering_suppressed) { + return; + } + var font_size = view.getFontSize(); + view.ctx.font = 'bold '+font_size +'px serif'; + view.ctx.clearRect(0,0,view.$canvas[0].width,view.$canvas[0].height); + view.ctx.fillStyle = 'black'; + var tracks = view.tracks; + for (var i=0; i y) { + break; + } else { + candidate_track = track_ids[i]; + } + } + return candidate_track; + } + } + var getLabelBelow = function(view, track_ids, y, track_to_exclude) { + if (y > view.label_tops[track_ids[track_ids.length-1]]) { + return null; + } else { + var candidate_track = null; + for (var i=track_ids.length-1; i>=0; i--) { + if (track_to_exclude !== null && track_to_exclude === track_ids[i]) { + continue; + } + if (view.label_tops[track_ids[i]] < y) { + break; + } else { + candidate_track = track_ids[i]; + } + } + return candidate_track; + } + } + + var startDragging = function(view, track_id, mouse_y) { + view.dragged_label_track_id = track_id; + view.drag_mouse_y = mouse_y; + renderAllLabels(view); + } + var stopDragging = function(view, new_previous_track_id) { + view.drag_callback(view.dragged_label_track_id, new_previous_track_id); + view.dragged_label_track_id = null; + renderAllLabels(view); + } + OncoprintLabelView.prototype.getWidth = function() { + //return this.maximum_label_width + 20; + return Math.max(this.maximum_label_width + 10, 70); + } + OncoprintLabelView.prototype.getFontSize = function() { + return Math.max(Math.min(this.base_font_size, this.minimum_track_height), 7); + + } + OncoprintLabelView.prototype.setDragCallback = function(callback) { + this.drag_callback = callback; + } + OncoprintLabelView.prototype.removeTrack = function (model, track_id) { + updateFromModel(this, model); + resizeAndClear(this, model); + renderAllLabels(this, model); + } + OncoprintLabelView.prototype.moveTrack = function (model) { + updateFromModel(this, model); + resizeAndClear(this, model); + renderAllLabels(this, model); + } + OncoprintLabelView.prototype.addTracks = function (model, track_ids) { + for (var i=0; i rule set id + this.track_active_rules = {}; // from track id to active rule map (map with rule ids as keys) + this.track_info = {}; + + // Rule Set Properties + this.rule_sets = {}; // map from rule set id to rule set + + // Cached and Recomputed Properties + this.track_present_ids = new CachedProperty({}, function(model, track_id) { + var curr_track_present_ids = model.track_present_ids.get(); + var ids = {}; + var data = model.getTrackData(track_id); + var data_id_key = model.getTrackDataIdKey(track_id); + for (var i = 0; i < data.length; i++) { + ids[data[i][data_id_key]] = true; + } + curr_track_present_ids[track_id] = ids; + return curr_track_present_ids; + }); + this.present_ids = new CachedProperty({}, function() { + return setUnion(objectValues(model.track_present_ids.get())); + }); + this.track_present_ids.addBoundProperty(this.present_ids); + + this.id_to_index = {}; + + this.track_groups = []; + this.track_group_sort_priority = []; + + this.track_id_to_datum = {}; + + this.track_tops = new CachedProperty({}, function () { + var tops = {}; + var groups = model.getTrackGroups(); + var y = 0; + for (var i = 0; i < groups.length; i++) { + var group = groups[i]; + for (var j = 0; j < group.length; j++) { + var track_id = group[j]; + tops[track_id] = y; + y += model.getTrackHeight(track_id); + } + if (group.length > 0) { + y += model.getTrackGroupPadding(); + } + } + return tops; + }); + this.cell_tops = new CachedProperty({}, function() { + var track_tops = model.track_tops.get(); + var cell_tops = {}; + for (var k in track_tops) { + if (track_tops.hasOwnProperty(k)) { + cell_tops[k] = track_tops[k] + model.getTrackPadding(k); + } + } + return cell_tops; + }); + this.label_tops = new CachedProperty({}, function() { + return model.cell_tops.get(); + }); + + this.track_tops.addBoundProperty(this.cell_tops); + this.cell_tops.addBoundProperty(this.label_tops); + + this.column_left = new CachedProperty({}, function() { + var cell_width = model.getCellWidth(true); + var cell_padding = model.getCellPadding(true); + var left = {}; + var ids = model.getIdOrder(); + for (var i = 0; i < ids.length; i++) { + left[ids[i]] = i * (cell_width + cell_padding); + } + return left; + }); + + this.zoomed_column_left = new CachedProperty({}, function() { + var cell_width = model.getCellWidth(); + var cell_padding = model.getCellPadding(); + var left = {}; + var ids = model.getIdOrder(); + for (var i = 0; i < ids.length; i++) { + left[ids[i]] = i * (cell_width + cell_padding); + } + return left; + }); + this.column_left.addBoundProperty(this.zoomed_column_left); + + this.precomputed_comparator = new CachedProperty({}, function(model, track_id) { + var curr_precomputed_comparator = model.precomputed_comparator.get(); + curr_precomputed_comparator[track_id] = new PrecomputedComparator(model.getTrackData(track_id), + model.getTrackSortComparator(track_id), + model.getTrackSortDirection(track_id), + model.getTrackDataIdKey(track_id)); + return curr_precomputed_comparator; + });// track_id -> PrecomputedComparator + } + + OncoprintModel.prototype.toggleCellPadding = function () { + this.cell_padding_on = !this.cell_padding_on; + this.column_left.update(); + return this.cell_padding_on; + } + + OncoprintModel.prototype.getCellPadding = function (base) { + return (this.cell_padding * (base ? 1 : this.horz_zoom)) * (+this.cell_padding_on); + } + + OncoprintModel.prototype.getHorzZoom = function () { + return this.horz_zoom; + } + + OncoprintModel.prototype.getMinZoom = function() { + return MIN_ZOOM_PIXELS / (this.getIdOrder().length*this.getCellWidth(true) + (this.getIdOrder().length-1)*this.getCellPadding(true)); + } + + OncoprintModel.prototype.setHorzScroll = function(s) { + this.horz_scroll = Math.max(0, s); + return this.horz_scroll; + } + OncoprintModel.prototype.setVertScroll = function(s) { + this.vert_scroll = Math.max(0, s); + return this.vert_scroll; + } + OncoprintModel.prototype.getHorzScroll = function() { + return this.horz_scroll; + } + OncoprintModel.prototype.getVertScroll = function() { + return this.vert_scroll; + } + OncoprintModel.prototype.setHorzZoom = function (z) { + var min_zoom = this.getMinZoom(); + if (z <= 1 && z >= min_zoom) { + this.horz_zoom = z; + } else if (z > 1) { + this.horz_zoom = 1; + } else if (z < min_zoom) { + this.horz_zoom = min_zoom; + } + this.column_left.update(); + return this.horz_zoom; + } + + + OncoprintModel.prototype.getVertZoom = function() { + return this.vert_zoom; + } + + OncoprintModel.prototype.setVertZoom = function (z) { + if (z <= 1 && z >= 0) { + this.vert_zoom = z; + } else if (z > 1) { + this.vert_zoom = 1; + } else if (z < 0) { + this.vert_zoom = 0; + } + this.track_tops.update(); + return this.vert_zoom; + } + + OncoprintModel.prototype.hideTrackLegend = function(track_id) { + this.getRuleSet(track_id).exclude_from_legend = true; + } + + OncoprintModel.prototype.showTrackLegend = function(track_id) { + this.getRuleSet(track_id).exclude_from_legend = false; + } + + OncoprintModel.prototype.getIdentifiedShapeListList = function(track_id, use_base_width, sort_by_z) { + var active_rules = {}; + var data = this.getTrackData(track_id); + var id_key = this.getTrackDataIdKey(track_id); + var shapes = this.getRuleSet(track_id).apply(data, this.getCellWidth(use_base_width), this.getCellHeight(track_id), active_rules); + this.track_active_rules[track_id] = active_rules; + + var z_comparator = function(shapeA, shapeB) { + var zA = parseFloat(shapeA.z); + var zB = parseFloat(shapeB.z); + if (zA < zB) { + return -1; + } else if (zA > zB) { + return 1; + } else { + return 0; + } + }; + return shapes.map(function(shape_list, index) { + if (sort_by_z) { + shape_list.sort(z_comparator); + } + return { + id: data[index][id_key], + shape_list: shape_list + }; + }); + } + + OncoprintModel.prototype.getActiveRules = function(rule_set_id) { + var list_of_active_rules_maps = []; + for (var track_id in this.track_rule_set_id) { + if (this.track_rule_set_id.hasOwnProperty(track_id) && this.track_rule_set_id[track_id] === rule_set_id) { + list_of_active_rules_maps.push(this.track_active_rules[track_id]); + } + } + var active_rules = setUnion(list_of_active_rules_maps); + return this.rule_sets[rule_set_id].getRulesWithId().filter(function(rule_with_id) { + return !!active_rules[rule_with_id.id]; + }); + } + + OncoprintModel.prototype.getRuleSets = function() { + var self = this; + return Object.keys(this.rule_sets).map(function(rule_set_id) { + return self.rule_sets[rule_set_id]; + }); + } + + OncoprintModel.prototype.getCellWidth = function (base) { + return this.cell_width * (base ? 1 : this.horz_zoom); + } + + OncoprintModel.prototype.getCellHeight = function (track_id) { + return this.cell_height[track_id] * this.vert_zoom; + } + + OncoprintModel.prototype.getTrackInfo = function(track_id) { + return this.track_info[track_id]; + } + + OncoprintModel.prototype.setTrackInfo = function(track_id, msg) { + this.track_info[track_id] = msg; + } + + OncoprintModel.prototype.getTrackHeight = function(track_id) { + return this.getCellHeight(track_id) + 2*this.getTrackPadding(track_id); + } + + OncoprintModel.prototype.getTrackPadding = function (track_id) { + return this.track_padding[track_id] * this.vert_zoom; + } + OncoprintModel.prototype.getBottomPadding = function() { + return this.bottom_padding; + } + OncoprintModel.prototype.getTrackSortDirection = function(track_id) { + return this.track_sort_direction[track_id]; + } + OncoprintModel.prototype.setTrackSortDirection = function(track_id, dir) { + // see above for dir options + this.track_sort_direction[track_id] = dir; + this.precomputed_comparator.update(this, track_id); + } + + var computeIdToIndex = function(model) { + model.id_to_index = {}; + var id_order = model.getIdOrder(true); + for (var i=0; i= model.track_groups.length) { + model.track_groups.push([]); + } + model.track_groups[target_group].push(track_id); + + + + model.computeTrackIdToDatum(track_id); + model.track_present_ids.update(model, track_id); + model.precomputed_comparator.update(model, track_id); + + model.setIdOrder(Object.keys(model.present_ids.get())); + } + + var _getContainingTrackGroup = function (oncoprint_model, track_id, return_reference) { + var group; + for (var i = 0; i < oncoprint_model.track_groups.length; i++) { + if (oncoprint_model.track_groups[i].indexOf(track_id) > -1) { + group = oncoprint_model.track_groups[i]; + break; + } + } + if (group) { + return (return_reference ? group : group.slice()); + } else { + return undefined; + } + } + + var isRuleSetUsed = function(model, rule_set_id) { + var used = false; + var tracks = model.getTracks(); + for (var i=0; i indB) { + // switch if necessary to make process WLOG + var tmp = indA; + indA = indB; + indB = tmp; + should_negate_result = true; + } + // See if any changepoints in [indA, indB) + var upper_bd_excl = this.change_points.length; + var lower_bd_incl = 0; + var middle; + var res = 0; + while (true) { + middle = Math.floor((lower_bd_incl + upper_bd_excl) / 2); + if (lower_bd_incl === upper_bd_excl) { + break; + } else if (this.change_points[middle] >= indB) { + upper_bd_excl = middle; + } else if (this.change_points[middle] < indA) { + lower_bd_incl = middle+1; + } else { + res = -1; + break; + } + } + if (should_negate_result) { + res = res * -1; + } + return res; + } + return PrecomputedComparator; +})(); +module.exports = OncoprintModel; +},{"./CachedProperty.js":1,"./binarysearch.js":2}],9:[function(require,module,exports){ +/* Rule: + * + * condition: function from datum to boolean + * shapes - a list of Shapes + * legend_label + * exclude_from_legend + * + * Shape: + * type + * x + * y + * ... shape-specific attrs ... + * + * Attrs by shape: + * + * rectangle: x, y, width, height, stroke, stroke-width, fill + * triangle: x1, y1, x2, y2, x3, y3, stroke, stroke-width, fill + * ellipse: x, y, width, height, stroke, stroke-width, fill + * line: x1, y1, x2, y2, stroke, stroke-width + */ + +var Shape = require('./oncoprintshape.js'); + +function ifndef(x, val) { + return (typeof x === "undefined" ? val : x); +} + +function makeIdCounter() { + var id = 0; + return function () { + id += 1; + return id; + }; +} + +function shallowExtend(target, source) { + var ret = {}; + for (var key in target) { + if (target.hasOwnProperty(key)) { + ret[key] = target[key]; + } + } + for (var key in source) { + if (source.hasOwnProperty(key)) { + ret[key] = source[key]; + } + } + return ret; +} + + +var NA_SHAPES = [ + { + 'type': 'rectangle', + 'fill': 'rgba(255, 255, 255, 1)', + 'stroke': 'rgba(210,210,210,1)', + 'stroke-width': '1', + 'z': 0, + }, + { + 'type': 'line', + 'x1': '0%', + 'y1': '0%', + 'x2': '100%', + 'y2': '100%', + 'stroke': 'rgba(85, 85, 85, 0.7)', + 'stroke-width': '1', + 'z': '1', + }, +]; +var NA_STRING = "na"; +var NA_LABEL = "N/A"; + +var non_mutation_rule_params = { + '*': { + shapes: [{ + 'type': 'rectangle', + 'fill': 'rgba(211, 211, 211, 1)', + 'z': 1 + }], + exclude_from_legend: true, + }, + 'cna': { + 'AMPLIFIED': { + shapes: [{ + 'type': 'rectangle', + 'fill': 'rgba(255,0,0,1)', + 'x': '0%', + 'y': '0%', + 'width': '100%', + 'height': '100%', + 'z': 2, + }], + legend_label: 'Amplification', + }, + 'GAINED': { + shapes: [{ + 'type': 'rectangle', + 'fill': 'rgba(255,182,193,1)', + 'x': '0%', + 'y': '0%', + 'width': '100%', + 'height': '100%', + 'z': 2, + }], + legend_label: 'Gain', + }, + 'HOMODELETED': { + shapes: [{ + 'type': 'rectangle', + 'fill': 'rgba(0,0,255,1)', + 'x': '0%', + 'y': '0%', + 'width': '100%', + 'height': '100%', + 'z': 2, + }], + legend_label: 'Deep Deletion', + }, + 'HEMIZYGOUSLYDELETED': { + shapes: [{ + 'type': 'rectangle', + 'fill': 'rgba(143, 216, 216,1)', + 'x': '0%', + 'y': '0%', + 'width': '100%', + 'height': '100%', + 'z': 2, + }], + legend_label: 'Shallow Deletion', + } + }, + 'mrna': { + 'UPREGULATED': { + shapes: [{ + 'type': 'rectangle', + 'fill': 'rgba(0, 0, 0, 0)', + 'stroke': 'rgba(255, 153, 153, 1)', + 'stroke-width': '2', + 'x': '0%', + 'y': '0%', + 'width': '100%', + 'height': '100%', + 'z': 0, + }], + legend_label: 'mRNA Upregulation', + }, + 'DOWNREGULATED': { + shapes: [{ + 'type': 'rectangle', + 'fill': 'rgba(0, 0, 0, 0)', + 'stroke': 'rgba(102, 153, 204, 1)', + 'stroke-width': '2', + 'x': '0%', + 'y': '0%', + 'width': '100%', + 'height': '100%', + 'z': 0, + }], + legend_label: 'mRNA Downregulation', + }, + }, + 'rppa': { + 'UPREGULATED': { + shapes: [{ + 'type': 'triangle', + 'x1': '50%', + 'y1': '0%', + 'x2': '100%', + 'y2': '33.33%', + 'x3': '0%', + 'y3': '33.33%', + 'fill': 'rgba(0,0,0,1)', + 'z': 4, + }], + legend_label: 'Protein Upregulation', + }, + 'DOWNREGULATED': { + shapes: [{ + 'type': 'triangle', + 'x1': '50%', + 'y1': '100%', + 'x2': '100%', + 'y2': '66.66%', + 'x3': '0%', + 'y3': '66.66%', + 'fill': 'rgba(0,0,0,1)', + 'z': 4, + }], + legend_label: 'Protein Downregulation', + } + }, +}; + +var distinguish_mutation_rule_params = { + 'mut_type': { + 'MISSENSE': { + shapes: [{ + 'type': 'rectangle', + 'fill': '#008000', + 'x': '0%', + 'y': '33.33%', + 'width': '100%', + 'height': '33.33%', + 'z': 5.2, + }], + legend_label: 'Missense Mutation', + }, + 'INFRAME': { + shapes: [{ + 'type': 'rectangle', + 'fill': 'rgba(159, 129, 112, 1)', + 'x': '0%', + 'y': '33.33%', + 'width': '100%', + 'height': '33.33%', + 'z': 5.2, + }], + legend_label: 'Inframe Mutation', + }, + 'TRUNC': { + shapes: [{ + 'type': 'rectangle', + 'fill': 'rgba(0, 0, 0, 1)', + 'x': '0%', + 'y': '33.33%', + 'width': '100%', + 'height': '33.33%', + 'z': 5.2, + }], + legend_label: 'Truncating Mutation', + }, + 'FUSION': { + shapes: [{ + 'type': 'triangle', + 'fill': 'rgba(0, 0, 0, 1)', + 'x1': '0%', + 'y1': '0%', + 'x2': '100%', + 'y2': '50%', + 'x3': '0%', + 'y3': '100%', + 'z': 5.1, + }], + legend_label: 'Fusion', + } + } +}; + +var dont_distinguish_mutation_rule_params = { + 'mut_type': { + 'MISSENSE,INFRAME,TRUNC': { + shapes: [{ + 'type': 'rectangle', + 'fill': '#008000', + 'x': '0%', + 'y': '33.33%', + 'width': '100%', + 'height': '33.33%', + 'z': 5.2, + }], + legend_label: 'Mutation', + }, + 'FUSION': { + shapes: [{ + 'type': 'triangle', + 'fill': 'rgba(0, 0, 0, 1)', + 'x1': '0%', + 'y1': '0%', + 'x2': '100%', + 'y2': '50%', + 'x3': '0%', + 'y3': '100%', + 'z': 5.1, + }], + legend_label: 'Fusion', + } + } +}; + +var DEFAULT_GENETIC_ALTERATION_PARAMS = { + rule_params: $.extend({}, non_mutation_rule_params, distinguish_mutation_rule_params) +}; + +var DEFAULT_GENETIC_ALTERATION_PARAMS_DONT_DISTINGUISH_MUTATIONS = { + rule_params: $.extend({}, non_mutation_rule_params, dont_distinguish_mutation_rule_params) +}; + +var RuleSet = (function () { + var getRuleSetId = makeIdCounter(); + var getRuleId = makeIdCounter(); + + function RuleSet(params) { + /* params: + * - legend_label + * - exclude_from_legend + */ + this.rule_set_id = getRuleSetId(); + this.legend_label = params.legend_label; + this.exclude_from_legend = params.exclude_from_legend; + this.active_rule_ids = {}; + this.rules_with_id = []; + + } + + RuleSet.prototype.getLegendLabel = function () { + return this.legend_label; + } + + RuleSet.prototype.getRuleSetId = function () { + return this.rule_set_id; + } + + RuleSet.prototype.addRules = function (list_of_params) { + var self = this; + return list_of_params.map(function (params) { + return self.addRule(params); + }); + } + + RuleSet.prototype.addRule = function (params) { + var rule_id = getRuleId(); + this.rules_with_id.push({id: rule_id, rule: new Rule(params)}); + return rule_id; + } + + RuleSet.prototype.removeRule = function (rule_id) { + var index = -1; + for (var i = 0; i < this.rules_with_id.length; i++) { + if (this.rules_with_id[i].id === rule_id) { + index = i; + break; + } + } + if (index > -1) { + this.rules_with_id.splice(index, 1); + } + delete this.active_rule_ids[rule_id]; + } + + RuleSet.prototype.getRuleWithId = function (rule_id) { + var ret = null; + for (var i = 0; i < this.rules_with_id.length; i++) { + if (this.rules_with_id[i].id === rule_id) { + ret = this.rules_with_id[i]; + break; + } + } + return ret; + } + + RuleSet.prototype.isExcludedFromLegend = function () { + return this.exclude_from_legend; + } + + RuleSet.prototype.getRecentlyUsedRules = function () { + var self = this; + return Object.keys(this.active_rule_ids).map( + function (rule_id) { + return self.getRule(rule_id); + }); + } + + RuleSet.prototype.applyRulesToDatum = function (rules_with_id, datum, cell_width, cell_height) { + var shapes = []; + var rules_len = rules_with_id.length; + for (var j = 0; j < rules_len; j++) { + shapes = shapes.concat(rules_with_id[j].rule.apply(datum, cell_width, cell_height)); + } + return shapes; + } + RuleSet.prototype.apply = function (data, cell_width, cell_height, out_active_rules) { + // Returns a list of lists of concrete shapes, in the same order as data + var ret = []; + for (var i = 0; i < data.length; i++) { + var rules = this.getRulesWithId(data[i]); + if (typeof out_active_rules !== 'undefined') { + for (var j = 0; j < rules.length; j++) { + out_active_rules[rules[j].id] = true; + } + } + ret.push(this.applyRulesToDatum(rules, data[i], cell_width, cell_height)); + } + return ret; + } + + return RuleSet; +})(); + +var LookupRuleSet = (function () { + function LookupRuleSet(params) { + RuleSet.call(this, params); + this.lookup_map_by_key_and_value = {}; + this.lookup_map_by_key = {}; + this.universal_rules = []; + + this.rule_id_to_conditions = {}; + + this.addRule(NA_STRING, true, { + shapes: NA_SHAPES, + legend_label: NA_LABEL, + exclude_from_legend: false, + legend_config: {'type': 'rule', 'target': {'na': true}} + }); + } + LookupRuleSet.prototype = Object.create(RuleSet.prototype); + + LookupRuleSet.prototype.getRulesWithId = function (datum) { + if (typeof datum === 'undefined') { + return this.rules_with_id; + } + var ret = []; + ret = ret.concat(this.universal_rules); + for (var key in datum) { + if (typeof datum[key] !== 'undefined') { + var key_rule = this.lookup_map_by_key[key]; + if (typeof key_rule !== 'undefined') { + ret.push(key_rule); + } + var key_and_value_rule = (this.lookup_map_by_key_and_value[key] && this.lookup_map_by_key_and_value[key][datum[key]]) || undefined; + if (typeof key_and_value_rule !== 'undefined') { + ret.push(key_and_value_rule); + } + } + } + return ret; + } + + var indexRuleForLookup = function (rule_set, condition_key, condition_value, rule_with_id) { + if (condition_key === null) { + rule_set.universal_rules.push(rule_with_id); + } else { + if (condition_value === null) { + rule_set.lookup_map_by_key[condition_key] = rule_with_id; + } else { + rule_set.lookup_map_by_key_and_value[condition_key] = rule_set.lookup_map_by_key_and_value[condition_key] || {}; + rule_set.lookup_map_by_key_and_value[condition_key][condition_value] = rule_with_id; + } + } + rule_set.rule_id_to_conditions[rule_with_id.id] = rule_set.rule_id_to_conditions[rule_with_id.id] || []; + rule_set.rule_id_to_conditions[rule_with_id.id].push({key: condition_key, value: condition_value}); + }; + + LookupRuleSet.prototype.addRule = function (condition_key, condition_value, params) { + var rule_id = RuleSet.prototype.addRule.call(this, params); + + indexRuleForLookup(this, condition_key, condition_value, this.getRuleWithId(rule_id)); + + return rule_id; + } + + LookupRuleSet.prototype.linkExistingRule = function (condition_key, condition_value, existing_rule_id) { + indexRuleForLookup(this, condition_key, condition_value, this.getRuleWithId(existing_rule_id)); + } + + LookupRuleSet.prototype.removeRule = function (rule_id) { + RuleSet.prototype.removeRule.call(this, rule_id); + + while (this.rule_id_to_conditions[rule_id].length > 0) { + var condition = this.rule_id_to_conditions[rule_id].pop(); + if (condition.key === null) { + var index = -1; + for (var i = 0; i < this.universal_rules.length; i++) { + if (this.universal_rules[i].id === rule_id) { + index = i; + break; + } + } + if (index > -1) { + this.universal_rules.splice(index, 1); + } + } else { + if (condition.value === null) { + delete this.lookup_map_by_key[condition.key]; + } else { + delete this.lookup_map_by_key_and_value[condition.key][condition.value]; + } + } + } + delete this.rule_id_to_conditions[rule_id]; + } + return LookupRuleSet; +})(); + +var ConditionRuleSet = (function () { + function ConditionRuleSet(params) { + RuleSet.call(this, params); + this.rule_id_to_condition = {}; + + this.addRule(function (d) { + return d[NA_STRING] === true; + }, + {shapes: NA_SHAPES, + legend_label: NA_LABEL, + exclude_from_legend: false, + legend_config: {'type': 'rule', 'target': {'na': true}} + }); + } + ConditionRuleSet.prototype = Object.create(RuleSet.prototype); + + ConditionRuleSet.prototype.getRulesWithId = function (datum) { + if (typeof datum === 'undefined') { + return this.rules_with_id; + } + var ret = []; + for (var i = 0; i < this.rules_with_id.length; i++) { + if (this.rule_id_to_condition[this.rules_with_id[i].id](datum)) { + ret.push(this.rules_with_id[i]); + } + } + return ret; + } + + ConditionRuleSet.prototype.addRule = function (condition, params) { + var rule_id = RuleSet.prototype.addRule.call(this, params); + this.rule_id_to_condition[rule_id] = condition; + return rule_id; + } + + ConditionRuleSet.prototype.removeRule = function (rule_id) { + RuleSet.prototype.removeRule.call(this, rule_id); + delete this.rule_id_to_condition[rule_id]; + } + + return ConditionRuleSet; +})(); + +var CategoricalRuleSet = (function () { + function CategoricalRuleSet(params) { + /* params + * - category_key + * - categoryToColor + */ + LookupRuleSet.call(this, params); + + this.colors = ["#3366cc", "#dc3912", "#ff9900", "#109618", + "#990099", "#0099c6", "#dd4477", "#66aa00", + "#b82e2e", "#316395", "#994499", "#22aa99", + "#aaaa11", "#6633cc", "#e67300", "#8b0707", + "#651067", "#329262", "#5574a6", "#3b3eac", + "#b77322", "#16d620", "#b91383", "#f4359e", + "#9c5935", "#a9c413", "#2a778d", "#668d1c", + "#bea413", "#0c5922", "#743411"]; // Source: D3 + this.category_key = params.category_key; + this.category_to_color = ifndef(params.category_to_color, {}); + for (var category in this.category_to_color) { + if (this.category_to_color.hasOwnProperty(category)) { + addCategoryRule(this, category, this.category_to_color[category]); + } + } + } + CategoricalRuleSet.prototype = Object.create(LookupRuleSet.prototype); + + var addCategoryRule = function (ruleset, category, color) { + var legend_rule_target = {}; + legend_rule_target[ruleset.category_key] = category; + var rule_params = { + shapes: [{ + type: 'rectangle', + fill: color, + }], + legend_label: category, + exclude_from_legend: false, + legend_config: {'type': 'rule', 'target': legend_rule_target} + }; + ruleset.addRule(ruleset.category_key, category, rule_params); + }; + + CategoricalRuleSet.prototype.apply = function (data, cell_width, cell_height, out_active_rules) { + // First ensure there is a color for all categories + for (var i = 0, data_len = data.length; i < data_len; i++) { + if (data[i][NA_STRING]) { + continue; + } + var category = data[i][this.category_key]; + if (!this.category_to_color.hasOwnProperty(category)) { + var color = this.colors.pop(); + this.category_to_color[category] = color; + addCategoryRule(this, category, color); + } + } + // Then propagate the call up + return LookupRuleSet.prototype.apply.call(this, data, cell_width, cell_height, out_active_rules); + }; + + return CategoricalRuleSet; +})(); + +var LinearInterpRuleSet = (function () { + function LinearInterpRuleSet(params) { + /* params + * - value_key + * - value_range + */ + ConditionRuleSet.call(this, params); + this.value_key = params.value_key; + this.value_range = params.value_range; + this.inferred_value_range; + + this.makeInterpFn = function () { + var range = getEffectiveValueRange(this); + if (range[0] === range[1]) { + // Make sure non-zero denominator + range[0] -= range[0] / 2; + range[1] += range[1] / 2; + } + var range_spread = range[1] - range[0]; + var range_lower = range[0]; + return function (val) { + return (val - range_lower) / range_spread; + }; + }; + } + LinearInterpRuleSet.prototype = Object.create(ConditionRuleSet.prototype); + + var getEffectiveValueRange = function (ruleset) { + var ret = (ruleset.value_range && [ruleset.value_range[0], ruleset.value_range[1]]) || [undefined, undefined]; + if (typeof ret[0] === "undefined") { + ret[0] = ruleset.inferred_value_range[0]; + } + if (typeof ret[1] === "undefined") { + ret[1] = ruleset.inferred_value_range[1]; + } + return ret; + }; + + LinearInterpRuleSet.prototype.apply = function (data, cell_width, cell_height, out_active_rules) { + // First find value range + var value_min = Number.POSITIVE_INFINITY; + var value_max = Number.NEGATIVE_INFINITY; + for (var i = 0, datalen = data.length; i < datalen; i++) { + var d = data[i]; + if (isNaN(d[this.value_key])) { + continue; + } + value_min = Math.min(value_min, d[this.value_key]); + value_max = Math.max(value_max, d[this.value_key]); + } + if (value_min === Number.POSITIVE_INFINITY) { + value_min = 0; + } + if (value_max === Number.NEGATIVE_INFINITY) { + value_max = 0; + } + this.inferred_value_range = [value_min, value_max]; + this.updateLinearRules(); + + // Then propagate the call up + return ConditionRuleSet.prototype.apply.call(this, data, cell_width, cell_height, out_active_rules); + }; + + LinearInterpRuleSet.prototype.updateLinearRules = function () { + throw "Not implemented in abstract class"; + }; + + return LinearInterpRuleSet; +})(); + +var GradientRuleSet = (function () { + function GradientRuleSet(params) { + /* params + * - color_range + */ + LinearInterpRuleSet.call(this, params); + this.color_range; + (function setUpColorRange(self) { + var color_start; + var color_end; + try { + color_start = params.color_range[0] + .match(/rgba\(([\d.,]+)\)/) + .split(',') + .map(parseFloat); + color_end = params.color_range[1] + .match(/rgba\(([\d.,]+)\)/) + .split(',') + .map(parseFloat); + if (color_start.length !== 4 || color_end.length !== 4) { + throw "wrong number of color components"; + } + } catch (err) { + color_start = [0, 0, 0, 1]; + color_end = [255, 0, 0, 1]; + } + self.color_range = color_start.map(function (c, i) { + return [c, color_end[i]]; + }); + })(this); + this.gradient_rule; + } + GradientRuleSet.prototype = Object.create(LinearInterpRuleSet.prototype); + + GradientRuleSet.prototype.updateLinearRules = function () { + if (typeof this.gradient_rule !== "undefined") { + this.removeRule(this.gradient_rule); + } + var interpFn = this.makeInterpFn(); + var value_key = this.value_key; + var color_range = this.color_range; + this.gradient_rule = this.addRule(function (d) { + return d[NA_STRING] !== true; + }, + {shapes: [{ + type: 'rectangle', + fill: function (d) { + var t = interpFn(d[value_key]); + return "rgba(" + color_range.map( + function (arr) { + return (1 - t) * arr[0] + + t * arr[1]; + }).join(",") + ")"; + } + }], + exclude_from_legend: false, + legend_config: {'type': 'gradient', 'range': this.inferred_value_range} + }); + }; + + return GradientRuleSet; +})(); + +var BarRuleSet = (function () { + function BarRuleSet(params) { + LinearInterpRuleSet.call(this, params); + this.bar_rule; + this.fill = params.fill || 'rgba(179,141,155,1)'; + } + BarRuleSet.prototype = Object.create(LinearInterpRuleSet.prototype); + + BarRuleSet.prototype.updateLinearRules = function () { + if (typeof this.bar_rule !== "undefined") { + this.removeRule(this.bar_rule); + } + var interpFn = this.makeInterpFn(); + var value_key = this.value_key; + this.bar_rule = this.addRule(function (d) { + return d[NA_STRING] !== true; + }, + {shapes: [{ + type: 'rectangle', + y: function (d) { + var t = interpFn(d[value_key]); + return (1 - t) * 100 + "%"; + }, + height: function (d) { + var t = interpFn(d[value_key]); + return t * 100 + "%"; + }, + fill: this.fill, + }], + exclude_from_legend: false, + legend_config: {'type': 'number', 'range': this.inferred_value_range, 'color': this.fill} + }); + }; + + return BarRuleSet; +})(); + +var GeneticAlterationRuleSet = (function () { + function GeneticAlterationRuleSet(params) { + /* params: + * - rule_params + */ + LookupRuleSet.call(this, params); + (function addRules(self) { + var rule_params = params.rule_params; + for (var key in rule_params) { + if (rule_params.hasOwnProperty(key)) { + var key_rule_params = rule_params[key]; + if (key === '*') { + self.addRule(null, null, shallowExtend(rule_params['*'], {'legend_config': {'type': 'rule', 'target': {}}})); + } else { + for (var value in key_rule_params) { + if (key_rule_params.hasOwnProperty(value)) { + var equiv_values = value.split(","); + var legend_rule_target = {}; + legend_rule_target[equiv_values[0]] = value; + var rule_id = self.addRule(key, (equiv_values[0] === '*' ? null : equiv_values[0]), shallowExtend(key_rule_params[value], {'legend_config': {'type': 'rule', 'target': legend_rule_target}})); + for (var i = 1; i < equiv_values.length; i++) { + self.linkExistingRule(key, (equiv_values[i] === '*' ? null : equiv_values[i]), rule_id); + } + } + } + } + } + } + })(this); + } + GeneticAlterationRuleSet.prototype = Object.create(LookupRuleSet.prototype); + + return GeneticAlterationRuleSet; +})(); + +var Rule = (function () { + function Rule(params) { + this.shapes = params.shapes.map(function (shape) { + if (shape.type === 'rectangle') { + return new Shape.Rectangle(shape); + } else if (shape.type === 'triangle') { + return new Shape.Triangle(shape); + } else if (shape.type === 'ellipse') { + return new Shape.Ellipse(shape); + } else if (shape.type === 'line') { + return new Shape.Line(shape); + } + }); + this.legend_label = params.legend_label || ""; + this.exclude_from_legend = params.exclude_from_legend; + this.legend_config = params.legend_config;// {'type':'rule', 'target': {'mut_type':'MISSENSE'}} or {'type':'number', 'color':'rgba(1,2,3,1), 'range':[lower, upper]} or {'type':'gradient', 'color_range':['rgba(...)' or '#...', 'rgba(...)' or '#...'], 'number_range':[lower, upper]} + } + Rule.prototype.getLegendConfig = function () { + return this.legend_config; + } + Rule.prototype.apply = function (d, cell_width, cell_height) { + // Gets concrete shapes (i.e. computed + // real values from percentages) + var concrete_shapes = []; + for (var i = 0, shapes_len = this.shapes.length; i < shapes_len; i++) { + concrete_shapes.push(this.shapes[i].getComputedParams(d, cell_width, cell_height)); + } + return concrete_shapes; + } + + Rule.prototype.isExcludedFromLegend = function () { + return this.exclude_from_legend; + } + + return Rule; +})(); + +module.exports = function (params) { + if (params.type === 'categorical') { + return new CategoricalRuleSet(params); + } else if (params.type === 'gradient') { + return new GradientRuleSet(params); + } else if (params.type === 'bar') { + return new BarRuleSet(params); + } else if (params.type === 'gene') { + // TODO: specification of params + if (!!params.dont_distinguish_mutations) { + return new GeneticAlterationRuleSet($.extend({}, DEFAULT_GENETIC_ALTERATION_PARAMS_DONT_DISTINGUISH_MUTATIONS, params)); + } else { + return new GeneticAlterationRuleSet($.extend({}, DEFAULT_GENETIC_ALTERATION_PARAMS, params)); + } + } +} +},{"./oncoprintshape.js":10}],10:[function(require,module,exports){ +var Shape = (function() { + var default_parameter_values = { + 'width': '100%', + 'height': '100%', + 'x': '0%', + 'y': '0%', + 'z': 0, + 'x1': '0%', + 'x2': '0%', + 'x3': '0%', + 'y1': '0%', + 'y2': '0%', + 'y3': '0%', + 'stroke': 'rgba(0,0,0,0)', + 'fill': 'rgba(23,23,23,1)', + 'stroke-width': '0' + }; + var parameter_name_to_dimension_index = { + 'width': 0, + 'x':0, + 'x1':0, + 'x2':0, + 'x3':0, + 'height':1, + 'y':1, + 'y1':1, + 'y2':1, + 'y3':1 + }; + function Shape(params) { + this.params = params; + this.params_with_type = {}; + this.completeWithDefaults(); + this.markParameterTypes(); + } + Shape.prototype.completeWithDefaults = function() { + var required_parameters = this.getRequiredParameters(); + for (var i=0; i 0) { + var elt = track_shapes.pop(); + elt.parentNode.removeChild(elt); + } + + var y = cell_view.getTrackTop(model, track_id) + model.getTrackPadding(track_id); + // Now y is the top of the cells + var cell_width = model.getCellWidth(); + var cell_padding = model.getCellPadding(); + var cell_height = model.getTrackHeight(track_id); + + var shape_list_list = model.getRuleSet(track_id).apply( + model.getTrackData(track_id), + cell_width, + cell_height); + for (var i = 0; i < shape_list_list.length; i++) { + var x = i * (cell_width + cell_padding); + var shape_list = shape_list_list[i]; + for (var j = 0; j < shape_list.length; j++) { + track_shapes.push(cell_view.renderShape(shape_list[j], x, y)); + } + } + } + + var renderTracks = function (cell_view, model) { + var tracks = model.getTracks(); + for (var i = 0; i < tracks.length; i++) { + renderTrack(cell_view, model, tracks[i]); + } + } + + OncoprintSVGCellView.prototype.addTracks = function (model, track_ids) { + for (var i = 0; i < track_ids.length; i++) { + renderTrack(this, model, track_ids[i]); + } + } + + OncoprintSVGCellView.prototype.renderShape = function (shape, x, y) { + var tag; + if (shape.type === 'rectangle') { + tag = this.renderRectangle(shape, x, y); + } else if (shape.type === 'triangle') { + tag = this.renderTriangle(shape, x, y); + } else if (shape.type === 'ellipse') { + tag = this.renderEllipse(shape, x, y); + } else if (shape.type === 'line') { + tag = this.renderLine(shape, x, y); + } + return tag; + } + + var makeSVGTag = function (tag, attrs) { + var el = document.createElementNS('http://www.w3.org/2000/svg', tag); + for (var k in attrs) { + if (attrs.hasOwnProperty(k)) { + el.setAttribute(k, attrs[k]); + } + } + return el; + } + OncoprintSVGCellView.prototype.renderRectangle = function (rectangle, x, y) { + var new_rect = makeSVGTag('rect', { + 'x': x + parseFloat(rectangle.x), + 'y': y + parseFloat(rectangle.y), + 'width': rectangle.width, + 'height': rectangle.height, + 'stroke': rectangle.stroke, + 'stroke-width': rectangle['stroke-width'], + 'fill': rectangle.fill + }); + this.$svg[0].appendChild(new_rect); + return new_rect; + } + + OncoprintSVGCellView.prototype.renderTriangle = function (rectangle, x, y, + cell_width, cell_height) { + // TODO: implement + } + + OncoprintSVGCellView.prototype.renderEllipse = function (rectangle, x, y, + cell_width, cell_height) { + // TODO: implement + } + + OncoprintSVGCellView.prototype.renderLine = function (rectangle, x, y, + cell_width, cell_height) { + // TODO: implement + } + + OncoprintSVGCellView.prototype.setCellPadding = function () { + // TODO: what parameters + // TODO: implementation + } + + + OncoprintSVGCellView.prototype.setZoom = function (model, z) { + renderTracks(this, model); + } + OncoprintSVGCellView.prototype.setOrder = function () { + // TODO: what parameters + // TODO: implementation + } + + OncoprintSVGCellView.prototype.setTrackData = function (model, track_id) { + renderTrack(this, model, track_id); + } + + OncoprintSVGCellView.prototype.setRuleSet = function (model, track_id) { + renderTrack(this, model, track_id); + } + + return OncoprintSVGCellView; +})(); + +module.exports = OncoprintSVGCellView; +},{}],13:[function(require,module,exports){ +var OncoprintToolTip = (function() { + function OncoprintToolTip($container) { + this.$container = $container; + this.$div = $('
').appendTo($container).css({'background-color':'rgba(255,255,255,1)', 'position':'absolute', 'display':'none', 'border':'1px solid black', 'max-width':300, 'min-width':150}).addClass("noselect"); + this.hide_timeout_id = undefined; + this.show_timeout_id = undefined; + this.center = false; + + var self = this; + this.$div.on("mousemove", function(evt) { + evt.stopPropagation(); + cancelScheduledHide(self); + }); + this.$div.on("mouseleave", function(evt) { + evt.stopPropagation(); + self.hide(); + }); + } + OncoprintToolTip.prototype.show = function(wait, page_x, page_y, html_str, fade) { + cancelScheduledHide(this); + if (typeof wait !== 'undefined') { + var self = this; + cancelScheduledShow(this); + this.show_timeout_id = setTimeout(function() { + doShow(self, page_x, page_y, html_str, fade); + }, wait); + } else { + doShow(this, page_x, page_y, html_str, fade); + } + } + var doShow = function(tt, page_x, page_y, html_str, fade) { + cancelScheduledShow(tt); + tt.show_timeout_id = undefined; + tt.$div.html(html_str); + if (!fade) { + tt.$div.show(); + } else { + tt.$div.stop().fadeIn('fast'); + } + var container_offset = tt.$container.offset(); + var x = page_x - container_offset.left - (tt.center ? tt.$div.width()/2 : 0); + var y = page_y - container_offset.top - tt.$div.height(); + tt.$div.css({'top':y, 'left':x, 'z-index':9999}); + }; + var doHide = function(tt, fade) { + cancelScheduledHide(tt); + tt.hide_timeout_id = undefined; + if (!fade) { + tt.$div.hide(); + } else { + tt.$div.fadeOut(); + } + }; + var cancelScheduledShow = function(tt) { + clearTimeout(tt.show_timeout_id); + tt.show_timeout_id = undefined; + }; + var cancelScheduledHide = function(tt) { + clearTimeout(tt.hide_timeout_id); + tt.hide_timeout_id = undefined; + }; + OncoprintToolTip.prototype.showIfNotAlreadyGoingTo = function(wait, page_x, page_y, html_str) { + if (typeof this.show_timeout_id === 'undefined') { + this.show(wait, page_x, page_y, html_str); + } + } + OncoprintToolTip.prototype.hideIfNotAlreadyGoingTo = function(wait) { + if (typeof this.hide_timeout_id === 'undefined') { + this.hide(wait); + } + }; + OncoprintToolTip.prototype.hide = function(wait) { + cancelScheduledShow(this); + if (typeof wait !== 'undefined') { + var self = this; + cancelScheduledHide(this); + this.hide_timeout_id = setTimeout(function() { + doHide(self); + }, wait); + } else { + doHide(this); + } + } + OncoprintToolTip.prototype.fadeIn = function(wait, page_x, page_y, html_str) { + this.show(wait, page_x, page_y, html_str, true); + } + return OncoprintToolTip; +})(); + +module.exports = OncoprintToolTip; +},{}],14:[function(require,module,exports){ +var svgfactory = require('./svgfactory.js'); + +var OncoprintTrackInfoView = (function() { + function OncoprintTrackInfoView($div) { + this.$div = $div; + this.font_size = 12; + this.font_family = 'serif'; + this.font_weight = 'bold'; + this.width = 0; + + this.rendering_suppressed = false; + } + var renderAllInfo = function(view, model) { + if (view.rendering_suppressed) { + return; + } + view.$div.empty(); + var tracks = model.getTracks(); + view.width = 0; + var label_tops = model.getLabelTops(); + for (var i=0; i').css({'position':'absolute', + 'font-family':view.font_family, + 'font-weight':view.font_weight, + 'font-size':view.font_size}) + .addClass('noselect'); + $new_label.text(model.getTrackInfo(tracks[i])); + $new_label.appendTo(view.$div); + $new_label.css({'top':label_tops[tracks[i]] + (model.getCellHeight(tracks[i]) - $new_label[0].clientHeight)/2}); + view.width = Math.max(view.width, $new_label[0].clientWidth); + } + }; + var resize = function(view, model) { + view.$div.css({'width':view.getWidth(), 'height':model.getCellViewHeight()}); + }; + OncoprintTrackInfoView.prototype.getWidth = function() { + return this.width + 10; + } + OncoprintTrackInfoView.prototype.addTracks = function(model) { + renderAllInfo(this, model); + resize(this, model); + } + OncoprintTrackInfoView.prototype.moveTrack = function(model) { + renderAllInfo(this, model); + resize(this, model); + } + OncoprintTrackInfoView.prototype.removeTrack = function(model) { + renderAllInfo(this, model); + resize(this, model); + } + OncoprintTrackInfoView.prototype.setTrackInfo = function(model) { + renderAllInfo(this, model); + resize(this, model); + } + OncoprintTrackInfoView.prototype.suppressRendering = function() { + this.rendering_suppressed = true; + } + OncoprintTrackInfoView.prototype.releaseRendering = function(model) { + this.rendering_suppressed = false; + renderAllInfo(this, model); + resize(this, model); + } + OncoprintTrackInfoView.prototype.toSVGGroup = function(model, offset_x, offset_y) { + var root = svgfactory.group((offset_x || 0), (offset_y || 0)); + var label_tops = model.getLabelTops(); + var tracks = model.getTracks(); + for (var i=0; i').text(text).css({'font-weight': weight, 'font-size': 12, 'cursor': 'pointer', 'border-bottom':'1px solid rgba(0,0,0,0.3)'}) + .click(callback) + .hover(function () { + $(this).css({'background-color': 'rgb(200,200,200)'}); + }, function () { + $(this).css({'background-color': 'rgba(255,255,255,0)'}); + }); + }; + var $makeDropdownSeparator = function() { + return $('
  • ').css({'border-top': '1px solid black'}); + }; + + var renderTrackOptions = function(view, model, track_id) { + var $div,$img,$dropdown; + if (model.isTrackRemovable(track_id) || model.isTrackSortDirectionChangeable(track_id)) { + $div = $('
    ').appendTo(view.$div).css({'position': 'absolute', 'left': '0px', 'top': model.getTrackTops(track_id) + 'px'}); + $img = $('').appendTo($div).attr({'src': 'images/menudots.svg', 'width': view.img_size, 'height': view.img_size}).css({'float': 'left', 'cursor': 'pointer', 'border': '1px solid rgba(125,125,125,0)'}); + $dropdown = $('
      ').appendTo($div).css({'width': 120, 'display': 'none', 'list-style-type': 'none', 'padding-left': '6', 'padding-right': '6', 'float': 'right', 'background-color': 'rgb(255,255,255)'}); + view.track_options_$elts[track_id] = {'$div': $div, '$img': $img, '$dropdown': $dropdown}; + } + + if (model.isTrackRemovable(track_id)) { + $dropdown.append($makeDropdownOption('Remove track', 'normal', function(evt) { + evt.stopPropagation(); + view.removeCallback(track_id); + })); + + $img.hover(function(evt) { + if (!view.menu_shown[track_id]) { + $(this).css({'border': '1px solid rgba(125,125,125,0.3)'}); + } + }, function(evt) { + if (!view.menu_shown[track_id]) { + $(this).css({'border': '1px solid rgba(125,125,125,0)'}); + } + }); + $img.click(function(evt) { + evt.stopPropagation(); + if ($dropdown.is(":visible")) { + hideTrackMenu(view, track_id); + } else { + showTrackMenu(view, track_id); + } + hideMenusExcept(view, track_id); + }); + } + if (model.isTrackSortDirectionChangeable(track_id)) { + $dropdown.append($makeDropdownSeparator()); + var $sort_inc_li; + var $sort_dec_li; + var $dont_sort_li; + $sort_inc_li = $makeDropdownOption('Sort a-Z', (model.getTrackSortDirection(track_id) === 1 ? 'bold' : 'normal'), function(evt) { + evt.stopPropagation(); + $sort_inc_li.css('font-weight', 'bold'); + $sort_dec_li.css('font-weight', 'normal'); + $dont_sort_li.css('font-weight', 'normal'); + view.sortChangeCallback(track_id, 1); + }); + $sort_dec_li = $makeDropdownOption('Sort Z-a', (model.getTrackSortDirection(track_id) === -1 ? 'bold' : 'normal'), function(evt) { + evt.stopPropagation(); + $sort_inc_li.css('font-weight', 'normal'); + $sort_dec_li.css('font-weight', 'bold'); + $dont_sort_li.css('font-weight', 'normal'); + view.sortChangeCallback(track_id, -1); + }); + $dont_sort_li = $makeDropdownOption('Don\'t sort track', (model.getTrackSortDirection(track_id) === 0 ? 'bold' : 'normal'), function(evt) { + evt.stopPropagation(); + $sort_inc_li.css('font-weight', 'normal'); + $sort_dec_li.css('font-weight', 'normal'); + $dont_sort_li.css('font-weight', 'bold'); + view.sortChangeCallback(track_id, 0); + }); + $dropdown.append($sort_inc_li); + $dropdown.append($sort_dec_li); + $dropdown.append($dont_sort_li); + } + }; + + OncoprintTrackOptionsView.prototype.enableInteraction = function() { + this.interaction_disabled = false; + } + OncoprintTrackOptionsView.prototype.disableInteraction = function() { + this.interaction_disabled = true; + } + OncoprintTrackOptionsView.prototype.suppressRendering = function() { + this.rendering_suppressed = true; + } + OncoprintTrackOptionsView.prototype.releaseRendering = function(model) { + this.rendering_suppressed = false; + renderAllOptions(this, model); + } + OncoprintTrackOptionsView.prototype.getWidth = function() { + return 10 + this.img_size; + } + OncoprintTrackOptionsView.prototype.addTracks = function(model) { + renderAllOptions(this, model); + } + OncoprintTrackOptionsView.prototype.moveTrack = function(model) { + renderAllOptions(this, model); + } + OncoprintTrackOptionsView.prototype.removeTrack = function(model, track_id) { + delete this.track_options_$elts[track_id]; + renderAllOptions(this, model); + } + return OncoprintTrackOptionsView; +})(); + +module.exports = OncoprintTrackOptionsView; +},{}],16:[function(require,module,exports){ +var gl_matrix = require('gl-matrix'); +var svgfactory = require('./svgfactory.js'); + +// TODO: antialiasing + +var getWebGLCanvasContext = function ($canvas) { + try { + var canvas = $canvas[0]; + var ctx = canvas.getContext("experimental-webgl", {alpha: false}); + ctx.clearColor(1.0, 1.0, 1.0, 1.0); + ctx.clear(ctx.COLOR_BUFFER_BIT | ctx.DEPTH_BUFFER_BIT); + ctx.viewportWidth = canvas.width; + ctx.viewportHeight = canvas.height; + ctx.viewport(0, 0, ctx.viewportWidth, ctx.viewportHeight); + ctx.enable(ctx.DEPTH_TEST); + ctx.enable(ctx.BLEND); + ctx.blendEquation(ctx.FUNC_ADD); + ctx.blendFunc(ctx.SRC_ALPHA, ctx.ONE_MINUS_SRC_ALPHA); + ctx.depthMask(false); + + return ctx; + } catch (e) { + return null; + } +}; + +var extractRGBA = function (str) { + var ret = [0, 0, 0, 1]; + if (str[0] === "#") { + // hex, convert to rgba + var r = parseInt(str[1] + str[2], 16); + var g = parseInt(str[3] + str[4], 16); + var b = parseInt(str[5] + str[6], 16); + str = 'rgba('+r+','+g+','+b+',1)'; + } + var match = str.match(/^[\s]*rgba\([\s]*([0-9]+)[\s]*,[\s]*([0-9]+)[\s]*,[\s]*([0-9]+)[\s]*,[\s]*([0-9.]+)[\s]*\)[\s]*$/); + if (match.length === 5) { + ret = [parseFloat(match[1]) / 255, + parseFloat(match[2]) / 255, + parseFloat(match[3]) / 255, + parseFloat(match[4])]; + } + return ret; +}; +var createShaderProgram = function (view, vertex_shader, fragment_shader) { + var program = view.ctx.createProgram(); + view.ctx.attachShader(program, vertex_shader); + view.ctx.attachShader(program, fragment_shader); + + view.ctx.linkProgram(program); + + var success = view.ctx.getProgramParameter(program, view.ctx.LINK_STATUS); + if (!success) { + var msg = view.ctx.getProgramInfoLog(program); + view.ctx.deleteProgram(program); + throw "Unable to link shader program: " + msg; + } + + return program; +}; +var createShader = function (view, source, type) { + var shader = view.ctx.createShader(view.ctx[type]); + view.ctx.shaderSource(shader, source); + view.ctx.compileShader(shader); + + var success = view.ctx.getShaderParameter(shader, view.ctx.COMPILE_STATUS); + if (!success) { + var msg = view.ctx.getShaderInfoLog(shader); + view.ctx.deleteShader(shader); + throw "Unable to compile shader: " + msg; + } + + return shader; +}; + +var OncoprintWebGLCellView = (function () { + function OncoprintWebGLCellView($container, $canvas, $overlay_canvas, $dummy_scroll_div, model, tooltip, highlight_area_callback) { + this.$container = $container; + this.$canvas = $canvas; + this.$overlay_canvas = $overlay_canvas; + getWebGLContextAndSetUpMatrices(this); + getOverlayContextAndClear(this); + this.visible_area_width = $canvas[0].width; + + this.tooltip = tooltip; + this.tooltip.center = true; + + this.scroll_x = 0; + this.scroll_y = 0; + this.$dummy_scroll_div = $dummy_scroll_div; + + this.identified_shape_list_list = {}; + + this.vertex_position_buffer_by_zone = {}; // track_id -> zone_id -> gl.createBuffer() + this.vertex_color_buffer_by_zone = {}; // track_id -> zone_id -> gl.createBuffer() + + this.vertex_position_array = {}; // track_id -> zone_id -> vertex list + this.vertex_color_array = {}; // track_id -> zone_id -> vertex list + + this.rendering_suppressed = false; + + this.highlight_area_callback = (typeof highlight_area_callback === 'undefined' ? function() {} : highlight_area_callback); // function(left, right) { ... } + + + (function initializeShaders(self) {// Initialize shaders + var vertex_shader_source = ['attribute vec3 aVertexPosition;', + 'attribute vec4 aVertexColor;', + '', + 'uniform float scrollX;', + 'uniform float zoomX;', + 'uniform float scrollY;', + 'uniform float zoomY;', + 'uniform mat4 uMVMatrix;', + 'uniform mat4 uPMatrix;', + 'uniform float offsetY;', + 'varying vec4 vColor;', + 'void main(void) {', + ' gl_Position = vec4(aVertexPosition, 1.0);', + ' gl_Position[1] += offsetY;', + ' gl_Position[0] *= zoomX;', + ' gl_Position -= vec4(scrollX, 0.0, 0.0, 0.0);', + ' gl_Position = uPMatrix * uMVMatrix * gl_Position;', + ' vColor = aVertexColor;', + '}'].join('\n'); + var fragment_shader_source = ['precision mediump float;', + 'varying vec4 vColor;', + '', + 'void main(void) {', + ' gl_FragColor = vColor;', + '}'].join('\n'); + var vertex_shader = createShader(self, vertex_shader_source, 'VERTEX_SHADER'); + var fragment_shader = createShader(self, fragment_shader_source, 'FRAGMENT_SHADER'); + + var shader_program = createShaderProgram(self, vertex_shader, fragment_shader); + shader_program.vertexPositionAttribute = self.ctx.getAttribLocation(shader_program, 'aVertexPosition'); + self.ctx.enableVertexAttribArray(shader_program.vertexPositionAttribute); + shader_program.vertexColorAttribute = self.ctx.getAttribLocation(shader_program, 'aVertexColor'); + self.ctx.enableVertexAttribArray(shader_program.vertexColorAttribute); + + shader_program.pMatrixUniform = self.ctx.getUniformLocation(shader_program, 'uPMatrix'); + shader_program.mvMatrixUniform = self.ctx.getUniformLocation(shader_program, 'uMVMatrix'); + shader_program.scrollXUniform = self.ctx.getUniformLocation(shader_program, 'scrollX'); + shader_program.zoomXUniform = self.ctx.getUniformLocation(shader_program, 'zoomX'); + shader_program.offsetYUniform = self.ctx.getUniformLocation(shader_program, 'offsetY'); + + self.shader_program = shader_program; + })(this); + + (function initializeOverlayEvents(self) { + var dragging = false; + var drag_time_minimum = 200; + var drag_diff_minimum = 10; + var drag_is_valid = false; + var drag_is_valid_timeout = null; + var drag_start_x; + + $(document).on("mousemove", function () { + if (self.rendering_suppressed) { + return; + } + clearOverlay(self); + tooltip.hide(); + }); + self.$overlay_canvas.on("mousemove", function(evt) { + evt.stopPropagation(); + if (self.rendering_suppressed) { + return; + } + clearOverlay(self); + var offset = self.$overlay_canvas.offset(); + var mouseX = evt.pageX - offset.left; + var mouseY = evt.pageY - offset.top; + if (!dragging) { + var overlapping_cell = model.getOverlappingCell(mouseX + self.scroll_x, mouseY); + var overlapping_datum = (overlapping_cell === null ? null : model.getTrackDatum(overlapping_cell.track, overlapping_cell.id)); + if (overlapping_datum !== null) { + var left = model.getZoomedColumnLeft(overlapping_cell.id) - self.scroll_x; + overlayPaintRect(self, left, model.getCellTops(overlapping_cell.track), model.getCellWidth(), model.getCellHeight(overlapping_cell.track), "rgba(0,0,0,1)"); + var tracks = model.getTracks(); + for (var i=0; i 0) { + vertex_position_array.push(x - stroke_width, y - stroke_width, -1); + vertex_position_array.push(x + width + stroke_width, y - stroke_width, -1); + vertex_position_array.push(x + width + stroke_width, y + height + stroke_width, -1); + + vertex_position_array.push(x - stroke_width, y - stroke_width, -1); + vertex_position_array.push(x + width + stroke_width, y + height + stroke_width, -1); + vertex_position_array.push(x - stroke_width, y + height + stroke_width, -1); + + addVertexColor(vertex_color_array, shape.stroke, 6); + } + + vertex_position_array.push(x, y, j); + vertex_position_array.push(x + width, y, j); + vertex_position_array.push(x + width, y + height, j); + + vertex_position_array.push(x, y, j); + vertex_position_array.push(x + width, y + height, j); + vertex_position_array.push(x, y + height, j); + + addVertexColor(vertex_color_array, shape.fill, 6); + } else if (shape.type === "triangle") { + vertex_position_array.push(offset_x + parseFloat(shape.x1), parseFloat(shape.y1), j); + vertex_position_array.push(offset_x + parseFloat(shape.x2), parseFloat(shape.y2), j); + vertex_position_array.push(offset_x + parseFloat(shape.x3), parseFloat(shape.y3), j); + + addVertexColor(vertex_color_array, shape.fill, 3); + } else if (shape.type === "ellipse") { + var center = {x: offset_x + parseFloat(shape.x) + parseFloat(shape.width) / 2, y: parseFloat(shape.y) + parseFloat(shape.height) / 2}; + var horzrad = parseFloat(shape.width) / 2; + var vertrad = parseFloat(shape.height) / 2; + + vertex_position_array.push(center.x, center.y, j); + vertex_position_array.push(center.x + horzrad, center.y, j); + vertex_position_array.push(center.x + halfsqrt2 * horzrad, center.y + halfsqrt2 * vertrad, j); + + vertex_position_array.push(center.x, center.y, j); + vertex_position_array.push(center.x + halfsqrt2 * horzrad, center.y + halfsqrt2 * vertrad, j); + vertex_position_array.push(center.x, center.y + vertrad, j); + + vertex_position_array.push(center.x, center.y, j); + vertex_position_array.push(center.x, center.y + vertrad, j); + vertex_position_array.push(center.x - halfsqrt2 * horzrad, center.y + halfsqrt2 * vertrad, j); + + vertex_position_array.push(center.x, center.y, j); + vertex_position_array.push(center.x - halfsqrt2 * horzrad, center.y + halfsqrt2 * vertrad, j); + vertex_position_array.push(center.x - horzrad, center.y, j); + + vertex_position_array.push(center.x, center.y, j); + vertex_position_array.push(center.x - horzrad, center.y, j); + vertex_position_array.push(center.x - halfsqrt2 * horzrad, center.y - halfsqrt2 * vertrad, j); + + vertex_position_array.push(center.x, center.y, j); + vertex_position_array.push(center.x - halfsqrt2 * horzrad, center.y - halfsqrt2 * vertrad, j); + vertex_position_array.push(center.x, center.y - vertrad, j); + + vertex_position_array.push(center.x, center.y, j); + vertex_position_array.push(center.x, center.y - vertrad, j); + vertex_position_array.push(center.x + halfsqrt2 * horzrad, center.y - halfsqrt2 * vertrad, j); + + vertex_position_array.push(center.x, center.y, j); + vertex_position_array.push(center.x + halfsqrt2 * horzrad, center.y - halfsqrt2 * vertrad, j); + vertex_position_array.push(center.x + horzrad, center.y, j); + + addVertexColor(vertex_color_array, shape.fill, 3 * 8); + } else if (shape.type === "line") { + // For simplicity of dealing with webGL we'll implement lines as thin triangle pairs + var x1 = parseFloat(shape.x1) + offset_x; + var x2 = parseFloat(shape.x2) + offset_x; + var y1 = parseFloat(shape.y1); + var y2 = parseFloat(shape.y2); + + if (x1 !== x2) { + // WLOG make x1,y1 the one on the left + if (Math.min(x1, x2) === x2) { + var tmpx1 = x1; + var tmpy1 = y1; + x1 = x2; + y1 = y2; + x2 = tmpx1; + y2 = tmpy1; + } + } + + var perpendicular_vector = [y2 - y1, x1 - x2]; + var perpendicular_vector_length = Math.sqrt(perpendicular_vector[0]*perpendicular_vector[0] + perpendicular_vector[1]*perpendicular_vector[1]); + var unit_perp_vector = [perpendicular_vector[0]/perpendicular_vector_length, perpendicular_vector[1]/perpendicular_vector_length]; + + var half_stroke_width = parseFloat(shape['stroke-width'])/2; + var direction1 = [unit_perp_vector[0]*half_stroke_width, unit_perp_vector[1]*half_stroke_width]; + var direction2 = [direction1[0]*-1, direction1[1]*-1]; + var A = [x1 + direction1[0], y1 + direction1[1]]; + var B = [x1 + direction2[0], y1 + direction2[1]]; + var C = [x2 + direction1[0], y2 + direction1[1]]; + var D = [x2 + direction2[0], y2 + direction2[1]]; + + vertex_position_array.push(A[0], A[1], j); + vertex_position_array.push(B[0], B[1], j); + vertex_position_array.push(C[0], C[1], j); + + vertex_position_array.push(C[0], C[1], j); + vertex_position_array.push(D[0], D[1], j); + vertex_position_array.push(B[0], B[1], j); + + addVertexColor(vertex_color_array, shape.stroke, 3*2); + } + } + } + view.vertex_position_array[track_id] = zone_to_vertex_position_array; + view.vertex_color_array[track_id] = zone_to_vertex_color_array; + }; + var getShapes = function(view, model, track_id) { + if (view.rendering_suppressed) { + return; + } + view.identified_shape_list_list[track_id] = model.getIdentifiedShapeListList(track_id, true, true); + }; + OncoprintWebGLCellView.prototype.isUsable = function () { + return this.ctx !== null; + } + OncoprintWebGLCellView.prototype.removeTrack = function (model, track_id) { + clearZoneBuffers(this, model); + delete this.identified_shape_list_list[track_id]; + delete this.vertex_position_array[track_id]; + delete this.vertex_color_array[track_id]; + + renderAllTracks(this, model); + } + OncoprintWebGLCellView.prototype.moveTrack = function (model) { + clearZoneBuffers(this, model); + renderAllTracks(this, model); + } + OncoprintWebGLCellView.prototype.addTracks = function (model, track_ids) { + clearZoneBuffers(this, model); + for (var i=0; i