Commit 0c64d6e7 authored by Floreal Cabanettes's avatar Floreal Cabanettes
Browse files

Add a toolbox to display match properties on moseover, Implements #135

parent f7b1e243
......@@ -25,7 +25,13 @@ Or, you can select the zone by selecting a query and a target in the dropdown me
To come back to the initial view, click on the icon at the top right of the dotplot or press ESC.
### (3) Export
### (3) Matches details
For each match, you can view positions on query and on target and the precise identity value by placing mouse cursor over it.
Due to technical limits, it doesn't work for too small matches. You can zoom to make it working for them.
### (4) Export
Several export options are available:
......@@ -41,7 +47,7 @@ And, if you sorted the dotplot:
* Download the Fasta query file with contigs in the same order as in the dotplot.
* Download all contigs of the query assembled like the chromosomes of the target. We take the diagonal match line, and for all contigs that match the same chromosome, we stick them together, separated by a 100-N block.
### (4) Color scheme
### (5) Color scheme
You can change the default color scheme. Five other color schemes are available:
......@@ -53,40 +59,40 @@ You can change the default color scheme. Five other color schemes are available:
To change color scheme, click on the legend.
### (5) Match size filtering
### (6) Match size filtering
You can remove too small matches by moving the slider. By increments, it remove matches with size of 0.001 to 0.2% of the dotplot width. Too small matches are also removed by the `Remove noise` button (see below).
### (6) Match identity filtering
### (7) Match identity filtering
Set the minimal identity to show. All matches with a lower identity value will be hidden.
### (7) Strong precision
### (8) Strong precision
When checked, the strong precision check-box reduces match borders removing small matches from the graphical panel, often showing gaps between non contiguous matches.
### (8) Line breadth
### (9) Line breadth
Change the match lines thickness with the slider.
### (9) Chrom. border breadth
### (10) Chrom. border breadth
Change the visibility of the chromosomes borders with the slider.
### (10) Sort
### (11) Sort
You can sort (or unsort) contigs by clicking on the button. Contigs of the query will be sorted according to the reference. It will take few seconds.
How it works? For each contig of the query we search the region which have the biggest matches with the target and store these coordinates. Then, we sort contigs by their associated coordinates.
### (11) Hide noise
### (12) Hide noise
To remove noise. A match is considered noise if its size is small and its size frequency is quite high. Therefore we group matches by size bins, the number of bins corresponds to one tenth of the number of alignments, the bins are scanned in increasing size order to find the most represented one and from this one the one corresponding to one percent of its count is searched. All the alignments in bins smaller in size than this one are considered noise. It will take few seconds.
### (12) Similarity summary
### (13) Similarity summary
To ease dot plot comparison, clicking the summary button generates a bar graph presenting the reference similarity profile, meaning the sums of the projections of the matches on the reference per similarity category divided by the total reference length. This graph is produced after sorting the query along the reference, removing included matches and noise filtering; result not shown on the graphical panel. It gives a realistic view of the overall reference and query similarity which is often not very precisely measured through visual inspection.
### (13) Delete job
### (14) Delete job
By clicking on the button, your job will be definitively removed on the server. Be careful, this operation can not be undone!
\ No newline at end of file
......@@ -329,7 +329,7 @@ footer {
}
footer hr {
margin-top: 0;
margin-top: 50px;
}
h4.header-form {
......
......@@ -276,6 +276,29 @@ d3.boxplot.select_zone = function (x=null, y=null, x_zone=null, y_zone=null, for
}, 0);
};
/**
* Get human readable size in Kb or Mb for a number in bases
*
* @param {int} nbases size in bases
* @param {int} precision unit to use (auto: select according to number size)
* @param {string} space space before unit (space or non-breaking space for example)
* @returns {string} human readable size
*/
d3.boxplot.get_human_readable_size = function (nbases, precision=1, space=" ") {
let lab = "";
let prec = parseInt("1" + "0".repeat(precision))
if (nbases > 1000000) {
lab = (Math.round(nbases / (1000000 / prec)) / prec).toString() + space + "M";
}
else if (nbases > 1000) {
lab = (Math.round(nbases / (1000 / prec)) / prec).toString() + space + "K";
}
else {
lab = Math.round(nbases).toString();
}
return lab;
};
/**
* Draw left axis
*
......@@ -305,16 +328,7 @@ d3.boxplot.draw_left_axis = function (y_max, y_min = 0) {
let y = axis_length / 10 * i;
let y_t = y_min + y_size / 10 * i;
if (y_t >= 0 && y_t <= d3.boxplot.y_len) {
let y_lab = "";
if (y_t > 1000000) {
y_lab = (Math.round(y_t / 100000) / 10).toString() + " M";
}
else if (y_t > 1000) {
y_lab = (Math.round(y_t / 100) / 10).toString() + " K";
}
else {
y_lab = Math.round(y_t).toString();
}
let y_lab = d3.boxplot.get_human_readable_size(y_t);
container_left.append("line")
.attr("x1", y)
.attr("y1", 15)
......@@ -361,16 +375,7 @@ d3.boxplot.draw_bottom_axis = function (x_max, x_min = 0) {
let x = axis_length / 10 * i;
let x_t = x_min + x_size / 10 * i;
if (x_t >= 0 && x_t <= d3.boxplot.x_len) {
let x_lab = "";
if (x_t >= 1000000) {
x_lab = (Math.round(x_t / 100000) / 10).toString() + " M";
}
else if (x_t >= 1000) {
x_lab = (Math.round(x_t / 100) / 10).toString() + " K";
}
else {
x_lab = Math.round(x_t).toString()
}
let x_lab = d3.boxplot.get_human_readable_size(x_t);
svg_bottom.append("line")
.attr("x1", x)
.attr("y1", 0)
......@@ -883,6 +888,7 @@ d3.boxplot.draw_lines = function (lines=d3.boxplot.lines, x_len=d3.boxplot.x_len
for (let i=0; i <4; i++) {
d3.boxplot.__draw_idy_lines(i.toString(), lines, x_len, y_len)
}
d3.boxplot.events.filter_size(d3.boxplot.min_size);
};
......
......@@ -3,6 +3,16 @@ if (!d3 || !d3.boxplot) {
}
d3.boxplot.mousetip = {};
/**
* Get color (black/white) depending on bgColor so it would be clearly seen.
* @param bgColor
* @returns {string}
*/
d3.boxplot.mousetip.getColorByBgColor = function(bgColor) {
if (!bgColor) { return ''; }
return (parseInt(bgColor.replace('#', ''), 16) > 0xffffff / 2) ? '#000' : '#fff';
}
/**
* Mouse tip basis
*
......@@ -31,8 +41,6 @@ $.fn.mousetip = function(my_tip, relative_to=null, x=20, y=20) {
tip.hide();
if (!e.ctrlKey && !d3.boxplot.zone_selected) {
window.setTimeout(() => {
let rect = relative_to === null ? this.getBoundingClientRect() : $(relative_to)[0].getBoundingClientRect();
......@@ -52,6 +60,8 @@ $.fn.mousetip = function(my_tip, relative_to=null, x=20, y=20) {
let x_g = (e.pageX - posX_g) / width_c * d3.boxplot.scale,
y_g = d3.boxplot.scale - ((e.pageY - posY_g) / height_c * d3.boxplot.scale);
let match = d3.boxplot.mousetip.get_match(e);
let x_zone = "unknown";
for (let zone in d3.boxplot.x_zones) {
if (d3.boxplot.x_zones[zone][0] < x_g && x_g <= d3.boxplot.x_zones[zone][1]) {
......@@ -67,26 +77,58 @@ $.fn.mousetip = function(my_tip, relative_to=null, x=20, y=20) {
}
}
tip.html(`<table class="drawtooltip">
let html = "";
if (match !== null) {
html = `<strong>Query:</strong> ${match.y_zone}<br/>(${match.y_match[0]} - ${match.y_match[1]})<br/>
<strong>Target:</strong> ${match.x_zone}<br/>(${match.x_match[0]} - ${match.x_match[1]})<br/>
<strong>Identity:</strong> ${Math.round(match.idy * 100) / 100}`
}
else {
html = `<table class="drawtooltip">
<tr>
<td class="tt-label">Query:</td><td>${y_zone}</td>
</tr>
<tr>
<td class="tt-label">Target:</td><td>${x_zone}</td>
</tr>
</table>`);
</table>`;
}
if (!hidden) {
tip.show().css({
tip.html(html);
top: mouseY, left: mouseX
let css = {top: mouseY, left: mouseX}
});
if (match === null) {
css.color = "#000";
css.background = "#ededed";
}
else {
let idy_class = "";
if (match.idy >= 0.75) {
idy_class = "3";
}
else if (match.idy >= 0.5) {
idy_class = "2";
}
else if (match.idy >= 0.25) {
idy_class = "1";
}
else if (match.idy >= 0) {
idy_class = "0";
}
else {
idy_class = "-1"
}
css.background = d3.boxplot.color_idy[d3.boxplot.color_idy_theme][idy_class];
css.color = d3.boxplot.mousetip.getColorByBgColor(css.background)
}
}, 0);
if (!hidden && ((!e.ctrlKey && !d3.boxplot.zone_selected) || match !== null)) {
tip.show().css(css);
}
}
}, 0);
});
};
......@@ -119,4 +161,79 @@ d3.boxplot.mousetip.get_label = function (label) {
}
}
return label;
};
/**
* Get match override by mouse cursor
*
* @param e mouse event
* @returns {{x_zone: string, y_zone: string, x_match: float[], y_match: float[], idy: float}}
*/
d3.boxplot.mousetip.get_match = function(e) {
let rect = $("g.container")[0].getBoundingClientRect();
let posX = rect.left + window.scrollX,
posY = rect.top + window.scrollY,
width_c = rect.width,
height_c = rect.height;
let c_x = (e.pageX - posX) / width_c * d3.boxplot.scale,
c_y = d3.boxplot.scale - ((e.pageY - posY) / height_c * d3.boxplot.scale);
c_x = c_x / d3.boxplot.scale * d3.boxplot.x_len;
c_y = c_y / d3.boxplot.scale * d3.boxplot.y_len;
let error_x = d3.boxplot.content_lines_width / d3.boxplot.scale * d3.boxplot.x_len;
let error_y = d3.boxplot.content_lines_width / d3.boxplot.scale * d3.boxplot.y_len;
// let error_x = 0,
// error_y = 0;
let match = null;
let found = false;
for (let i=3; i>=0; i--) {
let j = 0;
while(!found && j < d3.boxplot.lines[i].length) {
let line = d3.boxplot.lines[i][j];
//console.log(c_x, c_y, line[0],line[1],line[2],line[3]);
let x_a = Math.min(line[0], line[1]);
let x_b = Math.max(line[0], line[1]);
let y_a = Math.min(line[2], line[3]);
let y_b = Math.max(line[2], line[3]);
if (x_a <= c_x && c_x <= x_b && y_a <= c_y && c_y <= y_b) {
let m = (y_b - y_a) / (x_b - x_a);
let p = y_a - (m * x_a);
let y_xmouse = (m * c_x) + p;
if (y_xmouse - error_y <= c_y && c_y <= y_xmouse + error_y) {
match = line;
found = true;
}
}
j++;
}
}
if (match !== null) {
let y_zone = match[5];
let x_zone = match[6];
let y_min = null;
let y_max = null;
if (y_zone in d3.boxplot.y_zones) {
let cy_min = d3.boxplot.y_zones[y_zone][0] / d3.boxplot.scale * d3.boxplot.y_len;
y_min = d3.boxplot.get_human_readable_size(match[2] - cy_min, 3, "&nbsp;");
y_max = d3.boxplot.get_human_readable_size(match[3] - cy_min, 3, "&nbsp;");
}
let x_min = null;
let x_max = null;
if (x_zone in d3.boxplot.x_zones) {
let cx_min = d3.boxplot.x_zones[x_zone][0] / d3.boxplot.scale * d3.boxplot.x_len;
x_min = d3.boxplot.get_human_readable_size(match[0] - cx_min, 3, "&nbsp;");
x_max = d3.boxplot.get_human_readable_size(match[1] - cx_min, 3, "&nbsp;");
}
console.log("MATCH:");
console.log(y_zone, y_min, y_max);
console.log(x_zone, x_min, x_max);
console.log(match[4]);
return {
x_zone: x_zone,
y_zone: y_zone,
x_match: [x_min, x_max],
y_match: [y_min, y_max],
idy: match[4]
}
}
return null;
}
\ 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