about summary refs log tree commit diff
diff options
context:
space:
mode:
authorZachary Sloan2014-08-04 18:47:31 +0000
committerZachary Sloan2014-08-04 18:47:31 +0000
commit692d074cbe9141820acc0edcce07af8ba2e25bbe (patch)
treea7bf7bf1d51e481536171933e039f2d6adb7ed82
parente69c346553bc26c2f1ba8b0d9fc394add9f6784f (diff)
downloadgenenetwork2-692d074cbe9141820acc0edcce07af8ba2e25bbe.tar.gz
Fixed a problem with the heatmap after moving reorgLodData function to panelutils.js
Got rqtl working with converted BXD genofile and also got composite mapping working
-rw-r--r--wqflask/wqflask/heatmap/heatmap.py14
-rwxr-xr-xwqflask/wqflask/interval_mapping/interval_mapping.py7
-rwxr-xr-xwqflask/wqflask/marker_regression/marker_regression.py197
-rwxr-xr-xwqflask/wqflask/show_trait/show_trait.py25
-rw-r--r--wqflask/wqflask/static/new/javascript/create_interval_map.coffee35
-rw-r--r--wqflask/wqflask/static/new/javascript/iplotMScanone_noeff.coffee4
-rw-r--r--wqflask/wqflask/static/new/javascript/iplotMScanone_noeff.js2
-rw-r--r--wqflask/wqflask/static/new/javascript/lod_chart.coffee55
-rw-r--r--wqflask/wqflask/static/new/javascript/lod_chart.js24
-rw-r--r--wqflask/wqflask/static/new/javascript/lodheatmap.coffee18
-rw-r--r--wqflask/wqflask/static/new/javascript/lodheatmap.js13
-rw-r--r--wqflask/wqflask/static/new/javascript/manhattan_plot.coffee175
-rw-r--r--wqflask/wqflask/static/new/javascript/manhattan_plot.js140
-rw-r--r--wqflask/wqflask/static/new/javascript/panelutil.coffee357
-rw-r--r--wqflask/wqflask/static/new/javascript/panelutil.js758
-rwxr-xr-xwqflask/wqflask/static/new/javascript/show_trait_mapping_tools.coffee36
-rwxr-xr-xwqflask/wqflask/static/new/javascript/show_trait_mapping_tools.js13
-rwxr-xr-xwqflask/wqflask/static/packages/DT_bootstrap/DT_bootstrap.js18
-rwxr-xr-xwqflask/wqflask/templates/show_trait_mapping_tools.html34
-rwxr-xr-xwqflask/wqflask/views.py13
20 files changed, 1234 insertions, 704 deletions
diff --git a/wqflask/wqflask/heatmap/heatmap.py b/wqflask/wqflask/heatmap/heatmap.py
index 0433bf17..9b6b1b69 100644
--- a/wqflask/wqflask/heatmap/heatmap.py
+++ b/wqflask/wqflask/heatmap/heatmap.py
@@ -63,7 +63,14 @@ class Heatmap(object):
         

         self.all_sample_list = []

         self.traits = []

+        

+        chrnames = []

+        self.species = species.TheSpecies(dataset=self.trait_list[0][1])

+        for key in self.species.chromosomes.chromosomes.keys():

+            chrnames.append([self.species.chromosomes.chromosomes[key].name, self.species.chromosomes.chromosomes[key].mb_length])

+        

         for trait_db in self.trait_list:

+                

             this_trait = trait_db[0]

             self.traits.append(this_trait.name)

             this_sample_data = this_trait.data

@@ -91,7 +98,7 @@ class Heatmap(object):
         self.gen_reaper_results()

         #self.gen_pylmm_results()

             

-        chrnames = []

+        #chrnames = []

         lodnames = []

         chr_pos = []

         pos = []

@@ -101,8 +108,9 @@ class Heatmap(object):
             lodnames.append(trait)

         

         for marker in self.dataset.group.markers.markers:

-            if marker['chr'] not in chrnames:

-                chrnames.append(marker['chr'])

+            #if marker['chr'] not in chrnames:

+            #    chr_ob = [marker['chr'], "filler"]

+            #    chrnames.append(chr_ob)

             chr_pos.append(marker['chr'])

             pos.append(marker['Mb'])

             markernames.append(marker['name'])

diff --git a/wqflask/wqflask/interval_mapping/interval_mapping.py b/wqflask/wqflask/interval_mapping/interval_mapping.py
index 4ac7bde2..e70f11cb 100755
--- a/wqflask/wqflask/interval_mapping/interval_mapping.py
+++ b/wqflask/wqflask/interval_mapping/interval_mapping.py
@@ -169,7 +169,12 @@ class IntervalMapping(object):
         self.json_data['markernames'] = []
         for qtl in reaper_results:
             reaper_locus = qtl.locus
-            self.json_data['chr'].append(reaper_locus.chr)
+            #if reaper_locus.chr == "20":
+            #    print("changing to X")
+            #    self.json_data['chr'].append("X")
+            #else:
+            #    self.json_data['chr'].append(reaper_locus.chr)
+            ##self.json_data['chr'].append(reaper_locus.chr)
             self.json_data['pos'].append(reaper_locus.Mb)
             self.json_data['lod.hk'].append(qtl.lrs)
             self.json_data['markernames'].append(reaper_locus.name)
diff --git a/wqflask/wqflask/marker_regression/marker_regression.py b/wqflask/wqflask/marker_regression/marker_regression.py
index 648cb49b..59d87b36 100755
--- a/wqflask/wqflask/marker_regression/marker_regression.py
+++ b/wqflask/wqflask/marker_regression/marker_regression.py
@@ -12,6 +12,7 @@ import os
 import collections

 import uuid

 

+import rpy2.robjects as robjects

 import numpy as np

 from scipy import linalg

 

@@ -60,13 +61,20 @@ class MarkerRegression(object):
  

         self.mapping_method = start_vars['method']

         self.maf = start_vars['maf'] # Minor allele frequency

-        print("self.maf:", self.maf)

+        #print("self.maf:", self.maf)

  

         self.dataset.group.get_markers()

         if self.mapping_method == "gemma":

             qtl_results = self.run_gemma()

-        elif self.mapping_method == "rqtl":

-            qtl_results = self.run_rqtl()

+        elif self.mapping_method == "rqtl_plink":

+            qtl_results = self.run_rqtl_plink()

+        elif self.mapping_method == "rqtl_geno":

+            self.num_perm = start_vars['num_perm']

+            self.control = start_vars['control_marker']

+            self.control_db = start_vars['control_marker_db']

+            print("doing rqtl_geno")

+            qtl_results = self.run_rqtl_geno()

+            print("qtl_results:", qtl_results)

         elif self.mapping_method == "plink":

             qtl_results = self.run_plink()

             #print("qtl_results:", pf(qtl_results))

@@ -79,12 +87,14 @@ class MarkerRegression(object):
             

         self.lod_cutoff = 2    

         self.filtered_markers = []

+        highest_chr = 1 #This is needed in order to convert the highest chr to X/Y

         for marker in qtl_results:

             if marker['chr'] > 0 or marker['chr'] == "X" or marker['chr'] == "X/Y":

+                if marker['chr'] > highest_chr or marker['chr'] == "X" or marker['chr'] == "X/Y":

+                    highest_chr = marker['chr']

                 if 'lod_score' in marker:

                     self.filtered_markers.append(marker)

 

-

         self.json_data['chr'] = []

         self.json_data['pos'] = []

         self.json_data['lod.hk'] = []

@@ -94,7 +104,11 @@ class MarkerRegression(object):
         self.qtl_results = []

         for qtl in self.filtered_markers:

             print("lod score is:", qtl['lod_score'])

-            self.json_data['chr'].append(str(qtl['chr']))

+            if qtl['chr'] == highest_chr and highest_chr != "X" and highest_chr != "X/Y":

+                print("changing to X")

+                self.json_data['chr'].append("X")

+            else:

+                self.json_data['chr'].append(str(qtl['chr']))

             self.json_data['pos'].append(qtl['Mb'])

             self.json_data['lod.hk'].append(str(qtl['lod_score']))

             self.json_data['markernames'].append(qtl['name'])

@@ -104,9 +118,10 @@ class MarkerRegression(object):
         self.json_data['chrnames'] = []

         for key in self.species.chromosomes.chromosomes.keys():

             self.json_data['chrnames'].append([self.species.chromosomes.chromosomes[key].name, self.species.chromosomes.chromosomes[key].mb_length])

-            

             chromosome_mb_lengths[key] = self.species.chromosomes.chromosomes[key].mb_length

         

+        print("json_data:", self.json_data)

+        

         self.js_data = dict(

             json_data = self.json_data,

             this_trait = self.this_trait.name,

@@ -186,7 +201,7 @@ class MarkerRegression(object):
     #

     #

     

-    def run_rqtl(self):

+    def run_rqtl_plink(self):

         os.chdir("/home/zas1024/plink")

         

         output_filename = webqtlUtil.genRandStr("%s_%s_"%(self.dataset.group.name, self.this_trait.name))

@@ -199,6 +214,103 @@ class MarkerRegression(object):
         

         count, p_values = self.parse_rqtl_output(plink_output_filename)

     

+    def run_rqtl_geno(self):

+        robjects.packages.importr("qtl")

+        robjects.r('the_cross <- read.cross(format="csvr", dir="/home/zas1024/PLINK2RQTL/test", file="BXD.csvr")')

+        robjects.r('the_cross <- calc.genoprob(the_cross)')

+        pheno_as_string = "c("

+        #for i, val in enumerate(self.vals):

+        #    if val == "x":

+        #        new_val == "NULL"

+        #    else:

+        #        new_val = val

+        #    if i < (len(self.vals) - 1):

+        #        pheno_as_string += str(new_val) + ","

+        #    else: pheno_as_string += str(new_val)

+        null_pos = []

+        for i, val in enumerate(self.vals):

+            if val == "x":

+                null_pos.append(i)

+                if i < (len(self.vals) - 1):

+                    pheno_as_string +=  "NA,"

+                else:

+                    pheno_as_string += "NA"

+            else:

+                if i < (len(self.vals) - 1):

+                    pheno_as_string += str(val) + ","

+                else:

+                    pheno_as_string += str(val)

+            

+        pheno_as_string += ")"

+        

+        print("self.control:", self.control)

+        if self.control != "":

+            print("self.control_db:", self.control_db)

+            control_trait = GeneralTrait(name=str(self.control), dataset_name=str(self.control_db))

+            control_vals = []

+            for sample in self.dataset.group.samplelist:

+                if sample in control_trait.data:

+                    control_vals.append(control_trait.data[sample].value)

+                else:

+                    control_vals.append("x")

+            print("control_vals:", control_vals)

+            control_as_string = "c("

+            for j, val2 in enumerate(control_vals):

+                if val2 == "x":

+                    if j < (len(control_vals) - 1):

+                        control_as_string +=  "NA,"

+                    else:

+                        control_as_string += "NA"

+                else:

+                    if j < (len(control_vals) - 1):

+                        control_as_string += str(val2) + ","

+                    else:

+                        control_as_string += str(val2)

+                #if i < (len(control_vals) - 1):

+                #    control_as_string += str(new_val2) + ","

+                #else:

+                #    control_as_string += str(new_val2)

+            control_as_string += ")"

+            print("control_as_string:", control_as_string)

+        

+            r_string = 'scanone(the_cross, pheno.col='+pheno_as_string+', n.perm='+self.num_perm+', addcovar='+control_as_string+')'

+            

+            if self.num_perm > 0:

+                thresholds = robjects.r(r_string)

+                print("thresholds:", thresholds)

+            

+            #r_string = 'scanone(the_cross, pheno.col='+pheno_as_string+', addcovar='+control_as_string+')'

+            print("r_string:", r_string)

+        else:

+        #r_string = 'scanone(the_cross, pheno.col='+pheno_as_string+', n.perm='+self.num_perm+')'

+            r_string = 'scanone(the_cross, pheno.col='+pheno_as_string+')'

+            

+        print("r_string:", r_string)

+        result_data_frame = robjects.r(r_string)

+        #print("results:", result_data_frame)

+

+        qtl_results = self.process_rqtl_results(result_data_frame)

+        

+        return qtl_results

+    

+    def process_rqtl_results(self, result):

+        qtl_results = []

+        

+        output = [tuple([result[j][i] for j in range(result.ncol)]) for i in range(result.nrow)]

+        print("output", output)

+        

+        

+        for i, line in enumerate(result.iter_row()):

+            marker = {}

+            marker['name'] = result.rownames[i]

+            marker['chr'] = output[i][0]

+            marker['Mb'] = output[i][1]

+            marker['lod_score'] = output[i][2]

+            

+            qtl_results.append(marker)

+            

+        return qtl_results

+

     def run_plink(self):

     

         os.chdir("/home/zas1024/plink")

@@ -271,6 +383,41 @@ class MarkerRegression(object):
             output_file.write(new_line)

             

         output_file.close()

+        

+    def gen_pheno_txt_file_rqtl(self, pheno_filename = ''):

+        ped_sample_list = self.get_samples_from_ped_file()	

+        output_file = open("%s%s.txt" % (webqtlConfig.TMPDIR, pheno_filename), "wb")

+        header = 'FID\tIID\t%s\n' % self.this_trait.name

+        output_file.write(header)

+    

+        new_value_list = []

+        

+        #if valueDict does not include some strain, value will be set to -9999 as missing value

+        for i, sample in enumerate(ped_sample_list):

+            try:

+                value = self.vals[i]

+                value = str(value).replace('value=','')

+                value = value.strip()

+            except:

+                value = -9999

+    

+            new_value_list.append(value)

+            

+            

+        new_line = ''

+        for i, sample in enumerate(ped_sample_list):

+            j = i+1

+            value = new_value_list[i]

+            new_line += '%s\t%s\t%s\n'%(sample, sample, value)

+            

+            if j%1000 == 0:

+                output_file.write(newLine)

+                new_line = ''

+        

+        if new_line:

+            output_file.write(new_line)

+            

+        output_file.close()

     

     # get strain name from ped file in order

     def get_samples_from_ped_file(self):

@@ -292,42 +439,6 @@ class MarkerRegression(object):
         

         return sample_list

     

-    ################################################################

-    # Generate Chr list, Chr OrderId and Retrieve Length Information 

-    ################################################################		

-    #def getChrNameOrderIdLength(self,RISet=''):

-    #    try:

-    #        query = """

-    #            Select

-    #                Chr_Length.Name,Chr_Length.OrderId,Length from Chr_Length, InbredSet

-    #            where

-    #                Chr_Length.SpeciesId = InbredSet.SpeciesId AND

-    #                InbredSet.Name = '%s' 

-    #            Order by OrderId

-    #            """ % (self.dataset.group.name)

-    #        results =g.db.execute(query).fetchall()

-    #        ChrList=[]

-    #        ChrLengthMbList=[]

-    #        ChrNameOrderIdDict={}

-    #        ChrOrderIdNameDict={}

-    #        

-    #        for item in results:

-    #            ChrList.append(item[0])

-    #            ChrNameOrderIdDict[item[0]]=item[1] # key is chr name, value is orderId

-    #            ChrOrderIdNameDict[item[1]]=item[0] # key is orderId, value is chr name

-    #            ChrLengthMbList.append(item[2])				

-    #            

-    #    except:

-    #        ChrList=[]

-    #        ChrNameOrderIdDict={}

-    #        ChrLengthMbList=[]

-    #        

-    #    return ChrList,ChrNameOrderIdDict,ChrOrderIdNameDict,ChrLengthMbList

-    

-    

-    def parse_rqtl_output(self, output_filename):

-        return True

-    

     def parse_plink_output(self, output_filename):

         plink_results={}

     

diff --git a/wqflask/wqflask/show_trait/show_trait.py b/wqflask/wqflask/show_trait/show_trait.py
index 3034dec8..a4a24fd4 100755
--- a/wqflask/wqflask/show_trait/show_trait.py
+++ b/wqflask/wqflask/show_trait/show_trait.py
@@ -90,6 +90,10 @@ class ShowTrait(object):
 
         self.build_correlation_tools(self.this_trait)
 
+        #Get nearest marker for composite mapping
+        self.nearest_marker, self.nearest_marker_db = get_nearest_marker(self.this_trait)
+    
+
         self.make_sample_lists(self.this_trait)
 
         if self.dataset.group.allsamples:
@@ -101,6 +105,9 @@ class ShowTrait(object):
         hddn['method'] = "pylmm"
         hddn['mapping_display_all'] = True
         hddn['suggestive'] = 0
+        hddn['num_perm'] = 0
+        hddn['control_marker'] = self.nearest_marker
+        hddn['control_marker_db'] = self.nearest_marker_db
         hddn['maf'] = 0.01
         hddn['compare_traits'] = []
     
@@ -1267,3 +1274,21 @@ def get_samplelist_from_trait_data(this_trait, all_samples_ordered):
             other_sample_names.append(sample)
             
     return other_sample_names, all_samples_ordered
+
+def get_nearest_marker(this_trait):
+    this_chr = this_trait.chr
+    this_mb = this_trait.mb
+    query = """SELECT ProbeSet.Name, ProbeSetFreeze.Name
+               FROM ProbeSet, ProbeSetXRef, ProbeSetFreeze
+               WHERE ProbeSet.Chr = '{}' AND
+                     ProbeSet.Id=ProbeSetXRef.ProbeSetId AND
+                     ProbeSetXRef.ProbeSetFreezeId=ProbeSetFreeze.Id AND
+                     ProbeSetFreeze.Name='{}'
+               ORDER BY ABS( Mb - {}) LIMIT 2""".format(this_trait.chr, this_trait.dataset.name, this_trait.mb)
+    print("query:", query)
+
+    result = g.db.execute(query).fetchall()
+
+    return result[1][0], result[1][1]
+    
+    
diff --git a/wqflask/wqflask/static/new/javascript/create_interval_map.coffee b/wqflask/wqflask/static/new/javascript/create_interval_map.coffee
index 2b4b20c8..c8e1b318 100644
--- a/wqflask/wqflask/static/new/javascript/create_interval_map.coffee
+++ b/wqflask/wqflask/static/new/javascript/create_interval_map.coffee
@@ -78,38 +78,3 @@ $("#export_pdf").click =>
     form.find("#data").val(svg_xml)

     form.find("#filename").val(filename)

     form.submit()

-

-# two LOD charts within one SVG

-#d3.json "data.json", (data) ->

-#  mychart_em = lodchart().lodvarname("lod.em")

-#                         .height(h)

-#                         .width(w)

-#                         .margin(margin)

-#                         .ylab("LOD score (by EM)")

-#                         .pointsize(1)

-#                         .nyticks(9)

-#                         .title("Standard interval mapping")

-#  mychart_hk = lodchart().lodvarname("lod.hk")

-#                         .height(h)

-#                         .width(w)

-#                         .margin(margin)

-#                         .ylab("LOD score (by H-K)")

-#                         .linecolor("Crimson")

-#                         .yticks([0, 1, 2, 4, 6, 8])

-#                         .title("Haley-Knott regression")

-#

-#  svg = d3.select("div#bottomchart")

-#          .append("svg")

-#          .attr("height", totalh)

-#          .attr("width", totalw)

-#

-#  chart1 = svg.append("g").attr("id", "chart1")

-#

-#  chart2 = svg.append("g").attr("id", "chart2")

-#              .attr("transform", "translate(0, #{halfh})")

-#

-#  chart1.datum(data)

-#    .call(mychart_em)

-#

-#  chart2.datum(data)

-#    .call(mychart_hk)
\ No newline at end of file
diff --git a/wqflask/wqflask/static/new/javascript/iplotMScanone_noeff.coffee b/wqflask/wqflask/static/new/javascript/iplotMScanone_noeff.coffee
index 6aa301a0..7831b2d4 100644
--- a/wqflask/wqflask/static/new/javascript/iplotMScanone_noeff.coffee
+++ b/wqflask/wqflask/static/new/javascript/iplotMScanone_noeff.coffee
@@ -90,8 +90,8 @@ iplotMScanone_noeff = (lod_data, times, chartOpts) ->
         lodchart_curves = g_lodchart.append("g").attr("id", "lodcurves")

         for chr in lod_data.chrnames

             lodchart_curves.append("path")

-                           .datum(lod_data.posByChr[chr])

-                           .attr("d", lodcurve(chr, lodcolumn))

+                           .datum(lod_data.posByChr[chr[0]])

+                           .attr("d", lodcurve(chr[0], lodcolumn))

                            .attr("stroke", linecolor)

                            .attr("fill", "none")

                            .attr("stroke-width", linewidth)

diff --git a/wqflask/wqflask/static/new/javascript/iplotMScanone_noeff.js b/wqflask/wqflask/static/new/javascript/iplotMScanone_noeff.js
index e539d353..8f6787c3 100644
--- a/wqflask/wqflask/static/new/javascript/iplotMScanone_noeff.js
+++ b/wqflask/wqflask/static/new/javascript/iplotMScanone_noeff.js
@@ -68,7 +68,7 @@ iplotMScanone_noeff = function(lod_data, times, chartOpts) {
     _results = [];
     for (_i = 0, _len = _ref21.length; _i < _len; _i++) {
       chr = _ref21[_i];
-      _results.push(lodchart_curves.append("path").datum(lod_data.posByChr[chr]).attr("d", lodcurve(chr, lodcolumn)).attr("stroke", linecolor).attr("fill", "none").attr("stroke-width", linewidth).style("pointer-events", "none"));
+      _results.push(lodchart_curves.append("path").datum(lod_data.posByChr[chr[0]]).attr("d", lodcurve(chr[0], lodcolumn)).attr("stroke", linecolor).attr("fill", "none").attr("stroke-width", linewidth).style("pointer-events", "none"));
     }
     return _results;
   };
diff --git a/wqflask/wqflask/static/new/javascript/lod_chart.coffee b/wqflask/wqflask/static/new/javascript/lod_chart.coffee
index c9b277cc..dd2ad31d 100644
--- a/wqflask/wqflask/static/new/javascript/lod_chart.coffee
+++ b/wqflask/wqflask/static/new/javascript/lod_chart.coffee
@@ -39,7 +39,7 @@ lodchart = () ->
     chart = (selection) ->

       selection.each (data) ->

         

-        console.log("data:", data)

+        #console.log("data:", data)

         

         lodvarname = lodvarname ? data.lodnames[0]

         data[lodvarname] = (Math.abs(x) for x in data[lodvarname]) # take absolute values

@@ -142,8 +142,8 @@ lodchart = () ->
 

   

         redraw_plot = (chr_ob) ->

-             console.log("chr_name is:", chr_ob[0])

-             console.log("chr_length is:", chr_ob[1])

+             #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_Interval_Map(600, 1200, chr_ob)

@@ -254,35 +254,34 @@ lodchart = () ->
               .x((d) -> xscale[chr](d))

               .y((d,i) -> yscale(data.lodByChr[chr][i][lodcolumn]))

               

-        #if data['additive'].length > 0

-        if 'additive' of data

-            additivecurve = (chr, lodcolumn) ->

-                d3.svg.line()

-                  .x((d) -> xscale[chr](d))

-                  .y((d,i) -> additive_yscale(data.additiveByChr[chr][i][lodcolumn]))

+        #if 'additive' of data

+        #    additivecurve = (chr, lodcolumn) ->

+        #        d3.svg.line()

+        #          .x((d) -> xscale[chr](d))

+        #          .y((d,i) -> additive_yscale(data.additiveByChr[chr][i][lodcolumn]))

   

         curves = g.append("g").attr("id", "curves")

   

-        #for chr in data.chrnames

-        #  curves.append("path")

-        #        .datum(data.posByChr[chr[0]])

-        #        .attr("d", lodcurve(chr[0], lodvarnum))

-        #        .attr("stroke", lodlinecolor)

-        #        .attr("fill", "none")

-        #        .attr("stroke-width", linewidth)

-        #        .style("pointer-events", "none")

+        for chr in data.chrnames

+          curves.append("path")

+                .datum(data.posByChr[chr[0]])

+                .attr("d", lodcurve(chr[0], lodvarnum))

+                .attr("stroke", lodlinecolor)

+                .attr("fill", "none")

+                .attr("stroke-width", linewidth)

+                .style("pointer-events", "none")

         

-        #if data['additive'].length > 0

-        if 'additive' of data

-            for chr in data.chrnames

-                curves.append("path")

-                      .datum(data.posByChr[chr[0]])

-                      .attr("d", additivecurve(chr[0], lodvarnum))

-                      .attr("stroke", additivelinecolor)

-                      .attr("fill", "none")

-                      .attr("stroke-width", 1)

-                      .style("pointer-events", "none")

-  

+        ##if data['additive'].length > 0

+        #if 'additive' of data

+        #    for chr in data.chrnames

+        #        curves.append("path")

+        #              .datum(data.posByChr[chr[0]])

+        #              .attr("d", additivecurve(chr[0], lodvarnum))

+        #              .attr("stroke", additivelinecolor)

+        #              .attr("fill", "none")

+        #              .attr("stroke-width", 1)

+        #              .style("pointer-events", "none")

+        #

         # points at markers

         if pointsize > 0

           markerpoints = g.append("g").attr("id", "markerpoints_visible")

diff --git a/wqflask/wqflask/static/new/javascript/lod_chart.js b/wqflask/wqflask/static/new/javascript/lod_chart.js
index 0f91fc71..49ca962f 100644
--- a/wqflask/wqflask/static/new/javascript/lod_chart.js
+++ b/wqflask/wqflask/static/new/javascript/lod_chart.js
@@ -51,8 +51,7 @@ lodchart = function() {
   pointsAtMarkers = true;
   chart = function(selection) {
     return selection.each(function(data) {
-      var additive_yaxis, additivecurve, chr, curves, g, gEnter, hiddenpoints, lodvarnum, markerpoints, markertip, redraw_plot, rotate_additive_ylab, suggestive_bar, svg, titlegrp, x, xaxis, yaxis, _i, _len, _ref;
-      console.log("data:", data);
+      var additive_yaxis, chr, curves, g, gEnter, hiddenpoints, lodvarnum, markerpoints, markertip, redraw_plot, rotate_additive_ylab, suggestive_bar, svg, titlegrp, x, xaxis, yaxis, _i, _len, _ref;
       lodvarname = lodvarname != null ? lodvarname : data.lodnames[0];
       data[lodvarname] = (function() {
         var _i, _len, _ref, _results;
@@ -128,8 +127,6 @@ lodchart = function() {
       xaxis.append("text").attr("class", "title").attr("y", margin.top + height + axispos.xtitle).attr("x", margin.left + width / 2).attr("fill", "slateblue").text(xlab);
       redraw_plot = function(chr_ob) {
         var chr_plot;
-        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>');
         return chr_plot = new Chr_Interval_Map(600, 1200, chr_ob);
@@ -185,22 +182,11 @@ lodchart = function() {
           return yscale(data.lodByChr[chr][i][lodcolumn]);
         });
       };
-      if ('additive' in data) {
-        additivecurve = function(chr, lodcolumn) {
-          return d3.svg.line().x(function(d) {
-            return xscale[chr](d);
-          }).y(function(d, i) {
-            return additive_yscale(data.additiveByChr[chr][i][lodcolumn]);
-          });
-        };
-      }
       curves = g.append("g").attr("id", "curves");
-      if ('additive' in data) {
-        _ref = data.chrnames;
-        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
-          chr = _ref[_i];
-          curves.append("path").datum(data.posByChr[chr[0]]).attr("d", additivecurve(chr[0], lodvarnum)).attr("stroke", additivelinecolor).attr("fill", "none").attr("stroke-width", 1).style("pointer-events", "none");
-        }
+      _ref = data.chrnames;
+      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+        chr = _ref[_i];
+        curves.append("path").datum(data.posByChr[chr[0]]).attr("d", lodcurve(chr[0], lodvarnum)).attr("stroke", lodlinecolor).attr("fill", "none").attr("stroke-width", linewidth).style("pointer-events", "none");
       }
       if (pointsize > 0) {
         markerpoints = g.append("g").attr("id", "markerpoints_visible");
diff --git a/wqflask/wqflask/static/new/javascript/lodheatmap.coffee b/wqflask/wqflask/static/new/javascript/lodheatmap.coffee
index 6ffaefeb..5f3716bc 100644
--- a/wqflask/wqflask/static/new/javascript/lodheatmap.coffee
+++ b/wqflask/wqflask/static/new/javascript/lodheatmap.coffee
@@ -33,10 +33,14 @@ lodheatmap = () ->
             rectHeight = yscale(0)-yscale(1)

 

             xLR = {}

-            console.log("data.chrnames:", data.chrnames)

+            #console.log("data.posByChr:", data.posByChr["2"])

+            #console.log("data.chrnames:", data.chrnames)

             for chr in data.chrnames

-                console.log("chr is:", chr)

-                xLR[chr] = getLeftRight(data.posByChr[chr])

+                #console.log("posByChr:", data.posByChr)

+                #console.log("chr is:", chr)

+                xLR[chr[0]] = getLeftRight(data.posByChr[chr[0]])

+      

+            #console.log("xLR:", xLR)

       

             # z-axis (color) limits; if not provided, make symmetric about 0

             zmin = 0

@@ -54,7 +58,9 @@ lodheatmap = () ->
             zthresh = zthresh ? zmin - 1

 

             data.cells = []

-            for chr in data.chrnames

+            for chr_ob in data.chrnames

+                chr = chr_ob[0]

+                #console.log("xLR[chr]:", xLR[chr])

                 for pos, i in data.posByChr[chr]

                     for lod,j in data.lodByChr[chr][i]

                         if lod >= zthresh or lod <= -zthresh

@@ -78,7 +84,7 @@ lodheatmap = () ->
              .data(data.chrnames)

              .enter()

              .append("rect")

-             .attr("id", (d) -> "box#{d}")

+             .attr("id", (d) -> "box#{d[0]}")

              .attr("x", (d,i) -> data.chrStart[i])

              .attr("y", (d) -> margin.top)

              .attr("height", height)

@@ -101,7 +107,7 @@ lodheatmap = () ->
                  .append("text")

                  .attr("x", (d,i) -> (data.chrStart[i] + data.chrEnd[i])/2)

                  .attr("y", margin.top+height+axispos.xlabel)

-                 .text((d) -> d)

+                 .text((d) -> d[0])

             xaxis.append("text").attr("class", "title")

                  .attr("x", margin.left+width/2)

                  .attr("y", margin.top+height+axispos.xtitle)

diff --git a/wqflask/wqflask/static/new/javascript/lodheatmap.js b/wqflask/wqflask/static/new/javascript/lodheatmap.js
index b009b4cd..5ea16590 100644
--- a/wqflask/wqflask/static/new/javascript/lodheatmap.js
+++ b/wqflask/wqflask/static/new/javascript/lodheatmap.js
@@ -33,7 +33,7 @@ lodheatmap = function() {
   cellSelect = null;
   chart = function(selection) {
     return selection.each(function(data) {
-      var cells, celltip, chr, extent, g, gEnter, i, j, lod, lodcol, nlod, pos, rectHeight, svg, titlegrp, xLR, xaxis, yaxis, zmax, zmin, _i, _j, _k, _l, _len, _len1, _len2, _len3, _len4, _m, _ref, _ref1, _ref2, _ref3, _ref4;
+      var cells, celltip, chr, chr_ob, extent, g, gEnter, i, j, lod, lodcol, nlod, pos, rectHeight, svg, titlegrp, xLR, xaxis, yaxis, zmax, zmin, _i, _j, _k, _l, _len, _len1, _len2, _len3, _len4, _m, _ref, _ref1, _ref2, _ref3, _ref4;
       data = reorgLodData(data);
       data = chrscales(data, width, chrGap, margin.left, true);
       xscale = data.xscale;
@@ -41,12 +41,10 @@ lodheatmap = function() {
       yscale.domain([-0.5, nlod - 0.5]).range([margin.top + height, margin.top]);
       rectHeight = yscale(0) - yscale(1);
       xLR = {};
-      console.log("data.chrnames:", data.chrnames);
       _ref = data.chrnames;
       for (_i = 0, _len = _ref.length; _i < _len; _i++) {
         chr = _ref[_i];
-        console.log("chr is:", chr);
-        xLR[chr] = getLeftRight(data.posByChr[chr]);
+        xLR[chr[0]] = getLeftRight(data.posByChr[chr[0]]);
       }
       zmin = 0;
       zmax = 0;
@@ -73,7 +71,8 @@ lodheatmap = function() {
       data.cells = [];
       _ref2 = data.chrnames;
       for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) {
-        chr = _ref2[_k];
+        chr_ob = _ref2[_k];
+        chr = chr_ob[0];
         _ref3 = data.posByChr[chr];
         for (i = _l = 0, _len3 = _ref3.length; _l < _len3; i = ++_l) {
           pos = _ref3[i];
@@ -98,7 +97,7 @@ lodheatmap = function() {
       svg.attr("width", width + margin.left + margin.right).attr("height", height + margin.top + margin.bottom);
       g = svg.select("g");
       g.append("g").attr("id", "boxes").selectAll("empty").data(data.chrnames).enter().append("rect").attr("id", function(d) {
-        return "box" + d;
+        return "box" + d[0];
       }).attr("x", function(d, i) {
         return data.chrStart[i];
       }).attr("y", function(d) {
@@ -111,7 +110,7 @@ lodheatmap = function() {
       xaxis.selectAll("empty").data(data.chrnames).enter().append("text").attr("x", function(d, i) {
         return (data.chrStart[i] + data.chrEnd[i]) / 2;
       }).attr("y", margin.top + height + axispos.xlabel).text(function(d) {
-        return d;
+        return d[0];
       });
       xaxis.append("text").attr("class", "title").attr("x", margin.left + width / 2).attr("y", margin.top + height + axispos.xtitle).text(xlab);
       rotate_ylab = rotate_ylab != null ? rotate_ylab : ylab.length > 1;
diff --git a/wqflask/wqflask/static/new/javascript/manhattan_plot.coffee b/wqflask/wqflask/static/new/javascript/manhattan_plot.coffee
index 37226b8a..cc087493 100644
--- a/wqflask/wqflask/static/new/javascript/manhattan_plot.coffee
+++ b/wqflask/wqflask/static/new/javascript/manhattan_plot.coffee
@@ -32,8 +32,7 @@ lodchart = () ->
     ## 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

@@ -68,6 +67,8 @@ lodchart = () ->
         # if yticks not provided, use nyticks to choose pretty ones

         yticks = yticks ? yscale.ticks(nyticks)

   

+        console.log("data:", data)

+  

         # reorganize lod,pos by chromosomes

         data = reorgLodData(data, lodvarname)

   

@@ -206,7 +207,7 @@ lodchart = () ->
           markertip = d3.tip()

                         .attr('class', 'd3-tip')

                         .html((d) ->

-                          [d.name, " LRS = #{d3.format('.2f')(d.lod)}"])

+                          [d.name, " LOD = #{d3.format('.2f')(d.lod)}"])

                         .direction("e")

                         .offset([0,10])

           svg.call(markertip)

@@ -374,90 +375,90 @@ lodchart = () ->
 # 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

+#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")

+#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")

diff --git a/wqflask/wqflask/static/new/javascript/manhattan_plot.js b/wqflask/wqflask/static/new/javascript/manhattan_plot.js
index 203f3a1b..9618acb2 100644
--- a/wqflask/wqflask/static/new/javascript/manhattan_plot.js
+++ b/wqflask/wqflask/static/new/javascript/manhattan_plot.js
@@ -1,5 +1,5 @@
 // Generated by CoffeeScript 1.7.1
-var chrscales, formatAxis, lodchart, maxdiff, reorgLodData;
+var lodchart;
 
 lodchart = function() {
   var axispos, chart, chrGap, chrSelect, darkrect, height, lightrect, linewidth, lodcurve, lodlinecolor, lodvarname, margin, markerSelect, nyticks, pad4heatmap, pointcolor, pointhover, pointsAtMarkers, pointsize, pointstroke, rotate_ylab, title, titlepos, width, xlab, xscale, ylab, ylim, yscale, yticks;
@@ -46,7 +46,6 @@ lodchart = function() {
   chart = function(selection) {
     return selection.each(function(data) {
       var g, gEnter, hiddenpoints, lodvarnum, markerpoints, markertip, redraw_plot, svg, titlegrp, x, xaxis, yaxis;
-      console.log("data:", data);
       lodvarname = lodvarname != null ? lodvarname : data.lodnames[0];
       data[lodvarname] = (function() {
         var _i, _len, _ref, _results;
@@ -67,6 +66,7 @@ lodchart = function() {
       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]);
       yticks = yticks != null ? yticks : yscale.ticks(nyticks);
+      console.log("data:", data);
       data = reorgLodData(data, lodvarname);
       data = chrscales(data, width, chrGap, margin.left, pad4heatmap);
       xscale = data.xscale;
@@ -138,7 +138,7 @@ lodchart = function() {
       if (pointsAtMarkers) {
         hiddenpoints = g.append("g").attr("id", "markerpoints_hidden");
         markertip = d3.tip().attr('class', 'd3-tip').html(function(d) {
-          return [d.name, " LRS = " + (d3.format('.2f')(d.lod))];
+          return [d.name, " LOD = " + (d3.format('.2f')(d.lod))];
         }).direction("e").offset([0, 10]);
         svg.call(markertip);
         return markerSelect = hiddenpoints.selectAll("empty").data(data.markers).enter().append("circle").attr("cx", function(d) {
@@ -339,137 +339,3 @@ lodchart = function() {
   };
   return chart;
 };
-
-reorgLodData = function(data, lodvarname) {
-  var chr, i, j, lodcolumn, lodval, marker, pos, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2;
-  if (lodvarname == null) {
-    lodvarname = null;
-  }
-  data.posByChr = {};
-  data.lodByChr = {};
-  _ref = data.chrnames;
-  for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) {
-    chr = _ref[i];
-    data.posByChr[chr[0]] = [];
-    data.lodByChr[chr[0]] = [];
-    _ref1 = data.pos;
-    for (j = _j = 0, _len1 = _ref1.length; _j < _len1; j = ++_j) {
-      pos = _ref1[j];
-      if (data.chr[j] === chr[0]) {
-        data.posByChr[chr[0]].push(pos);
-        if (!Array.isArray(data.lodnames)) {
-          data.lodnames = [data.lodnames];
-        }
-        lodval = (function() {
-          var _k, _len2, _ref2, _results;
-          _ref2 = data.lodnames;
-          _results = [];
-          for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) {
-            lodcolumn = _ref2[_k];
-            _results.push(data[lodcolumn][j]);
-          }
-          return _results;
-        })();
-        data.lodByChr[chr[0]].push(lodval);
-      }
-    }
-  }
-  if (lodvarname != null) {
-    data.markers = [];
-    _ref2 = data.markernames;
-    for (i = _k = 0, _len2 = _ref2.length; _k < _len2; i = ++_k) {
-      marker = _ref2[i];
-      if (marker !== "") {
-        data.markers.push({
-          name: marker,
-          chr: data.chr[i],
-          pos: data.pos[i],
-          lod: data[lodvarname][i]
-        });
-      }
-    }
-  }
-  return data;
-};
-
-chrscales = function(data, width, chrGap, leftMargin, pad4heatmap) {
-  var L, chr, chrEnd, chrLength, chrStart, cur, d, i, maxd, rng, totalChrLength, w, _i, _j, _len, _len1, _ref, _ref1;
-  chrStart = [];
-  chrEnd = [];
-  chrLength = [];
-  totalChrLength = 0;
-  maxd = 0;
-  _ref = data.chrnames;
-  for (_i = 0, _len = _ref.length; _i < _len; _i++) {
-    chr = _ref[_i];
-    d = maxdiff(data.posByChr[chr[0]]);
-    if (d > maxd) {
-      maxd = d;
-    }
-    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;
-  }
-  if (pad4heatmap) {
-    data.recwidth = maxd;
-    chrStart = chrStart.map(function(x) {
-      return x - maxd / 2;
-    });
-    chrEnd = chrEnd.map(function(x) {
-      return x + maxd / 2;
-    });
-    chrLength = chrLength.map(function(x) {
-      return x + maxd;
-    });
-    totalChrLength += chrLength.length * maxd;
-  }
-  data.chrStart = [];
-  data.chrEnd = [];
-  cur = leftMargin;
-  if (!pad4heatmap) {
-    cur += chrGap / 2;
-  }
-  data.xscale = {};
-  _ref1 = data.chrnames;
-  for (i = _j = 0, _len1 = _ref1.length; _j < _len1; i = ++_j) {
-    chr = _ref1[i];
-    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;
-    data.xscale[chr[0]] = d3.scale.linear().domain([chrStart[i], chrEnd[i]]).range([data.chrStart[i], data.chrEnd[i]]);
-  }
-  return data;
-};
-
-maxdiff = function(x) {
-  var d, i, result, _i, _ref;
-  if (x.length < 2) {
-    return null;
-  }
-  result = x[1] - x[0];
-  if (x.length < 3) {
-    return result;
-  }
-  for (i = _i = 2, _ref = x.length; 2 <= _ref ? _i < _ref : _i > _ref; i = 2 <= _ref ? ++_i : --_i) {
-    d = x[i] - x[i - 1];
-    if (d > result) {
-      result = d;
-    }
-  }
-  return result;
-};
-
-formatAxis = function(d) {
-  var ndig;
-  d = d[1] - d[0];
-  ndig = Math.floor(Math.log(d % 10) / Math.log(10));
-  if (ndig > 0) {
-    ndig = 0;
-  }
-  ndig = Math.abs(ndig);
-  return d3.format("." + ndig + "f");
-};
diff --git a/wqflask/wqflask/static/new/javascript/panelutil.coffee b/wqflask/wqflask/static/new/javascript/panelutil.coffee
new file mode 100644
index 00000000..d1eb83e5
--- /dev/null
+++ b/wqflask/wqflask/static/new/javascript/panelutil.coffee
@@ -0,0 +1,357 @@
+# A variety of utility functions used by the different panel functions

+

+# determine rounding of axis labels

+formatAxis = (d, extra_digits=0) ->

+    d = d[1] - d[0]

+    ndig = Math.floor( Math.log(d % 10) / Math.log(10) )

+    ndig = 0 if ndig > 0

+    ndig = Math.abs(ndig) + extra_digits

+    d3.format(".#{ndig}f")

+

+# unique values of array (ignore nulls)

+unique = (x) ->

+    output = {}

+    output[v] = v for v in x when v

+    output[v] for v of output

+

+# Pull out a variable (column) from a two-dimensional array

+pullVarAsArray = (data, variable) ->

+    v = []

+    for i of data

+        v = v.concat data[i][variable]

+    v

+  

+  

+# 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

+      #console.log("chr:", chr)

+      data.posByChr[chr[0]] = []

+      data.lodByChr[chr[0]] = []

+      for pos, j in data.pos

+        if data.chr[j].toString() == chr[0]

+          #console.log(data.chr[j] + " AND " + 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)

+

+    #console.log("data.posByChr:", data.posByChr)

+

+    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

+    

+    

+# 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 = {}

+#

+#    #console.log("data.chr", data.chr)

+#    #console.log("data.chrnames:", data.chrnames)

+#    the_chr = "0"

+#    for chr,i in data.chrnames

+#        data.posByChr[chr] = []

+#        data.lodByChr[chr] = []

+#        for pos,j in data.pos

+#            console.log("data.chr[j][0]:", data.chr[j][0])

+#            if data.chr[j][0] == chr

+#                console.log("IS EQUAL")

+#                data.posByChr[chr].push(pos)

+#                data.lodnames = [data.lodnames] unless Array.isArray(data.lodnames)

+#                lodval = (data[lodcolumn][j] for lodcolumn in data.lodnames)

+#                data.lodByChr[chr].push(lodval)

+#

+#    if lodvarname?

+#        data.markers = []

+#        for marker,i in data.markernames

+#            if marker != ""

+#                data.markers.push({name:marker, chr:data.chr[i][0], 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])

+#        maxd = d if d > maxd

+#

+#        rng = d3.extent(data.posByChr[chr])

+#        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] = d3.scale.linear()

+#                             .domain([chrStart[i], chrEnd[i]])

+#                             .range([data.chrStart[i], data.chrEnd[i]])

+#

+#    # return data with new stuff added

+#    data

+

+# Select a set of categorical colors

+# ngroup is positive integer

+# palette = "dark" or "pastel"

+selectGroupColors = (ngroup, palette) ->

+    return [] if ngroup == 0

+

+    if palette == "dark"

+        return ["slateblue"] if ngroup == 1

+        return ["MediumVioletRed", "slateblue"] if ngroup == 2

+        return colorbrewer.Set1[ngroup] if ngroup <= 9

+        return d3.scale.category20().range()[0...ngroup]

+    else

+        return ["#bebebe"] if ngroup == 1

+        return ["lightpink", "lightblue"] if ngroup == 2

+        return colorbrewer.Pastel1[ngroup] if ngroup <= 9

+        # below is rough attempt to make _big_ pastel palette

+        return ["#8fc7f4", "#fed7f8", "#ffbf8e", "#fffbb8",

+                "#8ce08c", "#d8ffca", "#f68788", "#ffd8d6",

+                "#d4a7fd", "#f5f0f5", "#cc968b", "#f4dcd4",

+                "#f3b7f2", "#f7f6f2", "#bfbfbf", "#f7f7f7",

+                "#fcfd82", "#fbfbcd", "#87feff", "#defaf5"][0...ngroup]

+

+# expand element/array (e.g., of colors) to a given length

+#     single elment -> array, then repeated to length n

+expand2vector = (input, n) ->

+    return input unless input? # return null if null

+    return input if Array.isArray(input) and input.length >= n

+    input = [input] unless Array.isArray(input)

+    input = (input[0] for i of d3.range(n)) if input.length == 1 and n > 1

+    input

+

+# median of a vector

+median = (x) ->

+    return null if !x? 

+    n = x.length

+    x.sort((a,b) -> a-b)

+    if n % 2 == 1

+        return x[(n-1)/2]

+    (x[n/2] + x[(n/2)-1])/2

+

+# given a vector of x's, return hash with values to left and right, and the differences

+getLeftRight = (x) ->

+    n = x.length

+    x.sort( (a,b) -> a-b )

+

+    xdif = []

+    result = {}

+    for v in x

+        result[v] = {}

+

+    for i in [1...n]

+        #console.log("result:", result)

+        xdif.push(x[i]-x[i-1])

+        result[x[i]].left = x[i-1]

+    for i in [0...(n-1)]

+        result[x[i]].right = x[i+1]

+

+    xdif = median(xdif)

+    result.mediandiff = xdif

+

+    result[x[0]].left = x[0]-xdif

+    result[x[n-1]].right = x[n-1]+xdif

+    result.extent = [x[0]-xdif/2, x[n-1]+xdif/2]

+

+    result

+

+# 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

+

+# matrix extent, min max

+matrixMin = (mat) ->

+    result = mat[0][0]

+    for i of mat

+        for j of mat[i]

+            result = mat[i][j] if result > mat[i][j]

+    result      

+

+matrixMax = (mat) ->

+    result = mat[0][0]

+    for i of mat

+        for j of mat[i]

+            result = mat[i][j] if result < mat[i][j]

+    result      

+

+matrixMaxAbs = (mat) ->

+    result = Math.abs(mat[0][0])

+    for i of mat

+        for j of mat[i]

+            result = Math.abs(mat[i][j]) if result < mat[i][j]

+    result      

+

+matrixExtent = (mat) -> [matrixMin(mat), matrixMax(mat)]

+

+d3.selection.prototype.moveToFront = () ->

+    this.each () -> this.parentNode.appendChild(this)

+  

+d3.selection.prototype.moveToBack = () ->

+    this.each () ->

+        firstChild = this.parentNode.firstchild

+        this.parentNode.insertBefore(this, firstChild) if firstChild

+

+forceAsArray = (x) ->

+    return x unless x? # if null, return null

+    return x if Array.isArray(x)

+    [x]

+

+# any values in vec that appear in missing are made null

+missing2null = (vec, missingvalues=['NA', '']) ->

+    vec.map (value) -> if missingvalues.indexOf(value) > -1 then null else value

+

+# display error at top of page

+displayError = (message) ->

+    if d3.select("div.error").empty() # no errors yet

+        d3.select("body")

+          .insert("div", ":first-child")

+          .attr("class", "error")

+    d3.select("div.error")

+      .append("p")

+      .text(message)

+

+# sum values in an array

+sumArray = (vec) -> (vec.reduce (a,b) -> a+b)

+

+# calculate cross-tabulation

+calc_crosstab = (data) ->

+    nrow = data.ycat.length

+    ncol = data.xcat.length

+

+    result = ((0 for col in [0..ncol]) for row in [0..nrow]) # matrix of 0's

+

+    # count things up

+    for i of data.x

+        result[data.y[i]][data.x[i]] += 1

+

+    # row and column sums

+    rs = rowSums(result)

+    cs = colSums(result)

+

+    # fill in column sums

+    for i in [0...ncol]

+        result[nrow][i] = cs[i]

+

+    # fill in row sums

+    for i in [0...nrow]

+        result[i][ncol] = rs[i]

+

+    # fill in total

+    result[nrow][ncol] = sumArray(rs)

+

+    result

+

+# rowSums: the sums for each row

+rowSums = (mat) -> (sumArray(x) for x in mat)

+

+# transpose: matrix transpose

+transpose = (mat) -> ((mat[i][j] for i in [0...mat.length]) for j in [0...mat[0].length])

+

+# colSums = the sums for each column

+colSums = (mat) -> rowSums(transpose(mat))

+

+# log base 2

+log2 = (x) -> 

+    return(x) unless x?

+    Math.log(x)/Math.log(2.0)

+

+# log base 10

+log10 = (x) ->

+    return(x) unless x?

+    Math.log(x)/Math.log(10.0)

+

+# absolute value, preserving nulls

+abs = (x) -> 

+    return(x) unless x?

+    Math.abs(x)
\ No newline at end of file
diff --git a/wqflask/wqflask/static/new/javascript/panelutil.js b/wqflask/wqflask/static/new/javascript/panelutil.js
index 07384f2b..2acc1651 100644
--- a/wqflask/wqflask/static/new/javascript/panelutil.js
+++ b/wqflask/wqflask/static/new/javascript/panelutil.js
@@ -1,319 +1,439 @@
-// Generated by CoffeeScript 1.7.1

-var chrscales, expand2vector, forceAsArray, formatAxis, getLeftRight, matrixExtent, matrixMax, matrixMaxAbs, matrixMin, maxdiff, median, pullVarAsArray, reorgLodData, selectGroupColors, unique;

-

-formatAxis = function(d) {

-  var ndig;

-  d = d[1] - d[0];

-  ndig = Math.floor(Math.log(d % 10) / Math.log(10));

-  if (ndig > 0) {

-    ndig = 0;

-  }

-  ndig = Math.abs(ndig);

-  return d3.format("." + ndig + "f");

-};

-

-unique = function(x) {

-  var output, v, _i, _len, _results;

-  output = {};

-  for (_i = 0, _len = x.length; _i < _len; _i++) {

-    v = x[_i];

-    if (v) {

-      output[v] = v;

-    }

-  }

-  _results = [];

-  for (v in output) {

-    _results.push(output[v]);

-  }

-  return _results;

-};

-

-pullVarAsArray = function(data, variable) {

-  var i, v;

-  v = [];

-  for (i in data) {

-    v = v.concat(data[i][variable]);

-  }

-  return v;

-};

-

-reorgLodData = function(data, lodvarname) {

-  var chr, i, j, lodcolumn, lodval, marker, pos, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2;

-  if (lodvarname == null) {

-    lodvarname = null;

-  }

-  data.posByChr = {};

-  data.lodByChr = {};

-  _ref = data.chrnames;

-  for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) {

-    chr = _ref[i];

-    data.posByChr[chr] = [];

-    data.lodByChr[chr] = [];

-    _ref1 = data.pos;

-    for (j = _j = 0, _len1 = _ref1.length; _j < _len1; j = ++_j) {

-      pos = _ref1[j];

-      if (data.chr[j] === chr) {

-        data.posByChr[chr].push(pos);

-        if (!Array.isArray(data.lodnames)) {

-          data.lodnames = [data.lodnames];

-        }

-        lodval = (function() {

-          var _k, _len2, _ref2, _results;

-          _ref2 = data.lodnames;

-          _results = [];

-          for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) {

-            lodcolumn = _ref2[_k];

-            _results.push(data[lodcolumn][j]);

-          }

-          return _results;

-        })();

-        data.lodByChr[chr].push(lodval);

-      }

-    }

-  }

-  if (lodvarname != null) {

-    data.markers = [];

-    _ref2 = data.markernames;

-    for (i = _k = 0, _len2 = _ref2.length; _k < _len2; i = ++_k) {

-      marker = _ref2[i];

-      if (marker !== "") {

-        data.markers.push({

-          name: marker,

-          chr: data.chr[i],

-          pos: data.pos[i],

-          lod: data[lodvarname][i]

-        });

-      }

-    }

-  }

-  return data;

-};

-

-chrscales = function(data, width, chrGap, leftMargin, pad4heatmap) {

-  var L, chr, chrEnd, chrLength, chrStart, cur, d, i, maxd, rng, totalChrLength, w, _i, _j, _len, _len1, _ref, _ref1;

-  chrStart = [];

-  chrEnd = [];

-  chrLength = [];

-  totalChrLength = 0;

-  maxd = 0;

-  _ref = data.chrnames;

-  for (_i = 0, _len = _ref.length; _i < _len; _i++) {

-    chr = _ref[_i];

-    d = maxdiff(data.posByChr[chr]);

-    if (d > maxd) {

-      maxd = d;

-    }

-    rng = d3.extent(data.posByChr[chr]);

-    chrStart.push(rng[0]);

-    chrEnd.push(rng[1]);

-    L = rng[1] - rng[0];

-    chrLength.push(L);

-    totalChrLength += L;

-  }

-  if (pad4heatmap) {

-    data.recwidth = maxd;

-    chrStart = chrStart.map(function(x) {

-      return x - maxd / 2;

-    });

-    chrEnd = chrEnd.map(function(x) {

-      return x + maxd / 2;

-    });

-    chrLength = chrLength.map(function(x) {

-      return x + maxd;

-    });

-    totalChrLength += chrLength.length * maxd;

-  }

-  data.chrStart = [];

-  data.chrEnd = [];

-  cur = leftMargin;

-  if (!pad4heatmap) {

-    cur += chrGap / 2;

-  }

-  data.xscale = {};

-  _ref1 = data.chrnames;

-  for (i = _j = 0, _len1 = _ref1.length; _j < _len1; i = ++_j) {

-    chr = _ref1[i];

-    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;

-    data.xscale[chr] = d3.scale.linear().domain([chrStart[i], chrEnd[i]]).range([data.chrStart[i], data.chrEnd[i]]);

-  }

-  return data;

-};

-

-selectGroupColors = function(ngroup, palette) {

-  if (ngroup === 0) {

-    return [];

-  }

-  if (palette === "dark") {

-    if (ngroup === 1) {

-      return ["slateblue"];

-    }

-    if (ngroup === 2) {

-      return ["MediumVioletRed", "slateblue"];

-    }

-    if (ngroup <= 9) {

-      return colorbrewer.Set1[ngroup];

-    }

-    return d3.scale.category20().range().slice(0, ngroup);

-  } else {

-    if (ngroup === 1) {

-      return [d3.rgb(190, 190, 190)];

-    }

-    if (ngroup === 2) {

-      return ["lightpink", "lightblue"];

-    }

-    if (ngroup <= 9) {

-      return colorbrewer.Pastel1[ngroup];

-    }

-    return ["#8fc7f4", "#fed7f8", "#ffbf8e", "#fffbb8", "#8ce08c", "#d8ffca", "#f68788", "#ffd8d6", "#d4a7fd", "#f5f0f5", "#cc968b", "#f4dcd4", "#f3b7f2", "#f7f6f2", "#bfbfbf", "#f7f7f7", "#fcfd82", "#fbfbcd", "#87feff", "#defaf5"].slice(0, ngroup);

-  }

-};

-

-expand2vector = function(input, n) {

-  var i;

-  if (Array.isArray(input) && input.length >= n) {

-    return input;

-  }

-  if (!Array.isArray(input)) {

-    input = [input];

-  }

-  if (input.length === 1 && n > 1) {

-    input = (function() {

-      var _results;

-      _results = [];

-      for (i in d3.range(n)) {

-        _results.push(input[0]);

-      }

-      return _results;

-    })();

-  }

-  return input;

-};

-

-median = function(x) {

-  var n;

-  if (x == null) {

-    return null;

-  }

-  n = x.length;

-  x.sort(function(a, b) {

-    return a - b;

-  });

-  if (n % 2 === 1) {

-    return x[(n - 1) / 2];

-  }

-  return (x[n / 2] + x[(n / 2) - 1]) / 2;

-};

-

-getLeftRight = function(x) {

-  var i, n, result, v, xdif, _i, _j, _k, _len, _ref;

-  n = x.length;

-  x.sort(function(a, b) {

-    return a - b;

-  });

-  xdif = [];

-  result = {};

-  for (_i = 0, _len = x.length; _i < _len; _i++) {

-    v = x[_i];

-    result[v] = {};

-  }

-  for (i = _j = 1; 1 <= n ? _j < n : _j > n; i = 1 <= n ? ++_j : --_j) {

-    xdif.push(x[i] - x[i - 1]);

-    result[x[i]].left = x[i - 1];

-  }

-  for (i = _k = 0, _ref = n - 1; 0 <= _ref ? _k < _ref : _k > _ref; i = 0 <= _ref ? ++_k : --_k) {

-    result[x[i]].right = x[i + 1];

-  }

-  xdif = median(xdif);

-  result.mediandiff = xdif;

-  result[x[0]].left = x[0] - xdif;

-  result[x[n - 1]].right = x[n - 1] + xdif;

-  result.extent = [x[0] - xdif / 2, x[n - 1] + xdif / 2];

-  return result;

-};

-

-maxdiff = function(x) {

-  var d, i, result, _i, _ref;

-  if (x.length < 2) {

-    return null;

-  }

-  result = x[1] - x[0];

-  if (x.length < 3) {

-    return result;

-  }

-  for (i = _i = 2, _ref = x.length; 2 <= _ref ? _i < _ref : _i > _ref; i = 2 <= _ref ? ++_i : --_i) {

-    d = x[i] - x[i - 1];

-    if (d > result) {

-      result = d;

-    }

-  }

-  return result;

-};

-

-matrixMin = function(mat) {

-  var i, j, result;

-  result = mat[0][0];

-  for (i in mat) {

-    for (j in mat[i]) {

-      if (result > mat[i][j]) {

-        result = mat[i][j];

-      }

-    }

-  }

-  return result;

-};

-

-matrixMax = function(mat) {

-  var i, j, result;

-  result = mat[0][0];

-  for (i in mat) {

-    for (j in mat[i]) {

-      if (result < mat[i][j]) {

-        result = mat[i][j];

-      }

-    }

-  }

-  return result;

-};

-

-matrixMaxAbs = function(mat) {

-  var i, j, result;

-  result = Math.abs(mat[0][0]);

-  for (i in mat) {

-    for (j in mat[i]) {

-      if (result < mat[i][j]) {

-        result = Math.abs(mat[i][j]);

-      }

-    }

-  }

-  return result;

-};

-

-matrixExtent = function(mat) {

-  return [matrixMin(mat), matrixMax(mat)];

-};

-

-d3.selection.prototype.moveToFront = function() {

-  return this.each(function() {

-    return this.parentNode.appendChild(this);

-  });

-};

-

-d3.selection.prototype.moveToBack = function() {

-  return this.each(function() {

-    var firstChild;

-    firstChild = this.parentNode.firstchild;

-    if (firstChild) {

-      return this.parentNode.insertBefore(this, firstChild);

-    }

-  });

-};

-

-forceAsArray = function(x) {

-  if (Array.isArray(x)) {

-    return x;

-  }

-  return [x];

-};

+// Generated by CoffeeScript 1.7.1
+var abs, calc_crosstab, chrscales, colSums, displayError, expand2vector, forceAsArray, formatAxis, getLeftRight, log10, log2, matrixExtent, matrixMax, matrixMaxAbs, matrixMin, maxdiff, median, missing2null, pullVarAsArray, reorgLodData, rowSums, selectGroupColors, sumArray, transpose, unique;
+
+formatAxis = function(d, extra_digits) {
+  var ndig;
+  if (extra_digits == null) {
+    extra_digits = 0;
+  }
+  d = d[1] - d[0];
+  ndig = Math.floor(Math.log(d % 10) / Math.log(10));
+  if (ndig > 0) {
+    ndig = 0;
+  }
+  ndig = Math.abs(ndig) + extra_digits;
+  return d3.format("." + ndig + "f");
+};
+
+unique = function(x) {
+  var output, v, _i, _len, _results;
+  output = {};
+  for (_i = 0, _len = x.length; _i < _len; _i++) {
+    v = x[_i];
+    if (v) {
+      output[v] = v;
+    }
+  }
+  _results = [];
+  for (v in output) {
+    _results.push(output[v]);
+  }
+  return _results;
+};
+
+pullVarAsArray = function(data, variable) {
+  var i, v;
+  v = [];
+  for (i in data) {
+    v = v.concat(data[i][variable]);
+  }
+  return v;
+};
+
+reorgLodData = function(data, lodvarname) {
+  var chr, i, j, lodcolumn, lodval, marker, pos, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2;
+  if (lodvarname == null) {
+    lodvarname = null;
+  }
+  data.posByChr = {};
+  data.lodByChr = {};
+  _ref = data.chrnames;
+  for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) {
+    chr = _ref[i];
+    data.posByChr[chr[0]] = [];
+    data.lodByChr[chr[0]] = [];
+    _ref1 = data.pos;
+    for (j = _j = 0, _len1 = _ref1.length; _j < _len1; j = ++_j) {
+      pos = _ref1[j];
+      if (data.chr[j].toString() === chr[0]) {
+        data.posByChr[chr[0]].push(pos);
+        if (!Array.isArray(data.lodnames)) {
+          data.lodnames = [data.lodnames];
+        }
+        lodval = (function() {
+          var _k, _len2, _ref2, _results;
+          _ref2 = data.lodnames;
+          _results = [];
+          for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) {
+            lodcolumn = _ref2[_k];
+            _results.push(data[lodcolumn][j]);
+          }
+          return _results;
+        })();
+        data.lodByChr[chr[0]].push(lodval);
+      }
+    }
+  }
+  if (lodvarname != null) {
+    data.markers = [];
+    _ref2 = data.markernames;
+    for (i = _k = 0, _len2 = _ref2.length; _k < _len2; i = ++_k) {
+      marker = _ref2[i];
+      if (marker !== "") {
+        data.markers.push({
+          name: marker,
+          chr: data.chr[i],
+          pos: data.pos[i],
+          lod: data[lodvarname][i]
+        });
+      }
+    }
+  }
+  return data;
+};
+
+chrscales = function(data, width, chrGap, leftMargin, pad4heatmap) {
+  var L, chr, chrEnd, chrLength, chrStart, cur, d, i, maxd, rng, totalChrLength, w, _i, _j, _len, _len1, _ref, _ref1;
+  chrStart = [];
+  chrEnd = [];
+  chrLength = [];
+  totalChrLength = 0;
+  maxd = 0;
+  _ref = data.chrnames;
+  for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+    chr = _ref[_i];
+    d = maxdiff(data.posByChr[chr[0]]);
+    if (d > maxd) {
+      maxd = d;
+    }
+    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;
+  }
+  if (pad4heatmap) {
+    data.recwidth = maxd;
+    chrStart = chrStart.map(function(x) {
+      return x - maxd / 2;
+    });
+    chrEnd = chrEnd.map(function(x) {
+      return x + maxd / 2;
+    });
+    chrLength = chrLength.map(function(x) {
+      return x + maxd;
+    });
+    totalChrLength += chrLength.length * maxd;
+  }
+  data.chrStart = [];
+  data.chrEnd = [];
+  cur = leftMargin;
+  if (!pad4heatmap) {
+    cur += chrGap / 2;
+  }
+  data.xscale = {};
+  _ref1 = data.chrnames;
+  for (i = _j = 0, _len1 = _ref1.length; _j < _len1; i = ++_j) {
+    chr = _ref1[i];
+    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;
+    data.xscale[chr[0]] = d3.scale.linear().domain([chrStart[i], chrEnd[i]]).range([data.chrStart[i], data.chrEnd[i]]);
+  }
+  return data;
+};
+
+selectGroupColors = function(ngroup, palette) {
+  if (ngroup === 0) {
+    return [];
+  }
+  if (palette === "dark") {
+    if (ngroup === 1) {
+      return ["slateblue"];
+    }
+    if (ngroup === 2) {
+      return ["MediumVioletRed", "slateblue"];
+    }
+    if (ngroup <= 9) {
+      return colorbrewer.Set1[ngroup];
+    }
+    return d3.scale.category20().range().slice(0, ngroup);
+  } else {
+    if (ngroup === 1) {
+      return ["#bebebe"];
+    }
+    if (ngroup === 2) {
+      return ["lightpink", "lightblue"];
+    }
+    if (ngroup <= 9) {
+      return colorbrewer.Pastel1[ngroup];
+    }
+    return ["#8fc7f4", "#fed7f8", "#ffbf8e", "#fffbb8", "#8ce08c", "#d8ffca", "#f68788", "#ffd8d6", "#d4a7fd", "#f5f0f5", "#cc968b", "#f4dcd4", "#f3b7f2", "#f7f6f2", "#bfbfbf", "#f7f7f7", "#fcfd82", "#fbfbcd", "#87feff", "#defaf5"].slice(0, ngroup);
+  }
+};
+
+expand2vector = function(input, n) {
+  var i;
+  if (input == null) {
+    return input;
+  }
+  if (Array.isArray(input) && input.length >= n) {
+    return input;
+  }
+  if (!Array.isArray(input)) {
+    input = [input];
+  }
+  if (input.length === 1 && n > 1) {
+    input = (function() {
+      var _results;
+      _results = [];
+      for (i in d3.range(n)) {
+        _results.push(input[0]);
+      }
+      return _results;
+    })();
+  }
+  return input;
+};
+
+median = function(x) {
+  var n;
+  if (x == null) {
+    return null;
+  }
+  n = x.length;
+  x.sort(function(a, b) {
+    return a - b;
+  });
+  if (n % 2 === 1) {
+    return x[(n - 1) / 2];
+  }
+  return (x[n / 2] + x[(n / 2) - 1]) / 2;
+};
+
+getLeftRight = function(x) {
+  var i, n, result, v, xdif, _i, _j, _k, _len, _ref;
+  n = x.length;
+  x.sort(function(a, b) {
+    return a - b;
+  });
+  xdif = [];
+  result = {};
+  for (_i = 0, _len = x.length; _i < _len; _i++) {
+    v = x[_i];
+    result[v] = {};
+  }
+  for (i = _j = 1; 1 <= n ? _j < n : _j > n; i = 1 <= n ? ++_j : --_j) {
+    xdif.push(x[i] - x[i - 1]);
+    result[x[i]].left = x[i - 1];
+  }
+  for (i = _k = 0, _ref = n - 1; 0 <= _ref ? _k < _ref : _k > _ref; i = 0 <= _ref ? ++_k : --_k) {
+    result[x[i]].right = x[i + 1];
+  }
+  xdif = median(xdif);
+  result.mediandiff = xdif;
+  result[x[0]].left = x[0] - xdif;
+  result[x[n - 1]].right = x[n - 1] + xdif;
+  result.extent = [x[0] - xdif / 2, x[n - 1] + xdif / 2];
+  return result;
+};
+
+maxdiff = function(x) {
+  var d, i, result, _i, _ref;
+  if (x.length < 2) {
+    return null;
+  }
+  result = x[1] - x[0];
+  if (x.length < 3) {
+    return result;
+  }
+  for (i = _i = 2, _ref = x.length; 2 <= _ref ? _i < _ref : _i > _ref; i = 2 <= _ref ? ++_i : --_i) {
+    d = x[i] - x[i - 1];
+    if (d > result) {
+      result = d;
+    }
+  }
+  return result;
+};
+
+matrixMin = function(mat) {
+  var i, j, result;
+  result = mat[0][0];
+  for (i in mat) {
+    for (j in mat[i]) {
+      if (result > mat[i][j]) {
+        result = mat[i][j];
+      }
+    }
+  }
+  return result;
+};
+
+matrixMax = function(mat) {
+  var i, j, result;
+  result = mat[0][0];
+  for (i in mat) {
+    for (j in mat[i]) {
+      if (result < mat[i][j]) {
+        result = mat[i][j];
+      }
+    }
+  }
+  return result;
+};
+
+matrixMaxAbs = function(mat) {
+  var i, j, result;
+  result = Math.abs(mat[0][0]);
+  for (i in mat) {
+    for (j in mat[i]) {
+      if (result < mat[i][j]) {
+        result = Math.abs(mat[i][j]);
+      }
+    }
+  }
+  return result;
+};
+
+matrixExtent = function(mat) {
+  return [matrixMin(mat), matrixMax(mat)];
+};
+
+d3.selection.prototype.moveToFront = function() {
+  return this.each(function() {
+    return this.parentNode.appendChild(this);
+  });
+};
+
+d3.selection.prototype.moveToBack = function() {
+  return this.each(function() {
+    var firstChild;
+    firstChild = this.parentNode.firstchild;
+    if (firstChild) {
+      return this.parentNode.insertBefore(this, firstChild);
+    }
+  });
+};
+
+forceAsArray = function(x) {
+  if (x == null) {
+    return x;
+  }
+  if (Array.isArray(x)) {
+    return x;
+  }
+  return [x];
+};
+
+missing2null = function(vec, missingvalues) {
+  if (missingvalues == null) {
+    missingvalues = ['NA', ''];
+  }
+  return vec.map(function(value) {
+    if (missingvalues.indexOf(value) > -1) {
+      return null;
+    } else {
+      return value;
+    }
+  });
+};
+
+displayError = function(message) {
+  if (d3.select("div.error").empty()) {
+    d3.select("body").insert("div", ":first-child").attr("class", "error");
+  }
+  return d3.select("div.error").append("p").text(message);
+};
+
+sumArray = function(vec) {
+  return vec.reduce(function(a, b) {
+    return a + b;
+  });
+};
+
+calc_crosstab = function(data) {
+  var col, cs, i, ncol, nrow, result, row, rs, _i, _j;
+  nrow = data.ycat.length;
+  ncol = data.xcat.length;
+  result = (function() {
+    var _i, _results;
+    _results = [];
+    for (row = _i = 0; 0 <= nrow ? _i <= nrow : _i >= nrow; row = 0 <= nrow ? ++_i : --_i) {
+      _results.push((function() {
+        var _j, _results1;
+        _results1 = [];
+        for (col = _j = 0; 0 <= ncol ? _j <= ncol : _j >= ncol; col = 0 <= ncol ? ++_j : --_j) {
+          _results1.push(0);
+        }
+        return _results1;
+      })());
+    }
+    return _results;
+  })();
+  for (i in data.x) {
+    result[data.y[i]][data.x[i]] += 1;
+  }
+  rs = rowSums(result);
+  cs = colSums(result);
+  for (i = _i = 0; 0 <= ncol ? _i < ncol : _i > ncol; i = 0 <= ncol ? ++_i : --_i) {
+    result[nrow][i] = cs[i];
+  }
+  for (i = _j = 0; 0 <= nrow ? _j < nrow : _j > nrow; i = 0 <= nrow ? ++_j : --_j) {
+    result[i][ncol] = rs[i];
+  }
+  result[nrow][ncol] = sumArray(rs);
+  return result;
+};
+
+rowSums = function(mat) {
+  var x, _i, _len, _results;
+  _results = [];
+  for (_i = 0, _len = mat.length; _i < _len; _i++) {
+    x = mat[_i];
+    _results.push(sumArray(x));
+  }
+  return _results;
+};
+
+transpose = function(mat) {
+  var i, j, _i, _ref, _results;
+  _results = [];
+  for (j = _i = 0, _ref = mat[0].length; 0 <= _ref ? _i < _ref : _i > _ref; j = 0 <= _ref ? ++_i : --_i) {
+    _results.push((function() {
+      var _j, _ref1, _results1;
+      _results1 = [];
+      for (i = _j = 0, _ref1 = mat.length; 0 <= _ref1 ? _j < _ref1 : _j > _ref1; i = 0 <= _ref1 ? ++_j : --_j) {
+        _results1.push(mat[i][j]);
+      }
+      return _results1;
+    })());
+  }
+  return _results;
+};
+
+colSums = function(mat) {
+  return rowSums(transpose(mat));
+};
+
+log2 = function(x) {
+  if (x == null) {
+    return x;
+  }
+  return Math.log(x) / Math.log(2.0);
+};
+
+log10 = function(x) {
+  if (x == null) {
+    return x;
+  }
+  return Math.log(x) / Math.log(10.0);
+};
+
+abs = function(x) {
+  if (x == null) {
+    return x;
+  }
+  return Math.abs(x);
+};
diff --git a/wqflask/wqflask/static/new/javascript/show_trait_mapping_tools.coffee b/wqflask/wqflask/static/new/javascript/show_trait_mapping_tools.coffee
index 27bf80d5..fc687c7a 100755
--- a/wqflask/wqflask/static/new/javascript/show_trait_mapping_tools.coffee
+++ b/wqflask/wqflask/static/new/javascript/show_trait_mapping_tools.coffee
@@ -170,6 +170,42 @@ $ ->
         #this.my_timer = setInterval(get_progress, 1000)
         #return false
     )
+    
+    $("#rqtl_geno_compute").click(() =>
+        $("#progress_bar_container").modal()
+        url = "/marker_regression"
+        $('input[name=method]').val("rqtl_geno")
+        $('input[name=num_perm]').val($('input[name=num_perm_rqtl_geno]').val())
+        $('input[name=control_marker]').val($('input[name=control_rqtl_geno]').val())
+        form_data = $('#trait_data_form').serialize()
+        console.log("form_data is:", form_data)
+        
+        #remove_outliers = confirm("Remove outliers?")
+        #if use_outliers == true
+        #    block_outliers()
+        do_ajax_post(url, form_data)
+        
+        #$.ajax(
+        #    type: "POST"
+        #    url: url
+        #    data: form_data
+        #    error: (xhr, ajaxOptions, thrownError) =>
+        #        alert("Sorry, an error occurred")
+        #        console.log(xhr)
+        #        clearInterval(this.my_timer)
+        #        $('#progress_bar_container').modal('hide')
+        #        $("body").html("We got an error.")        
+        #    success: (data) =>
+        #        clearInterval(this.my_timer)
+        #        $('#progress_bar_container').modal('hide')
+        #        $("body").html(data)
+        #)
+        #console.log("settingInterval")
+        #
+        #this.my_timer = setInterval(get_progress, 1000)
+        #return false
+    )
+
 
     $("#plink_compute").click(() =>
         $("#static_progress_bar_container").modal()
diff --git a/wqflask/wqflask/static/new/javascript/show_trait_mapping_tools.js b/wqflask/wqflask/static/new/javascript/show_trait_mapping_tools.js
index d02bca4b..a8a42ffa 100755
--- a/wqflask/wqflask/static/new/javascript/show_trait_mapping_tools.js
+++ b/wqflask/wqflask/static/new/javascript/show_trait_mapping_tools.js
@@ -130,6 +130,19 @@ $(function() {
       return do_ajax_post(url, form_data);
     };
   })(this));
+  $("#rqtl_geno_compute").click((function(_this) {
+    return function() {
+      var form_data, url;
+      $("#progress_bar_container").modal();
+      url = "/marker_regression";
+      $('input[name=method]').val("rqtl_geno");
+      $('input[name=num_perm]').val($('input[name=num_perm_rqtl_geno]').val());
+      $('input[name=control_marker]').val($('input[name=control_rqtl_geno]').val());
+      form_data = $('#trait_data_form').serialize();
+      console.log("form_data is:", form_data);
+      return do_ajax_post(url, form_data);
+    };
+  })(this));
   $("#plink_compute").click((function(_this) {
     return function() {
       var form_data, url;
diff --git a/wqflask/wqflask/static/packages/DT_bootstrap/DT_bootstrap.js b/wqflask/wqflask/static/packages/DT_bootstrap/DT_bootstrap.js
index 328733f2..cfe3e9d2 100755
--- a/wqflask/wqflask/static/packages/DT_bootstrap/DT_bootstrap.js
+++ b/wqflask/wqflask/static/packages/DT_bootstrap/DT_bootstrap.js
@@ -148,12 +148,12 @@ if ( $.fn.DataTable.TableTools ) {
 
 
 /* Table initialisation */
-$(document).ready(function() {
-	$('#example').dataTable( {
-		"sDom": "<'row'<'span6'l><'span6'f>r>t<'row'<'span6'i><'span6'p>>",
-		"sPaginationType": "bootstrap",
-		"oLanguage": {
-			"sLengthMenu": "_MENU_ records per page"
-		}
-	} );
-} );
\ No newline at end of file
+//$(document).ready(function() {
+//	$('#example').dataTable( {
+//		"sDom": "<'row'<'span6'l><'span6'f>r>t<'row'<'span6'i><'span6'p>>",
+//		"sPaginationType": "bootstrap",
+//		"oLanguage": {
+//			"sLengthMenu": "_MENU_ records per page"
+//		}
+//	} );
+//} );
\ No newline at end of file
diff --git a/wqflask/wqflask/templates/show_trait_mapping_tools.html b/wqflask/wqflask/templates/show_trait_mapping_tools.html
index 705eda88..c58b7729 100755
--- a/wqflask/wqflask/templates/show_trait_mapping_tools.html
+++ b/wqflask/wqflask/templates/show_trait_mapping_tools.html
@@ -9,6 +9,9 @@
                 <li>
                     <a href="#pylmm" data-toggle="tab">pyLMM</a>
                 </li>
+                <li>
+                    <a href="#rqtl_geno" data-toggle="tab">rqtl</a>
+                </li>
                 {% if dataset.group.species == 'human' %}
                 <li>
                     <a href="#plink" data-toggle="tab">PLINK</a>
@@ -49,7 +52,7 @@
                     <div class="control-group mapping_method_fields">
                         <label for="mapping_permutations" class="control-label">Permutations (n)</label>
                         <div class="controls">
-                            <input name="num_permutations" value="2000" type="text" />
+                            <input name="num_perm_reaper" value="2000" type="text" />
                         </div>
                     </div>
     
@@ -66,7 +69,7 @@
                         <div class="controls" id="display_additive_effect">                      
                             <label class="radio inline">
                                 <input type="radio" name="display_additive"
-                                       id="display_dditive" value="yes" checked>
+                                       id="display_additive" value="yes" checked>
                                 Yes
                             </label>
                             <label class="radio inline">
@@ -171,6 +174,33 @@
                     </div>
                     
                 </div>
+                <div class="tab-pane" id="rqtl_geno">
+                    
+                    <div class="control-group">
+                        <label for="num_perm_rqtl_geno" class="control-label">Permutations (n)</label>
+                        <div class="controls">
+                            <input name="num_perm_rqtl_geno" value="0" type="text" />
+                        </div>
+                    </div>
+                    <div class="control-group">
+                        <label for="control_rqtl_geno" class="control-label">Control Marker</label>
+                        <div class="controls">
+                            <input name="control_rqtl_geno" value="{{ nearest_marker }}" type="text" />
+                        </div>
+                    </div>
+                    
+                    <div class="control-group">
+                        <div class="controls">
+                            <button id="rqtl_geno_compute"
+                                    class="btn btn-inverse submit_special"
+                                    data-url="/marker_regression"
+                                    title="Compute Marker Regression">
+                                <i class="icon-ok-circle icon-white"></i> Compute
+                            </button>
+                        </div>
+                    </div>
+                    
+                </div>
                 
                 {% if dataset.group.species == 'human' %}
                 <div class="tab-pane" id="plink">
diff --git a/wqflask/wqflask/views.py b/wqflask/wqflask/views.py
index 3c448988..26e690d4 100755
--- a/wqflask/wqflask/views.py
+++ b/wqflask/wqflask/views.py
@@ -209,7 +209,7 @@ def heatmap_page():
     start_vars = request.form
     temp_uuid = uuid.uuid4()
     
-    version = "v4"
+    version = "v5"
     key = "heatmap:{}:".format(version) + json.dumps(start_vars, sort_keys=True)
     print("key is:", pf(key))
     with Bench("Loading cache"):
@@ -252,7 +252,10 @@ def marker_regression_page():
         'dataset',
         'method',
         'suggestive',
-        'maf'
+        'num_perm',
+        'maf',
+        'control_marker',
+        'control_marker_db'
     )
 
     start_vars = {}
@@ -260,7 +263,7 @@ def marker_regression_page():
         if key in wanted or key.startswith(('value:')):
             start_vars[key] = value
 
-    version = "v3"
+    version = "v4"
     key = "marker_regression:{}:".format(version) + json.dumps(start_vars, sort_keys=True)
     print("key is:", pf(key))
     with Bench("Loading cache"):
@@ -296,7 +299,7 @@ def marker_regression_page():
         pickled_result = pickle.dumps(result, pickle.HIGHEST_PROTOCOL)
         print("pickled result length:", len(pickled_result))
         Redis.set(key, pickled_result)
-        Redis.expire(key, 60*60)
+        Redis.expire(key, 1*60)
 
     with Bench("Rendering template"):
         rendered_template = render_template("marker_regression.html", **result)
@@ -346,7 +349,7 @@ def interval_mapping_page():
         if key in wanted or key.startswith(('value:')):
             start_vars[key] = value
 
-    version = "v11"
+    version = "v4"
     key = "interval_mapping:{}:".format(version) + json.dumps(start_vars, sort_keys=True)
     print("key is:", pf(key))
     with Bench("Loading cache"):