Commit 4eee1a33 authored by jhoogenboom's avatar jhoogenboom
Browse files

Finishing StuttermodelVis

* Properly implemented the options on the StuttermodelVis HTML
  visualisation.
* Added filtering options for marker and repeat unit to
  StuttermodelVis.
* Added StuttermodelVis to the Vis tool.

General visualisation changes:
* Updated Vega to v2.2.4.
* Fixed glitch that caused mouseover events in HTML visualisations
  to stop working after the renderer was switched.
* The file name suggested by the Save Image link in HTML
  visualisations is now derived from the name of the loaded data
  file.
parent 4011295e
......@@ -60,10 +60,18 @@ _DEF_SUBGRAPH_PADDING = 70
# This value can be overridden by the -w command line option.
_DEF_WIDTH = 600
# Default graph height in pixels.
# This value can be overridden by the -H command line option.
_DEF_HEIGHT = 400
# Default marker name matching regular expression.
# This value can be overridden by the -M command line option.
_DEF_MARKER_REGEX = ".*"
# Default repeat unit matching regular expression.
# This value can be overridden by the -U command line option.
_DEF_UNIT_REGEX = ".*"
# Default data file that Vega will read when -V/--vega is specified
# without providing data to embed in the file.
# It is currently impossible to override this value.
......@@ -113,8 +121,8 @@ def set_axis_scale(spec, scalename, value):
def create_visualisation(vistype, infile, outfile, vega, online, tidy, min_abs,
min_pct, bar_width, padding, marker, width,
log_scale):
min_pct, bar_width, padding, marker, width, height,
log_scale, repeat_unit, no_alldata):
# Get graph spec.
spec = json.load(resource_stream(
"fdstools", "vis/%svis/%svis.json" % (vistype, vistype)))
......@@ -126,13 +134,26 @@ def create_visualisation(vistype, infile, outfile, vega, online, tidy, min_abs,
del spec["data"][0]["values"]
spec["data"][0]["url"] = _DEF_DATA_FILENAME
# Apply settings.
# Apply width, height, and padding settings.
spec["width"] = width
set_data_formula_transform_value(spec, "yscale", "barwidth", bar_width)
if vistype == "stuttermodel":
set_data_formula_transform_value(spec, "yscale", "graphheight", height)
else:
set_data_formula_transform_value(spec, "yscale", "barwidth", bar_width)
set_data_formula_transform_value(spec, "yscale", "subgraphoffset", padding)
set_data_formula_transform_value(
spec, "table", "filter_marker", "'" + marker + "'")
if vistype == "sample" or vistype == "bgraw":
spec,
"fitfunctions" if vistype == "stuttermodel" else "table",
"filter_marker", "'" + marker + "'")
# Apply type-specific settings.
if vistype == "stuttermodel":
set_data_formula_transform_value(
spec, "fitfunctions", "filter_unit", "'" + repeat_unit + "'")
set_data_formula_transform_value(
spec, "fitfunctions", "show_all_data",
"false" if no_alldata else "true")
elif vistype == "sample" or vistype == "bgraw":
set_data_formula_transform_value(
spec, "table", "amplitude_threshold", min_abs)
set_data_formula_transform_value(
......@@ -142,12 +163,15 @@ def create_visualisation(vistype, infile, outfile, vega, online, tidy, min_abs,
spec, "table", "filter_threshold", min_pct)
set_data_formula_transform_value(
spec, "table", "low", "0.001" if log_scale else "0")
if not log_scale:
set_axis_scale(spec, "x", "linear")
elif vistype == "sample":
set_axis_scale(spec, "x", "sqrt")
else:
set_axis_scale(spec, "x", "log")
# Apply axis scale settings.
if vistype != "stuttermodel":
if not log_scale:
set_axis_scale(spec, "x", "linear")
elif vistype == "sample":
set_axis_scale(spec, "x", "sqrt")
else:
set_axis_scale(spec, "x", "log")
# Stringify spec.
if tidy:
......@@ -192,12 +216,14 @@ def create_visualisation(vistype, infile, outfile, vega, online, tidy, min_abs,
def add_arguments(parser):
parser.add_argument('type', metavar="TYPE",
choices=("sample", "profile", "bgraw"),
choices=("sample", "profile", "bgraw", "stuttermodel"),
help="the type of data to visualise; use 'sample' to visualise "
"sample data files and BGCorrect output; use 'profile' to "
"visualise background noise profiles obtained with BGEstimate, "
"BGHomStats, and BGPredict; use 'bgraw' to visualise raw "
"background noise data obtained with BGHomRaw")
"sample data files and bgcorrect output; use 'profile' to "
"visualise background noise profiles obtained with bgestimate, "
"bghomstats, and bgpredict; use 'bgraw' to visualise raw "
"background noise data obtained with bghomraw; use "
"'stuttermodel' to visualise models of stutter obtained from "
"stuttermodel")
parser.add_argument('infile', metavar="IN", nargs="?",
help="file containing the data to embed in the visualisation file; if "
"not specified, HTML visualisation files will contain a file "
......@@ -235,9 +261,14 @@ def add_arguments(parser):
"percentage of the true allele (default: %(default)s)")
visgroup.add_argument('-M', '--marker', metavar="REGEX",
default=_DEF_MARKER_REGEX,
help="[sample, profile, bgraw] only show graphs for the markers that "
"match the given regular expression; the default value "
"'%(default)s' matches any marker name")
help="[sample, profile, bgraw, stuttermodel] only show graphs for the "
"markers that match the given regular expression; the default "
"value '%(default)s' matches any marker name")
visgroup.add_argument('-U', '--repeat-unit', metavar="REGEX",
default=_DEF_UNIT_REGEX,
help="[stuttermodel] only show graphs for the repeat units that match "
"the given regular expression; the default value '%(default)s' "
"matches any repeat unit sequence")
visgroup.add_argument('-L', '--log-scale', action="store_true",
help="[sample, profile, bgraw] use logarithmic scale (for sample: "
"square root scale) instead of linear scale")
......@@ -247,12 +278,19 @@ def add_arguments(parser):
"%(default)s)")
visgroup.add_argument('-p', '--padding', metavar="N", type=pos_int_arg,
default=_DEF_SUBGRAPH_PADDING,
help="[sample, profile, bgraw] amount of padding (in pixels) between "
"graphs of different markers/alleles (default: %(default)s)")
help="[sample, profile, bgraw, stuttermodel] amount of padding (in "
"pixels) between graphs of different markers/alleles (default: "
"%(default)s)")
visgroup.add_argument('-w', '--width', metavar="N", type=pos_int_arg,
default=_DEF_WIDTH,
help="[sample, profile, bgraw] width of the graph area in pixels "
"(default: %(default)s)")
help="[sample, profile, bgraw, stuttermodel] width of the graph area "
"in pixels (default: %(default)s)")
visgroup.add_argument('-H', '--height', metavar="N", type=pos_int_arg,
default=_DEF_HEIGHT,
help="[stuttermodel] height of the graph area in pixels (default: "
"%(default)s)")
visgroup.add_argument('-A', '--no-alldata', action="store_true",
help="[stuttermodel] if specified, show only marker-specific fits")
#add_arguments
......@@ -278,5 +316,6 @@ def run(args):
create_visualisation(args.type, args.infile, args.outfile, args.vega,
args.online, args.tidy, args.min_abs, args.min_pct,
args.bar_width, args.padding, args.marker, args.width,
args.log_scale)
args.height, args.log_scale, args.repeat_unit,
args.no_alldata)
#run
......@@ -109,7 +109,7 @@
</td>
</tr>
<tr>
<td>Render as:</td>
<td>Renderer</td>
<td><input type="radio" name="renderer" value="svg" id="renderSVG" checked> SVG</td>
<td><input type="radio" name="renderer" value="canvas" id="renderCanvas"> Canvas</td>
</tr>
......@@ -120,6 +120,7 @@
<div id="vis"></div>
<script type="text/javascript">
var graph = false;
var fileName = "graph";
function parse(){
vg.parse.spec(graph_spec, function(chart){
var rendererName = "canvas";
......@@ -192,7 +193,7 @@
function setRenderer(value){
if(graph)
graph.renderer(value);
graph.renderer(value).update();
}
//Load the data (input is a fileList object; only the first file is loaded).
......@@ -200,7 +201,11 @@
if(!graph_spec || !fileList || !fileList.length)
return;
var reader = new FileReader();
reader.onload = function(theFile){
reader.onload = function(e){
if(fileList && fileList.length && fileList[0].name)
fileName = fileList[0].name.substr(0, fileList[0].name.lastIndexOf("."));
else
fileName = "graph";
graph_spec["data"][0]["values"] = reader.result;
parse();
};
......@@ -210,16 +215,9 @@
//Save image function.
function saveImage(){
var link = document.getElementById("saveLink");
if(document.getElementById("renderSVG").checked){
var svg = document.querySelector("svg.marks");
svg.setAttribute("xmlns", "http://www.w3.org/2000/svg"); //Vega does not set this for us
link.setAttribute("href", "data:image/svg+xml," + encodeURIComponent(svg.outerHTML));
link.setAttribute("download", "graph.svg");
}
else{
link.setAttribute("href", document.getElementsByClassName('marks')[0].toDataURL());
link.setAttribute("download", "graph.png");
}
var imageType = document.getElementById("renderSVG").checked? "svg": "png";
link.setAttribute("href", graph.toImageURL(imageType));
link.setAttribute("download", fileName + "." + imageType);
link.click();
link.setAttribute("href", "javascript:void(saveImage())");
return false;
......
......@@ -105,7 +105,7 @@
</td>
</tr>
<tr>
<td>Render as:</td>
<td>Renderer</td>
<td><input type="radio" name="renderer" value="svg" id="renderSVG" checked> SVG</td>
<td><input type="radio" name="renderer" value="canvas" id="renderCanvas"> Canvas</td>
</tr>
......@@ -116,6 +116,7 @@
<div id="vis"></div>
<script type="text/javascript">
var graph = false;
var fileName = "graph";
function parse(){
vg.parse.spec(graph_spec, function(chart){
var rendererName = "canvas";
......@@ -189,7 +190,7 @@
function setRenderer(value){
if(graph)
graph.renderer(value);
graph.renderer(value).update();
}
//Load the data (input is a fileList object; only the first file is loaded).
......@@ -197,7 +198,11 @@
if(!graph_spec || !fileList || !fileList.length)
return;
var reader = new FileReader();
reader.onload = function(theFile){
reader.onload = function(e){
if(fileList && fileList.length && fileList[0].name)
fileName = fileList[0].name.substr(0, fileList[0].name.lastIndexOf("."));
else
fileName = "graph";
graph_spec["data"][0]["values"] = reader.result;
parse();
};
......@@ -207,16 +212,9 @@
//Save image function.
function saveImage(){
var link = document.getElementById("saveLink");
if(document.getElementById("renderSVG").checked){
var svg = document.querySelector("svg.marks");
svg.setAttribute("xmlns", "http://www.w3.org/2000/svg"); //Vega does not set this for us
link.setAttribute("href", "data:image/svg+xml," + encodeURIComponent(svg.outerHTML));
link.setAttribute("download", "graph.svg");
}
else{
link.setAttribute("href", document.getElementsByClassName('marks')[0].toDataURL());
link.setAttribute("download", "graph.png");
}
var imageType = document.getElementById("renderSVG").checked? "svg": "png";
link.setAttribute("href", graph.toImageURL(imageType));
link.setAttribute("download", fileName + "." + imageType);
link.click();
link.setAttribute("href", "javascript:void(saveImage())");
return false;
......
......@@ -109,7 +109,7 @@
</td>
</tr>
<tr>
<td>Render as:</td>
<td>Renderer</td>
<td><input type="radio" name="renderer" value="svg" id="renderSVG" checked> SVG</td>
<td><input type="radio" name="renderer" value="canvas" id="renderCanvas"> Canvas</td>
</tr>
......@@ -120,6 +120,7 @@
<div id="vis"></div>
<script type="text/javascript">
var graph = false;
var fileName = "graph";
function parse(){
vg.parse.spec(graph_spec, function(chart){
var rendererName = "canvas";
......@@ -192,7 +193,7 @@
function setRenderer(value){
if(graph)
graph.renderer(value);
graph.renderer(value).update();
}
//Load the data (input is a fileList object; only the first file is loaded).
......@@ -200,7 +201,11 @@
if(!graph_spec || !fileList || !fileList.length)
return;
var reader = new FileReader();
reader.onload = function(theFile){
reader.onload = function(e){
if(fileList && fileList.length && fileList[0].name)
fileName = fileList[0].name.substr(0, fileList[0].name.lastIndexOf("."));
else
fileName = "graph";
graph_spec["data"][0]["values"] = reader.result;
parse();
};
......@@ -210,16 +215,9 @@
//Save image function.
function saveImage(){
var link = document.getElementById("saveLink");
if(document.getElementById("renderSVG").checked){
var svg = document.querySelector("svg.marks");
svg.setAttribute("xmlns", "http://www.w3.org/2000/svg"); //Vega does not set this for us
link.setAttribute("href", "data:image/svg+xml," + encodeURIComponent(svg.outerHTML));
link.setAttribute("download", "graph.svg");
}
else{
link.setAttribute("href", document.getElementsByClassName('marks')[0].toDataURL());
link.setAttribute("download", "graph.png");
}
var imageType = document.getElementById("renderSVG").checked? "svg": "png";
link.setAttribute("href", graph.toImageURL(imageType));
link.setAttribute("download", fileName + "." + imageType);
link.click();
link.setAttribute("href", "javascript:void(saveImage())");
return false;
......
......@@ -5,7 +5,6 @@
<title>Stutter Model Visualisation - FDSTools</title/>
<!-- BEGIN_LIBRARIES -->
<script src="http://vega.github.io/vega-editor/vendor/d3.min.js"></script>
<!--<script src="../vega.js"></script>-->
<script src="http://vega.github.io/vega/vega.min.js"></script>
<!-- END_LIBRARIES -->
<style>
......@@ -61,7 +60,7 @@
<th>Input file</th>
</tr>
<tr>
<td>Raw background noise data file:</td>
<td>Stutter model file:</td>
</tr>
<tr>
<td><input id="fileselect" type="file"></td>
......@@ -75,17 +74,21 @@
<th colspan="2">Filtering options</th>
</tr>
<tr>
<td>Require at least</td>
<td><input type="text" value="15" id="minN" size="3"> reads</td>
</tr>
<td>Repeat unit</td>
<td><input type="text" value=".*" id="unitFilter" size="20"></td>
</tr><!--
<tr>
<td>Require at least</td>
<td><input type="text" value="0.5" id="minP" size="3">% of highest allele per marker</td>
</tr>
<td>Stutter amount</td>
<td><input type="text" value="" id="foldFilter" size="3"></td>
</tr>-->
<tr>
<td>Marker name</td>
<td><input type="text" id="markerFilter" size="20" title="Supports regular expression syntax: e.g., use '.*' to match anything."></td>
</tr>
<tr>
<td>Fit to all data</td>
<td><input type="checkbox" id="allDataFilter" checked> show</td>
</tr>
</table>
<table class="optiongroup">
<tr>
......@@ -93,14 +96,14 @@
</tr>
<tr>
<td>Graph dimensions</td>
<td colspan="2"><input type="text" value="400" id="graphheight" size="3">x<input type="text" value="600" id="graphwidth" size="3"> px</td>
<td colspan="2"><input type="text" value="400" id="graphheight" size="3"> x <input type="text" value="600" id="graphwidth" size="3"> px</td>
</tr>
<tr>
<td>Subgraph spacing</td>
<td colspan="2"><input type="text" value="70" id="subgraphoffset" size="3"> px</td>
</tr>
<tr>
<td>Render as:</td>
<td>Renderer</td>
<td><input type="radio" name="renderer" value="svg" id="renderSVG" checked> SVG</td>
<td><input type="radio" name="renderer" value="canvas" id="renderCanvas"> Canvas</td>
</tr>
......@@ -108,42 +111,6 @@
<a id="saveLink" href="javascript:void(saveImage())" style="display: none">Save image</a>
</div>
</div> <!--
Select unit:
<select id="units">
<option>A</option>
<option>C</option>
<option>AC</option>
<option>AG</option>
<option>AT</option>
<option>AAC</option>
<option>AAG</option>
<option>ACT</option>
<option>AGC</option>
<option>AGG</option>
<option>ATC</option>
<option>AAAG</option>
<option>AACG</option>
<option>AAGG</option>
<option>AATC</option>
<option>AATG</option>
<option>ACAG</option>
<option>ACCT</option>
<option selected>AGAT</option>
<option>AGGC</option>
<option>AGGG</option>
<option>ATCC</option>
<option>AAAAG</option>
<option>AATCT</option>
</select>
Stutter type:
<select id="fold">
<option value="f2">-2 stutter</option>
<option value="f1" selected>-1 stutter</option>
<option value="pf1">+1 stutter</option>
<option value="pf2">+2 stutter</option>
</select>
<input type="checkbox" name="filterMarker" value="All data" checked>Show fit to all homozygotes data
<input type="checkbox" id="showMarkerFits" checked>Show fits per marker<br>
Show:
<input type="checkbox" id="showPoints" checked>Raw homozygotes data (as circles),
<input type="checkbox" id="showFromProfiles" checked>Profile means (as diamonds and for AGAT only);
......@@ -162,6 +129,7 @@
<div id="vis"></div>
<script type="text/javascript">
var graph = false;
var fileName = "graph";
function parse(){
vg.parse.spec(graph_spec, function(chart){
var rendererName = "canvas";
......@@ -210,7 +178,7 @@
function setRenderer(value){
if(graph)
graph.renderer(value);
graph.renderer(value).update();
}
//Load the data (input is a fileList object; only the first file is loaded).
......@@ -218,7 +186,11 @@
if(!graph_spec || !fileList || !fileList.length)
return;
var reader = new FileReader();
reader.onload = function(theFile){
reader.onload = function(e){
if(fileList && fileList.length && fileList[0].name)
fileName = fileList[0].name.substr(0, fileList[0].name.lastIndexOf("."));
else
fileName = "graph";
graph_spec["data"][0]["values"] = reader.result;
parse();
};
......@@ -228,16 +200,9 @@
//Save image function.
function saveImage(){
var link = document.getElementById("saveLink");
if(document.getElementById("renderSVG").checked){
var svg = document.querySelector("svg.marks");
svg.setAttribute("xmlns", "http://www.w3.org/2000/svg"); //Vega does not set this for us
link.setAttribute("href", "data:image/svg+xml," + encodeURIComponent(svg.outerHTML));
link.setAttribute("download", "graph.svg");
}
else{
link.setAttribute("href", document.getElementsByClassName('marks')[0].toDataURL());
link.setAttribute("download", "graph.png");
}
var imageType = document.getElementById("renderSVG").checked? "svg": "png";
link.setAttribute("href", graph.toImageURL(imageType));
link.setAttribute("download", fileName + "." + imageType);
link.click();
link.setAttribute("href", "javascript:void(saveImage())");
return false;
......@@ -292,22 +257,16 @@
if(setDataFormulaTransformValue("yscale", "subgraphoffset", value) && graph)
parse();
}, false);
document.getElementById("minN").addEventListener('change', function(){
var value = parseFloat(this.value);
if(isNaN(value))
return;
if(setDataFormulaTransformValue("table", "amplitude_threshold", value) && graph)
document.getElementById("unitFilter").addEventListener('change', function(){
if(setDataFormulaTransformValue("fitfunctions", "filter_unit", "'" + this.value + "'") && graph)
parse();
}, false);
document.getElementById("minP").addEventListener('change', function(){
var value = parseFloat(this.value);
if(isNaN(value))
return;
if(setDataFormulaTransformValue("table", "amplitude_pct_threshold", value) && graph)
document.getElementById("markerFilter").addEventListener('change', function(){
if(setDataFormulaTransformValue("fitfunctions", "filter_marker", "'" + this.value + "'") && graph)
parse();
}, false);
document.getElementById("markerFilter").addEventListener('change', function(){
if(setDataFormulaTransformValue("table", "filter_marker", "'" + this.value + "'") && graph)
document.getElementById("allDataFilter").addEventListener('change', function(){
if(setDataFormulaTransformValue("fitfunctions", "show_all_data", this.checked? "true": "false") && graph)
parse();
}, false);
......@@ -321,12 +280,12 @@
}, false);
//Sync graph_spec and display.
//FIXME: document.getElementById("markerFilter").value = getDataFormulaTransformValue("table", "filter_marker").replace(/^(['"]?)(.*(?=\1$))\1$/, '$2');
document.getElementById("unitFilter").value = getDataFormulaTransformValue("fitfunctions", "filter_unit").replace(/^(['"]?)(.*(?=\1$))\1$/, '$2');
document.getElementById("markerFilter").value = getDataFormulaTransformValue("fitfunctions", "filter_marker").replace(/^(['"]?)(.*(?=\1$))\1$/, '$2');
document.getElementById("allDataFilter").checked = getDataFormulaTransformValue("fitfunctions", "show_all_data") == "true";
document.getElementById("graphwidth").value = graph_spec["width"];
document.getElementById("graphheight").value = getDataFormulaTransformValue("yscale", "graphheight");
document.getElementById("subgraphoffset").value = getDataFormulaTransformValue("yscale", "subgraphoffset");
document.getElementById("minN").value = getDataFormulaTransformValue("table", "amplitude_threshold");
document.getElementById("minP").value = getDataFormulaTransformValue("table", "amplitude_pct_threshold");
if(has_data){
document.getElementById("options").style.display = "none";
parse();
......
......@@ -25,6 +25,25 @@
}
},
"transform": [
{
"type": "formula",
"field": "filter_unit",
"expr": "'.*'"
},
{
"type": "formula",
"field": "filter_marker",
"expr": "'.*'"
},
{
"type": "formula",
"field": "show_all_data",
"expr": "true"
},
{
"type": "filter",
"test": "(datum.marker != 'All data' && test('^' + datum.filter_unit + '$', datum.unit) && test('^' + datum.filter_marker + '$', datum.marker)) || (datum.marker == 'All data' && datum.show_all_data)"
},
{
"type": "formula",
"field": "subgraph",
......
This diff is collapsed.
......@@ -24,14 +24,15 @@ DROP strandbias Find strand bias in the data
DROP annotate-alleles Annotate true alleles in sample based on allelelist
Visualisations:
MIDDLE trends Fit repeat length vs stutter amount
LOW allelegraph Homozygosity/heterozygosity
LOW allelegraph Homozygosity/heterozygosity*
LOW blame Common alleles
LOW qqplot Q-Q plot of normal/lognormal distribution
LOW stability Profile distance vs amount of subsampling
DONE samplevis Sample data
DONE profiles Background profiles
DONE bg Dotplots of noise ratios in homozygous samples
DONE trends Fit repeat length vs stutter amount
*Blocking issue: https://github.com/vega/vega/issues/338
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
......@@ -96,3 +97,4 @@ To-do:
account as well.
* Perhaps there should be a version of BGEstimate that makes a profile for each
genotype instead of each allele. This allows for the detection of hybrids.
* Add plotting of raw data points to StuttermodelVis.
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment