diff options
Diffstat (limited to 'wqflask/wqflask/static/new/javascript/manhattan_plot.coffee')
| -rw-r--r-- | wqflask/wqflask/static/new/javascript/manhattan_plot.coffee | 534 |
1 files changed, 534 insertions, 0 deletions
diff --git a/wqflask/wqflask/static/new/javascript/manhattan_plot.coffee b/wqflask/wqflask/static/new/javascript/manhattan_plot.coffee new file mode 100644 index 00000000..492c2f3f --- /dev/null +++ b/wqflask/wqflask/static/new/javascript/manhattan_plot.coffee @@ -0,0 +1,534 @@ +lodchart = () -> + width = 800 + height = 500 + margin = {left:60, top:40, right:40, bottom: 40, inner:5} + axispos = {xtitle:25, ytitle:30, xlabel:5, ylabel:5} + titlepos = 20 + ylim = null + nyticks = 5 + yticks = null + chrGap = 8 + darkrect = "#F1F1F9" + lightrect = "#FBFBFF" + lodlinecolor = "darkslateblue" + linewidth = 2 + pointcolor = "darkslateblue" # pink + pointhover = "#E9CFEC" # pink + pointsize = 2 # default = no visible points at markers + pointstroke = "black" + title = "" + xlab = "Chromosome" + ylab = "LOD score" + rotate_ylab = null + yscale = d3.scale.linear() + xscale = null + pad4heatmap = false + lodcurve = null + lodvarname = null + markerSelect = null + chrSelect = null + pointsAtMarkers = true + + ## the main function + chart = (selection) -> + selection.each (data) -> + + console.log("data:", data) + + lodvarname = lodvarname ? data.lodnames[0] + data[lodvarname] = (Math.abs(x) for x in data[lodvarname]) # take absolute values + ylim = ylim ? [0, d3.max(data[lodvarname])] + lodvarnum = data.lodnames.indexOf(lodvarname) + + # Select the svg element, if it exists. + svg = d3.select(this).selectAll("svg").data([data]) + + # Otherwise, create the skeletal chart. + gEnter = svg.enter().append("svg").append("g") + + # Update the outer dimensions. + svg.attr("width", width+margin.left+margin.right) + .attr("height", height+margin.top+margin.bottom) + + # Update the inner dimensions. + g = svg.select("g") + + # box + g.append("rect") + .attr("x", margin.left) + .attr("y", margin.top) + .attr("height", height) + .attr("width", width) + .attr("fill", darkrect) + .attr("stroke", "none") + + yscale.domain(ylim) + .range([height+margin.top, margin.top+margin.inner]) + + # if yticks not provided, use nyticks to choose pretty ones + yticks = yticks ? yscale.ticks(nyticks) + + # reorganize lod,pos by chromosomes + data = reorgLodData(data, lodvarname) + + # add chromosome scales (for x-axis) + data = chrscales(data, width, chrGap, margin.left, pad4heatmap) + xscale = data.xscale + + # chr rectangles + chrSelect = + g.append("g").attr("class", "chrRect") + .selectAll("empty") + .data(data.chrnames) + .enter() + .append("rect") + .attr("id", (d) -> "chrrect#{d[0]}") + .attr("x", (d,i) -> + return data.chrStart[i] if i==0 and pad4heatmap + data.chrStart[i]-chrGap/2) + .attr("width", (d,i) -> + return data.chrEnd[i] - data.chrStart[i]+chrGap/2 if (i==0 or i+1 == data.chrnames.length) and pad4heatmap + data.chrEnd[i] - data.chrStart[i]+chrGap) + .attr("y", margin.top) + .attr("height", height) + .attr("fill", (d,i) -> + return darkrect if i % 2 + lightrect) + .attr("stroke", "none") + .on("click", (d) -> + redraw_plot(d) + ) + + # x-axis labels + xaxis = g.append("g").attr("class", "x axis") + xaxis.selectAll("empty") + .data(data.chrnames) + .enter() + .append("text") + .attr("class", "chr_label") + .text((d) -> d[0]) + .attr("x", (d,i) -> (data.chrStart[i]+data.chrEnd[i])/2) + .attr("y", margin.top+height+axispos.xlabel) + .attr("cursor", "pointer") + .on("click", (d) -> + redraw_plot(d) + ) + + xaxis.append("text").attr("class", "title") + .attr("y", margin.top+height+axispos.xtitle) + .attr("x", margin.left+width/2) + .text(xlab) + + redraw_plot = (chr_ob) -> + console.log("chr_name is:", chr_ob[0]) + console.log("chr_length is:", chr_ob[1]) + $('#topchart').remove() + $('#chart_container').append('<div class="qtlcharts" id="topchart"></div>') + chr_plot = new Chr_Manhattan_Plot(600, 1200, chr_ob) + + + # y-axis + rotate_ylab = rotate_ylab ? (ylab.length > 1) + yaxis = g.append("g").attr("class", "y axis") + yaxis.selectAll("empty") + .data(yticks) + .enter() + .append("line") + .attr("y1", (d) -> yscale(d)) + .attr("y2", (d) -> yscale(d)) + .attr("x1", margin.left) + .attr("x2", margin.left+7) + .attr("fill", "none") + .attr("stroke", "white") + .attr("stroke-width", 1) + .style("pointer-events", "none") + + yaxis.selectAll("empty") + .data(yticks) + .enter() + .append("text") + .attr("y", (d) -> yscale(d)) + .attr("x", margin.left-axispos.ylabel) + .attr("fill", "blue") + .text((d) -> formatAxis(yticks)(d)) + + yaxis.append("text").attr("class", "title") + .attr("y", margin.top+height/2) + .attr("x", margin.left-axispos.ytitle) + .text(ylab) + .attr("transform", if rotate_ylab then "rotate(270,#{margin.left-axispos.ytitle},#{margin.top+height/2})" else "") + + # lod curves by chr + #lodcurve = (chr, lodcolumn) -> + # d3.svg.line() + # .x((d) -> xscale[chr](d)) + # .y((d,i) -> yscale(data.lodByChr[chr][i][lodcolumn])) + + #add_plot_points: () -> + # @plot_point = @svg.selectAll("circle") + # .data(@plot_coordinates) + # .enter() + # .append("circle") + # .attr("cx", (d) => + # return @x_scale(d[0]) + # ) + # .attr("cy", (d) => + # return @y_scale(d[1]) + # ) + # .attr("r", (d) => + # #if d[1] > 3 + # # return 3 + # #else + # return 2 + # ) + # .attr("fill", (d) => + # #if d[1] > 3 + # # return "white" + # #else + # return "black" + # ) + # .attr("stroke", "black") + # .attr("stroke-width", "1") + # .attr("id", (d) => + # return "point_" + String(d[2]) + # ) + # .classed("circle", true) + # .on("mouseover", (d) => + # console.log("d3.event is:", d3.event) + # console.log("d is:", d) + # this_id = "point_" + String(d[2]) + # d3.select("#" + this_id).classed("d3_highlight", true) + # .attr("r", 5) + # .attr("stroke", "none") + # .attr("fill", "blue") + # .call(@show_marker_in_table(d)) + # ) + # .on("mouseout", (d) => + # this_id = "point_" + String(d[2]) + # d3.select("#" + this_id).classed("d3_highlight", false) + # .attr("r", (d) => + # #if d[1] > 2 + # # return 3 + # #else + # return 2 + # ) + # .attr("fill", (d) => + # #if d[1] > 2 + # # return "white" + # #else + # return "black" + # ) + # .attr("stroke", "black") + # .attr("stroke-width", "1") + # ) + # .append("svg:title") + # .text((d) => + # return d[2] + # ) + + #curves = g.append("g").attr("id", "curves") + + #for chr in data.chrnames + # curves.append("path") + # .datum(data.posByChr[chr]) + # .attr("d", lodcurve(chr, lodvarnum)) + # .attr("stroke", lodlinecolor) + # .attr("fill", "none") + # .attr("stroke-width", linewidth) + # .style("pointer-events", "none") + + # points at markers + if pointsize > 0 + markerpoints = g.append("g").attr("id", "markerpoints_visible") + markerpoints.selectAll("empty") + .data(data.markers) + .enter() + .append("circle") + .attr("cx", (d) -> xscale[d.chr](d.pos)) + .attr("cy", (d) -> yscale(d.lod)) + .attr("r", pointsize) + .attr("fill", pointcolor) + .attr("stroke", pointstroke) + .attr("pointer-events", "hidden") + + # title + titlegrp = g.append("g").attr("class", "title") + .append("text") + .attr("x", margin.left+width/2) + .attr("y", margin.top-titlepos) + .text(title) + + # another box around edge + g.append("rect") + .attr("x", margin.left) + .attr("y", margin.top) + .attr("height", height) + .attr("width", () -> + return(data.chrEnd[-1..][0]-margin.left) if pad4heatmap + data.chrEnd[-1..][0]-margin.left+chrGap/2) + .attr("fill", "none") + .attr("stroke", "black") + .attr("stroke-width", "none") + + if pointsAtMarkers + # these hidden points are what gets selected...a bit larger + hiddenpoints = g.append("g").attr("id", "markerpoints_hidden") + + markertip = d3.tip() + .attr('class', 'd3-tip') + .html((d) -> + [d.name, " LRS = #{d3.format('.2f')(d.lod)}"]) + .direction("e") + .offset([0,10]) + svg.call(markertip) + + markerSelect = + hiddenpoints.selectAll("empty") + .data(data.markers) + .enter() + .append("circle") + .attr("cx", (d) -> xscale[d.chr](d.pos)) + .attr("cy", (d) -> yscale(d.lod)) + .attr("id", (d) -> d.name) + .attr("r", d3.max([pointsize*2, 3])) + .attr("opacity", 0) + .attr("fill", pointhover) + .attr("stroke", pointstroke) + .attr("stroke-width", "1") + .on "mouseover.paneltip", (d) -> + d3.select(this).attr("opacity", 1) + markertip.show(d) + .on "mouseout.paneltip", -> + d3.select(this).attr("opacity", 0) + .call(markertip.hide) + + ## configuration parameters + chart.width = (value) -> + return width unless arguments.length + width = value + chart + + chart.height = (value) -> + return height unless arguments.length + height = value + chart + + chart.margin = (value) -> + return margin unless arguments.length + margin = value + chart + + chart.titlepos = (value) -> + return titlepos unless arguments.length + titlepos + chart + + chart.axispos = (value) -> + return axispos unless arguments.length + axispos = value + chart + + chart.ylim = (value) -> + return ylim unless arguments.length + ylim = value + chart + + chart.nyticks = (value) -> + return nyticks unless arguments.length + nyticks = value + chart + + chart.yticks = (value) -> + return yticks unless arguments.length + yticks = value + chart + + chart.chrGap = (value) -> + return chrGap unless arguments.length + chrGap = value + chart + + chart.darkrect = (value) -> + return darkrect unless arguments.length + darkrect = value + chart + + chart.lightrect = (value) -> + return lightrect unless arguments.length + lightrect = value + chart + + chart.linecolor = (value) -> + return linecolor unless arguments.length + linecolor = value + chart + + chart.linewidth = (value) -> + return linewidth unless arguments.length + linewidth = value + chart + + chart.pointcolor = (value) -> + return pointcolor unless arguments.length + pointcolor = value + chart + + chart.pointhover = (value) -> + return pointhover unless arguments.length + pointhover = value + chart + + chart.pointsize = (value) -> + return pointsize unless arguments.length + pointsize = value + chart + + chart.pointstroke = (value) -> + return pointstroke unless arguments.length + pointstroke = value + chart + + chart.title = (value) -> + return title unless arguments.length + title = value + chart + + chart.xlab = (value) -> + return xlab unless arguments.length + xlab = value + chart + + chart.ylab = (value) -> + return ylab unless arguments.length + ylab = value + chart + + chart.rotate_ylab = (value) -> + return rotate_ylab if !arguments.length + rotate_ylab = value + chart + + chart.lodvarname = (value) -> + return lodvarname unless arguments.length + lodvarname = value + chart + + chart.pad4heatmap = (value) -> + return pad4heatmap unless arguments.length + pad4heatmap = value + chart + + chart.pointsAtMarkers = (value) -> + return pointsAtMarkers unless arguments.length + pointsAtMarkers = value + chart + + chart.yscale = () -> + return yscale + + chart.xscale = () -> + return xscale + + #chart.lodcurve = () -> + # return lodcurve + + chart.markerSelect = () -> + return markerSelect + + chart.chrSelect = () -> + return chrSelect + + # return the chart function + chart + + +# reorganize lod/pos by chromosome +# lodvarname==null -> case for multiple LOD columns (lodheatmap) +# lodvarname provided -> case for one LOD column (lodchart) +reorgLodData = (data, lodvarname=null) -> + data.posByChr = {} + data.lodByChr = {} + + for chr,i in data.chrnames + data.posByChr[chr[0]] = [] + data.lodByChr[chr[0]] = [] + for pos, j in data.pos + if data.chr[j] == chr[0] + data.posByChr[chr[0]].push(pos) + data.lodnames = [data.lodnames] unless Array.isArray(data.lodnames) + lodval = (data[lodcolumn][j] for lodcolumn in data.lodnames) + data.lodByChr[chr[0]].push(lodval) + + + if lodvarname? + data.markers = [] + for marker,i in data.markernames + if marker != "" + data.markers.push({name:marker, chr:data.chr[i], pos:data.pos[i], lod:data[lodvarname][i]}) + + data + + +# calculate chromosome start/end + scales, for heat map +chrscales = (data, width, chrGap, leftMargin, pad4heatmap) -> + # start and end of chromosome positions + chrStart = [] + chrEnd = [] + chrLength = [] + totalChrLength = 0 + maxd = 0 + for chr in data.chrnames + d = maxdiff(data.posByChr[chr[0]]) + maxd = d if d > maxd + + rng = d3.extent(data.posByChr[chr[0]]) + chrStart.push(rng[0]) + chrEnd.push(rng[1]) + L = rng[1] - rng[0] + chrLength.push(L) + totalChrLength += L + + # adjust lengths for heatmap + if pad4heatmap + data.recwidth = maxd + chrStart = chrStart.map (x) -> x-maxd/2 + chrEnd = chrEnd.map (x) -> x+maxd/2 + chrLength = chrLength.map (x) -> x+maxd + totalChrLength += (chrLength.length*maxd) + + # break up x axis into chromosomes by length, with gaps + data.chrStart = [] + data.chrEnd = [] + cur = leftMargin + cur += chrGap/2 unless pad4heatmap + data.xscale = {} + for chr,i in data.chrnames + data.chrStart.push(cur) + w = Math.round((width-chrGap*(data.chrnames.length-pad4heatmap))/totalChrLength*chrLength[i]) + data.chrEnd.push(cur + w) + cur = data.chrEnd[i] + chrGap + # x-axis scales, by chromosome + data.xscale[chr[0]] = d3.scale.linear() + .domain([chrStart[i], chrEnd[i]]) + .range([data.chrStart[i], data.chrEnd[i]]) + + # return data with new stuff added + data + +# maximum difference between adjacent values in a vector +maxdiff = (x) -> + return null if x.length < 2 + result = x[1] - x[0] + return result if x.length < 3 + for i in [2...x.length] + d = x[i] - x[i-1] + result = d if d > result + result + +# determine rounding of axis labels +formatAxis = (d) -> + d = d[1] - d[0] + ndig = Math.floor( Math.log(d % 10) / Math.log(10) ) + ndig = 0 if ndig > 0 + ndig = Math.abs(ndig) + d3.format(".#{ndig}f") |
